I am looking for dynamic insert statement in oracle - sql

I am looking for dynamic insert statement in oracle like below for each partition (which need trim for every CHAR datatype rest all columns will be as is)
insert into SCHEMA.TARGET_TABLE_NAME
select
M_NB,
trim(M_INSTRUMENT),
trim(M_H_FLOWTYPE),
M_F_EXDIVD,
M_SC_FC_AC,
M_SC_FC_UC,
M_G_BRK,
M_F_LEG,
M_F_DTEEVENT,
trim(M_F_TYPELAB2),
trim(M_TYPE_UFC),
trim(M_USER_CUR),
M_REF_DATA,
M_CNT_ORG,
M_F_AMOUNT,
M_F_AMOUNTF,
trim(M_F_ANALYTIC),
trim(M_F_TYPELAB0),
trim(M_F_TYPELAB1),
trim(M_F_TYPELAB3),
trim(M_F_TYPELAB4),
M_H_F_CCNOM,
trim(M_F_TYPE),
M_F_VALUE,
M_F_REF,
trim(M_F_OBSCOM),
trim(M_ACC_CUR),
trim(M_AMD_STS),
trim(M_F_FEECOD),
trim(M_TP_CNTRP),
M_TP_DTESYS,
trim(M_TP_PFOLIO),
trim(M_TP_RTFV0),
M_TRN_GTYPE,
M_F_CCFRMCD1,
M_F_CCFRMCD2,
M_F_CCFXGDT0,
M_F_CCRATE,
trim(M_F_CTP),
trim(M_F_CURRENCY),
trim(M_F_DESTLB),
M_MX_REF_JOB,
M_TP_RTCCP02,
M_TP_RTCCP12
from SCHEMA.SOURCE_TABLE_NAME partition (PARTITION_NAME);

Find the partition names and then build the statement using string concatenation and use EXECUTE IMMEDIATE:
DECLARE
partition_names SYS.ODCIVARCHAR2LIST;
v_sql VARCHAR2(4000) := 'insert into SCHEMA.TARGET_TABLE_NAME '
|| 'select col1, TRIM(col2), col3, TRIM(col4) '
|| 'from SCHEMA.SOURCE_TABLE_NAME';
BEGIN
SELECT partition_name
BULK COLLECT INTO partition_names
FROM ALL_TAB_PARTITIONS
WHERE table_owner = 'SCHEMA'
AND table_name = 'SOURCE_TABLE_NAME'
ORDER BY partition_position;
FOR i IN 1 .. partition_names.COUNT LOOP
EXECUTE IMMEDIATE v_sql || ' PARTITION ("' || partition_names(i) || '")';
END LOOP;
END;
/

Related

Using variables in oracle as table names for insert command oracle

Trying to create a procedure that will either insert or update a certain table that its name is stored in another table with more info.
CREATE OR REPLACE PROCEDURE LIMPAR_TAB_proc IS
--stmt VARCHAR2(1000);
n_tab sii_bck_cfg_tab.nome_tab%type;
prefix sii_bck_cfg_tab.pref_tab_bck%type;
max_reg sii_bck_cfg_tab.max_reg_bck%type;
id_fk sii_bck_cfg_tab.id_bck_cfg_tab%type;
n_tab2 sii_bck_tab.nome_tab%type;
testes VARCHAR2(500);
CURSOR c1 IS
SELECT ID_BCK_CFG_TAB,Nome_tab, pref_tab_bck, max_reg_bck FROM
sii_bck_cfg_tab WHERE desativado_em IS NULL OR desativado_em<=SYSDATE AND
n_dias_reten>0 ORDER BY criado_em;
CURSOR c2 IS
SELECT sii_bck_tab.ID_BCK_CFG_TAB , sii_bck_tab.nome_tab from
sii_bck_tab,sii_bck_cfg_tab WHERE
sii_bck_cfg_tab.id_bck_cfg_tab=sii_bck_tab.id_bck_cfg_tab and dt_fecho is
NULL ;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO id_fk,n_tab,prefix,max_reg;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Nome Tabela = ' || id_fk ||' '|| n_tab ||' '|| prefix
||' '|| max_reg);
OPEN c2;
LOOP
FETCH c2 INTO id_fk, n_tab2;
EXIT WHEN c2%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('chave aqui = ' || id_fk || n_tab2);
IF c2%FOUND THEN
testes:= 'INSERT INTO ' || n_tab2 || 'select * from ' || n_tab;
EXECUTE IMMEDIATE testes;
END IF;
END LOOP;
CLOSE c2;
END LOOP;
CLOSE c1;
so i will try to explain my final objective, i want to go through my cursor1 and when i find a table that passes through the verification then i go into my cursor2. During my second loop i will want to verify if there is a table associated with a table on my cursor1 (not implemented ), then if i find one that is not associated i will need to create one with the same fields as the original(which is why im trying to save the table names in a variable). In case it exists and its dt_fim(date end) is null then i will need to insert all the data from the table from cursor1 (n_tab) into the table found on cursor2(n_tab2).
I will try to explain any doubts further, its still confusing to me, just getting started.
Thank you for any advice/help.
This is the right syntax, but not recommended for such a simple operation
testes:= 'INSERT INTO ' || n_tab2 ||' SELECT * FROM ' || n_tab;
EXECUTE IMMEDIATE testes;
Because It is preferable(and safer) to explicitly specify the column names in an insert, for which you need extra blocks if you want to do it dynamically.
INSERT INTO tab2(col1,col2,col3) SELECT col1,col2,col3 FROM tab;
By the way, any reason why you've put table names in variable instead of doing a direct insert?
Try this below block to pass tablename as variables:
declare
table_1 varchar2(10):='N_tab';
table_2 varchar2(10):='N_tab2';
test varchar2(1000);
begin
test:= 'INSERT all into ' || table_2 || ' SELECT * FROM ' ||table_1;
EXECUTE IMMEDIATE test;
dbms_output.put_line (test);
end;

