0Pricing
Coding Interview Prep · 课时

可靠地返回前 N 行

了解没有平局裁决字段时,ORDER BY 加 LIMIT 为何可能产生不确定结果

可靠地返回前 N 行 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。

前 N 项查询中的隐藏错误

“请给我薪资最高的前 5 名员工”看起来很简单:ORDER BY salary DESC LIMIT 5。但面试官可能会故意设置陷阱。如果处于边界位置的六个人薪资相同,该怎么办?如果有很多行并列,又该怎么办?

核心问题在于确定性:当排序键存在并列值时,LIMIT 会任意截取结果,实际返回的行可能在不同运行之间发生变化。本课将帮助您可靠地实现前 N 项查询。

ORDER BY + LIMIT 为何可能不具确定性

假设排名第 4、第 5 和第 6 的薪资都为 50000。ORDER BY salary DESC LIMIT 5 必须准确返回 5 行,因此它会保留并列的三行中的两行并丢弃一行,但具体保留哪两行是未定义的。

如果运行两次查询,或者优化器更改了执行计划,您可能会得到不同的人员。面试官希望您发现的错误,就是这种不确定性。

SELECT name, salary
FROM employees
ORDER BY salary DESC
LIMIT 5;

修复方法 1:添加唯一决胜字段

最简单的修复方法是,在排序末尾添加一个唯一列,通常是主键,从而使排序顺序完整。这样,任意两行在完整键上都不会相同,因此截取结果具有确定性且可复现。

这不会改变结果中出现哪些薪资,但会让并列行之间的选择在多次运行中保持稳定。

SELECT id, name, salary
FROM employees
ORDER BY salary DESC, id ASC
LIMIT 5;

修复方法 2:使用 WITH TIES 包含所有并列行

有时需求是“包含所有与边界值并列的行”,而不是恰好返回 N 行。标准 SQL 和 SQL Server 提供了 WITH TIES,它会返回与最后一行的 ORDER BY 值相同的额外行。

如果第 5 高的薪资由三个人共享,该查询会返回 7 行。请注意,WITH TIES 要求同时使用 ORDER BY。

SELECT name, salary
FROM employees
ORDER BY salary DESC
FETCH FIRST 5 ROWS WITH TIES;

先明确需求

开始编码前,请询问面试官:“如果边界位置出现并列,您希望恰好返回 N 行,还是返回所有并列行?”这个澄清问题本身就能体现您的资深程度。

  • 恰好 N 行且结果稳定:添加唯一的决胜字段。
  • 包含所有并列行:使用 WITH TIES 或 RANK。
  • 不同的值:使用 DENSE_RANK。

可移植的窗口函数方法

许多数据库引擎不支持 WITH TIES。一种可移植且功能强大的模式是在子查询或 CTE 中使用排名窗口函数,然后根据排名进行筛选。ROW_NUMBER 会按照确定的排序键准确返回 N 行。

您必须将窗口函数包在外层查询中,因为不能在 WHERE 中直接引用它。

SELECT name, salary
FROM (
  SELECT name, salary,
         ROW_NUMBER() OVER (ORDER BY salary DESC, id ASC) AS rn
  FROM employees
) ranked
WHERE rn <= 5;

使用 RANK 保留并列行

如果希望保留所有并列行,并在排名中留下间隔,请将 ROW_NUMBER 换成 RANK。如果三行并列第 4 名,它们都会得到第 4 名,下一名则是第 7 名。

随后筛选 rank <= 5,就会返回薪资排名前五个位置中的所有行,包括并列行。

SELECT name, salary
FROM (
  SELECT name, salary,
         RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employees
) ranked
WHERE rnk <= 5;

使用 DENSE_RANK 获取前 N 个不同值

“薪资前 3 个层级”(而不是薪资最高的 3 个人)表示要获取不同的值。DENSE_RANK 会为并列值分配相同的排名,并且不会跳过编号,因此 dense_rnk <= 3 会返回薪资属于前三个最高不同薪资值的所有人。

了解哪种排名函数对应哪种问题表述,是面试中区分候选人水平的经典要点。

SELECT name, salary
FROM (
  SELECT name, salary,
         DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
  FROM employees
) ranked
WHERE drnk <= 3;

前 1 项的特殊情况

对于最高的一行,ORDER BY ... LIMIT 1 可以正常工作,但仍然可能遗漏并列行。如果希望返回所有达到最高值的行,可以与子查询中的最大值进行比较,或者使用 RANK() = 1。

使用最大值子查询的写法简洁,并且适用于任何数据库方言。

SELECT name, salary
FROM employees
WHERE salary = (SELECT MAX(salary) FROM employees);

比较各种方法

可靠获取前 N 项时,各种工具的适用场景总结如下:

  • LIMIT + 唯一决胜字段:恰好返回 N 行,结果稳定,最简单。
  • FETCH ... WITH TIES:返回恰好 N 行,并包含边界并列行,符合标准 SQL。
  • ROW_NUMBER:恰好返回 N 行,结果确定,完全可移植。
  • RANK:返回前 N 个排名位置中的所有行,包括并列行。
  • DENSE_RANK:返回前 N 个不同的值。

按组获取前 N 项预览

窗口函数方法还可以自然地推广。添加 PARTITION BY,即可获取每个组中的前 N 项,例如每个部门中薪资最高的前 2 个人。分组后仍然使用相同的 rn <= n 条件进行筛选。

按组获取前 N 项是实际面试中出现频率最高的问题之一,而它正是建立在您刚刚学过的模式之上。

SELECT department, name, salary
FROM (
  SELECT department, name, salary,
         ROW_NUMBER() OVER (PARTITION BY department
                            ORDER BY salary DESC, id ASC) AS rn
  FROM employees
) ranked
WHERE rn <= 2;

快速检查

将需求与正确的函数进行匹配。

回顾

要可靠地返回前 N 项:

  • 当排序键存在并列值时,单独使用 ORDER BY ... LIMIT 不具确定性。
  • 添加唯一的决胜字段,以稳定地返回恰好 N 行。
  • 使用 WITH TIES 或 RANK 保留边界并列行。
  • 使用 DENSE_RANK 获取前 N 个不同的值。
  • 始终确认面试官希望得到恰好 N 行,还是所有并列行。

常见问题解答

「可靠地返回前 N 行」课时是免费的吗?

是的 — 「可靠地返回前 N 行」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。

「可靠地返回前 N 行」这节课中我会学到什么?

了解没有平局裁决字段时,ORDER BY 加 LIMIT 为何可能产生不确定结果 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Coding Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「可靠地返回前 N 行」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Coding Interview Prep 课中编写并运行代码吗?

能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 多列排序与 NULL 位置
  2. LIMIT、OFFSET 与 FETCH FIRST
  3. 可靠地返回前 N 行
  4. 按表达式和别名排序
← 返回 Coding Interview Prep