直接改sql是最稳妥解法,禁用only_full_group_by仅临时绕过且结果不可控;报错“expression #n of select list is not in group by clause”表明第n个非聚合列既未分组也无聚合函数包裹,且无函数依赖关系;推荐优先使用any_value()显式声明接受不确定性,或补全group by字段。

直接改 SQL 是最稳妥的解法,禁用 ONLY_FULL_GROUP_BY 只能临时绕过,且会让结果不可控。
报错信息怎么看:Expression #N of SELECT list is not in GROUP BY clause
这是 MySQL 8.0 在告诉你:SELECT 列表里第 N 个字段(从 1 开始数)既没出现在 GROUP BY 子句中,也没被聚合函数包裹,而且它和分组列之间没有函数依赖关系。
- 比如
SELECT id, name, COUNT(*) FROM user GROUP BY dept_id,id是 Expression #1,name是 #2,都违规 - 错误末尾带
this is incompatible with sql_mode=only_full_group_by就能 100% 确认是这个模式触发的 - 注意“not functionally dependent”——哪怕你加了主键进
GROUP BY,如果表没定义主键或唯一约束,MySQL 8.0 仍可能拒绝
优先用 ANY_VALUE() 保留语义但通过校验
ANY_VALUE() 不是随机取值,而是向 MySQL 显式声明:“我接受这列在组内不唯一,结果不确定性由我承担”。它比关模式更可控,也比硬塞进 GROUP BY 更轻量。
- 把报错语句
SELECT id, name, COUNT(*) FROM user GROUP BY dept_id改成:SELECT ANY_VALUE(id), ANY_VALUE(name), dept_id, COUNT(*) FROM user GROUP BY dept_id -
ANY_VALUE()要求 MySQL ≥ 5.7.5;低版本只能换方案 - 如果业务真依赖某个确定值(比如最新创建的
id),ANY_VALUE()就不合适,得用窗口函数或子查询重写 - 它不会自动利用索引优化,可能影响性能,尤其在大表分组时
临时禁用 ONLY_FULL_GROUP_BY 的三种方式及风险
仅限开发调试或紧急上线过渡,生产环境慎用。关掉后,MySQL 仍可能从每组中任意选一行返回,下次执行结果可能不同——这不是 bug,是行为暴露。
- 当前会话生效(重启连接即失效):
SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); - 全局生效(需 SUPER 权限,影响所有新会话):
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
⚠️ 必须显式列出全部项,漏一个 MySQL 会自动补回默认集 - 永久修改(需重启 MySQL):在
my.cnf的[mysqld]段下添加:sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
云数据库(如阿里云 RDS)通常不开放配置文件或 GLOBAL 权限,只能通过控制台参数模板调整
ORM 自动生成的分组查询怎么修
Django、MyBatis、Laravel Eloquent 等框架生成的统计查询常漏字段对齐,尤其在多字段聚合 + 关联查询场景下容易触发。
- 先查慢日志或错误日志,定位具体哪条 SQL 报错
- 检查 ORM 的聚合调用链:比如 Django 的
.values('dept_id').annotate(count=Count('id'))是安全的;但若手动拼了.values_list('id', 'name', 'dept_id')再annotate,就大概率出问题 - MyBatis 中
<select></select>标签里手写的GROUP BY语句,要确保SELECT和GROUP BY字段严格对齐,或套MAX()/ANY_VALUE() - 别指望 ORM 自动帮你补
ANY_VALUE()—— 目前主流框架都不做这层适配
最容易被忽略的点:即使禁用了 ONLY_FULL_GROUP_BY,结果仍然可能不对。MySQL 不保证“同一组里选哪一行”,这个不确定性不会因为关模式就消失,只是从报错变成静默返回任意值。











