mysql升级至5.7.5+或8.0后group by报错,根本原因是默认启用only_full_group_by模式,要求select中非聚合列必须出现在group by中,属sql标准行为;应优先修改sql(如删冗余字段、用any_value()或补全group by),而非简单禁用该模式。

MySQL 升级到 5.7.5+ 或 8.0 后,GROUP BY 查询直接报错:Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ...,根本原因是默认启用了 ONLY_FULL_GROUP_BY 模式 —— 不能靠“关掉它”来治标,得先确认你是否真需要关。
为什么升级后突然报错
MySQL 5.7.5 起将 ONLY_FULL_GROUP_BY 加入默认 sql_mode(此前长期默认关闭)。该模式强制要求:SELECT 列表中所有非聚合字段(如 name、created_at)必须显式出现在 GROUP BY 子句中,否则拒绝执行。这是 SQL 标准行为,不是 bug。
常见触发场景:
- 旧代码写法:
SELECT id, name, COUNT(*) FROM user GROUP BY status(id和name既没聚合也没分组) - ORM 自动生成的统计查询漏了字段对齐
- 开发环境 MySQL 版本低,测试没覆盖新版本约束
修复方案优先级:先改 SQL,再动 sql_mode
直接禁用 ONLY_FULL_GROUP_BY 是最省事但风险最高的选择 —— 它会让原本语义模糊的查询“侥幸通过”,结果可能随机(取决于引擎选哪行作为代表值),线上数据一致性隐患大。
更稳妥的做法是按需调整 SQL:
- 如果只需要分组统计,删掉多余字段:
SELECT status, COUNT(*) FROM user GROUP BY status - 如果确实要带其他字段,明确语义:用
ANY_VALUE()显式声明接受任意值(MySQL 5.7.5+ 支持):SELECT ANY_VALUE(id), ANY_VALUE(name), status, COUNT(*) FROM user GROUP BY status - 如果字段有业务唯一性(比如
status和status_name是一对一),把它加进GROUP BY:GROUP BY status, status_name
ANY_VALUE() 不是兜底函数,它会抑制优化器的某些推断,但至少让意图可读、结果可预期。
临时或永久禁用 ONLY_FULL_GROUP_BY 的操作方式
仅在确认业务逻辑无歧义、且短期无法改 SQL 时使用。注意:不同作用域影响范围不同。
- 会话级(当前连接生效,重启即恢复):
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_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';(手动剔除ONLY_FULL_GROUP_BY) - 配置文件级(永久生效,需重启 MySQL):编辑
my.cnf或my.ini,在[mysqld]下添加:sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
⚠️ 注意:MySQL 8.0.11+ 移除了 NO_AUTO_CREATE_USER,配置时需核对版本文档;修改配置后务必验证 SELECT @@sql_mode; 是否生效。
容易被忽略的关键点
很多团队只改了配置却仍报错,往往卡在这几个地方:
- 应用连接池(如 HikariCP、Druid)可能缓存了初始连接的
sql_mode,重启应用比重启 MySQL 更有效 - Docker 环境下,
my.cnf挂载路径错误或权限不足导致配置未加载,用docker exec -it mysql_container mysql -e "SELECT @@sql_mode;"直接验证 - 云数据库(如阿里云 RDS、腾讯云 CDB)不开放
GLOBAL变量修改权限,只能通过控制台参数模板调整,且部分版本模板锁定不可改 -
ONLY_FULL_GROUP_BY在视图或存储过程中同样生效,不能只盯单条 SQL
真正难的不是关开关,而是判断哪条 SQL 的“非确定性”在业务上是否可接受 —— 这个决策没法自动化。











