how to make a loop for all columns of a table? - oracle

I have a table EMPLOYEES, i need to make a loop, on every step of the loop I use dbms_output.put_line(column_name)
How can I make this?

Just found this solution
begin
for rec in (SELECT column_name
FROM all_tab_columns
WHERE owner = 'HR'
AND table_name = 'EMPLOYEES')
loop
dbms_output.put_line(rec.column_name);
end loop;
end;

Try to use cursor as below:
set serveroutput on;
declare
var_col1 varchar2(30) ;
num_col2 number ;
CURSOR cur
is
select var_col ,num_col
from table
where <conditions>;
begin
open cur;
fetch cur into var_col1,num_col2;
LOOP
fetch cur into var_col1,num_col2;
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line( var_col1||' '||num_col2);
END LOOP;
close cur;
end;
here is another solution:
set serveroutput on;
begin
FOR cur in ( select var_col ,num_col from table where <conditions>)
LOOP
dbms_output.put_line(cur.var_col||' = '||cur.num_col);
END LOOP;
end;

Of course You can rewrite this SELECT example with WHERE after table_name. I dont compiled this code but I thinks its OK. Change ID_Employee and Name for your atributes
CREATE OR REPLACE PROCEDURE AAA_TEST
IS
BEGIN
DECLARE
v_id NUMBER :=0;
v_name VARCHAR2(25);
CURSOR cursorMails IS
SELECT Id_Employee,Name FROM EMPLOYEES;
BEGIN
OPEN cursorMails;
LOOP
FETCH cursorMails INTO v_id, v_name;
DBMS_OUTPUT.PUT_LINE('Number of Employee: ' || v_id || ' Name Employee: ' || v_name);
EXIT WHEN cursorMails%NOTFOUND;
END LOOP;
CLOSE cursorMails;
END;
END;
/

Related

Oracle PL equivalent of foreach-loop

I need to loop over about 10 strings, which are all known to me in advance -- I don't need to SELECT them from anywhere.
I attempted:
BEGIN
FOR tab IN ('one', 'two', 'three')
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || tab;
END LOOP;
END;
But that does not work (LOOP is unexpected)...
Is there a way to do this?
declare
type t_strings is table of varchar2(100);
strings t_strings:=t_strings('one','two','three');
begin
for i in 1..strings.count loop
dbms_output.put_line(strings(i));
end loop;
end;
/
Results:
one
two
three
Or you can use your own short-cut functions:
http://orasql.org/2017/10/02/plsql-functions-iterate-and-keys-for-associative-arrays/
Btw, Oracle 21 has some new features for FOR-LOOP:
1.1.1 PL/SQL Extended Iterators
FOR LOOP Iteration Enhancements in Oracle Database 21c
So it can be rewritten as:
declare
type t_strings is table of varchar2(100);
begin
for str in values of t_strings('one','two','three') loop
dbms_output.put_line(str);
end loop;
end;
/
"almost real" foreach loop:
declare
type tabList is table of varchar2(32);
tabs tabList := tabList ('one', 'two', 'three');
curr varchar2(32) := tabs.first;
begin
<<foreach>> loop exit foreach when curr is null;
execute immediate 'create table '||tabs(curr)||' (col char(1))';
execute immediate 'drop table '||tabs(curr)||' purge';
curr := tabs.next (curr);
end loop;
end;
/
PL/SQL procedure successfully completed.
begin
for tab_rec in ( select 'abc' val from dual
union
select '123' val from dual )
loop
EXECUTE IMMEDIATE 'DROP TABLE ' || tab_rec.val;
end loop;
end;

Use Input Parameter of procedure to declare %Rowtype record

I am trying to insert the data into a table using Bulk Collect.
Here is the code:
create or replace procedure insert_via_bulk_collect authid current_user
as
lc_status number;
cursor lc_old_tb_data is select * from old_table_name;
type lc_old_tb_type is table of old_table_name%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
forall i in 1..lc_old_tb_row.count
insert into new_table_name values lc_old_tb_row(i);
commit;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end insert_via_bulk_collect;
It's working for me. But I want to pass the table name dynamically.
like
insert_via_bulk_collect(new_tb_nm varchar2(30), old_tb_name varchar2(30))
But I am not able to use those variables in cursor and declaring the record of OLD_TABLE%rowtype.
Is there a way to do this?
Try below code
CREATE OR REPLACE PROCEDURE INSERT_VIA_BULK_COLLECT
(NEW_TABLE_NAME varchar2, OLD_TABLE_NAME varchar2)
as
l_str varchar2(4000);
begin
l_str := 'declare
CURSOR lc_old_tb_data IS SELECT * FROM ' || OLD_TABLE_NAME || ';
TYPE lc_old_tb_type IS TABLE OF '|| OLD_TABLE_NAME || '%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
Open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
FORALL i IN 1..lc_old_tb_row.count
INSERT INTO ' || NEW_TABLE_NAME || ' VALUES lc_old_tb_row(i);
COMMIT;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end; ';
dbms_output.put_line (l_str);
EXECUTE IMMEDIATE l_str;
end insert_via_bulk_collect;
You can add Exception handing. Also it is recommended to add limit for fetching of data as below fetch lc_old_tb_data bulk collect into lc_old_tb_row l_size; you can put l_size value as per your requirement.
As mentioned in comments, it is better to use direct insert ... select .... In your case, you can combine this way with execute immediate:
create or replace procedure insert_via_bulk_collect(new_tb_nm varchar2, old_tb_name varchar2) is
begin
execute immediate 'insert into ' || new_tb_nm ||
'(<columns list>) select <columns list> from ' || old_tb_nm;
end;

