我有两个名为DetailRental和Video的表。 VID_NUM是Video的PK和DetailRental的FK。
此代码想要实现的是当DetailRental表中的Detail_Returndate或Detail_Duedate更改(更新或插入新行)时,触发器将逐行检查Detail_Returndate的值。如果其值为null,则视频表中的相应(根据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;
答案 0 :(得分:2)
良好的数据模型是没有物理存储冗余信息的模型。如果你可以查看table.column中的一个(或多个)值并找出另一个table.column中的值,那么你就有了冗余。在您的情况下,一个人可以看到一个DETAILRENTAL.DETAIL_DUEDATE为VIDNUM 61367不为空,并且&#34;知道&#34; 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时。
答案 1 :(得分:0)
在阅读@KirkKirkpatrick的答案两三次之后,我意识到他是对的 - 个人视频的进/出状态 可以从数据库中的其他信息中导出。也就是说,你可能有这样做的实际理由。
坏消息是你不能从同一个表中的行触发器中选择表 - 这就是“变异表”问题的含义。好消息是,在这种情况下,你真的不需要。
我没有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;