窗口函数在sql执行顺序中位于group by和having之后、order by之前,即from→where→group by→having→select(含窗口函数)→order by→limit;因此where、group by、having中均不可直接引用窗口函数结果。

窗口函数不是在GROUP BY之后“顺手算一下”,而是必须等WHERE、JOIN、GROUP BY、HAVING全部跑完,才开始执行——它依赖完整中间结果集,不能提前剪枝。
窗口函数在SQL生命周期里到底卡在哪一步
执行器不会边扫数据边算窗口函数。它严格按顺序推进:FROM → WHERE → GROUP BY → HAVING → SELECT(此时才轮到窗口函数)→ ORDER BY → LIMIT。这意味着:
-
WHERE条件里不能写ROW_NUMBER()或SUM() OVER (),因为那些值还没生成 - 你无法用窗口函数结果做分组依据,
GROUP BY SUM(sal) OVER (PARTITION BY dept)是语法错误 -
SELECT中普通列(如name)、聚合列(如COUNT(*))、窗口列(如AVG(sal) OVER ())是并行求值的,但窗口列必须等其他列的数据流稳定后才能启动自己的计算流程
PARTITION BY 和 ORDER BY 怎么影响执行器行为
PARTITION BY 不是物理分表,而是执行器在内存中做的逻辑分桶;ORDER BY 也不是只为输出排序,它是窗口计算的前提条件:
- 没写
PARTITION BY,整个结果集就是一个桶,ROW_NUMBER() OVER (ORDER BY id)就是全局编号 - 写了
PARTITION BY deptno,执行器会按deptno值把已过滤/聚合好的行打上桶标签,每个桶独立维护自己的排序队列和帧指针 -
ORDER BY hiredate后若存在重复值(比如多人同一天入职),且没加id等唯一字段保序,RANK()结果可能每次执行不一致——这不是bug,是排序未完全确定导致的
ROWS BETWEEN 才是执行器真正干活的指令
ORDER BY 只决定“谁在前谁在后”,ROWS BETWEEN 才告诉执行器“当前行该拉哪几行来算”:
-
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:执行器做前缀扫描,累加时只保留一个运行值,内存友好 -
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:执行器维护一个三行滑动缓冲区,适合移动平均 -
RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW:基于值比较而非行偏移,需对每行做范围判断,性能通常比ROWS差 - 省略
ROWS或RANGE时,默认是RANGE UNBOUNDED PRECEDING AND CURRENT ROW,注意这会把相同ORDER BY值的行视为同一位置,可能隐式去重
为什么 RANK() 和 ROW_NUMBER() 看似无参却要维护状态
它们不接收字段参数,但执行器内部必须跟踪两个关键状态:
- 当前分区内的累计行数(用于
ROW_NUMBER()) - 当前排序键的“上一个不同值的位置”(用于
RANK()判断是否跳号) - 这些状态在每个分区切换时重置,在每行输入时更新——所以它们不是纯函数,而是带状态的迭代器
- 这也解释了为什么你不能在
WHERE或HAVING中引用它们:状态还没初始化,连第一行都没处理
最易被忽略的一点:窗口函数的“窗口”不是预先分配的内存块,而是执行器按需动态定位行集合的过程。哪怕只查 LAG(score, 1),执行器也得为每一行反向找上一行——这个查找成本在大数据量 + 无索引 ORDER BY 字段时会非常明显。











