在日常工作或者面试时,我们经常会遇到这样的问题,比如:“统计下用户最长连续登录的天数”,“统计下连续登录超过10天的用户”,“统计下连续3天交易额超过100W的店铺”。这种问题,其实都是一个套路,我们这里简单介绍一种解决思路。
用户连续登录天数
我们先来看下用户连续登录问题,我们简化下数据,只保留用户ID和登录时间:
其实做数据来说,我们的思路一定要清晰,知道我们要什么样的数据,就像这个连续问题,如果我们知道怎样来判断连续,只要再转化成SQL就行了。所以,我们先思考下,怎样来判断用户是不是连续登录。
连续其实就是这样,我今天登录了,然后昨天也登录了,说明我就连续两天登录了,我们也经常会在APP或者网站上遇到什么签到领积分,签到多少天领奖品之类的,其实都是产品为了提高日活提高用户留存的套路。
1.根据上次登录日期判断
我们回过头,继续看这个连续的问题,上面的数据已经有了,如果要手工判断用户登录是否连续的话,会怎样来呢?比如,用户今天登录了,我们只需要用户上一次登录是什么时候就可以了,如果用户昨天也登录了,就说明用户是连续登录第2天了,所以,我们将数据处理一下。
现在我们有了用户本次登录日期和上次登录日期,只要用户本次登录和上次登录的间隔天数是1,就说明用户连续登录了,我们来看看
这样,我们就可以看到,我们根据本次登录时间和上次登录时间,计算出间隔天数,间隔为1的都是连续的日期,首次登录的时候,我们可以填入默认值0。这样看上去标注黄色的都是连续的日期,但是要注意,第一个标黄的日期表示2天。那我们该怎样统计出用户每次连续的天数呢?如果只把间隔天数为1的记录拿出来,好像又没有办法统计哪些天是连续一起的,我们再对这个间隔天数做个排序
这样,我们根据排序的序号,就可以将间隔天数为1的记录拿出来聚合了。
下面我们就需要将上面的想法转化成SQL:
由于我们要使用mysql,mysql中没有开窗函数,写起来很麻烦,需要多次使用变量;
pg版可以参考之前的文章:SQL笔试题-连续登录天数
-- 1. 获取间隔天数
-- 初始化变量
set @pre_login_date:=null,@pre_user_id:=null;
drop table if exists tmp_20180415_1;
create table tmp_20180415_1 as
select
user_id,
login_date,
pre_login_date,
-- 计算本次登录和上次登录的差值
coalesce(datediff(login_date,pre_login_date),0) as diff_days
from (
select
-- 当前记录
user_id, -- 当前user_id
login_date, -- 当前login_date
-- 上一条记录
@pre_user_id as pre_user_id,
if(@pre_user_id=user_id , @pre_login_date , null) as pre_login_date,
-- 初始化上一条记录
@pre_login_date:=login_date as cur_login_date,
@pre_user_id:=user_id as cur_user_id
from
tm_login_log
order by
user_id,login_date
) x
order by user_id,login_date;
-- 2.对间隔天数进行排序
set @pre_user_id:=null,@pre_diff_days:=0,@rn:=1;
select
user_id,rn , min(pre_login_date) as from_login_date,max(login_date) as to_login_date
from (
select
user_id,
login_date,
pre_login_date,
diff_days,
if(@pre_user_id=user_id,if(@pre_diff_days=diff_days,@rn:=@rn,@rn:=@rn+1),@rn:=1) as rn,
@pre_user_id:=user_id,
@pre_diff_days:=diff_days
from tmp_20180415_1
order by user_id,login_date
) x
where diff_days=1
group by user_id,rn;
结果时这样的,用户ID和他连续登录的日期,但是这个rn并没有什么用,如果要看连续登录天数的话,我们可以重新根据开始时间和结束时间进行计算。继续延伸的话,我们还可以统计用户最长的登录天数。
2.根据登录日期排序
还有一种类似的思路,就是首先根据登录日期排序,这样我们获得的排序序号就是连续的,然后再统计每个登录日期和一个初始日期的间隔天数,如果登录连续的话,2个值相减之后也可以用来判断是否连续。
感兴趣的同学可以试试MySQL的实现。
后记
其实大家主要了解思路就行了,使用mysql来实现,比较麻烦,不会写也没问题,当然应该还有更简便的方法,只是我还没有想到。在日常工作中,其实还有很多别的方式来更方便的实现,比如,我们可以创建一张用户每天登录的表,然后在刷新当天数据的同时,去判断昨天该用户有没有登录,有的话则计入连续;Hive中也支持各种开窗函数,写起来很简单。