值相同时比较 RANK 与 DENSE_RANK
掌握经典问题:值相同时,有间隔排名与无间隔排名的区别
值相同时比较 RANK 与 DENSE_RANK 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
三个排名函数并列比较
SQL 提供了三个排名窗口函数,而窗口函数最常见的问题就是它们如何处理并列:
- ROW_NUMBER——始终唯一;并列项会获得任意的不同编号。
- RANK——并列项共享排名,之后的排名会跳过编号(产生间隔)。
- DENSE_RANK——并列项共享排名,之后的排名不会跳过编号(没有间隔)。
能够立即准确地说出这一差异,是面试中可靠的能力信号。
具体示例表
假设有五个按降序排列的分数:100、90、90、80、70。看看每个函数会如何为它们编号。
- ROW_NUMBER:1、2、3、4、5
- RANK:1、2、2、4、5(并列后跳过 3)
- DENSE_RANK:1、2、2、3、4(不跳过)
请记住 100/90/90/80/70 这个示例,它可以帮助您当场回答相关问题。
SELECT
score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS rownum,
RANK() OVER (ORDER BY score DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY score DESC) AS dense
FROM scores;RANK 如何计算间隔
RANK 为每一行分配一个排名,其值等于排序中严格排在该行之前的行数加一。两个分数为 90 的行之前都只有一行(100),因此它们的排名都是 2。下一个更低的值之前有三行,因此排名会跳到 4。
这就是为什么 RANK 类似于竞赛排名:并列的银牌得主,却没有铜牌。
-- scores 100,90,90,80 -> RANK = 1,2,2,4
SELECT score, RANK() OVER (ORDER BY score DESC) AS rnk
FROM scores;DENSE_RANK 如何避免间隔
DENSE_RANK 为每一行分配一个排名,其值等于当前值之前不同值的数量加一。90 并列之后,80 之前只有两个不同的值(100 和 90),因此它的排名是 3,没有间隔。
当您关心不同值的位置时,请使用 DENSE_RANK,例如查找“第二高薪资”时,并列值应只计算一次。
-- scores 100,90,90,80 -> DENSE_RANK = 1,2,2,3
SELECT score, DENSE_RANK() OVER (ORDER BY score DESC) AS dense
FROM scores;如何选择每个函数
面试官喜欢听到的快速判断指南:
- 需要每个名次恰好一行(最新记录、分页)?→ ROW_NUMBER。
- 需要并列者并列且跳号正确的排名(体育排名、包含并列项的前 3 名)?→ RANK。
- 需要第 N 个不同值(第 N 高的薪资)?→ DENSE_RANK。
错误的选择会改变返回的行,因此请清楚说明选择理由。
在分区内排名
这三个函数都接受 PARTITION BY,可以在每个分组内部独立排名。每个分区都会从排名 1 重新开始。
这里每个部门都按照薪资为自己的员工排名,并使用 DENSE_RANK 处理并列。每个部门中收入最高的员工排名为 1,薪资相同的员工共享同一个名次。
SELECT
department,
name,
salary,
DENSE_RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS salary_rank
FROM employees;示例:包含并列项的前 3 名
“返回每个部门薪资最高的前 3 名;如果有多人并列第三名,也要全部包含。”这要求使用 RANK 或 DENSE_RANK,而不是 ROW_NUMBER。
使用 DENSE_RANK <= 3 会保留前三个不同的薪资等级,以及处于这些等级的所有员工——这正是“包含并列项”的含义。
SELECT department, name, salary, salary_rank
FROM (
SELECT department, name, salary,
DENSE_RANK() OVER (
PARTITION BY department ORDER BY salary DESC
) AS salary_rank
FROM employees
) t
WHERE salary_rank <= 3;示例:第二高的薪资
这是最著名的数据库查询面试题。稳妥的答案是使用 DENSE_RANK,这样最高薪资重复时,不会把真正的第二个值挤出结果。
如果有三个人获得最高薪资,DENSE_RANK = 2 仍能正确找出下一个不同的薪资;而 ROW_NUMBER = 2 只会返回另一个最高薪资者。
SELECT DISTINCT salary AS second_highest
FROM (
SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
FROM employees
) t
WHERE dr = 2;RANK 与 DENSE_RANK:第 N 高值
对于第 N 高值问题,为什么选择 DENSE_RANK 而不是 RANK?因为 RANK 会留下跳号。薪资为 100、100、90 时,90 的 RANK = 3,但 DENSE_RANK = 2。
如果问题要求“第二高的不同薪资”,筛选 RANK = 2 会返回空结果,而 DENSE_RANK = 2 能正确返回 90。请始终将“第 N 个不同值”对应到 DENSE_RANK。
-- salaries 100,100,90
-- RANK: 1,1,3 -> WHERE rank = 2 returns NOTHING
-- DENSE_RANK: 1,1,2 -> WHERE dense_rank = 2 returns 90跨数据库方言说明
好消息是:ROW_NUMBER、RANK 和 DENSE_RANK 都符合结构化查询语言标准,在 PostgreSQL、微软数据库服务器、甲骨文数据库、MySQL 8+、BigQuery 和雪花数据库中行为一致。上面介绍的并列规则是通用的。
还有一个相关函数 PERCENT_RANK,它基于 RANK 给出从 0 到 1 的相对位置——如果面试官问到百分位排名,值得提及。
常见错误
请注意以下问题:
- 使用
ROW_NUMBER处理“第 N 高值”,从而遗漏并列的行。 - 问题要求不同值时使用
RANK,然后因为跳号而得到空结果。 - 忘记在
OVER中使用ORDER BY——没有它,排名就没有意义。 - 直接在
WHERE中筛选排名,而不是在外层查询中筛选。
快速检查
应用并列规则。
回顾:有跳号与无跳号
现在您可以立即选择正确的排名函数:
- ROW_NUMBER:编号唯一,不共享名次。
- RANK:并列者共享名次,之后会跳号(1、2、2、4)。
- DENSE_RANK:并列者共享名次,但不会跳号(1、2、2、3)。
- “第 N 个不同值”→
DENSE_RANK;“包含并列项的前 N 名”→RANK/DENSE_RANK;“每个名次一行”→ROW_NUMBER。
接下来:如何真正根据窗口结果进行筛选,因为您不能在 WHERE 中这样做。
常见问题解答
「值相同时比较 RANK 与 DENSE_RANK」课时是免费的吗?
是的 — 「值相同时比较 RANK 与 DENSE_RANK」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「值相同时比较 RANK 与 DENSE_RANK」这节课中我会学到什么?
掌握经典问题:值相同时,有间隔排名与无间隔排名的区别 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「值相同时比较 RANK 与 DENSE_RANK」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- OVER、PARTITION BY 与 ORDER BY
- 使用 ROW_NUMBER 生成唯一序列
- 值相同时比较 RANK 与 DENSE_RANK
- 按窗口结果筛选