ROW_NUMBER() 必须配合 OVER() 使用,否则报语法错误;OVER() 中至少需 ORDER BY,PARTITION BY 不要求字段出现在 SELECT 中,混用 RANK 等函数需注意重复值处理差异,性能依赖排序字段索引。

ROW_NUMBER() 必须配合 OVER() 才能用,单独写会报错
直接在 SELECT 里写 ROW_NUMBER() 而不加 OVER(),MySQL 8.0+、PostgreSQL、SQL Server 都会抛出语法错误,比如 PostgreSQL 报 ERROR: window function ROW_NUMBER requires an OVER clause。这不是版本问题,是 SQL 标准强制要求——窗口函数必须明确定义计算范围。
实操建议:
-
OVER()里至少得有ORDER BY,否则多数数据库拒绝执行(如 PostgreSQL 明确报错);MySQL 8.0 允许空OVER(),但结果不可预测,别依赖 - 如果只想按组编号、组内顺序无所谓,用
ORDER BY (SELECT NULL)(PostgreSQL/SQL Server)或ORDER BY 1(MySQL)凑合,但语义不清,建议显式加个稳定字段,比如主键 - 分组用
PARTITION BY,它控制“重置计数”的边界,和GROUP BY逻辑不同:前者不聚合行,后者会压缩行
分组编号时 PARTITION BY 字段必须出现在 SELECT 或 ORDER BY 中吗?
不需要。PARTITION BY 的字段只是用来切分窗口,不要求出现在最终输出列里。常见误解是以为必须和 GROUP BY 对齐,其实完全无关。
实操建议:
- 可以只按
user_id分组编号,但SELECT里只选order_id和ROW_NUMBER(),user_id不出现也合法 - 但如果
ORDER BY里用了未出现在SELECT的字段(比如按created_at排序),某些老版本 MySQL 可能报错,建议统一用已选字段或主键 - 注意 NULL 值处理:
PARTITION BY col会把所有col IS NULL的行归到同一组,不是每行一个组
ROW_NUMBER() 和 RANK()、DENSE_RANK() 混用导致排序错乱
三者都生成序号,但重复值处理完全不同:ROW_NUMBER() 强制唯一递增,RANK() 跳号,DENSE_RANK() 不跳号。混用时若没看清业务含义,比如用 RANK() 实现“每组第一条”,可能漏掉并列第一的记录。
实操建议:
- 要“严格顺序编号”(哪怕值相同也要区分先后),只用
ROW_NUMBER(),且ORDER BY必须含唯一字段(如主键) - 想按某字段值排名(如销售额 Top N),优先用
RANK()或DENSE_RANK(),别硬套ROW_NUMBER() - MySQL 8.0+ 支持所有三种,但旧版(5.7)完全不支持窗口函数,别忘了检查
VERSION()
性能隐患:ORDER BY 字段没索引,大表上 ROW_NUMBER() 极慢
ROW_NUMBER() 内部要对每个窗口做排序,如果 OVER(ORDER BY x) 的 x 列没索引,数据库就得临时排序全量数据,百万行以上可能秒变分钟级。
实操建议:
- 给
OVER()中的ORDER BY字段建索引,尤其是和PARTITION BY组合时,考虑联合索引,比如(category, created_at) - 避免在
ORDER BY里用函数或表达式(如ORDER BY UPPER(name)),索引失效,排序成本飙升 - 测试时用
EXPLAIN看是否走了索引;PostgreSQL 注意看WindowAgg节点下的Sort是否标记Using filesort
窗口函数不是语法糖,它是独立的计算阶段,和 WHERE、GROUP BY 的执行顺序严格固定。很多人调换 PARTITION BY 和 WHERE 位置试图“先过滤再编号”,结果发现编号还是基于全表——因为 WHERE 在窗口函数之前执行,但 PARTITION BY 的分组依据来自过滤后的结果集,这点容易混淆。










