I wrote this trigger
CREATE OR REPLACE TRIGGER update_expirationDate
AFTER INSERT ON DIFFUSION FOR EACH ROW
DECLARE
lastdiffusion DATE;
BEGIN
SELECT MAX(diffusionDate) INTO lastdiffusion FROM DIFFUSION WHERE idEpisode = :NEW.idEpisode;
UPDATE EPISODE SET expirationDate = lastdiffusion + 14 WHERE idEpisode = :NEW.idEpisode;
END;
/
The compilation is OK, but when I insert some values in DIFFUSION, I have the following error :
ORA-04091: table DIFFUSION is mutating, trigger/function may not see it
I don't see what is the problem..
Can someone help me and tell me what i am doing wrong ? Thanks.
The problem is that you are referencing the table being updated in the trigger itself. Oracle gets confused. And, if you think about this hard enough, you'll probably also get confused.
Fortunately, in this case, I don't think you need to reference the table. Instead, just use logic on the update:
CREATE OR REPLACE TRIGGER update_expirationDate
AFTER INSERT ON DIFFUSION FOR EACH ROW
BEGIN
UPDATE EPISODE
SET expirationDate = :NEW.diffusionDate + 14
WHERE idEpisode = :NEW.idEpisode AND
expirationDate < :NEW.diffusionDate + 14;
END;
That is, only update expirationDate when the value is going to change. If you do this for every insert, then it will always be the maximum value.
Just dont use the same table of the trigger in another query this cause a redundancy.
Use compound trigger,it solves your problem.
This article contains more info.
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 want to create an update trigger for a table. The trigger was created, but when I update the column finish, it say mutating table.
This is my code
CREATE OR REPLACE TRIGGER SET_COST BEFORE UPDATE OF finish ON PAY
FOR EACH ROW
BEGIN
UPDATE PAY
SET PAY.COST = (finish-start) * 20000
WHERE PAY.ID=:new.ID;
END;
This trigger gives me 'Mutating Table' error and so far I have been unable to fix it. Any Suggestion ? Thanks
First of all, you shouldn't be doing that at all. There's no use in storing values that are easily calculated whenever you need them.
As of your question: no UPDATE:
begin
:new.cost := (:new.finish - :new.start) * 20000;
end;
I have been trying to get the following trigger to update the price based on a discount rate when that rate is updated.
create or replace trigger discount_change
before update on maintain
for each row
begin
if :NEW.discount_rate != :OLD.discount_rate
then
update maintain m
set m.discount_price = :NEW.discount_rate * :OLD.price
where m.m_id = :NEW.m_id;
end if;
end;
As this is an exercise, these are in the same table (sadly) and when I attempt to do this I am consistently getting the error:
table %s.%s is mutating, trigger/function may not see it
I've been combing these forums looking for the answer and as far as I have researched, this shouldn't be throwing this error as I am not selecting anything from the table. What am I doing wrong here?
I would suggest to make the column discount_price as computed column so there is no need to define trigger for its updation
I am having an issue with a trigger. After I add the trigger to my database, when I try to insert a row that the trigger will act on, I get a mutating table error. I don't know how to avoid this. Would someone mind looking at this and tell me what I am doing wrong, and how to rewrite it?
CREATE OR REPLACE TRIGGER ORNG_INV_LINE_TOTAL_TRIGGER
FOR INSERT OR UPDATE ON ORNG_INV_LINE
COMPOUND TRIGGER
AFTER EACH ROW IS
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(ORNG_INV_LINE.Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE ORNG_INVOICE.INV_Num = :NEW.INV_Num);
END AFTER EACH ROW;
END ORNG_INV_LINE_TOTAL_TRIGGER;
/
I'm not sure why it is triggering an error. I'm trying to do the action after the update. All I want to do is get the sum for all of the lines of all matching invoice numbers, and write that value in the INVOICE table. Thanks for the help.
Your trigger is written on the table ORNG_INV_LINE for insert or update and again you made a select on the same table while updating ORNG_INVOICE table so trigger is mutating,
to over come this you have to use statement level trigger instead of row level trigger .
i.e after each statement should be there in code instead of after each row.
This can help you.
Here is the solution...apparently everyone is busy with Thanksgiving. Hopefully this helps the next guy.
create or replace TRIGGER ORNG_INV_L_TTL_TRIG
AFTER INSERT OR UPDATE OR DELETE ON ORNG_INV_LINE
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE INV_Num = ORNG_INVOICE.INV_Num);
END ORNG_INV_L_TTL_TRIG;
/
I want to prevent the database from storing any values bigger than 20 into a table.
CREATE OR REPLACE TRIGGER Dont_Allow
AFTER INSERT ON Cities
FOR EACH ROW
WHEN (new.IDCity > 20)
BEGIN
dbms_output.put_line(' Failed to insert ' || :new.IDCity);
delete from orase where IDCity=:new.IDCity;
END;
While this does work in terms of not actually adding anything with an ID > 20, every time the trigger tries to do its magic, this shows up:
ORA-04091: table SYSTEM.ORASE is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.DONT_ALLOW", line 6
ORA-04088: error during execution of trigger 'SYSTEM.DONT_ALLOW'
What's a proper way of doing what I want?
EDIT:
I've decided to use a trigger for this:
After a new row is inserted into Employees, a trigger checks the new guy's salary and if it's above 21 units / hour, it takes 5% off management's bonus. Lame, but hey - I'm using a trigger to solve a problem I don't have: the outcome won't be pretty.
CREATE OR REPLACE TRIGGER Bite_Bonus
AFTER INSERT ON Employees
FOR EACH ROW
WHEN (new.HourSalary > 20)
BEGIN
update Management set Bonus = Bonus - 5/100 * Bonus;
END;
You shouldn't be using a TRIGGER for that, you should be using a CHECK, like CONSTRAINT city_id_below_20 CHECK (IDCity < 20). You can use ALTER TABLE ADD CONSTRAINT to put it on an existing table.
As TC1 indicated, the proper way to enforce this sort of requirement is to use a constraint.
If you are forced to use the inferior approach because this is a school assignment, you most likely want to raise an exception in your trigger
CREATE OR REPLACE TRIGGER Dont_Allow
BEFORE INSERT OR UPDATE ON Cities
FOR EACH ROW
WHEN (new.IDCity > 20)
BEGIN
RAISE_APPLICATION_ERROR( -20001, 'IDCity cannot exceed 20 so rejecting invalid value: ' || :new.IDCity );
END;
If you need to use a trigger for this, make it a BEFORE INSERT trigger, not an AFTER INSERT - you don't want that insert to happen at all. Trying to "undo" it after the fact is not a good approach.
To abort the insert, all you need to do is raise an exception within that trigger. Probably the best thing for this is to raise an application error.