ROW_NUMBER()必须带ORDER BY且排序字段需含唯一列,否则报错或结果错乱;缺索引会导致全表排序、性能崩盘,应建覆盖WHERE+ORDER BY+主键的复合索引。

必须带 ORDER BY,且排序字段要含唯一列,否则结果错乱、性能崩盘——这不是优化建议,是 SQL Server 的硬性语法和执行逻辑要求。
为什么 ROW_NUMBER() 一用就慢甚至报错?
常见错误现象:Window function 'ROW_NUMBER' requires an OVER clause with ORDER BY 直接报错;或查询几秒没响应,执行计划里满屏红色 Sort 和 Table Scan。
-
ROW_NUMBER()在 SQL Server 中强制要求OVER (ORDER BY ...),漏写或写成OVER ()都会语法报错 - 没索引时,SQL Server 必须先对全表排序再编号——10 万行就可能触发内存溢出或 tempdb 压力,不是函数本身慢,是缺索引导致的硬排序
- 只给
created_at建单列索引,但OVER (ORDER BY created_at DESC, id DESC)是多列排序,索引完全不命中
ORDER BY 字段怎么选才稳?
核心原则:让每一行的排序组合值唯一,避免非确定性编号(同一查询多次执行结果不一致)。
- 优先用主键或含主键的组合,例如
ORDER BY status, created_at DESC, id DESC,比单用created_at DESC可靠得多 - 别在
ORDER BY里用表达式,比如ORDER BY DATEADD(day, 1, created_at)或ORDER BY UPPER(name),索引失效,必走Sort - 如果 WHERE 常带
status = 1,索引顺序必须是(status, created_at DESC, id DESC),把过滤条件放最左
外层 WHERE 怎么写才能触发 TopN 优化?
写错一个字符,优化器就放弃下推,全表扫描不可避免。
- 必须用
WHERE t.rn BETWEEN @start AND @end,不能写成WHERE t.rn > @start AND t.rn -
@start要算准:比如第 5 页、每页 20 条,@start = (5 - 1) * 20 + 1→81,漏加+1就丢首行 - 子查询必须起别名(如
AS t),且ROW_NUMBER()必须显式AS rn,否则t.rn报错Invalid column name 'rn'
参数和索引对不上,再对的 SQL 也白搭
很多团队建了索引还慢,问题出在“索引结构”和“SQL 写法”没咬合上。
- 写的是
OVER (ORDER BY created_at DESC, id DESC),索引就得是CREATE INDEX IX_logs_time_id ON logs(created_at DESC, id DESC),方向、列序、数量必须一致 - 传参类型要防溢出:
@page_index是INT,但(@page_index - 1) * @page_size超过 2147483647 就报Arithmetic overflow,得提前转BIGINT - 深度分页(如 page_index > 100000)别硬扛,这时
ROW_NUMBER()已退化为全量排序,应切到游标分页(WHERE (created_at, id) > (@last_time, @last_id))
真正卡住人的从来不是语法会不会写,而是索引是否覆盖了 WHERE 条件 + ORDER BY 字段 + 主键回表这三段路径——少一段,ROW_NUMBER() 就从高效变低效。

















