问题描述
我意识到每次使用 SUM 函数时,我的查询都会花费指数时间...
例如,下面的代码需要 2 秒
SELECT sub.a,SUM(sub.b)
FROM (
SELECT a,b,c
FROM temp
)sub
GROUP BY a;
使用第二个 SUM 现在需要 4 秒,依此类推...
SELECT sub.a,SUM(sub.b),SUM(sub.c)
FROM (
SELECT a,c
FROM temp
)sub
GROUP BY a;
似乎我所做的每个 SUM 都会再次执行子查询,这是否正确,避免时间成本的最佳做法是什么?
上面的例子只是以最基本的方式表示问题
解决方法
TL;DR:不,这是完全错误的。
当您在 SQL Server 中运行查询时,优化器会将其编译为它可以找到的最有效的方法。您可以通过点击 SSMS 中的 Include Actual Execution Plan
来查看结果。
对于您指定的查询,它通常会执行以下操作:
- 它指出子查询可以内联到查询中,并且这样做:
SELECT sub.a,SUM(sub.b),SUM(sub.c)
FROM temp
GROUP BY a;
-
然后它会评估通过
a
值聚合表的最佳方式。假设根本没有索引,这里最有可能选择Hash Aggregate
。 -
在执行时,每一行都被送入哈希,它建立一个内存哈希表,以
a
值作为键。每行都根据a
进行查找,如果之前没有看到过,则会将一个键添加到哈希表中。然后将b
和c
值添加到该键。 -
假设您在
a,b,c
上有一个索引。现在可以使用更快的方法,称为流聚合,因为现在值正在通过按a
排序的聚合。 -
每一行都通过聚合。如果
a
值与之前的行相同,则将b
和c
值添加到我们目前拥有的任何内容中。当a
值发生变化时,输出现有结果,我们再次开始聚合。
对额外的列求和确实是额外的开销,但与读取磁盘表或散列相比,这是非常小的,每个查询只执行一次。