直接关闭ONLY_FULL_GROUP_BY虽可让旧SQL运行,但掩盖语义问题,导致结果随机、不可复现、跨库不兼容;正确解法是重写SQL,如用ANY_VALUE()显式声明意图,或改用标准窗口函数、子查询关联等符合SQL标准的方式。

直接关掉 ONLY_FULL_GROUP_BY 能让旧 SQL 跑起来,但不解决语义问题——它只是把错误藏起来,结果可能随机、不可复现、跨数据库跑不通。
为什么一执行 GROUP BY 就报 Expression #1 of SELECT list is not in GROUP BY clause
这不是你写错了语法,是 MySQL 5.7+ 默认启用了 ONLY_FULL_GROUP_BY 模式,强制校验 SQL 标准合规性:只要 SELECT 列表里有非聚合字段(比如 name、email),它就必须出现在 GROUP BY 子句中,或被包裹进聚合函数(如 MAX(name))。否则直接报错,连执行计划都不生成。
常见触发场景:
- 从 MySQL 5.6 升级到 8.0 后,原来能跑的
SELECT id, name, COUNT(*) FROM orders GROUP BY user_id突然挂掉 - ORM 自动生成的查询漏掉了关联字段,比如 Django 或 Laravel 的
values('user_id').annotate(...)但又手动加了.values_list('name') - 开发本地关了模式,上云(如阿里云 RDS)却报错——因为云数据库通常禁用
SET GLOBAL,也不让你改配置文件
临时绕过:只对当前会话生效的 SET 操作
适合调试、CI 测试、迁移过渡期。执行后立刻生效,但断开连接就还原,不影响其他会话。
- 查当前会话模式:
SELECT @@sql_mode; - 去掉
ONLY_FULL_GROUP_BY:SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY,', ''));(注意末尾逗号要一起删) - 验证是否生效:
SELECT @@sql_mode;看输出里还有没有ONLY_FULL_GROUP_BY
⚠️ 容易踩的坑:REPLACE 不会自动处理空格和多逗号;如果原值是 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES',只删 ONLY_FULL_GROUP_BY 会留下开头逗号,导致模式解析失败。稳妥做法是显式拼接完整字符串,例如:SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
永久关闭:修改配置文件并重启(需服务器权限)
适用于自建 MySQL 且能重启服务的环境。云数据库(RDS、PolarDB)基本不支持此方式,得走控制台参数模板。
- 找到配置文件:
my.cnf(Linux/macOS)或my.ini(Windows) - 在
[mysqld]段下添加(必须显式列出全部项):sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION" - ⚠️ 关键细节:不能写成
sql_mode = ""或只留"STRICT_TRANS_TABLES",MySQL 会自动补回默认集,ONLY_FULL_GROUP_BY又回来了 - 重启服务:
sudo systemctl restart mysql或 Windows 服务管理器中重启 - 验证:
SELECT @@GLOBAL.sql_mode;,确认输出不含ONLY_FULL_GROUP_BY
真正安全的解法:重写 SQL,而不是关模式
关模式只是掩盖问题。如果你 SELECT 出来的 name 实际上和 user_id 是 1:1 关系(比如用户表主键分组),那应该用 ANY_VALUE(name) 显式声明“我接受任意一行的值”,而不是依赖 MySQL 随机选第一行。
- 用
ANY_VALUE()(MySQL 特有):SELECT user_id, ANY_VALUE(name), COUNT(*) FROM orders GROUP BY user_id; - 用标准窗口函数替代(推荐):
SELECT DISTINCT user_id, FIRST_VALUE(name) OVER (PARTITION BY user_id), COUNT(*) OVER (PARTITION BY user_id) FROM orders; - 子查询关联(兼容所有数据库):
SELECT t1.user_id, t2.name, t1.cnt FROM (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) t1 JOIN users t2 ON t1.user_id = t2.id;
最常被忽略的一点:即使关掉了 ONLY_FULL_GROUP_BY,如果分组字段类型不一致(比如 user_id 是字符串,但 GROUP BY 里用了数字型 ID),或者含 NULL 值未显式处理,结果依然可能为空或错乱——模式开关不解决数据逻辑问题。











