答案是mysql 5.7+和postgresql等严格模式下要求select中非聚合字段必须出现在group by子句或用聚合函数包裹,否则报错“expression #1 of select list is not in group by clause”;应优先补全group by字段、使用max/min等聚合函数,或改用窗口函数精确取值,而非禁用only_full_group_by模式。

SELECT列表中非聚合字段必须出现在GROUP BY中
MySQL 5.7+ 和 PostgreSQL 等严格模式下,如果 SELECT 中有非聚合字段(比如 name、email),但没在 GROUP BY 里声明,就会报错:ERROR 1055 (SQLSTATE HY000): Expression #1 of SELECT list is not in GROUP BY clause。这不是语法写错了,而是 SQL 标准要求:每个非聚合列必须唯一确定——靠 GROUP BY 保证。
常见错误写法:
SELECT user_id, name, COUNT(*) FROM orders GROUP BY user_id;
这里 name 没参与分组,数据库无法判断该取哪一行的 name 值。
- 修复方式一:把
name加进GROUP BY—— 仅当业务逻辑允许按user_id, name联合分组时才安全 - 修复方式二:用聚合函数包裹,如
MAX(name)或MIN(name)—— 前提是同一user_id下name实际一致(否则结果不可靠) - 修复方式三:改用子查询或窗口函数(如
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC))来精确取最新/主记录,避免模糊聚合
MySQL 8.0 默认 strict mode 下不能依赖隐式 GROUP BY
旧版 MySQL 允许 SELECT user_id, name FROM users GROUP BY user_id 这种写法,它会随机返回每组第一行的 name。MySQL 8.0 开启 sql_mode=ONLY_FULL_GROUP_BY 后直接拒绝执行。
临时绕过(不推荐):
SET sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
但这样会让结果不可预测,且上线环境通常禁止修改全局 sql_mode。
- 真正可靠的做法是:明确告诉数据库你要什么 —— 用
ANY_VALUE(name)(MySQL 特有)显式声明接受任意值,语义比关 strict mode 清晰 -
ANY_VALUE()不改变分组逻辑,只是告诉优化器“我知道这可能有歧义,但我确认可以接受” - 注意:PostgreSQL 没有
ANY_VALUE(),必须用MAX()/MIN()或子查询替代
聚合字段和 GROUP BY 字段语义不一致怎么办?
比如想查“每个用户的最近订单时间 + 订单金额”,但 ORDER BY created_at DESC LIMIT 1 在聚合里没法直接用。这时候 GROUP BY 和聚合目标出现语义错位。
典型陷阱:
SELECT user_id, MAX(created_at), amount FROM orders GROUP BY user_id;
这里 amount 是任意一行的值,和 MAX(created_at) 不一定来自同一订单。
- 正确解法是用窗口函数:
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)标记每组最新记录,再外层筛选rn = 1 - 兼容老版本 MySQL(WHERE created_at = (SELECT MAX(created_at) FROM orders o2 WHERE o2.user_id = orders.user_id)
- 别用
GROUP_CONCAT(id ORDER BY created_at DESC) + SUBSTRING_INDEX(..., ',', 1)这类 hack,可读性差且易出错
GROUP BY 字段包含表达式时容易漏掉括号或别名
当按计算字段分组,比如按年份分组订单,写成 GROUP BY YEAR(order_time) 是对的;但若用了别名却没在 GROUP BY 中复现表达式,就会报错。
错误示例:
SELECT YEAR(order_time) AS year, COUNT(*) FROM orders GROUP BY year;
PostgreSQL 和新 MySQL 都不认 GROUP BY 中的列别名,只认原始表达式或位置序号(GROUP BY 1 不推荐,可读性差)。
- 始终在
GROUP BY中重复表达式:GROUP BY YEAR(order_time) - 如果表达式很长,考虑用 CTE 提前计算,再分组,避免重复书写和潜在不一致
- 注意函数确定性:比如
NOW()、RAND()不能用于GROUP BY,会导致错误或不可预期行为
GROUP BY 定义了“组”的边界,而每个 SELECT 出来的值必须能在这个边界内唯一确定。哪怕加了 ANY_VALUE(),也要确认业务上是否真能接受“任意一行”的值。











