Skapa kapslade utlösare
gäller för:SQL Server
Azure SQL Database
Azure 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 PurchaseOrderDetail
om 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