题目:查看用户在某天刷题后第二天还会再来刷题的留存率
首先,分解题目
(一)查找同一个用户第二天的刷题记录,并保留未找到的数据
分析:把日期全部看成第一天,再自连查询第二天(日期间隔为1)的刷题记录。
细节:
- 因为一个用户一天内会多次提交所以需要使用distinct关键字去重。
- 因为内连接会自动去除未找到的数据,所以需要在join前面加上left
select distinct a.device_id,a.date day_one,b.date day_two from question_practice_detail a left join question_practice_detail b on a.device_id=b.device_id and datediff(b.date,a.date)=1;#条件:同一个用户,日期间隔为1
+-----------+------------+------------+ | device_id | day_one | day_two | +-----------+------------+------------+ | 2138 | 2021-05-03 | NULL | | 3214 | 2021-05-09 | NULL | | 3214 | 2021-06-15 | NULL | | 6543 | 2021-08-13 | NULL | | 2315 | 2021-08-13 | 2021-08-14 | | 2315 | 2021-08-14 | 2021-08-15 | | 2315 | 2021-08-15 | NULL | | 3214 | 2021-08-15 | 2021-08-16 | | 3214 | 2021-08-16 | NULL | | 3214 | 2021-08-18 | NULL | +-----------+------------+------------+ 10 rows in set (0.00 sec)
(二)求留存率
由“查询结果1”得知,一共10条记录其中有3条能找到第二天的做题记录。
留存率=two_day总数/总条数=3/10=0.3(符合用例)
这时候就有同学说,老师我懂了,我只要把上面这个查询当作子查询放在from后面然后select后面写count(day_two)/count(*)就可以了。
select count(t.day_two)/count(*) avg_ret from (select distinct a.device_id,a.date day_one,b.date day_two from question_practice_detail a left join question_practice_detail b on a.device_id=b.device_id and datediff(b.date,a.date)=1 ) t;
对没错你确实可以这么写,但如果我不想用子查询呢?
如果我们把1.1 select后面的语句换成 distinct a.device_id,b.date day_two,就能得到
+-----------+------------+ | device_id | day_two | +-----------+------------+ | 2138 | NULL | | 3214 | NULL | | 6543 | NULL | | 2315 | 2021-08-14 | | 2315 | 2021-08-15 | | 2315 | NULL | | 3214 | 2021-08-16 | +-----------+------------+ 7 rows in set (0.00 sec)
我们只需要关注day_two非空的数据和1.1查出来的是否一致就可以了,不用在意distinct在day_two为null时只根据device_id去重
因为count(distinct a.device_id,b.date)不会计算null值,所以select count(distinct a.device_id,b.date) ...执行结果为3
又因为select count(distinct a.device_id,a.date ,b.date)...执行结果为10
distinct a.device_id,a.date ,b.date可以把b.date简化掉,因为a.date确定a.date也能确定(a.date+1天=b.date)
最终:
select count(distinct a.device_id,b.date)/count(distinct a.device_id,a.date) as avg_ret from question_practice_detail a left join question_practice_detail b on a.device_id=b.device_id and datediff(b.date,a.date)=1;
这是我第一次写文章如有错漏或不清晰的地方欢迎指出,谢谢!