ROW_NUMBER()仅支持窗口函数的数据库(如PostgreSQL、SQL Server、Oracle、MySQL 8.0+、SQLite 3.25+),MySQL 5.7及以下需用变量模拟;必须带ORDER BY,推荐加别名、注意NULL处理与执行顺序限制。

SQL里用 ROW_NUMBER() 加行号,不是所有数据库都支持
只有支持窗口函数的数据库(如 PostgreSQL、SQL Server、Oracle、MySQL 8.0+、SQLite 3.25+)才能用 ROW_NUMBER()。MySQL 5.7 或更早版本直接报错 FUNCTION xxx.ROW_NUMBER does not exist,这时候不能硬套,得换思路(比如用变量模拟,但有并发和排序稳定性风险)。
基本写法是:ROW_NUMBER() OVER (ORDER BY column_name),括号里必须带 ORDER BY,否则语法错误 —— 这点很多人卡住,以为可以空着或用 ORDER BY NULL(部分老版本 MySQL 允许,但标准 SQL 不认)。
-
OVER后面不写PARTITION BY就是全结果集连续编号(1, 2, 3…) - 如果加了
PARTITION BY category,就会按 category 分组各自从 1 开始编号 - 排序字段最好有唯一性,否则相同值的行编号顺序不确定(虽然语法通过,但结果不可复现)
MySQL 8.0+ 和 PostgreSQL 的 ROW_NUMBER() 行为一致,但别漏写别名
生成的行号列本身没名字,不加 AS rn 之类别名,某些客户端(如 DBeaver、某些 Python DB API 驱动)会把列名识别为空字符串或默认名(如 ?column?),导致取值失败或报 KeyError。
示例(安全写法):
SELECT ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn, id, name FROM users LIMIT 10;
注意:如果 ORDER BY 字段含 NULL,不同数据库默认排序方向可能不同(PostgreSQL 默认 NULLS LAST,MySQL 8.0 默认 NULLS FIRST),影响行号位置。显式写 ORDER BY created_at DESC NULLS LAST 更稳妥(但 SQLite 和旧版 MySQL 不支持 NULLS 子句)。
想跳过前 N 行?别在 WHERE 里用 ROW_NUMBER()
ROW_NUMBER() 是窗口函数,只能出现在 SELECT 或 HAVING 子句里,不能用于 WHERE(因为执行顺序是 WHERE → GROUP BY → HAVING → SELECT,窗口函数在 SELECT 阶段才计算)。常见错误写法:
SELECT * FROM orders WHERE ROW_NUMBER() OVER (ORDER BY id) > 10; -- ❌ 语法错误
正确做法是用子查询或 CTE:
WITH numbered AS ( SELECT *, ROW_NUMBER() OVER (ORDER BY id) AS rn FROM orders ) SELECT * FROM numbered WHERE rn > 10 LIMIT 10;
或者 MySQL 8.0+ 支持直接嵌套(但可读性差):SELECT * FROM (SELECT *, ROW_NUMBER() OVER (...) AS rn FROM t) t1 WHERE rn BETWEEN 11 AND 20。
性能敏感场景下,ROW_NUMBER() 可能拖慢查询
只要用了 OVER (ORDER BY ...),数据库就必须对整个结果集排序(即使你只 LIMIT 10),无法利用索引跳过中间数据 —— 特别当基础表大、排序字段无索引时,延迟明显。
- 确认
ORDER BY字段已建索引(联合索引要注意最左匹配) - 如果只是分页且不需要绝对行号,优先考虑
LIMIT offset, size(MySQL)或OFFSET ... FETCH NEXT(PostgreSQL/SQL Server) - 真要稳定行号又大数据量,考虑物化中间结果(如临时表 + 主键索引),而不是每次查都算一遍
伪列不是免费的,它背后是排序开销;没想清楚为什么需要行号,就加 ROW_NUMBER(),大概率是给自己埋坑。










