应根据并列处理需求选择:需强制唯一序号用ROW_NUMBER()(1,2,3,4),允许并列且跳号用RANK()(1,2,2,4),允许并列且不跳号用DENSE_RANK()(1,2,2,3)。

rank()、dense_rank() 和 row_number() 到底该选哪个?
分组内排名不是只有一个函数能搞定,rank()、dense_rank() 和 row_number() 行为完全不同,选错会导致业务逻辑出错。比如销售榜里并列第二时,要不要跳过第三名——这直接决定用哪个函数。
-
rank():并列时占位,1, 2, 2, 4(跳过 3) -
dense_rank():并列不占位,1, 2, 2, 3 -
row_number():强制唯一序号,1, 2, 3, 4(哪怕值相同)
实际写法必须带 OVER 子句,且 PARTITION BY 定义分组维度,ORDER BY 定义排序依据。漏掉 PARTITION BY 就变成全表排名,不是“分组内”了。
PARTITION BY 写错位置会全盘失效
PARTITION BY 必须紧跟在 OVER 后面,不能写在 ORDER BY 之后,否则语法报错或语义错误。常见错误是把分组字段误放在 GROUP BY 里——窗口函数不需要也不允许配合 GROUP BY 使用,那是聚合函数的事。
- ✅ 正确:
OVER (PARTITION BY department ORDER BY salary DESC) - ❌ 错误:
OVER (ORDER BY salary DESC) GROUP BY department - ⚠️ 隐患:用
WHERE提前过滤数据,但忘了PARTITION BY字段有 NULL 值——NULL 会被单独分到一组,可能多出意料之外的“第 1 名”
ORDER BY 里多个字段怎么影响排名?
当排序依据不止一个字段时,ORDER BY 的顺序和方向直接影响并列判断。比如 ORDER BY score DESC, created_at ASC,先按分数降序,分数相同时再按时间升序——这样即使分数一样,row_number() 也不会随机分配序号。
- 如果业务要求“分数相同就完全并列”,那第二个字段就不能参与排序,否则
rank()和dense_rank()也会被拆开 -
NULLS FIRST或NULLS LAST要显式声明,不同数据库默认行为不同(PostgreSQL 默认NULLS FIRST,MySQL 8.0+ 默认NULLS LAST) - 别在
ORDER BY里用表达式如ABS(score)却忘了加括号或别名——某些数据库会报错或结果不可预期
性能差?可能是没加索引或用了复杂表达式
窗口函数本身不慢,但慢往往出在 ORDER BY 字段没索引,或者 PARTITION BY 字段基数太高(比如按用户 ID 分组,上千万行)。更隐蔽的问题是:在 ORDER BY 里用了函数或计算字段,导致无法走索引。
- 优先给
PARTITION BY + ORDER BY字段建联合索引,顺序要一致 - 避免在
ORDER BY中写UPPER(name)这类函数,改用生成列 + 索引(MySQL 5.7+ / PostgreSQL) - 大数据量下慎用
rank()或dense_rank()配合OFFSET做分页——它得算完整个分区才能取第 N 条,不如先用子查询过滤再排名
真正难的不是写对语法,而是想清楚“并列是否合理”“NULL 怎么归组”“排序字段有没有隐含业务含义”。这些地方一模糊,跑出来的排名就不是你要的“分组内”结果。

















