Vytváření vnořených triggerů
platí pro:SQL Server
Azure SQL Database
azure SQL Managed Instance
Triggery DML i DDL se vnořují, když trigger provede akci, která inicializuje další trigger. Tyto akce můžou inicializovat další triggery atd. Triggery DML a DDL je možné vnořit až do 32 úrovní. Můžete určit, jestli se triggery AFTER dají vnořit prostřednictvím možnosti vnořené triggery konfiguraci serveru. Triggery INSTEAD OF (vnořené mohou být pouze DML triggery) lze vložit bez ohledu na toto nastavení.
Poznámka
Jakýkoli odkaz na spravovaný kód z triggeru Transact-SQL se počítá jako jedna úroveň oproti limitu vnoření na úrovni 32. Metody vyvolané z spravovaného kódu se do tohoto limitu nezapočítávají.
Pokud je povoleno používat vnořené spouštěče a jeden ze spouštěčů v řetězu zahájí nekonečnou smyčku, je překročena úroveň vnoření a spouštěč se ukončí.
Vnořené triggery můžete použít k provádění užitečných funkcí úklidu, jako je uložení záložní kopie řádků ovlivněných předchozím triggerem. Můžete například vytvořit aktivační událost na PurchaseOrderDetail
, která uloží záložní kopii PurchaseOrderDetail
řádků, které delcascadetrig
trigger odstranil. Když je aktivační událost delcascadetrig
platná, odstranění PurchaseOrderID
1965 z PurchaseOrderHeader
odstraní odpovídající řádek nebo řádky z PurchaseOrderDetail
. Pokud chcete data uložit, můžete na PurchaseOrderDetail
vytvořit trigger DELETE, který uloží odstraněná data do jiné samostatně vytvořené tabulky del_save
. Například:
CREATE TRIGGER Purchasing.savedel
ON Purchasing.PurchaseOrderDetail
FOR DELETE
AS
INSERT del_save
SELECT * FROM deleted;
Nedoporučujeme používat vnořené triggery v posloupnosti závislé na pořadí. K kaskádové úpravě dat použijte samostatné triggery.
Poznámka
Vzhledem k tomu, že triggery se provádějí v rámci transakce, selhání na jakékoli úrovni sady vnořených triggerů zruší celou transakci a všechny úpravy dat se vrátí zpět. Do triggerů zahrňte příkazy PRINT, abyste mohli určit, kde k chybě došlo.
Rekurzivní triggery
Trigger AFTER se nevolá rekurzivně, pokud není nastavena možnost databáze RECURSIVE_TRIGGERS.
Existují dva typy rekurze:
Přímá rekurze
K této rekurzi dochází, když se trigger aktivuje a provede akci, která způsobí opětovné aktivaci stejného triggeru. Například aplikace aktualizuje tabulku T3; to způsobí aktivaci triggeru trig3. trig3 znovu aktualizuje tabulku T3; to způsobí opětovné spuštění triggeru Trig3.
K přímé rekurzi může dojít také tehdy, když je znovu volán stejný spouštěč, ale až po zavolání spouštěče jiného typu (PO nebo MÍSTO TOHO). Jinými slovy, přímá rekurze triggeru MÍSTO může nastat, když je stejný trigger MÍSTO volán podruhé, a to i v případě, že je mezi volán jeden nebo více AFTER triggerů. Stejně tak může dojít k přímé rekurzi spouště AFTER, když je stejná spoušť AFTER volána podruhé, i když se mezi tím volá jedna nebo více spouští MÍSTO. Aplikace například aktualizuje tabulku T4. Tato aktualizace způsobí, že se aktivuje místo triggeru Trig4. Trig4 aktualizuje tabulku T5. Tato aktualizace způsobí, že se spustí AFTER trigger Trig5. Trig5 aktualizuje tabulku T4 a tato aktualizace způsobí opětovné spuštění triggeru Trig4. Tento řetězec událostí je považován za přímé rekurze pro Trig4.
Nepřímá rekurze
K této rekurzi dochází, když se trigger aktivuje a provede akci, která způsobí aktivaci jiného triggeru stejného typu (ZA NEBO MÍSTO). Tato druhá spoušť provede akci, která způsobí znovu aktivaci původní spouště. Jinými slovy, nepřímá rekurze může nastat, když se aktivační událost INSTEAD OF vyvolá podruhé, ale pouze poté, co se mezi tím vyvolá jiná aktivační událost INSTEAD OF. Stejně tak může dojít k nepřímé rekurzi, když je aktivační událost AFTER volána podruhé, ale ne, dokud se mezi sebou nevolá jiná aktivační událost AFTER. Aplikace například aktualizuje tabulku T1. Tato aktualizace způsobí spuštění AFTER triggeru Trig1. Trig1 aktualizuje tabulku T2a tato aktualizace způsobí aktivaci triggeru AFTER Trig2. Trig2 zase aktualizuje tabulku T1, což způsobí opětovné spuštění AFTER Triggeru Trig1.
Pokud je možnost databáze RECURSIVE_TRIGGERS nastavena na VYPNUTO, je zabráněno pouze přímé rekurzi u triggerů typu AFTER. Pokud chcete zakázat nepřímou rekurzi triggerů AFTER, nastavte také možnost vnořených triggerů serveru na 0.
Příklady
Následující příklad ukazuje použití rekurzivních triggerů k vyřešení vztahu odkazujícího na sebe (označovaného také jako přechodné uzavření). Například tabulka emp_mgr
definuje následující:
Zaměstnanec (
emp
) ve společnosti.Vedoucí každého zaměstnance (
mgr
).Celkový počet zaměstnanců ve stromu organizace, kteří se hlásí jednotlivým zaměstnancům (
NoOfReports
).
Rekurzivní spouštěč UPDATE se dá použít k aktualizaci sloupce NoOfReports
up-to-date při vkládání nových záznamů zaměstnanců. Trigger INSERT aktualizuje NoOfReports
sloupec záznamu správce, který rekurzivně aktualizuje NoOfReports
sloupec jiných záznamů v hierarchii správy.
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
Tady jsou výsledky před aktualizací.
emp mgr NoOfReports
------------------------------ ----------------------------- -----------
Alice Harry 2
Dave Joe 0
Harry NULL 1
Joe Alice 1
Paul Alice 0
Tady jsou výsledky po aktualizaci.
emp mgr NoOfReports
------------------------------ ----------------------------- -----------
Alice Harry 2
Dave Harry 0
Harry NULL 2
Joe Alice 0
Paul Alice 0
Nastavení možnosti vnořených spouštěčů
Nastavení možnosti databáze RECURSIVE_TRIGGERS
Viz také
CREATE TRIGGER (Transact-SQL)
Konfigurace vnořené možnosti konfigurace serveru