SQL 触发加倍汇总金额
SQL Trigger Doubling Summary Amounts
构建购物车,等等。
两个 tables,Cart
和 CartLine
链接到 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
查看您的架构,我发现了一大堆问题。排名不分先后:
- 大多数列
NULL
可用。为什么?如果 Cart
没有 DateTimeCreated
,这意味着什么,为什么会有这样的行?那么Deleted
,有没有第三种不确定状态(量子力学?)既没有删除也没有not-deleted?
- 使用
money
数据类型,存在严重的舍入问题。请改用 decimal
,以达到适当的精度和比例。
- 使用
tbl
前缀很烦人,反正每个人都知道它们是 table。
- 您现有的触发代码也有问题。绝对缺乏正确的格式,使其不可读。空格是免费的,你知道的。
- 在更新和删除的情况下,您没有检查
deleted
虚拟 table。您需要通过主键加入它。
- 不需要多次re-join和table,你可以直接使用
inserted
和deleted
并减去差值。
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);
构建购物车,等等。
两个 tables,Cart
和 CartLine
链接到 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
查看您的架构,我发现了一大堆问题。排名不分先后:
- 大多数列
NULL
可用。为什么?如果Cart
没有DateTimeCreated
,这意味着什么,为什么会有这样的行?那么Deleted
,有没有第三种不确定状态(量子力学?)既没有删除也没有not-deleted? - 使用
money
数据类型,存在严重的舍入问题。请改用decimal
,以达到适当的精度和比例。 - 使用
tbl
前缀很烦人,反正每个人都知道它们是 table。 - 您现有的触发代码也有问题。绝对缺乏正确的格式,使其不可读。空格是免费的,你知道的。
- 在更新和删除的情况下,您没有检查
deleted
虚拟 table。您需要通过主键加入它。 - 不需要多次re-join和table,你可以直接使用
inserted
和deleted
并减去差值。 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);