百度用户增长SQL面试题

x33g5p2x  于2022-04-10 转载在 其他  
字(6.1k)|赞(0)|评价(0)|浏览(321)

🌹今天我们来刷点sql题,先说一下这几道题的侧重点吧,主要有常考点留存率、连续登陆天数的问题,以及其他像用户分级、最大观看时长的统计。对往期内容感兴趣的同学可以参考如下内容👇:

  • 链接: 牛客SQL大厂真题——某音短视频.
  • 链接: 京东数据分析SQL面试题.

🌰话不多说,让我们开始今日份的学习吧。

1. 题目介绍

这里有一张描述用户行为日志表tb_user_log,表的数据内容如下:

uid-用户ID, artical_id-文章ID, in_time-进入时间, out_time-离开时间, sign_in-是否签到

| id | uid | artical_id | in_time | out_time | sign_in |
| 1 | 101 | 0 | 2021-07-07 10:00:00 | 2021-07-07 10:00:09 | 1 |
| 2 | 101 | 0<br> | 2021-07-08 10:00:00 | 2021-07-08 10:00:09 | 1 |
| 3 | 101 | 0 | 2021-07-09 10:00:00 | 2021-07-09 10:00:42 | 1 |
| 4 | 101 | 0 | 2021-07-10 10:00:00 | 2021-07-10 10:00:09 | 1 |
| 5 | 101 | 0<br> | 2021-07-11 23:59:55 | 2021-07-11 23:59:59<br> | 1 |
| 6<br> | 101 | 0 | 2021-07-12 10:00:28<br> | 2021-07-12 10:00:50<br> | 1 |
| 7 | 101 | 0 | 2021-07-13 10:00:28<br> | 2021-07-13 10:00:50 | 1 |
| 8<br> | 102 | 0 | 2021-10-01 10:00:28 | 2021-10-01 10:00:50<br> | 1 |
| 9<br> | 102 | 0 | 2021-10-02 10:00:01 | 2021-10-02 10:01:50<br> | 1 |
| 10 | 102 | 0<br> | 2021-10-03 10:00:55 | 2021-10-03 11:00:59 | 1 |
| 11<br> | 102 | 0<br> | 2021-10-04 10:00:45<br> | 2021-10-04 11:00:55<br> | 0 |
| 12 | 102 | 0<br> | 2021-10-05 10:00:53<br> | 2021-10-05 11:00:59<br> | 1 |
| 13 | 102 | 0<br> | 2021-10-06 10:00:45<br> | 2021-10-06 11:00:55<br> | 1 |

注意 :artical_id-文章ID代表用户浏览的文章的ID,特殊情况artical_id-文章ID为0表示用户在非文章内容页(比如App内的列表页、活动页等)。注意:只有artical_id为0时sign_in值才有效。

2. 统计人均浏览文章时长

问题:统计2021年11月每天的人均浏览文章时长(秒数),结果保留1位小数,并按时长由短到长排序。

解题思路:求出2021年11月每日的浏览文章时长总和,然后处以人数(去重)即可解决问题

# timestampdiff函数两个时间点相减,小的时间在前,大的在后
select date(out_time),round(sum(timestampdiff(second,in_time,out_time))/count(distinct uid),1)avg_viiew_len_sec
from tb_user_log t
where date_format(out_time,'%Y-%m')='2021-11' and artical_id !='0'
group by date(out_time)
order by avg_viiew_len_sec

3. 每篇文章同一时刻最大在看人数

问题:统计每篇文章同一时刻最大在看人数,如果同一时刻有进入也有离开时,先记录用户数增加再记录减少,结果按最大人数降序。

解题思路:这道题需要统计同一时刻最大在看人数,那我们怎么知道同一时刻是那个时刻呢?很多小伙伴可能会觉得采用in_time和out_time进行判断,取交集,这样是正确的,但是实际操作起来,我们需要2条数据之间相互比较,比较的次数太多了。我们可以换种思路:

