mysql 5.7+默认启用only_full_group_by,select中非聚合字段必须出现在group by中或用聚合函数包裹,否则报错1055;旧版或禁用该模式时返回未定义行为的任意值,不可靠。

不是随机,是未定义行为——数据库没承诺返回哪一行的值,你看到的只是它碰巧选中的结果。
MySQL 5.7+ 之前默认“容忍”,但结果不可靠
旧版 MySQL(或关闭 ONLY_FULL_GROUP_BY 的实例)允许 SELECT dept, name, COUNT(*) FROM emp GROUP BY dept 执行成功,但它从每组中返回的 name 是任意一行的值,不保证是首行、最新行、最大值行,甚至同一条 SQL 多次执行都可能不同。这不是 bug,是标准缺失下的实现自由。
- 优化器可能按索引顺序取第一行
- 并行执行时可能取最后扫描到的一行
- 加了
ORDER BY也不影响分组内选值逻辑(ORDER BY在GROUP BY之后执行)
PostgreSQL / SQL Server / Oracle 根本不让你这么写
这些数据库直接报错:column "name" must appear in the GROUP BY clause or be used in an aggregate function。它们拒绝执行,因为 SQL 标准要求:非分组、非聚合字段的值必须语义确定。你没说清楚要哪个 name,它就不动。
- 没有
ANY_VALUE()这种“随便选一个”的函数(MySQL 特有) -
MAX(name)是合法的,但按字典序取最大,和业务意图常不一致 - 想取“薪资最高那个人的姓名”?必须用窗口函数或子查询,不能靠
GROUP BY硬凑
真正需要“某一行代表值”时,别依赖 GROUP BY
比如“每个部门薪资最高的员工姓名”,MAX(name) 和 MAX(salary) 来自不同行,ANY_VALUE(name) 不稳定,都是陷阱。
- 正确做法:用
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)标记每组排名,再WHERE rn = 1 - 如果只有一条记录需保留(如最新日志),
DISTINCT ON (id) ... ORDER BY id, ts DESC(PostgreSQL)更简洁 - 多表 JOIN 后误用 GROUP BY,还可能把一对多关系放大成笛卡尔积,让聚合值翻倍
最易被忽略的点:你以为在“取代表”,其实数据库在“扔数据”——GROUP BY 本质是压缩行,所有未参与分组或聚合的字段,其原始行信息已不可追溯。要保留明细,就得换思路,用窗口函数或关联子查询。










