数据来源于道才的文章:《用SQL计算用户连续登录天数》

需求如下:

计算每一个用户的最大连续登录天数,由左变换到右边。

MySQL8.0窗口函数实现

实现思路:

  1. 对用户ID和登录日期去重
  2. 对每个用户ID按照日期顺序进行编号
  3. 将登录日期减去编号对应的天数,使连续的日期转换为同一天

将连续日期转换为同一个日期之后就可以按照这个字段分组,后面就简单了。下面我们一步步看:

对用户ID和登录日期去重

SELECT DISTINCT role_id,$part_date `date` FROM role_login;

对每个用户ID按照日期顺序进行编号,并将登录日期减去该编号对应的天数(可以一步到位):

SELECTrole_id,`date`,DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_group
FROM(SELECT DISTINCT role_id,$part_date `date` FROM role_login
) a;

从结果我们可以看到已经成功的使连续的日期都转换到同一天。

然后我们就可以基于该结果统计每个用户的所有连续日期段:

SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_days
FROM(SELECTrole_id,`date`,DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_groupFROM(SELECT DISTINCT role_id,$part_date `date` FROM role_login) a
) b
GROUP BY role_id,data_group;

结果:

然后通过窗口函数标注每个用户的连续日期排名:

SELECTrole_id,begin_date,end_date,continuous_days ,row_number() OVER (PARTITION BY role_id ORDER BY continuous_days DESC) rk
FROM(SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_daysFROM(SELECTrole_id,`date`,DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_groupFROM(SELECT DISTINCT role_id,$part_date `date` FROM role_login) a) bGROUP BY role_id,data_group
) c;

注意:有时同一个用户的最大连续日期可能存在多个,这里以第一个为准;如果需要获取全部的最大日期可以使用rankdense_rank窗口函数,可以保证天数一致时排名一致。

SELECTrole_id,begin_date,end_date,continuous_days max_continuous_days
FROM(SELECTrole_id,begin_date,end_date,continuous_days ,row_number() OVER (PARTITION BY role_id ORDER BY continuous_days DESC) rkFROM(SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_daysFROM(SELECTrole_id,`date`,DATE_SUB(`date`,INTERVAL (row_number() OVER(PARTITION BY role_id ORDER BY `date`)) DAY) data_groupFROM(SELECT DISTINCT role_id,$part_date `date` FROM role_login) a) bGROUP BY role_id,data_group) c
) d
WHERE rk=1
ORDER BY max_continuous_days DESC,role_id;

成功得到结果:

MySQL变量实现(5.0也可以使用)

在MySQL5.0中并没有窗口函数,但是我们可以通过变量来实现。

思路仍然与上述相同,首先我们对数据集去重并排序:

SELECT DISTINCT role_id,$part_date `date` FROM role_login
ORDER BY role_id,$part_date;

然后利用变量逐行扫描数据集:

