如何使用 SQL 服务器 SUM 函数避免指数时间成本?
How to avoid exponencial time cost with SQL Server SUM function?
我意识到每次使用 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, b, c
FROM temp
)sub
GROUP BY a;
我做的每个 SUM 似乎都会再次执行子查询,这是否正确,避免时间成本的最佳做法是什么?
上面的例子只是以最基本的方式表示问题
TL;DR: 不,这是完全错误的。
当您在 SQL 服务器中 运行 查询时,优化器会将其编译成它能找到的最有效的方法。在SSMS中点击Include Actual Execution Plan
即可看到结果。
对于您指定的查询,它通常会执行如下操作:
- 它注意到子查询可以内联到查询中,并且这样做:
SELECT sub.a, SUM(sub.b), SUM(sub.c)
FROM temp
GROUP BY a;
然后评估将 table 聚合为 a
值的最佳方法。假设根本没有索引,这里最有可能选择 Hash Aggregate
。
在执行时,每一行都被送入哈希,它构建了一个内存中的哈希 table,以 a
值作为键。每一行都是根据 a
查找的,如果以前没有看到过,则将一个键添加到散列 table 中。然后将 b
和 c
值添加到该键。
假设您在 a,b,c
上有一个索引。现在可以使用一种更快的方法,称为流聚合,因为现在值正在通过按 a
.
排序的聚合
每一行都通过聚合。如果 a
值与之前的行相同,则将 b
和 c
值添加到我们目前拥有的任何值。当a
值发生变化时,输出已有的结果,我们重新开始聚合。
的确,对额外的列求和是额外的开销,但与读取磁盘或散列的 table 相比,这是非常小的,后者在整个查询中只执行一次。
我意识到每次使用 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, b, c
FROM temp
)sub
GROUP BY a;
我做的每个 SUM 似乎都会再次执行子查询,这是否正确,避免时间成本的最佳做法是什么?
上面的例子只是以最基本的方式表示问题
TL;DR: 不,这是完全错误的。
当您在 SQL 服务器中 运行 查询时,优化器会将其编译成它能找到的最有效的方法。在SSMS中点击Include Actual Execution Plan
即可看到结果。
对于您指定的查询,它通常会执行如下操作:
- 它注意到子查询可以内联到查询中,并且这样做:
SELECT sub.a, SUM(sub.b), SUM(sub.c)
FROM temp
GROUP BY a;
然后评估将 table 聚合为
a
值的最佳方法。假设根本没有索引,这里最有可能选择Hash Aggregate
。在执行时,每一行都被送入哈希,它构建了一个内存中的哈希 table,以
a
值作为键。每一行都是根据a
查找的,如果以前没有看到过,则将一个键添加到散列 table 中。然后将b
和c
值添加到该键。假设您在
排序的聚合a,b,c
上有一个索引。现在可以使用一种更快的方法,称为流聚合,因为现在值正在通过按a
.每一行都通过聚合。如果
a
值与之前的行相同,则将b
和c
值添加到我们目前拥有的任何值。当a
值发生变化时,输出已有的结果,我们重新开始聚合。
的确,对额外的列求和是额外的开销,但与读取磁盘或散列的 table 相比,这是非常小的,后者在整个查询中只执行一次。