Sal*_*n A 5 performance sql-server relational-division query-performance
鉴于此数据:
gid | val
1 | a
1 | a
1 | a
2 | b
3 | x
3 | y
3 | z
Run Code Online (Sandbox Code Playgroud)
以下查询返回包含一个不同值 (val) 的组 (gid):
SELECT gid FROM t GROUP BY gid HAVING MIN(val) = MAX(val)
SELECT gid FROM t GROUP BY gid HAVING COUNT(DISTINCT val) = 1
Run Code Online (Sandbox Code Playgroud)
人们似乎认为第一个变体会更快(如果假设存在适当的索引,那么查找 MIN 和 MAX 将比计算所有值更快)。这是事实还是神话。
简短的版本是您应该期望MIN(val) = MAX(val)
在所有情况下都COUNT(DISTINCT val) = 1
对行存储查询更好,并且在val
是字符串列时对列存储查询更好。
我将 640 万行放入一个表中进行测试。该数据与您的样本数据具有大致相似的数据分布:
DROP TABLE IF EXISTS dbo.t224998_2;
CREATE TABLE dbo.t224998_2 (
gid INT,
val VARCHAR(20)
);
INSERT INTO dbo.t224998_2 WITH (TABLOCK)
SELECT
CASE WHEN q.RN % 7 <= 2 THEN q.RN - q.RN % 7
WHEN q.RN % 7 >= 4 THEN q.RN - q.RN % 7 + 4
ELSE q.RN END
, CHAR(65 + q.RN % 7)
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
) q;
Run Code Online (Sandbox Code Playgroud)
这是表中的前 14 行:
以下是在没有任何索引的情况下运行查询对时的查询计划:
带有MIN
且MAX
只有一个散列聚合运算符的查询。该COUNT(DISTINCT)
查询有两个。对于第二个查询,最右边的运算符只保留不同的行,最左边的运算符执行计数。毫不奇怪,DISTINCT query
速度大约是其两倍。
创建以下索引使两个查询更具竞争力:
CREATE INDEX IX1 ON dbo.t224998_2 (gid, val);
Run Code Online (Sandbox Code Playgroud)
现在的计划是这样的:
现在不同的查询慢了大约 25%。这里应该强调的是,这些计划中没有一个是“查找”的MIN
和MAX
值。您正在查询没有过滤器的表。SQL Server 将扫描索引或表的所有行。索引很有用,因为它可以按键顺序进行扫描,并且可以更有效地计算聚合。对于MIN(val) = MAX(val)
查询,流聚合读取有序行并跟踪为 的每个唯一值看到的最小值和最大值gid
。当它找到 的新值时,它将行传递给下一个运算符gid
。在任何时候都不会执行索引查找来获取最小值或最大值。您可以编写一个查询来做到这一点,但这有点令人费解。
该COUNT(DISTINCT)
查询再次将工作拆分为两个聚合。两个聚合都利用了索引的排序。最右边的一个删除重复的行,最左边的一个执行计数。
如果我将表更改为没有任何非聚集索引的列存储,则第二个查询将成为赢家。以下是计划:
在MIN(val) = MAX(val)
完成所有行模式下的总的工作。批处理模式不支持返回字符串列的聚合。Microsoft记录了此限制。COUNT(DISTINCT val)
批处理模式支持,因此所有聚合工作都在批处理模式下执行。该查询的速度是MIN(val) = MAX(val)
选项的两倍多。
归档时间: |
|
查看次数: |
126 次 |
最近记录: |