【问题标题】:SQL Trigger - determine if a field has changedSQL 触发器 - 确定字段是否已更改
【发布时间】:2017-02-20 15:59:02
【问题描述】:

我有这个触发器

ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment] 
   ON [dbo].[OpportunityBase]
   FOR UPDATE
AS 
BEGIN

IF UPDATE(owninguser)
BEGIN
-- do the task
END
END

我只想在 owninguser 实际发生变化的情况下执行该任务。我该如何确定?

谢谢

【问题讨论】:

  • 您需要在deleted表和inserted表之间比较owninguser的值。但是,并不是每个语句触发触发器,而不是每行触发,这意味着如果您更新多行,触发器只会触发一次。
  • AFAIK 它甚至适用于多个更新的行。
  • 我错过了e:应该是note that triggers are...。无论如何,它确实适用于更新的多行,但也有可能并非所有更新的行在更新后都会在特定列中具有不同的值。
  • 触发器只能用于非常简单的任务(例如将日期时间戳写入审计字段)。触发 INSERT 语句的进程最好知道 owninguser 的值是否发生了变化,因此应该决定触发一个额外的任务;特别是如果该任务很复杂/需要长时间运行。

标签: sql sql-server triggers


【解决方案1】:
ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment] 
   ON [dbo].[OpportunityBase]
   FOR UPDATE
AS 
BEGIN
DECLARE HasChanged int = 0;

SELECT @HasChanged = 1
FROM Inserted AS I
INNER JOIN Deleted AS D
ON I.PK = D.PK
AND IsNull(I.owninguser,'~') <> IsNull(D.owninguser,'~')

IF @HasChanged = 1
BEGIN
-- do the task
END
END

比较 Inserted 和 Deleted 之间字段的值,将两个表连接到主键上。

【讨论】:

  • 这将引发更新语句的错误,其中包含更改了owninguser 列的多行。
  • @ZoharPeled 提出了一个很好的观点。此答案的代码假定 UPDATE 语句只会影响一行。
  • 多行更新不会报错。如果至少更新了一行,它会将 HasChanged 的​​值更改为 1。在触发器中,如果 HasChanged 为真,IF 语句仍然可以使用 Inserted/Deleted 表来影响进一步的逻辑。
  • 是的,@HasChanged 被分配了正确的值并且是正确的。但是,问题是如果“任务”必须在每个拥有用户的基础上执行,那么这个答案是不完整的。我们不知道——OP 没有明确指定这个 IMO。
  • @DMason - 它并不完整。只要至少有一个拥有用户发生了变化,它就会执行任务逻辑。这就是 OP 所要求的。 OP 没有就任务部分寻求帮助。我的回答不会因为 OP 可能会增加更多要求而无效。我们都不知道任务是什么,所以每个答案都会缺少这个。
【解决方案2】:
ALTER TRIGGER [dbo].[tInsertTaskFromOpportunityReassignment] 
   ON [dbo].[OpportunityBase]
   FOR UPDATE
AS
BEGIN
/*
    I will assume that your [dbo].[OpportunityBase] table has a PRIMARY KEY
    or UNIQUE column that is immutable to join the inserted and deleted
    tables. In this example, [OpportunityBaseId] is that column.

    The SELECT query returns a set of all records that had the value of [owninguser] 
    changed. What you would do from that point is up to you.
*/
SELECT 
    i.[OpportunityBaseId], i.owninguser New_owninguser, d.owninguser Old_owninguser
FROM inserted i
JOIN deleted d
    ON i.[OpportunityBaseId] = d.[OpportunityBaseId]
    AND 
        (
            --owninguser value was changed.
            i.[owninguser] <> d.[owninguser] OR 

            --owninguser changed from non-NULL to NULL.
            (i.[owninguser] IS NULL AND d.[owninguser] IS NOT NULL) OR 

            --owninguser changed from NULL to non-NULL.
            (i.[owninguser] IS NOT NULL AND d.[owninguser] IS NULL)
        )
END 
GO

【讨论】:

    【解决方案3】:

    我喜欢使用 concat,尽管它可能会给您带来 NULL 和 '' 的问题:

    CONCAT('', inserted.owninguser) <> CONCAT('', deleted.owninguser)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-09
      • 2012-04-14
      • 2016-12-19
      • 1970-01-01
      • 1970-01-01
      • 2019-09-27
      • 1970-01-01
      • 2016-06-23
      相关资源
      最近更新 更多