SELECTrole_id,`date`,IF(DATE_ADD(`date`,INTERVAL -1 DAY)=@prev_date,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值@prev_date:=`date` -- 记录前一条记录的日期
FROM(SELECT DISTINCT role_id,$part_date `date` FROM role_loginORDER BY role_id,$part_date
) a,(SELECT @prev_date:=NULL,@r:=0) t;

从结果可以看到,对于每个用户下连续的日期都给出了完全相同的分组编号:

然后就可以计算连续天数了:

SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_days
FROM(SELECTrole_id,`date`,IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值@prev_date:=`date` -- 记录前一条记录的日期FROM(SELECT DISTINCT role_id,$part_date `date` FROM role_loginORDER BY role_id,$part_date) a,(SELECT @prev_date:=NULL,@r:=0) t
) b
GROUP BY role_id,group_id;

然后借助变量标注排名:

SELECTrole_id,begin_date,end_date,continuous_days ,IF(@prev_id=role_id,@r2:=@r2+1,@r2:=1) rk,@prev_id:=role_id -- 记录前一条记录的用户ID
FROM(SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_daysFROM(SELECTrole_id,`date`,IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值@prev_date:=`date` -- 记录前一条记录的日期FROM(SELECT DISTINCT role_id,$part_date `date` FROM role_loginORDER BY role_id,$part_date) a,(SELECT @prev_date:=NULL,@r:=0) t1) bGROUP BY role_id,group_id
) c,(SELECT @prev_id:=NULL,@r2:=0) t2
ORDER BY role_id,continuous_days DESC;

可以看到变量已经成功实现了row_number的效果。

最后再进行一次过滤即可:

SELECTrole_id,begin_date,end_date,continuous_days max_continuous_days
FROM(SELECTrole_id,begin_date,end_date,continuous_days ,IF(@prev_id=role_id,@r2:=@r2+1,@r2:=1) rk,@prev_id:=role_id -- 记录前一条记录的用户IDFROM(SELECTrole_id,MIN(DATE) begin_date,MAX(DATE) end_date,COUNT(*) continuous_daysFROM(SELECTrole_id,`date`,IF(DATE_ADD(@prev_date,INTERVAL 1 DAY)=`date`,@r,@r:=@r+1) group_id, -- 日期变化大于1天(不连续)改变r值@prev_date:=`date` -- 记录前一条记录的日期FROM(SELECT DISTINCT role_id,$part_date `date` FROM role_loginORDER BY role_id,$part_date) a,(SELECT @prev_date:=NULL,@r:=0) t1) bGROUP BY role_id,group_id) c,(SELECT @prev_id:=NULL,@r2:=0) t2ORDER BY role_id,continuous_days DESC
) d
WHERE rk=1
ORDER BY max_continuous_days DESC,role_id;

成功得到结果:

Pandas实现

下面我们用MySQL窗口函数的实现思路,用pandas实现一遍。

首先读取数据集并去重:

import pandas as pddf = pd.read_excel("role_login.xlsx")
df = df[["role_id", "$part_date"]].drop_duplicates()
df

对每个用户ID按照日期顺序进行编号,并将登录日期减去该编号对应的天数:

data_group = df["$part_date"]-pd.to_timedelta(df.groupby("role_id")["$part_date"].rank(method="dense"), unit='d')

统计每个用户的所有连续日期段:

data_group = df["$part_date"]-pd.to_timedelta(df.groupby("role_id")["$part_date"].rank(method="dense"), unit='d')
df = df.groupby(["role_id", data_group], as_index=False).agg(begin_date=("$part_date", "min"),end_date=("$part_date", "max"),max_continuous_days=("$part_date", "count")
)
df

然后我们过滤出每个用户的最大连续日期:

ids = df.groupby("role_id")["max_continuous_days"].idxmax()
r1 = df.loc[ids].sort_values(["max_continuous_days", "role_id"], ascending=[False, True])
r1

按照窗口函数的思路代码如下:

mask=df.groupby("role_id")["max_continuous_days"].rank(method="first", ascending=False) == 1
r2 = df[mask].sort_values(["max_continuous_days", "role_id"], ascending=[False, True])
r2

两种思路的结果一致:

(r1 == r2).all()
role_id                True
begin_date             True
end_date               True
max_continuous_days    True
dtype: bool

但是窗口函数的思路相对idxmax快了近百倍:

所以针对这取分组最大的问题还是使用rank函数效果更高一些。

RANK 和 DENSE_RANK的差异

本题在一个用户存在多个最大连续日期时只要求取第一个,如果需要取每个用户所有的最大连续日期,则需要使用rankdense_rank窗口函数。

首先我们看看pandas中rank函数的几种method的差异:

import pandas as pdt1 = pd.DataFrame(data={'num': [2, 4, 4, 8, 8]})
t1['default_rank'] = t1['num'].rank()
t1['min_rank'] = t1['num'].rank(method='min')
t1['max_rank'] = t1['num'].rank(method='max')
t1['dense_rank'] = t1['num'].rank(method='dense')
t1['first_rank'] = t1['num'].rank(method='first')
t1

结果:

num default_rank min_rank max_rank dense_rank first_rank
0 2 1.0 1.0 1.0 1.0 1.0
1 4 2.5 2.0 3.0 2.0 2.0
2 4 2.5 2.0 3.0 2.0 3.0
3 8 4.5 4.0 5.0 3.0 4.0
4 8 4.5 4.0 5.0 3.0 5.0

可以看到MySQL的rank函数相当于pandas的min_rank,row_number相当于first_rank:

SELECTcookieid,createtime,pv,RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
FROM xxm_t2
WHERE cookieid = 'cookie1';
cookieid day           pv       rn1     rn2     rn3
--------------------------------------------------
cookie1 2022-04-12      7       1       1       1
cookie1 2022-04-11      5       2       2       2
cookie1 2022-04-15      4       3       3       3
cookie1 2022-04-16      4       3       3       4
cookie1 2022-04-13      3       5       4       5
cookie1 2022-04-14      2       6       5       6
cookie1 2022-04-10      1       7       6       7

对比MySQL和Pandas计算用户最大连续登录天数相关推荐

  1. 求用户最大连续登陆天数mysql实现

    用户连续登陆 一.规定时间内用户最大连续登陆天数 二.user_login表字段(所有用户登陆信息) 三.分解步骤 1.where初步筛选 2.窗口函数分组排序 4.计算获得连续的字段 5.分组计算连 ...

  2. mysql 连续天数_mysql计算连续天数,mysql连续登录天数,连续天数统计

    mysql计算连续天数,mysql连续登录天数,连续天数统计 >>>>>>>>>>>>>>>>>& ...

  3. mysql 连续七天不登录_使用MySQL窗口函数巧妙的解决用户连续登录天数等问题

    我们在工作中或者面试时,可能会遇到求出用户连续登录天数.连续签到天数等问题,这篇文章就是用窗口函数比较巧妙的解决这个问题的.文章比较长,建议先收藏后观看+_+ 首先是数据准备和处理 如下代码是创建用户 ...

  4. mysql 连续登录天数

    已知表名:csdata,表中有两个字段:user_id,log_time 问:用户连续登录天数为3的是?. user_id log_time 111111 2022/12/12 00:00:00 11 ...

  5. 每个用户连续登录天数

    id dt 1001 2022-05-01 1001 2022-05-02 1001 2022-05-04 1001 2022-05-05 1001 2022-05-08 1001 2022-05-1 ...

  6. SQL——最大连续登录天数、当前连续登录天数、最大连续未登录天数问题、连续登陆N天用户、连续座位号

    问题: 最大连续登录天数 当前连续登录天数 最大连续未登录天数 连续登陆3天用户(三种方法) 选出连续座位的编号 前三个问题所用数据: 原数据表:user_active表 表字段:用户.新增日期.活跃 ...

  7. Hive sql : 查询连续登录天数

    查询连续登录天数 1.问题描述 2.在Hive中建表 3.查询最大连续登录天数 1.问题描述 目前有两列数据,分别是用户ID和用户登录的时间,现需要统计用户连续登录的最大天数,中间如有断开,则不算连续 ...

  8. SQL(之二)-查询“最长连续登录天数“

    0-创建表及插入测试数据 说明:本文主要想分享,如何查询"最长连续登录天数",具体的测试数据,可以自行添加. CREATE TABLE USER_LOGINFO( USER_ID ...

  9. MySQL学习_计算用户支付方式占比_20161104

    计算用户支付方式占比 SELECT b.*#根据城市ID 年月排序 FROM (SELECT a.*FROM ( #纵向合并SELECT b1.ID,a1.城市,a1.收款方式,DATE_FORMAT ...

  10. 对比MySQL和Pandas统计直播间各时间段上线人数

    一道面试题如下: 简单理解就是求每个直播间,所有的最短时间段的上线人数.简单图解下: 有了上图,我们应该就秒懂了这题的解题思路,先将所有的时间点排序,按顺序分配得到每个时间分区,绿色为进入起始时间点, ...

最新文章

  1. 傅里叶变换取代Transformer自注意力层,谷歌这项研究GPU上快7倍、TPU上快2倍
  2. JavaScript内置一些方法的实现原理--new关键字,call/apply/bind方法--前戏
  3. 如何证明服从卡方分布_谈谈抽样分布定理
  4. 项目的ar指什么_AR眼镜显示测评标准解读——概述
  5. linux下配置iscsi存储,linux 下iscsi网络存储配置
  6. 数据科学和人工智能技术笔记 十七、聚类
  7. vivo NEX 3S 5G今日登场:无界瀑布屏加持 世界随处可及
  8. 计算机的桌面图标设置在哪里,电脑小图标怎么设置在哪里
  9. MongoDB可视化管理工具-Robo 3T
  10. 三款Linux文件传输工具简单介绍
  11. php中怎么添加css样式_html怎么添加css样式?
  12. 移动视频类设备平台国标GB28181输入输出,GB28181平台对接说明
  13. 【论文笔记】D2A U-Net: Automatic segmentation of COVID-19 CT slices based on dual attention and hybrid di
  14. linux下同一个tomcat部署多个项目 同一个端口
  15. 行为设计模式:中介者模式
  16. 戴尔台式机修复计算机,dell电脑win10启动修复无法修复你的电脑的解决方法
  17. 通过伴随矩阵怎么求逆矩阵
  18. 送给要健身或者准备健身的男朋友女朋友基友们 想让你们和我一样踏上条不归路
  19. wps分析工具库如何加载_【数据分析】关键词数据分析如何做?用这个工具比指数好...
  20. 商王令人毛骨悚然的“全人俎祭”

热门文章

  1. halcon图片上区域灰度值区间放大,可提高对比度
  2. 接口工具POSTMAN怎么设置中文
  3. OSChina 娱乐弹弹弹——周一至周五,汗滴禾下土
  4. MIS(管理信息系统)
  5. 广东第二师范学院学计算机,广东第二师范学院计算机科学系党总支一行来我院调研交流...
  6. 2023郑州大学计算机考研信息汇总
  7. BZOJ 3654: [湖南集训]图样图森破 SA
  8. iOS苹果开发者账号-企业账号、公司账号和个人账号的区别
  9. java赛马游戏_Java程序之赛马游戏
  10. DTM动态主题模型实战案例