我正在使用以下触发器检查inserted / deleted表的输出,如何在验证列后将截获的UPDATE命令传递给服务器?
CREATE TRIGGER Test1_LastUpdate ON Test1
INSTEAD OF UPDATE
AS
SELECT * FROM Inserted
SELECT * FROM Deleted
GO
编辑:我正在寻找一种在架构更新后不需要更改的解决方案。
CREATE TRIGGER Test1_LastUpdate2 ON Test1
INSTEAD OF UPDATE
AS
--COMMIT UPDATE THAT WAS INTERCEPTED
END
答案 0 :(得分:2)
在验证列后“将截获的UPDATE命令传递给服务器”的唯一方法是自己执行UPDATE
。
但是,您现在已经说过,当这些列添加到表中时,您不希望必须向触发器添加更多列。因此,您可以选择简单地回滚任何无效的更改。这可能看起来像这样:
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;
但是,如果您需要更多地控制更新实际需要的内容,例如需要在提交之前修改值,则必须自己执行更新。例如:
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
表上执行更新时,您会看到该值获得额外的{{1} } character - 您的更新被截获,并且在提交之前修改了+
值(表示更新后的建议更改)。
See a SQL Fiddle Demo这在行动中。
唯一需要注意的是递归:
直接递归
当您的更新可能导致其他触发器运行时修改相同的基表 - 然后您可以在它们之间进行乒乓,直到达到最大嵌套级别并回滚整个事务。因此请注意触发器之间可能的乒乓。
间接递归
您可能不必担心这个问题,因为SQL Server中默认情况下database-level RECURSIVE TRIGGERS
option处于关闭状态。但是,如果它处于打开状态,您可以根据新更新获得相同的触发器触发。
这些可以通过各种方式得到改善:
检查触发器内的Inserted
,如果嵌套已经足够深,则退出触发器。
要避免直接递归,请合并触发器。
在某些特定情况下,战略性地指定哪个触发器将首先/最后运行可能会解决问题。您不能指定绝对订单,但可以选择第一个和最后一个。
请注意,乒乓问题适用于任何类型的触发器TRIGGER_NESTLEVEL
或INSTEAD OF
,它会修改自己的基表,或者通过另一个表(包含修改另一个表的触发器...)最终返回修改基表。
我将此选项称为2B,因为它实际上是选项2,但具有增强功能。如果您不希望每次向表中添加列时手动更新触发器(我完全同意这种情绪),您可以自动执行此操作。创建一个存储过程,该过程可以创建适当的触发器,以观察您需要的所有验证。您可以将此验证的基本代码放入表中,然后在SP中将其选择为变量,通过在AFTER
视图中挖掘信息,在SQL脚本中添加更新列以进行最终更新,然后最终重写触发。这可以进一步附加到DDL触发器,以便100%自动化:您将在基表中添加或删除列,DDL触发器将触发,并为您重写DML触发器。
听起来好像很多工作,但是如果你把它设计成数据驱动的话,它可以推广到整个数据库中的任何表,这可能很有价值,这取决于你的使用场景。
答案 1 :(得分:0)
为了检索UPDATE语句,请使用SQLprofiler。插入的表是正在插入或更新的内容的快照(这里存储了新值)。已删除的表是您正在更新/删除的值的快照。您只会执行触发器而不是针对表Test1执行更新命令,您可以看到在插入时更新了什么。
检查此article有关触发器的信息。
答案 2 :(得分:0)
这是一个选择......
/*
Create Table ExampleTable (LastRefreshed DateTime)
Go
Insert ExampleTable
Select GetDate()
*/
Begin Tran
If Object_ID('tempdb..#check') Is Not Null Drop Table #check
Create Table #check (InsertedVal DateTime, DeletedVal DateTime)
Update ExampleTable
Set LastRefreshed = GetDate()
Output Inserted.LastRefreshed As InsertedVal, Deleted.LastRefreshed As DeletedVal Into #check
Select *
From #check
If Exists (Select 1
From #check
Where InsertedVal > DeletedVal)
Begin
Rollback Tran
End
Else
Begin
Commit Tran
End
这将创建一个包含DateTime记录的表。更新尝试将其更新为“now”,但它在事务中运行,并将其插入和删除的记录转储到临时表中以进行处理。更新后,您可以对表数据执行任何检查,以确定是要提交还是回滚更改。为了示例的目的,我写这篇文章总是回滚。