窗口函数不能替代GROUP BY,因二者作用层级不同:GROUP BY压缩行数(输出≤输入),OVER保留原始行结构叠加计算(输出=输入);需压缩用GROUP BY,需每行附加统计值才用OVER。

窗口函数和GROUP BY不是替代关系,而是分工明确:GROUP BY用来压缩数据、出汇总报表;窗口函数用来保留明细、加统计维度。
为什么GROUP BY不能直接换成OVER()
常见错误是看到AVG(salary)就以为加个OVER(PARTITION BY dept)就能替代GROUP BY dept——结果发现行数没变,字段还报错。这是因为:
-
GROUP BY dept会把100个员工压成5个部门行,输出只有dept和AVG(salary) -
AVG(salary) OVER (PARTITION BY dept)会返回100行,每行都带本部门平均值,但name、salary等原始字段全保留 - 想查“每个部门平均薪资”,必须用
GROUP BY;想查“每个员工薪资,同时标出所在部门平均值”,才轮到窗口函数
PARTITION BY漏写或写错的后果
PARTITION BY不是可选项,它定义计算边界。漏写或写错,结果就完全失真:
- 漏写
PARTITION BY→AVG(salary) OVER ()变成全表平均,每行都填同一个数 -
PARTITION BY dept, team写成PARTITION BY dept→ 部门内跨团队混算,排名/累计值全乱 - MySQL 8.0+ 和 PostgreSQL 对
ORDER BY在ROW_NUMBER()中是否强制要求不一致:PostgreSQL 直接报错,MySQL 可能按物理顺序编号,结果不可靠
WHERE里不能用窗口函数结果
想取每个用户的最新订单,写WHERE rn = 1一定失败,因为窗口函数执行在WHERE之后。正确做法只有一层嵌套:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders ) SELECT order_id, amount, created_at FROM ranked WHERE rn = 1;
注意:rn是计算列,不是原始字段,不能出现在WHERE或GROUP BY中;必须先WITH或子查询生成,再过滤。
性能和执行顺序的关键差异
两者底层执行逻辑不同,影响写法和效率:
-
GROUP BY走聚合路径:WHERE → GROUP → HAVING,常触发哈希分组或排序,内存消耗大 - 窗口函数走窗口路径:
FROM/WHERE → WINDOW → SELECT,依赖PARTITION BY和ORDER BY做分区+排序,ORDER BY缺失时部分数据库(如PostgreSQL)拒绝执行 -
COUNT(*) OVER (PARTITION BY u)比GROUP BY u后再JOIN回原表快得多,且不用处理ANY_VALUE()或MAX()取非分组字段的歧义
真正容易被忽略的是执行阶段:窗口函数永远在WHERE和GROUP BY之后运行,这意味着你不能用它来过滤行,也不能拿它的结果去分组——它只负责“追加”,不参与“裁剪”。

















