2

在文章为什么是 Arel?,作者提出问题:

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

他提出的解决方案(添加了换行符)是

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

4 回答 4

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.. 这是因为您选择的只能包含 group by 或 count 中的值。

您可以将您的版本修改为select users.id, count(photo.id)并且它会起作用,但它与他的查询的结果不同。

我不会说您必须特别先进才能提出一个可行的解决方案(或他提出的具体解决方案),但有必要在连接中或@ron tornambe 建议的单独查询中执行该组。

于 2012-09-28T20:59:11.763 回答
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 神话中

于 2012-09-28T21:00:44.677 回答
0

也许这篇文章的作者是错误的。您的解决方案也很有效,而且可能会更快。

就个人而言,我会完全放弃if。如果你想计算图片的数量,'没有图片'的结果是有意义的,0而不是null

于 2012-09-28T20:37:24.973 回答
0

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

SELECT u.*, (SELECT Count(*) FROM photos p WHERE p.userid=u.id) as cnt
FROM users u
于 2012-09-28T20:55:24.760 回答