-- 这里将用户的开始时间和结束时间进行union all,并进行排序,这样,我们得到了一个时间排序的表,通过累加的方式可以判断每个时间点有多少人在线,取最大即可。
-- 这里需要说明,union会去重,而union all不会去重。
select artical_id,in_time dt, 1 num
from tb_user_log
where artical_id!=0
union all
select artical_id,out_time dt, -1 num
from tb_user_log
where artical_id!=0
order by 1,2

有了上面的提示,我们解决这道题就简单了:

select tmp2.artical_id,max(tmp2.total) total
from(
-- 通过sum()over(partition by)来判断某时刻的最大在线人数
select tmp1.artical_id,sum(tmp1.num)over(partition by tmp1.artical_id order by tmp1.dt,tmp1.num desc) total
from(
select artical_id,in_time dt, 1 num
from tb_user_log
where artical_id!=0
union all
select artical_id,out_time dt, -1 num
from tb_user_log
where artical_id!=0
order by 1,2
)tmp1)tmp2
group by tmp2.artical_id
order by total desc

4. 新用户的次日留存率

问题:统计2021年11月每天新用户的次日留存率(保留2位小数)
解题思路:这道题是面试中被问的频率最高的题目之一,留存率的问题,留存率需要求解2个部分,新增用户和活跃用户。次日留存率为当天新增的用户数中第二天又活跃了的用户数占比。

select tmp1.dt,round(count(tmp2.uid)/count(tmp1.uid),2) rate
from
-- 主表为用户第一次登陆的日期
(select uid,min(date(in_time)) dt
from tb_user_log t
group by uid
 having date_format(dt,'%Y-%m')='2021-11'
)tmp1
-- 左链接用户次日登入的日期
left join (select uid,date(in_time) dt
           from tb_user_log 
           union
           select uid,date(out_time) dt
           from tb_user_log 
           --链接条件是第一次登入日期的后一天
)tmp2 on tmp1.uid=tmp2.uid and date_sub(tmp2.dt,interval 1 day)=tmp1.dt
group by tmp1.dt
order by tmp1.dt

5. 统计活跃间隔对用户分级结果

问题:统计活跃间隔对用户分级后,各活跃等级用户占比,结果保留两位小数,且按占比降序排序。

:忠实用户(近7天活跃过且非新晋用户)、新晋用户(近7天新增)、沉睡用户(近7天未活跃但更早前活跃过)、流失用户(近30天未活跃但更早前活跃过)。
假设今天就是数据中所有日期的最大值。
近7天表示包含当天T的近7天,即闭区间[T-6, T]。

解题思路:这道题主要是理解好题意,我们需要根据用户第一次登陆的时间、今日时间,和最近登陆时间来判断用户登陆的等级,根据最后登陆时间和今日时间的差值来判断是否最近7天登陆,根据今日时间和登录时间的差值来判断是否是新用户

select tmp3.user_grade,round(count(1)/(select count(distinct uid) from tb_user_log),2) ratio
from(
select tmp1.uid uid,
-- 根据最后登陆时间和今日时间的差值来判断是否最近7天登陆,根据今日时间和登录时间的差值来判断是否是新用户
case when datediff((select max(date(out_time)) from tb_user_log),tmp2.dt2)<7
and datediff((select max(date(out_time)) from tb_user_log),tmp1.dt1)>=7 then '忠实用户'
when datediff((select max(date(out_time)) from tb_user_log),tmp2.dt2)<7
and datediff((select max(date(out_time)) from tb_user_log),tmp1.dt1)<7 then '新晋用户'
when datediff((select max(date(out_time)) from tb_user_log),tmp2.dt2) between 7 and 29 then '沉睡用户'
when datediff((select max(date(out_time)) from tb_user_log),tmp2.dt2)>=30 then '流失用户' end user_grade	
from(
select uid,min(date(in_time)) dt1
from tb_user_log
group by uid)tmp1
left join (
select uid,max(date(in_time)) dt2
from tb_user_log
group by uid
)tmp2 on tmp1.uid=tmp2.uid)tmp3
group by tmp3.user_grade
order by ratio desc

