【问题标题】:Trigger : update column based on value of another column触发器:根据另一列的值更新列
【发布时间】:2021-09-17 10:48:30
【问题描述】:

我们有一个包含状态列的表,并且与该列关联的是用于跟踪状态何时更改值的列。

例如,我们的状态为“关闭”和“发现关闭”状态以及关联的列DateOffDateFoundOff。我正在尝试创建触发器以在状态更改时更新这些日期列。

这对我来说似乎很简单,但是当Status 正确更改关联的日期列更新,但另一个日期列变为空时,会发生这种情况。因此,如果我更改 Status = 'Off' DateOff 的日期正确,但 DateFoundOff 变为空,反之亦然。

我创建了两个触发器 - 第一个是:

ALTER TRIGGER [GIS].[UPDATE_FOUNDOFF]
ON [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
    IF (UPDATE (OutageStatus))
    BEGIN
        SET NOCOUNT ON;

        UPDATE [gis].[METEROUTAGEPOINTS] 
        SET DateFoundOff = CURRENT_TIMESTAMP
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        WHERE i.OutageStatus = 'Found Off'
    END

第二个

ALTER TRIGGER [GIS].[UPDATE_DATES]
ON  [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
    IF (UPDATE (OutageStatus))
    BEGIN
        SET NOCOUNT ON;

        UPDATE [gis].[METEROUTAGEPOINTS] 
        SET DateOff = CURRENT_TIMESTAMP 
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        WHERE i.OutageStatus = 'Off'
    END

我只是不明白一个触发器如何将 Date 列的值更改为与当前状态值无关的 null。

谢谢。

编辑:发现问题不在于触发器,而在于用于编辑数据的工具如何保持某些东西。不知道我明白为什么,但是通过更改编辑工作流程问题得到了解决。将答案标记为正确,因为它提供了一种更好的方法来编写触发器

【问题讨论】:

  • 表的主键是什么?
  • 请注意,IF UPDATE 没有达到您的预期。如果您只想在值实际更改时执行某些操作,则需要检查之前(删除)版本与之后(插入)版本。
  • 我建议设置一个 dbfiddle,因为它目前无法重现。
  • 我投票结束这个问题,因为我是 OP 我发现这个问题与帖子中提出的问题无关

标签: sql sql-server tsql triggers sql-update


【解决方案1】:

您的更新语句都不会将 DateFoundOffDateOff 更改为 null。一定是发生了其他事情。

不过,我会按如下方式改进您的触发器:

  1. 只使用一个触发器,每个触发器都有开销,一个触发器和一个更新语句将比 2 运行得快。
  2. 您实际上并没有检查状态是否已更改,您所检查的只是更新是否包含该列。添加对deleted 表的检查实际上检查值是否已更改。
ALTER TRIGGER [GIS].[UPDATE_FOUNDOFF]
ON [GIS].[METEROUTAGEPOINTS]
AFTER UPDATE
AS 
BEGIN
    -- Avoid doing any processing if no rows are updated
    IF NOT EXISTS (SELECT 1 FROM Inserted) RETURN;

    SET NOCOUNT ON;

    IF UPDATE(OutageStatus) BEGIN
        UPDATE [gis].[METEROUTAGEPOINTS] SET
            DateFoundOff = CASE WHEN i.OutageStatus = 'Found Off' AND d.OutageStatus <> 'Found Off' THEN CURRENT_TIMESTAMP ELSE DateFoundOff END
            , DateOff = CASE WHEN i.OutageStatus = 'Off' AND d.OutageStatus <> 'Off' THEN CURRENT_TIMESTAMP ELSE DateOff END
        FROM gis.METEROUTAGEPOINTS mop 
        INNER JOIN inserted AS i ON i.ConObject = mop.ConObject
        INNER JOIN deleted AS d ON d.ConObject = mop.ConObject;
    END;
END;

【讨论】:

  • 根据微软规范,你不应该把 IF (ROWCOUNT_BIG() = 0) RETURN;在这个触发器的开始? docs.microsoft.com/en-us/sql/t-sql/statements/…
  • @dale-k 我确实尝试将连接包含在 d 表中,尽管在我发布的示例中忽略了它。我将设置为单个语句,我最初尝试使用单个语句的方法,但根据您的示例可能不正确。我闯入了多个触发器,认为这可能是导致我的问题的原因。我将研究可以在数据库中配置以导致此问题的任何其他内容
  • 我实际上已经在我的触发器中转到IF (NOT EXISTS (SELECT 1 FROM inserted)),因为在ROWCOUNT之前放置任何语句的风险
  • ..@@rowcount 在 MERGEing 时不可靠
  • @DaleK 经过额外审查,该问题与用于编辑的工具以及它如何与行保持某些内容有关。不确定我是否了解这些细节,但在改变流程之后,一切都按预期工作。感谢您提供有关设置触发器的正确​​和最有效方法的所有建议
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
  • 2019-10-13
  • 2021-09-05
相关资源
最近更新 更多