Trigger for insert after - oracle

I need to insert a 'regist' in Historic data table , when I insert something in 'ALUGUER' the trigger should insert one row in 'HISTORICO':
My Trigger:
create or replace
trigger ADD_HISTORICO
AFTER INSERT
ON ALUGUER
FOR EACH ROW
DECLARE
cod_aluguer_p NUMBER(6,0);
cod_veiculo_p NUMBER(6,0);
BEGIN
SELECT ID_ALUGUER INTO cod_aluguer_p
FROM ALUGUER;
SELECT COD_VEICULO INTO cod_veiculo_p
FROM ALUGUER;
INSERT INTO HISTORICO(ID_ENTRADA,DESCRICAO,DATA_REGISTO,NOVO_VEICULO,NOVO_ALUGUER)
VALUES(SEQ_HISTORICO.nextval,'NOVA DESCRIÇÃO','21/11/2013',cod_veiculo_p,cod_aluguer_p);
END;
Error report:
SQL Error:
ORA-04091: BDDAD_DL1.ALUGUER table is mutating, trigger can
not read or modify
ORA-06512: at "BDDAD_DL1.ADD_HISTORICO", line 6
ORA-04088: error during execution of trigger 'BDDAD_DL1.ADD_HISTORICO'
04091. 00000 -. "Table% s% s is mutating, trigger / function may not see it"
* Cause: A trigger (or a user defined plsql function that is referenced in
            this statement) attempted to look at (or modify) a table que was
            in the middle of being modified by the statement Which fired it.
* Action: Rewrite the trigger (or function) so it does not read that table.

You are trying to read a record from the table that the trigger has fired on. This is a no-no. So, this code:
SELECT ID_ALUGUER INTO cod_aluguer_p
FROM ALUGUER;
SELECT COD_VEICULO INTO cod_veiculo_p
FROM ALUGUER;
is not allowed. Also, it makes no sense, as there is no WHERE clause on the select, so
all rows would be returned. Moot point, as you can't do this anyway. What you want to do is reference the :NEW values of the triggered row istead. Example:
cod_aluguer_p := :new.ID_ALUGUER ;
cod_veiculo_p L= :NEW.COD_VEICULO;
Further, you don't even needs those local variables, and just use :new directly.
INSERT INTO HISTORICO(ID_ENTRADA,DESCRICAO,DATA_REGISTO,NOVO_VEICULO,NOVO_ALUGUER)
VALUES(SEQ_HISTORICO.nextval,'NOVA DESCRIÇÃO','21/11/2013',:NEW.COD_VEICULO,:new.ID_ALUGUER );
I would recommend to read the Oracle docs on triggers

No SELECT is required, just refer to the :NEW values in the trigger. Also, you can use TRUNC(SYSDATE) to get the current date assuming that's what you intended:
create or replace
trigger ADD_HISTORICO
AFTER INSERT
ON ALUGUER
FOR EACH ROW
BEGIN
INSERT INTO HISTORICO(ID_ENTRADA,DESCRICAO,DATA_REGISTO,
NOVO_VEICULO,NOVO_ALUGUER)
VALUES(SEQ_HISTORICO.nextval,'NOVA DESCRIÇÃO',
TRUNC(SYSDATE),:NEW.COD_VEICULO ,:NEW.ID_ALUGUER );
END;

Related

ORACLE - After trigger is not passing the value to the procedure

I have a problem with a "after insert" trigger which won't pass the value to a stored procedure that i'm calling inside the trigger.
It works ok for update and also i should mention that i am querying the table in the procedure that i am calling.
It goes something like:
create or replace trigger test_trg
after insert or update on table1
for each row
begin
test_procedure(:new.value1);
end;
The procedure looks inside table1 and inserts in table2 hints about what should be added in table1 based on the current inserted/updated row.
Thanks in advance!!
I found it!
The problem was not the trigger, it was in the procedure. I was performing a "dirty read" based on the record ID that i was sending (the record id of a new row).
Even though my trigger is "after insert" it looks like my row can't be found by the procedure and i was basing all my operations on it.
Fixed it by adding more parameters to the procedure and passing :new values straight from the trigger for everything i needed.
A trigger transaction is depend on its statement transaction. Therefore you can not read uncommitted records. So you should transfer "test_procedure" codes into your trigger and use :New record values to insert data into table2.
Your code should look like this:
create or replace trigger test_trg
after insert or update on table1
for each row
begin
if inserting then
insert into table2(ID, F1, F2, ...)
values (NEW_ID_VALUE, :NEW.F1_VALUE, :NEW.F2_VALUE, ...);
elsif updating then
update table2
set F1 = :NEW.F1_VALUE
, F2 = :NEW.F2_VALUE
where TABLE1_ID_FK = :NEW.ID;
end if;
end;

