Oracle pl/sql Procedure to update table - exception handling - oracle

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;
/

Related

PL/SQL : Ignoring -942 EXCPETION issues - still bombs on PACKAGE Compilation

I have a Procedure within a Package that I create a "Temporary" table for processing. I wrote code to IGNORE the -942 error because at the end of the Procedure I drop the table. Therefore when it runs again, the table wouldn't exist and it needs to ignore that error.
However, when I compile the code (without the table present) it still bombs on me even with the EXCEPTION logic claiming the "table does not exist" which is true, but I don't care.
What am I doing wrong?
--Temp Table Setup
V_TEMP_COUNT INT;
NO_SUCH_TABLE EXCEPTION;
PRAGMA EXCEPTION_INIT (NO_SUCH_TABLE, -942);
--Declare Record Types
REC_P170201 CUR_P170201%ROWTYPE;
BEGIN --[SP_LOAD_A_DEPARTMENT_PVSS]
------------------------------------------------------------------------------------------
--PART P17.01 INITIALIZE --
------------------------------------------------------------------------------------------
BEGIN
SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
EXCEPTION
WHEN NO_SUCH_TABLE THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
-- other stuff
END SP_LOAD_A_DEPARTMENT_PVSS;
The following block of code will not raise the NO_SUCH_TABLE error when there is no table named F_DEPARTMENT_ADT
BEGIN
SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
EXCEPTION
WHEN NO_SUCH_TABLE THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
That is because the query is asking the view USER_TABLES if there is a row whose table_name column value is UPPER('F_DEPARTMENT_DAT'). Instead of a NO_SUCH_TABLE error, the error you will get is a no data found err (ORA-1403).
You cannot catch compilation errors. One way to handle the none existence of a table is to perform the SELECT-Statement as dynamic sql, so it does get compiled on runtime:
EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM USER_TABLES WHERE TABLE_NAME = UPPER(''F_DEPARTMENT_ADT'')'
INTO V_TEMP_COUNT;
But I think the code you posted is not the one that throws your error at compile time. It might be the declaration of REC_P170201. So in this case I would recommend you, to create your own record type with only the columns you need in your procedure and then just select the columns you need in the dynamic sql.
SELECT COUNT (*) INTO V_TEMP_COUNT
FROM USER_TABLES
WHERE TABLE_NAME = UPPER('F_DEPARTMENT_ADT');
Is not going to fail if F_DEPARTMENT_ADT does not exist. It will either return a value into V_TEMP_COUNT of 0 or 1. You will can use that to make decisions in the rest of your code.
If you need your code to actually use your table then it will need to be done in dynamic SQL so that the compilation of your PL/SQL is not dependent on the compilation of your SQL (which will sometimes fail).
Saying that, I 100% agree with EdStevens: you're after a global temporary table here.

Not handle no_data_found

I having emp table, in that there is no records. I used those table in SQL and PL/SQL block
SQL> BEGIN
2 FOR i IN (SELECT * FROM emp WHERE 1=2) -- emp table having no data
3 LOOP
4 dbms_output.put_line('Done');
5 END LOOP;
6 EXCEPTION
7 WHEN no_data_found THEN
8 dbms_output.put_line('No such value');
9 END;
10 /
PL/SQL procedure successfully completed. -- it shows completed, instead of handling no_data_found
SQL> SELECT * FROM emp
2 WHERE 1=2;
no rows selected -- but here it show no rows selected
In pl/sql block, instead of handling no_data_found it shows pl/sql successfully completed
But in SQL statement it shows no rows selected.
I want to know what is actually happened in plsql and sql statement?
The code between the LOOP and END LOOP clauses is executed once per row in the cursor. Your cursor returns zero rows so it never gets executed.
The cursor loops gracefully handles the empty result by design, because coming to the end of the result set is expected, even when it is zero rows. So it does not hurls the NO_DATA_FOUND exception.
That's why you see no messages.
On the other hand the SQL statement expects a query to return rows and raises no data found when none are returned. However, you are executing the SQL statement in a client - SQL*Plus in this case - which handles this outcome and displays a message.
If you want your PL/SQL to exhibit the same behaviour, don't use a cursor loop:
declare
l_emp_rec emp%rowtype;
begin
select *
into l_emp_rec
from emp
where 1 = 2; -- emp table having no data
dbms_output.put_line('Done');
exception
when no_data_found then
dbms_output.put_line('No such value');
end;
/

Function returns null instead of expected value

