Oracle Procedure- Statements outside of End Loop not Executing - oracle

Need help with below. statements after end loop not executing. Structure is as follows:
Create or replace procedure a.xyz (b in varchar2,c in varchar2.....) is
bunch of variable declaration
cursor c1
begin
open c1;
loop
fetch c1 into ....;
exit when c1%notfound;
insert
insert
merge
merge
commit;
end loop;
insert
select into
send email
exception
end;
insert, select into, send email not getting executed. Any leads?

You didn't post interesting parts of the procedure - what EXCEPTION does?
This is your pseudocode, modified. Read comments I wrote.
Create or replace procedure a.xyz (b in varchar2,c in varchar2.....) is
bunch of variable declaration
cursor c1
begin
open c1;
loop
begin --> inner begin - exception - end block
fetch c1 into ....;
exit when c1%notfound;
insert
insert
merge
merge
exception
when ... then ... --> handle exceptions you expect. If you used
-- WHEN OTHERS THEN NULL, that'a usually a huge mistake.
-- Never use unless you're testing something,
-- without RAISE, or if you really don't care if
-- something went wrong
end; --> end of inner block
end loop;
insert
select into
send email
commit; --> commit should be out of the loop; even better,
-- you should let the CALLER to decide when and
-- whether to commit, not the procedure itself
exception
when ... then ...
end;
Inner BEGIN-EXCEPTION-END block - if exceptions are properly handled - will let the LOOP end its execution. You should log the error you got (for testing purposes, it could be even
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(c1.id ||': '|| sqlerrm);
END;
so that you'd actually see what went wrong. If it were just
EXCEPTION
WHEN OTHERS THEN NULL;
END;
you have no idea whether some error happened, where nor why.

Related

handling excpetions in plsql block while running in a loop

I have a requirement where I need to drop partitions for more than one tables in a loop.If for some reason that partition doesnt exist in a table the whole procedure is giving error . But I want to drop other partititions which exists in other tables without coming out of the loop
Use an inner begin-exception-end block ("inner" meaning "within a loop"). Something like this:
begin
for cur_r in (select whatever from ...) loop
-- inner block begins here
begin
do stuff here
exception
when ... then ...
end;
-- inner block ends here
end loop;
end;

unable to call exception block in oracle

I have trying to execute below pl sql block in my oracle developer edition.I have made calls to functions and procedures and it works fines.But i am not able to call exception in case my query does not get executed.I have been trying to get a wrong query exectued by passing a string value instead of int value.So it throws error but also i need to get exception block to executed in case of such error.Block 2 should through exception as i am passing string value.But exception block does not get call,Any help?? Below is my block
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
SELECT DEVOPS_ISDBCEXECUTED(DBCID, 'DDL') INTO CNT FROM DUAL;
IF (CNT = 0) THEN
BEGIN
DEVOPS_DBCINSERT (DBCID,'DDL','hsolanki','Prj1','Item1','avarne');
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'DK'; //block 1
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE)
VALUES ('Dk',8);
END IF;
END;
END;
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'Ab';
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE) //block 2
VALUES ('Ab',s);
END IF;
END;
END;
DEVOPS_DBCUPDATE(DBCID, NULL,'SUCCESS');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('ERROR OCCURED : ' || sqlerrm);
DEVOPS_DBCUPDATE (DBCID,sqlerrm,'Failed');
rollback;
END;
END IF;
END;
Your exception handling block is within the 'IF (CNT = 0) THEN .. END IF' block. If you pass a string value, most probably the exception was thrown at the first function call ( SELECT DEVOPS_ISDBCEXECUTED...), which is not 'protected' by an excpetion handler. You would need to move the exception handler to the outermost block, e.g.:
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
....
EXCEPTION
WHEN OTHERS THEN
...
END;
So we know ...
DEVOPS_DBCUPDATE is a procedure which updates a table
... and ...
IN exception i am calling DEVOPS_DBCUPDATE ... my table is not getting updated
... and ...
i dont know what is pragma autonomous_transaction
Putting these clues altogether we can see that the rollback in the EXCEPTION block will wipe out the change to the table executed by the preceding call to DEVOPS_DBCUPDATE(), so it only seems as though the EXCEPTION block is not being executed ( a check on whether the DBMS_OUTPUT message is displayed would confirm that it is being called).
Anyway the solution is to make DEVOPS_DBCUPDATE() run in a nested transaction, so the change is applied regardless of what happens in the wider transaction. We do this with the autonomous_transaction pragma.
Obviously I don't know the exact structure of your code, but it will look something like this:
create or replace procedure DEVOPS_DBCUPDATE( ... ) is
pragma autonomous_transaction;
begin
update your_table
set ....
commit;
end;
The COMMIT in the procedure will persist the change to your table but will not save anything in the outer transaction. So the rollback in the EXCEPTION block would still reverse the inserts into the HS table.
Autonomous transactions are very useful when employed properly, but it is easy to misuse them. This scenario - persistent logging in the event of exception or rollback - is their main use case. But generally, use with caution: it's easy to abuse autonomous transactions and end up with a corrupted database. There's more information in the Oracle documentation.
An error raise in the DECLARE section is not handled by that block's EXCEPTION section. For this reason it is often safer to initialise variables after the BEGIN i.e.
DECLARE
DBCID INT;
CNT INT;
BEGIN
DBCID := 'xxx';
...
EXCEPTION
WHEN OTHERS THEN
... -- The assignment will be caught here
END;

