BULK COLLECT LIMIT in EXECUTE IMMEDIATE - oracle

Is it possible to put a limit in a bulk collect using execute immediate?
I have below script but I am getting error when using a LIMIT.
declare
v_query varchar2(3000);
begin
v_query := 'select 1 from dual' -- this is just a sample query.
execute immediate v_query
bulk collect into table1 --table type
end;
If limit is not possible with my code, is there any work around?
Thanks!

It seems that EXECUTE IMMEDIATE syntax doesn't allow for LIMIT in bulk collect clause
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/executeimmediate_statement.htm#CJACGJJG
bulk_collect_into_clause
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/returninginto_clause.htm#CJAIAGHJ
You can use a cursor and FETCH .. LIMIT command together with OPEN..FOR command, in this way:
DECLARE
....
cur sys_refcursor;
BEGIN
v_query := 'SELECT level AS x FROM dual CONNECT BY LEVEL <=10';
OPEN cur FOR v_query;
LOOP
FETCH cur BULK COLLECT INTO collection LIMIT 3;
EXIT WHEN collection.COUNT = 0;
/* Process data from `collection` */
END LOOP;
CLOSE cur;
END;
Example:
DECLARE
TYPE col_typ IS table of NUMBER;
collection col_typ;
v_query varchar2(3000);
cur sys_refcursor;
i int := 0;
x int;
BEGIN
v_query := 'SELECT level AS x FROM dual CONNECT BY LEVEL <=10';
OPEN cur FOR v_query;
LOOP
FETCH cur BULK COLLECT INTO collection LIMIT 3;
EXIT WHEN collection.COUNT = 0;
/* Process data from `collection` */
i := i + 1;
DBMS_OUTPUT.PUT_LINE('==== Batch nbr #' || i );
FOR x IN 1 .. collection.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( collection( x ) );
END LOOP;
END LOOP;
CLOSE cur;
END;
/
Result:
==== Batch nbr #1
1
2
3
==== Batch nbr #2
4
5
6
==== Batch nbr #3
7
8
9
==== Batch nbr #4
10

You can use the DBMS_SQL to do this. However, you have to go over cursor variables.
DECLARE
src_cur SYS_REFCURSOR;
curid NUMBER;
v_query varchar2(3000);
ret NUMBER;
BEGIN
-- DBMS_SQL.OPEN_CURSOR
curid := DBMS_SQL.OPEN_CURSOR;
v_query := 'select 1 from dual';
DBMS_SQL.PARSE(curid, v_query, DBMS_SQL.NATIVE);
ret := DBMS_SQL.EXECUTE(curid);
-- Switch from DBMS_SQL to native dynamic SQL
src_cur := DBMS_SQL.TO_REFCURSOR(curid);
-- Fetch with native dynamic SQL
FETCH src_cur BULK COLLECT INTO ... LIMIT x;
...

Related

DBMS_PARALLEL_EXECUTE exiting without running task

For large loads, we generally prefer to execute it in parallel and hence Oracle package DBMS_PARALLEL_EXECUTE. Here we are using chunking by row. When I run this, I am getting
ORA-29495: invalid state for resume task
Task status is CHUNKED and chunks are in UNASSIGNED status.
What is going on here?
declare
l_sql varchar2(500);
l_chunk_sql varchar2(500);
l_task_nm varchar2(50):='k_clearance_1';
l_try number:=0;
l_status number;
l_count number:=0;
begin
select count(1)
into l_count
from user_parallel_execute_tasks
where task_name=l_task_nm;
--create task if not exist
if l_count = 0 then
--create task
dbms_parallel_execute.create_task(l_task_nm);
end if;
--create chunk
dbms_parallel_execute.create_chunks_by_rowid(
task_name=>l_task_nm,
table_owner=>'RXTEAM',
table_name=>'RPM_STAGE_CLEARANCE_ID_FIX',
by_row=>TRUE,
chunk_size=>10000
);
l_sql := 'update XXXXXX krsc
set krsc.lgcy_extr_tmst = sysdate
where exists (select 1
from rxteam.rpm_stage_clearance_id_fix rscf
where rscf.rpm_stage_clearance_id=krsc.rpm_stage_clearance_id
and rscf.rowid between :start_id and :end_id)';
--run task
dbms_parallel_execute.run_task(task_name=>l_task_nm,
sql_stmt=>l_sql,
language_flag=>DBMS_SQL.NATIVE,
parallel_level=>40
);
l_try := 0;
l_status := DBMS_PARALLEL_EXECUTE.task_status(l_task_nm);
while (l_try < 2 and l_status != DBMS_PARALLEL_EXECUTE.FINISHED)
loop
l_try := l_try + 1;
DBMS_PARALLEL_EXECUTE.resume_task(l_task_nm);
l_status := DBMS_PARALLEL_EXECUTE.task_status(l_task_nm);
end loop;
--dbms_parallel_execute.drop_task(l_task_nm)
end;
/
In my case the problem was a (parse) error in the code of the PL/SQL anonymous block I submitted. I found out by looking in the USER_SCHEDULER_JOB_RUN_DETAILS table.
This might help:
SELECT t.task_name, t.sql_stmt, d.errors
--, d.* -- uncomment to see all job detail information
FROM user_parallel_execute_tasks t
JOIN user_scheduler_job_run_details d ON d.job_name = t.job_prefix||'_1'
WHERE t.status = 'CHUNKED'
ORDER BY t.task_name, d.job_name;

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;

