【场景】:连续签到、连续的表示

【分类】:窗口函数、date(in_time) - row_number()

分析思路

难点:

1.如何表示连接? 连续的表示: 日期减去排序的值相等

2.如何根据断签来分组?基于连续的表示,值相等就可以分组!

新学到:

连续的表示: 日期减去排序的值相等。

金币累计计算: 排序后取余

(1)统计用户签到的月份、日期、按用户分组对日期排序、日期减去排序的值

注:如果签到记录的in_time-进入时间和out_time-离开时间跨天了,也只记作in_time对应的日期签到了。所以只计算 in_time;

连续的表示: 日期减去排序的值相等;

  • [使用]:date(in_time) - row_number()

(2)统计每一行根据用户、日期减去排序的值分组排序的值

  • [使用]:row_number()

(3)根据排序结果计算签到积分

注:满足连续签到3天的条件 额外加2;满足连续签到7天条件 额外加6;

每连续签到7天后重新累积签到天数(即重置签到天数:连续第8天签到时记为新的一轮签到的第一天,领1金币):取余的手段;

  • [条件]:只有artical_id为0时sign_in值才有效

  • [使用]:group by 分组条件 [日期];order by 对查询结果排序 [日期升序];

扩展

前往查看: MySQL 连续记录 场景分析

求解代码

方法一:

with
    main as(
        #统计用户签到的月份、日期、按用户分组对日期排序、日期减去排序的值
        select
            uid,
            date_format(in_time,'%Y%m') as month,
            date(in_time) as dt,
            row_number() over (partition by uid order by in_time) as drk,
            date(in_time) - row_number() over (partition by uid order by in_time) as dt_drk
        from tb_user_log
        where artical_id = 0
        and sign_in = 1
        and date(in_time) between '2021-07-07' and '2021-10-31'
    )
    ,main1 as(
        #统计每个用户每月每个连续签到的积分
        select
            uid,
            month,
            dt_drk,
            count(*)+floor(count(*)/7)*(2+6)+floor(count(*)%7/3)*2 as num_coin
        from main
        group by uid,month,dt_drk
    )
#统计每个用户每月连续签到的积分
select
    uid,
    month,
    sum(num_coin) as coin
from main1
group by uid,month
order by month,uid

main表的查询结果:

1	101|202107|2021-07-07|1|20210706	
2	101|202107|2021-07-08|2|20210706	
3	101|202107|2021-07-09|3|20210706		
4	101|202107|2021-07-10|4|20210706		
5	101|202107|2021-07-11|5|20210706		
6	101|202107|2021-07-12|6|20210706		
7	101|202107|2021-07-13|7|20210706		
8	102|202110|2021-10-01|1|20211000		
9	102|202110|2021-10-02|2|20211000		
10	102|202110|2021-10-03|3|20211000		
11	102|202110|2021-10-05|4|20211001		
12	102|202110|2021-10-06|5|20211001		

main1表的查询结果:

1	101|202107|2021-07-07|1|20210706|1
2	101|202107|2021-07-08|2|20210706|2	
3	101|202107|2021-07-09|3|20210706|3		
4	101|202107|2021-07-10|4|20210706|4		
5	101|202107|2021-07-11|5|20210706|5		
6	101|202107|2021-07-12|6|20210706|6		
7	101|202107|2021-07-13|7|20210706|7		
8	102|202110|2021-10-01|1|20211000|1		
9	102|202110|2021-10-02|2|20211000|2		
10	102|202110|2021-10-03|3|20211000|3		
11	102|202110|2021-10-05|4|20211001|1		
12	102|202110|2021-10-06|5|20211001|2		

方法二:

优化方法

with
    main as(
        #统计用户签到的月份、日期、按用户分组对日期排序、日期减去排序的值
        select
            uid,
            date_format(in_time,'%Y%m') as month,
            date(in_time) as dt,
            row_number() over (partition by uid order by in_time) as drk,
            date(in_time) - row_number() over (partition by uid order by in_time) as dt_drk
        from tb_user_log
        where artical_id = 0
        and sign_in = 1
        and date(in_time) between '2021-07-07' and '2021-10-31'
    )
    ,main1 as(
        #统计每一行根据用户、日期减去排序的值分组排序的值
        select
            *,
            row_number() over(partition by uid,dt_drk order by dt) as rk
        from main
    )
#对每一行取余来判断是否额外加2或者额外加6;取余也解决了7天之后重置的情况
select
    uid,
    month,
    sum(case 
            when rk%7=0 then 7
            when rk%7=3 then 3
            else 1
        end) as coin
from main1
group by uid,month
order by month,uid