exception handling in cursors

This question is not duplicate. (i m using oracle 10g)
I searched a lot but my problem seems to be diffrent
I have following cursor
DECLARE
-- Some declarations
--
CURSOR C1 IS
-- some select statements
Begin
for r in c1 loop
-- Insert queries
DBMS_OUTPUT.PUT_LINE('INSERTED records');
End loop;
EXCEPTION WHEN others THEN
dbms_output.put_line('error' || SQLERRM);
END;
As per the above cursor, whenever error occures during insert , error is printed on output and execution stops.
whereas It should continue looping.
I tried adding exception block inside loop but still not working
Then you have to use another begin - exception - end within de loop.
Something like this.
DECLARE
-- Some declarations
--
CURSOR C1 IS
-- some select statements
Begin
for r in c1 loop
BEGIN
-- Insert queries
DBMS_OUTPUT.PUT_LINE('INSERTED records');
EXCEPTION
WHEN OTHERS
THEN
dbms_output.put_line('error in LOOP' || SQLERRM);
END;
End loop;
EXCEPTION WHEN others THEN
dbms_output.put_line('error' || SQLERRM);
END;

In Oracle, how to catch exception when table used for the loop doesn't exists

I have this code:
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
END;
My problem is that sometimes SOME_USER.SOMETABLE do not exists but I want the rest of the script to be run. I know that checking if the table exists before running the code (in a IF ... THEN block) will not work because SELECT * FROM SOME_USER.SOME_TABLE is evaluated at compile time.
So another avenue is to run the SELECT with EXECUTE IMMEDIATE. This way it will be evaluated at run time and I would be able to catch the exception. Unfortunately I can't find a way to use EXECUTE IMMEDIATE with my U1 IN loop. How I should achieve this?
I'm on Oracle 11g and the SQL script is run from a batch script on Windows.
You can use the 'OPEN FOR' syntax:
DECLARE
CUR SYS_REFCURSOR;
<variables or record type> -- declare as appropriate
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO <variables or record type>;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
END;
/
You need to fetch each row into variables or a record type, you can't use %ROWTYPE as the table still won't exist; and you can change to do bulk fetches if that's appropriate for your data volumes.
If you run that you'll still get ORA-00942, but if this is in a stored program you won't get it until run time, and you can now add an IF block to check for the table's existence before the OPEN.
Having a data model where objects may or may not exist at run-time seems rather fishy though...
Proposed solution with cursor is fine, I would add an exception handling for this particular exception : Table or view does not exist ORA-00942.
DECLARE
e_missing_t EXCEPTION;
pragma exception_init (e_missing_t,-942);
something number; --some variable you need to fetch to
CUR SYS_REFCURSOR;
BEGIN
OPEN CUR FOR 'SELECT * FROM SOME_USER.SOME_TABLE';
LOOP
FETCH CUR INTO something;
EXIT WHEN CUR%NOTFOUND;
-- do something with variables or record
END LOOP;
CLOSE CUR;
EXCEPTION
WHEN e_missing_t THEN
dbms_output.put_line('some_table is missing');
END;
/
You could possibly use a workaround -
Create a nested table type and store the results of the SELECT in it. Use that type to loop through values.
So,
SELECT data_obj(COL1, COL2) bulk collect into data_tbl_typ from data_table;
This part can go in the dynamic sql. (Remember to use bind variables)
And then just loop through this nested table type in your procedure.
Use the DBMS_SQL package to run the query.
Follow the examples in this Oracle documentation:
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#sthref6147
pl/sql has the exception clause for that. OTHERS catches pretty much everything. You can deal with the exception in the function, or print a message and pass it back to the main. Break your function up into smaller functions and have each one catch it's own exception.
BEGIN
FOR
U1 IN (SELECT * FROM SOME_USER.SOME_TABLE)
LOOP
BEGIN
-- do something;
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Oh well. The table isn't there.');
--RAISE;
END;