6. 每天的日活数及新用户占比

问题:统计每天的日活数及新用户占比


新用户占比=当天的新用户数÷当天活跃用户数(日活数)。
如果in_time-进入时间和out_time-离开时间跨天了,在两天里都记为该用户活跃过。
新用户占比保留2位小数,结果按日期升序排序。

解题思路:这道题比较常规,主要求当天的新用户数,和当天活跃用户数,这两部分的指标,最后相除即可

select t1.dt,t1.num1,coalesce(round(t2.num2/t1.num1,2),0) uv_new_ratio--相除即可
from
(select tmp1.dt dt,count(1) num1 
from(
select uid,date(in_time) dt
from tb_user_log
union
select uid,date(out_time) dt
from tb_user_log)tmp1
group by tmp1.dt)t1 --求当天活跃的用户啊
left join 
(select tmp2.dt dt,count(tmp2.uid) num2
from(
select uid,min(date(in_time)) dt
from tb_user_log
group by uid)tmp2-- 求当天新注册的用户
group by tmp2.dt)t2
on t1.dt=t2.dt
order by t1.dt

7. 连续签到领金币

问题:求用户签到获得多少金币,按uid ,月份排序
:artical_id-文章ID代表用户浏览的文章的ID,特殊情况artical_id-文章ID为0表示用户在非文章内容页(比如App内的列表页、活动页等)。注意:只有artical_id为0时sign_in值才有效。

从2021年7月7日0点开始,用户每天签到可以领1金币,并可以开始累积签到天数,连续签到的第3、7天分别可额外领2、6金币。
每连续签到7天后重新累积签到天数(即重置签到天数:连续第8天签到时记为新的一轮签到的第一天,领1金币)

问题:计算每个用户2021年7月以来每月获得的金币数(该活动到10月底结束,11月1日开始的签到不再获得金币)。结果按月份、ID升序排序。

如果签到记录的in_time-进入时间和out_time-离开时间跨天了,也只记作in_time对应的日期签到了。
解题思路:看完题目可知,我们要求的是连续登陆的天数,连续登陆3天可以多获得2进步,连续登陆7天可以多获得6金币,但是最后的结果需要以月份的方式分类,也就是说,我们需要计算每一天获取的金币数,然后按照月份分组求和即可。

-- 最后分组求和即可
select tmp3.uid,date_format(tmp3.dt,'%Y%m'),sum(d)
from 
(
-- 这一步还需要排序,这是因为我们需要用排序来判断今天是连续登陆的第几天,以次来计算金币数
select tmp2.uid,tmp2.dt,date_sub(tmp2.dt,interval rk day) ,
    case when (dense_rank()over(partition by tmp2.uid,date_sub(tmp2.dt,interval rk day) order by tmp2.dt))%7 =3 then 3
    when (dense_rank()over(partition by tmp2.uid,date_sub(tmp2.dt,interval rk day) order by tmp2.dt))%7 =0 then 7
    else 1 end d
from (
-- 这一步就是求连续登陆天数的方法,row_number排序,之后在用登陆时间剪去排序编号
select tmp1.uid,tmp1.dt,row_number()over(partition by tmp1.uid order by tmp1.dt ) rk
from(
select uid,date(in_time) dt
from tb_user_log
where artical_id=0 and sign_in=1 and in_time between '2021-07-07 00:00:00' and '2021-10-31 23:59:59'
group by uid,date(in_time))tmp1)tmp2)tmp3
group by tmp3.uid,date_format(tmp3.dt,'%Y%m')
order by date_format(tmp3.dt,'%Y%m'),tmp3.uid

8.总结

这几道题中,统计某一时刻最大观看人数、新用户的次日留存率和连续签到领金币都是需要一定的技巧才能完成的题目,也是面试过程中的常考题,大家需要注意一下。

相关文章