oracle insert using DBlink - sql

I have two different databases, say DB1 and DB2. In both DBs I have a common table called test_emp, but there is a difference in the columns of the tables:
DB1
-----
desc table1
--------------
empno number
ename varchar2
sal number
hiredate date
deptno number
DB2
-----
desc table2
--------------
empno number
ename varchar2
sal number
insert into table1 select * from table2#DBLink_Name
But here I have a problem with number of columns, target table has more columns than the source.
Here I can't specify column_names because I am passing table as dynamic.
Can you somebody please help?

You could look at the oracle data dictionary/metadata tables on both databases and generate the necessary sql for the insert. if i_table_name is your input.
select list_agg(column_name,',') columns -- comma separated list of columns
from (
select column_name
from all_tab_cols
where owner = 'XYZ'
and table_name = i_table_name
intersect
select column_name
from all_tab_cols#remote_database
where owner = 'XYZ' -- could be different (?)
and table_name = i_table_name
)
You could then use this string (comma separated list of colums) to do something along the lines of..
v_sql := 'insert into ' || i_table_name || ' ' || '(' ||
v_column_list || ')' ||
' select ' || '( ' || v_column_list ||
' from ' || i_table_name || '#' || remote_db_name;
dbms_output.put_line(v_sql);
execute immediate v_sql;
I haven't tested the code. Make sure you print the insert and confirm it matches your expectations.

use
INSERT INTO table1
SELECT a.*,
NULL,
NULL
FROM table2#dblink_

Related

Oracle get table names based on column value

