GROUP BY和OVER()不能互换,因为GROUP BY压缩行数(100行变10行),而OVER()保留所有原始行并附加计算值(100行仍是100行,每行新增“本组平均值”等列)。

不能替代,因为它们根本不是同一类操作——GROUP BY压缩行数,OVER()保留所有原始行并附加计算值。
为什么GROUP BY和OVER()不能互换
GROUP BY把 100 行变成 10 行;OVER(PARTITION BY x)让 100 行还是 100 行,只是每行多一个“本组平均值”或“本组第几行”。想统计“每个部门平均薪资”,用GROUP BY dept;想查“每个员工的薪资,同时标出本部门平均值”,就必须用AVG(salary) OVER (PARTITION BY dept)。
常见错误现象:SELECT name, dept, AVG(salary) FROM employees GROUP BY dept在 MySQL 8.0+ 下直接报错 ERROR 1055,因为 name 既没聚合也没分组。硬加 MAX(name) 可能返回张三,但平均薪资却是李四所在组的——逻辑错位。
PARTITION BY漏写或写错,结果就全错
PARTITION BY定义窗口的逻辑分组,不写就变成全表计算,每行都填同一个数。
-
COUNT(*) OVER (PARTITION BY user_id)→ 每行得到“该用户订单总数” -
COUNT(*) OVER ()(空括号)→ 每行都填整张表总行数,且可能触发隐式全表排序,性能风险高 -
ROW_NUMBER() OVER (PARTITION BY user_id)不加ORDER BY:PostgreSQL 直接报错;MySQL 可能按存储顺序编号,多次执行结果不同
排序字段必须有业务含义或稳定索引支撑:ORDER BY id只有在 id 严格递增且代表插入时序时才安全(截至 2026 年 7 月 26 日)。
哪些场景必须用窗口函数,GROUP BY完全无解
当你需要保留原始行,又得叠加分组统计值,OVER()是唯一干净解法:
- 查所有订单,同时显示“该客户累计消费额” →
SUM(amount) OVER (PARTITION BY customer_id ORDER BY created_at) - 列出全部学生成绩,并标出“班级最高分” →
MAX(score) OVER (PARTITION BY class) - 取每个用户的最新一笔订单完整字段 → 必须用
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)打标后嵌套过滤
注意:WHERE rn = 1 这种写法一定报错,因为窗口函数执行阶段晚于 WHERE。必须套一层:WITH ranked AS (...) SELECT * FROM ranked WHERE rn = 1。
最常被忽略的一点:多个不同 PARTITION BY 或 ORDER BY 的窗口函数共存时,PostgreSQL 和 SQL Server 往往无法复用排序结果,会分别扫描、排序两次甚至三次——性能下降比想象中更隐蔽。

















