pl/sql query to insert multiple rows using select statement - for-loop

I want to insert multiple rows into a table.
The query would be:
insert into temp(table_name,run_date,table_count)
select 'TABLE_A',sysdate,count(*) from A;
insert into temp(table_name,run_date,table_count)
select 'TABLE_B',sysdate,count(*) from B;
insert into temp(table_name,run_date,table_count)
select 'TABLE_C',sysdate,count(*) from C;
How do I write this in a loop using pl/sql?
Thanks,
Anju

For a variable list of tables, here's a script that reads the Oracle system table ALL_TABLES for a specified owner and inserts the counts into a temp table.
DECLARE
-- Define a cursor to get the list of tables you want counts for.
cursor c1 is
select table_name
from all_tables
where owner = 'YOUR_OWNER_HERE';
-- Dynamically created select.
stmt varchar2(200);
BEGIN
-- The cursor for loop implicitly opens and closes the cursor.
for table_rec in c1
loop
-- dynamically build the insert statement.
stmt := 'insert into temp(table_name,run_date,table_count) ';
stmt := stmt || 'select ''' || table_rec.table_name || ''','''|| sysdate||''','|| 'count(*) from ' || table_rec.table_name;
-- Execute the insert statement.
execute immediate(stmt);
end loop;
END;
commit;

Related

How many rows inserted into table by create table statement

Is there a way to count how many rows inserted into table by
Create table tbl1 as select * from tbl2;
statement which performed from PL\SQL function (execute immediate)?
When I'm using SQL%ROWCOUNT the result is 1.
Thanks.
You can try this. As mentioned in my comment you need to do it as followed. Remember that you need again to use Execute immediate else you get an issue that tab1 is undefined.
DECLARE
vsql VARCHAR2 (200);
cnt NUMBER;
BEGIN
vsql := 'create table tbl1 as select * from employee';
EXECUTE IMMEDIATE vsql;
vsql := 'select count(1) from tbl1';
EXECUTE IMMEDIATE vsql INTO cnt;
DBMS_OUTPUT.put_line (cnt);
END;
You can do one thing. You can first create an empty table tbl1 from tbl2. Then insert data using SELECT and then use- SQL%ROWCOUNT.
CREATE TABLE tbl1 AS SELECT * FROM tbl2 WHERE 1=2;
INSERT INTO tbl1 SELECT * FROM tbl2;
DBMS_OUTPUT.PUT_LINE ('No. of rows inserted in TBL2 from TBL1 = ' || SQL%ROWCOUNT);

PL SQL- Using Dynamic SQL to Generate Delete Statements

I want to create a stored procedure using PL SQL that allows me to find all tables that contain a specific column, and then delete records from those tables that have a specific value in that column.
For example, I want to find all tables that have the column "year" and then delete all records from all of those tables that have the year "2012"(this year will be a parameter that will be entered upon execution)
My attempt at this has been to create a stored procedure, use a cursor to get all of the tables that have this column of "year" and then loop through that cursor using Dynamic SQL which will generate my Delete Statements that I can execute.
CREATE OR REPLACE PROCEDURE year_deletion
(
p_year NUMBER --Input of the year for records to be deleted
)
IS
CURSOR c1 --Cursor that will find all tables that have the YEAR column
IS
SELECT owner, table_name
FROM all_tab_columns
WHERE column_name = 'YEAR'
AND owner = 'GTP';
BEGIN
FOR i IN c1 LOOP --Loop through all of the tables that the cursor found, generate a SQL statement for each table that will delete all of the records that have the year of p_year
EXECUTE IMMEDIATE ('SELECT * FROM' ||i.table_name|| 'WHERE YEAR = '||p_year||';');
END LOOP;
END;
Disclaimer: I am using a Select * From instead of a DELETE * From for testing purposes, I will change this when it this procedure executes correctly.
So far this stored procedure compiles correctly, but gives me an error during execution that a FROM keyword was expected but not found. Is this the best method to use for my purpose?
Is must be like this:
EXECUTE IMMEDIATE 'DELETE FROM ' ||i.table_name|| ' WHERE YEAR = :theYear' USING p_year;
Note the space after FROM and before WHERE.
You cannot simply replace DELETE by SELECT ... for testing because for SELECT you must have an INTO clause.
Your entire procedure can be like this
CREATE OR REPLACE PROCEDURE year_deletion(p_year IN NUMBER) IS
CURSOR c1 IS
SELECT owner, table_name
FROM all_tab_columns
WHERE column_name = 'YEAR'
AND owner = 'GTP';
res NUMBER;
BEGIN
FOR i IN c1 LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' ||i.table_name|| ' WHERE YEAR = :theYear' INTO res USING p_year;
DBMS_OUTPUT.PUT_LINE (res ||' rows will be deleted from table '||i.table_name );
EXECUTE IMMEDIATE
'DELETE FROM ' ||i.table_name|| ' WHERE YEAR = :theYear' USING p_year;
END LOOP;
END;
Hello you can try the below code. It will surely help you out.
CREATE OR REPLACE PROCEDURE year_deletion(
p_year IN NUMBER --Input of the year for records to be deleted
)
IS
BEGIN
FOR i IN (SELECT owner,
table_name
FROM all_tab_columns
WHERE column_name = 'YEAR'
AND owner = 'GTP')
LOOP --Loop through all of the tables that the cursor found, generate a SQL statement for each table that will delete all of the records that have the year of p_year
EXECUTE IMMEDIATE 'DELETE FROM ' ||i.table_name|| ' WHERE YEAR = '||p_year;
END LOOP;
END;

PL/SQL EXECUTE IMMEDIATE inside LOOP (procedure to truncate all tables in schema)

I need to create procedure which will delete all data from tables in one schema. I try something like that
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
sql_truncate VARCHAR2(50);
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
sql_truncate := 'TRUNCATE TABLE :text_string';
FOR table_name in c1
LOOP
EXECUTE IMMEDIATE sql_truncate USING table_name;
END LOOP;
END CLEAR_ALL;
But it gives me two errors which i cannot understand and fix.
Error(13,7): PL/SQL: Statement ignored
Error(13,44): PLS-00457: Statment must be type of SQL <-- (This error
i had to translate, cause i use University Oracle 11g base which have
Polish lang)
Why not just generate the statement and call it, like this?
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
vs_statement VARCHAR2(100);
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
FOR table_rec in c1
LOOP
vs_statement := 'TRUNCATE TABLE ' || table_rec.table_name;
EXECUTE IMMEDIATE vs_statement;
END LOOP;
END CLEAR_ALL;
You can't use bind variables (i.e. your using clause) as a placeholder for an object name. If you could, you wouldn't need to use dynamic SQL in the first place. You'll have to use concatenation or substitution instead:
CREATE OR REPLACE PROCEDURE CLEAR_ALL
IS
sql_truncate CONSTANT VARCHAR2(50) := 'TRUNCATE TABLE [text_string]';
cursor c1 is
SELECT table_name
FROM all_tables
WHERE owner = 'KARCHUDZ_S';
BEGIN
FOR row in c1
LOOP
EXECUTE IMMEDIATE replace(sql_truncate, '[text_string]', row.table_name);
END LOOP;
END CLEAR_ALL;

For loop with Table name in Stored Procedures

I am working on Oracle stored procedures.
My requirement is below
IF variable1 := 'true"
THEN
tableName=abr
ELSE
tableName=mvr
END IF;
FOR i IN (select unique(row1) as sc from tableName t where t.row2 = 'name') LOOP
BEGIN
-- required Logic
END
END LOOP;
But here I am not able to pass the table name in tableName parameter. How to do it?
You'll need to use Execute Immediate - it's designed for operations that aren't known until run time.
For normal operations, Oracle must know the tables and columns at compile time. You can't do SELECT * FROM tableName because it has no idea what tableName is and therefore it can't be compiled correctly.
Instead, you can do EXECUTE IMMEDIATE 'SELECT * FROM ' || tableName;
You can select your results INTO a variable, loop the result set, or BULK COLLECT into a structure and then iterate that.
For a simple select into, you can do this:
EXECUTE IMMEDIATE 'SELECT COL1, COL2 FROM ' || tableName INTO V_COL1, V_COL2
V_COL1 & V_COL2 are just local variables, tableName is a string representing your table name, and COL2 and COL2 are columns in the table you're selecting from. You can use the likes of ALL_TAB_COLUMNS to get the structure of a table dynamically.
Here is an example from Oracle docs:
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
http://docs.oracle.com/cd/B12037_01/appdev.101/b10795/adfns_dy.htm
You are going to have to build a for loop for each table then use your logic to determine which loop you will execute.

using pl/sql to update sequences

We have a sql script to update a set of sequences after seed data populated our tables. The code below would not work:
declare
cursor c1 is
select
'select nvl(max(id),0) from '||uc.table_name sql_text,
uc.table_name||'_SEQ' sequence_name
from
user_constraints uc,
user_cons_columns ucc
where uc.constraint_type='P'
and ucc.constraint_name = uc.constraint_name
and ucc.column_name='ID'
and uc.owner='ME';
alter_sequence_text varchar2(1024);
TYPE generic_cursor_type IS REF CURSOR;
max_id number;
c2 generic_cursor_type;
begin
for r1 in c1 loop
open c2 for r1.sql_text;
fetch c2 into max_id;
close c2;
if( max_id != 0 ) then
dbms_output.put_line( 'seq name = '||r1.sequence_name );
execute immediate 'alter sequence '||r1.sequence_name||' increment by '||to_char(max_id);
dbms_output.put_line( 'max_id = '||to_char(max_id) );
execute immediate 'select '||r1.sequence_name||'.nextval from dual';
dbms_output.put_line( 'sequence value = '||to_char(next_id) );
execute immediate 'alter sequence '||r1.sequence_name||' increment by 1';
dbms_output.put_line( 'sequence: '||r1.sequence_name||' is at '||to_char(max_id+1) );
end if;
end loop;
end;
After searching I found a reference that stated I needed to change the line:
execute immediate 'select '||r1.sequence_name||'.nextval from dual'
and add 'into next_id;' (of course declaring next_id appropriately) so the result would be:
execute immediate 'select '||r1.sequence_name||'.nextval from dual into next_id;
I've only dealt lightly with pl/sql and sql in general and am interested to know why this change was necessary to make the script work correctly.
Thanks.
When you are using select inside PL/SQL block you have to place data returned by that select statement somewhere. So you have to declare a variable of appropriate data type and use select into clause to put data select returns into that variable even if select statement is executed by execute immediate statement.
Examples
declare
x number;
begin
select count(*)
into x
from all_objects;
end;
declare
x number;
begin
execute immediate 'select count(*)from all_objects' into x;
end;
So your execute immediate statement would be
execute immediate 'select '||sequence_name||'.nextval from dual' into newseqval;
If you are using Oracle 11g onward you can assign sequence's value directly to a variable, there is no need of using select into clause.
declare
x number;
begin
x := Sequence_Name.nextval;
end;
select seq_name.nextval from dual implies the implicit cursor creation and the results of the cursor should be fetched somewhere so you need fetch it into any externally declared bind variable.

Resources