处理前 N 名中的并列值
了解何时使用 RANK 或 DENSE_RANK,以便包含所有并列行
处理前 N 名中的并列值 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。
并列追问
当您用 ROW_NUMBER 完成每组前 N 名的问题后,面试官可能会继续追问:“如果截止位置上有两名员工的薪资完全相同,是否应该将两人都包括在内?”
这能区分出只是记住了一个查询的候选人,以及真正理解排名语义的候选人。答案取决于在 ROW_NUMBER、RANK 和 DENSE_RANK 之间进行选择。本课将说明每个函数分别对应什么业务意图。
ROW_NUMBER 会隐藏并列
ROW_NUMBER 会为每一行分配严格唯一的整数。即使两行在排序列上并列,其中一行也会获得 2,另一行获得 3,顺序可能是任意的。
结果是:WHERE rn <= 3 筛选条件会返回恰好 3 行,并且可能悄悄排除本应被包括的并列行。如果您确实需要固定数量的行,这没有问题;但如果所有并列行都应符合条件,这样做就是错误的。
-- Salaries: 100, 90, 90, 80
-- ROW_NUMBER -> 1, 2, 3, 4 (the two 90s get 2 and 3 arbitrarily)
ROW_NUMBER() OVER (ORDER BY salary DESC)RANK 会留下间隔
RANK 会为并列行分配相同的排名,然后为计入并列而跳过后续排名。两行并列第 2 名时,它们都是 2,下一行会直接跳到第 4 名,而不是第 3 名。
当您需要“前 3 个名次”,且一个名次可以由多人占据时,请使用 RANK。这类似于真实的竞赛排名:两名银牌得主意味着没有铜牌得主。
-- Salaries: 100, 90, 90, 80
-- RANK -> 1, 2, 2, 4 (rank 3 is skipped)
RANK() OVER (ORDER BY salary DESC)DENSE_RANK 不会留下间隔
DENSE_RANK 同样会为并列行分配相同的排名,但之后不会跳过排名。两行并列第 2 名时,它们都是 2,接下来的不同值排名为 3。
当问题关注的是不同值时,请使用 DENSE_RANK,例如“薪资最高的 3 个不同等级”或“前 3 个价格层级”。它统计的是唯一值,而不是行数。
-- Salaries: 100, 90, 90, 80
-- DENSE_RANK -> 1, 2, 2, 3 (no gap)
DENSE_RANK() OVER (ORDER BY salary DESC)并列对比
对于薪资列表 100、90、90、80,三个函数的结果如下:
- ROW_NUMBER:1、2、3、4
- RANK:1、2、2、4
- DENSE_RANK:1、2、2、3
这张表值得熟练记忆。面试官可能会直接要求您为给定列表填表,而正确掌握是否存在间隔正是考察重点。
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn,
RANK() OVER (ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees;使用 RANK 包括所有并列行
如果要回答“前 3 名,但包括截止位置上所有并列者”,请筛选 RANK() <= 3。如果两人并列第 3 名,两人都会出现,因此结果可能包含超过 3 行。
这正是“前 3 个名次”而不是“任意 3 行”这类问法背后的意图。请明确说明,出现并列时行数可能超过 N。
WITH ranked AS (
SELECT name, department, salary,
RANK() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rnk
FROM employees
)
SELECT name, department, salary, rnk
FROM ranked
WHERE rnk <= 3
ORDER BY department, rnk;使用 DENSE_RANK 获取前 3 个不同等级
当需求是“薪资最高的 3 个不同等级”,并且希望包括处于这三个等级的每一名员工时,请使用 DENSE_RANK() <= 3。
这可能返回很多行:前三个薪资等级中的所有员工,无论这些等级中有多少人。关键在于,DENSE_RANK 统计的是不同值,因此筛选条件选出的是值的层级,而不是固定数量的行。
WITH ranked AS (
SELECT name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees
)
SELECT name, salary, drnk
FROM ranked
WHERE drnk <= 3
ORDER BY drnk;选择指南
根据题目措辞选择函数:
- “恰好 N 行”或分页 →
ROW_NUMBER(添加并列处理键)。 - “前 N 个名次,并列者共享名次” →
RANK。 - “前 N 个不同值/等级/层级” →
DENSE_RANK。
如果需求不明确,请询问:“如果截止位置出现并列,我应该包括所有并列行,还是将结果限制为 N 行?”提出这个澄清问题本身就能获得认可。
并列处理键仍然重要
即使使用 RANK 或 DENSE_RANK,添加次要的 ORDER BY 键也能控制并列行的显示顺序,并使输出保持稳定。它不会改变哪些行符合条件,但会让结果可复现。
使用 ROW_NUMBER 时,并列处理键至关重要,因为它会决定哪一行能够通过 rn 截止条件。
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC, name ASC
) AS rnk完整示例:得分最高者
一个 game_scores 表包含 player 和 score。面试官想要“得分排名前 2 的名次中的所有选手”。如果第一名出现并列,就会有两名选手并列第 1 名,接下来的不同得分为第 2 名。
“名次”且并列后不跳号,说明应使用 DENSE_RANK。如果面试官说的是“前 2 个有间隔的排名”,您就应改用 RANK。请仔细听清确切措辞。
WITH r AS (
SELECT player, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS pos
FROM game_scores
)
SELECT player, score, pos
FROM r
WHERE pos <= 2
ORDER BY pos, player;需要避免的常见错误
面试官会留意以下陷阱:
- 当题目要求包括并列行时使用
ROW_NUMBER,导致符合条件的行被悄悄排除。 - 混淆
RANK和DENSE_RANK是否会产生间隔。 - 忘记
RANK/DENSE_RANK筛选可能返回超过 N 行,然后错误地声称结果有问题。 - 当问题要求每组前 N 名时遗漏
PARTITION BY,结果变成对整张表排名。
快速检查
请根据所描述的并列处理方式选择正确的排名函数。
回顾:并列处理
三个函数,三种并列处理方式:
- ROW_NUMBER:始终唯一,恰好返回 N 行,并列时任意打破平局。
- RANK:并列项共享排名,随后会出现排名间隔。
- 密集排名:并列项共享排名,不产生间隔,按不同值计数。
请根据问题的措辞进行选择;含义不明确时请提出澄清问题,并记住,RANK/DENSE_RANK筛选结果按设计可能多于 N 行。
常见问题解答
「处理前 N 名中的并列值」课时是免费的吗?
是的 — 「处理前 N 名中的并列值」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。
「处理前 N 名中的并列值」这节课中我会学到什么?
了解何时使用 RANK 或 DENSE_RANK,以便包含所有并列行 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「处理前 N 名中的并列值」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Interview Prep 课中编写并运行代码吗?
能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 ROW_NUMBER 获取各组前 N 行
- 处理前 N 名中的并列值
- 安全地去除重复行
- 保留每个键对应的最新行