查询之间的细微差别?

时间:2012-09-28 20:32:12

标签: mysql sql arel

在文章 Why Arel? 中,作者提出了问题:

  

假设我们有一个用户表和一张照片表,我们想要选择他们创建的照片的所有用户数据和*计数*。

他建议的解决方案(添加换行符)是

SELECT users.*, photos_aggregation.cnt
FROM users
LEFT OUTER JOIN (SELECT user_id, count(*) as cnt FROM photos GROUP BY user_id)
  AS photos_aggregation
ON photos_aggregation.user_id = users.id

当我尝试编写这样的查询时,我想出了

select users.*, if(count(photos.id) = 0, null, count(photos.id)) as cnt
from users
left join photos on photos.user_id = users.id
group by users.id

(列列表中的if()只是为了让用户在没有照片时表现相同。)

文章的作者接着说

  

只有高级SQL程序员才知道如何写这个(我经常在求职面试中问过这个问题,我从来没有见过任何人做对了)。它应该不难!

我不认为自己是“高级SQL程序员”,所以我认为我错过了一些微妙的东西。我错过了什么?

4 个答案:

答案 0 :(得分:2)

我相信你的版本会产生错误,至少在某些数据库引擎中是这样。在MSSQL中,您的选择将生成[Column Name] is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.。这是因为您选择只能包含组中的值或计数。

您可以将您的版本修改为select users.id, count(photo.id),但这样可行,但与查询结果不一样。

我不会说你必须特别先进才能提出一个有效的解决方案(或者他提出的具体解决方案)但是有必要在联接中作为一个单独的查询来执行该组或者作为@ron托纳姆建议。

答案 1 :(得分:1)

在大多数DBMS中(MySQL和Postgres都是例外),您问题中的版本将无效。

您需要编写不使用派生表的查询

select users.*, CASE WHEN count(photos.id) > 0 THEN count(photos.id) END as cnt
from users
left join photos on photos.user_id = users.id
group by users.id, users.name, users.email /* and so on*/

MySQL允许您选择不在group by列表中的非聚合项目,但只有在功能上依赖于group by中的列时,这才是安全的。

虽然group by列表在没有派生表的情况下更详细,但我希望大多数优化器能够将其中一个转换为另一个。当然,在SQL Server中,如果它看到你正在通过PK和其他一些列进行分组,那么它实际上并不会通过对这些其他列的比较进行分组。

有关此MySQL行为与标准SQL的讨论在Debunking GROUP BY myths

答案 2 :(得分:0)

也许这篇文章的作者是错的。您的解决方案也可以正常运行,而且速度可能会更快。

就个人而言,我会全部放弃if。如果您想计算图片数量,那么“无图片”会导致0而不是null

答案 3 :(得分:0)

作为替代方案,您还可以编写相关的子查询:

SELECT u.*, (SELECT Count(*) FROM photos p WHERE p.userid=u.id) as cnt
FROM users u