Why does exception through me out of cursor loop? - oracle

I have a PL/SQL oracle procedure,
I run over list of sql_id to tune them.
I have an exception , but it through me out and finish.
Tried it with or without continue, but it's the same..
ANY IDEAS?
CREATE OR REPLACE Procedure sql_perf_for_sql_id
IS
v_sql_id VARCHAR2(13);
stmt_task VARCHAR2(64 CHAR);
retcode VARCHAR2(64 CHAR);
cursor c1 is
SELECT SQL_ID
FROM sql_for_tune
WHERE tuned='NO';
BEGIN
IF c1%ISOPEN THEN
CLOSE c1 ;
END IF;
open c1;
LOOP
--for r_c1 in c1 loop
BEGIN
fetch c1 into v_sql_id;
--v_sql_id := r_c1.sql_id;
exit when c1%notfound;
update sql_for_tune set remarks='RUNNING' where sql_id=v_sql_id;
commit;
stmt_task := NULL;
DBMS_OUTPUT.PUT_LINE (v_sql_id);
-- create a tuning task tune the statement
stmt_task := DBMS_SQLTUNE.CREATE_TUNING_TASK(sql_id => v_sql_id);
DBMS_OUTPUT.PUT_LINE( NVL(stmt_task,'Error CREATE_TUNING_TASK() Return Value'));
-- execute the resulting task
retcode := DBMS_SQLTUNE.EXECUTE_TUNING_TASK(stmt_task);
DBMS_OUTPUT.PUT_LINE( NVL(retcode,'Error EXECUTE_TUNING_TASK() Return Value'));
-- accept the resulting task
retcode := DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(stmt_task);
DBMS_OUTPUT.PUT_LINE( NVL(retcode,'Error ACCEPT_SQL_PROFILE() Return Value'));
-- Optional SELECT DBMS_SQLTUNE.report_tuning_task( stmt_task ) AS recommendations FROM dual;
update sql_for_tune set tuned='YES',
task_name = stmt_task ,
index_benefit=(SELECT max( r.benefit/100)
FROM dba_advisor_actions a,
dba_advisor_recommendations r
WHERE a.task_name=stmt_task
AND a.task_id = r.task_id
AND a.rec_id = r.rec_id
and a.command='CREATE INDEX'),
profile_benefit=(SELECT max( r.benefit/100)
FROM dba_advisor_actions a,
dba_advisor_recommendations r
WHERE a.task_name=stmt_task
AND a.task_id = r.task_id
AND a.rec_id = r.rec_id
and a.command='ACCEPT SQL PROFILE')
where sql_id=v_sql_id;
commit;
update sql_for_tune set remarks='END' where sql_id=v_sql_id;
commit;
EXCEPTION
WHEN OTHERS THEN
update sql_for_tune set tuned='CHECK', task_name = stmt_task where sql_id=v_sql_id;
commit;
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
--continue;
END;
END LOOP;
close c1;
END;
SQL> execute sql_perf_for_sql_id;
BEGIN sql_perf_for_sql_id; END;
*
ERROR at line 1:
ORA-20001: An error was encountered - -13786 -ERROR- ORA-13786: missing SQL
text of statement object "1" for tuning task "TASK_464272"
ORA-06512: at "SQL_PERF.SQL_PERF_FOR_SQL_ID", line 73
ORA-06512: at line 1
SQL>
Thank you

It exits because of
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
If you want the loop to continue, don't raise the error - log it using some other way (for example, dbms_output.put_line being the simplest; store it into a table by using a call of the autonomous transaction procedure).
Also, remove all those COMMITs out of the loop. Commit once, at the end of the procedure.

Related

PL/SQL: How to insert depending on column value

