SQL 的 ROW_NUMBER()、RANK()、DENSE_RANK() 都属于窗口函数中的排名函数,作用是在分区内按 ORDER BY 给每行赋一个名次;三者的唯一本质差异是如何处理并列(ties)。据 Snowflake 官方排名函数表(ima 知识库引述):ROW_NUMBER 给每行唯一序号、并列时任意分配且不跳号;RANK 并列同名次且名次会跳号;DENSE_RANK 并列同名次且名次不跳号。Microsoft Learn 的 RANK 解析也印证:三者执行计划基本一致,差异仅在于”遇到相同排序值时,下一个名次是否计数”。
一、三者共同的骨架
排名函数都写在 OVER() 内,且必须有 ORDER BY——没有排序就没有”名次”可言;PARTITION BY 可选,用来限定”在哪个组内排名”。
函数名() OVER (PARTITION BY 分组列 ORDER BY 排序列)
据 ConversationalSQL 的强调,PARTITION BY 定义”同侪组”,ORDER BY 定义”什么算更好”。三者真正的分岔点,只在排序值出现重复的那一刻。
二、核心差异:一个并列例子看清
用 dbsyntax 的经典分数示例(95、90、90、85)一眼看懂:
| 分数 | ROW_NUMBER() | RANK() | DENSE_RANK() |
|---|---|---|---|
| 95 | 1 | 1 | 1 |
| 90(并列) | 2 | 2 | 2 |
| 90(并列) | 3 | 2 | 2 |
| 85 | 4 | 4 | 3 |
- ROW_NUMBER():无论是否并列,都给唯一连续整数。两个 90 分被强行拆成第 2、第 3,谁的 2 谁 的 3 由数据库任意决定。
- RANK():并列者同名次,但”占用了几个位置就跳过几个”。两个第 2 名之后,下一个直接是第 4 名(1,2,2,4)。
- DENSE_RANK():并列者同名次,且不跳号——两个第 2 名之后是紧接的第 3 名(1,2,2,3)。
Microsoft Learn 用同样的逻辑说明:DENSE_RANK 的最大值恰好等于”排序列的去重值个数”,因为它不把并列行计入间隔。
三、为什么 ROW_NUMBER 在并列时不可靠
Stack Overflow 的高赞解答指出,当 ORDER BY 列存在重复值时,ROW_NUMBER 给并列行分配的数是任意的、不确定的——同一查询两次执行可能给出不同顺序。要让结果稳定,必须加次级排序列人为打破平局:
SELECT *, ROW_NUMBER() OVER (
PARTITION BY region
ORDER BY revenue DESC, signup_date ASC, user_id ASC
) AS rn
FROM user_revenue;
ConversationalSQL 也强调:把 ORDER BY YearCreated, Title 写成多键排序,并列才会按 Title 一致拆分。
四、各自的典型用途
据 dbsyntax 的”何时选用”与 ConversationalSQL 的总结:
ROW_NUMBER():每组只要唯一一行。 如”每个地区取销售额前 5 的客户”,即使第 5 名有并列,也只取恰好 5 行,由引擎挑一个。
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY region ORDER BY revenue DESC) AS rn
FROM user_revenue
) t WHERE rn <= 5;
RANK():并列共享名次、允许跳号。 体育赛事排名最贴合——两人并列第二,下一位就是第四,真实反映”被挤掉的位置”。
DENSE_RANK():并列共享名次、不跳号。 适合定价档位、等级分层这类”名次必须连续”的场景——并列后紧接下一档,不会出现空洞。
五、执行层面的小知识
Microsoft Learn 指出,只要三者共用同一个 PARTITION BY 和 ORDER BY,查询计划里的 Sequence Project 算子可一次性同时算出三个值,性能上并无额外代价。真正的性能开销来自排序本身,可用 ORDER BY 列上的索引避免显式排序。
六、选型速查
- 要”每组恰好一行 / 连续唯一序号” →
ROW_NUMBER()(记得补次级排序键)。 - 要”并列同名次、名次可跳” →
RANK()。 - 要”并列同名次、名次连续” →
DENSE_RANK()。
常见问题(FAQ)
三者必须写 ORDER BY 吗?
必须。排名函数依赖排序才有名次含义;缺 ORDER BY 多数引擎会报错或语义不明。
ROW_NUMBER 并列时顺序会变吗?
会。ORDER BY 列有重复时分配任意,结果不确定;加次级键可固定顺序。
DENSE_RANK 最大值等于什么?
等于排序列的去重值个数,因为它并列不跳号,名次数正好对应不同值的个数。