数据库:SQL Server,2017。
我需要在触发器中强制执行某些业务规则。现在,在您告诉我使用外键之前,请知道规则太复杂,无法通过外键执行。说完这些,让我开始回答我的问题:
如果违反业务规则,这是回滚/取消触发器中的插入/更新的正确方法吗:
CREATE TRIGGER [dbo].[MyTestTrigger]
ON [dbo].[MyTestTable]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL(OBJECT_ID('[dbo].[MyTestTrigger]')) > 1
BEGIN
RETURN;
END
if (condition_is_true) begin
;throw 51000, 'Bad data Message.',1;
end
end
我知道我的问题看起来很简单,但我偶然发现了这篇关于SQL Server 错误处理的“论文”。浏览完之后,我的印象是错误处理非常复杂。我感到困惑的一件事是,我是否需要rollback
在语句之前/之后显式调用该throw
语句?或者throw
本身会回滚更新/插入。根据我的测试,它throw
本身会回滚更新/插入。然而,在微软自己关于触发器的文档中,rollback
包含这样的语句:
USE AdventureWorks2022;
GO
IF OBJECT_ID ('Purchasing.LowCredit','TR') IS NOT NULL
DROP TRIGGER Purchasing.LowCredit;
GO
-- This trigger prevents a row from being inserted in the Purchasing.PurchaseOrderHeader table
-- when the credit rating of the specified vendor is set to 5 (below average).
CREATE TRIGGER Purchasing.LowCredit ON Purchasing.PurchaseOrderHeader
AFTER INSERT
AS
IF (ROWCOUNT_BIG() = 0)
RETURN;
IF EXISTS (SELECT 1
FROM inserted AS i
JOIN Purchasing.Vendor AS v
ON v.BusinessEntityID = i.VendorID
WHERE v.CreditRating = 5
)
BEGIN
RAISERROR ('A vendor''s credit rating is too low to accept new
purchase orders.', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;
纳入rollback
真的有必要吗?
不,绝对不是。
首先,
THROW
无论如何总是中止批处理,因此下一条语句永远不会执行,除非您有BEGIN CATCH
.并且
XACT_ABORT
始终ON
处于触发器状态,因此无论如何都会发生回滚。即使您使用
RAISERROR
which 也不一定会中止,如果您ROLLBACK
处于触发器中,则会收到虚假错误:有些客户端无法正确处理此问题,因此您最终只会看到该错误,而不是实际的错误。所以就
THROW
单独使用吧。虽然 Erland 关于错误处理的“专着”非常出色,但它却被广泛误解。它是关于错误处理的,即处理错误,而不是抛出错误。
在大多数情况下,您不想在 SQL 中处理错误。您希望客户端处理它,为此您不需要任何复杂的代码,只需
THROW
和XACT_ABORT ON
。Even
BEGIN CATCH
很差:它不能捕获所有错误,并且不能像前面提到的那样一起处理多个错误。如果您需要日志记录,请使用 XEvents,而不是一些自定义的记录器。;
是语句终止符,而不是起始符。终止你的所有陈述,或者被终止。