通过代码

select 
       a.dt,
       round(count(distinct b.uid)/count(a.uid),2) uv_left_rate
from (select 
             uid,
             min(date(in_time)) dt
      from 
           tb_user_log
      group by 
               uid) a
left join (
    select 
        uid , 
        date(in_time) dt
    from 
        tb_user_log
    union
    select 
        uid , 
        date(out_time) dt
    from 
        tb_user_log) b
on 
     a.uid = b.uid
and 
     a.dt = date_sub(b.dt, INTERVAL 1 day)
where 
      date_format(a.dt,'%Y-%m') = '2021-11'
group by 
         a.dt
order by 
         a.dt;

思路

统计2021年11月每天新用户的次日留存率(保留2位小数

注:

次日留存率为当天新增的用户数中第二天又活跃了的用户数占比

如果in_time-进入时间和out_time-离开时间跨天了,在两天里都记为该用户活跃过,结果按日期升序


那么问题来了,我们需要怎样的一张表来算出结果呢?

就是所有用户第一天上号的日期,以及对应的第二天日期这个人上号没,这个人的其他天数据没有用!

然后我们以用户第一天上号日期为组,计算当天人头数 / 后边对应第二天人头数就是留存率


好了,我们的需求已经知道了,

用户第一天上号日期好办,直接min就是第一次出现在表中的时间

表1.

select 
  uid,
  min(date(in_time)) dt
from 
  tb_user_log
group by 
  uid

怎么知道对应的第二天该用户上号没呢?

我们把这个人所有的上号记录找出来,与表1进行以表1为主表的外连接,筛选条件就是有第二天上号的记录没 连接之后,如果有就能count到,如果没有就是null,count会自动忽略。

然后题上说了

如果in_time-进入时间和out_time-离开时间跨天了,在两天里都记为该用户活跃过

这个条件怎么达成呢?

我们直接进行union

把in_time,out_itme union入同一列

因为union会自动过滤重复行

也就是说如果一个人是在今天in,但是在明天out,在这个新生成的上号表里就会有两条记录

表2.

select 
  uid , 
  date(in_time) dt
from 
  tb_user_log
union
select 
  uid , 
  date(out_time) dt
from 
  tb_user_log

我们用date_sub来计算差一的日期,也就是说表1 表2 连接条件是

a.uid = b.uid and a.dt = date_sub(b.dt, INTERVAL 1 day)

哦对了,最后别忘了count计算的时候要保留两位小数,还有筛选日期是2021年11月,以及最后的日期升序