如何在SQL Server中创建一个BEFORE UPDATED触发器?

56
我正在使用Sqlserver express,但无法进行“before updated”触发器。还有其他方法可以实现这个功能吗?
9个回答

51

确实,在MSSQL中没有“before triggers”。但是,您仍然可以通过同时使用“inserted”和“deleted”表来跟踪对表进行的更改。当更新引发触发器时,“inserted”表存储新值,“deleted”表存储旧值。有了这些信息,您就可以相对容易地模拟“before trigger”的行为。


3
这是对我起作用的答案;已删除的表格保存原始值,插入的表格保存新/更新后的值。在进行"更新后"操作时效果很好。 - gimlichael
1
被删除的表在DELETE和UPDATE语句期间存储受影响行的副本。 插入的表在INSERT和UPDATE语句期间存储受影响行的副本。 更新事务类似于先执行删除操作,然后执行插入操作;旧行首先被复制到已删除的表中,然后新行被复制到触发器表和插入的表中。 - csmith

50
MSSQL不支持"BEFORE"触发器。最接近的是"INSTEAD OF"触发器,但它们的行为与MySQL中的"BEFORE"触发器不同。
SQL Server提供了DML触发器。
DML触发器是一种特殊类型的存储过程,当发生影响到触发器所定义的表或视图的数据操作语言(DML)事件时,自动生效(插入、更新、删除)。
DML触发器语句使用两个特殊表:deleted和inserted表。
注意
触发器是覆盖触发语句标准操作的功能。因此,它们可用于对一个或多个列执行错误或值检查,并在插入、更新或删除行之前执行其他操作。
从现已下线的文档中获取
指定触发器在触发的SQL语句之前执行,从而覆盖触发语句的操作。
您可以在此处了解更多DML触发器类型
因此,如果触发器未正确编写/处理,可能不会执行更新操作。级联操作也会受到影响。
您可能希望使用不同的方法来实现您的目标。

你的链接现在打开了其他内容。 - NeWi-SL

16

不能确定这是否适用于 SQL Server Express,但是即使在触发器在更新之后执行时,您仍然可以访问“之前”的数据。必须从在表更改时临时创建的deletedinserted表中读取数据。这基本上是@Stamen所说的,但我仍需要进一步探索才能理解那个有帮助的答案。

deleted 表存储 DELETE 和 UPDATE 语句期间受影响行的副本。在执行 DELETE 或 UPDATE 语句期间,将从触发器表中删除行并转移到 deleted 表中...

inserted 表在 INSERT 和 UPDATE 语句期间存储受影响行的副本。在插入或更新事务期间,新行会添加到 inserted 表和触发器表中...

https://msdn.microsoft.com/zh-cn/library/ms191300.aspx

因此,您可以创建触发器从其中一个表中读取数据,例如:

CREATE TRIGGER <TriggerName> ON <TableName>
AFTER UPDATE
AS
  BEGIN
    INSERT INTO <HistoryTable> ( <columns...>, DateChanged )
    SELECT <columns...>, getdate()
    FROM deleted;
  END;

我的示例基于这里的示例:

http://www.seemoredata.com/en/showthread.php?134-Example-of-BEFORE-UPDATE-trigger-in-Sql-Server-good-for-Type-2-dimension-table-updates


5
要是它不晚了七年就好了。;-) - Charlie Joynt
1
这应该是正确的答案。 - Edgar Froes

9

T-SQL只支持AFTER和INSTEAD OF触发器,不像其他一些关系型数据库管理系统那样有BEFORE触发器。

我认为您需要使用一个INSTEAD OF触发器。


4

在SQL Server中,所有“普通”触发器都是“AFTER…”触发器。没有“BEFORE…”触发器。

如果想在更新之前执行某些操作,请查看INSTEAD OF UPDATE Triggers


2

在SQL Server中执行BEFORE UPDATE,我使用一个技巧。我对记录进行虚假更新(UPDATE Table SET Field = Field),以此方式获得记录的先前图像。


2

更新或删除的值存储在 DELETED 中。我们可以通过触发器中的以下方法获取它

完整示例:

CREATE TRIGGER PRODUCT_UPDATE ON PRODUCTS
FOR UPDATE 
AS
BEGIN
DECLARE @PRODUCT_NAME_OLD VARCHAR(100)
DECLARE @PRODUCT_NAME_NEW VARCHAR(100)

SELECT @PRODUCT_NAME_OLD = product_name from DELETED
SELECT @PRODUCT_NAME_NEW = product_name from INSERTED

END

1

完整示例:

CREATE TRIGGER [dbo].[trig_020_Original_010_010_Gamechanger]
   ON  [dbo].[T_Original]
   AFTER UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    DECLARE @Old_Gamechanger int;
    DECLARE @New_Gamechanger int;

    -- Insert statements for trigger here
    SELECT @Old_Gamechanger = Gamechanger from DELETED;
    SELECT @New_Gamechanger = Gamechanger from INSERTED;

    IF @Old_Gamechanger != @New_Gamechanger

        BEGIN

            INSERT INTO [dbo].T_History(ChangeDate, Reason, Callcenter_ID, Old_Gamechanger, New_Gamechanger)
            SELECT GETDATE(), 'Time for a change', Callcenter_ID, @Old_Gamechanger, @New_Gamechanger
                FROM deleted
            ;

        END

END

当OP要求“Before”更新时,我不确定这个响应与之相关。 - KMC
从我的角度来看,你可能想要一个“Before”触发器有两个原因:a)停止操作
b)在数据被更改之前对其进行处理,以免丢失。 对于a),最好使用不同的方法,如存储过程中的一致性测试。 对于b),您可以使用提供的代码-至少这就是我所做的,也是我为什么会遇到这个问题的原因。
- Nick Oetjen
比较Luciano 2012年5月3日的答案。 - Nick Oetjen

1

请记住,当您使用一个“代替”触发器时,它不会在触发器中自动提交插入操作,除非您明确告诉它这样做。 “代替”意味着执行此操作而不是通常的操作,因此不会发生任何正常的插入操作。


1
这应该作为对现有答案的评论而不是一个答案本身。 - TroySteven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接