ORA-04091 Mutating Table error during AFTER INSERT Trigger

I have the following AFTER INSERT trigger on a table called DM_USER_ROLE
create or replace TRIGGER "DM_USER_ROLE_T1"
AFTER
insert on "DM_USER_ROLE"
for each row
DECLARE
v_cert_enrolment_id number;
v_user_role_id number;
begin
v_cert_enrolment_id := "DM_CERTIFICATION_ENROLMEN_SEQ".nextval;
v_user_role_id := :new.USER_ROLE_ID;
/*
When a user is assigned a role, we create an enrolment record
in DM Certification record linked to this user/role combination.
We also insert into the DM_COURSE_ENROLMENT table the courses
associated with the certfication
*/
--FIRST AN ENROLMENT RECORD IS CREATED IN DM_CERTIFICATION_ENROLMENT
INSERT INTO DM_CERTIFICATION_ENROLMENT
(CERTIFICATION_ENROLMENT_ID, ALLOCATED_DT, DEADLINE_DATE, STATUS, USER_ROLE_ID)
VALUES
(
v_cert_enrolment_id,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled',
v_user_role_id
);
--COURSES LINKED TO THE CERTIFICATION ARE INSERTED INTO DM_COURSE_ENROLMENT
INSERT INTO DM_COURSE_ENROLMENT
(
CERTIFICATION_ENROLMENT_ID,
COURSE_ID,
ALLOCATED_DT,
DEADLINE_DT,
STATUS
)
SELECT v_cert_enrolment_id,
COURSE.COURSE_ID,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled'
FROM DM_CERTIFICATION_COURSE COURSE
WHERE CERTIFICATION_ID =
(
SELECT C.CERTIFICATION_ID FROM
DM_CERTIFICATION_ENROLMENT A,
DM_USER_ROLE B,
DM_ROLE_CERTIFICATION C
WHERE
A.USER_ROLE_ID = B.USER_ROLE_ID
AND
B.ROLE_ID = C.ROLE_ID
AND
A.CERTIFICATION_ENROLMENT_ID = v_cert_enrolment_id
);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM, 1, 2000));
end;
I need to populate 2 separate tables when an insert happens in this table, and I thought AFTER INSERT triggers avoided issues with mutating tables?
I am not sure what is causing it, perhaps the read in the second INSERT statement from DM_USER_ROLE, which is where this trigger is initiated...but I was under the impression AFTER INSERTs were safe to avoid mutations, as the update has already happened.
Error is:
ORA-04091: table AZLEARN_BACKUP.DM_USER_ROLE is mutating,
trigger/function may not see it
The first insert happens, the second one does not.
This article led me to believe AFTER triggers were safe.
http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm
-------UPDATE---------------
I changed it to do row by row insert using two parameterised cursors and it worked...still not sure what the error was:
create or replace TRIGGER "DM_USER_ROLE_T1"
AFTER
insert on "DM_USER_ROLE"
for each row
DECLARE
v_cert_enrolment_id number;
v_user_role_id number;
v_role_id number;
v_certification_id number;
cursor certs_for_role(p_role_id number) is
select * from DM_ROLE_CERTIFICATION where ROLE_ID = p_role_id;
r_certs_for_role certs_for_role%rowtype;
cursor courses_for_certs(p_cert_id number) is
select * from DM_CERTIFICATION_COURSE where CERTIFICATION_ID = p_cert_id;
r_courses_for_certs courses_for_certs%rowtype;
begin
v_user_role_id := :new.USER_ROLE_ID;
v_role_id := :new.ROLE_ID;
open certs_for_role(v_role_id);
loop
fetch certs_for_role into r_certs_for_role;
exit when certs_for_role%notfound;
v_cert_enrolment_id := "DM_CERTIFICATION_ENROLMEN_SEQ".nextval;
INSERT INTO DM_CERTIFICATION_ENROLMENT
(CERTIFICATION_ENROLMENT_ID, ALLOCATED_DT, DEADLINE_DATE, STATUS, USER_ROLE_ID, CERTIFICATION_ID)
VALUES
(
v_cert_enrolment_id,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled',
v_user_role_id,
r_certs_for_role.CERTIFICATION_ID
);
open courses_for_certs(r_certs_for_role.CERTIFICATION_ID);
loop
fetch courses_for_certs into r_courses_for_certs;
exit when courses_for_certs%notfound;
INSERT INTO DM_COURSE_ENROLMENT
(
CERTIFICATION_ENROLMENT_ID,
COURSE_ID,
ALLOCATED_DT,
DEADLINE_DT,
STATUS
)
VALUES
(
v_cert_enrolment_id,
r_courses_for_certs.COURSE_ID,
trunc(sysdate),
trunc(sysdate) + 60,
'Enrolled'
);
end loop;
close courses_for_certs;
end loop;
close certs_for_role;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQLERRM(-20299)));
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM, 1, 2000));
end;
The reason is simply you cannot select from table DM_USER_ROLE where the ROW LEVEL trigger is based on.
In your first solution you have a
SELECT ...
FROM DM_USER_ROLE ...
This is not allowed. Your second trigger does not select table DM_USER_ROLE thus it is working.
The advise in linked page is correct but misleading when they state 'Use an "after" or "instead of" trigger' - It should be more precisely 'Use an "after statement" or "instead of" trigger' . Oracle provides triggers based on following actions:
DML statements (INSERT, UPDATE, DELETE) on a particular table or view
DDL statements (CREATE or ALTER primarily)
Database events, such as logon/logoff, errors, or startup/shutdown
You have a DML trigger which can have different timing points:
Before the triggering statement executes
After the triggering statement executes
Before each row that the triggering statement affects
After each row that the triggering statement affects
Compound Trigger -> this one combines the four triggers listed above
INSTEAD OF Trigger (only for views)
Many people miss the difference between a statement level trigger and a row level trigger.
Row Level Triggers have keyword FOR EACH ROW and run for each row like the keyword implies. If you skip the FOR EACH ROW keyword, then the trigger is executed only once for each statement, no matter how many rows are affected by your INSERT/UPDATE/DELETE statement.
The most likely cause of a mutating table error is the misuse of triggers. Here is a typical example:
1.you insert a row in table A
2.a trigger on table A (for each row) executes a query on table A, for example to compute a summary column
3.Oracle throws an ORA-04091: table A is mutating, trigger/function may not see it
This is an expected and normal behaviour, Oracle wants to protect you from yourself since Oracle guarantees:
•(i) that each statement is atomic (i.e will either fail or succeed
completely)
•(ii) that each statement sees a consistent view of the data
Now in your trigger, when you do the second insert, it's doing a join on the DM_USER_ROLE table to fetch the records and that's the reason you face
ORA-04091: table AZLEARN_BACKUP.DM_USER_ROLE is mutating,
trigger/function may not see it

