窗口函数在SQL执行顺序中位于WHERE、GROUP BY、HAVING之后,SELECT阶段计算,早于外层ORDER BY和LIMIT,且基于未被LIMIT截断的完整结果集;不能在WHERE或GROUP BY中直接引用,须用子查询或CTE固化结果。

OVER 子句里的窗口函数不是按你写的顺序执行的,它在 SQL 生命周期里有固定位置:必须等 WHERE、GROUP BY、HAVING 全部跑完,才开始算。
窗口函数不能用在 WHERE 或 GROUP BY 里
因为它们还没出生——WHERE 过滤时,窗口函数的结果根本不存在;GROUP BY 分组时,窗口计算连数据都没见着。常见错误是写 WHERE RANK() OVER (...) > 1,直接报错 Unknown column 'RANK() OVER (...)' in 'where clause'。
- 想过滤排名结果?得套一层子查询或 CTE,把窗口函数结果当成普通列再过滤
-
GROUP BY中也不能引用窗口函数别名,哪怕 SELECT 里已经定义了 - 如果需要分组后再算窗口,确保
GROUP BY先完成,窗口函数写在最外层 SELECT
PARTITION BY 和 ORDER BY 的实际影响
PARTITION BY 不是“分组并聚合”,而是切数据块;ORDER BY 在每个块内决定行序——这直接影响 ROW_NUMBER() 编号、LAG() 取哪一行、SUM() OVER (ROWS BETWEEN ...) 累加范围。
- 没写
ORDER BY时,RANK()、ROW_NUMBER()行为未定义(MySQL 8.0 报错,其他引擎可能随机排序) -
PARTITION BY dept ORDER BY salary DESC和PARTITION BY dept ORDER BY hire_date算出来的累计值完全不同 - 用
FIRST_VALUE()或LAST_VALUE()时,不显式指定ROWS或RANGE框架,MySQL 默认是ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,容易漏掉后续行
ROWS 和 RANGE 的区别常被忽略
ROWS 按物理行数定位,RANGE 按排序值的逻辑范围定位。比如按日期排序时:RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW 会自动跨过无数据的日期;而 ROWS BETWEEN 6 PRECEDING AND CURRENT ROW 严格取最近 7 行,不管日期是否连续。
- 移动平均、同比环比优先考虑
ROWS,保证行数稳定 - 按金额区间统计(如“工资在当前值±500内的所有人”)必须用
RANGE - MySQL 8.0 对
RANGE支持有限,INTERVAL只支持DATE和DATETIME类型,数值列用RANGE会报错
性能开销比普通聚合高得多
窗口函数要缓存整个分区的数据才能逐行计算,不像 GROUP BY SUM() 可以边扫边累加。尤其当 PARTITION BY 基数低(比如全表只有一个分区)、或 ORDER BY 字段无索引时,执行计划里会出现 Using temporary; Using filesort。
- 检查
EXPLAIN输出,重点看Extra列是否含Window function和临时表提示 - 大表慎用
OVER ()(无分区),等价于全表扫描+全内存缓存 - 如果只是要“每行带部门平均值”,
AVG(salary) OVER (PARTITION BY dept)比关联子查询快,但比提前物化部门均值表慢
WHERE 和 GROUP BY 后的那批行,不多也不少。写之前先问自己:这行数据此时是否存在?这个排序在此刻是否已确定?

















