窗口函数中ORDER BY非可选,必须显式声明以确保排序确定性;默认RANGE帧对重复值敏感,需用ROWS明确逐行计算;NULL在排序中默认排最前,应显式指定NULLS LAST或提前过滤。

窗口函数里的 ORDER BY 不写就乱序
不加 ORDER BY 在 OVER() 里,ROW_NUMBER()、RANK()、LAG() 这类函数的结果就是非确定性的。数据库按物理存储顺序或优化器临时决定的顺序计算,同一语句执行两次可能得到完全不同的序号。
常见现象:导出报表时“第1名”每次都不一样;用 WHERE rn = 1 想取最新记录,结果随机命中某条。
-
OVER (PARTITION BY dept_id)—— 缺少ORDER BY,分组内排序无保证 -
OVER (ORDER BY score)—— 单字段排序,但 score 相同时顺序仍不确定 - 正确写法必须带完整排序键:
OVER (PARTITION BY dept_id ORDER BY score DESC, hire_date ASC)
NULL 值在 ORDER BY 中默认排哪?别猜,要显式声明
PostgreSQL/Oracle 默认 NULLS FIRST(升序)或 NULLS LAST(降序),MySQL 8.0+ 默认把 NULL 当最大值,SQL Server 根本不支持 NULLS LAST 语法——这种差异会让同一段 SQL 在不同库上跑出相反排名。
典型翻车:按 updated_at DESC 取最新记录,含 NULL 的行被标为 ROW_NUMBER() = 1,业务误判为“最新”。
- PostgreSQL / Oracle:直接写
ORDER BY updated_at DESC NULLS LAST - SQL Server:用
CASE WHEN updated_at IS NULL THEN 1 ELSE 0 END, updated_at DESC - MySQL 8.0+:确认版本 ≥ 8.0.22 再用
NULLS LAST,否则用COALESCE(updated_at, '1970-01-01')模拟(注意类型兼容) -
NULLS LAST必须紧贴排序表达式,不能写成ORDER BY updated_at DESC NULLS LAST, id ASC—— 后者只对updated_at生效
多个窗口函数并列时,谁先排?顺序影响结果
在一个 SELECT 列表里写多个窗口函数,它们不是并行计算的。数据库会按你写的顺序依次应用排序逻辑:排在前面的 OVER(ORDER BY ...) 会先重排结果集,后面的窗口函数就在这个新顺序上继续算。
现象:写 row_number() over (order by x) r1, row_number() over () r2,r2 并不是原始顺序,而是按 x 排完后的顺序编号。
- 想保留原始物理顺序?必须用
row_number() over ()且把它放在第一个位置 - 如果还要按别的字段排名,得明确写成:
row_number() over () as original_order, dense_rank() over (order by salary desc) as rank_by_salary - 别指望靠外层
ORDER BY original_order“还原”原始顺序——那只是最终输出排序,不影响窗口函数内部计算顺序
WHERE 里不能用窗口函数,但很多人硬塞
WHERE dense_rank() OVER (...) 这种写法在 MySQL、PostgreSQL、SQL Server 全部报错,提示类似 This function is not allowed in this context。窗口函数只能出现在 SELECT 列表或 HAVING 子句,不能用于 WHERE、GROUP BY 或子查询的 FROM 侧。
错误直觉:以为“先算排名再过滤”能一步到位;实际执行阶段,WHERE 在窗口计算之前就已解析完毕。
- 正确解法只有两种:
WITH ranked AS (...)CTE + 外层 WHERE,或嵌套子查询 - CTE 示例:
WITH t AS (SELECT *, dense_rank() OVER (PARTITION BY dept_id ORDER BY salary DESC) rnk FROM emp) SELECT * FROM t WHERE rnk - 性能注意:窗口函数本身不走索引,
PARTITION BY + ORDER BY组合在千万级表上可能触发磁盘排序,别只顾语法通顺,忘了看执行计划里的Sort操作
窗口函数的排序逻辑是隐式依赖执行顺序和数据库方言的,最危险的不是不会写,而是写了却没意识到它已经在后台悄悄重排了整张中间结果集。










