partition by 先执行,order by 后执行。sql 引擎先按 partition by 划分数据子集,再在每个子集内单独按 order by 排序;无 partition by 时 order by 作用于全表,有则仅限当前分区。

窗口函数里 PARTITION BY 和 ORDER BY 谁先执行?
分区永远先于排序。SQL 引擎在计算窗口函数前,先按 PARTITION BY 划分数据子集,再在每个子集内部单独执行 ORDER BY。这意味着:没有 PARTITION BY 时,ORDER BY 在全表范围内生效;加了 PARTITION BY 后,ORDER BY 只作用于当前分区,不同分区之间的顺序互不影响。
常见误解是以为 ORDER BY 全局排序后再分区——实际完全相反。这也是为什么 ROW_NUMBER() OVER (ORDER BY score DESC) 和 ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) 产生的序号完全不同。
ORDER BY 缺失时,ROWS BETWEEN 行为不可靠
当窗口定义中没写 ORDER BY,但用了 ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 这类帧子句,结果依赖物理存储顺序(如堆表插入顺序),不同数据库甚至同一库不同执行计划下都可能变化。PostgreSQL 会报错,MySQL 8.0+ 允许但警告,SQL Server 直接拒绝。
- 必须显式写
ORDER BY才能保证帧边界语义稳定 - 哪怕只是按主键排序:
ORDER BY id,也比不写强 -
RANGE帧更敏感——没ORDER BY时根本无法确定等值范围
PARTITION BY 字段含 NULL 时,所有 NULL 被归为同一组
这是 ANSI SQL 标准行为,不是某数据库的 bug。NULL 不等于 NULL,但在 PARTITION BY 中,所有 NULL 值被视为“相同”,合并进一个分区。
例如:PARTITION BY region,若 5 行 region 为 NULL,它们会共同构成一个分区,各自参与该分区内的窗口计算。如果想把 NULL 当作独立类别或排除,得提前用 CASE WHEN region IS NULL THEN 'UNKNOWN' ELSE region END 处理。
注意:这和 GROUP BY 中 NULL 的处理逻辑一致,但容易被忽略,尤其在做分组累计求和时导致意外聚合。
多字段 ORDER BY 影响 RANK() 和 DENSE_RANK() 的并列判断
RANK() 和 DENSE_RANK() 的“并列”只看 ORDER BY 列的值是否完全相等。比如 ORDER BY dept, salary,只有 dept 和 salary 都相同时才并列;而 ORDER BY salary 时,只要 salary 相同就并列,不管 dept。
实操建议:
- 检查业务上“并列”的定义是否真需要多字段联合判断
- 避免在
ORDER BY里混入高基数字段(如id),否则几乎不会出现并列,让RANK()退化成ROW_NUMBER() - 测试时用
SELECT *, RANK() OVER (ORDER BY x, y) AS r直接观察重复值对应的r是否符合预期
最易被绕开的点是:窗口函数的执行阶段独立于外部 ORDER BY。你在最外层写 ORDER BY name,完全不影响窗口内部的排序逻辑——那是 OVER 子句里自己定的。别指望靠最终查询排序来“修正”窗口结果顺序。










