SQL 窗口函数 ROW_NUMBER、RANK、DENSE_RANK 常用方法笔记
1. ROW_NUMBER()
作用
给查询结果中的每一行生成唯一编号。
特点:
- 每一行都有唯一序号
- 不允许出现重复编号
- 即使排序字段相同,也会继续编号
- 不存在并列情况
语法
sql
ROW_NUMBER() OVER(
PARTITION BY 分组字段
ORDER BY 排序字段
)
说明:
text
PARTITION BY
|
↓
决定哪些数据属于同一组
ORDER BY
|
↓
决定组内数据排序规则
ROW_NUMBER()
|
↓
按照排序结果生成编号
示例1:不分组编号
原始数据:
| 姓名 | 分数 |
|---|---|
| 张三 | 100 |
| 李四 | 90 |
| 王五 | 90 |
| 赵六 | 80 |
SQL:
sql
ROW_NUMBER() OVER(
ORDER BY SCORE DESC
)
结果:
| 姓名 | 分数 | ROW_NUMBER |
|---|---|---|
| 张三 | 100 | 1 |
| 李四 | 90 | 2 |
| 王五 | 90 | 3 |
| 赵六 | 80 | 4 |
理解:
text
100分
|
↓
编号1
90分
|
↓
编号2
90分
|
↓
编号3
虽然两个90分相同:
text
90
90
但是 ROW_NUMBER 会给每一行不同编号。
常见用途
1. 每组取最新一条数据
例如:
一个保单存在多个版本:
| POL_ID | UPDATE_TIME |
|---|---|
| P001 | 2026-01-01 |
| P001 | 2026-02-01 |
| P001 | 2026-03-01 |
SQL:
sql
ROW_NUMBER() OVER(
PARTITION BY POL_ID
ORDER BY UPDATE_TIME DESC
) RN
结果:
| POL_ID | UPDATE_TIME | RN |
|---|---|---|
| P001 | 2026-03-01 | 1 |
| P001 | 2026-02-01 | 2 |
| P001 | 2026-01-01 | 3 |
取:
sql
WHERE RN = 1
结果:
| POL_ID | UPDATE_TIME |
|---|---|
| P001 | 2026-03-01 |
业务理解:
text
同一个保单
|
↓
按照更新时间倒序
|
↓
最新数据编号=1
|
↓
保留最新记录
2. RANK()
作用
用于排名,相同值排名相同,但是会跳号。
特点:
- 相同数据排名相同
- 后面的排名会跳号
- 适合比赛排名场景
语法
sql
RANK() OVER(
ORDER BY 排序字段 DESC
)
示例
原始数据:
| 姓名 | 分数 |
|---|---|
| 张三 | 100 |
| 李四 | 90 |
| 王五 | 90 |
| 赵六 | 80 |
SQL:
sql
RANK() OVER(
ORDER BY SCORE DESC
)
结果:
| 姓名 | 分数 | RANK |
|---|---|---|
| 张三 | 100 | 1 |
| 李四 | 90 | 2 |
| 王五 | 90 | 2 |
| 赵六 | 80 | 4 |
理解:
text
第一名
第二名
第二名
第四名
为什么80分是第四名?
因为两个90分:
text
第二名
第二名
占用了两个排名位置。
所以:
text
1
2
2
4
3. DENSE_RANK()
作用
用于连续排名,相同值排名相同,但是不会跳号。
特点:
- 相同数据排名相同
- 后续排名连续
- 不会产生排名空缺
语法
sql
DENSE_RANK() OVER(
ORDER BY 排序字段 DESC
)
示例
原始数据:
| 姓名 | 分数 |
|---|---|
| 张三 | 100 |
| 李四 | 90 |
| 王五 | 90 |
| 赵六 | 80 |
SQL:
sql
DENSE_RANK() OVER(
ORDER BY SCORE DESC
)
结果:
| 姓名 | 分数 | DENSE_RANK |
|---|---|---|
| 张三 | 100 | 1 |
| 李四 | 90 | 2 |
| 王五 | 90 | 2 |
| 赵六 | 80 | 3 |
理解:
text
第一名
第二名
第二名
第三名
不会出现:
text
1
2
2
4
而是:
text
1
2
2
3
4. 三个窗口函数区别
数据:
| 数据 |
|---|
| 001 |
| 001 |
| 002 |
ROW_NUMBER()
结果:
| 数据 | 编号 |
|---|---|
| 001 | 1 |
| 001 | 2 |
| 002 | 3 |
说明:
不管数据是否重复,每一行都有唯一编号。
RANK()
结果:
| 数据 | 编号 |
|---|---|
| 001 | 1 |
| 001 | 1 |
| 002 | 3 |
说明:
相同数据排名一样,但是后面的编号跳过。
DENSE_RANK()
结果:
| 数据 | 编号 |
|---|---|
| 001 | 1 |
| 001 | 1 |
| 002 | 2 |
说明:
相同数据排名一样,但是后面的编号连续。
5. 三个函数总结对比
| 函数 | 是否允许重复 | 是否跳号 | 主要用途 |
|---|---|---|---|
| ROW_NUMBER | 不允许 | 不跳号 | 编号、去重、取第一条 |
| RANK | 允许 | 跳号 | 比赛排名 |
| DENSE_RANK | 允许 | 不跳号 | 连续排名 |
6. 快速记忆
ROW_NUMBER()
关键词:
一个数据一个编号
结果:
text
1
2
3
4
RANK()
关键词:
比赛排名
结果:
text
1
2
2
4
特点:
- 可以并列
- 并列占用名次
DENSE_RANK()
关键词:
等级排名
结果:
text
1
2
2
3
特点:
- 可以并列
- 不浪费编号
7. IFRS17 数据处理中常见场景
ROW_NUMBER() 示例
你的代码:
sql
ROW_NUMBER() OVER(
PARTITION BY
a.pol_id,
a.risk_code,
a.clm_no,
(CASE WHEN A.amount >= 0 THEN 0 ELSE 1 END)
ORDER BY
d.SUB_RISK_FLAG
) RN
含义:
按照:
text
POL_ID 保单号
+
RISK_CODE 险种代码
+
CLM_NO 理赔号
+
金额正负标识
进行分组。
其中:
sql
CASE WHEN A.amount >= 0 THEN 0 ELSE 1 END
表示:
| AMOUNT | 标识 |
|---|---|
| >=0 | 0 |
| <0 | 1 |
作用:
把:
text
正常金额
和:
text
冲销金额
分开。
然后:
sql
ORDER BY d.SUB_RISK_FLAG
表示:
组内按照主附险标识排序。
最终效果:
text
同一个保单
|
↓
同一个险种
|
↓
同一个理赔号
|
↓
同一种金额方向
|
↓
按照主附险排序
|
↓
生成编号
最终记忆
text
ROW_NUMBER:
编号
一行一个号码
RANK:
排名
可以并列
但是跳号
DENSE_RANK:
排名
可以并列
但是不跳号
一句话:
ROW_NUMBER 管编号,RANK 管比赛排名,DENSE_RANK 管连续等级排名。