Using variables in oracle as table names for insert command oracle - sql

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;

Related

How to create a view in a FOR loop in Oracle SQL

What I'm trying to do is create views based off a condition between two tables, and I want it to go through all tables that meet this condition.
I've been doing some research and I found that cursors would be helpful for this sort of thing, but I've been running into a "cursor out of scope" at line 15.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(all_syn.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM all_syn.SYNONYM_NAME,
our_tables.TABLE_NAME
WHERE our_tables.TABLE_NAME = ' || LTRIM(all_syn.SYNONYM_NAME, 'S_');
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;
The reason I am doing this is because my company has tables that aren't directly connected to a certain 3rd party DB link, so they had me change the table names by putting a 1 at the end of the affected tables, creating synonyms for these tables with the DB link, and then make views of these synonyms with the original table name so that they now have the DB link and act as the original table so that we don't have to change any code. I have to join the synonym tables with the changed tables, because we added some attributes that the 3rd party tables don't have.
If anyone has any suggestions or advice, it would be greatly appreciated! I'm new to using dynamic sql and PL/SQL, so bear with me please.
EDIT:
So I've improved my code, and I feel like I'm getting closer to my desired results, however I'm getting this weird error:
line 28, column 52:
PLS-00357: Table,View Or Sequence reference 'ALL_TABLES.TABLE_NAME' not allowed in this context
Which doesn't make sense to me as I'm declaring it in the query.
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ' AS
SELECT itemnum AS item_num,
keywordnum AS key_word_num,
keysetnum AS key_set_num,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS,
FROM ( SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE ' || '''AG!_%1''' || ' ESCAPE ' || '''!''' || '
AND ' || RTRIM(ALL_TABLES.TABLE_NAME, '1') ||' = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || ') our_tables,
' || v_rec.SYNONYM_NAME;
-- EXECUTE IMMEDIATE query_str;
END IF;
dbms_output.put_line(query_str);
END LOOP;
END;
You cannot reference cursor like that. Move the query_str creation inside the FOR LOOP and reference the record variable.
EDIT: I've tried to fix the FROM/WHERE clause, but you might be missing a join condition there.
DECLARE
query_str VARCHAR2(32000);
CURSOR all_syn IS
SELECT SYNONYM_NAME, TABLE_NAME
FROM ALL_SYNONYMS
WHERE SYNONYM_NAME LIKE 'S!_AG!_%' ESCAPE '!';
CURSOR our_tables IS
SELECT TABLE_NAME
FROM ALL_TABLES
WHERE TABLE_NAME LIKE 'AG!_%1' ESCAPE '!';
BEGIN
FOR v_rec IN all_syn LOOP
IF (v_rec.TABLE_NAME LIKE 'KEYXITEM%') THEN
query_str := 'CREATE OR REPLACE VIEW ' || LTRIM(v_rec.SYNONYM_NAME, 'S_') || 'AS
SELECT TO_CHAR(itemnum) itemnum,
TO_CHAR(keywordnum) keywordnum,
TO_CHAR(keysetnum) keysetnum,
MOD_BY_EMPLOYEE,
MOD_BY_PROCESS,
MOD_DATE_EMPLOYEE,
MOD_DATE_PROCESS
FROM ' || v_rec.SYNONYM_NAME || ',
' || v_rec.TABLE_NAME || '
WHERE ' || v_rec.TABLE_NAME = ' || LTRIM(v_rec.SYNONYM_NAME, 'S_');
EXECUTE IMMEDIATE query_str;
END IF;
END LOOP;
END;

Call stored procedure for each Row with/without using a cursor

I want to a run a stored procedure for almost 1000 records (P_SHIPMENT_GID) in one go and below is the pseudo code.
DECLARE
P_SHIPMENT_GID VARCHAR2(200);
BEGIN
P_SHIPMENT_GID := NULL;
ULE_PKG_UNPLANNED_ICT_CALC.UNPLANNED_ICT_CALC(
P_SHIPMENT_GID => P_SHIPMENT_GID
);
END;
How can I achieve this with or without using cursors?
It is not all that clear what you want to do (where are the 1000 records from?) but here is a "pattern" I am pretty sure you can use :
BEGIN
FOR i IN (SELECT table_name, status FROM user_tables) LOOP
dbms_output.put_line('name : ' || i.table_name ||
' status : ' || i.status);
END LOOP;
END;
This creates a loop on an implicit cursor and allows you to use the returned rows/column in a readable way.
You can write this anonymous block for your requirement. Although its not clear from where you are storing your SHIPMENT_GID values which you wanted to pass to your procedure/pkg.
BEGIN
FOR rec IN ( --Assuming your shipmentid are stored in a table
SELECT SHIPMENT_GID
FROM Your_TABLE)
LOOP
ULE_PKG_UNPLANNED_ICT_CALC.UNPLANNED_ICT_CALC (
P_SHIPMENT_GID => rec.SHIPMENT_GID);
END LOOP;
END;

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;

create view and alter in all tables

I am attempting to create a logic within the procedure using cursors to create database views for all the tables and create a new column called HISTORY_DATE for all tables in the schema. I need help building the logic below.
create or replace PROCEDURE ALTER_TABLES(
RC OUT INT
,MSG OUT VARCHAR)
AS
BEGIN
DECLARE
CURSOR TBL_CUR IS
SELECT TABLE_NAME FROM USER_TABLES;
TBL_REC TBL_CUR%ROWTYPE;
SQL_STMT VARCHAR(2000);
BEGIN
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO TBL_REC;
EXIT WHEN TBL_CUR%NOTFOUND;
PRINT_DETAILS(TBL_REC.TABLE_NAME);
SQL_STMT:= 'ALTER TABLE '
|| TBL_REC.TABLE_NAME
|| ' ADD HISTORY_DATE DATE'
|| ' AND'
|| ' CREATE OR REPLACE VIEW all_tbl AS'
|| ' SELECT *'
|| ' FROM USER_TABLES'
;
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
END LOOP;
CLOSE TBL_CUR;
rollback;
END;
END;
You only want to create the view once; and you cannot do two things at once as Gordon Linoff mentioned. So take the view creation outside of the loop; something like this (untested):
create or replace PROCEDURE ALTER_TABLES(RC OUT INT
,MSG OUT VARCHAR)
AS
BEGIN
DECLARE
CURSOR TBL_CUR IS
SELECT TABLE_NAME FROM USER_TABLES;
TBL_REC TBL_CUR%ROWTYPE;
SQL_STMT VARCHAR(2000);
BEGIN
SQL_STMT := 'CREATE OR REPLACE VIEW all_tbl AS'
|| ' SELECT *'
|| ' FROM USER_TABLES';
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
OPEN TBL_CUR;
LOOP
FETCH TBL_CUR INTO TBL_REC;
EXIT WHEN TBL_CUR%NOTFOUND;
PRINT_DETAILS(TBL_REC.TABLE_NAME);
SQL_STMT:= 'ALTER TABLE '
|| TBL_REC.TABLE_NAME
|| ' ADD (HISTORY_DATE DATE)'
;
PRINT_DETAILS(SQL_STMT);
EXECUTE IMMEDIATE SQL_STMT;
END LOOP;
CLOSE TBL_CUR;
--rollback;
END;
Now, I don't see a need to create a view all_tbl as all it is, is a copy of the view USER_TABLES -- so just use USER_TABLES -- but I left it's creation there so if you need only certain columns from USER_TABLES or certain rows, you know where to place that.

writing a generic procedure in oracle

i want to write procedure which accents name of 2 tables as arguments and then compare the number or rows of the 2.
Also i want to each field of the 2 columns.The row which has a missmatch shold be
moved to another error table.
Can anyone give a PL/SQL procedure for doing this.
I want to achive this in oracle 9
Pablos example wont work, the idea is right though.
Something like this do it.
create or replace PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_r1 number;
v_r2 number;
v_sql1 varchar2(200);
v_sql2 varchar2(200);
BEGIN
v_sql1 := 'select count(1) from ' || T1;
v_sql2 := 'select count(1) from ' || T2;
EXECUTE IMMEDIATE v_sql1 into v_r1;
EXECUTE IMMEDIATE v_sql2 into v_r2;
dbms_output.put_line(T1 || ' count = ' || v_r1 || ', ' || T2 || ' count = ' || v_r2);
END;
DBMS_SQL is your friend for such operations.
You can use dynamic sql in PL/SQL. EXECUTE IMMEDIATE is your friend.
So, if you take two table names and trying to compare their row counts, you would do something like:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2(200), T2 IN VARCHAR2(200)) AS
v_cursor integer;
v_r1 integer;
v_r2 integer;
v_sql varchar2(200);
BEGIN
v_sql := "select count(1) into :1 from " || T1;
EXECUTE IMMEDIATE v_sql USING v_r1;
v_sql := "select count(1) into :1 from " || T2;
EXECUTE IMMEDIATE v_sql USING v_r2;
-- compare v_r1 and v_r2
END;
Not 100% sure about PL/SQL syntax. It's been a while since the last time I coded in great PL/SQL!
You can achieve same results with similar approach using DBMS_SQL. Syntax is a little bit more complicated though.
I am just posting here to note that all answers gravitate around dynamic SQL, and do not turn the attention to the implied problems using it.
Consider passing the following string as first or second parameter:
dual where rownum = 0 intersect
SELECT 0 FROM dual WHERE exists (select 1 from user_sys_privs where UPPER(privilege) = 'DROP USER')
I'll leave it to that.
To answer your question - Oracle actually stores these values in the data dictionary, so if you have access to it:
CREATE OR REPLACE PROCEDURE COMPARE_ROW_COUNT(T1 IN VARCHAR2, T2 IN VARCHAR2) AS
v_text varchar2(1000);
BEGIN
select listagg(owner || ' ' || table_name || ' count = ' || num_rows, ',')
into v_text
from all_tables --user, all or dba tables depends on requirements
where table_name in (T1, T2);
dbms_output.put_line(v_text);
exception
when others then raise; -- Put anything here, as long as you have an exception block
END COMPARE_ROW_COUNT;