I'm a novice at PL/SQL. I have attempted various approaches to use a Cursor to insert into a temp table depending on whether or not the value already exists in the temp table. I either get too many rows or nothing is inserted.
This is my last pseudocode approach and is the bare essence of what I'm attempt to accomplish:
DB: Oracle 12
Using SQL Developer
Goal: Take duplicate accountno info from table1 and merge / combine into single row in temptable
1. Add initial accountno info if it doesn’t already exists in temptable
2. If accountno exists in temptable add the additional info to accountno row
Suggestions are greatly appreciated.
Pseudocode
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN EXIT; END IF;
select accountno INTO v_cnt from table1 where Exists(select 1 from temptable where accountno <> c1d.accountno);
IF v_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
If you strictly want to correct your pseudo code, You may try -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
BEGIN
SELECT accountno
INTO V_cnt
FROM temptable
WHERE accountno = c1d.accountno
AND ROWNUM = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_cnt := NULL;
END;
IF V_cnt is NULL THEN
INSERT INTO temptable (accountno)
values(c1d.accountno);
END IF;
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;
I would strongly recommend to use below pseudo code -
Declare
V_cnt number (20);
CURSOR c1 is select * from table1;
C1d c1%rowtype;
BEGIN
--
OPEN C1;
LOOP
FETCH C1 INTO c1d;
EXIT WHEN C1%NOTFOUND;
-- Limit attempts
IF LINE > 5 THEN
EXIT;
END IF;
MERGE INTO temptable
USING table1
ON (accountno = c1d.accountno)
WHEN NOT MATCHED THEN
INSERT (accountno)
values(c1d.accountno);
LINE:= LINE + 1;
END LOOP;
CLOSE C1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('NO DATA');
END;

Oracle PL/SQL capture lock exception in loop and continue

I have a procedure below, which loops through a cursor does some logic. I have put FOR UPDATE NOWAIT on the cursor to lock my records set, in case someone from another session wants to update the same set.
But there may be a scenario where the records I have selected in my cursor, is locked by someone as well, in which case, I want to simply log the locked record in a log table and continue to the next record in the loop.
PROCEDURE test(p_id IN NUMBER)
IS
CURSOR cur_test IS
SELECT emp_id,
emp_name
FROM
EMP
FOR UPDATE NOWAIT;
row_locked EXCEPTION;
PRAGMA EXCEPTION_INIT(row_locked, -54);
BEGIN
FOR r_cur_test IN cur_test
LOOP
BEGIN
--do something
EXCEPTION
WHEN row_locked THEN
--log locked record in log table
log_lock('Record is locked');
COMMIT;
END;
END LOOP;
--call log function to log a successful run
COMMIT;
EXCEPTION
WHEN OTHERS THEN
--Unsuccessful completion of the run.Trap all unhandled exceptions.
--log error in error table
log_lock('Process exited with error');
ROLLBACK;
END;
for testing, I open session 1 and executed
select * from EMP FOR UPDATE NOWAIT
and in session 2, I executed
begin
test(1);
end;
it always went to the OTHERS exception, as a result of which, the procecure just terminated without continuing looping through the cursor.
Can anyone give me some advice? Thanks a lot
When you do open cursor for update this statement works in scenario: at first run select query and set lock for all records and after then do fetch operations.
PROCEDURE test(p_id IN NUMBER)
IS
CURSOR cur_test IS
SELECT emp_id,
emp_name,
rowid as row_id
FROM
EMP;
row_locked EXCEPTION;
PRAGMA EXCEPTION_INIT(row_locked, -54);
v_sql varchar2(4000) := 'SELECT 1 FROM EMP t WHERE rowid = :row_id FOR UPDATE NOWAIT';
c int;
n int;
BEGIN
c := dbms_sql.open_cursor;
dbms_sql.parse(c, v_sql, dbms_sql.native);
FOR r_cur_test IN cur_test
LOOP
BEGIN
dbms_sql.bind_variable (c, 'row_id', i.row_id);
n := dbms_sql.execute(c);
--do something
EXCEPTION
WHEN row_locked THEN
--log locked record in log table
log_lock('Record is locked');
COMMIT;
END;
END LOOP;
dbms_sql.close_cursor(c);
--call log function to log a successful run
COMMIT;
EXCEPTION
WHEN OTHERS THEN
--Unsuccessful completion of the run.Trap all unhandled exceptions.
--log error in error table
log_lock('Process exited with error');
dbms_sql.close_cursor(c);
ROLLBACK;
END;

when no_data_found stop the current iteration and resume the next

