真正安全的解法是让sql语义自洽:分组字段补全或非聚合列加聚合函数;临时set session失效主因是连接池复用旧连接,需同一连接内select @@sql_mode验证;永久修改须在my.cnf[mysqld]段显式完整配置并重启mysql。

直接关掉 ONLY_FULL_GROUP_BY 不是修复,而是掩盖问题;真正安全的解法是让 SQL 语义自洽——要么分组字段补全,要么非聚合列加聚合函数。
为什么 SET SESSION sql_mode = REPLACE(...) 临时改配置后还报错
常见现象:执行了 SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));,再跑原 SQL 还是报错 ERROR 1055。
- 根本原因不是没生效,而是你改的是
SESSION级,而应用连接池(如 Spring Boot 的 HikariCP)可能复用旧连接——新连接才读新 session 设置 - 更隐蔽的情况:某些 ORM(如 MyBatis-Plus)会在 SQL 前自动加
SELECT SQL_CALC_FOUND_ROWS或其他 hint,触发 MySQL 内部重写逻辑,导致模式检查绕过失效 - 验证是否真生效:必须在**同一连接内**执行
SELECT @@sql_mode;,确认返回结果里已不含ONLY_FULL_GROUP_BY
永久修改 my.cnf 时最常漏掉的关键点
云数据库(阿里云 RDS、腾讯云 CDB)不让你碰 my.cnf,但自建 MySQL 改配置文件也极易翻车。
-
sql_mode必须显式列出所有项,不能只写sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_DATE,..."就完事——漏掉任何一项(比如NO_AUTO_CREATE_USER在 8.0.11+ 已废弃,但删了它会导致 MySQL 自动补回默认集,ONLY_FULL_GROUP_BY就又回来了 - Windows 下是
my.ini,Linux 下是/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,路径错一个字符,MySQL 启动时就静默忽略该配置 - 改完必须重启 MySQL 进程(
systemctl restart mysqld),仅 reload 配置无效——sql_mode是启动期加载的全局参数
用 ANY_VALUE() 比关模式更可控,但有硬限制
这是 MySQL 官方提供的“明确声明非确定性”的方式,比全局关模式更精准,但容易误用。
-
ANY_VALUE(name)只应在你**确认该字段在每组内值完全一致**时使用(例如user_id和user_name是 1:1 关系),否则结果不可预测 - 它不保证返回哪一行的值,MySQL 可能每次执行都取不同行——测试环境看不出问题,线上高并发下数据对不上才暴露
- 跨数据库迁移时失效:
PostgreSQL、SQL Server、Oracle全都不支持ANY_VALUE,直接报语法错误 - 替代方案更稳妥:用窗口函数重写,比如
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)+ 外层过滤rn = 1
云数据库用户唯一可行的路径:控制台调参模板
阿里云 RDS、华为云 GaussDB 等平台不开放 GLOBAL 权限和配置文件,SET GLOBAL 会报 Access denied。
- 必须进控制台 → 参数设置 → 找到
sql_mode参数 → 编辑 → 删除ONLY_FULL_GROUP_BY→ 保存并重启实例(注意:部分厂商要求“重启后生效”,不是热更新) - 切勿在控制台里留空
sql_mode字段——MySQL 会自动恢复成默认值,包括ONLY_FULL_GROUP_BY - 改完立刻连上新会话执行
SELECT @@sql_mode;,确认返回值中已剔除该模式;再跑原 SQL,如果还报错,说明应用代码里某处硬编码了SET SESSION覆盖了你的设置
真正难处理的从来不是怎么关掉 ONLY_FULL_GROUP_BY,而是关掉之后,那些靠“随机取一行”侥幸跑通的查询,在数据分布变化或索引调整后突然返回错误结果——这种问题不会报错,只会悄悄污染业务数据。