SQL Error: ORA-04091 while executing Trigger

I am trying to execute my trigger:
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete
AFTER DELETE ON CMS_VIDEO
FOR EACH ROW
DECLARE
CODE varchar2(60);
BEGIN
SELECT CODE INTO CODE
from CMS_VIDEO
WHERE CODE = :OLD.CODE;
IF CODE IS NOT NULL THEN
INSERT INTO ASSET_DELETE_INDEX(CODE,ASSET_TYPE,IW_VPATH,LANGUAGE,MODIFIED_DTE)
VALUES (CODE,'Video',:old.IW_VPATH,:old.CONTENT_LANGUAGE,sysdate);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO ASSET_DELETE_INDEX (CODE,ASSET_TYPE,IW_VPATH,LANGUAGE,MODIFIED_DTE)
VALUES (CODE,'Video',null,:old.CONTENT_LANGUAGE,sysdate);
COMMIT;
END Trg_video_bfr_delete;
/
But I am getting the following error while executing a delete command on the table
Error report -
SQL Error: ORA-04091: table LSDS.CMS_VIDEO is mutating, trigger/function may not see it
ORA-06512: at "LSDS.TRG_VIDEO_BFR_DELETE", line 6
ORA-04088: error during execution of trigger 'LSDS.TRG_VIDEO_BFR_DELETE'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Could anyone please help?
You have written trigger AFTER DELETE ON CMS_VIDEO. This trigger will fire when DELETE is performed from CMS_VIDEO table. So when CMS_VIDEO table is being modified, you cannot modify or query the same table in any of your triggers, procedures or functions.
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete
AFTER DELETE ON CMS_VIDEO
FOR EACH ROW
BEGIN
IF :old.CODE IS NOT NULL THEN
INSERT INTO ASSET_DELETE_INDEX(CODE, ASSET_TYPE, IW_VPATH, LANGUAGE, MODIFIED_DTE)
VALUES (:old.CODE, 'Video', :old.IW_VPATH, :old.CONTENT_LANGUAGE, sysdate);
END IF;
END Trg_video_bfr_delete;
/
This is because you are deleting record from table and same table is used as reference inside trigger. This is case of "Mutating Error". You should switch to statement level trigger is it fits your requirement or you can use compound trigger which has multiple entry point and are capable of handling such scenarios.
Please refer https://oracle-base.com/articles/9i/mutating-table-exceptions
As per the snippet provided, I dont see any point in doing a SELECT on mutating TABLE.
We can easily ise :OLD.CODE to fetch the value. Hope below snippet helps.
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete AFTER
DELETE ON CMS_VIDEO FOR EACH ROW
-- DECLARE CODE VARCHAR2(60);
BEGIN
-- SELECT CODE INTO CODE FROM CMS_VIDEO WHERE CODE = :OLD.CODE;
IF CODE IS NOT NULL THEN
INSERT
INTO ASSET_DELETE_INDEX
(
CODE,
ASSET_TYPE,
IW_VPATH,
LANGUAGE,
MODIFIED_DTE
)
VALUES
(
:old.code,
'Video',
:old.IW_VPATH,
:old.CONTENT_LANGUAGE,
sysdate
);
END IF;
EXCEPTION
WHEN OTHERS THEN
INSERT
INTO ASSET_DELETE_INDEX
(
CODE,
ASSET_TYPE,
IW_VPATH,
LANGUAGE,
MODIFIED_DTE
)
VALUES
(
:old.code,
'Video',
NULL,
:old.CONTENT_LANGUAGE,
sysdate
);
COMMIT;
END Trg_video_bfr_delete;

