0Pricing
SQL Interview Prep · 课时

使用 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. OVER、PARTITION BY 与 ORDER BY
  2. 使用 ROW_NUMBER 生成唯一序列
  3. 值相同时比较 RANK 与 DENSE_RANK
  4. 按窗口结果筛选
← 返回 SQL Interview Prep