Udostępnij za pośrednictwem


Tworzenie zagnieżdżonych wyzwalaczy

Dotyczy:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Wyzwalacze DML i DDL są zagnieżdżone, gdy wyzwalacz wykonuje akcję, która uruchamia inny wyzwalacz. Te akcje mogą inicjować inne wyzwalacze itd. Wyzwalacze DML i DDL można zagnieżdżać do 32 poziomów. Możesz kontrolować, czy wyzwalacze AFTER można zagnieżdżać za pomocą opcji konfiguracji serwera nested triggers. Wyzwalacze INSTEAD OF (tylko wyzwalacze DML mogą być wyzwalaczami INSTEAD OF) można zagnieżdżać niezależnie od tego ustawienia.

Notatka

Każde odwołanie do kodu zarządzanego z wyzwalacza Transact-SQL jest liczone jako jeden poziom w ramach limitu 32 poziomów zagnieżdżania. Metody wywoływane z poziomu kodu zarządzanego nie są liczone względem tego limitu.

Jeśli zagnieżdżone wyzwalacze są dozwolone, a wyzwalacz w łańcuchu uruchamia nieskończoną pętlę, poziom zagnieżdżania jest przekraczany i wyzwalacz kończy się.

Zagnieżdżone wyzwalacze umożliwiają wykonywanie przydatnych funkcji zarządzających, takich jak przechowywanie kopii zapasowej dla wierszy, wywołanych przez poprzedni wyzwalacz. Można na przykład utworzyć wyzwalacz na PurchaseOrderDetail, który zapisuje kopię zapasową wierszy PurchaseOrderDetail usuniętych przez wyzwalacz delcascadetrig. Po uruchomieniu wyzwalacza delcascadetrig usunięcie PurchaseOrderID 1965 z PurchaseOrderHeader spowoduje usunięcie odpowiedniego wiersza lub wierszy z PurchaseOrderDetail. Aby zapisać dane, możesz utworzyć wyzwalacz DELETE w PurchaseOrderDetail, który zapisuje usunięte dane w innej oddzielnie utworzonej tabeli, del_save. Na przykład:

CREATE TRIGGER Purchasing.savedel  
   ON Purchasing.PurchaseOrderDetail  
FOR DELETE  
AS  
   INSERT del_save
   SELECT * FROM deleted;  

Nie zalecamy używania zagnieżdżonych wyzwalaczy w sekwencji zależnej od kolejności. Użyj oddzielnych wyzwalaczy, aby kaskadowo modyfikować dane.

Notatka

Ponieważ wyzwalacze są wykonywane w ramach transakcji, niepowodzenie na dowolnym poziomie zestawu zagnieżdżonych wyzwalaczy anuluje całą transakcję, a wszystkie modyfikacje danych zostaną wycofane. W wyzwalaczach umieść instrukcje PRINT, aby móc określić, gdzie wystąpił błąd.

Rekursywne wyzwalacze

Wyzwalacz AFTER nie wywołuje się rekursywnie, chyba że ustawiono opcję RECURSIVE_TRIGGERS bazy danych.

Istnieją dwa typy rekursji:

  • Rekursja bezpośrednia

    Ta rekursja występuje, gdy wyzwalacz jest uruchamiany i wykonuje akcję, która powoduje ponowne uruchomienie tego samego wyzwalacza. Na przykład aplikacja aktualizuje tabelę T3; powoduje to uruchomienie wyzwalacza Trig3. Trig3 ponownie aktualizuje tabelę T3; to powoduje ponowne uruchomienie wyzwalacza Trig3.

    Rekursja bezpośrednia może również wystąpić, gdy ten sam wyzwalacz jest ponownie wywoływany, ale dzieje się to po wywołaniu wyzwalacza innego typu (AFTER lub ZAMIAST). Innymi słowy, bezpośrednia rekursja wyzwalacza ZAMIAST może wystąpić, gdy ten sam wyzwalacz ZAMIAST jest wywoływany po raz drugi, nawet jeśli jeden lub więcej wyzwalaczy AFTER jest wywoływanych między. Podobnie bezpośrednia rekursja wyzwalacza typu AFTER może wystąpić, gdy ten sam wyzwalacz AFTER jest wywoływany po raz drugi, nawet jeśli jeden lub więcej wyzwalaczy ZAMIAST jest wywoływany w międzyczasie. Na przykład aplikacja aktualizuje tabelę T4. Ta aktualizacja powoduje uruchomienie wyzwalacza INSTEAD OF, Trig4. Trig4 aktualizuje tabelę T5. Ta aktualizacja powoduje uruchomienie wyzwalacza typu AFTER Trig5. Trig5 aktualizuje tabelę T4, a ta aktualizacja powoduje ponowne uruchomienie wyzwalacza Trig4. Ten łańcuch zdarzenia jest uważany za bezpośrednią rekurencję dla Trig4.

  • Rekursja pośrednia

    Ta rekursja występuje, gdy wyzwalacz jest uruchamiany i wykonuje akcję, która powoduje uruchomienie innego wyzwalacza tego samego typu (AFTER lub ZAMIAST). Ten drugi wyzwalacz wykonuje akcję, która powoduje ponowne uruchomienie oryginalnego wyzwalacza. Innymi słowy, rekursja pośrednia może wystąpić, gdy wyzwalacz INSTEAD OF zostanie wywołany po raz drugi, ale tylko wtedy, gdy inny wyzwalacz INSTEAD OF został wywołany pomiędzy nimi. Podobnie, rekursja pośrednia może mieć miejsce, gdy wyzwalacz AFTER jest wywoływany po raz drugi, ale nie wcześniej niż po wywołaniu innego wyzwalacza AFTER pomiędzy. Na przykład aplikacja aktualizuje tabelę T1. Ta aktualizacja powoduje uruchomienie wyzwalacza AFTER Trig1. Trig1 aktualizuje tabelę T2, a ta aktualizacja powoduje uruchomienie wyzwalacza AFTER Trig2. trig2 z kolei aktualizuje tabelę T1, która powoduje ponowne uruchomienie wyzwalacza AFTER Trig1.

