Tworzenie zagnieżdżonych wyzwalaczy
Dotyczy:SQL Server
Azure SQL Database
Azure 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