【问题标题】:Do I need to set XACT_ABORT to ON in every trigger?我需要在每个触发器中将 XACT_ABORT 设置为 ON 吗?
【发布时间】:2021-05-18 21:50:29
【问题描述】:

这是一个旧的 SQL Server 2008 Express 数据库(实际上有五个),我刚刚迁移到 SQL Server 2019 Express。在我的工作人员进来之前,一切似乎都运行良好,我们到处都遇到了错误。结果我们在触发器中有RAISEERROR,即使我的兼容性似乎设置为2008(100),我们仍然收到错误。所以我升级到THROW。现在一切似乎都运行良好,但由于我不是 DBA,我担心我的升级会损坏一些数据或留下孤儿。以下是其中一个触发器的示例:

USE [toddAPB]
GO
/****** Object:  Trigger [dbo].[T_tSaleLineItem_ITrig]    Script Date: 5/18/2021 1:32:55 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[T_tSaleLineItem_ITrig] ON [dbo].[tSaleLineItem] FOR INSERT AS
SET NOCOUNT ON
/* * PREVENT INSERTS IF NO MATCHING KEY IN 'tProduct' */
IF (SELECT COUNT(*) FROM inserted) !=
   (SELECT COUNT(*) FROM tProduct, inserted WHERE (tProduct.RecNumP = inserted.RecNumP))
    BEGIN
        ;THROW 44447, 'The record can''t be added or changed. Referential integrity rules require a related record in table ''tProduct''.',1;
        ROLLBACK TRANSACTION
    END

/* * PREVENT INSERTS IF NO MATCHING KEY IN 'tSale' */
IF (SELECT COUNT(*) FROM inserted) !=
   (SELECT COUNT(*) FROM tSale, inserted WHERE (tSale.RecNumS = inserted.RecNumS))
    BEGIN
        ;THROW 44447, 'The record can''t be added or changed. Referential integrity rules require a related record in table ''tSale''.',1;
        ROLLBACK TRANSACTION
    END

我需要在每个触发器(数百个)上加上SET XACT_ABORT ON 吗?我是不是该?在每个THROW 之后我还需要ROLLBACK TRANSACTION 吗?

在我调整为 THROW 之前,我遇到了“44447 附近的语法错误”错误。该行以前看起来更像这样:

RAISERROR 44447 'The record can''t be added or changed. Referential integrity rules require a related record in table ''tProduct''.'

感谢您的帮助。

【问题讨论】:

  • SET XACT_ABORT ON 是触发器的默认值。这与从raiserror 更改为throw 有什么关系? throw 默认回滚。此外,如果您使用; 正确终止语句,则无需在throw 前面添加一个(但由于错误,SSMS 仍将其显示为红色)。你得到的实际错误是什么? raiserror 还支持吗?
  • @DauntlessRob 我会相信official docs 在一个 SO 问题上。
  • 您可以将 xact_abort 配置为在服务器级别打开,如果您为每个过程都设置它会更有意义?
  • 我建议实施适当的外键约束并完全转储此触发器。
  • @Stu 谢谢!我不知道。刚打开...等待尖叫声...

标签: sql sql-server error-handling triggers


【解决方案1】:

其实你应该用外键约束来解决这个问题(感谢 Charlieface),但是如果那不实用,请继续阅读......

SET XACT_ABORT ON 是触发器的默认值。

OFF 是 T-SQL 语句中的默认设置,而 ON 是触发器中的默认设置。

您还应该使用 proper 连接而不是隐式连接。我已经说明了如何编写触发器,并使用正确终止的语句。

ALTER TRIGGER [dbo].[T_tSaleLineItem_ITrig]
ON [dbo].[tSaleLineItem]
FOR INSERT AS
BEGIN
    SET NOCOUNT ON;

    /* PREVENT INSERTS IF NO MATCHING KEY IN 'tProduct' */
    IF (SELECT COUNT(*) FROM inserted) != (SELECT COUNT(*) FROM tProduct P INNER JOIN inserted I ON P.RecNumP = I.RecNumP)
    BEGIN
        THROW 44447, 'The record can''t be added or changed. Referential integrity rules require a related record in table ''tProduct''.', 1;
    END;

    /* PREVENT INSERTS IF NO MATCHING KEY IN 'tSale' */
    IF (SELECT COUNT(*) FROM inserted) != (SELECT COUNT(*) FROM tSale S INNER JOIN inserted I ON S.RecNumS = I.RecNumS)
    BEGIN
        THROW 44447, 'The record can''t be added or changed. Referential integrity rules require a related record in table ''tSale''.', 1;
    END;
END;

您当然可以继续使用RAISERROR,但它的语法似乎在您最初编写后发生了变化——因此您必须更正它。由于无论如何您都必须修改它们,因此转到THROW 似乎是合适的。

我发现您的完整性检查很有趣,由于我处理逻辑的方式,我会写如下,但我不认为它更好。

IF EXISTS (
    SELECT 1
    FROM Inserted I
    WHERE NOT EXISTS (SELECT 1 FROM tProduct P WHERE P.RecNumP = I.RecNumP)
)

【讨论】:

  • 非常感谢!这是一个很大的帮助。 (是我之前的几位前辈设置了这一切,我没有接受过 DBA 培训。)
  • 显然你会使用 FK 而不是触发器来进行完整性检查
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-16
  • 2023-03-09
  • 2017-04-21
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多