您“在验证列后将拦截的 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 - 在触发器中执行 UPDATE
但是,如果您需要对更新实际需要的内容进行更多控制,例如需要在提交之前修改值,则必须自己执行更新。例如:
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
值(表示更新后建议的更改)在提交之前进行了修改。
请参阅此操作的 SQL Fiddle 演示。
唯一需要注意的是递归:
直接递归
当您的更新可能导致其他触发器运行修改同一个基表时,您可以在它们之间进行乒乓操作,直到达到最大嵌套级别并回滚整个事务。所以要注意触发器之间可能的乒乓球。
间接递归
您可能不必担心这一点,因为在 SQL Server 中,数据库级RECURSIVE TRIGGERS
选项默认处于关闭状态。但是,如果它打开,您可以根据新更新获得相同的触发器触发。
这些可以通过多种方式得到改善:
请注意,乒乓问题适用于任何类型的触发器,INSTEAD OF
或者AFTER
,它修改自己的基表,或者通过最终出现的另一个表(具有修改另一个表的触发器......)参与更新链返回修改基表。
选项 2B - 预处理 AFTER UPDATE 触发器。
我将此选项称为 2B,因为它确实是选项 2,但具有增强功能。如果您不想每次向表中添加列时都必须手动更新触发器(我完全同意这种观点),您可以自动执行此操作。创建一个存储过程,该过程可以创建一个适当的触发器来观察您需要的所有验证。您可以将此验证的基本代码放入表中,然后在 SP 中将其选择为变量,添加 SQL 脚本通过挖掘INFORMATION_SCHEMA.COLUMNS
视图中的信息来更新列以进行最终更新,然后最终重写触发器。这还可以附加到 DDL 触发器,从而实现 100% 自动化:您将在基表中添加或删除列,DDL 触发器将触发,并为您重写 DML 触发器。
这听起来像是很多工作,但如果您将其设计为数据驱动的,则可以将其推广到与整个数据库中的任何表一起使用,这可能具有很大的价值,具体取决于您的使用场景。