ROW_NUMBER() 是窗口函数,必须配合 OVER 子句使用,且至少包含 PARTITION BY 和 ORDER BY;直接 SELECT , ROW_NUMBER() 会报错;正确用法如 SELECT , ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn FROM products。

ROW_NUMBER() 必须配合窗口 PARTITION BY 和 ORDER BY 使用
单独写 ROW_NUMBER() 会报错,因为它是个窗口函数,不支持在没有定义窗口范围的情况下调用。常见错误是直接写 SELECT *, ROW_NUMBER() FROM t,MySQL 8.0+、PostgreSQL、SQL Server 都会提示 “window function requires OVER clause”。
正确写法必须包含 OVER 子句,且至少要有 PARTITION BY(分组)和 ORDER BY(组内排序):
SELECT *,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn
FROM products;这里按 category 分组,每组内按 sales 降序排,rn = 1 就是该类销量最高的那条记录。
取每组前 N 条要嵌套子查询或 CTE,不能直接 WHERE rn
WHERE 子句在逻辑上早于窗口函数执行,所以不能在同一个 SELECT 层级里用 WHERE rn ——会提示 “unknown column ‘rn’”。必须把带 <code>ROW_NUMBER() 的查询作为子查询或 CTE,再在外层过滤:
- MySQL / PostgreSQL / SQL Server 均支持 CTE 写法(推荐,可读性好):
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employees
)
SELECT * FROM ranked WHERE rn <= 3;- 若数据库不支持 CTE(如旧版 MySQL),改用派生表:
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employees
) t WHERE t.rn <= 3;ROW_NUMBER() 和 RANK() / DENSE_RANK() 的行为差异直接影响“前 N 条”结果
当存在并列值(比如两个员工工资都是 20000)时,不同函数处理方式不同:
-
ROW_NUMBER():强制编号,即使值相同也给不同序号(1, 2, 3…),可能把本该并列第 1 的第二条记录挤出前 3; -
RANK():相同值同名次,跳过后续名次(1, 1, 3),取前 3 条可能只返回 2 条实际记录; -
DENSE_RANK():相同值同名次,不跳号(1, 1, 2),更符合“取成绩前 3 名”的业务直觉。
如果你的业务要求是“不管有没有并列,只要最多取 3 条”,就用 ROW_NUMBER();如果要求“取所有排名 ≤ 3 的记录(允许超过 3 行)”,就得换 DENSE_RANK() 并注意结果行数可能膨胀。
性能关键:PARTITION BY 和 ORDER BY 字段必须有索引
没索引时,ROW_NUMBER() 可能触发全表扫描 + 大量临时排序,尤其在千万级表上延迟明显。优化要点:
- 复合索引顺序应匹配窗口定义:例如
PARTITION BY region, type ORDER BY created_at DESC,对应索引应为(region, type, created_at)或(region, type, created_at DESC); - 避免在
ORDER BY中使用函数或表达式(如ORDER BY UPPER(name)),会导致索引失效; - PostgreSQL 中可考虑物化 CTE(
MATERIALIZED)避免重复计算,但仅适用于中间结果集不大且多次引用的场景。
真正卡住性能的往往不是 ROW_NUMBER() 本身,而是它背后隐含的排序成本——这点很容易被忽略,直到数据量上去才暴露。

















