How to execute from select result (oracle sql) - sql

I created table with grants list. How I can execute grants from this table ?
Something like
select * from grants_table;
then EXECUTE IMMEDIATE result from select

You could write a loop
begin
for grant in (select * from grants_table)
loop
execute immediate grants.column_with_ddl;
end loop;
end;
Most likely, you'll want to do some amount of logging/ exception handling/ etc.

If your table is of considerable size you could take advantage of the bulk operations and binds, something among the lines:
DECLARE
TYPE cursor_ref IS REF CURSOR;
c1 cursor_ref;
TYPE grants_t IS TABLE OF grants%ROWTYPE;
grants_tab grants_t;
rows_fetched NUMBER;
errors NUMBER;
dml_errors EXCEPTION;
PRAGMA exception_init(dml_errors, -24345);
BEGIN
OPEN c1 FOR 'SELECT * FROM grants';
FETCH c1 BULK COLLECT INTO grants_tab;
rows_fetched := c1%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('Number of grants: ' || TO_CHAR(rows_fetched));
BEGIN
FORALL i IN 1 .. grants_tab.count
EXECUTE IMMEDIATE '< some ddl> :1' USING grants_tab(i);
EXCEPTION WHEN dml_errors THEN
errors := SQL%BULK_EXCEPTIONS.COUNT;
DBMS_OUTPUT.PUT_LINE('Number of errors is ' || errors);
FOR j IN 1..errors LOOP
DBMS_OUTPUT.PUT_LINE('Error ' || j || ' occurred on iteration ' || SQL%BULK_EXCEPTIONS(j).ERROR_INDEX);
DBMS_OUTPUT.PUT_LINE('Oracle error is ' || SQLERRM(-SQL%BULK_EXCEPTIONS(j).ERROR_CODE));
END LOOP;
END;
END;
/

Related

For loop fetching data from a cursor is getting skipped

