【问题标题】:ORA-04091: table name is mutating - when trigger from child table wants to update parent tableORA-04091: 表名发生变化 - 当来自子表的触发器想要更新父表时
【发布时间】:2020-12-21 22:09:28
【问题描述】:

我有 2 个简单的表格:

CREATE TABLE ORDERS
( ORDER_KEY number(10) NOT NULL,
  ORDER_NR varchar2(50) NOT NULL,
  LAST_UPDATE DATE,
  CONSTRAINT ORDERS_PK PRIMARY KEY (ORDER_KEY)
);

CREATE TABLE ORDER_POSITIONS
( ORDER_POS_KEY number(10) NOT NULL,
  ORDER_POS_NR number(10),
  ORDER_POS_DESCRIPTION varchar2(50),
  ORDER_KEY NUMBER(10) NOT NULL,
  CONSTRAINT ORDER_POSITIONS_PK PRIMARY KEY (ORDER_POS_KEY),
  CONSTRAINT ORDERS_FK
    FOREIGN KEY (ORDER_KEY)
    REFERENCES ORDERS(ORDER_KEY)
    ON DELETE CASCADE
);

在 ORDER_POSITIONS 表上,我创建了一个触发器,该触发器应在删除位置时更新 LAST_UPDATE 列。

CREATE OR REPLACE TRIGGER TGAUDIT_ORDER_POS
AFTER DELETE
    ON ORDER_POSITIONS 
FOR EACH ROW

DECLARE

BEGIN
    UPDATE ORDERS O SET O.LAST_UPDATE = SYSDATE WHERE O.ORDER_KEY = :OLD.ORDER_KEY;   
END;

如果我从表 ORDER_POSITION 中删除一个位置,一切正常(LAST_UPDATE 列已更新)。

但是,如果我想删除一个订单,它的所有仓位也会被删除(通过 CASCADE DELETE)。此时,表 ORDER_POSITIONS 上的触发器也被引发,它想要更新当前正在删除的表的列 - ORDERS。显然我得到了错误:ORA-04091 Table ORDERS is mutating.

有办法解决吗?

【问题讨论】:

  • 向外键添加 DEFERRABLE 子句有帮助吗?
  • 不,我遇到了同样的错误。

标签: sql oracle plsql


【解决方案1】:

我终于用复合触发器解决了:

CREATE OR REPLACE TRIGGER TGAUDIT_ORDER_POS
  FOR DELETE ON ORDER_POSITIONS
    COMPOUND TRIGGER
     
    TYPE parent_key_type IS TABLE OF ORDERS.ORDER_KEY%TYPE;
    parent_keys parent_key_type := parent_key_type();
    
    AFTER EACH ROW IS BEGIN
      IF DELETING THEN 
        BEGIN 
          parent_keys.extend;
          parent_keys(parent_keys.last) := :old.ORDER_KEY;
        END;
      END IF;
    END AFTER EACH ROW;
    
    AFTER STATEMENT IS BEGIN
      FOR i IN 1..parent_keys.count LOOP
        UPDATE DEVART_TEST.ORDERS O SET O.LAST_UPDATE = SYSDATE WHERE O.ORDER_KEY = parent_keys(i);   
      END LOOP;
    END AFTER STATEMENT;
    
END;

更新:另一种选择是在常规触发器中捕获此特定异常。

CREATE OR REPLACE TRIGGER TGAUDIT_ORDER_POS
AFTER DELETE
  ON ORDER_POSITIONS 
  FOR EACH ROW

  DECLARE
    TABLE_MUTATING EXCEPTION;
    PRAGMA EXCEPTION_INIT(TABLE_MUTATING, -4091 );

BEGIN
    UPDATE ORDERS O SET O.LAST_UPDATE = SYSDATE WHERE O.ORDER_KEY = :OLD.ORDER_KEY;   
    EXCEPTION 
      WHEN TABLE_MUTATING THEN 
        NULL; -- suppress 
END;

【讨论】:

    【解决方案2】:

    我将重新创建没有ON DELETE CASCADE 子句的外键约束,并在删除订单之前删除所有订单位置。这样,您就可以避免两个表同时发生变异,从而避免变异表错误。

    【讨论】:

    • 虽然这将是一个解决方案,但在我们的案例中应用它并不容易。原因:我们想为几个表添加这种审计。删除 CASCADE DELETE 约束意味着,我们必须找到并更改大型和旧代码库中删除这些表的所有位置,并通过手动删除它们的子项来扩展这些方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-02
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多