【问题标题】:SQL Server AFTER INSERT Trigger updating a 2nd table multiple times per insertSQL Server AFTER INSERT 触发器每次插入多次更新第二个表
【发布时间】:2017-11-24 15:31:30
【问题描述】:

这让我很难过。我们有一个带有 AFTER INSERT 触发器的事务表,用于更新客户表中的余额。触发器将在为单个客户插入的虚拟表中触发多行是完全正常的。

例子:

客户余额为 500

在为客户生成多行的交易表上进行 INSERT,第一行是 50 借方,第二行是 100 借方。

预期结果后触发器是客户余额为 350。实际结果是 450,因为看起来触发器中的联接到客户每次返回相同的余额,而不是前一行更新后的余额。

insert #1 取出 500 的余额并从中扣除 50 插入 #2 取 500 而不是 450 的余额并从中扣除 100

这里是触发器:

CREATE TRIGGER [dbo].[tBalances] 
ON  [dbo].[tblCustomerTransaction]
AFTER INSERT
AS 
BEGIN
    IF @@ROWCOUNT = 0 RETURN
    SET NOCOUNT ON; 

    UPDATE  C
    SET     C.AvailableBalance = C.AvailableBalance + CASE WHEN TT.bIsDebit = 1 THEN I.decAmount * -1 ELSE I.decAmount END,
            C.BonusBalance = C.BonusBalance + CASE WHEN TT.bIsDebit = 1 THEN I.decBonusAmount * -1 ELSE I.decBonusAmount END
    FROM    inserted AS I
            INNER JOIN tblCustomer AS C ON I.iCustomerID = C.CustomerId         
            INNER JOIN tblTransactionType AS TT ON I.iTransactionTypeID = TT.iTransactionTypeID         
    WHERE   I.iManagerID = 12345
END

为什么客户表的更新没有针对插入的每一行进行?

【问题讨论】:

  • 两个概念:1)SQL Server 触发器总是语句级别 2)All-at-once 原则
  • UPDATEs(和其他 DML)好像所有行都在同时受到影响。没有“前一行”需要考虑。您是否有理由不只是使用索引视图自动维护这些聚合?
  • 好吧,这是有道理的。这个问题的解决方案是什么?确定不是光标循环?

标签: sql-server triggers


【解决方案1】:

如果您必须手动计算这些聚合(而不是仅仅定义索引视图并让 SQL Server 自动维护它们),请确保每个目标行只接受一次更新 - 使用分组:

UPDATE  C
SET     C.AvailableBalance = C.AvailableBalance + BalanceChange,
        C.BonusBalance = C.BonusBalance + BonusChange
FROM    (SELECT iCustomerID,
           SUM(CASE WHEN TT.bIsDebit = 1 THEN I.decAmount * -1
                                         ELSE I.decAmount END)
           as BalanceChange,
           SUM(CASE WHEN TT.bIsDebit = 1 THEN I.decBonusAmount * -1
                                         ELSE I.decBonusAmount END)
           as BonusChange
        FROM inserted AS I
        INNER JOIN tblTransactionType AS TT ON I.iTransactionTypeID = TT.iTransactionTypeID
        WHERE   I.iManagerID = 12345
        GROUP BY iCustomerID) t
 INNER JOIN tblCustomer AS C ON t.iCustomerID = C.CustomerId

(如果不完全正确,希望您能看到这如何应用于您的实际查询)

【讨论】:

  • 当你说定义一个索引视图时,你的意思是在客户表上创建一个视图来计算所有客户交易中的余额吗?
  • @IntoNET - Indexed Views - 基本上,视图的 result 由 SQL Server 存储,它使用类似于触发器的东西来根据更改来更新结果基表(除了类似触发器的代码已经过测试并使用了 数百万次 次)
  • 谢谢。它在短期内解决了我的问题,但我也会研究索引视图的想法。
猜你喜欢
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-16
相关资源
最近更新 更多