How to Refer to a Column by ID or Index Number

In Oracle PL/SQL, I have run a query and am trying to read through each column for each row one by one so I can concatenate them together with a delimiter (hard format requirement). The script is used on multiple tables of varying sizes, so the number of columns is not known in advance. I used
SELECT COUNT(column_name) INTO NumColumns FROM all_tabs_cols
WHERE table_name = Table_Array(i);
where Table_Array has already been defined. This is in the middle of a for loop and has successfully gotten me a total number of columns. Table_Cursor is a SELECT * statement. After this I am trying to do something like
FOR j IN 0..NumColumns-1 LOOP
FETCH TABLE_CURSOR.column(j) INTO DataValue;
DBMS_OUTPUT.PUT(DataValue || '/');
END LOOP
The above is pseudo code. It illustrates the concept I am after. I do not know PL/SQL well enough to know how to get a value like this out of a row. I am also worried about accidentally advancing the cursor while doing this. How can I accomplish this task?
You must use some form of dynamic SQL. Here is a quick example:
It builds the SQL statement that will select the '/' separated columns from the table you want. Then it uses dynamic SQL to run that SQL statement.
DECLARE
p_table_name VARCHAR2(30) := 'DBA_OBJECTS';
l_sql VARCHAR2(32000);
TYPE varchar2tab IS TABLE OF VARCHAR2(32000);
l_array varchar2tab;
BEGIN
SELECT 'SELECT ' || listagg(column_name,' ||''/''||') within group ( order by column_id ) || ' FROM ' || owner || '.' || table_name || ' WHERE ROWNUM <= 100'
INTO l_sql
FROM dba_tab_columns
where table_Name = 'DBA_OBJECTS'
group by owner, table_Name;
EXECUTE IMMEDIATE l_sql BULK COLLECT INTO l_array;
FOR i in l_array.first .. l_array.last LOOP
dbms_output.put_line(l_array(i));
END LOOP;
END;
This is how your code should look:
SELECT F1 || ', ' || F2 || ', ' || ... || ', ' || FN
FROM TABLE
NO LOOPS
Here is how you can generate code that does not use loops.
Note, if you want you can take out the where statement and generate the code for the whole database.
Test with just one table first.
SELECT 'SELECT '|| LISTAGG(COLUMN_NAME, ' || '', '' || ') || ' FROM '||TABLE_NAME as sql_stm
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME='tablename'
GROUP BY TABLE_NAME;

Alter every table of a schema that has a name like 'something'?

I'd like to know if it is possible to alter every single table in a schema that contains a column name like 'something' in Oracle DB.
You can use a loop to iterate over USER_TAB_COLUMNS and generate the SQL statement:
declare
l_SQL varchar2(4000);
begin
for cur in (
select table_name, column_name
from user_tab_columns utc
where upper(utc.column_name) like '%SOMETHING%')
loop
l_SQL := 'alter table ' || cur.table_name || ' drop column ' || cur.column_name;
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;
Yes this is possible. You have to dynamically create the DDL or DML and execute immediate out of a PL/SQL routine. With "alter" do you mean change the content of the tables columns or do you mean change the columns properties?
EDIT:
You can use Frank's Routine but for a column modify you do this.
l_SQL := 'alter table ' || cur.table_name ||
' modify (' || cur.column_name || ' varchar2(50)); ';
I agree with Frank to not blindly modify the columns, use the dbms output as a generated script.
EDIT2:
There is one more thing I realized. Table user_tab_columns gives you also columns of views. You could exclude them by joining with user_tables:
set serveroutput on
declare
l_SQL varchar2(4000);
begin
for cur in (
select utc.table_name, utc.column_name
from user_tab_columns utc
join user_tables ut on (UT.TABLE_NAME = utc.table_name)
where upper(utc.column_name) like '%SO')
loop
l_SQL := 'alter table ' || cur.table_name || ' modify (' || cur.column_name || ' varchar2(50)); ';
dbms_output.put_line(l_SQL);
-- execute immediate l_SQL; -- UNCOMMENT TO RUN; DO NOT DO THIS IN PRODUCTION!
end loop;
end;

