窗口函数不能替代group by提性能;group by适合汇总压缩行数,窗口函数仅在需保留原始行并叠加分组计算时适用,如替代关联子查询实现累计值等。

窗口函数不能代替 GROUP BY 提高性能——它根本不是干这个的。想压缩行数、做汇总报表,GROUP BY 更快更轻;窗口函数只在必须「保留所有原始行」的前提下叠加分组计算时才有意义。
AVG() OVER(PARTITION BY x) 不是 GROUP BY 的提速方案
常见误解:以为 AVG(salary) OVER(PARTITION BY dept) 比 SELECT dept, AVG(salary) FROM employees GROUP BY dept 更快。实际相反:
- 前者输出 100 行(假设员工数),每行多一列
dept_avg_salary,需完成分区、排序(隐式)、广播三步,开销更大 - 后者输出 3 行(假设 3 个部门),直接哈希聚合或索引扫描,无内存排序压力
- 如果只查“各部门平均薪资”,硬套窗口函数只会拖慢查询、浪费内存
-
PARTITION BY dept若未建索引,MySQL/PostgreSQL 可能触发WindowAgg节点全表排序,EXPLAIN 中占比飙升
真正能提性能的场景:替代关联子查询
当你要给每行附带一个「分组内累计值」「历史总和」「最新时间戳」时,传统写法常依赖子查询或自连接,而窗口函数单次扫描就能搞定:
- 查每个订单,并附带“该客户截至当前的消费总额”:
SUM(amount) OVER (PARTITION BY customer_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 替代这种低效子查询:
(SELECT SUM(o2.amount) FROM orders o2 WHERE o2.customer_id = o1.customer_id AND o2.created_at - 性能差异明显:10 万行数据下,子查询可能耗时 14 秒,窗口函数通常压在 0.3 秒内
- 注意:
ORDER BY created_at必须存在且有索引,否则累计逻辑错乱,且排序成本翻倍
ROW_NUMBER() + CTE 是取每组最新一行的唯一可靠写法
别信 GROUP BY user_id ORDER BY created_at DESC LIMIT 1 这类写法——它在 MySQL 8.0+ 下大概率报 ERROR 1055,即使侥幸执行,order_id 和 MAX(created_at) 也未必来自同一行。
- 正确路径只有一步:
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC)打标 - 必须套 CTE 或子查询,因为窗口函数不能出现在
WHERE中:WITH ranked AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC) AS rn FROM orders) SELECT * FROM ranked WHERE rn = 1 - 漏掉
id DESC(或其他唯一字段)会导致同秒创建的订单排序不稳定,PostgreSQL 直接报错,MySQL 返回不可重现结果 - 若业务允许并列(如两个订单同秒都算“最新”),改用
RANK(),但要注意它会返回多行
多个窗口函数共存时,性能可能比想象中更差
一个 SELECT 里写三个不同 PARTITION BY 或不同 ORDER BY 的窗口函数,引擎往往无法复用排序结果:
-
COUNT(*) OVER (PARTITION BY a)、AVG(x) OVER (PARTITION BY b ORDER BY t)、LAG(y) OVER (PARTITION BY c ORDER BY u)→ 很可能触发三次独立排序 - 千万级表上,
EXPLAIN ANALYZE里能看到多个高占比的WindowAgg或Sort节点 - 优化方向不是“少写几个”,而是合并分区逻辑:比如把
a、b、c设计为同一维度,或提前用WHERE过滤掉无效数据再进窗口计算 - 空括号写法
COUNT(*) OVER ()在 PostgreSQL 中会强制全表排序,比显式SELECT COUNT(*) FROM t再 JOIN 还慢
最容易被忽略的是:窗口函数的性能瓶颈不在函数本身,而在 PARTITION BY 字段是否可走索引、ORDER BY 是否稳定、以及是否误把 GROUP BY 的任务强塞给它。一旦写错,不是慢一点,而是查不出正确结果。










