应使用ANY_VALUE()显式声明非确定性取值,或用窗口函数实现“每组取最新一条”等明确逻辑,避免关闭ONLY_FULL_GROUP_BY导致数据不可靠。
MySQL 8.0+ 中
报错 “Expression #1 of SELECT list is not in GROUP BY clause” 怎么办
这是 MySQL 严格模式下的典型报错,本质不是语法错误,而是 SQL 模式(
)拒绝“非确定性选择”。比如你写了
,
和
没聚合也没出现在
里,MySQL 不知道该取哪一行的值。
最直接的解法是关掉
—— 但不推荐。它掩盖问题,上线后可能返回任意行,查到的数据不可靠。
用
显式声明“我就要随便取一个”,语义清晰,兼容 MySQL 5.7.5+
用窗口函数(如
)先排序再过滤,适合“每组取最新一条”这类明确逻辑
如果只是想查分组统计 + 某个代表值(比如部门负责人),建议提前在子查询或 CTE 里把代表值算好,再和分组结果 JOIN
看似简单,但容易忽略的三个行为细节
不是随机函数,也不是取首行,它是“优化器自由选择的一行值”——具体选哪行取决于执行计划、索引、数据分布,甚至 MySQL 版本升级都可能变。所以它只适用于你真不在乎是哪一行的场景。
不能用于主键或唯一列的“去重替代”,比如
在
下可能返回任意匹配的
,无法保证一致性
和
同时用没意义:
中的
不影响
的取值
在视图或物化查询中使用时,如果底层表加了索引,
可能稳定返回索引顺序的第一行,但这属于实现细节,不应依赖
想“每组取最新一条”,为什么别硬套
而要用窗口函数
因为
解决不了“按时间/优先级选”的需求。例如“每个部门最新入职的员工”,
可能返回三年前的老员工,而你要的是
最大的那条。
窗口函数才是正解:
注意点:
保证每组编号唯一;如果允许并列(比如同一天入职多人),改用
或
MySQL 8.0+ 支持,5.7 及更早版本不支持,得用自连接或变量模拟,复杂度陡增
如果数据量大,
列上最好有索引,否则
子句会触发临时表和文件排序
PostgreSQL 或 SQLite 怎么处理类似问题
它们没有
,但默认行为不同:PostgreSQL 严格要求所有非聚合列都在
中;SQLite 则允许“隐式任意取值”,但行为未定义,不建议依赖。
PostgreSQL 推荐用
配合
实现“每组取第一条”:
SQLite 若必须兼容老写法,可开启
pragma(仅 3.30+),但同样不保序、不保确定性
跨数据库移植时,别写
这种语句——它在 MySQL(开 ONLY_FULL_GROUP_BY)和 PostgreSQL 下都会报错,在 SQLite 下看似能跑,实则危险
真正麻烦的从来不是语法怎么写,而是你有没有想清楚:那个没聚合的列,到底应该代表什么?是随便一个?第一个?最新的?还是该被拆出去单独关联?这个问题没理清,换什么函数都只是把问题往后拖。
GROUP BYsql_modeSELECT id, name, COUNT(*) FROM users GROUP BY deptidnameGROUP BYONLY_FULL_GROUP_BYANY_VALUE()ROW_NUMBER() OVER (PARTITION BY dept ORDER BY created_at DESC)ANY_VALUE()ANY_VALUE()ANY_VALUE(id)GROUP BY nameidORDER BYSELECT ANY_VALUE(name) FROM t GROUP BY dept ORDER BY deptORDER BYANY_VALUE()ANY_VALUE()ANY_VALUE()ANY_VALUE()ANY_VALUE()created_atSELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY created_at DESC) AS rn
FROM employees
) t
WHERE rn = 1;ROW_NUMBER()RANK()DENSE_RANK()PARTITION BYOVERANY_VALUE()GROUP BYDISTINCT ON (dept)ORDER BYSELECT DISTINCT ON (dept) dept, name, salary FROM employees ORDER BY dept, created_at DESClegacy_group_bySELECT a, b, COUNT(*) FROM t GROUP BY a