【问题标题】:SQL: Trigger is not collecting every rowSQL:触发器没有收集每一行
【发布时间】:2020-07-27 10:30:17
【问题描述】:

我的触发器有问题,当我在另一个表中有新行时应该更新一个表。

我有两张桌子:

第一:

SELECT 
    [Id], [Timestamp], [MachineName], [StatusId], [Quantity]
FROM [dbo].[Events]

第二个:

SELECT 
    [Id], [MachineName], [StatusId], [QuantitySum], [StatusLastRefresh]
FROM [dbo].[ActualParams]

到表 [Events] 会处理来自流分析的每台机器上的所有状态更改。对于每一个新行,第二个表都应该更新并显示每台机器的最后一个值,最后一个时间戳在 StatusLastRefresh 中。实际上表[ActualParams]有4行数据。

我已经尝试了 2 个触发器来这样做:

ALTER TRIGGER [dbo].[AfterInsertEvent] 
ON [dbo].[Events] 
AFTER INSERT
AS 
    MERGE ActualParams AS ap
    USING (SELECT Id, Machine, Status, Timestamp, Quantity 
           FROM inserted) AS ev ON ev.MachineName = ap.MachineName

    WHEN MATCHED THEN
       UPDATE SET map.StatusId = ev.StatusId, 
                  ap.StatusLastRefresh = ev.Timestamp, 
                  ap.QuantitySum = ap.QuantitySum + ev.Quantity;

触发器 #2:

ALTER TRIGGER [dbo].[AfterInsertEvent] 
ON [dbo].[Events] 
AFTER INSERT
BEGIN
    DECLARE @mn nchar(10), @si int, @ts datetime2(7), @q int

    SELECT @mn = MachineName, @si = StatusId, @ts = Timestamp, @q = Quantity 
    FROM inserted

    UPDATE ActualParams
    SET StatusId = @si, 
        StatusLastRefresh = @ts, 
        QuantitySum = QuantitySum + @q  
    WHERE ActualParams.MachineName = @mn
END

但它们都不能 100% 正确工作。

首先,Merge 触发器在每个 MachineName 同时插入一个行时可以正常工作,但有时对于同一 MachineName 插入的新行很少同时出现,我收到错误它不能同时将多行写入一个并且一切都卡住了。

其次,更简单的Update 触发器有时会跳过一行,并且在 ActualParams 表中有先前的 StatusId 值,而不是实际值。实际上我有 4 台机器,通常其中一台的 StatusId 错误。我想知道当有 20 台或更多机器时会发生什么。

MachineName 对于每台机器都是唯一的。

我用 UPDATE 尝试了 CURSOR,但有时仍会跳过该行,并且状态不是实际的。

我不知道如何将 CURSOR 与 MERGE 结合起来,也许这将是解决方案。

它必须对每一行做出反应,因为我还想在第二个表中汇总数量,所以这就是为什么我不能跳过任何行(之前忘记了 - 已编辑)

有什么想法吗?我正在使用 SSMS 17。

【问题讨论】:

  • 您使用的是哪个 dbms?
  • @jarlh 我正在使用 SSMS
  • 最重要的是:AVOID 触发器内的游标 不惜一切代价! 游标很糟糕、很慢,而且几乎没有需要 - 触发器应该非常小、灵活和快速 - NOT 混合使用这两者!你可以给自己带来的最糟糕的性能噩梦......

标签: sql-server tsql database-trigger sql-merge


【解决方案1】:

您为什么不直接用update/join 写这个?

UPDATE ap
    SET StatusId = i.StatusId,
        StatusLastRefresh = i.TimeStamp
    FROM ActualParams ap
         inserted i
         ON ap.MachineName = i.MachineName;

如果同一台机器可以在一个insert中多次更新,那么使用窗口函数获取最后一行:

UPDATE ap
    SET StatusId = i.StatusId,
        StatusLastRefresh = i.TimeStamp
    FROM ActualParams ap
         (SELECT i.*,
                 ROW_NUMBER() OVER (PARTITION BY MachineName ORDER BY TimeStamp DESC) as seqnum
          FROM inserted i
         ) i
         ON ap.MachineName = i.MachineName AND seqnum = 1;

【讨论】:

  • 简单性很好——第一个代码。我只是在“插入 i”之前添加了“JOIN”,因为它似乎错过了。在超过 500 个新行之后没有损失。谢谢! :)
猜你喜欢
  • 1970-01-01
  • 2018-03-18
  • 2019-08-16
  • 1970-01-01
  • 1970-01-01
  • 2015-04-03
  • 2018-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多