如何使用 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即可看到结果。

对于您指定的查询,它通常会执行如下操作:

  1. 它注意到子查询可以内联到查询中,并且这样做:
SELECT sub.a, SUM(sub.b), SUM(sub.c)
FROM temp
GROUP BY a;
  1. 然后评估将 table 聚合为 a 值的最佳方法。假设根本没有索引,这里最有可能选择 Hash Aggregate

  2. 在执行时,每一行都被送入哈希,它构建了一个内存中的哈希 table,以 a 值作为键。每一行都是根据 a 查找的,如果以前没有看到过,则将一个键添加到散列 table 中。然后将 bc 值添加到该键。

  3. 假设您在 a,b,c 上有一个索引。现在可以使用一种更快的方法,称为流聚合,因为现在值正在通过按 a.

    排序的聚合
  4. 每一行都通过聚合。如果 a 值与之前的行相同,则将 bc 值添加到我们目前拥有的任何值。当a值发生变化时,输出已有的结果,我们重新开始聚合。

的确,对额外的列求和是额外的开销,但与读取磁盘或散列的 table 相比,这是非常小的,后者在整个查询中只执行一次。