我有一个SQL Server 2012
并查询
SELECT ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as badSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM SomeTable
GROUP BY ProductId
一些表定义
ManagerId | SoldInDay | Category
1 50 PC
1 20 Laptop
2 30 PC
3 40 Laptop
所以,问题是: 这是否意味着Sql会遍历所有行两次?那么,每个聚合函数是否在表中所有行的单独循环中执行?或者它更聪明?
这个查询无关紧要,这是我的梦想。
答案 0 :(得分:1)
(看来你的问题是通过评论解决的,但为了完整起见,我在这里提供了正式答案。)
首先,您的示例SQL不会运行。您在字段列表中包含ManagerId,但不包括GROUP BY。您将收到类似于此的错误:
消息8120,级别16,状态1,行9列'@ SomeTable.ManagerID'是 在选择列表中无效,因为它不包含在任何一个中 聚合函数或GROUP BY子句。
假设您在字段列表中使用“ManagerId”而不是“ProductId”,我会重现您的情况并审核执行计划。它只显示了一个“Stream Aggregate”运算符。您可以通过将聚合分成两个不同的公用表表达式(CTE)并将它们重新连接在一起来强制它在表上运行两次。在这种情况下,您会看到两个Stream Aggregate运算符(每个运行一个运行表)。
以下是生成执行计划的代码:
DECLARE @SomeTable TABLE
(
ManagerId int,
SoldInDay int,
Category varchar(50)
);
INSERT INTO @SomeTable (ManagerId, SoldInDay, Category) VALUES (1, 50, 'PC');
INSERT INTO @SomeTable (ManagerId, SoldInDay, Category) VALUES (1, 20, 'Laptop');
INSERT INTO @SomeTable (ManagerId, SoldInDay, Category) VALUES (2, 30, 'PC');
INSERT INTO @SomeTable (ManagerId, SoldInDay, Category) VALUES (3, 40, 'Laptop');
/*
This produces an error:
Msg 8120, Level 16, State 1, Line 9
Column '@SomeTable.ManagerID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as badSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM @SomeTable
GROUP BY ProductId;
*/
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as BadSoldDays,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM @SomeTable
GROUP BY ManagerId;
WITH DaysWithSoldPcTable AS
(
SELECT
ManagerId,
SUM(CASE WHEN Category = 'PC' THEN 1 ELSE 0 END) as DaysWithSoldPc
FROM @SomeTable
GROUP BY ManagerId
), BadSoldDaysTable AS
(
SELECT
ManagerId,
SUM(CASE WHEN SoldInDay < 30 THEN 1 ELSE 0 END) as BadSoldDays
FROM @SomeTable
GROUP BY ManagerId
)
SELECT
DaysWithSoldPcTable.ManagerId,
DaysWithSoldPcTable.DaysWithSoldPc,
BadSoldDaysTable.BadSoldDays
FROM DaysWithSoldPcTable
JOIN BadSoldDaysTable
ON DaysWithSoldPcTable.ManagerId = BadSoldDaysTable.ManagerId;