SQL Select Like Column Name from table

The goal is to create a Oracle Function that is capable of query column name off a token provided by the user as to create a function with such capabilities
select cols_like('%e%') from table
This is the point I am currently at
CREATE OR REPLACE Function COLS_LIKE
(v_search in VARCHAR2, v_table in VARCHAR2)
RETURN VARCHAR
IS
TYPE r_cursor IS REF CURSOR;
c_emp r_cursor;
crs_cols VARCHAR(255);
column_list VARCHAR(1000);
BEGIN
OPEN c_emp FOR
'select COLUMN_NAME from cols
where TABLE_NAME = ''' || v_table || '''
and column_name like ''' || v_search || '''';
LOOP
FETCH c_emp INTO crs_cols;
EXIT WHEN c_emp%NOTFOUND;
if column_list IS NULL THEN
column_list := crs_cols;
else
column_list := column_list || ', ' || crs_cols;
end if;
END LOOP;
RETURN column_list;
END;
Where you call the function such as this
Declare
tests VARCHAR(100);
sql_stmt VARCHAR2(200);
begin
tests := COLS_LIKE('%E%', 'table');
DBMS_OUTPUT.PUT_LINE(tests);
-- OR
sql_stmt := 'select ' || COLS_LIKE('%E%', 'table') || ' from table';
DBMS_OUTPUT.PUT_LINE(sql_stmt);
end;
The end goal would be something such as this
select COLS_LIKE('%E%', 'table') from table;
What modifications can I make to my function or how I am calling to so that this function can be applied correctly.
Why you'd want to do such a thing I've no idea but you could return an open cursor to PL/SQL fairly easily:
create or replace function cols_like (
PTable in varchar2
, PColumn in varchar2
) return sys_refcursor
l_cols varchar2(32767);
c_curs sys_refcursor;
begin
select listagg(column_name, ', ') within group (order by column_id)
into l_cols
from user_tab_cols
where table_name = upper(Ptable)
and column_name like '%' || upper(PColumn) || '%'
;
open c_curs for '
select ' || l_cols || '
from ' || Ptable;
return c_curs;
end;
/
Returning this to a standard SQL statement will be a lot more difficult, this is because in selecting this function you're only selecting one column's worth of data. You want to be able to select N columns, which means you need to start returning nested tables that have been dynamically created.
I'm sure it's possible; but, before you get anywhere close to starting to attempt to do this think about why you're doing it. Ask a question where you don't state your end goal but where you state what your actual problem is. Chances are there's a lot simpler solution.
I was also having same problem and found this, and it is working for me. I was just making comparison of mobile numbers from two tables and in one of the tables some numbers have 0 in start and some don't. Finally got solution and just added % in start:
a.number was having numbers, in some of them starting 0 was missing. and b.number was accurate.
b.number like CONCAT('%',a.number)

generic stored procedure in oracle

I want to write a PLSQL stored procedure that accepts a table name as argument. This table is source table. Now inside my procedure i want to manipulate the fields of that table.
EX: I want to insert the records of this source table into another target table whose name is XYZ_<source table name>. The column names for source and target tables are the same. But there may be some extra fields in target table. How do i do it? The order of column names is not same.
You will have to build the INSERT statement dynamically.
create or replace procedure gen_insert
(p_src_table in user_tables.table_name%type
, p_no_of_rows out pls_integer)
is
col_str varchar2(16000);
begin
for rec in ( select column_name
, column_id
from user_tab_columns
where table_name = p_src_table
order by column_id )
loop
if rec.column_id != 1 then
col_str := col_str || ',' || rec.column_name;
else
col_str := rec.column_name;
end if:
end loop;
execute immediate 'insert into xyz_' || p_src_table || '('
|| col_str || ')'
|| ' select ' || col_str
|| ' from ' || p_src_table;
p_no_of_rows := sql%rowcount;
end;
/
Obviously you may want to include some error handling and other improvements.
edit
Having edited your question I see you have a special requirement for naming the target table which was obscured by the SO formatting.
You can do this using Dynamic SQL. Here's a link with basic info on Oracle Dynamic SQL