11

I have 2 tables with the cascade delete rule - [dbo].[Invoices] and [dbo].[InvoiceRows]:

CREATE TABLE [dbo].[Invoices]
(
    [InvoiceId] [int] IDENTITY(1,1) NOT NULL,
    --other columns
CONSTRAINT [PK_Invoices] 
    PRIMARY KEY CLUSTERED ([InvoiceId] ASC)

) GO

CREATE TABLE [dbo].[InvoiceRows] ( [InvoiceRowId] [int] IDENTITY(1,1) NOT NULL, [ProductId] [int] NOT NULL, [Price] [money] NOT NULL, [Quantity] [int] NOT NULL, [InvoiceId] [int] NOT NULL --other columns

CONSTRAINT [PK_InvoiceRows] 
    PRIMARY KEY CLUSTERED ([InvoiceRowId] ASC)

)

ALTER TABLE [dbo].[InvoiceRows] WITH CHECK ADD CONSTRAINT [FK_InvoiceRows_Invoices] FOREIGN KEY([InvoiceId]) REFERENCES [dbo].[Invoices] ([InvoiceId]) ON UPDATE CASCADE ON DELETE CASCADE GO

ALTER TABLE [dbo].[InvoiceRows] CHECK CONSTRAINT [FK_InvoiceRows_Invoices] GO

enter image description here

I want any change in the [dbo].[InvoiceRows] to cause a recalculation of some register.

For this I added a trigger:

CREATE TRIGGER [dbo].[TrInvoiceRows_Delete_UpdateProductRegister]
ON [dbo].[InvoiceRows]
AFTER DELETE
AS 
BEGIN
    SET NOCOUNT ON;
PRINT 'TRIGGER Tr_InvoiceRows_Delete_UpdateProductRegister fired'

--trigger logic

END

All triggers fire correctly when I work directly with [dbo].[InvoiceRows]. The triggers work when I delete [dbo].[Invoices] using SSMS.

But recently I noticed that deleting [dbo].[Invoices] using Entity Framework generates the following code and the trigger IS NOT FIRED. That is, there are no any errors; it just ignores the trigger.

EXEC sp_executesql
    N'
    SET IMPLICIT_TRANSACTIONS OFF; 
    SET NOCOUNT ON; 
    DELETE FROM [Invoices]
    OUTPUT 1 
    WHERE [InvoiceId] = @p0; ',
    N'@p0 int',
    @p0=19936;

I noticed that the problem is OUTPUT 1 and compared the queries:

enter image description here

I realize that I have many options to fire a trigger on the application side (do not use EF or delete the cascade table rows first).

I want to know if it is possible to solve the problem on SQL Server ? That is, make the cascading table delete trigger always fire without any surprises.

Paul White
  • 94,921
  • 30
  • 437
  • 687
mrigrek74
  • 113
  • 3

2 Answers2

11

It seems like this behaviour was changed for SQL Server 2012.

In SQL Server 2008 R2, the trigger fires successfully:

2008 R2

From SQL Server 2012 onwards, executing the demo script with OUTPUT (not OUTPUT INTO) either fails to run the trigger (latest versions) or throws an internal exception (older versions).

A severe error occurred on the current command. The results, if any, should be discarded.

Version Patch Level Outcome
2008 R2 SP3 Trigger runs
2012 SP4 Trigger not run
2014 SP3 Internal exception
2014 SP3-CU4 Trigger not run
2016 SP3 Trigger not run
2017 CU30 Internal exception
2017 CU31 Trigger not run
2019 CU16 Internal exception
2019 CU32 Trigger not run
2022 All Trigger not run
Azure SQL Database Current Trigger not run

The product documentation is not clear on whether this specific behaviour should be allowed. Triggers on the immediate target table are disallowed, but the cascading case is not explicitly excluded.

It seems likely this is an undocumented restriction without a proper error message.

Microsoft Feedback Report

Paul White
  • 94,921
  • 30
  • 437
  • 687
7

It seems there is a bug in SQL Server 2022 * that makes it skip execution of AFTER DELETE triggers for cascade deletes when a DELETE statement specifies the OUTPUT clause without the INTO keyword.

You can follow the guidance in Breaking changes in EF Core 7.0 (EF7) to configure EF Core so that it will use OUTPUT INTO instead of OUTPUT when deleting from Invoices, causing the AFTER DELETE trigger on InvoiceRows to run.


* Per Paul White's answer, this bug is reproducible in SQL Server versions from 2012 onwards (including Azure SQL Database), while SQL Server 2008 R2 behaves as expected (runs the trigger).

Paul White
  • 94,921
  • 30
  • 437
  • 687