ONLY_FULL_GROUP_BY 是什么
它要求 SELECT 里的非聚合列必须出现在 GROUP BY 中,或者函数依赖于分组键。MySQL 从 5.7 起默认启用,PostgreSQL 一直按标准执行。当分组键包含主键时,同表的其它列可以由它唯一确定,因此允许直接出现在 SELECT 里。
基本信息
| MySQL | 默认启用 ONLY_FULL_GROUP_BY,可用 SELECT @@sql_mode 查看 |
|---|---|
| PostgreSQL | 按 SQL 标准执行,同样拒绝非聚合列 |
| 函数依赖 | 分组键含主键或唯一键时,同表其它列被允许 |
| 报错信息 | MySQL 1055;PG 提示 must appear in the GROUP BY clause |
| 关闭代价 | 取到的非聚合列值不确定,不同执行计划可能给出不同结果 |
| 正确改法 | 改写成聚合、先去重再统计,或调整分组粒度 |
报错与三种修正
报错与三种修正
-- 错误:name 既不在 GROUP BY 中也不是聚合函数
SELECT city, name, COUNT(*)
FROM users
GROUP BY city; -- MySQL 报 ERROR 1055,PG 报 must appear in GROUP BY
-- 修正一:把列加进分组(注意分组粒度随之变化)
SELECT city, name, COUNT(*) FROM users GROUP BY city, name;
-- 修正二:把非聚合列改成聚合表达
SELECT city, MAX(name) AS sample_name, COUNT(*) FROM users GROUP BY city;
-- 修正三:主键函数依赖,两库都允许
SELECT id, name, COUNT(*) FROM users GROUP BY id; -- id 是主键行为对照
| 维度 | MySQL 8 | PostgreSQL |
|---|---|---|
| 默认行为 | 默认启用 ONLY_FULL_GROUP_BY | 按标准执行 |
| 函数依赖 | 主键可带动同表其它列 | 主键同样可带动同表其它列 |
| 关闭方式 | 改 sql_mode(不推荐) | 没有关闭开关 |
| 典型报错 | 1055 not in GROUP BY | must appear in the GROUP BY clause |
| 替代写法 | 聚合函数或窗口函数 | 同左 |
典型使用场景
- 老代码升级:从 MySQL 5.6 迁到 5.7 及以上时集中暴露的报错
- 跨库迁移:同一句 SQL 在 PostgreSQL 上同样被拒绝
- 代码评审:确认非聚合列的取值是否唯一确定
- 数据质量:把“随便取一行”的历史逻辑改成明确口径
- 排查线上 1055 报错:定位缺少分组键的语句
- 报表重写:用窗口函数替代原来不确定的非聚合列
报错其实是好事:它把“取到哪一行并不确定”的隐患提前暴露出来。与其关掉 ONLY_FULL_GROUP_BY 让旧 SQL 跑通,不如把语句改成聚合或调整分组粒度。
关闭 ONLY_FULL_GROUP_BY 的代价是结果不确定:索引、数据分布或版本一变,同一句 SQL 可能取到不同的非聚合列值,线上偶发的“数字对不上”常常来自这里。