exception handling in cursors - oracle

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;

Related

Oracle Procedure- Statements outside of End Loop not Executing

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.

Oracle Pl/SQL Exception Flow

Given a simple SP like;
CREATE OR REPLACE PROCEDURE TEST1
AS
BEGIN
EXECUTE IMMEDIATE 'truncate table missingtable';
dbms_output.put_line('here');
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
I never get to the output statement, I thought control returned to the same block, which is the only block.. and yes, missingtable reports a -942 if I try to truncate it.
it's a logic problem, in fact the exception happens but you coded to raise an exception only if the return code is different of 942 which is the the error happening.
if you want to continue to the dbms_output in you first block you need an inner exception
CREATE OR REPLACE PROCEDURE TEST1
AS
BEGIN
BEGIN
EXECUTE IMMEDIATE 'truncate table missingtable';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
ELSE
NULL;
END IF;
END;
dbms_output.put_line('here');
END;
/

Why are none of my conditions being met in this proc?

So i have a stored procedure (that's been watered down below for demo purposes) that aren't passing any conditions and thus aren't inserting/passing any values into my table. I've tried converting the varchar/string that is being passed in by Java to a number but nothing is working. Below is my 'simplified code'
Create or Replace Procedure SAMPLE(rValue IN VARCHAR)
IS
v_Max value.value%type;
v_forecast value.value%type;
BEGIN
--
SELECT BUFFER_MAX_VALUE
INTO v_MAX
FROM look_up;
--
EXCEPTION
WHEN no_data_found
THEN SELECT 0
INTO v_forecast
FROM DUAL;
--
IF to_Number(rValue) < 0 OR to_Number(rValue) > v_MAX)
THEN
dbms_output.put_line('IF1 Works');
insert into value(value_id, value)
values(1, rValue);
ELSIF rValue is null OR to_Number(rValue) = 0
THEN
dbms_output.put_line('IF1A ONLY Works');
END IF;
ELSE
insert into value(value_id, value)
values(1, v_forecast);
dbms_output.put_line('IF1 ELSE ONLY Works');
END SAMPLE;
i tried passing the following in:
BEGIN
SAMPLE('-7');
END;
If the first SELECT BUFFER_MAX_VALUE returns anything, nothing else will be executed because you put absolutely everything into the EXCEPTION section. If you meant to handle that statement only, you should have enclosed it into its own BEGIN-END block, such as
create procedure ...
begin
-- its own begin starts now
begin
select buffer_max_value into v_max
from look_up;
exception
when no_data_found then
-- do something here
end;
-- its own end ends now
-- put the rest of your code here
end;
By the way, does LOOK_UP table contain no rows or only one row, always? Because, as SELECT you wrote contains no WHERE clause, it might raise TOO_MANY_ROWS (which you should also handle).
You declared rValue as VARCHAR2, and then apply TO_NUMBER to it. Why don't you declare it to be a NUMBER, instead? Because, nothing prevents you from passing, for example, 'XYZ' to the procedure, and then TO_NUMBER will miserably fail with the INVALID NUMBER error.
[EDIT: some more exception handling]
EXCEPTION section handles all exceptions that happen in that BEGIN-END block, no matter how many SELECT statements you have. Though, you won't know which one failed, unless you include a little bit of additional (simple) programming.
Note that this is just for showing what I meant; don't handle errors with DBMS_OUTPUT (as, most probably, nobody will see it), and rarely you'd want to handle errors with WHEN OTHERS.
create procedure ...
l_position number;
begin
l_position := 1;
select ... into ... from ...;
l_position := 2;
select ... into ...
exception
when others then
dbms_output.put_line('Error on position ' || l_position ||' '|| sqlerrm);
raise;
end;
As far as I can tell, you wanted the exception section to trap the situation where there is nothing in the lookup table. In that case, you set v_forecast and then continue. That means you need to put the select inside its own block.
I also avoiding multiple to_number calls by setting a constant.
I got rid of the unnecessary select from dual.
I also really really hope that you do not have a table named VALUE with a column named VALUE. Choose more meaningful names.
See how this works for you.
CREATE OR REPLACE PROCEDURE sample (rvalue IN VARCHAR2)
IS
c_rvalue CONSTANT NUMBER := rvalue;
v_max VALUE.VALUE%TYPE;
v_forecast VALUE.VALUE%TYPE;
BEGIN
BEGIN
SELECT buffer_max_value INTO v_max FROM look_up;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
v_forecast := 0;
END;
IF c_rvalue < 0 OR c_rvalue > v_max
THEN
DBMS_OUTPUT.put_line ('IF1 Works');
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, rvalue);
ELSIF c_rvalue IS NULL OR c_rvalue = 0
THEN
DBMS_OUTPUT.put_line ('IF1A ONLY Works');
ELSE
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, v_forecast);
DBMS_OUTPUT.put_line ('IF1 ELSE ONLY Works');
END IF;
END sample;

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.

Continue loop in reading excel rows even when an exception occurred in PL/SQL

Here's the overview of the code:
PROCEDURE
BEGIN
WHILE LOOP --loop each row
--Read each row and add in table
COMMIT;
END LOOP;
EXCEPTION
WHEN OTHERS
--log error
ROLLBACK;
END;
Ex: There are 5 lines. The 3rd line has an error in it. The program stops right after it reads the error line, making the other lines next to it not evaluated/read. My goal is after it went to the exception, it will output the line and continue with reading the other lines. So how do I return back to the loop if it went to the exception?
Try this, then you will cache error only in inner anonymous block:
PROCEDURE
BEGIN
WHILE LOOP --loop each row
BEGIN
--Read each row and add in table
COMMIT;
EXCEPTION
WHEN OTHERS
--log error
ROLLBACK;
END;
END LOOP;
END;
Try using a independent PL/SQL block in the loop itself.
BEGIN
WHILE LOOP --loop each row
BEGIN
--Read each row and add in table
COMMIT;
EXCEPTION --Catch the exception here and print the data and it will move on
END;
END LOOP;
EXCEPTION
WHEN OTHERS
--log error
ROLLBACK;
END;

Resources