窗口函数应在需保留原始行且叠加分组计算时替代group by;group by适用于仅需分组汇总结果的场景;partition by不可省略,索引与过滤顺序影响性能。

什么时候该用窗口函数替代GROUP BY
只在你需要「保留原始每一行」的同时,叠加分组计算结果时才适合。比如查员工名单,还想每行都带本部门平均薪资——GROUP BY dept会只剩3行(假设3个部门),而AVG(salary) OVER(PARTITION BY dept)保持100行不变,多一列dept_avg_salary。
如果目标只是列出各部门平均薪资,GROUP BY dept更轻、更快、更直接,别硬套窗口函数。
AVG() OVER(PARTITION BY) 替代 GROUP BY 求均值
这是最常见也最容易踩坑的场景。核心是:PARTITION BY必须写,ORDER BY在求静态均值时可省略,但漏掉PARTITION BY就变成全表均值,毫无分组意义。
-
AVG(salary) OVER(PARTITION BY dept)→ 每个部门内算均值,广播到该部门所有行 -
AVG(salary) OVER()→ 全表均值,每行都一样,相当于加了个常量列 - 别在
SELECT里混用非分组字段和GROUP BY,MySQL 8.0+会直接报ERROR 1055;窗口函数天然绕过这限制
示例:
SELECT name, dept, salary,
AVG(salary) OVER(PARTITION BY dept) AS dept_avg
FROM employees;
ROW_NUMBER() 替代 GROUP BY 取每组第一行
GROUP BY无法可靠取“每组最新一条”,因为它的ORDER BY作用于最终结果,不是分组内排序。窗口函数才是解法,但必须套子查询或CTE——窗口函数不能出现在WHERE里。
-
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY created_at DESC, id DESC)确保时间相同时有确定顺序 - 用
RANK()或DENSE_RANK()会返回并列第一的多行,只要求“严格一行”,必须用ROW_NUMBER() - 没写
ORDER BY时,PostgreSQL直接报错,MySQL可能返回不稳定结果
正确写法:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY created_at DESC, id DESC) AS rn FROM orders ) SELECT user_id, order_id, amount FROM ranked WHERE rn = 1;
PARTITION BY 字段没索引,性能可能比 GROUP BY 还差
窗口函数不是银弹。PARTITION BY category若只有3个值,而表有千万行,数据库得把大部分数据拉进内存排序,WindowAgg节点容易成瓶颈。
-
EXPLAIN ANALYZE中看到高占比的WindowAgg或Using filesort,就是信号 - 建联合索引优先按
PARTITION BY字段 +ORDER BY字段,例如(category, created_at) - 先
WHERE status = 'active'再开窗,别让窗口函数白算百万无效行 - 窗口函数输出不能直接用于
WHERE或HAVING,想筛“部门均值 > 10000”的人,还得套一层子查询
真正容易被忽略的,是PARTITION BY字段的区分度和索引覆盖——它不只影响语法对错,更决定查询从秒级变分钟级。











