I have a stored proc that I call from an external program to upsert data. The way the proc is setup is that it inserts into the table and on exception 'dup_val_on_index' it does an update instead.
How could I get this error if I'm catching the 'dup_val_on_index' exception and doing an update inside of it? I assume a PK would catch this specific exception since it creates a duplicate?
Yes, the dup_val_on_index named error would be raised for a primary key constraint error.
Perhaps your update is the code that is causing the error to be raised.
Why don't you just use MERGE in the first place?
In case you really don't want to use merge, just build an inner pl/sql-block and catch the exception there:
create or replace procedure proc(param1 varchar2)
as
some_var varchar2(50);
begin
-- do some things
begin
insert on your table;
exception when dup_val_on_index then
update on your table;
end;
--proceed with some more things
end proc;
Related
guys:
I wonder if there is a way to write a trigger in Oracle to do both things: saving data to a log table and raising a user defined exception as well?
I am trying to figure out a strange error on my team's database, which causes data inconsistency per business logic. Multiple team's application can access this database. So I wrote a trigger to monitor certain column in a table which causes the problem. I want to save data such as user ID, saving time etc. to a log table if value is incorrect, but I also want to raise exception to attract attention. However, whenever my trigger raises the user defined exception, saving data to log table is not finished. Can anyone give a suggestion about it? Thank you in advance.
You can write a logging function that uses an autonomous transaction
create or replace procedure log_autonomous( p_log_message in varchar2,
p_other_parameters... )
as
pragma autonomous_transaction;
begin
insert into log_table ...
commit;
end;
and then call that logging function from your trigger
create or replace trigger my_trigger
before insert or update on some_table
for each row
declare
begin
if( some_bad_thing )
then
log_autonomous( 'Some message', ... );
raise_application_error( -20001, 'Some error' );
end if;
end;
The log_table message will be preserved because it was inserted in a separate (autonomous) transaction. The triggering transaction will be rolled back because the trigger raises an exception.
I want to create a trigger that manages 2 magazines, 'Elle' and 'Time'.
So if the user tries to insert a new record in the magazine table that does not match these two, the latter should not be retained.
I already have all the tables and stuff ready there's no problem in that.
The problem is that I can't figure out a way to do this properly, a friend said that this code of mine interacts with lines only, and that I need a code for the whole table and recommended that I use a Cursor.
Here's my code:
Create or replace trigger TMag
After INSERT on Magazine
FOR EACH ROW
DECLARE
e EXCEPTION;
BEGIN
IF :new.mag_nom!= 'Elle' or :new.mag_nom!= 'Time' THEN
Delete from Magazine where ISBN=:new.ISBN;
raise e;
END IF;
exception
when e then dbms_output.put_line('nom mag incorrecte');
END;
Here's a look on my tables:
CLIENT(CIN, CL_NOM, CL_ADDR, CL_VILLE, EMAIL, CONTACT_NUM);
MAGAZINE(ISBN, MAG_NOM, PRIX_Mois);
ABONNEMENT(AB_ID, #ISBN, #IN_ID, Months);
INVOICE(IN_ID, #CIN, dateI, State) ;
Thanks in advance
I think you don't need a trigger, rather resolve the problem internally by creating a CHECK Constraint such as
ALTER TABLE Magazine
ADD CONSTRAINT correcte_mag_nom
CHECK (mag_nom IN ('Elle', 'Time'));
If the provided value is not eligible for mag_nom column, then it will hurl as
ORA-02290 check constrain (<schema>.CORRECTE_MAG_NUM) violated
You cannot use DML (your delete) on the same table that the trigger belongs to. Change your trigger to fire before the insert, and just raise the error to prevent the insert from happening in the first place.
Note that you must also declare a code number for the exception, and your logic should be if new.name!='Elle' AND new.name!='Time'. If you use "OR" then your trigger will not allow any inserts on the table at all...
CREATE OR REPLACE TRIGGER TMAG
BEFORE INSERT ON MAGAZINE
FOR EACH ROW
DECLARE
e_bad_mag_nom EXCEPTION;
PRAGMA EXCEPTION_INIT (e_bad_mag_nom, -20001)
BEGIN
IF :new.mag_nom!= 'Elle' AND :new.mag_nom!= 'Time' THEN
raise e_bad_mag_nom;
END IF;
EXCEPTION
when e_bad_mag_nom then dbms_output.put_line('nom mag incorrecte');
END;
See here for references:
https://www.oracletutorial.com/plsql-tutorial/plsql-raise/
How to raise an Exception inside a Trigger? Is there a way to do this?
Suppose I have the following trigger :
create or replace trigger trigInsertSaloane before insert on saloane
for each row
declare
myExcp exception;
pragma exception_init (myExcp,-20005);
begin
for i in (select * from saloane) loop
if(:new.numar_salon=i.numar_salon) and (trim(upper(:new.nume_sectie))=trim(upper(i.nume_sectie))) then
raise myExcp;
end if;
end loop;
exception when myExcp then dbms_output.put_line('Record exists');
end;
/
All I want is to not insert the row if exception is raised, so something like rollback. In my case if exception is raised and caught, the line is also inserted. I don`t want that. Also I want to make that in a pretty way, by showing up a message and not getting any errors.How to make it?
ok.. a few points.
1) you need to raise an exception from a trigger for the insert will fail and not be inserted. so either do not catch your exception or re RAISE it again.
2) using dbms_output.put_line() will only display a message if the user/client has it turned on.
3) you do not need to loop over your cursor. adding a where clause is more efficient
4) your trigger will not work.. it will throw
ORA-04091: table SALOANE is mutating, trigger/function may not see it if you insert more then 1 row at a time. (try insert into saloane select * from saloane )
5) it may just be your example.. it looks like you could more simply use a unique constraint on the given columns to enforce this requirement.
Adding few more points as mentined in other answer.
Since you are handling the EXCEPTION (PRETTY WELL :p ) in the
Trigger so ideally Trigger event execution is successful so
INSERT/UPDATE/DELETE will happen.
What you need here is to RAISE some kind of exception which will
force the Trigger to fails with the Exception so
in this you need to have RAISE_APPLICATION_ERROR condition to
handle this.
As mentioned above UNIQUE Key constraint will be your best friend to
work in this case.
I have just created an error_log table to log any errors that a procedure/package may run into. the error log table is as follows
CREATE TABLE APMS.ERROR_LOG
(
ORA_ERR_TMSP TIMESTAMP(6) NOT NULL,
ORA_ERR_NUMBER NUMBER(5) NOT NULL,
ORA_ERR_MSG VARCHAR2(200 CHAR) NOT NULL,
ORA_ERR_TXT VARCHAR2(500 CHAR) NOT NULL,
ORA_ERROR_OPTYP CHAR(1 CHAR) NOT NULL,
PROGRAM_NAME VARCHAR2(50 CHAR) NOT NULL,
ORA_IN_OUT VARCHAR2(500 CHAR) NOT NULL
)
I created a mock table an purposely induced the ORA-06512 error by inserting a character string in a timestamp field using a procedure. here is the procedure which inserts dummy data into my mock table with the purpose of inducing an error and logging it into my error_log table.
create or replace procedure test_procedure as
begin
insert into mockdata values ('data1','mockname','mockcity');
commit;
exception
when others then
insert into error_log
values
(ora_err_tmsp,ora_err_number,ora_err_msg,ora_err_txt,ora_err_optyp,program_name,ora_in_out);
values
(current_timestamp,sqlcode,'sqlerrm', 'detailed information','i','test_procedure','i');
commit;
end;
/
when I attempt to run/compile it I get the following error.
[Error] PLS-00103 (9: 1): PLS-00103: Encountered the symbol "VALUES" when expecting one of the following:
( begin case declare end exit for goto if loop mod null
pragma raise return select update when while with
<an
I am a complete beginner at pl/sql so any help is appreciated.
Before getting into your code: if you are planning to write that kind of logging in every procedure you write, maybe you should simply avoid doing it and keep at reach of your hand this trigger and enable it just when you need it: this trigger might be handy in a testing environment or if you are in deep trouble and you need to collect all possible error that happen in a given time:
CREATE OR REPLACE TRIGGER log_all_errors AFTER SERVERERROR ON DATABASE
declare
procedure log_error is
pragma autonomous_transaction;
begin
INSERT INTO error_log
VALUES (SYSDATE, SYS.LOGIN_USER, SYS.INSTANCE_NUM, SYS.DATABASE_NAME, DBMS_UTILITY.FORMAT_ERROR_STACK);
commit;
end;
BEGIN
log_error;
END log_all_errors ;
this trigger will log ALL errors that will happen in your system. it is not a good idea to keep it enabled permanently: you can use it for doing some troubleshooting in emergency and you might want adjust its code to log only some kind of errors, but it is a starting point.
if you do some research you will discover that you can even log the SQL text of the statement that caused the error.
keep in mind that there are some errors that are not catched by this trigger (i am referring to NO_DATA_FOUND and TOO_MANY_ROWS errors): there is simply too much code that normally uses these exceptions during its normal lifetime, so the guys at oracle decided not to trap these errors.
Now let's get back to your code: your approach, as other have pointed out, is not neutral to the normal execution of the program:
you are logging the error, right, but you are hiding the error to the program calling your procedure. It is not a good idea: the calling program surely would like to know that something wrong has happened and it would like to issue a "rollback" in order to cancel all previous work, instead of continuing like nothing wrong did happen.
Not only you are hiding the error, but you are also issuing a commit whenever an error happens: this is most likely the opposite of what your calling program would do in case of an error: surely the caller would issue a rollback. Instead you are making permanent all the partial work done until the error happened.
as a side note: the above problem of the unwanted commit even when no errors happen within your procedure: you are committing also when no errors are raised. this means that if the calling program enconuters some problems after having called your procedure, it won't be able to rollback anything it did before calling your code.
It generally is a bad practice to commit or rollback inside a procedure, unless you are more than sure that such procedure will never be called as part of a bigger transaction (for example: it is ok to commit if your procedure is the main body of a database job)
So: how can you write an error log without interfering with the calling program? you do it by writing the log in a dedicted autonomous transaction" procedure: whatever you do in procedure marked as "authonomous transaction" runs in its own separate transaction: you commit or rollback only what happens inside it.
(As you can see, I used an autonomous transaction also in the above trigger)
your code would be more "kind", to the calling program, if written this way:
CREATE OR REPLACE PROCEDURE test_procedure AS
procedure write_error_log (errcode number, errstr varchar2) is
pragma autonomous_transaction;
-- whatever we do in this procedure stays in its own new private transaction
begin
INSERT INTO error_log
(ora_err_tmsp,
ora_err_number,
ora_err_msg,
ora_err_txt,
ora_err_optyp,
program_name,
ora_in_out)
values (CURRENT_TIMESTAMP,
errcode,
errstr,
'detailed information',
'i',
'test_procedure',
'i');
COMMIT; -- this commit does not interfere with the caller's transaction.
end write_error_log;
BEGIN
INSERT INTO mockdata
VALUES ('data1', 'mockname', 'mockcity');
--here you were committing: you'd better not do it:
-- you are making impossible for the calling program to roll back
-- COMMIT;
exception when others then
write_error_log(sqlcode,sqlerrm);
raise; -- you should NOT hide the exception to the caller program, so you'd better re-raise it!
END test_procedure;
Try this:
CREATE OR REPLACE PROCEDURE test_procedure
AS
BEGIN
INSERT INTO mockdata
VALUES ('data1', 'mockname', 'mockcity');
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
INSERT INTO error_log
(ora_err_tmsp,
ora_err_number,
ora_err_msg,
ora_err_txt,
ora_err_optyp,
program_name,
ora_in_out)
values (CURRENT_TIMESTAMP,
SQLCODE,
sqlerrm,
'detailed information',
'i',
'test_procedure',
'i');
COMMIT;
END;
/
I am writing the below queries in oracle:
DBMS_OUTPUT.....'Ashish'
Select col1 into val1 from tab_1
DBMS_OUTPUT.....'Ubale'
when I run this procedure I get the output as "Ashish" only why?
also what will be the value of v_val1 variable
Note: the table does not contain any records
Since the table is empty, the "select into" statement will raise the NO_DATA_FOUND exception. That's why you don't get the second message. val1 will have the same value as before the select - i.e. null if you didn't previously assign a value.
The fact that you don't know you got the NO_DATA_FOUND exception suggests that you have made one of the biggest errors PL/SQL developers ever make:
EXCEPTION
-- Never do this in real code!!!
WHEN OTHERS THEN NULL;
END;
Did you get error? If the table doesn't have rows in it. You might get no_data_found exception.
By the way, where is your entire code?