1

我有三个表: A 和 B 关系是 A 可以有很多 B 所以 B 引用 A.id 作为其列之一

Table A
|id|date|...

Table B
|id|A_id|...

我在表 A 上创建了一个 Oracle 触发器,以便在更新它时更新一个 A_Mod 表。

这个触发器是

CREATE OR REPLACE TRIGGER TR_A_INSERT_UPDATE
AFTER  INSERT OR UPDATE ON A
FOR EACH ROW
BEGIN
    INSERT INTO A_Mod values(..., :new.date, ...)
END;

这很好用:)

我的问题是为表 B 创建触发器。

触发器是:

CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
     AFTER  INSERT OR UPDATE ON B
     FOR EACH ROW
     DECLARE
       ts TIMESTAMP;

     BEGIN
   SELECT aa.date INTO ts FROM B bb 
       INNER JOIN A aa ON a.id = bb.A_id
       WHERE bb.id = :new.id;

       INSERT INTO A_Mod values(..., :new.date, ...)
    END;

此触发器正在读取表 B 中更新行的 ID,然后从表 A 中的相应行获取日期。然后尝试将其插入 A_Mod

问题是我得到一个变异错误

Error report:
SQL Error: ORA-04091: table B is mutating, trigger/function may not see it
ORA-06512: at "TR_B_INSERT_UPDATE", line 5
ORA-04088: error during execution of trigger 'TR_B_INSERT_UPDATE'
04091. 00000 -  "table %s.%s is mutating, trigger/function may not see it"
*Cause:    A trigger (or a user defined plsql function that is referenced in
           this statement) attempted to look at (or modify) a table that was
           in the middle of being modified by the statement which fired it.
*Action:   Rewrite the trigger (or function) so it does not read that table.

查看文档,我可以通过删除该FOR EACH ROW行并让触发器在每个语句而不是每行触发一次来消除此错误。不幸的是,我使用的是 ORM 映射器,所以不要控制更新是如何发生的。我认为有时更新可能会覆盖多行。

文档说了一些关于创建临时表的内容,但我不确定这会有什么帮助。我是否必须在触发器内创建一个临时表,在这个临时表上创建一个触发器,然后更新 A_Mod,在触发触发器时更新这个临时表,然后删除之后的所有内容?

非常感谢任何提示。

谢谢

4

1 回答 1

2

您的触发器中似乎没有任何理由B查询该B表。您应该能够A使用:new.a_id

CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
  AFTER  INSERT OR UPDATE ON B
  FOR EACH ROW
DECLARE
   ts TIMESTAMP;
BEGIN
  SELECT a.date 
    INTO ts 
    FROM A a
   WHERE a.id = :new.a_id;

  INSERT INTO A_Mod values(..., :new.date, ...)
END;

但是,从数据建模的角度来看,如果子表上的触发器需要从父表中查询任何信息或将数据插入到父表写入的同一个历史表中,我会非常担心。这似乎表明存在正常化问题。

于 2012-10-24T11:50:24.660 回答