How to store a column of result of select query in an array?

If we have a column in a table of type number, how can we store the result of select query on that column in an array ?
This sample uses a list (table of numbers) to achieve this, because i find
those lists much more handy:
CREATE OR REPLACE TYPE numberlist AS TABLE OF NUMBER;
DECLARE
v_numberlist numberlist;
BEGIN
SELECT intval numbercolumn
BULK COLLECT INTO v_numberlist
FROM lookup;
FOR i IN 1..v_numberlist.count
LOOP
dbms_output.put_line( v_numberlist(i) );
END LOOP;
END;
Create a type which store number:-
CREATE OR REPLACE TYPE varray is table of number;
--write your select query inside for loop () where i am extracting through level
declare
p varray := varray();
BEGIN
for i in (select level from dual connect by level <= 10) loop
p.extend;
p(p.count) := i.level;
end loop;
for xarr in (select column_value from table(cast(p as varray))) loop
dbms_output.put_line(xarr.column_value);
end loop;
END;
output:-
1
2
3
4
5
6
7
8
9
10
Just an option to use some native SQL datatype. Hope it helps.
SET SERVEROUTPUT ON;
DECLARE
lv_num_tab DBMS_SQL.NUMBER_TABLE;
BEGIN
SELECT LEVEL BULK COLLECT INTO lv_num_tab FROM DUAL CONNECT BY LEVEL < 10;
FOR I IN lv_num_tab.FIRST..lv_num_tab.LAST
LOOP
dbms_output.put_line(lv_num_tab(i));
END LOOP;
END;
You may also want to put the whole select in a table. You can use a BULK COLLECT to an array:
CREATE OR REPLACE TYPE t_my_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
PROCEDURE get_tables(p_owner in varchar2)
as
v_res t_my_list;
v_qry varchar2(4000) := '';
begin
v_qry := ' SELECT table_name from all_tables where owner='''||p_owner||'''';
dbms_output.put_line(v_qry);
-- all at once in the table
execute immediate v_qry bulk collect into v_res;
FOR I in 1..v_res.count
loop
dbms_output.put_line(v_res(i));
end loop;
exception
when others then
raise;
end get_tables;
/
begin
get_tables('E') ;
end;
/

How to output result of SELECT statement which is executed using native dynamic SQL?

I have a string which contains SQL SELECT statement.
I wonder how can I output result of the execution of that statement on the screen, execution will be done using native dynamic SQL (EXECUTE IMMEDIATE).
example:
DECLARE
v_stmt VARCHAR2 := 'SELECT * FROM employees';
BEGIN
EXECUTE IMMEDIATE v_stmt; -- ??? how to output result of that select on the screen.
END;
Important remark: structure of table can be any. I have to write a procedure which accepts name of the table as parameter, so I can't hardcode a table structure and don't want to do it.
Thanks for responses. Any ideas very appreciated/
If you are on Oracle 12c with a 12c client, this should work:
declare
rc sys_refcursor;
begin
open rc for 'select * from dual';
dbms_sql.return_result(rc);
end;
Yes we can execute select statement dynamically.
Let say we have a table test. It has four column Row_id,Name,Rank etc
When we do select * from test;
Result will be
Row_id Name Rank
1 R1 5
2 R2 1
3 R3 2
4 R4 4
Now we can use DBMS_SQL package to execute dynamically SELECT Sql Statament.
Code is below:
DECLARE
v_CursorID NUMBER;
v_table VARCHAR2(50):='test';
v_SelectRecords VARCHAR2(500);
v_NUMRows INTEGER;
v_MyNum INTEGER;
v_Myname VARCHAR2(50);
v_Rank INTEGER;
BEGIN
v_CursorID := DBMS_SQL.OPEN_CURSOR;
v_SelectRecords := 'SELECT * from ' || v_table ;
DBMS_SQL.PARSE(v_CursorID,v_SelectRecords,DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,1,v_MyNum);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,2,v_Myname,50);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,3,v_Rank);
v_NumRows := DBMS_SQL.EXECUTE(v_CursorID);
LOOP
IF DBMS_SQL.FETCH_ROWS(v_CursorID) = 0 THEN
EXIT;
END IF;
DBMS_SQL.COLUMN_VALUE(v_CursorId,1,v_MyNum);
DBMS_SQL.COLUMN_VALUE(v_CursorId,2,v_Myname);
DBMS_SQL.COLUMN_VALUE(v_CursorId,3,v_Rank);
DBMS_OUTPUT.PUT_LINE(v_MyNum || ' ' || v_Myname || ' ' || v_Rank );
END LOOP;
EXCEPTION
WHEN OTHERS THEN
RAISE;
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
end;

ORA-01775: looping chain of synonyms but there are no synonyms

Can't figure out why I'm getting 'SQL Statement ignored' and 'ORA-01775: looping chain of synonyms' on line 52 of this stored procedure. Got any ideas?
CREATE OR REPLACE PACKAGE PURGE_LOG_BY_EVENT_DAYS AS
TYPE dual_cursorType IS REF CURSOR RETURN dual%ROWTYPE;
PROCEDURE log_master_by_event_days (event_id NUMBER, purge_recs_older_than NUMBER, result_cursor OUT dual_cursorType);
END PURGE_LOG_BY_EVENT_DAYS;
/
CREATE OR REPLACE PACKAGE BODY PURGE_LOG_BY_EVENT_DAYS
AS
err_msg VARCHAR2(4000);
PROCEDURE log_master_by_event_days (event_id NUMBER, purge_recs_older_than NUMBER, result_cursor OUT dual_cursorType)
IS
TYPE type_rowid IS TABLE OF ROWID INDEX BY BINARY_INTEGER;
TYPE type_ref_cur IS REF CURSOR;
l_rid type_rowid;
c1 type_ref_cur;
l_sql_stmt VARCHAR2(4000);
proc_start_time DATE := sysdate;
purge_date DATE;
l_bulk_collect_limit NUMBER := 1000;
retry NUMBER := 5;
retry_count NUMBER := 0;
loop_count NUMBER := 0;
err_code VARCHAR2(10);
BEGIN
purge_date := to_date(sysdate - purge_recs_older_than);
l_sql_stmt := '';
l_sql_stmt := l_sql_stmt ||' SELECT rowid FROM LOG_MASTER ';
l_sql_stmt := l_sql_stmt ||' WHERE last_changed_date < :purge_date';
l_sql_stmt := l_sql_stmt ||' AND event_id = :event_id';
-- The following while loop
-- executes the purge code
-- 'retry' number of times in case of ORA-01555
WHILE retry > 0 LOOP
BEGIN
-- START of purge code
OPEN c1 FOR l_sql_stmt USING purge_date, event_id;
LOOP
FETCH c1 BULK COLLECT into l_rid LIMIT l_bulk_collect_limit;
FORALL i IN 1..l_rid.COUNT
DELETE from log_master
WHERE rowid = l_rid(i);
COMMIT;
loop_count := loop_count + 1;
EXIT WHEN c1%NOTFOUND;
END LOOP;
CLOSE c1;
-- End of purge code
-- if processing reached this point
-- Process completed successfuly, set retry = 0 to exit loop
retry := 0;
EXCEPTION
WHEN OTHERS THEN
-- ====================================
-- Get error msg
-- ====================================
ROLLBACK;
err_code := sqlcode;
dbms_output.put_line(err_code);
-- ====================================
-- Check if it is 01555
-- if so retry, else exit loop
-- ====================================
retry := retry - 1;
if err_code = '-1555' and retry > 0 THEN
CLOSE c1;
retry_count := retry_count + 1;
else
err_msg := sqlerrm;
exit;
end if;
END;
END LOOP;
IF err_msg IS NULL THEN
open result_cursor for select '1 - PURGE_LOG_BY_EVENT_DAYS ran successfully (event_id : '||event_id||', loop_count : '||loop_count||', bulk_limit : '||l_bulk_collect_limit||', retries : '||retry_count||') ' from dual;
ELSE
open result_cursor for select '2 - PURGE_LOG_BY_EVENT_DAYS After (event_id : '||event_id||', loop_count : '||loop_count||', bulk_limit : '||l_bulk_collect_limit||', retries : '||retry_count||') with Error: ' || err_msg from dual;
END IF;
END log_master_by_event_days;
END PURGE_LOG_BY_EVENT_DAYS;
I have no idea why you're getting the synonym error. But that's a lot of code for something that should be a single DELETE statement. I assume you've changed it to commit-every-n to avoid rollback errors. It would be nice if you could get your DBA to increase the undo space so you can actually do the work you need to do. Failing that, I think you can still make it much simpler:
LOOP
DELETE FROM log_master
WHERE last_changed_date < :purge_date
AND event_id = :event_id
AND rownum <= :batch_delete_limit
USING purge_date, event_id, l_bulk_collect_limit;
EXIT WHEN SQL%NOTFOUND;
END LOOP;
And you can throw your retry logic around that if you want.
Apologies if I've missed some subtlety that makes this different from what you're doing.
SELECT table_owner, table_name, db_link
FROM dba_synonyms
WHERE owner = 'PUBLIC' and db_link is not null
returns 0 rows
as far as i know, there are no synonyms.......

Resources