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
Related
I have a trigger set to fire after an update of a specific column. I want to update the value of a specific column after a specific value from another column changes to another specific value.
I'm getting the errors:
ORA-04091: table tableName is mutating, trigger/function may not see it
ORA-06512: at "triggerName", line 14
ORA-04088: error during execution of trigger 'triggerName'
I've rewritten this as a before update and after update, as well as tried storing the logic in a function, and using "pragma autonomous_transaction", but the error still gets thrown.
BEFORE update of columnName1 ON tableName
FOR EACH ROW
BEGIN
if :new.columnName1 = 3 AND :old.columnName1 = 1 then
update tablename
set columnName2= MOD(sequenceName.NextVal, 5) + 1
where tableName.columnName2 = :old.columnName2;
end if;
END;
/
I don't understand why the entire table is labelled as "mutating" when I am not updating the column affected by the update the trigger is responding to. Surely, you should be able to update the value of a column in a table if another value in the table changes, or am I crazy here?
Note: Only one entry would be affected at a time. In my application logic, I update the status of some person in the database. I want the database to do some logic only on a specific status change, and I want to avoid using API calls for the logic here, as you can see, it is simply one line of logic in PLSQL.
Thanks
Perhaps you don't really want to update the table but just change a value in the specific row being updated.
If so, eschew the update and just set the value:
BEFORE update of columnName1 ON tableName
FOR EACH ROW
BEGIN
if :new.columnName1 = 3 AND :old.columnName1 = 1 then
:new.columnName2 := MOD(sequenceName.NextVal, 5) + 1 ;
end if;
END;
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 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.
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 have this table that represents a weak entity and is a typical table for introducing items ordered:
insert into ITEM_FORNECIMENTO values (a_orderId,a_prodId,a_prodQtd, a_buyPrice);
I want my trigger to update the last column (the total price of products WITHOUT iva) to do this : totalPrice= totalPrice*(1+(iva/100), each time I insert or update an entry on that table.
so, I came up with this, but I'm totally wrong when it comes to work with new and old values.
create or replace
trigger t_replaceTotal
after insert or update of id_prod,qtd_if,prec_total_if on item_fornecimento
for each row
declare
iva produto.iva_prod%type;
idProd produto.id_prod%type;
r_old item_fornecimento.prec_total_if%type:=null;
r_new item_fornecimento.prec_total_if%type:=null;
begin
select iva_prod,id_prod into iva,idprod from produto p where p.id_prod = id_prod;
r_old:= :old.prec_total_if;
r_new:= :new.prec_total_if;
update item_fornecimento item set prec_total_if = r_old * (1+(iva/100)) where item.id_prod = idprod;
end;
Could someone please help rewriting this code? I'm getting the error: ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "FUSION.T_REPLACETOTAL", line 8
ORA-04088: error during execution of trigger 'FUSION.T_REPLACETOTAL'
The problem is that you are trying to fetch every row from the table because you are matching a column (id_prod) to itself. I suspect you want to use :new.id_prod or :old.id_prod.
select .... from produto p where p.id_prod = id_prod;
Next, make this a BEFORE INSERT/UPDATE trigger and replace the UPDATE statement with
:new.prec_total_if := r_old * (1+(iva/100));
Otherwise you'll get a mess of mutating table errors.
That error is telling you that a query that saves its result into a variable is returning more than one result. As such it doesn't know what you want to save in the variable.
Try running the following:
select iva_prod,id_prod from produto p where p.id_prod = id_prod;
and I bet it will give you more than one result, which it can't save into iva,idprod.