Insert inside the trigger in oracle [duplicate]

This question already has answers here:
Oracle trigger after insert or delete
(3 answers)
Closed 5 years ago.
Im trying to make simple trigger for students project. It will be firing on the prices table.
CREATE TABLE "STD_USER"."HT_PRICES"
( "PRC_ID" NUMBER,
"PRC_DATE_FROM" DATE,
"PRC_DATE_TO" DATE,
"PRC_STD_ID" NUMBER,
"PRC_AMOUNT" NUMBER
)
The main goal of the trigger is to checking if any reservation do not have any reference to updated Prices row.
If any row actually have reference on updated row we will need to insert new price into the prices table with starting date (PRC_DATE_FROM) equal to the end of the last reservation.
Trigger body:
create or replace
TRIGGER "TRG_NEW_PRICE" BEFORE UPDATE ON HT_PRICES
REFERENCING OLD AS old NEW AS new
FOR EACH ROW
DECLARE
v_last_rsv_date DATE;
v_temp_date_to DATE;
v_std_id NUMBER;
v_amount NUMBER;
BEGIN
BEGIN
SELECT MAX(RSV_DATE_TO)
INTO v_last_rsv_date
FROM HT_RESERVATION,
HT_ROOMS,
HT_STANDARDS,
HT_PRICES
WHERE RSV_ROM_ID=ROM_ID
AND ROM_STD_ID=STD_ID
AND STD_ID=:new.PRC_STD_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN;
END;
--do skrocenia starej ceny
v_temp_date_to:= :new.PRC_DATE_TO;
--poprawna data dla nowej ceny to v_last_rsv_date
:new.PRC_DATE_TO:= v_last_rsv_date;
v_amount:=:new.PRC_AMOUNT;
:new.PRC_AMOUNT:=:old.PRC_AMOUNT;
v_std_id:=:new.PRC_STD_ID;
:new.PRC_STD_ID:=:old.PRC_STD_ID;
COMMIT;
--skrocenie starej ceny w nowym rekordzie cenowym
HP_MANAGING.ADD_PRICE(v_last_rsv_date, v_temp_date_to, v_std_id, v_amount);
END;
Any ideas how to fix it? When I'm trying to test it I'm getting logs:
Error starting at line 10 in command:
update HT_PRICES SET PRC_AMOUNT=501 where prc_id=1
Error report:
SQL Error: ORA-04091: table STD_USER.HT_PRICES is mutating, trigger/function may not see it
ORA-06512: at "STD_USER.TRG_NEW_PRICE", line 8
ORA-04088: error during execution of trigger 'STD_USER.TRG_NEW_PRICE'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
1) Place your SELECT into a local procedure marked with pragma autonomous_transaction, this will allow select to run, though you won't see your current transaction uncommitted changes.
2) What is the reason to have COMMIT; in a BEFORE DML trigger?

Getting the value that fired the Oracle trigger

Am very new to Oracle triggers. Suppose I have a trigger on an insert in the table emp. Is there a way to find out what was the inserted record that fired the trigger. I wanted the trigger to have code that does something if the inserted record was a particular value.
Assuming you have a row-level trigger, you can simply use the :NEW pseudo-record
CREATE TRIGGER name_of_trigger
BEFORE INSERT ON emp
FOR EACH ROW
DECLARE
<<declare variables>>
BEGIN
IF( :new.ename = 'JUSTIN' )
THEN
<<do something if the newly inserted ENAME value is 'JUSTIN'>>
END IF;
END;
For a DDL trigger, the approach is completely different. In that case, the pseudofunctions ora_dict_obj_owner and ora_dict_obj_name will return the owner and name of the table that the DDL statement is operating on.
The row that is being insertes is available as NEW in the trigger
Check out the manual for more details.
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/create_trigger.htm#BABEBAAB
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955

Resources