row_number() 是窗口函数,必须配合 over 子句使用,且 order by 为必需项;需用子查询或 cte 先生成序号再过滤,不可在 where 中直接引用。

ROW_NUMBER() 必须配合 OVER 子句,否则直接报错
单独写 ROW_NUMBER() 会触发类似 Window function 'ROW_NUMBER' requires an OVER clause 的错误。它不是普通聚合函数,而是窗口函数,必须明确指定排序逻辑和(可选)分组逻辑。
常见误写:SELECT name, ROW_NUMBER() FROM users; —— 这在任何主流 SQL 引擎(PostgreSQL、SQL Server、MySQL 8.0+、Oracle)里都会失败。
-
OVER (ORDER BY score DESC):全表按 score 降序编号,不带分组 -
OVER (PARTITION BY category ORDER BY score DESC):先按 category 分组,组内再按 score 降序编号 - ORDER BY 是必需的;PARTITION BY 是可选的,但 TopN 分组场景下几乎总要加
分组 TopN 的标准写法:子查询 + ROW_NUMBER() 过滤
不能在 WHERE 或 HAVING 中直接用 ROW_NUMBER(),因为窗口函数在逻辑上晚于 WHERE 执行。必须用子查询(或 CTE)先算出序号,再在外层过滤。
SELECT category, name, score
FROM (
SELECT category, name, score,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY score DESC) AS rn
FROM products
) t
WHERE rn
<p>这个结构在 PostgreSQL、SQL Server、MySQL 8.0+、BigQuery 等都通用。注意别漏掉别名 <code>t</code>,否则 MySQL 会报 <code>Every derived table must have its own alias</code>。</p>
- 如果只要每个分组的第 1 名,把
rn 改成 <code>rn = 1即可 - 用
RANK()或DENSE_RANK()会处理并列情况,但ROW_NUMBER()严格保证唯一序号,适合“取前 N 条”而非“分数前 N 名” - ORDER BY 中多个字段(如
ORDER BY score DESC, id ASC)能稳定排序结果,避免因主键缺失导致每次执行顺序不同
MySQL 5.7 或更早版本不支持 ROW_NUMBER()
如果你连 ROW_NUMBER() 都报语法错误,大概率是 MySQL 版本低于 8.0。这时候不能硬套,得换方案。
替代思路:用自增变量模拟序号(仅限单线程查询,且需确保 ORDER BY 生效):
SELECT category, name, score
FROM (
SELECT category, name, score,
@rn := IF(@prev = category, @rn + 1, 1) AS rn,
@prev := category
FROM products
JOIN (SELECT @rn := 0, @prev := '') AS _
ORDER BY category, score DESC
) t
WHERE rn
- 该写法在 MySQL 5.7 可用,但不推荐用于生产环境:变量赋值顺序在新版 MySQL 中已不被保证,且无法并行执行
- 真正稳妥的降级方案是应用层分组 + 排序,或升级到 MySQL 8.0+
- SQLite 直到 3.25.0 才支持窗口函数,旧版也得绕开
性能陷阱:没加索引时 PARTITION BY + ORDER BY 很慢
PARTITION BY category ORDER BY score DESC 实际上要求引擎对每个 category 内部做排序。如果没有合适索引,全表扫描 + 多次内部排序会让查询急剧变慢,尤其当 category 值很多、数据量大时。
- 最优索引:复合索引
(category, score)(注意顺序!category在前) - 如果常查
score DESC,建索引时显式声明方向(如 PostgreSQL 支持CREATE INDEX ON products(category, score DESC)) - 在 SQL Server 中,加上
INCLUDE列(如INCLUDE (name))可避免回表,提升 SELECT 效率 - EXPLAIN 查看执行计划时,重点关注是否用了索引、是否出现
WindowAgg或Sort节点及对应行数