This is the code I'm using. I'm a newbie to PL/SQL making some modifications to existing pl/sql code.
There is a cursor C_GET_REFERENCED_USER_GROUPS which is storing data from sec_user_group table based on user input which is stored inside variable r_username.
So I'm giving some input which will be taken as the value for variable used inside cursor C_GET_REFERENCED_USER_GROUPS and it has output with few rows. During the execution of the cursor inside a for loop, the for loop is getting completely skipped despite the fact that output is there and I haven't given any lines to exit out of the code.
This is how for loop has been created which loops through the cursor and adds to a table.
FOR R_GROUP_NAME IN C_GET_REFERENCED_USER_GROUPS
LOOP
BEGIN
add_user_to_group_cmd :='insert into PROD.SEC_USER_GROUP VALUES('||R_GROUP_NAME.group_id||','''||l_user_id||''',sys_context(''userenv'', ''current_user''),sysdate)';
dbms_output.put_line(add_user_to_group_cmd||';');
execute immediate (add_user_to_group_cmd);
exception
when others then
dbms_output.put_line('>>>>> ERROR : Unable to add user to group : '||SQLERRM);
END;
END LOOP;
COMMIT;
I don't get any errors while I'm executing this code, it's just the the execution finishes after the looping through the execution of for loop which loops through cursor C_GET_ROLE_DETAIL and just skips the above mentioned cursor execution which is executed last.
I had also printed the cursor during the beginning of the execution of PL/SQL block just to verify that the output exists by using simple for loop:
FOR rec IN C_GET_REFERENCED_USER_GROUPS
LOOP
DBMS_OUTPUT.put_line (rec.group_id);
END LOOP;
The above does print couple of rows yet the same rows are not being inserted during the last part of pl/sql insertion.
Please see below for the Full code:
SET lines 200
SET trimspool ON
SET echo OFF
SET verify OFF
--SET serveroutput ON size 1000000
SET serveroutput ON size unlimited
DECLARE
l_cmd VARCHAR2(4000);
l_rmsschema VARCHAR2(5000) := trim('&&rmsschema');
l_username VARCHAR2(5000);
l_password VARCHAR2(5000);
l_name VARCHAR2(5000);
l_email VARCHAR2(5000);
l_user_id VARCHAR2(5000) :=trim('&&userid');
l_pwd VARCHAR2(5000) :=trim('&&password');
l_new_username VARCHAR2(5000) :=trim('&&new_user_name');
l_user_email VARCHAR2(5000) :=trim('&&emailid');
r_username VARCHAR2(5000) :=trim('&&referenced_schema_name');
insert_new_user_cmd VARCHAR2(5000);
add_user_to_group_cmd VARCHAR2(5000);
l_granted_roles VARCHAR2(5000);
l_granted_groups VARCHAR2(5000);
RMS_SCHEMA_ERROR EXCEPTION;
CURSOR C_GET_NEW_USER_DETAIL
IS
select distinct nuc.user_id,nuc.pwd,nuc.user_name,nuc.user_email from PROD.NEW_USER_CREATION nuc
where not exists ( select 1 from dba_users du where du.username=nuc.user_id );
CURSOR C_GET_ROLE_DETAIL
IS
select distinct nuc.user_id,nuc.granted_role from PROD.NEW_USER_CREATION nuc order by 1;
CURSOR C_GET_REFERENCED_USER_ROLES
IS
select GRANTED_ROLE from dba_role_privs where grantee=r_username;
CURSOR C_GET_REFERENCED_USER_GROUPS
IS
select group_id from prod.sec_user_group where user_id=r_username;
BEGIN
----Testing the cursor values by printing it-----
FOR rec IN C_GET_REFERENCED_USER_GROUPS
LOOP
DBMS_OUTPUT.put_line (rec.group_id);
END LOOP;
------Insert into NEW_USER_CREATION_TABLE----------
OPEN C_GET_REFERENCED_USER_ROLES;
dbms_output.put_line('TRUNCATE TABLE PROD.NEW_USER_CREATION');
EXECUTE immediate 'TRUNCATE TABLE PROD.NEW_USER_CREATION';
LOOP
FETCH C_GET_REFERENCED_USER_ROLES into l_granted_roles;
EXIT when C_GET_REFERENCED_USER_ROLES%NOTFOUND;
insert_new_user_cmd := 'insert into PROD.NEW_USER_CREATION(USER_ID,PWD,USER_NAME,USER_EMAIL,GRANTED_ROLE) VALUES('''|| l_user_id ||''','''||l_pwd||''','''||l_new_username||''','''||l_user_email||''','''||l_granted_roles||''')';
dbms_output.put_line(insert_new_user_cmd||';');
execute immediate (insert_new_user_cmd);
commit;
END LOOP;
CLOSE C_GET_REFERENCED_USER_ROLES;
-- User Creation-------------------------------------------------------------------------
FOR A_REC IN C_GET_NEW_USER_DETAIL
LOOP
BEGIN
l_username := A_REC.USER_ID;
l_password := A_REC.PWD;
l_name := A_REC.USER_NAME;
l_email := A_REC.USER_EMAIL;
BEGIN
dbms_output.put(CHR(10));
dbms_output.put_line('Creating RMS');
BEGIN
l_cmd := 'CREATE USER "'||l_username||'" identified by "'||l_password||'" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP01';
dbms_output.put_line(l_cmd||';');
EXECUTE immediate (l_cmd);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('>>>>> ERROR : CREATE USER : '||SQLERRM);
END;
BEGIN
--l_cmd := 'INSERT INTO '||l_rmsschema||'.USER_ATTRIB(USER_ID, USER_NAME, LANG, USER_EMAIL) VALUES('''||l_username||''','''||l_name||''',''1'','''||l_email||''')';
l_cmd := 'MERGE INTO '||l_rmsschema||'.USER_ATTRIB TGT';
l_cmd := l_cmd || ' USING (SELECT DISTINCT USER_ID,USER_NAME, USER_EMAIL FROM '||l_rmsschema||'.NEW_USER_CREATION WHERE user_id=''' || A_REC.USER_ID || ''') SRC';
l_cmd := l_cmd || ' ON (TGT.USER_ID = SRC.USER_ID )';
l_cmd := l_cmd || ' WHEN NOT MATCHED THEN';
l_cmd := l_cmd || ' INSERT (USER_ID, USER_NAME, LANG, USER_EMAIL)';
l_cmd := l_cmd || ' VALUES ('''||l_username||''','''||l_name||''',''1'','''||l_email||''')';
dbms_output.put_line(l_cmd||';');
EXECUTE immediate(l_cmd);
COMMIT;
--CR_SYNONYMS
BEGIN
declare
missing_object varchar2(128);
prefix1 varchar2(128);
prefix2 varchar2(128);
cursor c_get_missing_object is
select object_name
from dba_objects
where owner = upper(l_rmsschema)
and object_type in ('TABLE', 'VIEW', 'CLUSTER', 'FUNCTION', 'PACKAGE', 'PROCEDURE', 'SEQUENCE', 'TYPE')
and object_name not like 'DBC_%'
and object_name not like 'BIN$%'
MINUS
select object_name
from dba_objects
where owner = upper(l_username)
order by 1;
begin
if l_username <> '&_USER' then
prefix1:='"' || l_username || '".';
else
prefix1:='';
end if;
if l_rmsschema <> '&_USER' then
prefix2:='"' || l_rmsschema || '".' ;
else
prefix2:='';
end if;
open c_get_missing_object;
LOOP
fetch c_get_missing_object into missing_object;
--When at end of objects, exit
if c_get_missing_object%NOTFOUND then
exit;
end if;
BEGIN
execute immediate 'CREATE SYNONYM '||prefix1||'"'||missing_object||'" FOR '||prefix2||'"'||missing_object||'"';
dbms_output.put_line('Created synonym '||prefix1||missing_object||' pointing to '||prefix2||missing_object);
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Create synonym FAILED '||missing_object||' '||SQLCODE||' - '||SQLERRM);
END;
END LOOP;
close c_get_missing_object;
end;
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('>>>>> ERROR RMS: INSERT USER_ATTRIB: '||SQLERRM);
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('>>>>> ERROR : '||SQLERRM);
END;
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT ;
END;
END LOOP;
-- RMS Role Assignment-------------------------------------------------------------------------
FOR A_REC IN C_GET_ROLE_DETAIL
LOOP
-- RMS Role Assignment-------------------------------------------------------------------------
begin
l_cmd := 'grant '|| A_REC.GRANTED_ROLE ||' to "'||A_REC.USER_ID||'"';
dbms_output.put_line(l_cmd||';');
execute immediate (l_cmd);
exception
when others then
dbms_output.put_line('>>>>> ERROR : GRANT PRIVILEGES : '||SQLERRM);
end;
end loop;
commit;
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'Invalid path. Create directory or set UTL_FILE_DIR.');
WHEN OTHERS THEN
dbms_output.put_line('>>>>> ERROR : '||SQLERRM);
-----Adding user to groups-----------------------------------------------------------------------
FOR R_GROUP_NAME IN C_GET_REFERENCED_USER_GROUPS
LOOP
BEGIN
add_user_to_group_cmd :='insert into PROD.SEC_USER_GROUP VALUES('||R_GROUP_NAME.group_id||','''||l_user_id||''',sys_context(''userenv'', ''current_user''),sysdate)';
dbms_output.put_line(add_user_to_group_cmd||';');
execute immediate (add_user_to_group_cmd);
exception
when others then
dbms_output.put_line('>>>>> ERROR : Unable to add user to group : '||SQLERRM);
END;
END LOOP;
COMMIT;
END;
/
I had spooled the output into a log file:Here is part of the log file:
Enter value for rmsschema: prod
Enter value for userid: SCOTT
Enter value for password: scott
Enter value for new_user_name: scott
Enter value for emailid: scott#test
Enter value for referenced_schema_name: 23093
1404
8322//These are the two values that are returned by the cursor and after that should be inserted into the table SEC_USER_GROUP but it's somehow getting skipped.////
TRUNCATE TABLE prod.NEW_USER_CREATION
insert into prod.NEW_USER_CREATION(USER_ID,PWD,USER_NAME,USER_EMAIL,GRANTED_ROLE) VALUES('SCOTT','scott','scott','scott#test','TEST_USER_ROLE');
insert into prod.NEW_USER_CREATION(USER_ID,PWD,USER_NAME,USER_EMAIL,GRANTED_ROLE) VALUES('SCOTT','scott','scott','scott#test','CUSTOMER_ROLE');
Creating RMS
CREATE USER "SCOTT" identified by "scott" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP01;
MERGE INTO prod.USER_ATTRIB TGT USING (SELECT DISTINCT USER_ID,USER_NAME, USER_EMAIL FROM prod.NEW_USER_CREATION WHERE user_id='SCOTT') SRC ON (TGT.USER_ID = SRC.USER_ID ) WHEN NOT
MATCHED THEN INSERT (USER_ID, USER_NAME, LANG, USER_EMAIL) VALUES ('SCOTT','scott','1','scott#test');
//Synonyms for the user created
Created synonym "SCOTT".AA_XX_ITEM pointing to "prod".AA_XX_ITEM
Created synonym "SCOTT".ACC_VALIDATE_API pointing to "prod".ACC_VALIDATE_API
Created synonym "SCOTT".ADDR pointing to "prod".ADDR
Created synonym "SCOTT".ADDRESS_SQL pointing to "prod".ADDRESS_SQL
.................................................................
grant TEST_USER_ROLE to "SCOTT";
grant CUSTOMER_ROLE to "SCOTT";
PL/SQL procedure successfully completed.
As seen above the last part of the code doesn't get executed even though the cursor returns rows.

How to write Execute immediate in cursor select Query

How to wite EXECUTE IMMEDIATE in the cursor select Query.
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
CURSOR cur_attendee_data
IS
EXECUTE IMMEDIATE l_cur_query;
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
here I try to use initialize cursor outside begin block but it rises exception, how to write a dynamic query for the cursor,
cur_attendee_data is
EXECUTE IMMEDIATE l_cur_query;
Error(113,8): PLS-00103: Encountered the symbol "CUR_ATTENDEE_DATA" when expecting one of the following: := . ( # % ;
If you fetch all rows with BULK COLLECT INTO ... then you need only one loop, the second loop is useless.
The basic solution would be this one:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from {table X} where c1=:d';
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
However, I don't see any reason to make dynamic SQL. You can simply run
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR SELECT * from {table X} where c1 = in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
You can use below code instead -
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (100) := 'SELECT * from table X where c1=:in_from_date';
TYPE t_cur IS REF CURSOR;
cur_attendee_data t_cur
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
You need to use open cursor for '' as following:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
cur_attendee_data SYS_REFCURSOR; -- JUST DECLARED THE CURSOR
--TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
-- INDEX BY PLS_INTEGER; -- this declaration must be at schema level
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data for l_cur_query; -- OPEN THE CURSOR WITH DYNAMIC QUERY
..
.. -- YOUR CODE AS IT IS
..
Cheers!!

Dynamically Identify Table (Table Identified by Variable)

I'm trying to create a procedure that will allow me to write an existing row to another table dynamically but the row declaration and insert-statement in the following snippet don't work. The error message indicates that the view hasn't been identified although outputting the target_table.table_name works just fine.
More will be added to the block later on - such as a column with the operation (e.g. INSERT or UPDATE). This is just a simple example and the last procedure (pass_reference) is used to trigger the procedure.
Any help would be much appreciated.
CREATE OR REPLACE PROCEDURE denormalize (new_cursor sys_refcursor, target_table_name varchar)
IS
target_table user_tables%rowtype;
sql_target_table varchar(200) := 'select * from user_tables where table_name = :target_table_name';
row target_table%rowtype;
BEGIN
execute immediate sql_target_table into target_table using target_table_name;
LOOP
fetch new_cursor into row;
exit when new_cursor%notfound;
insert into target_table values row;
commit;
END LOOP;
END denormalize;
/
CREATE OR REPLACE PROCEDURE pass_reference
AS
new_cursor sys_refcursor;
BEGIN
open new_cursor for select * from sales where sales_id=1;
denormalize(new_cursor, 'NEW_SALES');
END;
/
please check this code, it's not working only as for example, as you see for working columns in your cursor should be named as columns in destination table.
I take this code from package that create html table in mail base on view, hope you found this example useful
good luck
declare
in_view_name varchar2(30);
in_table_name varchar2(30) := 'your_new_table';
out_rc number;
out_rc_txt varchar2(1000);
l_cursor number;
l_sql varchar2(50) := 'select * from ' || in_view_name;
l_col_cnt binary_integer;
l_col_tab dbms_sql.desc_tab;
l_column_value varchar2(4000);
l_is_empty boolean := true;
l_insert_header varchar2(1000);
l_insert varchar2(32000);
begin
out_rc := 0;
out_rc_txt := 'OK';
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse(l_cursor, l_sql, dbms_sql.native);
l_col_cnt := dbms_sql.execute(l_cursor);
dbms_sql.describe_columns(l_cursor, l_col_cnt, l_col_tab);
l_insert_header := 'insert into '||in_table_name||'(';
if l_col_cnt > 0 then
-- header
for i in l_col_tab.first .. l_col_tab.last loop
dbms_lob.append(l_insert_header, l_col_tab(i).col_name);
if i != l_col_tab.last then
dbms_lob.append(l_insert_header, ',');
end if;
dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
end loop;
l_insert_header := l_insert_header || ') values(';
-- data
while dbms_sql.fetch_rows(l_cursor) > 0 loop
l_is_empty := false;
l_insert := l_insert_header;
for i in l_col_tab.first .. l_col_tab.last loop
dbms_sql.column_value(l_cursor, i, l_column_value);
l_insert := l_insert || '''' || l_column_value || ''','
if not in_attachment then
dbms_lob.append(out_table, l_td);
end if;
if (not in_attachment) or (l_column_value is not null) then
dbms_lob.append(out_table, nvl(l_column_value, l_nbsp));
end if;
if (not in_attachment) or (i != l_col_tab.last) then
dbms_lob.append(out_table, l_tdc);
end if;
end loop;
l_insert := substr(l_insert, 1, length(l_insert) - 1) || ')';
execute immediate l_insert;
end loop;
end if;
dbms_sql.close_cursor(l_cursor);
end;

