确认only_full_group_by导致报错需执行select @@session.sql_mode,若结果含该模式且错误为error 1055,则属实;优先用any_value()显式表达非聚合字段语义,而非关闭模式。

报错就是 ONLY_FULL_GROUP_BY 在起作用,不是 SQL 写错了,是 MySQL 开始认真执行 SQL 标准了。别急着删模式,先确认是不是它、再决定怎么修。
怎么确认真是 ONLY_FULL_GROUP_BY 导致的?
执行这条语句:SELECT @@sql_mode; 或 SELECT @@SESSION.sql_mode;。如果返回结果里包含 ONLY_FULL_GROUP_BY(通常排最前面),且错误信息含 ERROR 1055 或 this is incompatible with sql_mode=only_full_group_by,就坐实了。
注意:@@GLOBAL.sql_mode 是全局配置,但应用连上来用的是会话级模式;云数据库(比如阿里云 RDS)往往禁止改 @@GLOBAL.sql_mode,直接 SET GLOBAL 会报 ERROR 1227。
优先用 ANY_VALUE() 显式表达语义
这是最干净的做法——不是绕过规则,而是告诉 MySQL:“这个字段我不聚合,但我接受任意一行的值”。比 MAX()/MIN() 更诚实,也比关模式更安全。
- 原报错 SQL:
SELECT user_id, product, SUM(amount) FROM orders GROUP BY user_id; - 改成:
SELECT user_id, ANY_VALUE(product), SUM(amount) FROM orders GROUP BY user_id; -
ANY_VALUE()从 MySQL 5.7.5 起可用,不影响索引和执行计划 - 别用
MAX(product)伪装——除非你真要最大值,否则后续维护的人会误读业务逻辑 - 如果字段有明确业务含义(比如“取最新订单的
status”),ANY_VALUE()不适用,得用窗口函数或子查询
临时关闭 ONLY_FULL_GROUP_BY 仅限调试
只在本地开发、CI 测试或紧急排查时用,生产环境长期开着等于放弃数据一致性保障。
- 正确写法:
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; - 别用
REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', '')——字符串里会多出逗号,MySQL 解析失败后悄悄回退到默认值(含ONLY_FULL_GROUP_BY) - 某些客户端(如 MySQL Workbench)会在连接初始化时重置
sql_mode,你手动SET SESSION可能被覆盖 - 该设置只对当前连接生效,应用重启、连接池重建后就失效
永久修改配置需完整替换 sql_mode
如果确实要改配置文件,不能只删 ONLY_FULL_GROUP_BY,必须把整个 sql_mode 值写全,否则 MySQL 启动时会用内置默认值兜底(又含 ONLY_FULL_GROUP_BY)。
- 编辑
/etc/my.cnf(Linux)或my.ini(Windows) - 在
[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:
service mysqld restart - 改完立刻执行
SELECT @@GLOBAL.sql_mode;验证是否生效
真正容易被忽略的点是:很多老 SQL 表面看只是“少写了 GROUP BY 字段”,实际隐藏着“每组取哪条记录”的业务歧义——ANY_VALUE() 能让意图可读,但解决不了“我要最新一条”这种需求,那得靠窗口函数或关联子查询,不是加个函数就能糊弄过去的。











