【问题标题】:trigger - select count into - ORA-04091触发器 - 选择计数到 - ORA-04091
【发布时间】:2013-02-13 14:13:21
【问题描述】:

我想在表 (CMS_CONTENT_ENROLLMENT) 上插入或更新,然后对同一个表进行计数并将结果更新到另一个表中。遗憾的是,我收到了 ORA-04091:表名正在发生变化,触发器/函数可能看不到它

CREATE OR REPLACE TRIGGER CMS_CONTENT_ENROLLMENT_CNT_TR
    AFTER INSERT OR UPDATE
    ON CMS_CONTENT_ENROLLMENT
    FOR EACH ROW
DECLARE
BEGIN
    UPDATE CMS_CONTENT CNT
    SET CNT.REGISTRATIONCOUNT =
    (
        SELECT COUNT (ENROLLMENTID)
        FROM CMS_CONTENT_ENROLLMENT
        WHERE DELETED = 0 AND CONTENTID = CNT.CONTENTID
    )
    WHERE CNT.CONTENTID = :NEW.CONTENTID;
END;
/

【问题讨论】:

  • 为什么需要在 CMS_CONTENT 中维护这个计数,因为您可以随时查询 CMS_CONTENT_ENROLLMENT?无论如何,您已经对这两者进行了规范化,但随后转身想要将片段(同时)非规范化为 cms_content。 KISS 并根据需要查询,请勿尝试维护此计数器 imo。

标签: oracle triggers oracle11g


【解决方案1】:

警告:我认为在触发器中嵌入复杂的应用程序逻辑代码不是一个好主意,因为代码分布在许多对象中,并且很难测试和维护。通常 ORA-04091 表明您正在尝试执行一些对于触发器来说过于复杂的事情。这可以被规避,但会导致复杂的逻辑在某些方面经常存在缺陷,尤其是在像数据库这样的多用户环境中。因此,如果您需要汇总表,我建议您使用常规视图、物化视图或过程逻辑。

如果你想使用触发器,你可以让它使用增量逻辑,像这样(未经测试,假设你永远不会更新contendiddeleted 不是 NULL,你永远不会从表并且该行存在于汇总表中):

CREATE OR REPLACE TRIGGER CMS_CONTENT_ENROLLMENT_CNT_TR
   AFTER INSERT OR UPDATE ON CMS_CONTENT_ENROLLMENT
   FOR EACH ROW
DECLARE
BEGIN
   IF :NEW.deleted = 0 AND ((inserting) OR :OLD.deleted != 0) THEN
      UPDATE CMS_CONTENT CNT
         SET CNT.REGISTRATIONCOUNT = CNT.REGISTRATIONCOUNT + 1
       WHERE cnt.contentid = :NEW.contentid;
   ELSIF :NEW.deleted != 0 AND :OLD.deleted = 0 THEN
      UPDATE CMS_CONTENT CNT
         SET CNT.REGISTRATIONCOUNT = CNT.REGISTRATIONCOUNT - 1
       WHERE cnt.contentid = :NEW.contentid;
   END IF;
END;
/

【讨论】:

  • 非常感谢您的意见,我一定会认真考虑您的警告!
【解决方案2】:

您的FOR EACH ROW 触发器正在查询它插入/更新的同一个表。由于触发器会针对每一行触发,因此查询的结果(如果允许的话)将取决于行的处理顺序是不确定的 - 这是不合逻辑的,因此 Oracle 提出了ORA-04091: table name is mutating, trigger/function may not see it

此外,我强烈赞同文森特关于在多用户(甚至多会话)环境中编写基于触发器的逻辑的警告。 Oracle 将允许您的代码的多个版本同时运行,并且它们在提交之前不会看到彼此的更改。您的触发器已经“计算”了当时可以看到的行数,这可能意味着 两个 会话都会出错。

解决这个问题的唯一方法是 (a) 构建您的应用程序,使其不需要在任何地方存储计数,而是在需要时动态查询它;或者 (b) 引入某种形式的序列化来阻止多个进程同时尝试对表进行插入/更新。

我强烈推荐选项 (a)。选项 (b) 很容易出错,除非您首先非常了解 Oracle 的工作原理。

【讨论】:

  • 感谢您的回答,我将使用即时版本!
猜你喜欢
  • 1970-01-01
  • 2018-11-30
  • 2013-05-13
  • 1970-01-01
  • 2011-05-21
  • 2015-08-03
  • 2020-02-21
  • 2015-06-28
  • 2012-05-17
相关资源
最近更新 更多