Jos*_*ell 12 sql-server aggregate database-internals group-by sql-server-2017
看看这个查询。它非常简单(有关表和索引定义以及重现脚本,请参见文章末尾):
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1 AND 1 = (SELECT 1);
Run Code Online (Sandbox Code Playgroud)
注意:“AND 1 = (SELECT 1) 只是为了防止此查询被自动参数化,我觉得这使问题变得混乱 - 尽管有或没有该子句,它实际上获得了相同的计划
这是计划(粘贴计划链接):
由于那里有一个“top 1”,我很惊讶地看到流聚合运算符。对我来说似乎没有必要,因为保证只有一行。
为了测试这个理论,我尝试了这个逻辑上等效的查询:
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
GROUP BY Id;
Run Code Online (Sandbox Code Playgroud)
这是那个计划(粘贴计划链接):
果然,group by 计划能够在没有流聚合操作符的情况下通过。
请注意,两个查询都从索引的末尾“向后”读取并执行“前 1”以获得最大修订。
我在这里缺少什么? 流聚合是否在第一个查询中真正起作用,还是应该能够消除它(这只是优化器的一个限制,它不是)?
顺便说一下,我意识到这不是一个非常实际的问题(两个查询都报告 0 毫秒的 CPU 和经过时间),我只是对这里展示的内部/行为感到好奇。
这是我在运行上述两个查询之前运行的设置代码:
DROP TABLE IF EXISTS dbo.TheOneders;
GO
CREATE TABLE dbo.TheOneders
(
Id INT NOT NULL,
Revision SMALLINT NOT NULL,
Something NVARCHAR(23),
CONSTRAINT PK_TheOneders PRIMARY KEY NONCLUSTERED (Id, Revision)
);
GO
INSERT INTO dbo.TheOneders
(Id, Revision, Something)
SELECT DISTINCT TOP 1000
1, m.message_id, 'Do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
INSERT INTO dbo.TheOneders
(Id, Revision, Something)
SELECT DISTINCT TOP 100
2, m.message_id, 'Do that thing you do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
GO
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 16
如果没有与该WHERE子句匹配的行,您可以看到此聚合的作用。
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
AND 1 = 1 /*To avoid auto parameterisation*/
AND Id%3 = 4 /*always false*/
Run Code Online (Sandbox Code Playgroud)
在这种情况下,零行进入聚合,但它仍然发出一,因为NULL在这种情况下将返回正确的语义。
这是一个标量聚合而不是向量聚合。
您的“逻辑等效”查询不等效。添加GROUP BY Id将使其成为向量聚合,然后正确的行为是不返回任何行。
有关这方面的更多信息,请参阅使用标量和向量聚合的乐趣。
| 归档时间: |
|
| 查看次数: |
691 次 |
| 最近记录: |