2009-01-10 5 views
17

Angenommen, Sie haben die Tabellen Presentations und Events. Wenn eine Präsentation gespeichert wird und grundlegende Ereignisinformationen wie Ort und Datum enthält, wird automatisch ein Ereignis mithilfe eines Triggers erstellt. (Ich fürchte, es ist aus technischen Gründen nicht möglich, die Daten einfach an einem Ort zu belassen und eine Ansicht zu verwenden.) Wenn diese Informationen später in der Präsentation geändert werden, kopiert der Auslöser die Aktualisierungen ebenfalls auf das Ereignis. etwa so:Verhindern gegenseitig rekursive Ausführung von Triggern?

CREATE TRIGGER update_presentations 
ON Presentations 
AFTER UPDATE 
AS 
BEGIN 
    UPDATE Events 
    SET Events.Date = Presentations.Date, 
     Events.Location = Presentations.Location 
    FROM Presentations INNER JOIN Events ON Presentations.EventID = Events.ID 
    WHERE Presentations.ID IN (SELECT ID FROM inserted) 
END 

Nun möchte der Kunde es so, dass, wenn ein Benutzer immer die Informationen im Ereignis ändert, sollte es auch auf die Präsentation zurück. Aus offensichtlichen Gründen kann ich das Gegenteil nicht tun:

CREATE TRIGGER update_events 
ON Events 
AFTER UPDATE 
AS 
BEGIN 
    UPDATE Presentations 
    SET Presentations.Date = Events.Date, 
     Presentations.Location = Events.Location 
    FROM Events INNER JOIN Presentations ON Events.PresentationID = Presentations.ID 
    WHERE Events.ID IN (SELECT ID FROM inserted) 
END 

Schließlich würde dies jeder Trigger verursachen nacheinander abzufeuern. Was ich tun könnte, ist eine Spalte last_edit_by zu beiden Tabellen hinzufügen, die eine Benutzer-ID enthält. Wenn durch den Trigger mit einer speziellen ungültigen ID gefüllt (etwa durch alle IDs der tatsächlichen Personen Benutzer machen positiv, aber Benutzer-IDs von Skripten negativ), könnte ich das als Ausgangsbedingung verwenden:

AND last_edit_by >= 0 

Dies könnte funktionieren , aber ich möchte dem SQL-Server mitteilen, dass ein Trigger innerhalb einer Transaktion nur einmal ausgelöst werden sollte. Gibt es eine Möglichkeit, dies zu überprüfen? Oder vielleicht um zu überprüfen, ob eine Tabelle bereits von einem Trigger betroffen ist?


Antwort dank Steve Robbins:

einfach die potenziell verschachtelt UPDATE Anweisungen in einer IF-Bedingung für trigger_nestlevel() Überprüfung wickeln. Zum Beispiel:

CREATE TRIGGER update_presentations 
ON Presentations 
AFTER UPDATE 
AS 
BEGIN 
    IF trigger_nestlevel() < 2 
     UPDATE Events 
     SET Events.Date = Presentations.Date, 
      Events.Location = Presentations.Location 
     FROM Presentations INNER JOIN Events ON Presentations.EventID = Events.ID 
     WHERE Presentations.ID IN (SELECT ID FROM inserted) 
END 

Beachten Sie, dass trigger_nestlevel() erscheint 1-basieren, nicht 0-basiert. Wenn Sie möchten, dass jeder der beiden Trigger einmal, aber nicht öfter ausgeführt wird, suchen Sie in beiden Triggern nach trigger_nestlevel() < 3.

Antwort

18

Ich bin mir nicht sicher, es pro Transaktion zu tun, aber brauchen Sie geschachtelte Trigger für andere Teile eingeschaltet? Wenn Sie sie auf dem Server ausschalten, wird ein Trigger nicht von einem anderen Trigger ausgelöst, der eine Tabelle aktualisiert.

EDIT (Antwort aus den Kommentaren): Sie müssen Trigger A verändern TRIGGER_NESTLEVEL

+0

Verschachtelte Trigger tatsächlich auf diese Datenbank deaktiviert zu verwenden. Mein Problem ist ein Fall von Trigger A feuernd B feuernd A, nicht feuernd A direkt. Die Einstellung verhindert dies nicht. –

+0

Das ist rekursive Trigger, ich bin im Moment nicht an einer Maschine, aber ich bin sicher, es gibt auch verschachtelte Trigger, die Sie mit sp_config konfigurieren können. Vor ein paar Jahren war ich mit diesem Problem konfrontiert. –

+0

Sie haben Recht; geschachtelte Trigger ist eine andere Option. Aber so, wie ich seine Dokumentation lese, würde es B daran hindern, nach A zu feuern, während ich verhindern möchte, dass A ein zweites Mal feuert. –