0Pricing
SQL Interview Prep · 课时

使用 DENSE_RANK 查找第 N 高值

推广到第 N 个不重复值,并处理重复项

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

推广到第 N 高

当您能够找到第二高薪资后,面试官通常会立即追问:“现在请给出第 N 高薪资。”最简洁、最有说服力的答案是使用 DENSE_RANK。

模式始终相同:按降序为不重复薪资排名,然后过滤出排名等于 N 的行。由于这个逻辑不会随 N 改变,因此同一种方法就能回答整类问题。

我们将逐步构建这种方法,处理并列和重复项,并讨论为什么对于“不重复值”语义,DENSE_RANK 是正确的排名函数。

核心模板

下面是可复用的“第 N 高”模板。将常量替换为面试官要求的 N。

您需要在内层查询中计算 DENSE_RANK(窗口函数不能写在 WHERE 中),然后在外层过滤 rnk = N。如果要查找第三高薪资,请将过滤条件设为 rnk = 3。

SELECT salary AS nth_highest
FROM (
  SELECT salary,
         DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employee
) ranked
WHERE rnk = 3;

DENSE_RANK 如何为不重复值编号

DENSE_RANK 为相等的值分配相同排名,并且后续排名不会出现间隔。这正是面试官所说的“第 N 个不重复值”的定义。

对于薪资 800、800、600、600、400:

  • 800 -> 排名 1
  • 600 -> 排名 2
  • 400 -> 排名 3

因此第三高薪资是 400,尽管总共有五行。重复项会自动合并到一个排名中。

为什么 RANK 会给出错误答案

如果换成 RANK,答案就会出错。RANK 留下的间隔大小取决于并列的数量。

对于 800、800、600、600、400:

  • 800、800 -> 排名 1(共两个)
  • 600、600 -> 排名 3(存在间隔,没有排名 2)
  • 400 -> 排名 5

过滤 rnk = 3 会返回 600,而过滤 rnk = 2 则不会返回任何结果。除非面试官明确要求竞赛排名,否则对于“第 N 个不重复薪资”,正确的选择是 DENSE_RANK。

为什么 ROW_NUMBER 在这里也不正确

ROW_NUMBER 为每一行分配唯一编号,完全忽略并列。对于 800、800、600、600、400,它会生成 1、2、3、4、5。

因此 rn = 3 会返回 600,但 rn = 2 会返回重复的 800,而不是第二个不重复值。ROW_NUMBER 回答的是“第 N 个行”,而不是“第 N 个不重复值”。

只有当问题确实要求某个特定行时,才应使用 ROW_NUMBER,例如去重,或每组取前 N 个且每组只保留一行。

SELECT salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn
FROM employee;

安全地将 N 参数化

在实际代码中,您不会把排名写死。请将 N 作为参数传入并与之比较。窗口定义保持不变,只有外层过滤条件需要参数化。

这也让您可以返回排名为 N 的所有并列薪资:由于 DENSE_RANK 会为并列值共享同一个排名,如果多名员工的薪资并列为第 N 个不重复薪资,WHERE rnk = N 可能会返回多行,而这通常正是我们想要的结果。

SELECT id, salary
FROM (
  SELECT id, salary,
         DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
  FROM employee
) ranked
WHERE rnk = :n;

相关计数的推广

窗口函数之前的方法同样可以推广:当严格高于某个薪资的不重复薪资恰好有 N - 1 个时,该薪资就是第 N 高的不重复薪资。

对于第三高薪资,要求严格高于它的不重复薪资恰好有 2 个。这种方法适用于不支持窗口函数的旧版数据库引擎,但扩展性较差,因为内层计数会针对每一行外层数据重新运行。

SELECT DISTINCT salary AS nth_highest
FROM employee e
WHERE (
  SELECT COUNT(DISTINCT e2.salary)
  FROM employee e2
  WHERE e2.salary > e.salary
) = 2;

面试官会要求的 MySQL 函数形式

LeetCode 风格的“第 N 高薪资”问题通常要求编写一个返回单个值的存储函数。函数体只是将 DENSE_RANK 模板包装起来,以返回一个薪资值。

面试时不必死记确切的函数语法,但了解在不重复薪资上使用 LIMIT N-1, 1 是 MySQL 中简洁的惯用写法,仍然很有价值。

SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 2;  -- N = 3, so OFFSET N-1

示例:第四高薪资

薪资:1000、900、900、700、500、500、300。

使用 DENSE_RANK 按降序排列不重复薪资:

  • 1000 -> 1
  • 900 -> 2
  • 700 -> 3
  • 500 -> 4
  • 300 -> 5

第四高薪资是 500。请注意,两行 500 的排名都是 4,因此如果同时选择员工编号,过滤 rnk = 4 会返回两名薪资为 500 的员工。

性能注意事项

这些方法在数据量扩大时表现如何?

  • DENSE_RANK:对数据进行一次排序,然后过滤。效率较高,并且查询规划器可以利用薪资列上的索引进行排序。
  • 相关计数:可能达到 O(n²),因为内层聚合会针对每一行运行。在大型表上应避免使用。
  • LIMIT/OFFSET:对于较小的 N 速度很快,但仍然必须排序;较大的偏移量会扫描并丢弃许多行。

优先使用 DENSE_RANK,通常不会出错。

需要提及的边界情况

优秀的候选人会在被问到之前主动指出边界情况:

  • N 大于不重复薪资的数量:过滤条件匹配不到任何行,结果为空。第 4 课将介绍如何强制返回单个 NULL。
  • 排名 N 处存在并列:DENSE_RANK 会返回所有并列的员工;请先确定这是否符合需求。
  • N = 1:模板仍然有效,并会返回最大值。

快速测验

应用“第 N 高”模板。

总结

查找第 N 高薪资有一个首选答案:在子查询中使用 DENSE_RANK() OVER (ORDER BY salary DESC) 为不重复薪资排名,然后过滤 WHERE rnk = N。

  • DENSE_RANK 表示“第 N 个不重复值”,并列值共享排名且没有间隔。
  • RANK 会产生间隔;ROW_NUMBER 统计的是行,而不是值。
  • 相关计数 = N-1 技巧表达了相同的思路,不使用窗口函数,但扩展性较差。

请始终指出“N 超出可用值数量”这一边界情况,下一节将解决它。

常见问题解答

「使用 DENSE_RANK 查找第 N 高值」课时是免费的吗?

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

「使用 DENSE_RANK 查找第 N 高值」这节课中我会学到什么?

推广到第 N 个不重复值,并处理重复项 你通过在浏览器中直接运行的动手代码来练习 SQL Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

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

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

「使用 DENSE_RANK 查找第 N 高值」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. 第二高薪资的五种写法
  2. 使用 DENSE_RANK 查找第 N 高值
  3. 各部门最高薪资者
  4. 不存在第 N 个值时返回 NULL
← 返回 SQL Interview Prep