ROW_NUMBER() OVER (PARTITION BY ...) 是 SQL 中用于在分组内生成唯一序号的窗口函数,核心作用是在指定的分区(分组)内,为每一行数据生成一个从 1 开始递增的唯一序号。
- ROW_NUMBER():生成行号,序号从 1 开始,在每个分区内连续递增。
- OVER():标识这是一个窗口函数,定义函数的作用范围。
- PARTITION BY:按指定字段分组,类似 GROUP BY,但不会合并行,每个分区独立编号。
- ORDER BY:定义组内排序方式,决定编号的顺序(默认升序 ASC,可指定降序 DESC)。
语法结构解析
- ROW_NUMBER():生成行号。
- OVER():表示这是一个窗口函数。
- PARTITION BY column_name:按照某个字段进行分组,类似 GROUP BY,但不会合并行。
- ORDER BY column_name:在每个分区内按什么顺序编号。
示例
示例 1:按销售人员分组排名假设有一张销售记录表 sales,想查看每个销售人员按销售额从高到低的排名:
SELECT salesperson, sale_date, amount, ROW_NUMBER() OVER (PARTITION BY salesperson ORDER BY amount DESC) AS rankFROM sales;解释:
- 按 salesperson 分组。
- 每组内部按 amount 降序排序。
- 为每条记录生成从 1 开始的编号,表示排名。
示例 2:分组排名(员工每月业绩)查看每位员工每月的业绩排名:
ROW_NUMBER() OVER (PARTITION BY employee_id, month ORDER BY performance_score DESC)解释:
- 按 employee_id 和 month 分组。
- 每组内部按 performance_score 降序排序。
- 生成排名编号,显示每个人每个月的业绩排名。
示例 3:去重(保留每个用户最新日志)假设 user_logs 表中有重复的用户记录,想只取每个用户最新的一条:
WITH ranked_data AS ( SELECT user_id, log_time, action, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY log_time DESC) AS rn FROM user_logs)SELECT * FROM ranked_data WHERE rn = 1;解释:
- 按 user_id 分组。
- 每组内部按 log_time 降序排序。
- 生成编号 rn,取 rn = 1 的记录,即每个用户最近的一条日志。
常见使用场景
- 分组排名:如按销售人员分组后按销售额排序。
- 去重:如保留每个用户最新或最旧的一条记录。
- 筛选:如筛选每个分组中的前 N 条记录。
使用时需要注意的细节
- 分区字段要选对:PARTITION BY 后面的字段决定了分组方式,漏掉关键字段会导致结果错误。
- 排序方式影响编号顺序:默认升序 ASC(从小到大),可指定降序 DESC(从大到小),根据业务需求选择。
- 编号唯一且连续:ROW_NUMBER() 生成的编号在每个分区内严格从 1 开始递增,不会有重复值,这点与 RANK() 和 DENSE_RANK() 不同。
总结
ROW_NUMBER() OVER (PARTITION BY ...) 是一个实用的窗口函数,特别适合做排名、去重、筛选等操作。使用时需明确:
- 如何分组(PARTITION BY)。
- 如何排序(ORDER BY)。
掌握这些后,就能灵活运用该函数解决实际问题。多练习几次,对分区字段和排序方向的选择会更加熟练。