ORA-04091: table [blah] is mutating, trigger/function may not see it

前端 未结 4 648
清歌不尽
清歌不尽 2020-12-02 00:54

I recently started working on a large complex application, and I\'ve just been assigned a bug due to this error:

ORA-04091: table SCMA.TBL1 is mutating, trig         


        
4条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-02 01:03

    I agree with Dave that the desired result probalby can and should be achieved using built-in constraints such as unique indexes (or unique constraints).

    If you really need to get around the mutating table error, the usual way to do it is to create a package which contains a package-scoped variable that is a table of something that can be used to identify the changed rows (I think ROWID is possible, otherwise you have to use the PK, I don't use Oracle currently so I can't test it). The FOR EACH ROW trigger then fills in this variable with all rows that are modified by the statement, and then there is an AFTER each statement trigger that reads the rows and validate them.

    Something like (syntax is probably wrong, I haven't worked with Oracle for a few years)

    CREATE OR REPLACE PACKAGE trigger_pkg;
       PROCEDURE before_stmt_trigger;
       PROCEDURE for_each_row_trigger(row IN ROWID);
       PROCEDURE after_stmt_trigger;
    END trigger_pkg;
    
    CREATE OR REPLACE PACKAGE BODY trigger_pkg AS
       TYPE rowid_tbl IS TABLE OF(ROWID);
       modified_rows rowid_tbl;
    
       PROCEDURE before_stmt_trigger IS
       BEGIN
          modified_rows := rowid_tbl();
       END before_each_stmt_trigger;
    
       PROCEDURE for_each_row_trigger(row IN ROWID) IS
       BEGIN
          modified_rows(modified_rows.COUNT) = row;
       END for_each_row_trigger;
    
       PROCEDURE after_stmt_trigger IS
       BEGIN
          FOR i IN 1 .. modified_rows.COUNT LOOP
             SELECT ... INTO ... FROM the_table WHERE rowid = modified_rows(i);
             -- do whatever you want to
          END LOOP;
       END after_each_stmt_trigger;
    END trigger_pkg;
    
    CREATE OR REPLACE TRIGGER before_stmt_trigger BEFORE INSERT OR UPDATE ON mytable AS
    BEGIN
       trigger_pkg.before_stmt_trigger;
    END;
    
    CREATE OR REPLACE TRIGGER after_stmt_trigger AFTER INSERT OR UPDATE ON mytable AS
    BEGIN
       trigger_pkg.after_stmt_trigger;
    END;
    
    CREATE OR REPLACE TRIGGER for_each_row_trigger
    BEFORE INSERT OR UPDATE ON mytable
    WHEN (new.mycolumn IS NOT NULL) AS
    BEGIN
       trigger_pkg.for_each_row_trigger(:new.rowid);
    END;
    

提交回复
热议问题