I created a trigger like that :
create or replace
TRIGGER "TRIG_DECLENCHEMENT_PARAM"
AFTER UPDATE ON t_balise
FOR EACH ROW
WHEN (NEW.no_serie like '2%')
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
P_UPDATE_BALISE(:NEW.no_serie, :NEW.date, :NEW.vitesse);
COMMIT;
END;
P_UPDATE_BALISE it's a method in another data base which update another table. It works well like that. I want to update this Trigger and change the condition NEW.no_serie like '2%' to NEW.no_serie between 200 and 299. There is a script like Alter Trigger... which make an update of Trigger ?
The alter trigger statement only lets you "enable, disable, or compile a database trigger". The documentation states:
Note:
This statement does not change the declaration or definition of an existing trigger. To redeclare or redefine a trigger, use the CREATE TRIGGER statement with the OR REPLACE keywords.
You will have to modify your existing statement (which should probably be in source control anyway) to have the new condition:
create or replace
TRIGGER "TRIG_DECLENCHEMENT_PARAM"
AFTER UPDATE ON t_balise
FOR EACH ROW
WHEN (NEW.no_serie between 200 and 299)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
P_UPDATE_BALISE(:NEW.no_serie, :NEW.date, :NEW.vitesse);
COMMIT;
END;
You've changed that comparison from a string to a number. Hopefully no_serie is actually a number and it was the old check that was incorrectly treating it as a string, and triggering on values you didn't want (2, 20, 2000 etc.).
Not directly relevant, but having this trigger as an autonomous transaction means that if the update on t_balise is rolled back, any changes made by the call to P_UPDATE_BALISE will not be rolled back - since they've been committed independently. That isn't generally something you want - the update on this table and whatever changes are made elsewhere (you said to another table; hopefully you haven't made this autonomous because the procedure is actually updating the same table) would normally be atomic and part of the same transaction. Breaking atomicity is something that is very rarely needed or desirable, so I'd check that is really what you intended and isn't just a hack to avoid a deeper problem.
Related
I get an error (ORA-04091: table DBPROJEKT_AKTIENDEPOT.AKTIE is mutating, trigger/function may not see it) when executing my trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
AFTER
INSERT OR UPDATE OF TAGESKURS
OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
bfr number;
Begin
bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
UPDATE AKTIE
SET BILANZ = TAGESKURS - WERT_BEIM_EINKAUF;
IF bfr < -50
THEN
DBMS_OUTPUT.PUT_LINE('ACHTUNG: The value (Nr: '||:new.AKTIEN_NR||') is very low!');
END IF;
END;
I want to check the value "BILANZ" after calculating it, wether it is under -50.
Do you have any idea why this error is thrown?
Thanks for any help!
There are several issues here:
Oracle does not allow you to perform a SELECT/INSERT/UPDATE/DELETE against a table within a row trigger defined on that table or any code called from such a trigger, which is why an error occurred at run time. There are ways to work around this - for example, you can read my answers to this question and this question - but in general you will have to avoid accessing the table on which a row trigger is defined from within the trigger.
The calculation which is being performed in this trigger is what is referred to as business logic and should not be performed in a trigger. Putting logic such as this in a trigger, no matter how convenient it may seem to be, will end up being very confusing to anyone who has to maintain this code because the value of BILANZ is changed where someone who is reading the application code's INSERT or UPDATE statement can't see it. This calculation should be performed in the INSERT or UPDATE statement, not in a trigger. It considered good practice to define a procedure to perform INSERT/UPDATE/DELETE operations on a table so that all such calculations can be captured in one place, instead of being spread out throughout your code base.
Within a BEFORE ROW trigger you can modify the values of the fields in the :NEW row variable to change values before they're written to the database. There are times that this is acceptable, such as when setting columns which track when and by whom a row was last changed, but in general it's considered a bad idea.
Best of luck.
You are modifying the table with the trigger. Use a before update trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
BEFORE INSERT OR UPDATE OF TAGESKURS OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
v_bfr number;
BEGIN
v_bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
:new.BILANZ := v_bfr;
IF v_bfr < -50 THEN
Raise_Application_Error(-20456,'ACHTUNG: The value (Nr: '|| :new.AKTIEN_NR || ') is very low!');
END IF;
END;
I'm quite a newbie in PL/SQL and I'm trying to do quite complex data integrity checks via triggers.
I've already understood how to avoid problems when calling a table inside a trigger that is used on the same table (via a temporary external table) but now I'm facing a really mind-blowing problem : I thought that ":NEW" was referencing the value in my table AFTER an update but things don't look that simple... It is the new value SET by the update or insert... which looks to be NULL if nothing has been specified, even if the corresponding field value is NOT NULL after the update... wich is driving me crazy.
My trigger is set when inserting or updating several variables :
CREATE OR REPLACE TRIGGER TRG_INS_UP_INSTRUMENT_EVENT
AFTER INSERT OR UPDATE OF EVENT_ID, DATE_BEGIN,DATE_END,INSTR_ID,TYPE_EVENT_ID ON AIS_INSTRUMENT_EVENT
But now... If there already is a line with non-null fields and I do an
UPDATE AIS_INSTRUMENT_EVENT SET INSTR_ID='642' WHERE EVENT_ID='6479'
I actually get a ":NEW.DATE_BEGIN" which is NULL... event thought nor the older or newer values are NULL (because I just didn't update it).
How can I distinguish - in my trigger - the case when the DATE_BEGIN is updated and SET voluntary to NULL from the case in which nothing has been specified (and this field must thus remain the same but not necessarily NULL...). I have to many possible combination to check one by one...
Thanks in advance for your help!
What you are saying is not true. :new contains the full row regardless whether the column is referenced in the UPDATE statement:
CREATE TABLE test (test INTEGER, last_changed DATE);
CREATE OR REPLACE TRIGGER TRG_INS_UP_TEST
AFTER INSERT OR UPDATE OF test, last_changed ON test
FOR EACH ROW
BEGIN
dbms_output.put_line('LAST CHANGED IS ' || :new.last_changed);
END;
INSERT INTO test (test, last_changed) VALUES (1, SYSDATE);
COMMIT;
UPDATE test SET test = test + 1;
DBMS Output:
LAST CHANGED IS 01.09.17
To achieve what you want the mechanism works slightly different. You have to look at two different use cases:
1.) You want the trigger not to fire unless a certain column is mentioned. This use cases is by the reference in the trigger declaration (INSERT OR UDATE OF "column_name"). If the INSERT/UPDATE statement only affects columns that are not mentioned the trigger will not fire.
2.) You want the trigger not to fire unless a certain row is modified. So you want the trigger to only if fire is a value has actually changed. This is done by the WHEN restriction of the trigger. It is usually used in conjunction with DECODE, like so:
CREATE OR REPLACE TRIGGER TRG_INS_UP_TEST
AFTER INSERT OR UPDATE OF test, last_changed ON test
FOR EACH ROW
WHEN (DECODE(new.test,old.test,0,1)=1 OR DECODE(new. last_changed,old. last_changed,0,1)=1)
BEGIN
...
END;
So to answer your original question: If you want to the trigger too only fire in cases where the column DATE_BEGIN is set to NULL you will have to declare your trigger using both approaches
CREATE OR REPLACE TRIGGER TRG_INS_UP_INSTRUMENT_EVENT
AFTER INSERT OR UPDATE OF DATE_BEGIN ON AIS_INSTRUMENT_EVENT
FOR EACH ROW
WHEN (DECODE(new.DATE_BEGIN,old. DATE_BEGIN,0,1)=1 AND new.DATE_BEGIN IS NULL)
The limitation to certain columns ("INSERT OR UPDATE OF DATE_BEGIN") is not strictly necessary but it is good practice since it improves performance since it excludes the trigger from firing at all.
Sorry I think I made a to quick conclusion... The bug was mine. I've tested on a "Toy" table and, indeed, the :NEW was not null, even when not set by the UPDATE. I found the bug in the meantime. All this is too new to me ;-).
Sorry for disturbing.
I have 2 triggers on a table A one is BEFORE INSERT and one AFTER INSERT :
CREATE OR REPLACE TRIGGER DEMO_TRG
AFTER INSERT
ON A
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
UPDATE A
SET STATUS = 'DONE'
WHERE id_id =:new.p_id;
commit;
END;
/
Why the update command in the after insert doesn't work (the status is not set to DONE)? Did I missed something?
Most likely the issue is due to the AUTONOMOUS_TRANSACTION - when you do that, you're effectively switching to a different transaction, which won't know about your uncommitted transaction (ie. your insert), so there is nothing for it to update.
What you should do is amend the column (:new.status := 'DONE') in your before trigger. Or, even better, avoid triggers and have the logic in a stored procedure and don't allow anyone to directly insert into the table, although I appreciate this may be a big switch if you have lots of applications inserting directly into tables etc
I have an error: 'ORA-04092: cannot COMMIT in a trigger' when trying to execute ddl command in one simple oracle after update trigger. Trigger needs to create public database link after one field in column is updated. Here is the source:
create or replace
TRIGGER CreateLinkTrigger
after UPDATE of Year ON tableInit
for each row
DECLARE
add_link VARCHAR2(200);
BEGIN
IF :new.year = '2014'
then
add_link := q'{create public database link p2014 connect to test14 identified by temp using 'ora'}';
execute immediate add_link;
END IF;
END;
So, as You can see i need to create new public database link after new year has been activated.
So when i try to update table 'tableInit' with year value of '2014' i get ORA-04092 error.
Is there any way to avoid this error, or another solution for this?
Thanks...
Creating a database link on the fly seems like an unusual thing to do; your schema should generally be static and stable. However, if you must, it would be simpler to wrap the update and the link in a procedure, or just issue two statements - presumably whatever performs the update is fairly controlled anyway, otherwise you'd have to deal with multiple people triggering this multiple times, which would be even more of a mess.
You can probably make this work by adding PRAGMA autonomous_transaction; to your trigger, as demonstrated for a similar issue (creating a view rather than a link) in this answer, but I'm not in a position to test that at the moment.
create or replace
TRIGGER CreateLinkTrigger
after UPDATE of Year ON tableInit
for each row
DECLARE
add_link VARCHAR2(200);
PRAGMA autonomous_transaction;
BEGIN
...
You could also make the trigger submit an asynchronous job to perform the DDL, as described in this answer, and there's more of an example in this answer, where you'd change the job's anonymous block to do your execute immediate.
It would probably be better to just create the links for the next few years in advance during a maintenance window, or on a schedule, or from a procedure; rather than trying to associate a schema change to a data change.
Let's say I have 2 tables, EMPLOYEE and EMP_BAK. I need to create a backup table for all the data that deleted from employee, even these that are rolled back
My trigger:
CREATE OR REPLACE TRIGGER emp_del_bak_trg
before delete ON employee
FOR EACH row
DECLARE
oldname department.department_name%type;
newname department.department_name%type;
BEGIN
INSERT INTO emp_bak
VALUES (:OLD.employee_id, :OLD.employee_name, :OLD.job
,:OLD.hire_date,:OLD.department_id, sysdate);
--commit;
end;
Now, if I rollback then the data will be deleted; if I uncomment out commit, I'll get an error on deleting. The idea is to keep the record plus keep track of the system updates.
Any ideas how to get around this?
There is almost never a good reason to commit inside a trigger.
Now, that's out of the way your situation seems to be extra special, you need to track it, even if the transaction is rolled back. This is a fairly unusual requirement, but I'm going to assume you have a very good reason for doing this.
If you really, really, want to do this you need to use an autonomous transaction, this enables an independent transaction to be committed within another. As a trigger is a PL/SQL block, you can do this in a trigger.
The Oracle documentation has a separate section dealing with autonomous transactions in triggers, with plenty of examples for you. Wherever you need to use one the syntax is as follows, this is always placed in the DECLARE block:
PRAGMA AUTONOMOUS_TRANSACTION;
Your trigger, therefore, would look as follows:
create or replace trigger emp_del_bak_trg
before delete on employee
for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
insert into emp_bak
values ( :old.employee_id, :old.employee_name, :old.job
, :old.hire_date, :old.department_id, sysdate);
commit;
end;
/
As a little note I always case this differently so it's obvious you're doing something that you shouldn't be. Autonomous transactions are dangerous and should be used with extreme care.