应修改SQL而非关闭严格模式,如用ANY_VALUE()包裹非分组字段,或重构查询逻辑以符合SQL标准,避免因关闭ONLY_FULL_GROUP_BY引发的数据安全风险。

MySQL 5.7+ 报错 Expression #1 of SELECT list is not in GROUP BY 怎么办
这是 MySQL 5.7 及以上版本的默认严格模式(sql_mode 包含 ONLY_FULL_GROUP_BY)导致的,不是语法错误,而是 SQL 合规性校验。只要 SELECT 列中存在非聚合字段且未出现在 GROUP BY 中,就会直接报错。
临时关闭 ONLY_FULL_GROUP_BY(开发/调试用)
执行以下语句可立即生效(仅当前会话):
SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));
如果想对当前连接的所有后续查询生效(包括新建的 prepared statement),用 SESSION 级别即可。注意:这不改变全局配置,重启或新连接后失效。
常见误操作:
- 写成
SET GLOBAL sql_mode = '...'却没加ONLY_FULL_GROUP_BY的移除逻辑,结果覆盖掉其他必要模式(如STRICT_TRANS_TABLES),引发插入截断静默失败 - 在应用连接池场景下只改了单个连接的 SESSION 模式,其他连接仍报错,误以为“没生效”
永久修改 my.cnf(生产环境慎用)
编辑 MySQL 配置文件(如 /etc/my.cnf 或 /usr/my.cnf),在 [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"
关键点:
- 必须显式列出所有需要保留的 mode,不能只写
sql_mode = ''或漏掉STRICT_TRANS_TABLES,否则可能降低数据安全性 - 修改后需重启 MySQL(
systemctl restart mysqld),且所有新连接才生效 - Docker 容器中要确保挂载的配置文件路径正确,并检查容器内实际读取的是哪个 cnf(可用
mysql --help | grep "Default options"确认)
更推荐的解法:改 SQL 而不是改模式
根本原因是查询本身不符合 SQL 标准(比如 SELECT name, COUNT(*) FROM user GROUP BY dept_id 中 name 和 dept_id 没有函数依赖关系)。修复方式取决于业务意图:
- 若想取每个部门的任意一个用户名:
SELECT ANY_VALUE(name), dept_id, COUNT(*) FROM user GROUP BY dept_id - 若想取每个部门的最小 ID 对应的完整记录:得用窗口函数或关联子查询,不能靠 GROUP BY 直出
- 若 SELECT 所有列但只按某字段分组:大概率逻辑错误,应检查是否真需要
SELECT *,还是该用GROUP_CONCAT或拆成多条查询
ANY_VALUE() 是 MySQL 5.7+ 提供的兼容函数,不会报错,但需确认业务能接受“任意值”语义——它不保证稳定性(不同执行计划可能返回不同行)。
真正难处理的其实是 ORM 自动生成的 GROUP BY 查询(比如 Django 的 .values('x').annotate(...)),这时候得看 ORM 版本是否支持自动注入 ANY_VALUE(),或者手动干预 query set 的 raw SQL。











