2011-06-23 180 views
5

我是一个MySQL查询noobie,所以我确信这是一个明显的答案。MySQL Group By和HAVING

但是,我正在看这两个查询。他们会返回不同的结果集?我知道排序过程会以不同的方式开始,但我相信他们会返回相同的结果,而第一个查询的效率稍高一点。

查询1:HAVING,然后与

SELECT user_id 
FROM forum_posts 
GROUP BY user_id 
    HAVING COUNT(id) >= 100 
    AND user_id NOT IN (SELECT user_id FROM banned_users) 

查询2:式中,然后将具有

SELECT user_id 
FROM forum_posts 
WHERE user_id NOT IN(SELECT user_id FROM banned_users) 
GROUP BY user_id 
    HAVING COUNT(id) >= 100 

回答

1

实际上所述第一查询的效率会降低(施加WHEREHAVING)。
UPDATE

一些伪代码来说明如何你的查询执行([十分]的简化版本)。
第一个查询:
1. SELECT user_id FROM forum_posts
2. SELECT user_id FROM banned_user
第3组,计数等
4.排除记录从当它们存在在第二

第二查询
呈现设定第一结果 1. SELECT user_id FROM forum_posts
2. SELECT user_id FROM banned_user
3.如果它们在第二
呈现设定的第一结果排除记录4.组,计数等

步骤1,2的顺序并不重要,mysql可以选择任何它认为更好的。重要的区别在于步骤3,4。申请后GROUP BY。分组通常比加入费用更高(不包括记录在本例中可以考虑为加入操作),因此分组所需的记录越少,性能就越好。

+0

太好了,谢谢!有趣的是,我将不得不运行一些测试。我认为效率更高,因为在分组之后比较NOT IN banned_users部分的记录比较少,如果这是有道理的。 – kimmothy

+0

是的,该条件将仅在分组之前根据结果分组进行测试,而不是全部分组。 – aorcsik

+0

@ kimmothy:'NOT IN'中的子查询确实只需要执行一次。 – a1ex07

0

HAVING条件适用于按结果分组的,并且由于您按user_id进行分组,因此它们的所有可能值都将出现在分组结果中,因此放置user_id条件并不重要。

+0

配售很重要。如果应用了'WHERE',分组将在更少的行中完成(甚至为零),所以COUNT()必须仅针对那些行进行计算。如果它留给'HAVING'子句,则对所有行进行分组(和计数),然后检查条件。结果:如果禁止的用户在所有用户中所占的比例很大,则差异是速度会很快(按比例) –

+0

当然,只有在优化程序针对2个查询产生不同计划时才会出现速度差异。 –

+0

非常感谢您的点赞,我从这里的答案中学到了很多东西。 :) – aorcsik

-1

不,它没有给出相同的结果。

因为第一查询将从计数(ID)的条件下筛选记录

另一查询过滤器记录,然后施加具有子句。

第二个查询是正确写入

+2

由于您所说的结果会有所不同,因此在您知道哪些问题正在得到解决之前,您几乎不可能声称哪一个是正确编写的。至少,它们都是正确的*语法*。事实上,结果也是一样的。它们的效率会有所不同。 –

+0

@Andriy:你确定效率有差别吗? –

+0

@ypercube:我期望HAVING在WHERE之后进行评估,事实上,即使在GROUP BY之后(我认为这也是在WHERE之后计算的)。因此,第一个查询将不必要地计算以后根据'user_id'丢弃的行数。第二个在聚合之前在'user_id'上过滤掉。 –

0

对我来说,第二个查询更有效,因为它降低了对GROUP BY和HAVING记录数。

或者,您可以尝试下面的查询,以避免使用IN:

SELECT `fp`.`user_id` 
FROM `forum_posts` `fp` 
LEFT JOIN `banned_users` `bu` ON `fp`.`user_id` = `bu`.`user_id` 
WHERE `bu`.`user_id` IS NULL 
GROUP BY `fp`.`user_id` 
HAVING COUNT(`fp`.`id`) >= 100 

希望这有助于。

0

您已经回答了这两个查询会显示相同的结果以及其中一个更有效的各种意见。

我opininion是将有效率(速度)一个差,仅当优化产率与不同的计划为2次的查询。我认为,对于最新的MySQL版本,优化器足够聪明,可以为任一查询找到相同的计划,因此在所有上都没有差别,但是当然可以使用EXPLAIN测试并查看执行计划或运行2个查询针对一些测试表。

我会在任何情况下使用第二个版本,只是为了安全起见。


让我补充说:

  • COUNT(*)通常比在MySQL COUNT(notNullableField)更有效。在未来MySQL版本修复之前,请在适用的地方使用COUNT(*)

因此,您还可以使用:

SELECT user_id 
FROM forum_posts 
WHERE user_id NOT IN 
    (SELECT user_id FROM banned_users) 
GROUP BY user_id 
HAVING COUNT(*) >= 100 
  • 也有其他的方式申请GROUP BY之前达到同样的(以NOT IN)分结果。

使用LEFT JOIN/NULL

SELECT fp.user_id 
FROM forum_posts AS fp 
    LEFT JOIN banned_users AS bu 
    ON bu.user_id = fp.user_id 
WHERE bu.user_id IS NULL 
GROUP BY fp.user_id 
HAVING COUNT(*) >= 100 

使用NOT EXISTS

SELECT fp.user_id 
FROM forum_posts AS fp 
WHERE NOT EXISTS 
    (SELECT * 
    FROM banned_users AS bu 
    WHERE bu.user_id = fp.user_id 
) 
GROUP BY fp.user_id 
HAVING COUNT(*) >= 100 

其中的3种方法比较快取决于你的表的大小和很多其他因素的影响,所以最好是测试与你的数据。