Logging the erronous ID but not breaking the LOOP

Here is my problem. I am looping through some values and some of those values raise an exception. I want to log those values, but the program flow should not break. I mean, if I encounter such a value, I will simply log the error and skip to the next value.
here is the simplified version :
drop table test;
--destination Table
create table test
(
id varchar2(2)
);
-- Error log table
create table test_log
(
id varchar2(10)
);
DECLARE
l_num NUMBER;
BEGIN
FOR c IN 90..102
LOOP
INSERT INTO test
VALUES (c);
l_num:=c;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(l_num);
INSERT INTO test_log
VALUES (l_num);
COMMIT;
--raise;
END;
/
My problem is, when it's encountering an error, it simply jumps to the exception section and not looping through the later values in the loop.
You can catch the exception in an inner block:
DECLARE
l_num NUMBER;
BEGIN
FOR c IN 90..102
LOOP
l_num:=c;
BEGIN -- inner block
INSERT INTO test
VALUES (c);
EXCEPTION -- in inner block
WHEN OTHERS THEN
dbms_output.put_line(l_num);
INSERT INTO test_log
VALUES (l_num);
END; -- inner block
END LOOP;
END;
/
The loop won't be interrupted if an exception occurs; only that single insert is affected. Note that you don't really want to commit inside the exception handler as that will commit all the successful inserts so far, not just the error. (If you wanted to log the errors but later roll back you could use an autonomous procedure to do the logging, but that's a separate discussion). And you don't want to re-raise the exception as that would still break the loop and the whole outer anonymous block.
Catching 'others' is generally not a good idea; if you have known errors you could encounter - like a bad data or number format - it's better to catch those explicitly. If you have a wider problem like not being able to extend a data file then the insert inside the exception handler would presumably fail anyway though.
You don't really need l_num any more as c is still in-scope for the inner exception handler, so you could simplify slightly to:
BEGIN
FOR c IN 90..102
LOOP
BEGIN -- inner block
INSERT INTO test
VALUES (c);
EXCEPTION -- in inner block
WHEN OTHERS THEN
dbms_output.put_line(c);
INSERT INTO test_log
VALUES (c);
END; -- inner block
END LOOP;
END;
/
You can also use FORALL.
That answer to your request with SAVE EXCEPTION statement because the FORALL loop to continue even if some DML operations fail. And it will be more efficient than a simple LOOP.
Ex :
DECLARE
CURSOR LCUR$VAL IS
SELECT ID
FROM test1;
TYPE LT$TAB IS TABLE OF TEST%ROWTYPE;
LA$TAB_TEST LT$TAB;
dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_errors, -24381);
LC$ERRORS NUMBER(11);
LC$ERRNO NUMBER(11);
LC$MSG VARCHAR2(4000 CHAR);
LC$IDX NUMBER(11);
BEGIN
OPEN LCUR$VAL;
LOOP
FETCH LCUR$VAL BULK COLLECT INTO LA$TAB_TEST LIMIT 1000;
BEGIN
FORALL x IN 1 .. LA$TAB_TEST.COUNT SAVE EXCEPTIONS
INSERT INTO test VALUES LA$TAB_TEST(x);
EXIT WHEN LCUR$VAL%NOTFOUND;
EXCEPTION
WHEN DML_ERRORS THEN
LC$ERRORS := sql%BULK_EXCEPTIONS.COUNT;
FOR idx IN 1 .. LC$ERRORS
LOOP
LC$ERRNO := sql%BULK_EXCEPTIONS (idx).ERROR_CODE;
LC$MSG := sqlerrm(-LC$ERRNO);
LC$IDX := sql%BULK_EXCEPTIONS(idx).error_index;
-- here you can log in table : test_log...
END LOOP;
END;
END LOOP;
CLOSE LCUR$VAL;
END;
Hoping that it can help.

Resources