oracle - 表正在发生变化,触发器/函数可能看不到它 ORA-06512

标签 oracle if-statement plsql triggers sql-update

我有两个表,分别称为 DetailRental 和 Video。 VID_NUM是Video的PK和DetailRental的FK。

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

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

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;

最佳答案

好的数据模型是不物理存储冗余信息的模型。如果您可以查看 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 表并创建一个 View VIDEO,该 View 显示 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 时。

关于oracle - 表正在发生变化,触发器/函数可能看不到它 ORA-06512,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33850539/

相关文章:

sql - 如何在类型对象上创建索引?

oracle - 创建 Oracle PL/SQL 存储过程

sql - 如何简化 where 子句中的嵌套 select?

iOS内联if else编译错误: "Expected : "; "Expected expression"

php - SELECT 1 FROM 和 if 语句

javascript - 简单的 Javascript 我不知道如何将函数放在 if-else 语句中

sql - 如何在Oracle中使用Timestamp_to_scn和Scn_to_timestamp?

oracle - 为 Delphi + Oracle 构建自动化

xml - PL SQL XMLDOM 改变行结尾

oracle - 如何以编程方式从 oracle 表单连接到数据库