PL/SQL Exceptions on Update/Delete of non-existing row - oracle

I am learning PL/SQL these days and currently working with Procedures and exceptions using oracle HR schema.
Here is my simple procedure.
create or replace
PROCEDURE DEL_JOB
(p_jobid jobs.job_id%TYPE)
AS
sqle NUMBER;
sqlm VARCHAR2(300);
BEGIN
DELETE FROM JOBS
WHERE JOB_ID = UPPER(p_jobid);
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('No such record');
END IF;
EXCEPTION
WHEN OTHERS THEN
sqle := SQLCODE;
sqlm := SQLERRM;
DBMS_OUTPUT.PUT_LINE('There is no job with this id that could be deleted');
DBMS_OUTPUT.PUT_LINE('Error Code ='||sqle||' Error message =' ||sqlm);
END;
When I execute this procedure the output is
No such record
PL/SQL procedure successfully complete.
However, according to the Oracle PDF it should throw an exception and I should really get the message I entered in the exception.
Same thing happened with the Update on non existing record.
Please advise. Thanks

I believe SQL%NOTFOUND returns true when no records are found. Your IF would evaluate to true in that case, and therefore write your put_line to terminal. The SQL statement executed successfully. If you execute that SQL statement by itself from command line, you will receive 0 rows updated/deleted, not an Oracle error.
If you want to have an exception thrown, you could use RAISE inside your IF and point it to the exception in the exception block you want to have thrown.

There is no "exception" - the sql executed successfully. It successfully deleted every record that matched the criteria...which was 0 records. Same thing if a similar update statement was executed. You used the SQL%NOTFOUND to determine there were no records that were affected, but this does not mean there was an "exception".
Perhaps you're thinking of the NO_DATA_FOUND Exception raised if you try a "select into" clause and it doesn't find any matching records.

to do so you need to use
IF SQL%ROWCOUNT = 0 THEN
RAISE no_delete;
END IF;
and define your

Related

Oracle pl/sql Procedure to update table - exception handling

This is my first query on PL/SQL and I did spend an hour trying to find answers on the net, anyway - here it goes.
I'm writing a procedure to update a table and it all works fine, however when I typed in to update a job_id that doesn't exist, I expected my exception handling to tell me that the job_id is invalid, however I got no error message.
My code is as follows:
CREATE OR REPLACE PROCEDURE UPD_JOB(p_job_id jobs.job_id%TYPE, p_jobnew jobs.job_title%TYPE)
IS
BEGIN
UPDATE JOBS SET job_title =p_jobnew WHERE JOB_ID = p_job_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No rows have been updated');
END;
/
I then tried to update a job_id that I knew didn't exist to see if the exception works by typing the following
EXECUTE UPD_JOB('ABCXXX','WILL FAIL');
From "Handling PL/SQL Errors":
NO_DATA_FOUND
A SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table. SQL aggregate functions such as AVG and SUM always return a value or a null. So, a SELECT INTO statement that calls an aggregate function never raises NO_DATA_FOUND. The FETCH statement is expected to return no rows eventually, so when that happens, no exception is raised.
You're not using a statement that would cause a NO_DATA_FOUND exception to be raised.
Maybe you can use SQL%ROWCOUNT. From "Performing SQL Operations from PL/SQL":
To find out how many rows are affected by DML statements, you can check the value of SQL%ROWCOUNT...
CREATE OR REPLACE PROCEDURE UPD_JOB (p_job_id jobs.job_id%TYPE,
p_jobnew jobs.job_title%TYPE)
IS
BEGIN
UPDATE JOBS
SET job_title = p_jobnew
WHERE JOB_ID = p_job_id;
IF SQL%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('No rows have been updated');
END IF;
END;
/

how to insert the result of dbms_output.put_line to a table for error catch?

Exception
WHEN OTHERS THEN
--dbms_output.put_line('pl_update_sidm_user r: ERROR CODE:' || sqlcode || '~' ||
--sqlerrm || ' EMPL_NBR:' || r.EMPL_NBR);
insert into ERROR_MSG (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL, 'pl_update_sidm_user_duty_role r2');
END;
I would like to put the error result to a table.
However, how can I do that?
Can I put the result of dbms_output to a table as a string?
If not, can I get the sqlcode,sqlerrm without using dbms_output?
Thank you !!
From the documentation,
A SQL statement cannot invoke SQLCODE or SQLERRM. To use their values
in a SQL statement, assign them to local variables first
Also,
Oracle recommends using DBMS_UTILITY.FORMAT_ERROR_STACK except when using the FORALL statement with its SAVE EXCEPTIONS clause
So, for SQLCODE or SQLERRM, you should assign them into variables and use them.
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION
WHEN OTHERS THEN
v_errcode := SQLCODE;
v_errmsg := SQLERRM;
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) --change your table name
values (ERROR_MSG_ID_SEQ.NEXTVAL,
v_errcode||':'||v_errmsg);
END;
/
Preferably use insert like this instead, as per Oracle's recommendation.
insert into ERROR_TABLE (ERROR_MSG_ID,ERROR_MSG) values (ERROR_MSG_ID_SEQ.NEXTVAL,
DBMS_UTILITY.FORMAT_ERROR_STACK);
Demo
Technically what others are suggesting is correct: the "insert" operation executed in the "exception when others" block will actually insert a new row in the log table.
the problem is that such insert statement will be part of the same transaction of the main procedure and, since you had an error while executing it, you are very likely to rollback that transaction, and this will rollback also the insert in your log table
I suppose the problem you are facing is not that you aren't successfully logging the error message: it is that you are rolling it back immediately afterwards, along with all the other writes you did in the same transaction.
Oracle gives you a way of executing code in a SEPARATE transaction, by using "autonomous transaction" procedures.
you need to create such a procedure:
create or replace procedure Write_Error_log(
arg_error_code number,
arg_error_msg varchar2,
arg_error_backtrace varchar2) is
PRAGMA AUTONOMOUS_TRANSACTION;
begin
INSERT INTO error_msg (
error_msg_id,
error_code,
error_msg,
error_stack)
VALUES (
error_msg_id_seq.NEXTVAL,
arg_error_code,
arg_error_msg,
arg_error_backtrace);
commit; -- you have to commit or rollback always, before exiting a
-- pragma autonomous_transaction procedure
end;
What this procedure does is to write a new record in the log table using a totally separate and independent transaction: the data will stay in the log table even if you execute a roll back in your calling procedure. You can also use such a procedure to create a generic log (not only errors).
All you have to do now is to call the procedure above whenever you need to log something, so your code becomes:
DECLARE
v_errcode NUMBER;
v_errmsg VARCHAR2(1000);
BEGIN
--some other statements that may raise exception.
EXCEPTION WHEN OTHERS THEN
Write_Error_log(SQLCODE, SQLERRM, dbms_utility.format_error_backtrace);
END;
/
P.S: there might be some typos in my code: I can't test it right now since I can't reach an oracle server in this moment.

