如果可能的話在你的使用情況,我建議切換到BEFORE
TRIGGER
和變異傳入:NEW.FINE
,只要不是玩多個觸發器會互相干擾。如果BEFORE
不適用於您,我會在下面概述AFTER
替代方案。
使用BEFORE TRIGGER
避免了更新整個表的成本,並避免將任何UPDATE
發生在其觸發附表以及在變異表外,並允許在INSERT OR UPDATE
觸發。
下面是一個例子:
首先創建測試表(據推測,這裏還有其他列關於借項)
CREATE TABLE BORROWED_BY (
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
然後創建觸發器(我建議截取日期{或得到一些四捨五入到混合},所以你FINE
s爲整數):
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
所有記錄都已經有自己的罰款更新。
末編輯
如果表變大,你可以(可能的話,總是建議基準),降低成本(有點)通過複合觸發。這裏是一個通用的例子,但我會建議比較替代方案和針對您的用例進行調整。
重新創建表的主鍵:
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
嚴重?您在更新語句中缺少分號。 – OldProgrammer
更新後更新觸發器中的同一個表...突變表異常看起來像這將最終結束。 – MT0
好吧,至少如果它插入後,它應該沒問題。 – Teja