如果您对如何处理您的查询有疑问,请熟悉execution plans以及如何阅读它们。接下来的所有内容都是使用这些实验建立的。
聚合不会多次计算,但涉及它们的表达式是。考虑:
SELECT CompanyId, SUM(Weight)/COUNT(*)
FROM Customer
GROUP BY CompanyId
HAVING SUM(Weight)/COUNT(*) > 100
SUM(Weight)
和COUNT(*)
将只计算一次,但分割将被执行两次(一次滤波时,一旦其中选择)。当然,这对性能没有可衡量的影响 - 关键是它最大限度地减少了通过所有数据的次数。
这意味着,即使你的HAVING
是从SELECT
名单完全不同,该表将仍然只被扫描一次,并汇总一次:
SELECT CompanyId, MAX(Weight), MIN(Weight), COUNT(*) as Total
FROM Customer
GROUP BY CompanyId
HAVING MAX(Weight) > 2 * MIN(Weight) AND AVG(Weight) > 0.5
有四个聚集在这里:MAX(Weight)
,MIN(Weight)
,AVG(Weight)
和COUNT(*)
。 优化程序将在一次通过中计算所有这些值,将整个值分组CompanyId
,应用HAVING
过滤器,然后选择所需的结果。免责声明:与所有关于优化程序的声明一样,所有这些都可能在任何SQL Server版本中发生更改,并且可能随跟踪标志,统计信息,索引和特定查询的具体情况而变化。至少,对于两个特定的数据库,至少在SQL Server 2012和2016中,上述情况是正确的,至少在索引不起作用的情况下。
AVG
实际上不是在自己的总和;内部优化器将其扩展为SUM/COUNT(*)
,并通过检查来防止被零除。所以聚合实际上是MAX
,MIN
,SUM
和COUNT
。
- 顺序计划就是这种情况。对于一个并行计划,由于需要将多个并行扫描结合在一起,事情会变得更加复杂,但是聚合不会多次计算(如果可能)仍然如此。
同类问题https://dba.stackexchange.com/questions/119575/how-to-avoid-invoking-functions-twice-when-using-group-by-and-having/119578 – OutOfMind