SQL<计算用户的平均次日留存率>的三种解法
题目
题目:现在运营想要查看用户在某天刷题后第二天还会再来刷题的留存率。请你取出相应数据。
牛客:SQL29 计算用户的平均次日留存率
示例:question_practice_detail
id | device_id | question_id | result | date |
---|---|---|---|---|
1 | 2138 | 111 | wrong | 2021-05-03 |
2 | 3214 | 112 | wrong | 2021-05-09 |
3 | 3214 | 113 | wrong | 2021-06-15 |
4 | 6543 | 111 | right | 2021-08-13 |
5 | 2315 | 115 | right | 2021-08-13 |
6 | 2315 | 116 | right | 2021-08-14 |
7 | 2315 | 117 | wrong | 2021-08-15 |
8 | 3214 | 112 | wrong | 2021-05-09 |
9 | 3214 | 113 | wrong | 2021-08-15 |
10 | 6543 | 111 | right | 2021-08-13 |
11 | 2315 | 115 | right | 2021-08-13 |
12 | 2315 | 116 | right | 2021-08-14 |
13 | 2315 | 117 | wrong | 2021-08-15 |
14 | 3214 | 112 | wrong | 2021-08-16 |
15 | 3214 | 113 | wrong | 2021-08-18 |
16 | 6543 | 111 | right | 2021-08-13 |
根据示例,你的查询应返回以下结果:
avg_ret |
---|
0.3000 |
题解
⚠️ 下述方法依次更好,若无需递进,可由后往前看。
方法1:留存日总数/日期总数——各用户的留存日/日期数量获取——留存日的判断与打标签
思路:分用户判断与打标签
1.通过窗口函数lag判断各个用户当前日相较前一日是否留存,并标记。
2.汇总每个用户的留存日总数;汇总每个用户的登陆日总数。
3.汇总所有用户的留存日总数、登陆日总数,相除得到留存率。
select round((sum(retention_sum) / sum(total_sum)), 4) avg_ret S3.汇总所有用户的留存日数量、登陆日数量,相除得到留存率。 from ( select device_id, sum(retention_status) retention_sum S2.汇总各用户的留存日总数 from ( select device_id, date, case when date - lag (date, 1) over ( partition by device_id order by date ) = 1 then 1 else 0 end as retention_status S1.通过日期相减 判断每个用户 相对于前一天 是否留存; 留存当日记录为1;因为order by date,所以重复日期的记录不会被多余标记 from question_practice_detail ) as sub group by device_id ) as sub_retention join ( 连接各用户的留存日数量、登录日数量表 select device_id, count(distinct date) as total_sum S2.汇总各用户登陆日总数; 因为数据不是截选,前后均没登录,考虑留存比较的登陆日数量时,登入日数量不用-1 from question_practice_detail group by device_id ) as sub_total on sub_retention.device_id = sub_total.device_id
方法2:留存日总数/日期总数——用户与日期的去重组合——留存日的判断与打标签
思路:利用用户与日期的去重组合
1.获取用户与登陆日的去重组合。
2.判断每个组合的登陆日留存情况,并标记。
3.汇总标记为留存的组合,除以组合总数,得到留存率。
SELECT SUM(IF(retention=1,1,0))/COUNT(device_id) avg_ret 汇总留存的用户、登陆日组合,除以所有用户、登陆日的组合 这里用到IF,因为两日期可能不连续,相减大于1,故只考虑1(留存情况)。 FROM( SELECT device_id, date, date - (lag(date,1) over(PARTITION BY device_id ORDER BY date )) AS retention 获取每个用户、登陆日组合的留存情况 FROM( SELECT DISTINCT device_id,date from question_practice_detail 用户、登陆日组合去重 ) sub_group ORDER BY device_id ) sub_retention
方法3:留存日总数/日期总数——用户与日期的去重组合——通过多字段表连接判断留存
思路:利用用户与日期的去重组合,利用多字段表连接判断留存。
1.去重组合用户、日期。
2.left join上datelag列,用多字段表连接进行匹配。
3.对主表进行count并相除。
select count(sub2.device_id)/count(sub1.device_id) as avg_ret sub1完整,left join的sub2仅包含匹配的datelag from( select distinct device_id, date datenow from question_practice_detail ) as sub1 用户、日期的去重组合 left join( LEFT JOIN: datenow完整呈现,对应展现datelag匹配值 select distinct device_id, date datelag from question_practice_detail ) as sub2 on sub1.device_id=sub2.device_id and sub1.datenow=sub2.datelag+1 用多字段表连接将同用户的datelag与datenow匹配