使用 ROW_NUMBER 生成唯一序列
在每个分区内分配确定性的行号
使用 ROW_NUMBER 生成唯一序列 是 CoddyKit 上的免费 SQL Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Interview Prep 课程共包含 4 节课。
ROW_NUMBER 的保证
ROW_NUMBER() 会按照窗口中的 ORDER BY,为分区内的每一行分配一个从 1 开始的唯一且连续的整数。即使排序值相同,也不会有两行共享同一个编号。
正因为具有唯一性,面试官经常使用它来解决“每组选择一行”和去重问题。其他排名函数(RANK、DENSE_RANK)并不保证排序值相同时编号唯一。
最简 ROW_NUMBER 查询
最简单的形式是按照指定顺序为整个结果集编号。ROW_NUMBER 始终需要在 OVER 中使用 ORDER BY——否则编号会是任意的,而且大多数数据库引擎会拒绝执行或发出警告。
在这里,如果按入职日期降序排列,最近入职的员工就是第 1 行。
SELECT
name,
hire_date,
ROW_NUMBER() OVER (ORDER BY hire_date DESC) AS rn
FROM employees;在分区内编号
添加 PARTITION BY,即可在每个组内独立地为行编号。每遇到新的分区值,计数器都会重置为 1。
在示例中,每个部门都有自己的 1、2、3 序列,并按薪资排序。每个部门中收入最高的员工都是编号 1,这正是“每组取前 N 个”模式的起点。
SELECT
department,
name,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_rn
FROM employees;ROW_NUMBER 与并列:不存在并列编号
面试中的关键知识点:当两行的排序值相等时,ROW_NUMBER 仍会为它们分配不同的编号。除非添加用于打破并列的列,否则哪一行获得较小编号是不确定的。
RANK会为并列的行分配相同的排名。ROW_NUMBER会在并列行之间任意确定顺序。
为了让结果可复现,请始终在 ORDER BY 中添加唯一的并列决胜列。
SELECT
name,
salary,
ROW_NUMBER() OVER (
ORDER BY salary DESC, employee_id -- employee_id breaks ties deterministically
) AS rn
FROM employees;确定性并列决胜规则
如果您的 ORDER BY 不是严格排序(没有任何列组合是唯一的),即使数据相同,每次运行时行编号也可能发生变化。面试官经常在分页和“最新记录”问题中设置这个陷阱。
经验法则是:只要结果必须稳定,就应将主键或其他唯一列追加为最后的排序键。
-- Unstable: many rows can share the same created_at
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)
-- Stable: id guarantees a single deterministic winner
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC)示例:每位客户的最新订单
这是一个经典问题:“返回每位客户最近的订单。”先按日期降序为每位客户的订单编号,然后在外层查询中保留 rn = 1。
由于 ROW_NUMBER 保证每个分区中恰好有一行的编号为 1,因此您会准确得到每位客户的一行;即使两个订单的时间戳相同,也不会产生重复行,因为并列决胜列会解决这个问题。
SELECT customer_id, order_id, order_date, amount
FROM (
SELECT
customer_id, order_id, order_date, amount,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC, order_id DESC
) AS rn
FROM orders
) t
WHERE rn = 1;示例:对行进行去重
ROW_NUMBER 是最简洁的去重工具。先按定义重复项的列进行分区,再依据偏好规则排序,最后保留 rn = 1。
在这里,我们将电子邮件地址相同的行视为重复项,并保留最早创建的那一行。所有 rn > 1 的行都是重复项,您可以将其删除或忽略。
SELECT id, email, created_at
FROM (
SELECT
id, email, created_at,
ROW_NUMBER() OVER (
PARTITION BY email
ORDER BY created_at ASC, id ASC
) AS rn
FROM users
) t
WHERE rn = 1;使用 ROW_NUMBER 实现分页
在 OFFSET/FETCH 尚未普及之前,分页主要依靠 ROW_NUMBER 实现;如今它仍用于 SQL Server 和许多 ETL 作业。先按稳定的顺序为行编号,再在外层查询中筛选编号范围。
面试时请注意:只有排序具有确定性,分页才是正确的;否则同一行可能出现在两个页面中,或者完全被跳过。
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at, id) AS rn
FROM articles
) t
WHERE rn BETWEEN 21 AND 40; -- page 2, 20 per page为数据重新排序指定序号
有时您只需要一个连续索引,例如在排序后重新为行编号、创建 1..N 标签,或将行与生成的序列配对。对所需顺序使用 ROW_NUMBER,即可生成没有间隔的整齐连续序列。
这也是为任意无序集合指定稳定位置,以便之后进行连接的方法。
SELECT
ROW_NUMBER() OVER (ORDER BY score DESC, player_id) AS leaderboard_position,
player_id,
score
FROM scores;ROW_NUMBER 与 COUNT 对比:“第 N 行”
当题目要求查找“最近的第 3 个订单”或“第 2 行”时,请使用 ROW_NUMBER,并在外层查询中筛选精确的编号值。由于编号唯一,rn = 3 会准确返回一行。
请与 RANK 对比:如果您要查找第 2 个不同的值(例如包括并列薪资在内的第二高薪资),那么 ROW_NUMBER 就不是正确工具——这需要使用 DENSE_RANK,下一课会介绍它。
SELECT order_id, order_date
FROM (
SELECT order_id, order_date,
ROW_NUMBER() OVER (ORDER BY order_date DESC, order_id DESC) AS rn
FROM orders
) t
WHERE rn = 3; -- exactly the 3rd most recent order易错点回顾
在面试压力下,请牢记以下几点:
ROW_NUMBER在分区内始终唯一且连续。- 它需要在
OVER中使用ORDER BY;没有唯一的并列决胜列时,出现并列会导致结果不确定。 - 不能在
WHERE中对它进行筛选——请将其包在子查询或 CTE 中。 - 使用它实现每组一行、去重和分页;当并列项必须共享编号时,请使用
DENSE_RANK。
快速检查
当行在 ORDER BY 值上并列时,ROW_NUMBER 会如何处理它们?
回顾:确定性排序编号
ROW_NUMBER 是在分区内进行唯一、连续编号的工具。您已经学会了如何:
- 为整个集合以及每个分区内的组进行编号。
- 添加唯一的并列决胜列,以获得确定且可复现的结果。
- 通过在外层查询中筛选
rn,解决每个键的最新行、去重、分页和第 N 行问题。
接下来,您将了解 RANK 和 DENSE_RANK 如何有意地为并列项分配相同的编号,以及它们在编号间隔方面有何不同。
常见问题解答
「使用 ROW_NUMBER 生成唯一序列」课时是免费的吗?
是的 — 「使用 ROW_NUMBER 生成唯一序列」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 SQL Interview Prep 课程共包含 4 节课。
「使用 ROW_NUMBER 生成唯一序列」这节课中我会学到什么?
在每个分区内分配确定性的行号 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「使用 ROW_NUMBER 生成唯一序列」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Interview Prep 课中编写并运行代码吗?
能。每节 SQL Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- OVER、PARTITION BY 与 ORDER BY
- 使用 ROW_NUMBER 生成唯一序列
- 值相同时比较 RANK 与 DENSE_RANK
- 按窗口结果筛选