MySQL分组并具有
我是MySQL查询高手,所以我相信这是一个答案显而易见的问题 但是,我在看这两个问题。它们会返回不同的结果集吗?我知道排序过程将以不同的方式开始,但我相信它们将返回相同的结果,而第一次查询的效率会稍高一些 问题1:拥有、然后和MySQL分组并具有,mysql,group-by,having,Mysql,Group By,Having,我是MySQL查询高手,所以我相信这是一个答案显而易见的问题 但是,我在看这两个问题。它们会返回不同的结果集吗?我知道排序过程将以不同的方式开始,但我相信它们将返回相同的结果,而第一次查询的效率会稍高一些 问题1:拥有、然后和 SELECT user_id FROM forum_posts GROUP BY user_id HAVING COUNT(id) >= 100 AND user_id NOT IN (SELECT user_id FROM ban
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
实际上,第一个查询的效率较低(
在WHERE
之后应用了)。
更新
一些伪代码说明如何执行查询([非常]简化的版本)。
第一个查询:
1. <代码>从论坛帖子中选择用户id
2. <代码>从禁用用户中选择用户id
3.分组、计数等
4.从第一个结果集中排除记录(如果记录在第二个结果集中) 第二次查询
1. <代码>从论坛帖子中选择用户id
2. <代码>从禁用用户中选择用户id
3.如果记录在第二个结果集中,则从第一个结果集中排除这些记录
4.分组、计数等
步骤1、2的顺序并不重要,mysql可以选择它认为更好的。重要的区别在于步骤3、4。在
分组后应用have。分组通常比加入更昂贵(在这种情况下,排除记录可被视为加入操作),因此它必须分组的记录越少,性能越好。有条件应用于按结果分组,并且由于您按用户id分组,所有可能的值都将出现在分组结果中,因此,用户id条件的放置并不重要。不,它不会给出相同的结果
因为第一个查询将从计数(id)条件中筛选记录
另一个查询过滤记录,然后应用having子句
第二次查询写得正确对我来说,第二次查询效率更高,因为它减少了GROUP BY和HAVING的记录数
或者,您可以尝试以下查询以避免在中使用:
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
希望这会有所帮助。您已经回答了这两个查询将显示相同的结果和不同的意见,其中一个更有效
我的观点是,只有当优化器为这两个查询提供不同的计划时,效率(速度)才会有差异。我认为,对于最新的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
- 在应用
分组方式之前,还有其他方法可以实现相同的子结果(不在中)
使用左连接/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
使用不存在
:
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种方法中哪一种更快取决于您的表大小和许多其他因素,因此最好使用您的数据进行测试。太好了,谢谢!有趣的是,我得做一些测试。我认为这样会更有效率,因为在分组后,比较非被禁止用户部分的记录比以前少了?如果这有意义的话。是的,该条件将仅针对分组结果进行测试,而不是全部,在分组之前。@kimmothy:NOT in
中的子查询实际上只需要执行一次。我添加了一个更新来说明两个查询之间的差异。@a1ex07:您确定实际执行计划中有差异吗?因为您说结果会不同,在你知道解决了什么问题之前,你很难断言哪一个写得正确。至少,它们在语法上都是正确的。事实上,结果也是一样的。这是他们在效率上的不同。@Andrey:你确定效率上有差异吗?@ypercube:我希望在WHERE之后,甚至在GROUP BY之后(我认为,这也是在WHERE之后计算的)进行评估。因此,第一个查询将不必要地计算稍后将根据user\u id
丢弃的行的计数。第二个是在聚合之前过滤掉user\u id
。放置很重要。如果应用了中的,则分组的行数更少(甚至为零),因此必须仅为这些行计算COUNT()。如果留给HAVING
子句,则对所有行进行分组(和计数),然后检查条件。结果:如果被禁止的用户占所有用户的很大比例,那么速度的差异将是巨大的(成比例的)。当然,只有当优化器对这两个查询有不同的计划时,速度的差异才会存在。感谢您指出这一点,我从这里的答案中学到了很多。:)