I am writing a trigger to check if an employee is allocated to two flights that overlap.
It would be something like:
select flight_id from flight
where arrival_time > :new.departure_time
and departure_time < :new.arrival_time;
if flight_id is empty
[do nothing]
if flight_id exists
[raise application error]
Can anyone help me out in how I would have to code the conditional logic there? This is my first time working with oracle (university coursework).
SELECT ... INTO is your friend. It will raise an exception, you catch it, do nothing, otherwise and raise your own error.
select flight_id into v
from flight
where arrival_time > :new.departure_time
and departure_time < :new.arrival_time;
raise_application_error(-20000, 'not good');
exception
when NO_DATA_FOUND
then return 1
when others
then raise_application_error(-20011,'really good');
DECLARE
V_some_varible NUMBER;
BEGIN
Seclect 1 into v_some_varible from dual where 1 = 0;
EXCEPTION -- exception handlers begin
WHEN NO_DATA_FOUND THEN
INSERT INTO errors (message) VALUES ('no data found');
WHEN OTHERS THEN -- handles all other errors
ROLLBACK;
END;
Note the 1 = 0 to force the no data found exception. This example should work in any Oracle database with the exception of the insert into an errors table.
Related
I am migrating stored procedures to pl/sql blocks, and I have little knowledge in error handling in oracle and nothing in sybase can you help me.
example: sql SYBASE
DELETE table_1
WHERE N=0
SELECT #myrowcount = ##rowcount, #myerror = ##error, #mystat = ##sqlstatus
if (#myerror <> 0)
begin
raiserror 40900 "Error: When Generating Exception List #table_1 (error= %1!)", #mystat
select #cod_err= 1
return #cod_err
end
Edit: sql oracle i dont know if this is right
begin
DELETE table_1
WHERE N=0
EXCEPTION WHEN OTHERS THEN
SWV_error := SQLCODE;
end;
v_mi_error := SWV_error;
if v_mi_error != 0 then
RAISE_APPLICATION_ERROR(-40900,'Error: When Generating Exception List table_1');
return;
end if;
Which error do you expect for delete? It'll either delete some rows, or won't. If table (or column) doesn't exist, code wouldn't even compile so you wouldn't reach runtime error.
Anyway: in Oracle, it looks like this:
begin
delete table_1 where n = 0;
exception
when others then
raise_application_error(-20000, 'Error: ' || sqlerrm);
end;
/
others is exception which handles various things; you don't really care which error it is, as could be any error. Oracle reserved error codes from -20000 to -20999 for us, developers so you have to pick one of these (which means that -40900 won't work).
sqlerrm is error message (its description). If you want, you can get its code via sqlcode.
Example which shows how it actually works (not with delete, though) is query that fetches employee name for non-existent employee number. That raises predefined no_data_found error (whose code is -01403) so you could handle it, directly, or - as my previous example shows - use others.
SQL> declare
2 l_name varchar2(10);
3 begin
4 select ename
5 into l_name
6 from emp
7 where empno = -1;
8 exception
9 when others then
10 raise_application_error(-20000, 'Error: ' || sqlcode ||': '|| sqlerrm);
11 end;
12 /
declare
*
ERROR at line 1:
ORA-20000: Error: 100: ORA-01403: no data found
ORA-06512: at line 10
SQL>
I am trying to insert 0 rows into a table that has a unique constraint and I am getting ORA-00001: unique constraint violated...
Below is the PL/SQL block I've used to hopefully capture the issue well
declare
l_cnt number;
begin
set transaction isolation level serializable;
select count(*) into l_cnt from test_view;
dbms_output.put_line('count = ' || to_char(l_cnt));
insert into <table>(<columns>)
select <columns> from test_view
log errors ('run1')
reject limit 0
;
dbms_output.put_line('success');
exception
when others then
dbms_output.put_line('ERRROR!');
dbms_output.put_line(sqlerrm);
rollback;
end;
/
This is the output
count = 0
ERRROR!
ORA-00001: unique constraint (<SCHEMA>.<CONSTRAINT>) violated
And sure enough, there is a record in the ERR$_<table> table...
If I add where 1 = 0 to the in the insert statement, everything works, nothing is inserted.
I still don't believe what I am seeing :)
Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
Update 1
Below sample without using count(*) on the view as this could lead to a different query plan that selecting all required values for the insert.
declare
l_cnt number;
begin
set transaction isolation level serializable;
insert into testx_table
select * from testx_view d;
select count(*) into l_cnt from testx_table;
dbms_output.put_line('count = ' || to_char(l_cnt));
insert into <table>(<columns>)
select * from testx_view d
log errors ('run2')
reject limit 0;
dbms_output.put_line('success');
exception
when others then
dbms_output.put_line('ERRROR!');
dbms_output.put_line(sqlerrm);
rollback;
end;
/
UPDATE 2
I was able to reproduce the behaviour.
CREATE TABLE A(ID NUMBER PRIMARY KEY)
/
CREATE FUNCTION F(ID_ NUMBER) RETURN NUMBER
AS
L_ID NUMBER;
BEGIN
SELECT ID INTO L_ID FROM A WHERE ID = ID_;
RETURN L_ID;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
/
BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG('A');
END;
/
BEGIN
INSERT INTO A VALUES (1);
INSERT INTO A SELECT 1 FROM DUAL WHERE F(1) IS NULL
LOG ERRORS INTO ERR$_A;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/
SELECT * FROM ERR$_A
/
sqlfiddle
And it all boils down to querying table that is being modified from within the function. The functions throws the ORA-04091: table A is mutating, trigger/function may not see it but the code catches all exceptions and returns null.
Obviously, selecting from table that's mutating is a nono and must be fixed.
And I am quite angry as I cannot count the number of times I told my collegues to stop using exception when others then return null. This is again an example where it completely masked the issue and I've spent the whole day deubgging it.
Select returned 0 rows, that was ok.
However when inserting rows to table, we were actually querying the same table through a function. The function returned ORA-04091: table A is mutating, trigger/function may not see it but it was catched in an exception block and null was returned. This caused the query to return rows...
Never use exception when others then return null!!!
Please let me know whether it is possible to catch multiple exception at same time in oracle. Not like 1 user defined and 1 is oracle default .I need to catch multiple user defined exception at same time . Kindly let me know how to do .
Thank you !
Certainly, there is - if I understood the question correctly. It is called WHEN OTHERS. Though, people usually misuse it, especially when they use
exception
when others then
null;
end;
as it successfully hides any errors that might appear. WHEN OTHERS is OK during development process, but might be really bad in production, especially if it doesn't contain raise.
Yes, you can do what you want from "When Others" as indicated by #Littlefoot or bulk processing errors (not covered here). But additionally you can have an OR condition exception name clause on the WHEN . It's not very useful as generally requires more code the 2 separate WHEN condition, but it is valid syntax. The following demonstrates various error definition methods and exception processing.
create table except_demo ( id integer, col1 varchar2(20));
insert into except_demo (id, col1)
select 5,'OK' from dual union all
select 6,'Too Many' from dual union all
select 6,'Still' from dual;
select id, count(*) from except_demo group by id;
create or replace procedure catcher(which_in integer, select_in boolean default False)
is
e_user_1 exception;
e_user_2 exception;
invalid_which_range exception;
appErr_inactive_acct exception;
sample_ora_error exception;
pragma exception_init (sample_ora_error, -00060);
rae exception;
rae_num constant integer := -20100;
pragma exception_init (rae, -20100);
col1_value except_demo.col1%type;
begin
dbms_output.put( 'Enter catcher(' || which_in || ') Result=>');
if which_in > 8
then raise invalid_which_range;
end if ;
if select_in
then
select col1
into col1_value
from except_demo
where id = which_in;
dbms_output.put_line('Select Successful 1 row selected.');
else
case which_in
when 1 then raise e_user_1;
when 2 then raise e_user_2;
when 3 then raise appErr_inactive_acct;
when 4 then raise sample_ora_error;
else raise_application_error(rae_num, 'Which_In=' || which_in || ' invalid. Please specify number 1-7 only');
end case;
end if;
exception
when e_user_1
then dbms_output.put_line('Error e_user_1'); -- user error
when e_user_2
then dbms_output.put_line('Error e_user_2');
when no_data_found or too_many_rows
then dbms_output.put_line('Select except_demo where id=' || which_in ||'; Returned 0 or more than 1 row. Must return exactly 1.' ); -- oracle predefined error
when sample_ora_error
then dbms_output.put_line('Ora Error:: ' || sqlerrm ); -- oracle error NOT predefined
when appErr_inactive_acct
then dbms_output.put_line('Error Account id ' || which_in || ' is inactive.'); -- user error
when rae
then dbms_output.put_line(sqlerrm);
end catcher;
declare
do_select boolean;
begin
for i in 1..9
loop
do_select := (i between 5 and 7);
catcher(i,do_select);
end loop;
exception
when others
then
dbms_output.put_line('Error returned from catcher=>' || sqlerrm);
raise;
end ;
drop procedure catcher;
drop table except_demo;
In a live environment the dbms_output statement would be replaced writing the message and other information to a exception log table and NOT dbms_output.
I have a very minor disagreement with Littlefoot. I firmly believe that what ever is written in development, whether intended or not, will run in production. Too often it is the unintended that gets you into trouble. Therefore the example of a misused WHEN OTHERS is invalid even in development.
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;
/
I wrote this trigger to impede the database's writing
create or replace trigger spese_autorizzate_trg
before insert or update on spese
for each row
declare
boolean integer := 0;
voceSpesa tipospesa.descrizione%TYPE := NULL;
begin
select
descrizione into voceSpesa
from
tipospesa
where
id = :new.tipospesa
and approvazione = 'n';
if voceSPesa is NULL then
raise_application_error(-20000, 'La spesa '||voceSpesa||' non è rimborsabile');
end if;
end;
If the value of tipospesa is 4 or 5, the writing should be impeded
but when I insert a row like this
insert into spese(id, importo, tipospesa, data) values (4, 20, 3, TO_DATE('15-jul-18', 'DD-MON-RR'))
I have this error
Error report:
SQL Error: ORA-01403: no data found
ORA-06512: at "PARLAMENTO2018.SPESE_AUTORIZZATE_TRG", line 7
ORA-04088: error during execution of trigger
'PARLAMENTO2018.SPESE_AUTORIZZATE_TRG'
01403. 00000 - "no data found"
*Cause:
*Action:
and the writing isn't done. Why?
To me, it seems that you'd have to handle NO_DATA_FOUND, instead of handling possibility that DESCRIZIONE is NULL. Something like this:
create or replace trigger spese_autorizzate_trg
before insert or update on spese
for each row
declare
-- boolean integer := 0; -- Uh, no! Boolean is name of PL/SQL datatype;
-- don't use it as name of a variable
-- Besides, it is never used in your code.
voceSpesa tipospesa.descrizione%TYPE; -- No need to set it to NULL explicitly;
-- it is NULL anyway
begin
select descrizione
into voceSpesa
from tipospesa
where id = :new.tipospesa
and approvazione = 'n';
exception
when no_data_found then
raise_application_error(-20000, 'La spesa '||voceSpesa||' non è rimborsabile');
when too_many_rows then
-- what then? Do you know? Can that select return more than a single row? If so,
-- you should handle it
null;
end;
True, you could save some typing by using select max(descrizione) ..., but that's kind of tricky. If someone else inherits your code, will they know that you used MAX to avoid NO_DATA_FOUND, or whether you intentionally meant to select the largest value of that column? Therefore, I'd say that it is better to actually handle exceptions you expect and avoid any doubt.
The problem is that an INTO clause will fail if no rows are returned from the table, raising no_data_found
MAX or MIN may be used to give you a NULL in case there were no rows.
select
MAX(descrizione) into voceSpesa
from
tipospesa
where
id = :new.tipospesa
and approvazione = 'n';
Do remember that this will also have null in cases where the column descrizione itself is null. But, it purely depends on your requirement how you would like to handle that situation if it ever occurs.