How can I print a multi line result using PL/SQL?

PROCEDURE A(
...
BEGIN
stmt := 'select * from '||src;
execute immediate stmt;
dbms_output.put_line(??);
END A;
If you know the structure of the table named in "src" when writing the code then you can do this:
PROCEDURE A IS
...
l_cur sys_refcursor;
BEGIN
stmt := 'select * from '||src;
open l_cur for stmt;
loop
fetch l_cur into ??; -- record or list of variables that matches columns of "src"
exit when l_cur%notfound;
dbms_output.put_line(??);
end loop;
close l_cur;
END A;
If you will not know the structure until run time then you will need to use the DBMS_SQL package, which is very powerful but not simple.
I'm not sure wether this is working with your "execute immediate stmt" approach, but with static Sql, following is working for me:
for my_result in
(
select * from my_table tbl
where ...
order by tbl.my_id_col
) loop
dbms_output.put_line(my_result.field1 || ', ' || my_result.field2 || ...);
end loop;

Oracle: Bulk collect into "table of number" gives "numeric or value error"

I've come across an example of doing this on the net which also fails on my DB (10g) but here's my version.
...
TYPE prog_rec_type IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
prog_rec_list prog_rec_type;
begin
...
EXECUTE IMMEDIATE 'SELECT PROGRESS_RECID FROM ' || v_table_name || v_where BULK COLLECT INTO prog_rec_list;
--ERROR FOUND IN THIS SECTION
FOR i IN prog_rec_list.FIRST..prog_rec_list.LAST
LOOP
--DBMS_OUTPUT.PUT_LINE('FOR LOOP: ' || i);
null;
END LOOP;
...
END;
Much appreciate the help.
The result set is empty. So you need to check that you got some results from that SELECT PROGRESS_RECID FROM ' || v_table_name || v_where
you may try this,
FOR i IN 1 .. prog_rec_list.COUNT LOOP
..
END LOOP