GROUP BY本质是压缩行:先按分组列拆分数据为若干组,再将每组压成一行;SELECT中非聚合列必须出现在GROUP BY子句中或套用聚合函数,否则因值不确定而报错。

因为数据库无法确定该列在分组后该返回哪个值——它不是“算出来的”,又没被明确指定“按什么分”,结果必然歧义。
GROUP BY 本质是压缩行,不是筛选行
执行 GROUP BY dept 时,数据库先把所有行按 dept 值拆成若干组,再把每组压成一行。这时,SELECT 中每个非聚合列(比如 name)在组内可能有多个不同值。数据库不会猜、也不会随机挑——它直接拒绝执行,避免返回不可靠结果。
- 错误示例:
SELECT dept, name, AVG(salary) FROM employees GROUP BY dept→name在“技术部”组里可能是张三、李四、王五,选谁? - 正确做法:要么加进
GROUP BY dept, name(此时每组只含同部门同名员工),要么用聚合函数包裹,如MAX(name)(但注意:字典序最大 ≠ 薪资最高) - MySQL 5.7+ 默认开启
ONLY_FULL_GROUP_BY,这类语句会直接报错:Expression #2 of SELECT list is not in GROUP BY clause
别指望主键或外键能“自动推导”分组依赖
即使 id 是主键,SELECT id, status, created_at FROM logs GROUP BY id 依然非法。SQL 标准不基于约束做逻辑推导,只看语法结构:只要 status 没出现在 GROUP BY 子句里,也没套聚合函数,就视为未定义行为。
- PostgreSQL 比 MySQL 更严格:连“函数依赖”(如
emp_id → dept)都不认,dept必须显式写进GROUP BY才能出现在SELECT中 - 多列
GROUP BY时,SELECT中所有非聚合列必须全部列出,顺序无关,但缺一不可 - 联表查询中最易漏写:比如
JOIN Articles ON a.id = b.articleid后,articleid出现在SELECT却不在GROUP BY中,立刻报错
想保留原行又加聚合值?别硬套 GROUP BY,改用窗口函数
GROUP BY 是“压缩后汇总”,OVER() 是“带着原样算汇总”。两者解决的问题完全不同。
- 错误思路:
ANY_VALUE(name)敷衍过去 → 不保证返回哪一行,查询结果可能每次都不一样 - 正确场景:查每个员工的工资,同时附带“所在部门平均工资” → 必须用
AVG(salary) OVER (PARTITION BY dept) - 关键约束:
OVER()子句不能省;PARTITION BY相当于逻辑分组,但不删行;所有原始字段(name、salary、hire_date)可直接出现在SELECT中
这个限制不是 MySQL 特有,而是 SQL 标准行为。哪怕你关掉 ONLY_FULL_GROUP_BY,返回的 name 仍是不确定的——它可能来自任意一条匹配行,且优化器重写或并发执行时可能变化。真要业务可靠,就别绕开规则。

















