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

Posted

技术标签:

【中文标题】使用聚合函数而不选择聚合列且不使用子查询【英文标题】:Using aggregate function without selecting the aggregate column and without subquery 【发布时间】:2018-07-31 18:48:09 【问题描述】:

我的数据如下所示:

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 类似,但不讨论子查询的必要性。

【问题讨论】:

我相信在sqlite中没有子查询是不可能的。 不过,postgresql 有一个解决方案,使用DISTINCT ON:***.com/questions/3800551/… 取决于您使用的 dbms。 一个 id 的两个不同水果的最高分相同的预期结果是什么? 问题中的示例查询本质上只是一个SELECT DISTINCT id, name FROM data 为什么需要子查询呢?或者,如果您对组中最大值的名称感兴趣,则该查询不可靠,您得到实际值只是一个幸运的巧合。 【参考方案1】:

可移植的解决方案意味着标准 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 支持这一点。

【讨论】:

能否请您详细说明为什么我的问题中的查询无效?

以上是关于使用聚合函数而不选择聚合列且不使用子查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server聚合函数

sql聚合函数的应用

SqlServer聚合函数

SQL Server“不能对包含聚合或子查询的表达式执行聚合函数”,但 Sybase 可以

递归 SQL:使用递归子查询分解的聚合函数

为啥聚合函数不能放在where后面?