MySQL 5.7默认启用ONLY_FULL_GROUP_BY是行为升级而非bug,强制要求SELECT列必须在GROUP BY中出现或被聚合函数包裹,以符合SQL标准、保障结果确定性;SET SESSION后仍报错常见原因包括未验证生效、ORM重置会话、新连接继承全局模式、sql_mode字符串处理错误或云数据库需通过参数模板修改。

MySQL 5.7 默认启用 ONLY_FULL_GROUP_BY,不是 bug,是行为升级 —— 它强制要求:所有 SELECT 列要么出现在 GROUP BY 子句中,要么被聚合函数包裹(如 MAX()、COUNT()、ANY_VALUE()),否则直接报错。这是对 SQL 标准的靠拢,不是配置错了。
为什么 SET SESSION sql_mode 去掉 ONLY_FULL_GROUP_BY 后还是报错
常见错误现象:SET SESSION sql_mode = '...' 执行成功,但后续查询仍报 this is incompatible with sql_mode=only_full_group_by
- 执行语句前没确认当前会话是否真的生效:必须用
SELECT @@sql_mode;查看输出里是否还有ONLY_FULL_GROUP_BY;REPLACE操作漏写或拼错会导致原值未变 - 某些 ORM(如 Laravel Eloquent、Django ORM)或连接池(如 HikariCP)会在每次 query 前重置会话变量,
SET SESSION的效果被覆盖 - 你改的是会话级,但应用代码用了新连接(比如短连接模式),新连接仍继承全局
sql_mode,需同步改GLOBAL或配置文件 -
sql_mode是以逗号分隔的字符串,去掉ONLY_FULL_GROUP_BY时若多出空格或换行(比如复制粘贴带不可见字符),MySQL 会静默忽略整条赋值,回退到默认值
云数据库(RDS/CDB)改不了 my.cnf 怎么办
阿里云 RDS、腾讯云 CDB、华为云 DCS 等不开放 my.cnf 文件修改权限,也不能执行 SET GLOBAL(缺 SUPER 权限)
- 必须走控制台「参数模板」:找到
sql_mode参数项,手动删掉ONLY_FULL_GROUP_BY,保存后触发「重启实例」——注意:这是服务中断操作,务必选业务低峰期 - 部分云厂商(如 AWS RDS)允许通过 Parameter Group 修改,但修改后仍需 reboot 才生效,且模板绑定到实例后不能热更新
- 如果连参数模板也不让动(某些金融云/私有云),唯一合规路径是重写 SQL:把所有非聚合字段补进
GROUP BY,或改用ANY_VALUE(col)显式声明容忍歧义
为什么 ANY_VALUE() 比关掉 ONLY_FULL_GROUP_BY 更安全
关模式是“掩盖问题”,ANY_VALUE() 是“声明意图”:告诉 MySQL “我知道这列在组内可能有多个值,我接受任意一个”
-
SELECT id, product_id, SUM(amount) FROM `order` GROUP BY product_id报错,因为id不确定取哪一行;改成SELECT ANY_VALUE(id), product_id, SUM(amount) FROM `order` GROUP BY product_id就合法 -
ANY_VALUE()在语义上等价于旧版 MySQL 的“默认取第一行”,但它是显式的、可审计的,不会因优化器变更(如 derived_merge)意外失效 - 它不影响性能:不增加计算开销,只是绕过语义检查;而关
ONLY_FULL_GROUP_BY后,一旦 SQL 逻辑本身有歧义(比如ORDER BY字段没进GROUP BY),结果可能随 MySQL 版本或执行计划漂移 - 注意:5.7.5+ 支持,5.7.4 及更早版本不识别该函数,会报
FUNCTION any_value does not exist
ORDER BY 在子查询里失效的根本原因
像 SELECT * FROM (SELECT * FROM t ORDER BY c DESC) s GROUP BY x 在 5.7 中常返回乱序结果,不是 ONLY_FULL_GROUP_BY 导致的,而是优化器特性 derived_merge 自动合并子查询,把 ORDER BY 当作无效丢弃了
- 临时禁用:加
SELECT SQL_NO_CACHE ...或用UNION SELECT 1破坏合并条件 - 可靠写法:用
LIMIT强制物化子查询,例如(SELECT * FROM t ORDER BY c DESC LIMIT 999999999) - 本质解法:别依赖子查询排序 + GROUP BY 组内取最新,改用窗口函数(8.0+)或关联最大 ID(
WHERE id IN (SELECT MAX(id) ...))
真正难的不是怎么关掉 ONLY_FULL_GROUP_BY,而是判断哪条 SQL 确实不该关——比如 SELECT name, COUNT(*) FROM user GROUP BY dept_id,name 和 dept_id 并无函数依赖关系,关模式只会让结果随机,而用 ANY_VALUE(name) 至少让随机变得可预期。


















