Dela via


Skapa kapslade utlösare

gäller för:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Både DML- och DDL-utlösare kapslas när en utlösare utför en åtgärd som initierar en annan utlösare. Dessa åtgärder kan initiera andra utlösare och så vidare. DML- och DDL-utlösare kan kapslas upp till 32 nivåer. Du kan styra om AFTER-utlösare kan kapslas via kapslade utlösare serverkonfigurationsalternativet. I stället för utlösare (endast DML-utlösare kan vara I stället för utlösare) kan kapslas oavsett den här inställningen.

Not

Alla referenser till hanterad kod från en Transact-SQL utlösare räknas som en nivå mot kapslingsgränsen på 32 nivåer. Metoder som anropas inifrån hanterad kod räknas inte mot den här gränsen.

Om kapslade utlösare tillåts och en utlösare i kedjan startar en oändlig loop överskrids kapslingsnivån och utlösaren avslutas.

Du kan använda kapslade utlösare för att utföra användbara hushållningsfunktioner som att lagra en säkerhetskopia av rader som påverkas av en tidigare utlösare. Du kan till exempel skapa en utlösare på PurchaseOrderDetail som sparar en säkerhetskopia av de PurchaseOrderDetail rader som delcascadetrig utlösaren har tagit bort. När delcascadetrig utlöses tas motsvarande rad eller rader bort från PurchaseOrderDetailom du tar bort PurchaseOrderID 1965 från PurchaseOrderHeader . Om du vill spara data kan du skapa en DELETE-utlösare på PurchaseOrderDetail som sparar borttagna data i en annan separat skapad tabell, del_save. Till exempel:

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

Vi rekommenderar inte att du använder kapslade utlösare i en ordningsberoende sekvens. Använd separata utlösare för ändringar i kaskaddata.

Notera

Eftersom utlösare körs inom en transaktion, avbryts hela transaktionen om ett fel uppstår på någon nivå av en uppsättning inbäddade utlösare, och alla dataändringar rullas tillbaka. Inkludera PRINT-instruktioner i dina utlösare så att du kan avgöra var felet har inträffat.

Rekursiva utlösare

En AFTER-utlösare anropar sig inte rekursivt om inte databas-alternativet RECURSIVE_TRIGGERS har angetts.

Det finns två typer av rekursion:

  • Direkt rekursion

    Den här rekursionen inträffar när en utlösare utlöses och utför en åtgärd som gör att samma utlösare utlöses igen. Till exempel en programuppdateringstabell T3; Detta gör att utlösaren Trig3 utlöses. Trig3 uppdaterar tabellen T3 igen. Detta gör att utlösaren Trig3 utlöses igen.

    Direkt rekursion kan också inträffa när samma utlösare anropas igen, men efter att en utlösare av en annan typ (EFTER eller I STÄLLET FÖR) anropats. Med andra ord kan direkt rekursion av en INSTEAD OF-trigger inträffa när samma INSTEAD OF-trigger anropas för andra gången, även om en eller flera AFTER-utlösare anropas däremellan. På samma sätt kan direkt rekursion av en AFTERutlösare inträffa när samma AFTERutlösare anropas en gång till, även om en eller flera ISTÄLLET FÖR-utlösare anropas däremellan. Till exempel uppdaterar en applikation tabell T4. Den här uppdateringen gör att INSTEAD OF-utlösaren Trig4 aktiveras. Trig4 uppdaterar tabellen T5. Den här uppdateringen gör att AFTER-utlösaren Trig5- utlöses. Trig5 uppdaterar tabellen T4, och denna uppdatering gör att INSTEAD OF-triggern Trig4 utlöses igen. Den här händelsekedjan anses vara direkt rekursion för Trig4.

  • Indirekt rekursion

    Den här rekursionen inträffar när en utlösare utlöses och utför en åtgärd som gör att en annan utlösare av samma typ (EFTER eller I STÄLLET för) utlöses. Den här andra utlösaren utför en åtgärd som gör att den ursprungliga utlösaren utlöses igen. Med andra ord kan indirekt rekursion inträffa när en I STÄLLET FÖR-utlösare anropas en andra gång, men inte förrän en annan I STÄLLET FÖR-utlösare anropas däremellan. På samma sätt kan indirekt rekursion inträffa när en AFTER-utlösare anropas en andra gång, men inte förrän en annan AFTER-utlösare anropas däremellan. Till exempel uppdaterar en applikation tabell T1. Den här uppdateringen gör att AFTER-utlösaren Trig1 utlöses. Trig1 uppdaterar tabellen T2och den här uppdateringen gör att AFTER-utlösaren Trig2 utlöses. Trig2 i sin tur uppdaterar tabellen T1 som gör att AFTER-utlösaren Trig1 utlöses igen.

Endast direkt rekursion av AFTER-utlösare förhindras när RECURSIVE_TRIGGERS-databasalternativet är inställt på OFF. Om du vill inaktivera indirekt rekursion av AFTER-utlösare anger du även alternativet kapslade utlösare server till 0.

Exempel

I följande exempel visas hur du använder rekursiva utlösare för att lösa en självrefererande relation (kallas även transitiv stängning). Tabellen emp_mgr definierar till exempel följande:

  • En anställd (emp) i ett företag.

  • Chefen för varje anställd (mgr).

  • Det totala antalet anställda i organisationsträdet som rapporterar till varje anställd (NoOfReports).

En rekursiv UPDATE-utlösare kan användas för att hålla kolumnen NoOfReports up-to-date uppdaterad när nya poster för medarbetare läggs till. INSERT-utlösaren uppdaterar kolumnen NoOfReports i chefsrekordet, vilket rekursivt uppdaterar kolumnen NoOfReports i andra rekord högre upp i ledningshierarkin.

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  

Här är resultatet före uppdateringen.

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

Här är resultatet efter uppdateringen.

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

Att ange alternativet för kapslade utlösare

Om du vill ange RECURSIVE_TRIGGERS databasalternativet

Se även

CREATE TRIGGER (Transact-SQL)
Konfigurera serverns konfigurationsalternativ för kapslade utlösare