用 limit + offset 实现第 n 高需先降序排序,offset 为 n-1;row_number() 保证唯一编号;dense_rank() 处理并列;oracle 旧版须嵌套子查询配合 rownum。

用 LIMIT + OFFSET 实现第 N 高(MySQL / PostgreSQL)
MySQL 和 PostgreSQL 支持 LIMIT 和 OFFSET,这是最直接的方式。但要注意:第 1 高是 OFFSET 0,第 N 高对应 OFFSET N-1,且必须先按降序排序,否则“最高”无意义。
常见错误是漏掉 ORDER BY 或把 OFFSET 写成 N:
-
SELECT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 2→ 第 3 高(不是第 2 高) - 没写
ORDER BY salary DESC→ 结果随机,不保证“最高” - 对空结果集不做处理 → 返回空行,而非 NULL 或提示
实际写法示例(查第 3 高 salary):
SELECT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 2;
用窗口函数 ROW_NUMBER()(SQL Server / PostgreSQL / Oracle / MySQL 8.0+)
ROW_NUMBER() 更可靠,尤其当存在重复值时逻辑明确:它强制给每行唯一编号(即使 salary 相同,也会排 1、2、3…),适合“严格第 N 条记录”。
注意点:
-
ROW_NUMBER()必须配合OVER (ORDER BY ...),且ORDER BY要写清楚方向(DESC才是“高”) - 子查询必须命名(如
AS ranked),否则外层WHERE rn = N会报错“unknown column” - 如果 N 超出总行数,结果为空 —— 若需返回 NULL,得套一层
COALESCE或用LEFT JOIN补位(较复杂,一般业务层处理)
示例(第 2 高 salary,含去重后并列处理):
SELECT salary FROM ( SELECT salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn FROM employees ) AS ranked WHERE rn = 2;
用 DENSE_RANK() 处理“并列第 N 高”
如果业务要求“salary=9000 和 9000 并列第 1 高,那么 8500 是第 2 高”,就不能用 ROW_NUMBER(),而要用 DENSE_RANK() —— 它跳过重复后的编号,不产生间隙。
典型误用场景:
- 用
RANK():重复值占位,比如两个 9000 → 都是 rank 1,下一个 8500 就是 rank 3,中间跳了 2 - 混淆
DENSE_RANK()和ROW_NUMBER()的语义,导致“第 2 高”查不到数据 - 忘记去重字段:若表里有 id、name 等非排序字段,
DENSE_RANK()仍会为每行计算,可能返回多行;应先SELECT DISTINCT salary再开窗
安全写法(查并列第 2 高的 salary 值):
SELECT DISTINCT salary
FROM (
SELECT DISTINCT salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
FROM employees
) AS t
WHERE dr = 2;
Oracle 用 ROWNUM(仅适用于老版本或简单场景)
Oracle 12c 以前不支持 LIMIT 或标准窗口函数,常用 ROWNUM 嵌套子查询。但它必须在排序后立即限制,否则 ROWNUM 是物理读取顺序,不是逻辑排序后顺序。
致命陷阱:
- 写成
SELECT * FROM employees WHERE ROWNUM = N→ 永远只返回 0 行(因为 ROWNUM 从 1 开始分配,不可能等于 >1 的数) - 没套两层子查询:外层
ROWNUM必须作用于已排序的内层结果 - Oracle 对 NULL 的排序默认是最大值(
NULLS LAST),若 salary 允许 NULL,可能干扰“第 N 高”结果
正确结构(查第 4 高 salary):
SELECT salary FROM (
SELECT salary, ROWNUM rn FROM (
SELECT DISTINCT salary FROM employees ORDER BY salary DESC
)
WHERE ROWNUM <p>窗口函数和 <code>LIMIT/OFFSET</code> 是目前主流解法,<code>ROWNUM</code> 方式容易出错且可读性差,新项目尽量避免。另外,N 为变量时(如存储过程参数),不同数据库的参数占位符不同(<code>?</code>、<code>$1</code>、<code>@n</code>),拼接前务必确认语法边界。</p>










