【问题标题】:Trigger doesn't work as expected触发器未按预期工作
【发布时间】:2013-06-13 08:23:25
【问题描述】:

我的触发器有点问题。 它应该:

  • 在我的主表中更新插入的历史表并添加时间戳。
  • 如果历史表中的一行已经具有相同的值,则不应添加任何内容,但应增加计数器并更新时间。
  • 如果距离上次更新超过 24 小时,将创建一个新行。

草率的伪代码:

IF NOT EXISTS ( --if the value isn't in the history table
    SELECT History.value1 FROM History, INSERTED
    WHERE History.value1 LIKE INSERTED.value1
    AND History.value2 LIKE INSERTED.value2
       ) 
       OR EXISTS ( --or if it has been added over 24h ago
        SELECT History.value1 FROM History, INSERTED
        WHERE History.value1 LIKE INSERTED.value1
        AND History.value2 LIKE INSERTED.value2
        AND DATEDIFF(HOUR,History.time, GETDATE()) > 24
       )     
    BEGIN --Insert it
        INSERT INTO History(value1, value2, counter, time)
        SELECT value1, value2, counter GETDATE() FROM INSERTED
    END
    ELSE
    BEGIN  -- else, increase counter and add new time
        UPDATE History
        SET History.time = GETDATE(), 
                               History.Items = History.Items + INSERTED.Items
        FROM History
        JOIN INSERTED ON History.value1 = INSERTED.value1
        AND History.value2 = INSERTED.value2
            AND DATEDIFF(HOUR, _History.time, GETDATE()) < 24;
    END

示例表:

__________________________________________________
| value1 | value2 | counter | time(last updated) |
+------------------------------------------------+
| test1  | test2  |    1    |        < 24h       |
| test3  | test4  |    1    |        > 24h       |
| test3  | test4  |    1    |        < 24h       |
+------------------------------------------------+

输入:

INSERT INTO main_table(value1, value2, counter)
VALUES ('test3', 'test4', 1);

结果表:

__________________________________________________
| value1 | value2 | counter | time(last updated) |
+------------------------------------------------+
| test1  | test2  |    1    |        < 24h       |
| test3  | test4  |    1    |        > 24h       |
| test3  | test4  |    1    |        < 24h       | <--This counter+time should be updated
| test3  | test4  |    1    |        < 24h       | <--This row shouldn't be added
+------------------------------------------------+

我明白为什么会发生这种情况(因为代码找到了超过 24 小时的历史值,而忽略了较新的值)但我不知道如何修复它。

【问题讨论】:

  • 请记住inserted 可以包含多个 行,我认为您的IF/ELSE 逻辑可能不正确。如果 some 行已经有历史值而有些没有呢?另外,您使用的是哪个版本的 SQL Server?
  • 在服务器版本上运行:11.0.2100.60

标签: sql sql-server database triggers


【解决方案1】:

我认为如果 inserted 包含混合行,您的触发器仍然会被破坏 - 因为您的 IF/ELSE 结构对要采取的操作做出单一决定。

最好有一个MERGE,比如:

;MERGE INTO History h USING INSERTED i
   ON h.Value1 = i.Value1 and h.Value2 = i.Value2 and
      DATEDIFF(HOUR,h.time, GETDATE()) <= 24
WHEN MATCHED THEN
   UPDATE SET time = GETDATE(), Items = h.Items + i.Items
WHEN NOT MATCHED THEN
   INSERT (Value1,Value2,Items,time)
   VALUES (i.Value1,i.Value2,i.Items,GETDATE());

应该替换你的整个触发器体。


顺便说一下,DATEDIFF 计算跨越边界的转换次数,而不是计算精确的差异(例如,DATEDIFF(hour,'00:59','01:01') 是 1)。如果您想更接近同时考虑分钟和秒的 24 小时截止时间,更好的比较是:

h.Time >= DATEADD(day,-1,GETDATE())

顺便说一句,这也允许使用包含Historytime 列的索引。

【讨论】:

  • 我已经按照您发布的方式进行了尝试,但它在第 2 行显示“';' 附近的语法不正确”(ON h.value = i.value.....)
  • @Yoan - 你有你的表、数据库等。我没有。这使得发布完全调试的查询有点困难。
【解决方案2】:

--像这样更改您的标准语句。它会起作用的。反正你写的触发器真不好。触发器应设计为处理集合而不是记录,因为一批插入/更新/删除只能触发一次。

IF NOT EXISTS ( --if the value isn't in the history table
SELECT History.value1 FROM History, INSERTED
WHERE History.value1 LIKE INSERTED.value1
AND History.value2 LIKE INSERTED.value2
   ) 
   OR EXISTS ( --or if it has been added over 24h ago
    SELECT INSERTED.value1 
    FROM INSERTED
    cross apply
        (select max(History.[time]) mx_time
        from    History
        where   History.value1 LIKE INSERTED.value1
                AND History.value2 LIKE INSERTED.value2
        ) as t
    WHERE DATEDIFF(HOUR,t.mx_time, GETDATE()) >= 24
   )     
BEGIN --Insert it
    INSERT INTO History(value1, value2, items, time)
    SELECT value1, value2, items, GETDATE() FROM INSERTED
END
ELSE
BEGIN  -- else, increase counter and add new time

    UPDATE History
    SET History.time = GETDATE(), 
                           History.Items = History.Items + INSERTED.Items
    FROM History
    JOIN INSERTED ON History.value1 = INSERTED.value1
    AND History.value2 = INSERTED.value2
        AND DATEDIFF(HOUR, History.time, GETDATE()) < 24;
END

【讨论】:

    【解决方案3】:

    我的天啊,我解决了它^^ 一直在尝试修复它大约 2 小时,就在我将它发布到 stackoverflow 时,我设法修复它^^

    我做了什么:

    将第二个“EXIST”测试从:

    OR EXISTS ( --or if it has been added over 24h ago
            SELECT History.value1 FROM History, INSERTED
            WHERE History.value1 LIKE INSERTED.value1
            AND History.value2 LIKE INSERTED.value2
            AND DATEDIFF(HOUR,History.time, GETDATE()) > 24
           )     
    

    到:

    OR NOT EXISTS ( --or if it has been added over 24h ago
            SELECT History.value1 FROM History, INSERTED
            WHERE NOT History.value1 LIKE INSERTED.value1
            OR History.value2 LIKE INSERTED.value2
            OR DATEDIFF(HOUR,History.time, GETDATE()) > 24
           )     
    

    【讨论】:

    • "已解决" - 您阅读并理解我的评论了吗?您的触发器可能仍然损坏。
    • 是的,但我认为插入不会用多行来完成。
    • 编写因多行插入(更新、删除)而损坏的触发器是一个坏习惯。尤其是在这种情况下,它不会产生错误消息,甚至不会产生警告——它只会做错事。
    • 对如何修复它有任何建议吗?因为我不知道^^'
    • 我已经发布了我的答案(虽然我只是稍微润色了一下)
    猜你喜欢
    • 2013-07-19
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多