row_number() 是窗口函数,必须配合 over() 使用,否则报错;不可与 group by 混用,需用 partition by 分组、order by 排序;where 不能直接过滤窗口函数结果。

ROW_NUMBER() 必须配合 OVER() 才能用,否则直接报错
单独写 ROW_NUMBER() 会触发类似 ERROR: window function ROW_NUMBER requires an OVER clause 的错误。它不是普通函数,而是窗口函数,必须指定排序逻辑和分组边界。
常见误写:SELECT name, ROW_NUMBER() FROM sales GROUP BY region; —— 这语法非法,GROUP BY 和窗口函数不能这样混用。
- 正确姿势是去掉
GROUP BY,改用OVER(PARTITION BY ... ORDER BY ...) -
PARTITION BY定义“分组”,对应你想要的每个类别(比如每种region、每个category) -
ORDER BY在组内排序,决定谁是第1、第2……没有它,行号顺序不可控
查每组前3条:WHERE 不能直接套在 ROW_NUMBER() 上
像 WHERE ROW_NUMBER() OVER(...) 会报错,因为 <code>WHERE 执行阶段窗口函数还没计算——SQL 执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY,而窗口函数在 SELECT 阶段才生效。
所以得用子查询或 CTE 把编号先算出来:
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY region ORDER BY amount DESC) AS rn
FROM sales
) t
WHERE t.rn
- 别漏掉别名(如
t),否则外层WHERE无法引用rn - 如果只要前N条但不关心顺序,
ORDER BY仍要写(哪怕用id或created_at),否则结果不稳定 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也行,但旧版不支持
ROW_NUMBER() vs RANK() vs DENSE_RANK():选错会影响“前N条”的实际数量
当组内有并列值(比如两个销售金额都是 10000),三者行为不同:
-
ROW_NUMBER():严格按顺序编号,1, 2, 3, 4… 即使值相同也不重复 -
RANK():并列则同号,跳过后续号,如 1, 1, 3, 4 -
DENSE_RANK():并列则同号,不跳号,如 1, 1, 2, 3
如果你要“销售额最高的前3人”,且允许并列,用 RANK() 更合理;但如果明确只要最多3条记录(不管有没有并列),就必须用 ROW_NUMBER(),否则可能拿到4条甚至更多。
性能隐患:没索引时 PARTITION BY + ORDER BY 可能很慢
数据库需要对每个分组做独立排序,如果 PARTITION BY region ORDER BY amount DESC 没对应索引,大表上容易触发全表扫描+临时文件排序。
- 建议在高频分组字段和排序字段上建联合索引,例如:
CREATE INDEX idx_region_amount ON sales (region, amount DESC); - 注意 PostgreSQL 中
DESC索引需显式声明,MySQL 8.0+ 支持,但旧版忽略方向 - 如果只是取前N条且 N 很小(比如 N=1),有些场景可用
DISTINCT ON(PostgreSQL)或相关子查询替代,可能更快
窗口函数看着简洁,但底层开销不小;上线前务必在真实数据量下测执行计划。










