所选项目不必出现在 GROUP BY 子句中或在聚合函数中使用

Posted

技术标签:

【中文标题】所选项目不必出现在 GROUP BY 子句中或在聚合函数中使用【英文标题】:selected items don't have to appear in the GROUP BY clause or be used in an aggregate function 【发布时间】:2020-02-01 23:40:38 【问题描述】:

我被教导并听说在 sql/mysql 中,select 子句中的项目必须出现在 GROUP BY 子句中或用于聚合函数,如 here

但是,下面的例子可能改变了我的想法。

两个表: 学生(sid是关键)

sid  | name | email
========================
99901| mike | mike@a.edu
99902| jane | jane@b.edu
99903| peter| pete@b.edu

Took(sid+oid一起是关键,oid代表offering id)

sid  | oid| grade
==================
99901| 1  | 100
99901| 2  | 30
99901| 3  | 40
99902| 4  | 100
99902| 5  | 100
99902| 6  | 40
99903| 6  | 95

问题:我想找出每个至少修过 2 门课程的学生的 sid、姓名和平均成绩。

回答

select s.sid, name, avg(grade) as average
from Student as s, Took as t
where s.sid = t.sid
group by s.sid
having count(*) >= 2;

结果:

sid  | name | avgerage
=======================
99901| mike | 56.6667
99902| jane | 80.0000

基于必须出现在 GROUP BY 子句中或用于聚合函数中,查询应该是不正确的,因为name 既不在组子句中也不是聚合函数。

我看了一些帖子和this,我的理解是虽然name既不是分组子句也不是聚合函数,但我们按sid分组,这是关键,每个sid只对应一个@ 987654338@,所以它不会返回多个选项,其中 sql 不知道返回哪个选项。为了确认我的理解,如果我多选一列email,还是可以的;但如果我选择oid,则会出现错误,因为每个sid 对应多个oid

如果有错误,有人可以纠正我的理解或详细说明此声明:must appear in the GROUP BY clause or be used in an aggregate function

谢谢。

第一次编辑

顺便说一句,我在 MySQL 8.0.17 中测试过

二次编辑

当您阅读下面的答案/cmets 时,只是对有用链接的摘要。

Functional depedency

SQL standard change

【问题讨论】:

mysql 会在这方面为您做出推断,而所有其他主要 dbms 都会严格执行该规则。我建议即使在 mysql 中也可以简单地遵循规则,以免造成任何混乱,但您似乎对此非常了解。 @AaronDietz 不了解其他供应商,但在 MySQL 中,当 GROUP BY 子句中包含非索引列时,可能会出现性能问题。 见:Detection of Functional Dependence ONLY_FULL_GROUP_BY 控制这个,顺便说一句。 (在 mysql 8 中默认开启...)More reading 这篇博文(旧的但直到今天仍然相关)可以消除围绕FULL_GROUP_BY 和 SQL 标准的所有误解和神话:rpbouman.blogspot.com/2007/05/debunking-group-by-myths.html 【参考方案1】:

首先,您应该使用正确、明确的JOIN 语法:

select s.sid, s.name, avg(grade) as average
from Student s join
     Took t
     on s.sid = t.sid
group by s.sid
having count(*) >= 2;

这将因为称为功能依赖的东西而起作用。基本上,这是标准的一部分:如果您 group by 主键或唯一键,那么您可以包含该表中的任何列。

Here 是有关该主题的文档。

也就是说,因为数据库知道 s.sid 是唯一的,所以使用其他列是安全的。这是标准的一部分。我知道的唯一支持此功能的其他通用数据库是 Postgres。

【讨论】:

谢谢,很有帮助。我不明白的一件事是为什么显式 JOINcartisian product 更好,在这个例子中,它们执行相同的功能,可能是因为速度问题? 可读性,因为很明显这是内部连接,在 MySQL 优化中,那些显式 JOIN 将再次变为逗号连接 @Kenny 因为逗号连接仅支持 CROSS JOIN/INNER JOIN 结果,其中 MySQL 中的显式 JOIN支持 CROSS/LEFT/RIGHT/INNER,因此如果您需要其他结果,可以更简单地更改查询.. @RaymondNijland 谢谢。我也刚刚找到它here【参考方案2】:

你被教对了。

根据 SQL 标准,当您使用 GROUP BY 时,SELECT 子句中可以出现的列分为三类:

    GROUP BY 子句中包含的列。在这种情况下,您有 s.sid。 聚合列。在这种情况下,您有 avg(grade)。 案例 #1 的功能相关列。由于s.sid 是表的PK,您可以包含s.name 而不聚合它。

一切都好。

但是,您应该知道 MySQL 5.7.4 和更早版本确实允许您以非聚合形式包含其他列。这是我个人认为容易出错的 MySQL 的错误/功能。如果你这样做,MySQL 将静默选择一个值随机而不聚合它,也不告诉你。

可以通过在较新版本的 MySQL 中使用 ONLY_FULL_GROUP_BY 配置参数(正如 @Shawn 在 cmets 中指出的那样)打开此功能,以允许运行旧/错误查询。不过,我会尽量避免使用它。

【讨论】:

运行 sql_mode ONLY_FULL_GROUP_BY 的 MySQL 5.7.5+ 确实正确实现了功能依赖规则检测的 ANSI/ISO SQL 1999 标准,据我所知...

以上是关于所选项目不必出现在 GROUP BY 子句中或在聚合函数中使用的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql 错误:列必须出现在 GROUP BY 子句中或在聚合函数中使用

GroupingError:错误:列必须出现在 GROUP BY 子句中或在聚合函数中使用

仅休眠错误:“列必须出现在 GROUP BY 子句中或在聚合函数中使用”

列“ic.inscount”必须出现在 GROUP BY 子句中或在聚合函数中使用

Django 与 Postgresql,列必须出现在 GROUP BY 子句中或在聚合函数中使用

Postgresql“列必须出现在 GROUP BY 子句中或在聚合函数中使用”和唯一字段