四种排序开窗函数
参考
https://zhuanlan.zhihu.com/p/484290987
SqlServer四种排序:ROW_NUMBER()/RANK()/DENSE_RANK()/ntile() over()
总结:
使用方法 | 区别 |
---|---|
row_number() over(partition by col1 order by col2) | 若有并列的排名,序号递增。序号从1到n连续。 e.g., 两个人都排第3,则排序为:1,2,3,4,... |
rank() over(partition by col1 order by col2) | 若有并列的排名,会占用下一名次的。序号从1到n不连续。 e.g., 两个人都排第3,则排序为:1,2,3,3,5,... |
dense_rank() over(partition by col1 order by col2) | 若有并列的排名,不会占用下一名次的。序号从1到n连续。 e.g., 两个人都排第3,则排序为:1,2,3,3,4,... |
ntile(n) over(partition by col1 order by col2) | 将每个分区内排序后的结果均分成N个桶,排序对应的数字为桶号。如果不能平均分配,则较小桶号的桶分配额外的行,并且各个桶中能放的数据条数最多相差1 |
1 ROW_NUMBER()
例:table1: 学生考试成绩表
name | score | date |
---|---|---|
Alice | 90 | 2023-06-01 |
Alice | 87 | 2023-06-08 |
Bob | 77 | 2023-06-01 |
想要取每个学生最近一次的考试成绩:
1 | select name, score, `date` |
结果如下:
name | score | date |
---|---|---|
Alice | 87 | 2023-06-08 |
Bob | 77 | 2023-06-01 |
中间结果:
name | score | date | rn |
---|---|---|---|
Alice | 87 | 2023-06-08 | 1 |
Alice | 90 | 2023-06-01 | 2 |
Bob | 77 | 2023-06-01 | 1 |
当排序出现重复值时,结果如下:
name | score | date | rn |
---|---|---|---|
Alice | 87 | 2023-06-08 | 1 |
Alice | 90 | 2023-06-01 | 2 |
Bob | 80 | 2023-07-01 | 1 |
Bob | 77 | 2023-06-01 | 2 |
Bob | 71 | 2023-06-01 | 3 |
1.1 结合 case when 使用
另外,排序规则还可结合case when 使用:
例:用户群组有不同的状态:status=1 代表近期有过交易,status=2 代表已开户但近期未交易,status=3 代表未开户。 规则: 1、首先根据status由小到大排序,即近期有过交易的优先,未开户的排最后。 2、status=1时,根据被联系次数由高到低排;status=2时,先根据被联系次数由高到低排,相等的情况下根据开户时间由近到远排;status=3时,根据被联系次数由低到高排。
1 | row_number() over(partition by group_id order by status, case when status=1 or status=2 then call_count end desc, case when status=2 then open_account_time end desc, case when status=3 then call_count end) as row_rank |
2 RANK()
与ROW_NUMBER() 的区别主要在于对于重复值的处理
例:
1 | SELECT |
这时排序的应该为3的值,因为出现重复数据,所以也变成了2,但是最后的行数和最后编号的值是一样的。
3 DENSE RANK()
例:
1 | SELECT |
保持了重复的数据RN值相同,但值得注意的是第4行数据变成了3,也就是说行数和最后的编号是发生了变化的。
4 NTILE()
NTILE() 括号里面是几,就会分成几组。
例如5行数据, 分成2组: NTILE(2) -> 前面3个,后面2个,默认把多余的行分给前面; 分成3组:NTILE(3) -> 第一组2个,第二组2个,第三组1个
例:
1 | SELECT |
1 | SELECT |
4.1 rand()
另:还可通过rand(),结合ntile() 实现随机均分 例:一共1600个用户,想随机均分为4组,每组400个
1 | select |
5 其他类似的开窗函数
5.1 sum() over()
进行累加
1 | select |
结果如下:
login_id | amt | create_time | cumsum |
---|---|---|---|
1001 | 100 | 2023-06-01 | 100 |
1001 | 50 | 2023-06-07 | 150 |
1002 | 24 | 2023-06-02 | 24 |
5.2 max() over()
max(A) over(partition by B order by C)
根据B分组,组内通过C排序,取A最大值作为这个字段的值。
1 | select |
class | name | age | max_age |
---|---|---|---|
class1 | bob | 30 | 30 |
class1 | alice | 20 | 30 |
class2 | cathy | 25 | 25 |