I have table like this:
Table-1
Table-2
Table-3
Table-4
Table-5
each table is having many columns and one of the column name is employee_id.
Now, I want to write a query which will
1) return all the tables which is having this columns and
2) results should show the tables if the column is having values or empty values by passing employee_id.
e.g. show table name, column name from Table-1, Table-2,Table-3,... where employee_id='1234'.
If one of the table doesn't have this column, then it is not required to show.
I have verified with link, but it shows only table name and column name and not by passing some column values to it.
Also verified this, but here verifies from entire schema which I dont want to do it.
UPDATE:
Found a solution, but by using xmlsequence which is deprecated,
1)how do I make this code as xmltable?
2) If there are no values in the table, then output should have empty/null. or default as "YES" value
WITH char_cols AS
(SELECT /*+materialize */ table_name, column_name
FROM cols
WHERE data_type IN ('CHAR', 'VARCHAR2') and table_name in ('Table-1','Table-2','Table-3','Table-4','Table-5'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Employee_ID",
SUBSTR (table_name, 1, 14) "Table",
SUBSTR (column_name, 1, 14) "Column"
FROM char_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select "'
|| column_name
|| '" from "'
|| table_name
|| '" where upper("'
|| column_name
|| '") like upper(''%'
|| :val
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) t ORDER BY "Table"
/
This query can be done in one step using the (non-deprecated) XMLTABLE.
Sample Schema
--Table-1 and Table-2 match the criteria.
--Table-3 has the right column but not the right value.
--Table-4 does not have the right column.
create table "Table-1" as select '1234' employee_id from dual;
create table "Table-2" as select '1234' employee_id from dual;
create table "Table-3" as select '4321' employee_id from dual;
create table "Table-4" as select 1 id from dual;
Query
--All tables with the column EMPLOYEE_ID, and the number of rows where EMPLOYEE_ID = '1234'.
select table_name, total
from
(
--Get XML results of dynamic query on relevant tables and columns.
select
dbms_xmlgen.getXMLType(
(
--Create a SELECT statement on each table, UNION ALL'ed together.
select listagg(
'select '''||table_name||''' table_name, count(*) total
from "'||table_name||'" where employee_id = ''1234'''
,' union all'||chr(10)) within group (order by table_name) v_sql
from user_tab_columns
where column_name = 'EMPLOYEE_ID'
)
) xml
from dual
) x
cross join
--Convert the XML data to relational.
xmltable('/ROWSET/ROW'
passing x.xml
columns
table_name varchar2(128) path 'TABLE_NAME',
total number path 'TOTAL'
);
Results
TABLE_NAME TOTAL
---------- -----
Table-1 1
Table-2 1
Table-3 0
Just try to use code below.
Pay your attention that may be nessecery clarify scheme name in loop.
This code works for my local db.
set serveroutput on;
DECLARE
ex_query VARCHAR(300);
num NUMBER;
emp_id number;
BEGIN
emp_id := <put your value>;
FOR rec IN
(SELECT table_name
FROM all_tab_columns
WHERE column_name LIKE upper('employee_id')
)
LOOP
num :=0;
ex_query := 'select count(*) from ' || rec.table_name || ' where employee_id = ' || emp_id;
EXECUTE IMMEDIATE ex_query into num;
if (num>0) then
DBMS_OUTPUT.PUT_LINE(rec.table_name);
end if;
END LOOP;
END;
I tried with the xml thing, but I get an error I cannot solve. Something about a zero size result. How difficult is it to solve this instead of raising exception?! Ask Oracle.
Anyway.
What you can do is use the COLS table to know what table has the employee_id column.
1) what table from table TABLE_LIKE_THIS (I assume column with table names is C) has this column?
select *
from COLS, TABLE_LIKE_THIS t
where cols.table_name = t
and cols.column_name = 'EMPLOYEE_ID'
-- think Oracle metadata/ think upper case
2) Which one has the value you are looking for: write a little chunk of Dynamic PL/SQL with EXECUTE IMMEDIATE to count the tables matching above condition
declare
v_id varchar2(10) := 'JP1829'; -- value you are looking for
v_col varchar2(20) := 'EMPLOYEE_ID'; -- column
n_c number := 0;
begin
for x in (
select table_name
from all_tab_columns cols
, TABLE_LIKE_THIS t
where cols.table_name = t.c
and cols.column_name = v_col
) loop
EXECUTE IMMEDIATE
'select count(1) from '||x.table_name
||' where Nvl('||v_col||', ''##'') = ''' ||v_id||'''' -- adding quotes around string is a little specific
INTO n_c;
if n_c > 0 then
dbms_output.put_line(n_C|| ' in ' ||x.table_name||' has '||v_col||'='||v_id);
end if;
-- idem for null values
-- ... ||' where '||v_col||' is null '
-- or
-- ... ||' where Nvl('||v_col||', ''##'') = ''##'' '
end loop;
dbms_output.put_line('done.');
end;
/
Hope this helps

plsql List all table.column containing null values

I'd like to find all column of a set of table with null values in them.
I can find the table and column names
SELECT TABLE_NAME, COLUMN_NAME
FROM user_tab_cols
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
And also check if the are nulls
select count(*) from myTable where myColumn is null;
but how can I put this toghether to have as result
table_name column_name
myTable myColumn
myTable myCol2
myTable2 col4
An approach could be with some dynamic SQL, but this would require some PL/SQL code; the following only uses SQL to get the result you need:
select *
from (
select table_name,
column_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name || ' where ' || column_name || ' is null')),'/ROWSET/ROW/C')) as rowcount
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2')
)
where rowcount > 0
This could be an approach with dynamic SQL:
declare
type tableOfNumber is table of number;
type tableOfChar is table of varchar2(30);
--
vSQl varchar2(4000);
vListNumbers tableOfNumber;
vListTables tableOfChar;
vListColumns tableOfChar;
begin
select listagg( 'select ''' ||
table_name || ''' as table_name, ''' ||
column_name || ''' as column_name, count(*) as rowCount from ' ||
table_name ||
' where ' ||
column_name ||
' is null having count(*) > 0' ,
' UNION ALL '
) within group ( order by table_name, column_name)
into vSQL
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
--
dbms_output.put_line(vSQL);
/* whatever you may want to do with the query */
/* for example, fetch into some variables and print the result */
execute immediate vSQL
bulk collect into vListTables, vListColumns, vListNumbers;
--
if vListTables.count() > 0 then
for i in vListTables.first .. vListTables.last loop
dbms_output.put_line('Table ' || vListTables(i) ||
', column ' || vListColumns(i) ||
', number of nulls: ' || vListNumbers(i)
);
end loop;
end if;
end;
Here's a routine I wrote a while back to do exactly that. It will output the required DDL to make those columns that are nullable (but do not contain any nulls) not nullable.
https://connormcdonald.wordpress.com/2016/03/11/tightening-up-your-data-model/
It can do the task for a schema or a single table.

SQL insert query with values like JUM'A ABDUL FATTAH causing missing , error

I am trying to insert data into a table which is derived dynamically.
I am selecting data from MASTER_TABLE which has col1 and col2. Depending on the values in col1 and col2, I am inserting the data in table SLAVE_col1value_col2value.
my sql query looks like:
insert_query_str :='INSERT INTO SLAVE_'
|| Col1
|| '_'
|| Col2
|| ' VALUES ( '''
|| CUST_NAME
|| ''','''
|| APPLICATION_DATA
|| ''')';
EXECUTE IMMEDIATE insert_query_str;
However, name like JUM'A ABDUL FATTAH cause the insert query string to look like this
INSERT INTO SLAVE_XYZ_ABC VALUES ( 'JUM'A ABDUL FATTAH','APPSPECIFICDATA');
and I get a missing comma error on the line.
I know if I double quote the name then this error can be sorted out but when I try that it hardcodes the || too and I don't get the real name in the query.
I am using Oracle SQL 11g and Toad.
Thank you in advance
Use placeholders together with USING clause:
insert_query_str :='INSERT INTO SLAVE_'
|| Col1
|| '_'
|| Col2
|| ' VALUES ( :cust_name, :app_data )';
EXECUTE IMMEDIATE insert_query_str USING CUST_NAME, APPLICATION_DATA;

Get Aggregate Values of Each Column In a Table

I am new to Oracle; I want to be able to generate a report that gives me the aggregate values for each column on a table. I haven't found anything that does this automatically, so I created a table where I upload the columns names, then I am using the data in that table to build individual SQL Statements:
Here is my table:
CREATE TABLE CARIAS.TEST_CA_1
(
DATA_BASE_NAME VARCHAR2(100 BYTE),
SCHEMA_NAME VARCHAR2(100 BYTE),
TABLE_NAME VARCHAR2(100 BYTE),
FIELD_NAME VARCHAR2(20 BYTE)
);
INSERT INTO TEST_CA_1 (DATA_BASE_NAME, SCHEMA_NAME, TABLE_NAME, FIELD_NAME) VALUES ('FDWP', 'FDW', 'D_CLAIM', 'FIELD_CLAIM_OFFICE');
INSERT INTO TEST_CA_1 (DATA_BASE_NAME, SCHEMA_NAME, TABLE_NAME, FIELD_NAME) VALUES ('FDWP', 'FDW', 'D_CLAIM', 'CLAIM_SYMBOL_CODE');
INSERT INTO TEST_CA_1 (DATA_BASE_NAME, SCHEMA_NAME, TABLE_NAME, FIELD_NAME) VALUES ('FDWP', 'FDW', 'D_CLAIM', 'HANDLING_DIVISION');
Here is the query that builds the statements:
SELECT
FIELD_NAME,
SQL_STATEMENT
FROM
(
--Creates Count Statements
SELECT 'SELECT ' || FIELD_NAME ||', COUNT(*) AS CNT FROM ' || SCHEMA_NAME || '.' || TABLE_NAME ||' GROUP BY ' || FIELD_NAME || ';' AS SQL_STATEMENT, FIELD_NAME FROM TEST_CA_1
UNION ALL
--Creates MIN and MAX Values Statement
SELECT 'SELECT MIN(' || FIELD_NAME || ') AS MIN_VALUE, MAX(' || FIELD_NAME || ') AS MAX_VALUE FROM ' || SCHEMA_NAME || '.' || TABLE_NAME || ';' AS SQL_STATEMENT
,FIELD_NAME
FROM TEST_CA_1
)
ORDER BY FIELD_NAME;
Then I use those "built" statements to get what I want. Is there an easier way to do this?
A first approach could be this:
create or replace function exec_on_table_field(i_table in varchar2,
i_field in varchar2,
i_function in varchar2)
return clob is
l_result clob;
begin
execute immediate 'select to_clob(' || i_function || '(' || i_field ||
')) from ' || i_table
into l_result;
return l_result;
end;
call this function with
select u.TABLE_NAME, u.COLUMN_NAME,
to_char(exec_on_table_field(u.TABLE_NAME, u.COLUMN_NAME, 'min')),
to_char(exec_on_table_field(u.TABLE_NAME, u.COLUMN_NAME, 'max')) from
user_tab_cols u where u.TABLE_NAME like 'PS_CS%' and rownum < 5;
hope it helps
The answer provided by Frank Ockenfuss worked great. I also found some of the information I was looking for by using the following Oracle View:
SELECT *
FROM ALL_TAB_COL_STATISTICS U
WHERE U.TABLE_NAME LIKE 'MY_TABLE_NAME';

Select all Table/View Names with each table Row Count in Teredata

I have been stuck into a question.
The question is I want to get all Table name with their Row Count from Teradata.
I have this query which gives me all View Name from a specific Schema.
I ] SELECT TableName FROM dbc.tables WHERE tablekind='V' AND databasename='SCHEMA' order by TableName;
& I have this query which gives me row count for a specific Table/View in Schema.
II ] SELECT COUNT(*) as RowsNum FROM SCHEMA.TABLE_NAME;
Now can anyone tell me what to do to get the result from Query I (TableName) and put it into QUERY II (TABLE_NAME)
You help will be appreciated.
Thanks in advance,
Vrinda
This is a SP to collect row counts from all tables within a database, it's very basic, no error checking etc.
It shows a cursor and dynamic SQL using dbc.SysExecSQL or EXECUTE IMMEDIATE:
CREATE SET TABLE RowCounts
(
DatabaseName VARCHAR(30) CHARACTER SET LATIN NOT CASESPECIFIC,
TableName VARCHAR(30) CHARACTER SET LATIN NOT CASESPECIFIC,
RowCount BIGINT,
COllectTimeStamp TIMESTAMP(2))
PRIMARY INDEX ( DatabaseName ,TableName )
;
REPLACE PROCEDURE GetRowCounts(IN DBName VARCHAR(30))
BEGIN
DECLARE SqlTxt VARCHAR(500);
FOR cur AS
SELECT
TRIM(DatabaseName) AS DBName,
TRIM(TableName) AS TabName
FROM dbc.Tables
WHERE DatabaseName = :DBName
AND TableKind = 'T'
DO
SET SqlTxt =
'INSERT INTO RowCounts ' ||
'SELECT ' ||
'''' || cur.DBName || '''' || ',' ||
'''' || cur.TabName || '''' || ',' ||
'CAST(COUNT(*) AS BIGINT)' || ',' ||
'CURRENT_TIMESTAMP(2) ' ||
'FROM ' || cur.DBName ||
'.' || cur.TabName || ';';
--CALL dbc.sysexecsql(:SqlTxt);
EXECUTE IMMEDIATE sqlTxt;
END FOR;
END;
If you can't create a table or SP you might use a VOLATILE TABLE (as DrBailey suggested) and run the INSERTs returned by following query:
SELECT
'INSERT INTO RowCounts ' ||
'SELECT ' ||
'''' || DatabaseName || '''' || ',' ||
'''' || TableName || '''' || ',' ||
'CAST(COUNT(*) AS BIGINT)' || ',' ||
'CURRENT_TIMESTAMP(2) ' ||
'FROM ' || DatabaseName ||
'.' || TableName || ';'
FROM dbc.tablesV
WHERE tablekind='V'
AND databasename='schema'
ORDER BY TableName;
But a routine like this might already exist on your system, you might ask you DBA. If it dosn't have to be 100% accurate this info might also be extracted from collected statistics.
Use dnoeth's answer but instead use create "create volatile table" this will use your spool to create the table and will delete all data when your session is closed. You need no write access to use volatile tables.
Is there any way to find the row count of a table/view from system tables with out using count(). Count() took ages to run for big tables. dbc.statsv view only include tables as we are not able to collect the stat of a view.