2016-05-15 74 views
2

我是比較新的觸發器,所以原諒我,如果這看起來不應該如此。我創建了一個觸發器,用於檢查上次付款日期的用戶帳戶,如果他們沒有付款,則將值設置爲0。我創建了我認爲是正確的觸發器,但當觸發它時,我得到錯誤「觸發器執行期間的錯誤」。據我所知,select語句在選擇正在被更改的值時會導致錯誤。這是我的代碼。執行觸發器時出錯。如何重新選擇語句?

CREATE OR REPLACE TRIGGER t 
    BEFORE 
    UPDATE OF LASTLOGINDATE 
    ON USERS 
    FOR EACH ROW 
DECLARE 
    USER_CHECK NUMBER; 
    PAYMENTDATE_CHECK DATE; 
    ISACTIVE_CHECK CHAR(1); 

BEGIN 
    SELECT U.USERID, U.ISACTIVE, UP.PAYMENTDATE 
    INTO USER_CHECK, PAYMENTDATE_CHECK, ISACTIVE_CHECK 
    FROM USERS U JOIN USERPAYMENTS UP ON U.USERID = UP.USERID 
    WHERE UP.PAYMENTDATE < TRUNC(SYSDATE-60); 

    IF ISACTIVE_CHECK = 1 THEN 
    UPDATE USERS U 
    SET ISACTIVE = 0 
    WHERE U.USERID = USER_CHECK; 

    INSERT INTO DEACTIVATEDUSERS 
    VALUES(USER_CHECK,SYSDATE); 

END IF; 


END; 

從我心想,既然選擇是在開始的語句,它會運行一個更新之前,不會有任何改變有關的表,直到後,如果通過觸發運行。我嘗試過,但是在選擇變量前面使用:old,但似乎沒有正確的用法。

這裏是我試圖更新的聲明。

UPDATE USERS 
SET LASTLOGINDATE = SYSDATE 
WHERE USERID = 5; 
+0

每個用戶可以有多個用戶付款記錄嗎? – trincot

+0

是的,可以。這是每次付款時的記錄。 – rinaldo13531

回答

0

的一些問題:

  1. select您在觸發做將變量isactive_check的付款日期,反之亦然。那裏有一個意外開關,這將對下一個if產生負面影響;

  2. 同樣select應返回一個記錄,這由它的外觀是沒有保證的,因爲你有桌子userpayments,這可能對滿足條件的,或者根本就沒有選擇的用戶數支付加盟。更改該select做一個聚合。

  3. 如果用戶有多個付款記錄,則該條件可能對一個記錄爲真,而對另一個記錄則不適用。因此,如果您只對尚未付款的用戶感興趣,則不應將此用戶包括在內,即使他們有舊的付款記錄。相反,您應該檢查所有記錄是否符合條件。這可以通過having子句來完成。

  4. 由於表users正在發生變化(更新觸發器在該表上),因此您無法對該表執行所有操作,否則會導致某種死鎖。這意味着你需要重新思考觸發的目的是什麼。由於這是關於特定用戶的更新,所以實際上不需要檢查整個表格,而只需檢查正在更改的記錄。爲此,您可以使用特殊的new變量。

我建議這個SQL來代替:

SELECT MAX(UP.PAYMENTDATE) 
INTO  PAYMENTDATE_CHECK 
FROM  USERPAYMENTS 
WHERE USERID = :NEW.USERID 

,然後繼續進行檢查:

IF :NEW.ISACTIVE = 1 AND PAYMENTDATE_CHECK < TRUNC(SYSDATE-60) THEN 
    :NEW.ISACTIVE := 0; 

    INSERT INTO DEACTIVATEDUSERS (USER_ID, DEACTIVATION_DATE) 
    VALUES(USER_CHECK,SYSDATE); 

END IF; 

現在你已經避免了在表users做任何事情,並已取得了通過:new「記錄」進行檢查和修改。

此外,它是很好的做法,何況在insert語句中的列名,我在上面的代碼做了(根據需要適應列名):

確保觸發編譯並不會產生編譯錯誤。

+0

所以你說的是有道理的。如果我改變它的編譯錯誤,「SQL語句被忽略」和「ORA-00936:缺少表達式」,那些似乎沒有指向任何相關的東西。 – rinaldo13531

+0

對不起,我在那個SQL中有一個懸而未決的逗號。現在刪除。 – trincot

+0

是的,我看到,它編譯,但我仍然得到錯誤。*原因:觸發器(或 此引用中引用的用戶定義的plsql函數)試圖查看(或修改)一個表是 在被解僱的陳述中修改。 *操作:重寫觸發器(或函數),使其不讀取該表。 – rinaldo13531