如何使用 SQL Server SUM 函数避免指数时间成本?

问题描述

我意识到每次使用 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 来查看结果。

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

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

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

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

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

对额外的列求和确实是额外的开销,但与读取磁盘表或散列相比,这是非常小的,每个查询只执行一次。