SQL Server - 插入表

时间:2013-04-09 14:53:16

标签: sql sql-server database triggers relational-database

我正在使用以下触发器检查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

3 个答案:

答案 0 :(得分:2)

在验证列后“将截获的UPDATE命令传递给服务器”的唯一方法是自己执行UPDATE

选项1 - ROLLBACK

但是,您现在已经说过,当这些列添加到表中时,您不希望必须向触发器添加更多列。因此,您可以选择简单地回滚任何无效的更改。这可能看起来像这样:

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表上执行更新时,您会看到该值获得额外的{{1} } character - 您的更新被截获,并且在提交之前修改了+值(表示更新后的建议更改)。

See a SQL Fiddle Demo这在行动中。

唯一需要注意的是递归:

  1. 直接递归

    当您的更新可能导致其他触发器运行时修改相同的基表 - 然后您可以在它们之间进行乒乓,直到达到最大嵌套级别并回滚整个事务。因此请注意触发器之间可能的乒乓。

  2. 间接递归

    您可能不必担心这个问题,因为SQL Server中默认情况下database-level RECURSIVE TRIGGERS option处于关闭状态。但是,如果它处于打开状态,您可以根据新更新获得相同的触发器触发。

  3. 这些可以通过各种方式得到改善:

    • 检查触发器内的Inserted,如果嵌套已经足够深,则退出触发器。

    • 要避免直接递归,请合并触发器。

    • 在某些特定情况下,战略性地指定哪个触发器将首先/最后运行可能会解决问题。您不能指定绝对订单,但可以选择第一个和最后一个。

    请注意,乒乓问题适用于任何类型的触发器TRIGGER_NESTLEVELINSTEAD OF,它会修改自己的基表,或者通过另一个表(包含修改另一个表的触发器...)最终返回修改基表。

    选项2B - 预处理AFTER UPDATE触发器。

    我将此选项称为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”,但它在事务中运行,并将其插入和删除的记录转储到临时表中以进行处理。更新后,您可以对表数据执行任何检查,以确定是要提交还是回滚更改。为了示例的目的,我写这篇文章总是回滚。