HAVING和SELECT中的计算是否意味着它将被执行两次?

时间:2017-08-25 09:50:27

标签: sql sql-server group-by

说我有这个问题:

private static ApiFuture<String> publishMessage(Publisher publisher, ByteString message)
            throws Exception {
    // convert message to bytes
    PubsubMessage pubsubMessage = 
    PubsubMessage.newBuilder().setData(message).build();
    return publisher.publish(pubsubMessage);
}

我的查询中有两次SELECT CompanyId, COUNT(*) as Total FROM Customer GROUP BY CompanyId HAVING COUNT(*) > 100 。这是否意味着COUNT(*)执行了两次?

这是一个简单的例子,但是当我有一个更复杂的计算(类似COUNT)时,我担心它可能会影响性能。或者任何性能影响是否可以忽略不计?

我使用的是MS SQL 2012,但不能SUM(Weight) / COUNT(*)

2 个答案:

答案 0 :(得分:3)

如果您对查询的处理方式有兴趣,请熟悉execution plans以及如何阅读它们。以下所有内容都是通过实验确定的。

聚合不会被计算多次,但涉及它们的表达式是。考虑:

SELECT CompanyId, SUM(Weight) / COUNT(*)
FROM Customer
GROUP BY CompanyId
HAVING SUM(Weight) / COUNT(*) > 100

SUM(Weight)COUNT(*)将只计算一次,但除法将执行两次(一次过滤,一次选择)。当然,这对性能没有可测量的影响 - 关键是它最大限度地减少了必须通过所有数据的次数。

这意味着,即使您的HAVINGSELECT列表完全不同,该表仍会只扫描一次并汇总一次:

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(*) 1 优化器将计算所有这些聚合传递,按CompanyId对整体进行分组,应用HAVING过滤器,然后选择所需的结果。 2

免责声明:与关于优化程序所做的所有声明一样,所有这些都可能在SQL Server的任何版本中发生变化,并且可能因跟踪标记,统计信息,索引和特定查询的细节而有所不同。对于SQL Server 2012和2016,至少对于两个特定的数据库,至少在索引不起作用的情况下,情况就是如此。

  1. AVG实际上并不是一个聚合物;在内部,优化器将其扩展为SUM / COUNT(*),并进行检查以防止除以零。因此,聚合实际上是MAXMINSUMCOUNT
  2. 顺序计划就是这种情况。对于并行计划,由于必须将多个并行扫描连接在一起,事情会变得更加复杂,但是聚合不会被计算多次(如果可能),这仍然是正确的。

答案 1 :(得分:1)

尽管决定权取决于查询优化程序,但COUNT(*) 将完成两次

而且,这几乎没有任何区别。

聚合数据所花费的精力在于移动数据,而不是在聚合函数中,尤其是像COUNT(*)这样的函数。一些聚合函数更昂贵(COUNT(DISTINCT)浮现在脑海中),但一般来说,数据移动比简单聚合函数更昂贵。

您没有提及数据库,但许多人允许:

HAVING Total > 100

或者使用子查询/ CTE通常对性能没有影响。