前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >sql技巧之开窗函数rank()的使用

sql技巧之开窗函数rank()的使用

作者头像
可以叫我才哥
发布2022-11-11 10:29:58
7470
发布2022-11-11 10:29:58
举报
文章被收录于专栏:可以叫我才哥

今天分享一篇交流群里群友的问题和某群友的解答!

题目

当前数据库的”testsql”日志中,存在日期字段”gl_dt”(varchar)和玩具id字段”loan_amt”(varchar),现需求找出每个月相较于上个月新增玩具名,和每个月相较于下个月失去的玩具名。以下为数据样例展示:

代码语言:javascript
复制
gl_dt loan_amt
varchar varchar
2021/2/28 5001273101
2021/2/28 5001192158
2021/2/28 5001174891
2021/2/28 5001066042
2021/2/28 5001243311
2021/3/31 5001193986
2021/3/31 5001273101
2021/3/31 5001066042
2021/3/31 5001243311
2021/4/30 5001193986
2021/4/30 5001273101
2021/4/30 5001192158
2021/4/30 5001174891
2021/5/31 5001193986
2021/5/31 5001273101
2021/5/31 5001192158
2021/5/31 5001174891
2021/5/31 5001066043
2021/5/31 5001243312

思路分析/踩坑总结

清洗数据

日期是varchar格式,根据个人实际代码思路考虑是否能转为date格式,后清洗至“每月”的格式。这里我用的是 substr(GL_DT,1,7),不转日期格式,只保留日期的前7位。

如何解题
  • “上月新增”和“下月失去”的概念,可以抽象并统称为“回流”或“流失”。
  • 回流概念简述:用户在某日登录时,前x日没有登录记录(题里不设计新增排除问题)。根据题目可理解为,玩具于上月未记录,本月出现“回流”玩具。
  • 流失概念简述:用户在某日登录后,在x日内不再登录即为流失。根据题目可理解为,玩具在本月记录后,下月不记录即为流失

使用sql处理回流/流失的方法简述如下

a)使用left join

b)Left join的左右表完全一致。示例中使用group by主要怕实际代码里可能有重复值,如果没有可以忽略,dense_rank()先跳过,下面会讲。示例代码:

代码语言:javascript
复制
(select LOAN_AMT
       ,substr(GL_DT,1,7) as date1
       ,dense_rank()over(order by substr(GL_DT,1,7))as rn
       from testsql
       group by LOAN_AMT,substr(GL_DT,1,7)
       )as a
left join(select LOAN_AMT
       ,substr(GL_DT,1,7)as date2
       ,dense_rank()over(order by substr(GL_DT,1,7))as rn
       from testsql
       group by LOAN_AMT,substr(GL_DT,1,7)
       )as b

c)on后匹配条件保证”玩具id”一致,匹配周期相差一个月 示例代码:

代码语言:javascript
复制
on a.LOAN_AMT = b.LOAN_AMT and date_diff(a.date1,b.date2) = 1

d)最后取匹配的不相交部分,即为“回流”或“流失”值 实际代码:where b.LOAN_AMT is null

为什么要用rank()over()开窗函数

Left join使用方法中,date_diff()的使用方法是错的,GL_DT不是date格式,不能使用这个函数,需要使用rank()over()达到替代效果并满足“回流”或“流失”中的减法匹配定义(下月-上月=1)。

具体的分级效果为,从最小的月份开始排序,rank级别为1,每增加一个月,rank+1,同月的所有数据处于同一rank下。

示例代码:dense_rank()over(order by substr(GL_DT,1,7))as rn

dense_rank(),rank(),row_number()的区别

a)row_number():不考虑数据的重复性 按照顺序一次打上标号 如:1 2 3 4

b)rank()是跳跃排序, 如:1 2 2 4会跳过3

c)dense_rank()是连续排序, 如:1 2 2 3 序号连续 这里使用dense_rank()作为排序函数,如果使用rank(),假设现在一共有10w条数据,rank的排序结尾值为10w,会失去月份排序的效果

多次left join需要注意的问题

如题所示,“回流”和“流失”给数据使用者展示时,其实最好分列展示,分列需要做两次left join,每次left join时,都要注意主表是否会产生变化,比如出现一对多的情况导致数据翻倍,写了where xxx is null导致主表变化。

完整代码展示

代码语言:javascript
复制
select a.LOAN_AMT as "玩具id"
       ,a.date1  as "日期"
       ,b.date2   as "下月未失去日期"
       ,c.date3   as "上月新增日期"
      from
(select LOAN_AMT
       ,substr(GL_DT,1,7) as date1
       ,dense_rank()over(order by substr(GL_DT,1,7))as rn
       from testsql
       group by LOAN_AMT,substr(GL_DT,1,7)
       )as a
left join(select LOAN_AMT
       ,substr(GL_DT,1,7)as date2
       ,dense_rank()over(order by substr(GL_DT,1,7))as rn
       from testsql
       group by LOAN_AMT,substr(GL_DT,1,7)
       )as b
on a.LOAN_AMT = b.LOAN_AMT
 and b.rn-a.rn = 1
left join(select LOAN_AMT
       ,substr(GL_DT,1,7)as date3
       ,dense_rank()over(order by substr(GL_DT,1,7))as rn
       from testsql
       group by LOAN_AMT,substr(GL_DT,1,7)
       )as c
on a.LOAN_AMT = c.LOAN_AMT
 and a.rn-c.rn = 1
where c.LOAN_AMT is  null
 or b.LOAN_AMT is  null
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可以叫我才哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目
  • 思路分析/踩坑总结
    • 清洗数据
      • 如何解题
        • 为什么要用rank()over()开窗函数
          • dense_rank(),rank(),row_number()的区别
            • 多次left join需要注意的问题
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档