Where clause in Dynamic sql

I am trying to get all the tables where bank_id is 01.
I have written the following block
DECLARE
cursor cBankId is
select owner||'.'||table_name from all_tab_columns where column_name = 'BANK_ID';
v_table all_tab_columns.table_name%TYPE;
vcount varchar2(50);
BEGIN
open cBankId;
loop
fetch cBankId into v_table;
exit when cBankId%notfound;
execute immediate 'select count(*) from ' || v_table into vcount || ' where bank_id = 01';
IF vcount > 0 THEN
DBMS_OUTPUT.PUT_LINE (v_table);
END IF;
end loop;
close cBankId;
END;
I want to know how to put where clause in the execute immediate statement .
I am getting the error
ORA-06550: line 15, column 67:
PLS-00103: Encountered the symbol "|" when expecting one of the following:
. ( , % ; return returning using
You only need to change the order you're writing the parts of your query.
When using dynamic SQL, you need something like this:
SQL> declare
2 v_table varchar2(30);
3 vCount number;
4 begin
5 v_table := 'dual';
6 execute immediate 'select count(*) from ' || v_table || ' where 1=1' into vcount;
7 --
8 dbms_output.put_line('vCount: ' || vCount);
9 end;
10
11 /
vCount: 1
PL/SQL procedure successfully completed.
That is: execute immediate 'select ... from ... where ...' INTO ...;
You can't dynamically use a variable for the table name, instead use:
DECLARE
cursor cBankId is
select owner||'.'||table_name from all_tab_columns where column_name = 'BANK_ID';
v_table all_tab_columns.table_name%TYPE;
vcount varchar2(50);
v_sql varchar2(1000);
BEGIN
open cBankId;
loop
fetch cBankId into v_table;
exit when cBankId%notfound;
v_sql := 'select count(*) from ' || v_table || ' into vcount where bank_id = 01';
execute immediate v_sql;
IF vcount > 0 THEN
DBMS_OUTPUT.PUT_LINE (v_table);
END IF;
end loop;
close cBankId;
END;

what might be the logic reason in my cursor that cause invalid cursor 01001

there is a logic error in the following procedure that I can't find , can you find what is it ? the below procedure is causing the following error :
ora-01001 invalid cursor 01001
and this is the procedure:
CREATE OR REPLACE PROCEDURE P_C is
v_tab_name varchar2(40);
-- v_col_name varchar2(100);
var1 varchar2(2000);
var2 varchar2(2000);
tab_var varchar2(2000);
/* First cursor */
CURSOR get_tables IS
SELECT tab.table_name
FROM user_tables tab;
/* Second cursor */
CURSOR get_columns IS
SELECT DISTINCT cols.column_name
FROM user_tab_cols cols
WHERE cols.table_name = v_tab_name;
BEGIN
var1 := null;
-- Open first cursor
for gettab in get_tables
LOOP
tab_var :=gettab.table_name;
-- Open second cursor
for getcols in get_columns
LOOP
if var1 is null then
var1 :=getcols.column_name;
else
var1 := var1 ||' , '|| getcols.column_name;
end if;
END LOOP;
CLOSE get_columns;
END LOOP;
CLOSE get_tables;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
end P_C;
check the following code, this will work. The main thing is, to use a cursor variable. So the code will be much clearer and easier.
But consider, that you will get all columns of all tables. That's a lot!
CREATE OR REPLACE PROCEDURE P_C is
var1 varchar2(32000);
/* First cursor */
CURSOR get_tables IS
SELECT tab.table_name FROM user_tables tab;
/* Second cursor */
CURSOR get_columns (ci_tab_name in varchar2) IS
SELECT DISTINCT cols.column_name
FROM user_tab_cols cols
WHERE cols.table_name = ci_tab_name;
BEGIN
var1 := null;
for c1 in get_tables loop
for c2 in get_columns (c1.table_name) loop
if var1 is not null then
var1 := var1 || ', ';
end if;
var1 := var1 || c2.column_name;
end loop;
end loop;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,
'An error was encountered - ' || SQLCODE ||
' -ERROR- ' || SQLERRM);
end P_C;
If I've understood your code right, I think you should change second cursor to something like:
/* Second cursor */
CURSOR get_columns(v_tab_p VARCHAR2) IS
SELECT DISTINCT cols.column_name
FROM user_tab_cols cols
WHERE cols.table_name = v_tab_p;
then change the second loop to something like:
tab_var :=gettab.table_name;
-- Open second cursor
for getcols in get_columns(tab_var) LOOP
and finally, I'm not sure you need to explicitly close the 2 cursors.

Why the Oracle procedure not compiling?

CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
sql_stmt VARCHAR2(2000);
BEGIN
FOR sql_stmt IN (select 'update '|| table_name ||
' set version = 0'
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt;
END LOOP;
COMMIT;
END;
Why the about procedure is not compiling? Its giving error saying
Error(8,26): PLS-00382: expression is of wrong type
How to resolve this?
Two things:
You need to remember that when iterating a cursor it returns a ROW, not a VALUE, and
The SELECT in the cursor needs to give a name to the value in the row being generated by the cursor.
Try:
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR aRow IN (select 'update '|| table_name ||
' set version = 0' AS SQL_STMT
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE aRow.SQL_STMT;
END LOOP;
COMMIT;
END;
Share and enjoy.
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR sql_stmt IN (
select 'update '|| table_name || ' set version = 0' as x
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt.x;
END LOOP;
COMMIT;
END;

Resources