使用聚合函数而不选择聚合列且不使用子查询

时间:2018-02-21 08:56:26

标签: sql group-by aggregate-functions greatest-n-per-group

我的数据如下:

id  name       score 
--------------------
a   apple        0.2
a   apple        0.7
a   apple        1.1
a   banana       1.2
b   cherry       0.8
b   lemon        0.9
c   mango        2.4
c   raspberry    1.9
d   strawberry   0.7
d   lemon        1.1

对于每个id,我想选择得分最高的行,但只选择id和name:

id  name
----------
a   banana
b   lemon
c   mango
d   lemon

以下查询使用sqlite完成工作。 (在this answer中对此进行了解释,为什么此查询在大多数DBMS中实际上无效):

SELECT id, name from (SELECT id, name, max(score) from data group by id);

问题是:如果没有子查询,这可能吗?

注意:我目前正在使用sqlite,但我正在寻找便携式解决方案。如果只有特定于供应商的解决方案,那么它也是一个有效的答案。 This question类似,但不讨论子查询的必要性。

1 个答案:

答案 0 :(得分:2)

便携式解决方案意味着标准SQL。在标准SQL中,这通常使用窗口函数来解决。

select id, name
from (
   select id, name, dense_rank() over (partition by id order by score desc) as rnk
   from the_table
) t 
where rnk = 1;

以上是标准SQL,基本上适用于所有现代DBMS(甚至是MariaDB和即将推出的MySQL 8.0)。但是,我不认为SQLite支持窗口功能。

您原始的子查询:

SELECT id, name, max(score) 
from data 
group by id

是无效的标准SQL,因为name列既不是GROUP BY的一部分,也不是在聚合函数中使用。基本上每个其他DBMS都会拒绝该查询 - 包括默认启用ONLY_FULL_GROUP_BY的MySQL的新版本。显然,SQLite允许这种无效分组,从而产生非确定性(=随机)结果。

该规则的唯一例外是,所有非分组列的分组对分组列具有已知的功能依赖性。这意味着如果分组列是主键,并且所有非分组列都属于该主键的表。据我所知,只有Postgres目前支持这一点。