“在验证列后将截获的 UPDATE 命令传递给服务器”的唯一方法是自己执行 UPDATE。
选项 1 - 回滚
但是,您现在已经说过,当这些列添加到表中时,您不希望向触发器添加更多列。因此,您可以选择简单地回滚任何无效的更改。这可能看起来像这样:
CREATE TRIGGER TR_Sample_U ON dbo.Sample -- No AFTER trigger needed here!
AS
IF EXISTS ( --check for disallowed modifications
SELECT *
FROM
Inserted I
INNER JOIN Deleted D
ON I.SampleID = D.SampleID
WHERE
I.Something <> D.Something
AND I.UpdateDate = D.UpdateDate
)
ROLLBACK TRAN;
选项 2 - 在触发器中执行更新
但是,如果您需要更多地控制更新实际需要的内容,例如需要在提交之前修改值,则您必须自己执行更新。例如:
CREATE TRIGGER TR_Sample_U ON dbo.Sample
INSTEAD OF UPDATE
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;
UPDATE S
SET S.Value = I.Value + '+'
FROM
dbo.Sample S
INNER JOIN Inserted I
ON S.SampleID = I.SampleID
;
这是一个不做任何检查的小例子,但你明白了——当你对Sample 表执行更新时,你会看到该值获得了一个额外的+ 字符——您的更新被拦截,Inserted 值(表示更新后建议的更改)在提交之前已被修改。
See a SQL Fiddle Demo 这个在行动。
唯一需要注意的是递归:
-
直接递归
当您的更新可能导致其他触发器运行修改同一个基表时,您可以在它们之间进行乒乓操作,直到达到最大嵌套级别并回滚整个事务。因此,请注意触发器之间可能出现的乒乓现象。
-
间接递归
您可能不必担心这个,因为database-level RECURSIVE TRIGGERS option 在 SQL Server 中默认是关闭的。但是,如果它打开,您可以根据新更新获得相同的触发器触发。
这些可以通过各种方式得到改善:
请注意,乒乓问题适用于任何类型的触发器,INSTEAD OF 或 AFTER,它们修改自己的基表,或通过另一个表参与更新链(具有修改另一个表...)最终会回来修改基表。
选项 2B - 预处理 AFTER UPDATE 触发器。
我将此选项称为 2B,因为它确实是选项 2,但具有增强功能。如果您不想每次向表中添加列时都必须手动更新触发器(我完全同意这种观点),您可以自动执行此操作。创建一个存储过程,该过程可以创建一个适当的触发器来观察您需要的所有验证。您可以将此验证的基本代码放入表中,然后在 SP 中将其选择为变量,添加 SQL 脚本通过挖掘 INFORMATION_SCHEMA.COLUMNS 视图中的信息来更新列以进行最终更新,然后最终重写触发器.这还可以附加到 DDL 触发器,因此它是 100% 自动化的:您可以从基表中添加或删除列,DDL 触发器会触发,并为您重写 DML 触发器。
听起来工作量很大,但如果您将其设计为数据驱动的,则可以将其推广到与整个数据库中的任何表一起使用,这可能具有很大的价值,具体取决于您的使用场景。