2016-05-15 8 views
2

Ich bin relativ neu zu Triggern, also vergib mir, wenn das nicht so aussieht wie es sollte. Ich erstelle einen Trigger, der ein Benutzerkonto auf das letzte Zahlungsdatum überprüft und einen Wert auf 0 setzt, wenn sie eine Weile nicht bezahlt haben. Ich habe erstellt, was ich dachte, war ein richtiger Auslöser, aber ich bekomme den Fehler, "Fehler während der Ausführung von Trigger", wenn es ausgelöst wird. Nach meinem Verständnis verursacht die Select-Anweisung den Fehler, wenn Werte ausgewählt werden, die gerade geändert werden. Hier ist mein Code.Fehler bei der Ausführung des Triggers. So bearbeiten Sie Select-Anweisungen

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; 

Von dem, was ich dachte, da die Auswahl in der Erklärung beginnen, wäre es vor einem Update ausführen, würde nichts über die Tabellen durch den Auslöser erst nach, wenn laufen Ändern werden. Ich habe versucht, aber mit: alt vor den ausgewählten Variablen, aber das scheint nicht die richtige Verwendung zu sein.

Und hier ist die Update-Anweisung, die ich versuchte.

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

Kann es mehr Benutzerdaten pro Benutzer geben? – trincot

+0

Ja könnte es sein. Es ist ein Rekord für jede Zahlung. – rinaldo13531

Antwort

0

Einige Fragen:

  1. Die select Sie im Trigger tun, um die Variable isactive_check zu einem Zahlungstermin setzt, und umgekehrt. Dort wird versehentlich umgeschaltet, was sich negativ auf die nächste if auswirken wird;

  2. Die gleichen select sollte genau einen Datensatz zurückgeben, die von den Blicken von ihm nicht garantiert ist, da Sie mit Tabelle userpayments, an denen mehrere Zahlungen für den ausgewählten Benutzer haben können, die die Bedingung, oder gar nicht erfüllen. Ändern Sie das select, um eine Aggregation durchzuführen.

  3. Wenn ein Benutzer mehr als einen Zahlungsdatensatz hat, kann die Bedingung für einen, aber nicht für einen anderen gelten. Wenn Sie also nur an Nutzern interessiert sind, die lange nicht bezahlt haben, sollten solche Nutzer nicht berücksichtigt werden, obwohl sie einen alten Zahlungsdatensatz haben. Stattdessen sollten Sie prüfen, ob alle Datensätze die Bedingung erfüllen. Dies können Sie mit einer having Klausel tun.

  4. Da die Tabelle users mutiert (der Aktualisierungstrigger befindet sich in dieser Tabelle), können Sie nicht jede Aktion für dieselbe Tabelle ausführen, da dies sonst zu einer Art Deadlock führen würde. Dies bedeutet, dass Sie überdenken müssen, was der Zweck des Auslösers ist. Da es sich um ein Update für einen bestimmten Benutzer handelt, müssen Sie nicht die gesamte Tabelle überprüfen, sondern nur den Datensatz, der gerade geändert wird. Dazu können Sie die spezielle Variable new verwenden.

würde ich dieses SQL stattdessen vorschlagen:

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

und dann mit den Kontrollen fortsetzen:

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; 

Jetzt haben Sie vermieden etwas in der Tabelle zu tun users und gemacht haben die prüft und ändert über den :new "record".

Auch ist es gute Praxis, die Spaltennamen in einer insert Aussage zu erwähnen, die ich in obigem Code getan habe (Spaltennamen anpassen je nach Bedarf):

Sicherstellen, dass der Auslöser kompiliert und erzeugt keine Kompilierungsfehlern .

+0

Also, was Sie gesagt haben, macht Sinn. Wenn ich die Auswahl ändern kompiliert mit Fehler, "SQL-Anweisung ignoriert" und "ORA-00936: fehlende Ausdruck" und diese scheinen nicht auf etwas Relevantes zu zeigen. – rinaldo13531

+0

Entschuldigung, ich hatte ein baumelndes Komma in diesem SQL. Gelöscht jetzt. – trincot

+0

Yep ich sah, dass es kompiliert, aber ich bekomme immer noch den Fehler. * Ursache: Ein Trigger (oder eine benutzerdefinierte plsql -Funktion, die in diese Anweisung referenziert) versucht, eine Tabelle zu suchen (oder zu ändern), die war in der Mitte von der Aussage geändert werden, die es ausgelöst hat. * Aktion: Schreiben Sie den Trigger (oder die Funktion) neu, damit diese Tabelle nicht gelesen wird. – rinaldo13531