user8845641
user8845641

Reputation: 25

Mutating error on an AFTER insert trigger

CREATE OR REPLACE TRIGGER TRG_INVOICE
    AFTER INSERT
    ON INVOICE
    FOR EACH ROW
DECLARE
    V_SERVICE_COST FLOAT;
    V_SPARE_PART_COST FLOAT;
    V_TOTAL_COST FLOAT;
    V_INVOICE_DATE DATE;
    V_DUEDATE DATE;
    V_REQ_ID INVOICE.SERVICE_REQ_ID%TYPE;
    V_INV_ID INVOICE.INVOICE_ID%TYPE;
BEGIN
    V_REQ_ID := :NEW.SERVICE_REQ_ID;
    V_INV_ID := :NEW.INVOICE_ID;

    SELECT SUM(S.SERVICE_COST) INTO V_SERVICE_COST
    FROM INVOICE I, SERVICE_REQUEST SR, SERVICE S, SERVICE_REQUEST_TYPE SRT
    WHERE I.SERVICE_REQ_ID = SR.SERVICE_REQ_ID
    AND SR.SERVICE_REQ_ID = SRT.SERVICE_REQ_ID
    AND SRT.SERVICE_ID = S.SERVICE_ID
    AND I.SERVICE_REQ_ID = V_REQ_ID;


    SELECT SUM(SP.PRICE) INTO V_SPARE_PART_COST
    FROM INVOICE I, SERVICE_REQUEST SR, SERVICE S, SERVICE_REQUEST_TYPE SRT, 
    SPARE_PART_SERVICE SRP,
     SPARE_PART SP
    WHERE I.SERVICE_REQ_ID = SR.SERVICE_REQ_ID
    AND SR.SERVICE_REQ_ID = SRT.SERVICE_REQ_ID
    AND SRT.SERVICE_ID = S.SERVICE_ID
    AND S.SERVICE_ID = SRP.SERVICE_ID
    AND SRP.SPARE_PART_ID = SP.SPARE_PART_ID
    AND I.SERVICE_REQ_ID = V_REQ_ID;


    V_TOTAL_COST := V_SERVICE_COST + V_SPARE_PART_COST;


    SELECT SYSDATE INTO V_INVOICE_DATE FROM DUAL;


    SELECT ADD_MONTHS(SYSDATE, 1) INTO V_DUEDATE FROM DUAL;

    UPDATE INVOICE
    SET COST_SERVICE_REQ = V_SERVICE_COST, COST_SPARE_PART = 
    V_SPARE_PART_COST,
   TOTAL_BALANCE = V_TOTAL_COST, PAYMENT_DUEDATE = V_DUEDATE, INVOICE_DATE = 
    V_INVOICE_DATE
    WHERE INVOICE_ID = V_INV_ID;



END;

I'm trying to calculate some columns after the user inserts a row. Using the service_request_id I want to calculate the service/parts/total cost. Also, I would like to generate the creation and due dates. But, I keep getting

INVOICE is mutating, trigger/function may not see it

Not sure how the table is mutating after the insert statement.

Upvotes: 0

Views: 46

Answers (1)

krokodilko
krokodilko

Reputation: 36127

Not sure how the table is mutating after the insert statement.

Imagine a simple table:

create table x(
  x int,
  my_sum int
);

and an AFTER INSERT FOR EACH ROW trigger, similar to yours, which calculates a sum of all values in the table and updates my_sum column.

Now imagine this insert statement:

insert into x( x )
select 1 as x from dual
connect by level <= 1000;

This single statement basically inserts 1000 records, each one with 1 value, see this demo: http://sqlfiddle.com/#!4/0f211/7


Since in SQL each individual statement must be ATOMIC (more on this here: Statement-Level Read Consistency, Oracle is free to perform this query in any way as long as the final result is correct (consistent). It can save records in the order of execution, maybe in reverse order, it can divide the batch into 10 threads and do it in parallel.


Since the trigger is fired individually after inserting each row, and it cannot know in advance the "final" result, then considering the above all the below results are possible depending on "internal" method choosed by Oracle to execute this query. As you see, these result do not meet the definition of consistency. And Oracle prevents this issuing mutating table error.

In other words - your assumption are bad and your design is flawed, you need to change it.

| X | MY_SUM |
|---|--------|
| 1 |      1 |
| 1 |      2 |
| 1 |      3 |
| 1 |      4 |
...
...

or maybe :

| X | MY_SUM |
|---|--------|
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
...

or maybe:

| X | MY_SUM |
|---|--------|
| 1 |      4 |
| 1 |      8 |
| 1 |     12 |
| 1 |     16 |
| 1 |     20 |
| 1 |     24 |
| 1 |     28 |
...
...

Upvotes: 1

Related Questions