SQL 触发加倍汇总金额

SQL Trigger Doubling Summary Amounts

构建购物车,等等。

两个 tables,CartCartLine 链接到 CartID 字段。当添加、更新、删除 CartLine 记录时,我需要一个 sql 触发器来更新 Cart table 中的总字段。我的代码如下。当 update/etc 时,我的金额似乎翻了一番。发生。

代码: '

--Update cart totals.
begin
with cte2 as (
    select
      c.CartID,
      isnull(sum(cl.GoodsTotal), 0) [TotalGoods],
      isnull(sum(cl.LineTotal), 0) [TotalPrice],
      isnull(sum(cl.TaxTotal), 0) [TotalTax],
      (isnull(sum(cl.LineTotal), 0) + isnull(sum(cl.TaxTotal), 0)) [TotalTotal]

    from tblCartLine cl with (nolock)
    join inserted i with (nolock) on cl.CartID = i.CartID
    inner join tblCart c with (nolock) on i.CartID = c.CartID
    where isnull(cl.Deleted, 0) = 0
    group by c.CartID
)
    
update tblCart
set
  TotalCost = cte2.TotalGoods,
  TotalPrice = cte2.TotalPrice,
  TotalTax = cte2.TotalTax,
  TotalTotal = cte2.TotalTotal
                    
from tblCart c
inner join cte2 on c.CartID = cte2.CartID
where c.CartID = cte2.CartID

db<>fiddle

查看您的架构,我发现了一大堆问题。排名不分先后:

  • 大多数列 NULL 可用。为什么?如果 Cart 没有 DateTimeCreated,这意味着什么,为什么会有这样的行?那么Deleted,有没有第三种不确定状态(量子力学?)既没有删除也没有not-deleted?
  • 使用 money 数据类型,存在严重的舍入问题。请改用 decimal,以达到适当的精度和比例。
  • 使用 tbl 前缀很烦人,反正每个人都知道它们是 table。
  • 您现有的触发代码也有问题。绝对缺乏正确的格式,使其不可读。空格是免费的,你知道的。
  • 在更新和删除的情况下,您没有检查 deleted 虚拟 table。您需要通过主键加入它。
  • 不需要多次re-join和table,你可以直接使用inserteddeleted并减去差值。
  • NOLOCK 是错误的做法。如果您担心锁定,那么您可能应该使用 SNAPSHOT 隔离,如果您担心性能,您可以使用 WITH (TABLOCK) 以获得相同的好处。
  • 触发器似乎不需要修改CartLine,你可以使用计算列:
    ALTER TABLE tblCartLine
        ADD ExtCost AS (Quantity * Cost);
    ALTER TABLE tblCartLine
        ADD TaxTotal AS (Quantity * Price) * (TaxRate / 100.0);
    ALTER TABLE tblCartLine
        ADD LineTotal AS (Quantity * i.Price);
    

然后你的触发器应该是这样的

CREATE TRIGGER [dbo].[UtblCartLine] 
   ON  [dbo].[tblCartLine] 
   AFTER INSERT,DELETE,UPDATE
AS 

SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.UtblCartLine')) > 0
    RETURN;
IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
    RETURN;

UPDATE tblCart
SET
  TotalCost  += i.DiffGoods,
  TotalPrice += i.DiffPrice,
  TotalTax   += i.DiffTax,
  TotalTotal += i.DiffTotal
FROM tblCart c
JOIN (
    SELECT
      ISNULL(i.CartID, d.CartID) CartID,
      ISNULL(SUM(i.GoodsTotal), 0) - ISNULL(SUM(d.GoodsTotal), 0) DiffGoods,
      ISNULL(SUM(i.LineTotal), 0) - ISNULL(SUM(d.LineTotal), 0) DiffPrice,
      ISNULL(SUM(i.TaxTotal), 0) - ISNULL(SUM(d.TaxTotal), 0) DiffTax,
      ISNULL(SUM(i.LineTotal + i.TaxTotal), 0) - ISNULL(SUM(d.LineTotal + d.TaxTotal), 0) DiffTotal
    FROM inserted i
    FULL JOIN deleted d ON d.CartLineID = i.CartLineID
    GROUP BY
      ISNULL(i.CartID, d.CartID)
) i ON i.CartID = c.CartID;

添加 Deleted = 0 需要条件聚合才能正确执行。


但是,我建议您完全不要使用触发器。

改用视图。如果性能需要,您可以使用 索引视图 。服务器可以根据任何 updates/inserts 维护视图上的索引,并有效地自动执行上述所有代码。

索引视图有一些限制。特别是:

  • 必须是 schema-bound,因此您不能在不删除视图的情况下更改基础列。
  • 仅内部联接。
  • 没有子查询或派生 tables.
  • 允许聚合,但您必须有一个 COUNT_BIG(*) 列,唯一允许的其他聚合是 SUM.
CREATE VIEW CartTotal
WITH SCHEMABINDING AS

SELECT
  cl.CartID,
  COUNT_BIG(*) NumberOfLines,
  SUM(cl.GoodsTotal) TotalGoods,
  SUM(cl.LineTotal) TotalPrice,
  SUM(cl.TaxTotal) TotalTax,
  SUM(cl.LineTotal + cl.TaxTotal) TotalTotal
FROM tblCartLine cl
WHERE cl.Deleted = 0
GROUP BY
  cl.CartID;

go
CREATE UNIQUE CLUSTERED INDEX CX_CartTotal ON CartTotal (CartID);