非聚合列必须明确语义归属:放入GROUP BY或用窗口函数保留行上下文;否则严格模式下报错,因数据库无法确定其值来源。

不能直接混合,必须明确非聚合列的语义归属——要么放进 GROUP BY,要么用窗口函数保留行上下文。
为什么SELECT里出现非聚合列会报错
MySQL 5.7+、PostgreSQL、SQL Server 默认启用严格模式(如 ONLY_FULL_GROUP_BY),要求:SELECT 列表中每个非聚合字段,必须出现在 GROUP BY 子句里。否则报错:Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column。
这不是数据库“较真”,而是防止你误以为某列值有确定含义。比如 SELECT dept, name, AVG(salary) FROM employees GROUP BY dept 中,name 没分组也没聚合,数据库无法回答“这个 name 到底是张三还是李四?”
- 硬加
MAX(name)不等于“取最高薪的人名”,而是按字符串字典序取最大值 -
ANY_VALUE(name)看似能绕过报错,但返回哪一行完全不确定,尤其在并行执行或查询重写时可能漂移 - 把所有非聚合列都塞进
GROUP BY会导致分组过细,失去聚合意义(例如GROUP BY dept, name, id, email, hire_date...)
想保留明细行 + 附带聚合值?用窗口函数
窗口函数不是 GROUP BY 的替代品,而是解决另一类问题:原始行不能丢,还要每行带一个组内统计值。
关键约束:OVER() 必须存在,且 PARTITION BY 定义逻辑分组范围。
-
AVG(salary) OVER (PARTITION BY dept)→ 每行都显示所在部门平均工资 -
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)→ 每人在部门内按薪资排名 - 所有原始字段(
name、salary、hire_date)可直接出现在 SELECT 中,不冲突
示例:
SELECT name, dept, salary,
AVG(salary) OVER (PARTITION BY dept) AS dept_avg,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees;
想每组只取一条代表记录?别用 ANY_VALUE,用窗口函数过滤
典型需求:每个部门取薪资最高的员工完整信息(含 name、email、hire_date)。错误做法是:
-
SELECT dept, ANY_VALUE(name), MAX(salary) FROM employees GROUP BY dept→ANY_VALUE(name)和MAX(salary)不来自同一行 - 子查询 JOIN 多层嵌套,易漏多值情况(如两人同为最高薪)
正确路径:
- 先用
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)标记每组第一 - 外层
WHERE rn = 1过滤,确保取到真实匹配的整行 - 若需保留所有并列最高者,改用
RANK()或DENSE_RANK()
示例:
SELECT name, dept, salary, email, hire_date
FROM (
SELECT name, dept, salary, email, hire_date,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) ranked
WHERE rn = 1;
最易被忽略的一点:窗口函数的 ORDER BY 在 OVER 内部,不是整个查询的 ORDER BY;而 GROUP BY 后的排序必须靠外层再套一层或显式 ORDER BY,两者作用域和语法位置完全不同,混用必报错。

















