我有一个名为Orders
的表,其中包含客户下的每个订单的信息。出于测试目的,我创建了一个名为OrderCountTable
的表,用于统计每个客户下了多少订单。以更新在Orders
表上插入触发器之后创建的该表i,从而在表中插入记录之后,触发器将为客户更新OrderCountTable
中的订单计数。当我在Orders
表中插入一条新记录时,从输出来看,触发器似乎被触发了,但当我检查OrderCountTable
时,它总是空的,我错过了什么吗??
CREATE TABLE OrderCountTable(
CustomerID int,
OrderCount int);
Go
CREATE TRIGGER CustomerOrderCount
ON Orders
AFTER INSERT
AS
BEGIN
DECLARE @CUID as int = (select CustomerID from inserted);
IF((select COUNT(CustomerID) from OrderCountTable where CustomerID = @CUID) IS NULL)
BEGIN
INSERT INTO OrderCountTable VALUES(@CUID,1);
Print 'New record was inserted for ' + CONVERT(varchar,@CUID);
END
ELSE
BEGIN
UPDATE OrderCountTable SET OrderCount = OrderCount + 1;
Print 'Update the record for the customer with ID ' + CAST(@CUID as varchar);
END
END
--插入新记录:
INSERT INTO Orders(ProductID,CustomerID,Quantity,OrderDate,[Status]) VALUES(1,3,4,'2022-04-3 16:22:53','OPENED');
--输出:
(0 rows affected)
Update the record for the customer with ID 3
(1 row affected)
Completion time: 2022-04-03T16:53:54.6452596+03:00
--检查两个表:
OrderID ProductID CustomerID Quantity OrderDate Status
17 1 3 4 2022-04-03 16:22:53 OPENED
CustomerID OrderCount
您的触发器主体应该是:
UPDATE oct SET OrderCount += 1
FROM dbo.OrderCountTable AS oct
INNER JOIN inserted AS i
ON i.CustomerID = oct.CustomerID;
INSERT dbo.OrderCountTable(CustomerID, OrderCount)
SELECT CustomerID, COUNT(*)
FROM inserted AS i
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.OrderCountTable AS oct
WHERE oct.CustomerID = i.CustomerID
);
您永远不应该依赖于将inserted
或deleted
中的单个值分配给变量,因为与其他平台不同,即使DML操作影响多行,SQL Server也只触发一次触发器。这种策略似乎";工作;但在多行操作的情况下,变量只分配一个任意值,以后的操作将忽略所有其他受影响的行。这仅仅是";"成功";因为它通过了一些单元测试和/或没有异常,而且人们无法检查触发器的操作是否对所有目标行都正确工作。几年前的这篇演讲值得一看。
也许你有它们,但没有提到,或者什么都没有被删除,但正如SMor所指出的,当订单被删除时,你还需要带逻辑的触发器来减少第二个表中的计数(或者当客户被删除时完全删除行(,或者当两个客户被合并或Orders.CustomerID因其他原因更新时合并行。
或者,您可以考虑不将这些信息存储在任何地方,因为您总是可以从Orders
获取信息,而不需要第二个表或触发器:
SELECT CustomerID, OrderCount = COUNT(*)
FROM dbo.Orders
GROUP BY CustomerID;
您还可以创建一个索引视图来替换您的表(也可以消除对触发器的任何需求(:
CREATE VIEW dbo.RedundantOrderCount
WITH SCHEMABINDING
AS
SELECT CustomerID, OrderCount = COUNT_BIG(*)
FROM dbo.Orders
GROUP BY CustomerID;
GO
CREATE UNIQUE CLUSTERED INDEX CIX_RedundantOrderCount
ON dbo.RedundantOrderCount(CustomerID);
这也会自动处理订单被更新或删除的情况。但是,维护该视图仍然可能是浪费,除非有太多的活动导致直接查询变得过于昂贵并且您运行查询的频率高于更新表的频率,这是不可能的。
过早的优化(这是你通过冗余存储聚合来避免以后计算它们(看起来很有趣,也很有用,但通常这是一种浪费,会对你的整体工作量产生负面影响。至少在理论上,使用稍微快一点的查询来检索计数通常会让您忽略工作负载的写入方面的工作难度。它不应被忽视。