2017-04-23 67 views
0

我試圖在插入觸發器後更新一些數據,但我對語法感到困惑,不知道我在做什麼錯誤。任何人都可以分享你的意見。謝謝。在將數據插入到表中後使用觸發器更新列

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

嚴重?您在更新語句中缺少分號。 – OldProgrammer

+1

更新後更新觸發器中的同一個表...突變表異常看起來像這將最終結束。 – MT0

+0

好吧,至少如果它插入後,它應該沒問題。 – Teja

回答

1

如果可能的話在你的使用情況,我建議切換到BEFORETRIGGER和變異傳入: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  

+0

是否可以將已插入數據的觸發器應用於表中? – Teja

+0

謝謝@Teja是的,在這篇文章的三個例子中,第二個(簡單的語句級觸發器)將應用於已經在表中的數據。您可以通過創建示例表來進行測試,添加一些應該有罰款但沒有的行,然後創建觸發器並添加新數據。新數據和舊數據都將被更新。或者,如果按照第一個示例中的建議切換到BEFORE觸發器,則可以在創建觸發器時對所有現有數據執行一次性更新,以確保所有現有數據處於預期狀態。這些選項是否適合你? – alexgibbs

+0

我試過你的第二個例子簡單語句級觸發器,但它不更新表中的現有數據... – Teja

相關問題