优先修改sql而非禁用only_full_group_by:确认报错后,通过select @@sql_mode检查模式,改sql使语义明确(如删冗余字段、扩展group by或用any_value()),临时可set session修改,永久需在配置文件中显式列出完整sql_mode并重启。

直接改 sql_mode 禁用 ONLY_FULL_GROUP_BY 是最省事的解法,但结果不可控——同一查询两次执行,id 或 name 可能取自不同行,主从之间也可能不一致。优先改 SQL,其次才动模式。
确认是不是 ONLY_FULL_GROUP_BY 在报错
别猜,先查当前生效的模式:
- 执行
SELECT @@sql_mode;,看返回字符串里是否含ONLY_FULL_GROUP_BY(通常排第一位) - 错误信息末尾带
this is incompatible with sql_mode=only_full_group_by就是它 - 云数据库如阿里云 RDS 通常限制
@@GLOBAL.sql_mode写权限,SET GLOBAL会直接报ERROR 1227 -
@@sql_mode返回的是英文逗号+空格分隔的字符串,比如'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,',空格不能忽略
优先改 SQL:让语义明确,而非依赖引擎“猜”
旧写法 SELECT id, name, COUNT(*) FROM user GROUP BY status 的问题在于:id 和 name 没聚合、也没分组,MySQL 不知道该返回哪一行的值。
- 如果只想要统计结果,删掉冗余字段:
SELECT status, COUNT(*) FROM user GROUP BY status - 如果字段和分组键存在一对一关系(比如
status和status_name),把它加进GROUP BY:GROUP BY status, status_name - 如果业务上真接受“任取一个”,用
ANY_VALUE()显式表达意图:SELECT ANY_VALUE(id), ANY_VALUE(name), status, COUNT(*) FROM user GROUP BY status -
ANY_VALUE()是 MySQL 特有,跨库迁移时需重写;它不是兜底函数,但至少让结果可预期
临时绕过:只改当前会话,断开即还原
开发调试或 CI 流水线跑通时可用,不影响其他连接:
- 正确写法:
SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); - 别手拼字符串,比如
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,'—— 容易漏项 - 别设空值
'',MySQL 会自动补回默认集,ONLY_FULL_GROUP_BY还在 - JDBC 的
connectionInitSql可能被忽略,建议在应用首次query前显式执行一次
永久修改配置文件:必须写全 sql_mode 并重启
仅限自建 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 自动补回含ONLY_FULL_GROUP_BY的默认集 - Docker 环境需进容器编辑
/etc/mysql/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf - Windows 是
my.ini,Linux/macOS 通常是/etc/my.cnf或/etc/mysql/my.cnf - 即使关了
ONLY_FULL_GROUP_BY,MySQL 仍可能从每组中任意选一行返回非聚合字段——这不是 bug,是行为暴露
真正容易被忽略的点:禁用模式后,SELECT id, name FROM t GROUP BY status 的 id 和 name 值依然可能每次都不一样,甚至主从之间也不一致。这不是配置没生效,而是 MySQL 在“合法范围内”自由选择的结果。











