首页/数据/SQL 速查/ONLY_FULL_GROUP_BY 与分组合法性
聚合与窗口函数54 条

ONLY_FULL_GROUP_BY 与分组合法性

非聚合列不在 GROUP BY 中时:MySQL 默认模式直接报错(ONLY_FULL_GROUP_BY),PostgreSQL 同样拒绝;识别主键函数依赖可放行。

SQL 速查数据更新于 2026-10-13

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 8PostgreSQL
默认行为默认启用 ONLY_FULL_GROUP_BY按标准执行
函数依赖主键可带动同表其它列主键同样可带动同表其它列
关闭方式改 sql_mode(不推荐)没有关闭开关
典型报错1055 not in GROUP BYmust appear in the GROUP BY clause
替代写法聚合函数或窗口函数同左

典型使用场景

  • 老代码升级:从 MySQL 5.6 迁到 5.7 及以上时集中暴露的报错
  • 跨库迁移:同一句 SQL 在 PostgreSQL 上同样被拒绝
  • 代码评审:确认非聚合列的取值是否唯一确定
  • 数据质量:把“随便取一行”的历史逻辑改成明确口径
  • 排查线上 1055 报错:定位缺少分组键的语句
  • 报表重写:用窗口函数替代原来不确定的非聚合列
报错其实是好事:它把“取到哪一行并不确定”的隐患提前暴露出来。与其关掉 ONLY_FULL_GROUP_BY 让旧 SQL 跑通,不如把语句改成聚合或调整分组粒度。
关闭 ONLY_FULL_GROUP_BY 的代价是结果不确定:索引、数据分布或版本一变,同一句 SQL 可能取到不同的非聚合列值,线上偶发的“数字对不上”常常来自这里。

易错点

  • 关掉 ONLY_FULL_GROUP_BY 让旧 SQL 跑通:非聚合列取值不确定,等于把 bug 藏起来
  • 把列一股脑塞进 GROUP BY:分组粒度变细,统计口径被悄悄改变,先考虑去重或聚合函数
  • 以为 PostgreSQL 宽松:它同样直接拒绝非聚合列,迁移时一样要改
  • 把函数依赖当成通用规则:只有分组键包含主键或唯一键时同表其它列才被放行,跨表列不适用

数据来源与参考

  • MySQL 8.0 Reference Manual · MySQL Handling of GROUP BY
  • PostgreSQL Documentation · GROUP BY
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