ROW_NUMBER()、RANK()、DENSE_RANK() 这三个都是 SQL 中的窗口函数,用于为结果集中的每一行分配一个排名。它们的核心区别在于如何处理并列(相同排序值) 以及后续排名是否连续。
快速对比表
| 函数 |
并列处理 |
排名是否连续 |
举例(分数: 100,100,90) |
| ROW_NUMBER() |
强行区分,任意给一个顺序(不并列) |
1,2,3 |
✅ 连续 |
| RANK() |
并列占用下一个排名的位置(跳号) |
1,1,3 |
❌ 不连续 |
| DENSE_RANK() |
并列不跳号 |
1,1,2 |
✅ 连续 |
详细说明与例子
假设有一张成绩表 score:
| student |
score |
| 张三 |
100 |
| 李四 |
100 |
| 王五 |
90 |
| 赵六 |
80 |
1. ROW_NUMBER()
- 规则:即使值相同,也强行分出一个唯一序号(通常基于未知或指定的顺序)
- 结果:1,2,3,4(严格递增,无并列)
SELECT student, score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rn
FROM score;
| student |
score |
rn |
| 张三 |
100 |
1 |
| 李四 |
100 |
2 |
| 王五 |
90 |
3 |
| 赵六 |
80 |
4 |
2. RANK()
- 规则:并列排名相同,但下一个排名会“跳号”
- 结果:1,1,3,4
SELECT student, score,
RANK() OVER (ORDER BY score DESC) AS rk
FROM score;
| student |
score |
rk |
| 张三 |
100 |
1 |
| 李四 |
100 |
1 |
| 王五 |
90 |
3 |
| 赵六 |
80 |
4 |
3. DENSE_RANK()
- 规则:并列排名相同,且下一个排名不跳号
- 结果:1,1,2,3
SELECT student, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS dr
FROM score;
| student |
score |
dr |
| 张三 |
100 |
1 |
| 李四 |
100 |
1 |
| 王五 |
90 |
2 |
| 赵六 |
80 |
3 |
典型使用场景
| 函数 |
适用场景 |
| ROW_NUMBER() |
需要唯一行号,例如分页、去重保留一条、取每组前N条(不关心并列) |
| RANK() |
比赛排名(奥运奖牌榜风格:并列第一后是第三名) |
| DENSE_RANK() |
连续排名需求(例如:“你超过了多少人”的百分比计算) |
一个容易被忽略的点
ROW_NUMBER() 不是稳定排序。如果 ORDER BY 列有重复值,ROW_NUMBER() 会随机(或按物理存储顺序)分配序号,但通常不保证一致性。若需确定性顺序,可在 ORDER BY 中加一个唯一列(如 ORDER BY score DESC, id)。
语法结构(通用)
函数名() OVER (
PARTITION BY 分组列 -- 可选,分组内排名
ORDER BY 排序列 [ASC|DESC]
)
比如按班级分组排名:
RANK() OVER (PARTITION BY class_id ORDER BY score DESC)
如果你愿意,我也可以给你一个带 PARTITION BY 的实际例子来进一步说明它们三个在分组内的区别。