I have a code like below:
DECLARE
rec emp%ROWTYPE
BEGIN
<<start_again>>
FOR rec IN c1 LOOP
SELECT emp_index
INTO flag
FROM m_sme006
WHERE emp_id = rec.emp_id
AND eff_dt = rec.eff_dt
AND end_dt = rec.end_dt
AND last_maint_ts > rec.last_maint_ts;
END LOOP;
EXCEPTION
WHEN no_data_found THEN
GOTO start_again;
END;
Every time the no_data_found exception occurs, I want the execution to go back and continue with next iteration. But it throws the error PLS-00375: illegal GOTO statement
Also can i include this line dbms_output.Put_line('Outside exception block'rec.sme006_index); in the exception block to see where this happened?
Thanks in Advance
You can't return the program flow from the exception block back to the execution block. You need to put the exception block inside the loop, like this:
DECLARE
rec emp%ROWTYPE
BEGIN
FOR rec IN c1 LOOP
BEGIN
SELECT emp_index
INTO flag
FROM m_sme006
WHERE emp_id = rec.emp_id
AND eff_dt = rec.eff_dt
AND end_dt = rec.end_dt
AND last_maint_ts > rec.last_maint_ts;
EXCEPTION
WHEN no_data_found THEN
dbms_output.Put_line('Outside exception block ' || rec.sme006_index);
END;
END LOOP;
END;
I believe you are looking for something like this:
DECLARE
rec emp%ROWTYPE
BEGIN
FOR rec IN c1 LOOP
BEGIN
SELECT emp_index
INTO flag
FROM m_sme006
WHERE emp_id = rec.emp_id
AND eff_dt = rec.eff_dt
AND end_dt = rec.end_dt
AND last_maint_ts > rec.last_maint_ts;
EXCEPTION
WHEN no_data_found THEN
-- whatever code you want to log your error
END;
END LOOP;
END;

PL SQL numeric or value error

On Executing this query, im having an error:
*Error report:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 34
06502. 00000 - "PL/SQL: numeric or value error%s"*
.
My oracle version is:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
please help me..
Regards,
DECLARE CURSOR c1 is SELECT TABLE_NAME FROM All_Tables WHERE TABLE_NAME NOT LIKE '%$%' AND Owner NOT IN ('WMSYS', 'EXFSYS', 'CTXSYS', 'WKSYS', 'SYSMAN', 'SYSTEM', 'FLOWS_030000', 'MDSYS', 'ORDSYS', 'DBSNMP', 'XDB', 'OLAPSYS');
col_names SYS_REFCURSOR; TYPE dat_res IS RECORD(tab_name VARCHAR2(1000),col_name VARCHAR2(1000));
TYPE dr is table of dat_res;
act_dat dr:= dr();
status NUMBER := 0;
cnt NUMBER := 1;
sql_stmt VARCHAR2(10000);
tab_name1 VARCHAR2(100);
col_name1 VARCHAR2(100);
BEGIN
FOR I IN C1 LOOP sql_stmt:= 'SELECT table_name,column_name FROM all_Tab_cols WHERE table_name = '||CHR(39)||I.table_name||CHR(39);
OPEN col_names FOR sql_stmt; LOOP FETCH col_names INTO tab_name1,col_name1;
EXIT WHEN col_names%NOTFOUND;
BEGIN
EXECUTE IMMEDIATE
'SELECT 1 FROM '||tab_name1|| ' WHERE REGEXP_LIKE('||'TO_CHAR('||col_name1||'), '||CHR(39)||'^[%][a-bA-B]'||CHR(39)||') '
INTO status;
EXCEPTION
WHEN VALUE_ERROR THEN NULL;
WHEN NO_DATA_FOUND THEN NULL;
WHEN OTHERS THEN NULL;
END;
IF (status = 1) THEN act_dat.extend; act_dat(cnt).tab_name:= tab_name1; act_dat(cnt).col_name:= col_name1; status := 0; cnt:= cnt + 1;
END IF; END LOOP;
CLOSE col_names;
END LOOP;
dbms_output.put_line('Table Name : Column Name');
FOR K IN act_dat.FIRST..act_dat.LAST LOOP insert into my_SAuditor_table VALUES (act_dat(K).tab_name, act_dat(K).col_name);
END LOOP;
Execute IMMEDIATE 'SELECT * FROM my_SAuditor_table';
END;
You might be looping over an empty collection.
You need to check that act_dr has some rows before using act_dr.FIRST and act_dr.LAST, otherwise you get null which is not a number.
This can be done with:
if act_dr.count > 0 then
FOR K IN act_dat.FIRST..act_dat.LAST LOOP
insert into my_SAuditor_table VALUES (act_dat(K).tab_name, act_dat(K).col_name);
END LOOP;
end if;
Problem is that column table name definitions are not consistent. In the end you end up inserting a 1000 bytes column in a 512 bytes column and the funny part is, you only read the column with a max length of 100 bytes.
I'd advise to cleanup the code.

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