When I call my function for a value that does not exist in the table:
SELECT pacjent_usun_PK_ERROR(5) from dual
(PESEL='5' does not exist in my table)
The function returns NULL.
But when I pass a valid value:
SELECT pacjent_usun_PK_ERROR(1) from dual
(PESEL='1' exists in my table)
The function returns 1.
Here is my function:
CREATE OR REPLACE FUNCTION pacjent_usun_PK_ERROR
( PES IN NUMBER )
RETURN NUMBER
IS
ILE NUMBER;
ZMIENNA NUMBER;
BEGIN
SELECT PACJENTID INTO ZMIENNA FROM PACJENT WHERE PESEL=PES;
SELECT COUNT(PRZYJECIEID) INTO ILE FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=ZMIENNA and rownum=1;
RETURN ILE;
END;
When I test my last SELECT from my function, with manual inserted PRZYJECIEID
(PACJENTID='1111' does not exist in my table)
SELECT COUNT(PRZYJECIEID) FROM PRZYJECIE_NA_ODDZIAL WHERE PACJENTID='1111' and rownum=1;
Result is: 0
and testing with PACJENTID='1' (exist in my table)
Result is: 1
I am trying to understand why the function returns NULL instead of 0 when there are no rows.
I am using Oracle SQL Developer
I don't know what causes that behavior; if I can figure it out I will post again.
In any case, the following version should work. I created a similar function using the tables EMP and DEPT in the standard (Oracle) SCOTT schema, and the change I show below worked in that setting. Essentially I eliminate the intermediate variable; I do everything in a single query and assignment.
create or replace FUNCTION pacjent_usun_PK_ERROR
(PES IN NUMBER)
RETURN NUMBER
IS
ILE NUMBER;
BEGIN
SELECT COUNT(PRZYJECIEID) INTO ILE
FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=(SELECT PACJENTID FROM PACJENT WHERE PESEL=PES)
and rownum=1;
RETURN ILE;
end;
Added: To clarify... if select into... returns no rows, PL/SQL should raise an exception, specifically the NO_DATA_FOUND exception. The mystery that is worth investigating is why PL/SQL does not raise this exception in this case.
Final edit - it appears this has always been the behavior in PL/SQL functions, as opposed to PL/SQL procedures. NO_DATA_FOUND is an exception (it can be handled in the usual manner) but it is not raised. See this old discussion on AskTOM: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:10321465390114
This means that when the first select into returns no data, the function is in an unhandled exception state. This is why the second select into doesn't result in a count of 0 - execution doesn't even get that far.
This also means that you may keep your function code exactly as it is, but you need to add an exception handling block:
exception
when no_data_found then return 0;
right before end;
I tested this on my "mock-up" of your function (in the SCOTT schema) and it works.

RAISE_APPLICATION_ERROR doesn't return the message

IF l_value = 'FALSE' THEN
RAISE_APPLICATION_ERROR(-20299, 'some error message');
END IF;
This is part of table trigger. It should return me a error number and message, but when alert pops out it returns only message number. No 'some error message'. Whats wrong
Maybe the name RAISE_APPLICATION_ERROR is misleading for you. It will not pop up something onto your GUI. That you program yourself depending on what client you are using. Put you can use RAISE_APPLICATION_ERROR to create your own SQL errors on which you act upon.
Example
-- a example table
create table mytest (col_a number, col_b char(20));
-- a example trigger
CREATE OR REPLACE TRIGGER mytest_before
BEFORE UPDATE
ON mytest
FOR EACH ROW
DECLARE
BEGIN
if :new.col_a < 0 then
RAISE_APPLICATION_ERROR(-20299, 'negative value not allowed for column A');
end if;
END;
insert into mytest values (1,'hallo');
set serveroutput on
DECLARE
negative_value EXCEPTION; -- declare exception
PRAGMA EXCEPTION_INIT (negative_value, -20299); -- assign error code to exception
BEGIN
update mytest set col_a = -1 where col_b = 'hallo';
EXCEPTION
WHEN negative_value THEN -- handle exception
-- do whatever you need to do to bring the error to the user
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
END;
/
The above will bring you the output in SQL*Plus or SQL Developer of that sort.
table MYTEST created.
TRIGGER mytest_before compiled
1 rows inserted.
anonymous block completed
ORA-20299: negative value not allowed for column A
ORA-06512: at "DEMO.MYTEST_BEFORE", line 4
ORA-04088: error during execution of trigger 'DEMO.MYTEST_BEFORE
Instead of DBMS_OUTPUT.PUT_LINE you can do whatever you need to do to show the user whatever you want him to show.
The alert in your form has been raised by some trigger code on your form. Have a look at your ON-ERROR trigger - what code does it have?
You may need to augment it to show DBMS_ERROR_TEXT in the alert.

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

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

Resources