Sdílet prostřednictvím


Vytváření vnořených triggerů

platí pro:SQL ServerAzure SQL Databaseazure 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ěčů

  • sp_configure (Transact-SQL)

Nastavení možnosti databáze RECURSIVE_TRIGGERS

Viz také

CREATE TRIGGER (Transact-SQL)
Konfigurace vnořené možnosti konfigurace serveru