MySQL 8.0默认启用ONLY_FULL_GROUP_BY模式,强制要求SELECT中所有非聚合字段必须显式出现在GROUP BY子句中,否则报错;该模式遵循SQL标准,旨在避免因语义模糊导致结果不可控。

直接改 SQL 是最稳妥的解法,禁用 ONLY_FULL_GROUP_BY 只能临时绕过,且会让结果不可控。
为什么 GROUP BY 突然报错:Expression #1 of SELECT list is not in GROUP BY clause
MySQL 8.0 默认启用 ONLY_FULL_GROUP_BY 模式,它强制要求:SELECT 列表里的每个非聚合字段(比如 id、name)必须显式出现在 GROUP BY 子句中,否则直接拒绝执行。
常见触发写法:
-
SELECT id, name, COUNT(*) FROM user GROUP BY dept_id——id和name既没聚合也没分组 - ORM 自动生成的统计查询漏了字段对齐
- 开发环境用的是 MySQL 5.6/5.7,上线到 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 - 注意:如果业务上真依赖某个特定值(比如最新创建的
id),ANY_VALUE()就不合适,得用子查询或窗口函数重写 -
ANY_VALUE()在 MySQL 5.7.5+ 才可用;低版本只能选其他方案
临时禁用 ONLY_FULL_GROUP_BY 的三种方式及风险
仅限开发调试或紧急迁移过渡,生产环境慎用。
- 当前会话生效(重启连接即失效):
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):在
my.cnf的[mysqld]段下添加:sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"—— 必须显式列出全部项,不能留空或只写"",否则 MySQL 会自动补回默认集
云数据库(如阿里云 RDS)通常不开放配置文件或 GLOBAL 权限,只能通过控制台参数模板调整 sql_mode。
最容易被忽略的点:关掉 ONLY_FULL_GROUP_BY 后结果仍不对
即使禁用了 ONLY_FULL_GROUP_BY,MySQL 仍可能从每组中任意选一行作为代表值——这个“任意”取决于存储引擎、数据分布、优化器路径,甚至同一条 SQL 多次执行都可能返回不同行。
这意味着:
- 前端分页展示时出现重复或漏数据
- 导出报表内容每次都不一致
- 下游系统依赖某列固定值做判断,逻辑悄然出错
真正安全的做法,是让 SQL 自身表达清楚意图:要么删掉冗余字段,要么用 ANY_VALUE() 显式标注,要么补全 GROUP BY 字段(前提是它们和分组键存在函数依赖关系)。


















