Sql 是否需要在每个触发器中将XACT_ABORT设置为ON?

Sql 是否需要在每个触发器中将XACT_ABORT设置为ON?,sql,sql-server,error-handling,triggers,Sql,Sql Server,Error Handling,Triggers,这是一个旧的SQL Server 2008 Express数据库(实际上有五个),我刚刚迁移到SQL Server 2019 Express。一切似乎都很顺利,直到我的船员进来,我们到处都有错误。事实证明,触发器中有RAISEERROR,即使我的兼容性似乎设置为2008(100),我们仍然会收到错误。所以我升级到了THROW。现在一切看起来都很好,但由于我不是DBA,我担心我的升级会损坏一些数据或留下孤儿。以下是其中一个触发器的示例: USE [toddAPB] GO /****** Objec

这是一个旧的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
?我应该吗?每次抛出
后是否仍需要
回滚事务

在我调整到抛出之前,我得到了“语法错误接近44447”的错误。这条线以前看起来更像这样:

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

谢谢你的帮助。

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

是触发器的默认值

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

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

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;
当然,您可以继续使用,但自您最初编写它以来,语法似乎已经发生了变化,因此您必须纠正这一点。由于您无论如何都必须修改它们,因此移动到似乎是合适的

我觉得你的完整性检查很有趣,我会写如下由于我如何处理逻辑,但我不认为它更好

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

实际上,您应该使用外键约束来解决这个问题(谢谢Charlieface),但是如果这不实用,请继续阅读

是触发器的默认值

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

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

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;
当然,您可以继续使用,但自您最初编写它以来,语法似乎已经发生了变化,因此您必须纠正这一点。由于您无论如何都必须修改它们,因此移动到似乎是合适的

我觉得你的完整性检查很有趣,我会写如下由于我如何处理逻辑,但我不认为它更好

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

将XACT_ABORT设置为ON
是触发器的默认设置。这与从
raiserror
更改为
throw
有什么关系?默认情况下,
throw
也会回滚。此外,如果您使用
正确终止您的语句
您不需要在您的
抛出之前添加一个(但由于存在错误,SSMS仍会将其显示为红色)。你得到的实际错误是什么
raiserror
仍然受支持?@DauntlessRob我相信这个问题已经解决了。您可以将xact_abort配置为在服务器级别打开,如果您为每个过程都设置它,这将更有意义。我建议实施适当的外键约束并完全转储此触发器。@Stu谢谢!我不知道。刚打开它。。。等待尖叫声…
将XACT_ABORT设置为ON
是触发器的默认设置。这与从
raiserror
更改为
throw
有什么关系?默认情况下,
throw
也会回滚。此外,如果您使用
正确终止您的语句
您不需要在您的
抛出之前添加一个(但由于存在错误,SSMS仍会将其显示为红色)。你得到的实际错误是什么
raiserror
仍然受支持?@DauntlessRob我相信这个问题已经解决了。您可以将xact_abort配置为在服务器级别打开,如果您为每个过程都设置它,这将更有意义。我建议实施适当的外键约束并完全转储此触发器。@Stu谢谢!我不知道。刚打开它。。。等待尖叫声…非常感谢!这是一个很大的帮助。(我之前的几位前辈都设置了这一切,我没有DBA方面的培训。)显然,无论如何,您都会使用FK而不是触发器来进行完整性检查。非常感谢!这是一个很大的帮助。(在我之前有几位前辈设置了所有这些,我没有DBA方面的培训。)显然,无论如何,您都会使用FK而不是触发器来进行完整性检查