我试图使用插入触发器后更新一些数据,但我对语法感到困惑,不知道我做错了什么。任何人都可以分享你的意见。谢谢。
Trigger code:-
CREATE OR REPLACE TRIGGER calculate_fine_amt
AFTER INSERT OR UPDATE ON BORROWED_BY
FOR EACH ROW
BEGIN
UPDATE BORROWED_BY B
SET B.FINE = CASE WHEN B.DUEDATE-B.RETURNDATE < 0
THEN ABS( B.DUEDATE-B.RETURNDATE )*5
ELSE 0
END
END
;
答案 0 :(得分:1)
如果可能在您的使用案例中,我建议您切换到BEFORE
TRIGGER
并改变传入的:NEW.FINE
,前提是没有多个触发器会干扰彼此。如果AFTER
对您不适用,我将在下面进一步列出BEFORE
个替代方案。
使用BEFORE TRIGGER
可以避免更新整个表的成本,并避免在其触发器中附加表上的任何UPDATE
发生的变异表异常,并允许触发器在INSERT OR UPDATE
。
以下是一个例子:
首先创建测试表(可能这里有关于借用项目的其他列。)
CREATE TABLE BORROWED_BY (
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
然后创建触发器(我建议截断日期{或者将某些四舍五入到混音中},这样您的FINE
就是整数):
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
BEFORE INSERT OR UPDATE ON BORROWED_BY
FOR EACH ROW
BEGIN
:NEW.FINE := CASE WHEN (:NEW.DUEDATE - :NEW.RETURNDATE < 0)
THEN ABS(TRUNC(:NEW.DUEDATE) - TRUNC(:NEW.RETURNDATE)) * 5
ELSE 0 END;
END;
/
然后测试一下:
按时返回
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE)
VALUES (SYSDATE - 10, SYSDATE - 20);
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-APR-17 03-APR-17 0
然后更新它:
UPDATE BORROWED_BY SET RETURNDATE = SYSDATE;
DUEDATE RETURNDATE FINE
13-APR-17 23-APR-17 50
然后再次更新:
UPDATE BORROWED_BY SET RETURNDATE = SYSDATE - 100;
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-APR-17 13-JAN-17 0
新项目迟到了:
ROLLBACK;
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE)
VALUES (SYSDATE - 5, SYSDATE);
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
18-APR-17 23-APR-17 25
如果这确实必须是AFTER
触发器,那么会有更高的成本关联,我建议您只切换到AFTER INSERT
而不是INSERT OR UPDATE
以避免递归触发器执行。正如其他人在评论中指出的那样,您需要切换到语句级(或复合)触发器。
以下是一个例子:
作为语句级触发器。这很昂贵(并且可能与其他触发器有不良的交互等),因为它会在每个语句后更新整个表:
编辑:添加语句级触发器更新现有数据的演示。
DROP TABLE BORROWED_BY;
CREATE TABLE BORROWED_BY (
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
添加一些不正确的初始数据:
INSERT INTO BORROWED_BY VALUES (SYSDATE - 100, SYSDATE, -1919);
INSERT INTO BORROWED_BY VALUES (SYSDATE - 200, SYSDATE, -1919);
INSERT INTO BORROWED_BY VALUES (SYSDATE - 300, SYSDATE, -1919);
COMMIT;
并验证它:
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-JAN-17 23-APR-17 -1919
05-OCT-16 23-APR-17 -1919
27-JUN-16 23-APR-17 -1919
然后创建触发器:
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
AFTER INSERT ON BORROWED_BY
BEGIN
UPDATE BORROWED_BY
SET FINE = CASE WHEN (DUEDATE - RETURNDATE < 0)
THEN ABS(TRUNC(DUEDATE) - TRUNC(RETURNDATE)) * 5
ELSE 0 END;
END;
/
并添加新数据:
INSERT INTO BORROWED_BY VALUES (SYSDATE, SYSDATE, NULL);
检查旧记录和新记录:
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-JAN-17 23-APR-17 500
05-OCT-16 23-APR-17 1000
27-JUN-16 23-APR-17 1500
23-APR-17 23-APR-17 0
所有记录的FINE都已更新。
结束修改
如果表变大,您可以(可能始终建议使用基准测试)通过复合触发器(稍微)降低成本。这是一个一般的例子,但我建议比较替代方案和调整您的用例。
使用主键重新创建表:
CREATE TABLE BORROWED_BY (
BORROWED_BY_ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
然后创建一个复合触发器:
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
FOR INSERT ON BORROWED_BY
COMPOUND TRIGGER
TYPE NUMBER_TAB IS TABLE OF NUMBER;
V_KEYS NUMBER_TAB;
BEFORE STATEMENT IS
BEGIN
V_KEYS := NUMBER_TAB();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
V_KEYS.EXTEND;
V_KEYS(V_KEYS.COUNT) := :NEW.BORROWED_BY_ID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR BORROWED_POINTER IN 1..V_KEYS.COUNT
LOOP
UPDATE BORROWED_BY
SET FINE = CASE WHEN (DUEDATE - RETURNDATE < 0)
THEN ABS(TRUNC(DUEDATE) - TRUNC(RETURNDATE)) * 5
ELSE 0 END
WHERE BORROWED_BY_ID = V_KEYS(BORROWED_POINTER);
END LOOP;
END AFTER STATEMENT;
END CALCULATE_FINE_AMT;
/
然后测试它:
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 5, SYSDATE);
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 20, SYSDATE - 10);
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 30, SYSDATE - 40);
SELECT *
FROM BORROWED_BY;
BORROWED_BY_ID DUEDATE RETURNDATE FINE
4 18-APR-17 23-APR-17 25
5 03-APR-17 13-APR-17 50
6 24-MAR-17 14-MAR-17 0