row_number() 必须配合 over() 使用,否则报错;over() 中 order by 不可省略,partition by 可选但去重需二者组合;无索引时性能差,旧版 mysql 不支持。

ROW_NUMBER() 必须配合 OVER() 才能用,单独写会报错
直接写 SELECT ROW_NUMBER() 不起作用,MySQL、PostgreSQL、SQL Server 都一样。它不是普通函数,而是窗口函数,语法强制要求带 OVER() 子句,否则解析失败,错误信息通常是 ERROR: window function calls require an OVER clause 或类似提示。
常见误写:SELECT name, ROW_NUMBER() FROM users —— 这条语句在任何主流数据库里都会报错。
-
OVER()里至少得有ORDER BY,比如ROW_NUMBER() OVER (ORDER BY created_at) - 如果要分组内编号,必须加
PARTITION BY,例如按部门分组:ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) -
PARTITION BY和ORDER BY顺序不能颠倒,ORDER BY必须存在,PARTITION BY可选
用 ROW_NUMBER() 去重时,别漏掉 PARTITION BY + ORDER BY 的组合逻辑
很多人想用 ROW_NUMBER() 删除重复行,比如保留每个用户最新的一条记录。这时候容易只写 ORDER BY updated_at DESC,却忘了加 PARTITION BY user_id,结果整个表被当成一个组编号,只留下全局第一条,而不是每个用户各留一条。
正确写法示例(保留每个 user_id 最新记录):
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn FROM user_logs ) t WHERE rn = 1;
- 没写
PARTITION BY→ 全表只排一次序,rn = 1只返回一行 - 只写
PARTITION BY不写ORDER BY→ 报错,OVER()中ORDER BY不可省略 - 排序字段含 NULL 值时,不同数据库行为不一致(PostgreSQL 默认 NULLS LAST,MySQL 8.0 默认同值排前面),建议显式写
ORDER BY updated_at DESC NULLS LAST(如支持)
性能陷阱:ORDER BY 字段没索引,ROW_NUMBER() 会很慢
ROW_NUMBER() 的底层依赖排序操作,如果 OVER 子句里的 ORDER BY 字段没索引,大表上执行可能触发全表扫描+外部排序,几百万行就明显卡顿。
- 检查执行计划,确认是否用了索引:PostgreSQL 看
Sort节点是否出现在Index Scan后;MySQL 看Extra是否含Using filesort - 复合索引要匹配
PARTITION BY+ORDER BY顺序,例如PARTITION BY dept_id ORDER BY salary DESC,最佳索引是(dept_id, salary) - 避免在
ORDER BY里用表达式或函数,如ORDER BY UPPER(name),会导致索引失效
MySQL 8.0+ 和 PostgreSQL 支持完整语法,但旧版 MySQL 完全不支持
MySQL 5.7 及更早版本没有窗口函数,强行写 ROW_NUMBER() 会报错 FUNCTION xxx.ROW_NUMBER does not exist。升级到 8.0 是硬性前提。
- PostgreSQL 8.4+ 支持,但早期版本不支持
PARTITION BY的某些优化路径 - SQL Server 2005+ 支持,但 SQL Server 2012+ 才支持
OFFSET/FETCH配合窗口函数做分页优化 - SQLite 3.25+ 支持,但注意其
OVER子句不支持ROWS BETWEEN等高级帧定义,不过ROW_NUMBER()基本用法没问题
跨数据库移植时,别假设 ROW_NUMBER() 一定可用,先查版本,再看文档确认语法细节。











