Trigger execution order after delete
-
Let's say I have two tables, one called
ParentTable
, and another calledChildTable
, with a one-to-many relationship, where one Parent may have many Children.I'm creating an
AFTER DELETE
trigger onChildTable
. In that trigger, I'm checking a value fromParentTable
(if it exists), something like this:-- ... FROM deleted d JOIN ParentTable pt ON d.FK_Parent = pt.ID WHERE pt.Foo = 'bar'
If I delete from
ChildTable
first, then fromParentTable
, does theAFTER DELETE
trigger fire before or after the row inParentTable
is deleted?a. These two
DELETE
statements would most often be in an explicit transaction, as I'm using Entity Framework.If I set the foreign key in
ChildTable
toCASCADE DELETE
, then deleted fromParentTable
only, will the row(s) inParentTable
still be accessible when theAFTER DELETE
trigger fires?
-
https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql?view=sql-server-ver16#remarks-for-dml-triggers
An AFTER trigger is run only after the triggering SQL statement has run successfully. This successful execution includes all referential cascade actions and constraint checks associated with the object updated or deleted.
Deleting from
ChildTable
first, then fromParentTable
, will always execute the child trigger before the parent one, because triggers fire per statement, not per transaction or batch.Cascading foreign-keys you can test quite easily.
CREATE TABLE Parent (id int identity PRIMARY KEY); CREATE TABLE Child (id int identity PRIMARY KEY, pId int REFERENCES Parent(id) ON DELETE CASCADE);
CREATE TRIGGER trParent ON Parent AFTER DELETE AS
PRINT 'trParent';
DECLARE @i int;
SELECT @i = COUNT() FROM Parent;
PRINT @i;
SELECT @i = COUNT() FROM Child;
PRINT @i;
CREATE TRIGGER trChild ON Child AFTER DELETE AS PRINT 'trChild'; DECLARE @i int; SELECT @i = COUNT(*) FROM Parent; PRINT @i; SELECT @i = COUNT(*) FROM Child; PRINT @i;
INSERT Parent DEFAULT VALUES; INSERT Child(pId) VALUES (SCOPE_IDENTITY()); DELETE Parent;
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=55f40682e31038e65fa374226d36d211
This prints
trChild 0 0 trParent 0 0
So the
Child
trigger is fired first, then theParent
trigger. But there are no rows in either table before even the first trigger was called. To access the original data you instead use thedeleted
table in the trigger.Note that if there are no rows to cascade, it appears the child trigger https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=6af4a818d77b0548175e638c543164e0 .