mysql 8.0报错“expression #2 of select list is not in group by clause”表示select中第2个非聚合字段(如product)既未出现在group by子句,也未使用max()、any_value()等函数处理,因only_full_group_by模式强制遵循sql标准而拒绝执行。

因为 MySQL 8.0 默认启用 ONLY_FULL_GROUP_BY 模式,而你原来的 SQL 语句里有非聚合字段没出现在 GROUP BY 子句中——这不是 bug,是标准变严格了。
报错信息里“Expression #2 of SELECT list is not in GROUP BY clause”是什么意思
它直白地告诉你:SELECT 列表里的第 2 个字段(比如 product 或 nickname),既没写进 GROUP BY,也没套 MAX()、MIN()、ANY_VALUE() 这类函数。MySQL 8.0 拒绝执行这种语义模糊的查询。
常见触发场景:
- 旧代码直接
SELECT id, name, COUNT(*) FROM user GROUP BY status——id和name都没分组也没聚合 - ORM 自动生成的统计语句漏掉了字段对齐逻辑
- 开发环境用的是 MySQL 5.6/5.7 且
sql_mode里关了ONLY_FULL_GROUP_BY,测试没覆盖 8.0
为什么不能直接在 my.cnf 里删掉 ONLY_FULL_GROUP_BY
删了它,SQL 是能跑通,但结果可能随机——比如同一 user_id 分组里有多条 product,MySQL 会“随便挑一行”的值返回,下次执行可能就变了。这不是你想要的确定性,而是掩盖了业务逻辑缺陷。
更实际的风险:
- 线上报表数据某天突然跳变,排查困难
- 不同从库因选行策略差异导致主从不一致
- 后续升级到更高版本或迁移到其他数据库(如 PostgreSQL)时再次暴雷
用 ANY_VALUE() 代替裸字段是否安全
ANY_VALUE() 是 MySQL 5.7.5+ 提供的合法兜底函数,比关模式强,但它不是万能解药。
使用前必须确认:
- 该字段在业务上确实“任取一个都可接受”(比如用户头像 URL 在同一分组内本就相同)
- 你不需要它和聚合结果(如
SUM(amount))来自同一行记录(ANY_VALUE()不保证这点) - 执行计划里它会抑制某些优化器推断,可能影响性能,尤其在大表上
ORDER BY 中引用非 GROUP BY 字段也会报错
很多人只注意 SELECT 列表,忘了 ORDER BY 也受 ONLY_FULL_GROUP_BY 约束。例如:
SELECT user_id, SUM(amount) FROM orders GROUP BY user_id ORDER BY created_at DESC;
如果 created_at 不在 GROUP BY 里,也不被聚合,这条语句在 8.0 同样报错。解决方式要么加进 GROUP BY,要么改用 MAX(created_at)。
最易被忽略的一点:哪怕你只改了应用层 SQL,如果 ORM 底层拼的语句仍带裸字段 + GROUP BY,问题照旧。得一层层查生成逻辑,不能只盯着报错那条 SQL 看。











