问题
需求 : 计算每个用户最大的连续登录天数,可以间隔一天。解释:如果一个用户在第 1, 3, 5, 6, 9 天登录游戏,则视为连续 6 天登录
CREATE TABLE log (
id VARCHAR(1),
time date
);
insert into log values('A','2024-12-20');
insert into log values('A','2024-12-24');
insert into log values('A','2024-12-22');
insert into log values('A','2024-12-25');
insert into log values('A','2024-12-28');
insert into log values('A','2024-12-30');
insert into log values('A','2024-12-31');
insert into log values('B','2024-12-20');
insert into log values('B','2024-12-21');
insert into log values('B','2024-12-23');
insert into log values('B','2024-12-24');
如上表格,正确结果应该输出一下结果。
解释:
- A用户有两段连续登录记录,一段是从
2024-12-20
登录到2024-12-25
,中间间隔日期只有1天,所以共6天,另一段是2024-12-28
到2024-12-31
,中间间隔日期只有15天,共4天。这里请注意区分为什么是两段连续登录而不是一段连续登录,原因:A用户2024-12-28
到2024-12-30
是间隔2天。 - B用户有一段连续登录记录,是从
2024-12-20
登录到2024-12-24
,共5天 - A用户取最大连续登录为6天, B用户取最大连续登录是5天
id | cont_day |
---|---|
A | 6 |
B | 5 |
方案:
两个方法的核心切入点都是
- 求每条记录与前一条记录的差值,只是后续处理差值的逻辑不同。
- 目的都是找出一个字段,能够区分不同的连续登录片段。
方法一:
思路:
- 计算每个记录的日期(time字段)与前一条记录的日期的差值, 记为diff字段, 注意按日期排序,方法是使用
lag
开窗函数 - 如果差值(diff字段)小于等于2,说明间隔小于等于1天,直接保留差值的原本值即可,如果差值大于2天,说明间隔大于1天,已经开始新的连续登录日期了, 我们把差值置为0。
- 对差值累加求和sum(注意要加上order by, 若无order by则无法实现累加功能), sum实际上表达的是“ 与 连续登录区域的第一天的差值” ,这个可以想一想。我们将求和sum记为con_day字段
- 使用日期 减去 con_day,即可求出每段连续登录记录的第一天, 记为start_date
- group by id, start_date, 然后求出每个group的最大和最小日期,使用“最大日期 - 最小日期+1” ,就是连续登录连段的连续登录天数,记为cont_day
- 再group by id, 求最大的cont_day, 就是答案。
select id, max(cont_day) as max_cont_day
from
(
select id, start_date, max(time) as maxd , min(time) as mind, datediff(max(time), min(time)) + 1 as cont_day
from
(
select id, time, pre_date, diff, con_day, date_sub(time, interval con_day day) as start_date
from
(
select id, time, pre_date, diff, sum(diff) over(partition by id order by time) as con_day
from
(
select id, time ,pre_date, if(datediff(time, pre_date) > 2, 0, datediff(time, pre_date)) as diff
from
(
SELECT
id, time,
lag(time,1,'1970-01-01') over(partition by id order by time) as pre_date
from log
) t0
) t1
) t2
) t3
group by id, start_date
) t4
group by id
方法二
思路:
- 计算每个记录的日期(time字段)与前一条记录的日期的差值, 记为diff字段, 注意按日期排序,方法是使用
lag
开窗函数 - 如果差值(diff字段)小于等于2,说明间隔小于等于1天,则置为0,如果差值大于2天,说明间隔大于1天,已经开始新的连续登录日期了, 我们把差值置为1。 diff实际表达了是否开始了一个新的连续片段。
- 对差值累加求和sum(注意要加上order by, 若无order by则无法实现累加功能), 我们将求和sum记为flag字段。每开始一段新的连续片段时, sum会加一次1。
- group by id, flag, 然后求出每个group的最大和最小日期,使用“最大日期 - 最小日期+1” ,就是连续登录连段的连续登录天数,记为cont_day
- 再group by id, 求最大的cont_day, 就是答案。
select id, max(cont_day) as max_cont_day
from
(
select id, flag, max(time), min(time), datediff(max(time), min(time))+1 as cont_day
from
(
select id, time, pre_date, diff, sum(diff) over(partition by id order by time) as flag
from
(
select id, time ,pre_date, if(datediff(time, pre_date) > 2, 1, 0) as diff
from
(
SELECT
id, time,
lag(time,1,'1970-01-01') over(partition by id order by time) as pre_date
from log
) t0
) t1
) t2
group by id, flag
) t3
group by id