I am a PL/SQL newbie and I'm struggling with a trigger.
Description:
I have three objects - PRODUCT, CONTAINS, ORDER. One product can have many CONTAINS and one ORDER can have many CONTAINS (basically it used to be Many-to-many relationship between PRODUCT and ORDER).
Each Product has a column "value", each CONTAINS has a column "amount" and each ORDER has a column "total".
When I add a new PRODUCT to ORDER via creating new CONTAINS, I want to recalculate field "total" on ORDER.
Example: PRODUCT X has "value" of 100. PRODUCT Y has "value" of 200. We have an ORDER O. Now I create CONTAINS between Product X and ORDER O with column "amount" of 5. Now the trigger should multiply 5 * 100 and update the ORDER column "total" to 500. Then I create CONTAINS between PRODUCT Y and ORDER O with column "amount" of 10. Now the trigger should recalculate 5 * 100 + 10 * 200 and update the "total" column on ORDER O to 2500.
My faulty trigger:
create or replace TRIGGER TRIGGER1
AFTER DELETE OR INSERT OR UPDATE OF AMOUNT, PRODUCT_ID_PRODUCT, ORDER_ID_ORDER ON CONTAINS
REFERENCING NEW AS n
FOR EACH ROW
DECLARE
value number;
amount number;
total number;
BEGIN
LOOP
FOR emp IN (SELECT AMOUNT, PRODUCT_ID_PRODUCT, ORDER_ID_ORDER FROM CONTAINS WHERE ORDER_ID_ORDER = :n.ORDER_ID_ORDER)
LOOP
(SELECT SUM(VALUE) into product FROM PRODUCT WHERE ID_PRODUCT = :emp.PRODUCT_ID_PRODUCT);
amount:= emp.AMOUNT;
total:= total + (product * amount);
UPDATE ORDER SET ORDER.TOTAL = total WHERE ID_ORDER = :n.ORDER_ID_ORDER;
END LOOP;
END LOOP;
END;
EDIT: The error shows on here:
(SELECT SUM(VALUE) into product FROM PRODUCT WHERE ID_PRODUCT = :emp.PRODUCT_ID_PRODUCT)
saying I can't use "emp".
EDIT2: Error message:
10/2 PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: ( - + case mod new not null continue avg count current exists max min prior sql stddev sum variance execute forall merge time timestamp interval date pipe <an alternat 10/89 PLS-00103: Encountered the symbol ")" when expecting one of the following: . ( * @ % & - + ; / at for mod remainder rem <an exponent (**)> and or group having intersect minus order start union where connect || indicator multiset 15/5 PLS-00103: Encountered the symbol "LOOP" when expecting one of the following: ;
Simplified the trigger by removing loops/cursors that isn't actually required.
Refer DB Fiddle link for solution :https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=3be867f6ab2e93978ae45a7d305434a1
PS:Triggers can cause performance bottleneck at time when the DMLs in the triggers are not tuned well enough.Recommendation is to check the explain plan for SELECT,INSERT,UPDATE statements inside a trigger and tune them as desired.If Indexes are not available for CONTAINS.ORDER_ID_ORDER and PRODUCTS.ID_PRODUCT creating one would be beneficial but would recommend consulting with DBA in-charge.
UPDATE : Now since you need to Select from the table on which trigger is fired we have to live with famous Mutating trigger error
ORA-04091: table MYTABLE.CONTAINS is mutating, trigger/and luckily Oracle has an easy solution for it usingCompound triggerthat was added fromOracle Database 11g Release1version onwards.For more details and technical explanation on Compound Trigger you may refer http://stevenfeuersteinonplsql.blogspot.com/2016/12/get-rid-of-mutating-table-trigger.html
Trigger Code goes like this, ta.da.. So we take rows to a pl/sql table for row operation and perform statement operation for each of the rows from the pl/sql table.
Updated solution can be found in DBfiddle link https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=1fb40eef7cf3a647bc5560ed19490240