Bezpośrednia rekursja wyzwalaczy AFTER jest zapobiegana tylko wtedy, gdy opcja bazy danych RECURSIVE_TRIGGERS jest ustawiona na WYŁĄCZONE. Aby wyłączyć pośredni rekursję wyzwalaczy AFTER, ustaw również zagnieżdżone wyzwalacze opcji serwera, aby 0.

Przykłady

W poniższym przykładzie pokazano użycie wyzwalaczy cyklicznych w celu rozwiązania relacji odwołującej się do siebie (nazywanej również zamknięciem przechodnim). Na przykład tabela emp_mgr definiuje następujące elementy:

  • Pracownik (emp) w firmie.

  • Menedżer dla każdego pracownika (mgr).

  • Całkowita liczba pracowników w drzewie organizacyjnym podlegających każdemu pracownikowi (NoOfReports).

Rekurencyjny wyzwalacz AKTUALIZACJI może być użyty do aktualizowania kolumny NoOfReports up-to-date podczas wstawiania nowych rekordów pracowników. Wyzwalacz INSERT aktualizuje kolumnę NoOfReports rekordu menedżera, która cyklicznie aktualizuje kolumnę NoOfReports innych rekordów w hierarchii zarządzania.

USE AdventureWorks2022;  
GO  
-- Turn recursive triggers ON in the database.  
ALTER DATABASE AdventureWorks2022  
   SET RECURSIVE_TRIGGERS ON;  
GO  
CREATE TABLE dbo.emp_mgr (  
   emp char(30) PRIMARY KEY,  
    mgr char(30) NULL FOREIGN KEY REFERENCES emp_mgr(emp),  
    NoOfReports int DEFAULT 0  
);  
GO  
CREATE TRIGGER dbo.emp_mgrins ON dbo.emp_mgr  
FOR INSERT  
AS  
DECLARE @e char(30), @m char(30);  
DECLARE c1 CURSOR FOR  
   SELECT emp_mgr.emp  
   FROM   emp_mgr, inserted  
   WHERE emp_mgr.emp = inserted.mgr;  
  
OPEN c1;  
FETCH NEXT FROM c1 INTO @e;  
WHILE @@fetch_status = 0  
BEGIN  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Add 1 for newly  
   WHERE emp_mgr.emp = @e ;                           -- added employee.  
  
   FETCH NEXT FROM c1 INTO @e;  
END  
CLOSE c1;  
DEALLOCATE c1;  
GO  
-- This recursive UPDATE trigger works assuming:  
--   1. Only singleton updates on emp_mgr.  
--   2. No inserts in the middle of the org tree.  
CREATE TRIGGER dbo.emp_mgrupd ON dbo.emp_mgr FOR UPDATE  
AS  
IF UPDATE (mgr)  
BEGIN  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Increment mgr's  
   FROM inserted                            -- (no. of reports) by  
   WHERE emp_mgr.emp = inserted.mgr;         -- 1 for the new report.  
  
   UPDATE dbo.emp_mgr  
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports - 1 -- Decrement mgr's  
   FROM deleted                             -- (no. of reports) by 1  
   WHERE emp_mgr.emp = deleted.mgr;          -- for the new report.  
END  
GO  
-- Insert some test data rows.  
INSERT dbo.emp_mgr(emp, mgr) VALUES  
    ('Harry', NULL)  
    ,('Alice', 'Harry')  
    ,('Paul', 'Alice')  
    ,('Joe', 'Alice')  
    ,('Dave', 'Joe');  
GO  
SELECT emp,mgr,NoOfReports  
FROM dbo.emp_mgr;  
GO  
-- Change Dave's manager from Joe to Harry  
UPDATE dbo.emp_mgr SET mgr = 'Harry'  
WHERE emp = 'Dave';  
GO  
SELECT emp,mgr,NoOfReports FROM emp_mgr;  
  
GO  

Poniżej przedstawiono wyniki przed aktualizacją.

emp                            mgr                           NoOfReports  
------------------------------ ----------------------------- -----------  
Alice                          Harry                          2  
Dave                           Joe                            0  
Harry                          NULL                           1  
Joe                            Alice                          1  
Paul                           Alice                          0  

Poniżej przedstawiono wyniki po aktualizacji.

emp                            mgr                           NoOfReports  
------------------------------ ----------------------------- -----------  
Alice                          Harry                          2  
Dave                           Harry                          0  
Harry                          NULL                           2  
Joe                            Alice                          0  
Paul                           Alice                          0  

Aby ustawić opcję zagnieżdżonych wyzwalaczy

Aby ustawić opcję bazy danych RECURSIVE_TRIGGERS

Zobacz też

UTWÓRZ TRIGGER (Transact-SQL)
Konfigurowanie zagnieżdżonych wyzwalaczy opcji konfiguracji serwera