ROW_NUMBER()生成唯一连续序号,重复值仍递增编号;RANK()实现并列排名,重复值名次相同且后续名次跳过。二者本质区别在于:ROW_NUMBER()是行号生成器,RANK()是名次计算器,业务需求决定选型——分页、去重用前者,真实排名用后者。

ROW_NUMBER() 和 RANK() 的编号逻辑完全不同
别被“都是排名函数”误导——ROW_NUMBER() 只管物理顺序,RANK() 模拟真实竞赛名次。同一个排序条件下,它们输出的数字几乎总是不一致。
比如对成绩 [95, 88, 88, 76] 降序排列:
-
ROW_NUMBER()输出[1, 2, 3, 4]:每行强制唯一编号,不管值是否重复 -
RANK()输出[1, 2, 2, 4]:两个 88 并列第 2 名,直接跳过 3,下一个是第 4 名
根本区别在于:ROW_NUMBER() 是行号生成器,RANK() 是名次计算器。
WHERE 子句里不能直接用 ROW_NUMBER() 或 RANK()
这两个函数返回的是窗口计算结果,不是原始表字段,所以不能写成 WHERE ROW_NUMBER() OVER (...) = 1 —— 会报错 Window function is not allowed in WHERE clause。
必须套一层子查询或 CTE:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY score DESC) AS rn FROM students ) t WHERE rn BETWEEN 11 AND 20;
常见错误是试图在 WHERE 或 HAVING 中直接调用,结果查不到数据或语法报错。
分页和去重场景该选哪个?
选错函数会导致业务逻辑出错,不是“差不多”,而是“完全不对”:
- 做 Web 分页(取第 21–30 条)→ 必须用
ROW_NUMBER(),它保证连续、唯一、可精确切片 - 生成排行榜(并列第 2 名后是第 4 名)→ 用
RANK(),符合“名次跳号”的业务语义 - 想保留每组最新一条记录(如每个用户最新订单)→ 用
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY create_time DESC),再WHERE rn = 1;若用RANK(),可能因时间相同返回多条
特别注意:RANK() 在 PARTITION BY 下也会跳号,这点常被忽略——它跳的是“组内名次”,不是全局序号。
MySQL 8.0+ 和 SQL Server 都支持,但低版本不认
ROW_NUMBER() 和 RANK() 是标准窗口函数,MySQL 从 8.0 开始支持,SQL Server 2005 起就有。如果你用的是 MySQL 5.7 或更早,直接执行会报错 FUNCTION xxx does not exist。
替代方案只能靠变量模拟(不稳定、不可并发),或者升级数据库。别指望加个索引或改写 WHERE 就能绕过去——这是语法层缺失,不是性能问题。
真正容易被忽略的点是:即使你只写了个简单 SELECT ROW_NUMBER() OVER(ORDER BY id),只要数据库版本不够,整条语句就跑不通,连试错机会都没有。

















