所选项目不必出现在 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。
【讨论】:
谢谢,很有帮助。我不明白的一件事是为什么显式JOIN
比 cartisian 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 子句中或在聚合函数中使用