HAVING子句是否多余?

14
以下两个查询产生完全相同的结果:
select country, count(organization) as N
from ismember
group by country
having N > 50;

select * from (
  select country, count(organization) as N
  from ismember
  group by country) x
where N > 50;

每个HAVING子句是否都可以像这样替换为子查询和WHERE子句?还是有些情况下,HAVING子句是绝对必要/更强大/更高效/更优等的?


3
我猜你应该向问题中定义RDBMS。在SQL Server 2008中,你的第一个查询无效,因为你不能在Having中引用选择中的别名。只能在OrderBy部分中,因为逻辑查询处理的原因。 - András Ottó
我怀疑是MySQL的问题?同样的原因,第一个查询在Oracle中也无效。 - Ben
@Ben 是的,我刚刚添加了MySQL标签以澄清。谢谢。 - fredoverflow
请查看以下链接:HAVING A Blunderful Time or Wish You Were WHERE - Martin Smith
6个回答

11

这里有两个问题:第一个问题的答案是:带有HAVING子句的查询的结果集与带有WHERE子句的子查询的结果集相同。

第二个问题涉及性能和表达能力,这里我们要深入实现。在MySQL中,有一条微弱的分界线,当内部查询的结果集无法在内存中保存时,性能就会开始下降。在这种情况下,MySQL会创建一个内部查询的磁盘表示,然后在其上使用WHERE选择器。如果使用了HAVING子句,则不会发生这种情况,被淘汰的组将从结果集中删除。

这意味着,HAVING子句的选择性越高,它的性能影响就越大:考虑内部查询的结果集为一百万行,被HAVING子句减少到五行的情况 - 很可能,内部查询的结果集不能保存在内存中,但最终结果集很可能可以保存在内存中。

编辑

我曾经遇到过这样的情况:查询从一个非常均匀分布的表中选择出了一些异常值(一家工厂每天生产的物品数量)。我进行了调查,因为IO负载很高。

编辑2

请注意,查询缓存不会用于子查询 - 我认为这是开发应该更加关注的地方 - 因此,子查询模式不会从内部查询作为缓存的结果集中获益。


9
在Sql Server 2008中,两个类似的查询具有完全相同的执行计划。我还研究了许多由Entity Framework生成(使用SS 2008),到目前为止,我从未看到带有HAVING子句的查询。对聚合结果进行条件分组查询总是被转换为带有子查询的查询。我相信ADO.Net团队知道他们在做什么...

我完全不信任那个。EF(以及Linq-to-SQL)生成的查询非常糟糕。 - Rob Farley
1
@RobFarley 我知道它们无法与手工制作和优化的查询相竞争,但对于自动化查询来说,它们并不那么糟糕。不过,在编写 LINQ 时,你应该知道一些要做和不要做的事情。 - Gert Arnold
也许这两者是相似的,就像SQL Server将子查询版本转换为查询的聚合版本一样?ツ - Michael Buen

4

HAVING子句非常有用,可以避免子查询增加的复杂性。然而,这两种方法在逻辑上是等效的,每个HAVING子句都可以使用子查询进行重写。

如果您想知道,如果您准备将GROUP BY极端化,您也可以将每个WHERE子句编写为HAVING子句。


不确定你最后一行说的是否正确?假设有一个名为 number 的列和三行值为 VALUES (1),(1),(2) 的表格,如何使用 HAVING 模拟 SELECT number FROM T WHERE number = 1 - Martin Smith
那只会返回一行。 - Martin Smith
哦,抱歉 - 我在手机上阅读数字列表时看错了(跨越了两行)。您可以引入一个不同的器,如 row_number,并将其包含在组表达式中。如果您按照独特的方式分组,HAVING 和 WHERE 将成为等价的。 - Rob Farley

1

我知道您已经将它从一般更改为MySQL,但我想在这里添加一个(可能有用的)注释。通过一些修改,我尝试了您的查询语句在SQL Server 2008中的运行情况。

就想给那些需要更多详细信息的人提供参考,两个查询的执行计划在SQL Server 2008中甚至完全相同。因此,优化器以相同的性能和估算方式处理这两个命令。


0

在我看来,使用 HAVING 子句应该是高效的,因为在第二种情况下,除了运行过滤条件之外,还需要对包含分组结果的工作表进行额外的遍历。


子查询不会扩展为工作表。这两个查询(尽管在其他平台上已删除别名问题)应该被视为相同。 - Rob Farley
@RobFarley 这并不完全正确:如果结果集超过一定大小,它将被实体化。 - Eugen Rieck
1
好的。不是在SQL Server或Oracle中。这些系统会简化查询。 - Rob Farley
1
我特别是在谈论MySQL - 我应该更清楚地表达。对此抱歉。 - Eugen Rieck

0

从逻辑上讲,结果最终将是相同的。但性能可能会有所不同。HAVING子句可能会导致数据库更改不同的执行计划。

对于上面的人们(无法直接评论),需要注意的是执行计划不仅取决于您的查询。它还可能根据运行时的统计信息(如表大小等)由数据库进行调整。至少对于DB2来说是这样...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接