What will be the ORA code if the Stored Procedure executes nothing?

I have a Stored Procedures which I am calling from Java code. There is an IF block and that IF condition is false the procedure executes nothing. So what will be the ORA code in this case?
The ORA code will be ORA-00000: normal successful completion because no errors occurred.
If you want a different outcome you need to code something specific. What you do depends on the business rules you're enforcing. Perhaps you need to raise an exception? This example tests whether a parameter is populated and hurls an exception if it isn't:
if p_str is null then
raise_application_error(-20000, 'parameter P_STR must be populated');
else
....
end if;
In this scenario the ORA code will be ORA-20000. Oracle reserves error numbers -20999 to -20000 for our own use.
"what if update is running with no change in table"
Same thing. Anything which does not hurl an exception is a successful completion. In this case we can test whether an update changed anything with the sql%rowcount value:
update your_table
set whatever = p_str
where id = p_id;
if sql%rowcount = 0 then
raise_application_error(-20001, 'No rows in YOUR_TABLE match ID = '||p_id);
end if;
The value returned by a call to SQLCODE (the "ORA code" you mention) is always going to be zero unless the function is called from within an exception handler.

Not able to continue loop after exception in Oracle stored procedure

I am trying to return list of number from a stored procedure. My stored procedure gets stopped in for loop when exception occur. I have added exception clause and control is going inside the exception clause to continue the loop but still no luck.
How to continue the loop when exception occur? Thanks.
CREATE OR REPLACE PROCEDURE getNumber(l_list IN CUSTOMLIST, l_output OUT NUMLIST)
IS
n_num varchar2(5);
BEGIN
l_output := NUMLIST();
FOR i IN l_list.FIRST .. l_list.LAST LOOP
l_output.EXTEND(l_list.LAST);
BEGIN
SELECT NUM into n_num
FROM sometable WHERE some condition;
l_output(i) := n_num;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('TRUE');
CONTINUE;
END;
END LOOP;
END;
Maybe you should handle other exceptions too.
Try with adding
WHEN OTHERS THEN
[statements]
The code I posted in question is working when I restart the SQL developer. Something was wrong at SQL developer level when I copy paste the EXCEPTION clause. But when I type the EXCEPTION clause it's started showing be no_data_found option and that one I selected and it worked.

PL/SQL check SQL excution if it went OK or not?

I have a few SQL (Select/Update/Insert) syntax that I will run inside PL/SQL one after another
is there any way to check if each syntax completed correctly and if there is some error it will not halt the whole PL/SQL, it will just return "OK" or "Not OK" to a variable so I can use it with IF?
UPDATE
I came up with this function, but it dose not seems to work, it returns 0 all time!
create or replace
FUNCTION EXECUTE_SQL(
V_SQL IN VARCHAR2 )
RETURN NUMBER
AS
V_RESULTS NUMBER := 1;
BEGIN
BEGIN
EXECUTE IMMEDIATE V_SQL;
EXCEPTION
WHEN OTHERS THEN
-- the following line is just for debugging!
dbms_output.put_line(SQLERRM);
V_RESULTS:= 0;
END;
RETURN V_RESULTS;
END EXECUTE_SQL;
what is wrong wit it (if any)!
cheers
if sql%rowcount > 0 then
-- insert or update statement affected sql%rowcount rows
end if;
As for the correct syntax: if the syntax is wrong, it won't even compile. If there's a data consistency error (such as divide by 0 error, or primary key violation) an exception will be thrown. Such exception can be caught in exception handlers
In the exception handler, you can then check sqlerrm for more details:
begin
update t set x = ...
exception when others then
dbms_output.put_line(SQLERRM);
end;
There are also a few predefined exceptions that you can check on:
begin
update t set x = ...
exception
when DUP_VAL_ON_INDEX
-- primary key or unique key violation
when OTHERS
-- other kind of exception
end;
If the syntax is not correct the entire block will be invalid, so you'll not be able to run it.
If you want to run all statements, despite that one can raise an exception, you can:
BEGIN
BEGIN
statement1;
EXCEPTION
when exception1 then
some commands or null;
when exception2 then
some commands or null;
END;
BEGIN
statement2;
EXCEPTION
when exception3 then
some commands or null;
when exception4 then
some commands or null;
END;
etc.
END;
Write show errors
begin
update t set x = ...
exception
when DUP_VAL_ON_INDEX
-- primary key or unique key violation
when OTHERS
-- other kind of exception
end;
/
show errors
It will show errors if any.

Resources