仅在修改列时触发 SQL 更新

Posted

技术标签:

【中文标题】仅在修改列时触发 SQL 更新【英文标题】:SQL update trigger only when column is modified 【发布时间】:2012-08-21 15:40:28 【问题描述】:

通过查看其他示例,我想出了以下内容,但它似乎无法按我的意愿工作:我希望它仅在 QtyToRepair 值已更新时才更新修改后的信息...但它不会那样做。

如果我注释掉 where 则修改后的信息在每种情况下都会更新。正如我所说,其他例子让我保持乐观。任何线索表示赞赏。谢谢。

沃尔特

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    UPDATE SCHEDULE SET modified = GETDATE()
        , ModifiedUser = SUSER_NAME()
        , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S
    INNER JOIN Inserted I on S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    WHERE S.QtyToRepair <> I.QtyToRepair
END

【问题讨论】:

关于update() 的警告 - 它只测试列是否出现在更新列表中,并且对于插入总是正确的。它不会检查列值是否已更改,因为您可能有不止一行,其中一些值已更改,而有些则没有。 【参考方案1】:

你的问题有两种方式:

1- 在触发器中使用更新命令。

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
        ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END 
END

2- 在插入的表和删除的表之间使用连接

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;    

    UPDATE SCHEDULE 
    SET modified = GETDATE()
       , ModifiedUser = SUSER_NAME()
       , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S 
    INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber                  
    WHERE S.QtyToRepair <> I.QtyToRepair
    AND D.QtyToRepair <> I.QtyToRepair
END

当您对表 SCHEDULE 使用更新命令并将 QtyToRepair 列设置为新值时,如果新值等于一行或多行中的旧值,则解决方案 1 更新计划表中的所有更新行,但解决方案 2 仅更新安排旧值不等于新值的行。

【讨论】:

很抱歉没有提到它是 SQLServer。我需要使用已删除的表。我最终写入了一个单独的表(以维护历史记录)。 方法 #2 更好,如果您不希望在列更改为相同值时触发触发器。 我想我可能遗漏了一些东西 - 但方法 #1 不起作用,因为这是 更新之后,所以当前行将始终具有与插入行。如果您在更新后使用,您必须加入删除 @Rob “IF UPDATE”语句正在调用一个过程,该过程可以访问作为查询的一部分更新的列的列列表,因此不需要知道以前的值。 (我们刚刚在我们的环境中遇到了这个问题,并确认无论“AFTER UPDATE”子句如何,“IF UPDATE”都是有效的) 为什么要在第二个查询中加入inserted 表?它应该和表本身一样,不是吗?【参考方案2】:

fyi 我最终得到的代码:

IF UPDATE (QtyToRepair)
    begin
        INSERT INTO tmpQtyToRepairChanges (OrderNo, PartNumber, ModifiedDate, ModifiedUser, ModifiedHost, QtyToRepairOld, QtyToRepairNew)
        SELECT S.OrderNo, S.PartNumber, GETDATE(), SUSER_NAME(), HOST_NAME(), D.QtyToRepair, I.QtyToRepair FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber 
        WHERE I.QtyToRepair <> D.QtyToRepair
end

【讨论】:

我发现这很有帮助,因为上面的 2- Use Join between 答案由于 where 条件 WHERE S.QtyToRepair &lt;&gt; I.QtyToRepair AND D.QtyToRepair &lt;&gt; I.QtyToRepair 永远不会触发/匹配,因为第一个条件永远不会正确 -插入的表始终与实际表值匹配。使用 ` WHERE I.QtyToRepair D.QtyToRepair` 是我的关键。还有来自多个触发器触发的IF UPDATE (field) 帮助。【参考方案3】:

首先应该检查QtyToRepair 是否更新。

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
            ON S.OrderNo = I.OrderNo and S.PartNumber =    I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

【讨论】:

【参考方案4】:

您想要执行以下操作:

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;

    IF (UPDATE(QtyToRepair))
    BEGIN
        UPDATE SCHEDULE SET modified = GETDATE()
            , ModifiedUser = SUSER_NAME()
            , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo AND S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

请注意,无论值是否相同,每次更新列时都会触发此触发器。

【讨论】:

【参考方案5】:

每当一条记录更新时,一条记录就会被“删除”。这是我的例子:

ALTER TRIGGER [dbo].[UpdatePhyDate]
   ON  [dbo].[M_ContractDT1]
   AFTER UPDATE
AS 
BEGIN
    -- on ContarctDT1 PhyQty is updated 
    -- I want system date in Phytate automatically saved
    SET NOCOUNT ON;

    declare @dt1ky as int   

    if(update(Phyqty))
    begin
        select @dt1ky = dt1ky from deleted

        update M_ContractDT1 set PhyDate=GETDATE() where Dt1Ky=  @dt1ky     

    end

END

效果很好

【讨论】:

这是不正确的,因为触发器认为它适用于一条记录,但实际上它适用于记录集。

以上是关于仅在修改列时触发 SQL 更新的主要内容,如果未能解决你的问题,请参考以下文章

通过触发器修改列时重新分配序列号

当用户在 MS Access 中修改表中的另一列时,如何在 SQL Server 中将列设置为今天的日期 [关闭]

sql 检测更新触发器中修改的字段(sql server 2005)?

在更新一个表的列相对于另一个表的列时避免多个 SELECT

当使用特定值更新多个列时,在 VIEW 上使用触发器执行存储过程

phPmyAdmin,如何创建一个触发器,当有人更新一行中的任何列时,它将在特定列中添加一个“1”