如何在 oracle 中使用触发器插入后更新表时修复表突变异常?

How to fix table is mutating exception while updating the table after insert using trigger in oracle?

提问人:Turing 提问时间:11/16/2023 最后编辑:Thorsten KettnerTuring 更新时间:11/16/2023 访问量:22

问:

我已经制作了一个触发器来更新表“SchemeComp”行,同时插入新行。我一次插入多个/批量行,但它抛出异常并且该行未插入。这是我得到的例外:

Error report -
ORA-04091: table ABC009.SchemeComp is mutating, trigger/function may not see it
ORA-06512: at "ABCS009.TrCreateParentChildRelation", line 3
ORA-04088: error during execution of trigger 'ABCS009.TrCreateParentChildRelation' `

这是我对触发器的查询:

CREATE OR REPLACE TRIGGER "TrCreateParentChildRelation"
AFTER INSERT ON "SchemeComp"
FOR EACH ROW
BEGIN
  IF :NEW."ParentComponentCode" IS NOT NULL THEN
    UPDATE "SchemeComp" sc1
    SET sc1."ParentId" = (
        SELECT sc2."Id"
        FROM "SchemeComp" sc2
        WHERE sc1."ParentComponentCode" = sc2."ComponentCode"
          AND sc1."SchemeId" = sc2."SchemeId"
    )
    WHERE sc1."ParentComponentCode" IS NOT NULL
      AND sc1."SchemeId" = :NEW."SchemeId";
  END IF;
END;
/

下面是我的插入查询,我同时运行两个插入查询。

INSERT INTO "SchemeComp" ("ParentId", "ComponentCode", "ParentComponentCode", "ComponentName", "SchemeId", "EntryDate", "IsActive", "CreatedBy", "IP")
VALUES (null, 'B', null, 'Tribal Area Sub Plan', '0R55', SYSTIMESTAMP, '1', 'BANKADMIN002', '::1');

INSERT INTO "SchemeComp" ("ParentId", "ComponentCode", "ParentComponentCode", "ComponentName", "SchemeId", "EntryDate", "IsActive", "CreatedBy", "IP")
VALUES (null, 'B.1', 'B', 'Recurring', '0R55', SYSTIMESTAMP, '1', 'BANKADMIN002', '::1');
Oracle 触发器

评论

0赞 Thorsten Kettner 11/16/2023
DBMS 告诉您它正在更改表,因此您无法查询表,因为结果不是确定性的,因为行可能会以任意顺序插入,从而不可预测地影响查询结果。你到底想做什么?为什么插入新行会影响现有行?而且,为了更好地理解您的表:行代表什么?表的唯一键是什么?

答:

1赞 Littlefoot 11/16/2023 #1

您不能直接(或从中选择值)要插入行的表,因为它 - 正如错误所说 - 正在变异update

但是,如果您重写触发器,以便首先选择所需的值,然后更新它,那么它应该没问题。 像这样:

CREATE OR REPLACE TRIGGER "TrCreateParentChildRelation"
   BEFORE INSERT
   ON "SchemeComp"
   FOR EACH ROW
DECLARE
   l_parentid  "SchemeComp"."ParentId"%TYPE;
BEGIN
   IF :NEW."ParentComponentCode" IS NOT NULL
   THEN
      SELECT sc2."Id"
        INTO l_parentid
        FROM "SchemeComp" sc2
       WHERE     sc2."ComponentCode" = :new."ParentComponentCode"
             AND sc2."SchemeId" = :new."SchemeId";

      :new."ParentId" := l_parentid;
   END IF;
END;

评论

1赞 Thorsten Kettner 11/16/2023
我看到这被接受了,并对此感到惊讶。触发器是在每行之前还是之后都没有关系。由于插入,表正在发生变化。单个 insert 语句可以任意顺序插入多行,因此我们不能查询表,因为结果不会是确定性的。也许这恰好有效,只要我们只插入单行,否则就会失败。我不知道。
1赞 Littlefoot 11/16/2023
啊!傻我,@Thorsten!我什至没有注意到它大约是同一张桌子!相反,我关注的是混合字母大小写。你是对的,除非插入单行,否则这是行不通的。经过一些测试后,我们将看到图灵说了什么。如果事实证明这个答案是无用的,我会删除它。
0赞 Turing 11/16/2023
@Littlefoot这很好用!谢谢。
0赞 Littlefoot 11/16/2023
别客气。