【问题标题】:Table is mutating, trigger/function may not see it ORA-06512 [duplicate]表正在变异,触发器/函数可能看不到它 ORA-06512 [重复]
【发布时间】:2016-02-24 08:22:37
【问题描述】:

我有两个名为 DetailRental 和 Video 的表。 VID_NUM 是 Video 的 PK 和 DetailRental 的 FK。

这段代码想要实现的是,当 DetailRental 表中的 Detail_Returndate 或 Detail_Duedate 发生变化(更新或插入新行)时,触发器将逐行检查 Detail_Returndate 的值。如果其值为空,则视频表中对应的(根据 VID_NUM)属性 VID_STATUS 将更改为“OUT”。

触发器已成功创建。但是,当我想更新日期时。甲骨文给我错误:

ORA-04091: table SYSTEM2.DETAILRENTAL is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM2.TRG_VIDEORENTAL_UP", line 3
ORA-04088: error during execution of trigger 'SYSTEM2.TRG_VIDEORENTAL_UP'

1. UPDATE DETAILRENTAL
2. SET DETAIL_RETURNDATE = null
3. WHERE RENT_NUM = 1006 AND VID_NUM = 61367 

下面是我的代码:

CREATE OR REPLACE TRIGGER trg_videorental_up
  AFTER INSERT OR UPDATE OF DETAIL_RETURNDATE, DETAIL_DUEDATE ON DETAILRENTAL
  FOR EACH ROW
AS
  DECLARE
    DTRD DATE;
  BEGIN
    SELECT DETAIL_RETURNDATE
      INTO DTRD
      FROM DETAILRENTAL;
    IF DTRD IS NULL
      THEN UPDATE VIDEO
        SET VIDEO.VID_STATUS = 'OUT'
        WHERE EXISTS
          (SELECT DETAILRENTAL.VID_NUM
            FROM DETAILRENTAL
            WHERE DETAILRENTAL.VID_NUM = VIDEO.VID_NUM
          );
    END IF;
END;

非常感谢!

这里解决的问题是代码:

CREATE OR REPLACE TRIGGER trg_videorental_up
AFTER INSERT OR UPDATE OF DETAIL_RETURNDATE, DETAIL_DUEDATE ON DETAILRENTAL
FOR EACH ROW
DECLARE DETAIL_RETURNDATE DATE;
BEGIN
IF :NEW.DETAIL_RETURNDATE IS NULL THEN UPDATE VIDEO SET VID_STATUS = 'OUT' WHERE VID_NUM = :NEW.VID_NUM;
ELSIF :NEW.DETAIL_RETURNDATE > SYSDATE THEN UPDATE VIDEO SET VID_STATUS = 'OUT' WHERE VID_NUM = :NEW.VID_NUM;
ELSIF :NEW.DETAIL_RETURNDATE <= SYSDATE AND TO_CHAR(DETAIL_RETURNDATE)!= '01/01/0001' THEN UPDATE VIDEO SET VID_STATUS = 'IN' WHERE VID_NUM = :NEW.VID_NUM;
ELSIF :NEW.DETAIL_RETURNDATE = '01/01/0001' THEN UPDATE VIDEO SET VID_STATUS = 'LOST' WHERE VID_NUM = :NEW.VID_NUM;
END IF;
END;

【问题讨论】:

  • 问题已解决。这是代码:
  • 这个问题可能是重复的,但是如果风吹错了方向,Oracle 中的突变触发器就会发生。触发器,不知道为什么要这样命名它们,因为它们不是用于事件驱动的编程。当然,您可以更新您的“updated_by”列 - 但除此之外,与 Microsoft SQL Server 相比,它就是一块粪土。

标签: oracle if-statement plsql triggers sql-update


【解决方案1】:

一个好的数据模型是没有物理存储冗余信息的模型。如果您可以查看 table.column 中的一个(或多个)值并找出另一个 table.column 中的值应该是什么,那么您就有了冗余。在您的情况下,一个人可以看到 VIDNUM 61367 的 DETAILRENTAL.DETAIL_DUEDATE 不为空,并且“知道” VIDEO.STATUS 字段应该为 OUT。

最容易修复的方法是:
1) 创建一个 VIDEO_BASE 表,包含除 VID_STATUS 之外的所有 VIDEO 列:

CREATE TABLE VIDEO_BASE AS 
SELECT {list all columns except STATUS}
FROM VIDEO;  

2) 删除原始 VIDEO 表并创建为视图 VIDEO,它显示 VIDEO_BASE 的所有列,并将 STATUS 公开为派生字段:

CREATE OR REPLACE VIEW VIDEO 
AS 
SELECT  V.*,
        CASE WHEN 
            (
            SELECT COUNT(*) 
            FROM
                (
                SELECT 'X'
                FROM DETAILRENTAL D
                WHERE D.VID_NUM = V.VID_NUM 
                      AND DETAIL_RETURNDATE IS NOT NULL
                      AND ROWNUM <= 1
                )
            ) > 0 
        THEN 'OUT' 
        ELSE NULL
        END VID_STATUS
FROM VIDEO_BASE V;

一般来说,如果您觉得需要一个触发器来保持两个不同的表同步,那么您就遇到了数据模型问题。在我 15 年以上的 Oracle 经验中,修复有问题的触发器的唯一最佳方法是修复数据模型 - 知道所有触发器是否正常工作的最可靠方法是当数据库中的触发器数量为 0 时。

【讨论】:

    【解决方案2】:

    在阅读了@KevinKirkpatrick 的回答两三遍后,我意识到他是对的——单个视频的输入/输出状态可以从数据库中的其他信息推导出来。也就是说,您这样做可能有实际的理由。

    坏消息是,您无法从同一个表的行触发器中的表中进行选择 - 这就是“变异表”问题的含义。好消息是,在这种情况下,您真的不需要。

    我没有可以测试它的 Oracle 安装,所以我不保证语法正确性,但它应该足够接近让你开始。

    CREATE OR REPLACE TRIGGER trg_videorental_up
      AFTER INSERT OR UPDATE
        OF detail_duedate, detail_returndate
      ON detailrental
      FOR EACH ROW
    AS
    BEGIN
      IF :new.detail_returndate IS NULL
        AND :new.detail_duedate IS NOT NULL
      THEN
        UPDATE video
          SET status = 'OUT'
          WHERE video_num = :new.video_num;
      END IF;
    END;
    

    【讨论】:

      猜你喜欢
      • 2015-06-28
      • 1970-01-01
      • 2021-12-20
      • 2010-09-27
      • 2013-11-24
      • 2016-08-11
      • 1970-01-01
      • 2016-04-29
      相关资源
      最近更新 更多