我需要在每个触发器中将 XACT_ABORT 设置为 ON 吗?
Do I need to set XACT_ABORT to ON in every trigger?
这是我刚迁移到 SQL Server 2019 Express 的旧 SQL Server 2008 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''.'
感谢您的帮助。
其实你应该用外键约束来解决这个问题(感谢 Charlieface),但是如果那不切实际请继续阅读...
SET XACT_ABORT ON
是触发器的默认值。
OFF is the default setting in a T-SQL statement, while ON is the default setting in a trigger.
您还应该使用适当的 连接而不是隐式连接。我已经列出了如何使用正确终止的语句编写触发器。
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
but it appears the syntax has changed since you originally wrote it - so you would have to correct that. Since you are having to modify them all anyway, moving to THROW
似乎合适。
我觉得你的完整性检查很有趣,由于我处理逻辑的方式,我会把它写成下面这样,但我认为它没有什么更好的。
IF EXISTS (
SELECT 1
FROM Inserted I
WHERE NOT EXISTS (SELECT 1 FROM tProduct P WHERE P.RecNumP = I.RecNumP)
)
这是我刚迁移到 SQL Server 2019 Express 的旧 SQL Server 2008 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''.'
感谢您的帮助。
其实你应该用外键约束来解决这个问题(感谢 Charlieface),但是如果那不切实际请继续阅读...
SET XACT_ABORT ON
是触发器的默认值。
OFF is the default setting in a T-SQL statement, while ON is the default setting in a trigger.
您还应该使用适当的 连接而不是隐式连接。我已经列出了如何使用正确终止的语句编写触发器。
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
but it appears the syntax has changed since you originally wrote it - so you would have to correct that. Since you are having to modify them all anyway, moving to THROW
似乎合适。
我觉得你的完整性检查很有趣,由于我处理逻辑的方式,我会把它写成下面这样,但我认为它没有什么更好的。
IF EXISTS (
SELECT 1
FROM Inserted I
WHERE NOT EXISTS (SELECT 1 FROM tProduct P WHERE P.RecNumP = I.RecNumP)
)