ROW_NUMBER()必须搭配OVER子句且ORDER BY不可省略,PARTITION BY实现分组编号;ORDER BY需含唯一字段防重复编号;无索引时性能骤降,应建(a,b)联合索引。

ROW_NUMBER() OVER (PARTITION BY … ORDER BY …) 是唯一正确写法
直接用 ROW_NUMBER() 不加 OVER 会报错,比如 SELECT ROW_NUMBER() FROM t —— 这根本不能运行。必须搭配 OVER 子句,且里面至少要有 ORDER BY;PARTITION BY 才是实现“分组内编号”的关键。
常见错误是把 PARTITION BY 写成 GROUP BY,或者漏掉 ORDER BY(哪怕只是按主键排)—— 没有排序依据,行号就无定义,不同执行可能出不同结果。
-
PARTITION BY指定分组字段,如PARTITION BY category表示每个 category 内独立编号 -
ORDER BY必须存在,决定组内行号顺序,如ORDER BY created_at DESC - 不能在
WHERE或GROUP BY中直接引用ROW_NUMBER()别名,得套一层子查询或 CTE
想取每组最新一条?别只靠 ROW_NUMBER()=1
很多人写 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn,然后加 WHERE rn = 1,看起来没问题,但实际可能返回多行 —— 因为 updated_at 可能重复。数据库不会自动去重,它严格按 ORDER BY 的确定性排序给号。
解决办法只有两个:
- 在
ORDER BY里补一个唯一字段兜底,比如ORDER BY updated_at DESC, id DESC - 如果业务允许“任选其一”,就加
QUALIFY ROW_NUMBER() OVER (...) = 1(BigQuery / Snowflake 支持),但 MySQL 8.0+ 和 PostgreSQL 需用子查询
MySQL 8.0+ 和 PostgreSQL 差异很小,但 SQL Server 要注意兼容模式
语法层面三者都支持标准的 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...),但 SQL Server 在旧兼容级别(如 80、90)下不识别窗口函数,会直接报错 Incorrect syntax near 'OVER'。
检查方式:SELECT compatibility_level FROM sys.databases WHERE name = DB_NAME(),值必须 ≥ 100(对应 SQL Server 2008)才支持。
- MySQL 5.7 及更早版本完全不支持窗口函数,强行用会报
FUNCTION xxx.ROW_NUMBER does not exist - PostgreSQL 从 8.4 就支持,但
PARTITION BY字段类型要和ORDER BY字段可比对,比如不能拿text和int混排
性能隐患:没索引时 PARTITION BY + ORDER BY 会触发临时文件排序
当分组字段和排序字段都没有索引时,数据库必须把全部数据拉进内存(或磁盘临时文件)做分组 + 排序,大表上可能慢到超时或 OOM。
最有效的优化就是建联合索引:
- 对
PARTITION BY a ORDER BY b,建INDEX idx_a_b (a, b) - 如果还要倒序,MySQL 8.0+ 和 PostgreSQL 支持
(a, b DESC),SQL Server 则需显式指定CREATE INDEX ... (a, b DESC) - 别只建单列索引——
INDEX(a)+INDEX(b)对窗口函数几乎没用
分组排序看着简单,但行号是否稳定、能否命中索引、是否被优化器下推,全取决于这三个部分怎么组合。漏掉任意一个细节,结果就可能错或慢。

















