数据描述 用户行为日志表tb_user_log id uid artical_id in_time out_time sign_cin 1 101 9001 2021-11-01 10:00:00 2021-11-01 10:00:31 0 2 102 9001 2021-11-01 10:00:00 2021-11-01 10:00:24 0 3 102 9002 2021-11-01 11:00:00 2021-11-01 11:00:11 0 4 101 9001 2021-11-02 10:00:00 2021-11-02 10:00:50 0 5 102 9002 2021-11-02 11:00:01 2021-11-02 11:00:24 0 (uid-用户ID, artical_id-文章ID, in_time-进入时间, out_time-离开时间, sign_in-是否签到) 场景逻辑说明:artical_id-文章ID代表用户浏览的文章的ID,artical_id-文章ID为0表示用户在非文章内容页(比如App内的列表页、活动页等)。 输出示例: 示例数据的输出结果如下 dt avg_viiew_len_sec 2021-11-01 33.0 2021-11-02 36.5 解释: 11月1日有2个人浏览文章,总共浏览时长为31+24+11=66秒,人均浏览33秒; 11月2日有2个人浏览文章,总共时长为50+23=73秒,人均时长为36.5秒。 SQL代码 输出示例: 示例数据的输出结果如下 artical_id max_uv 9001 3 9002 2 解释:10点0分10秒时,有3个用户正在浏览文章9001;11点01分0秒时,有2个用户正在浏览文章9002。 注意:题目要求在瞬时统计时遵循【先进后出】:如果同一时刻有进入也有离开时,先记录用户数增加,再记录减少。 本题难点在于如何计算瞬时的最大计数(在看人数) 首先,在此对原表in_time和out_time进行编码,in为观看人数+1, out为观看人数-1 然后,进行两次SELECT联立,并按artical_id升序,时间戳升序, 观看记录【先进后出】降序。最后通过max函数取到最大观看人数。 SQL代码 注: 输出示例: 示例数据的输出结果如下 dt uv_left_rate 2021-11-01 0.67 2021-11-02 1.00 2021-11-03 0.00 解释: 11.01有3个用户活跃101、102、103,均为新用户,在11.02只有101、103两个又活跃了,因此11.01的次日留存率为0.67; 11.02有104一位新用户,在11.03又活跃了,因此11.02的次日留存率为1.00; 11.03有105一位新用户,在11.04未活跃,因此11.03的次日留存率为0.00; 11.04没有新用户,不输出。 解题思路: SQL代码 注: 输出示例: 示例数据的输出结果如下 user_grade ratio 忠实用户 0.43 新晋用户 0.29 沉睡用户 0.14 流失用户 0.14 解释: 今天日期为2021.11.04,根据用户分级标准,用户行为日志表tb_user_log中忠实用户有:109、108、104;新晋用户有105、102;沉睡用户有103;流失用户有101;共7个用户,因此他们的比例分别为0.43、0.29、0.14、0.14。 解题思路: 先建立用户活跃信息表(包含用户id,最后一次活跃日期,成为新用户日期,以及“今天”日期),之后再根据等级划分的条件定义各分层用户,最后统计分层用户占比数据。 SQL代码 注: 输出示例: 示例数据的输出结果如下 dt dau uv_new_ratio 2021-10-30 2 1.00 2021-11-01 3 0.33 2021-11-02 3 0.67 2021-11-03 5 0.40 解释: 2021年10月31日有2个用户活跃,都为新用户,新用户占比1.00; 2021年11月1日有3个用户活跃,其中1个新用户,新用户占比0.33; 解题思路: 先建立一张用户活跃基础信息表(这张表要包含用户id,活跃日,注册日。因为用户可能1天活跃N次,所以要做去重处理,活跃日直接并联in_time和out_time); 其次定义新用户(如果dt=new_dt那么这天就是用户首次登录成为新用户的日子),再计算新用户占比; SQL代码 注:如果签到记录的in_time-进入时间和out_time-离开时间跨天了,也只记作in_time对应的日期签到了。 场景逻辑说明: 输出示例: 示例数据的输出结果如下: uid month coin 101 202107 15 102 202110 7 解释: 101在活动期内连续签到了7天,因此获得1*7+2+6=15金币; 102在10.01~10.03连续签到3天获得5金币 10.04断签了,10.05~10.06连续签到2天获得2金币,共得到7金币。 SQL代码问题:统计2021年11月每天的人均浏览文章时长(秒数),结果保留1位小数,并按时长由短到长排序。
select
date(in_time) dt,
round(sum(timestampdiff(second,in_time,out_time))/count(distinct uid),1) avg_viiew_len_sec
from tb_user_log
where date(in_time) like "2021-11%"
and artical_id !=0
group by dt
order by avg_viiew_len_sec
问题:统计每篇文章同一时刻最大在看人数,如果同一时刻有进入也有离开时,先记录用户数增加再记录减少,结果按最大人数降序。
with t as
(select artical_id,
sum(ct) over (partition by artical_id order by dt,ct desc ) uv
from
(select artical_id, in_time as dt , 1 ct from tb_user_log
union all
select artical_id, out_time as dt , -1 ct from tb_user_log) p
where artical_id !=0)
select artical_id, max(uv) max_uv
from t
group by artical_id
order by max_uv desc
问题:统计2021年11月每天新用户的次日留存率(保留2位小数)
select a.dt as dt,
round(count(b.uid)/count(a.uid),2) uv_left_rate
from
(select uid,min(date(in_time)) as dt
from tb_user_log
group by uid) a #注册表
left join
(
select uid,date(in_time) dt from tb_user_log b
union
select uid,date(out_time) dt from tb_user_log b
) as b #用户活跃表
on a.uid=b.uid and a.dt=date_sub(b.dt,interval 1 day)
where a.dt like "2021-11%"
group by a.dt
问题:统计活跃间隔对用户分级后,各活跃等级用户占比,结果保留两位小数,且按占比降序排序。
with a as
(select
case when datediff(now_dt,min_dt)<=6 then "新晋用户"
when datediff(now_dt,min_dt)>6 and datediff(now_dt,max_dt)<=6 then "忠实用户"
when datediff(now_dt,max_dt)>=7 and datediff(now_dt,max_dt)<=29 then "沉睡用户"
when datediff(now_dt,max_dt)>=30 then "流失用户"
end as user_grade
from
(select
uid,
min(date(in_time)) min_dt, #用户注册时间
max(date(out_time)) max_dt, #用户最后一次活跃时间
max(max(date(out_time)))over() now_dt #现在的时间
from tb_user_log
group by uid) t
)
select user_grade,round(count(user_grade)/sum(count(user_grade))over(),2) ratio
from a
group by user_grade
order by ratio desc;
问题:统计每天的日活数及新用户占比
select a.dt,
count(distinct a.uid) dau,
round(count(if(a.dt=b.new_dt,b.uid,null))/count(distinct a.uid),2) uv_new_ratio
from
(select distinct uid,date(in_time) dt from tb_user_log
union
select distinct uid,date(out_time) dt from tb_user_log ) a
left join
(
select uid,min(date(in_time)) new_dt from tb_user_log group by uid
)b
on a.uid=b.uid
group by a.dt
order by a.dt;
问题:计算每个用户2021年7月以来每月获得的金币数(该活动到10月底结束,11月1日开始的签到不再获得金币)。结果按月份、ID升序排序。
WITH t1 AS( -- t1表筛选出活动期间内的数据,并且为了防止一天有多次签到活动,distinct 去重
SELECT
DISTINCT uid,
DATE(in_time) dt,
DENSE_RANK() over(PARTITION BY uid ORDER BY DATE(in_time)) rn -- 编号
FROM
tb_user_log
WHERE
DATE(in_time) BETWEEN '2021-07-07' AND '2021-10-31' AND artical_id = 0 AND sign_in = 1
),
t2 AS (
SELECT
*,
DATE_SUB(dt,INTERVAL rn day) dt_tmp,
case DENSE_RANK() over(PARTITION BY DATE_SUB(dt,INTERVAL rn day),uid ORDER BY dt )%7 -- 再次编号
WHEN 3 THEN 3
WHEN 0 THEN 7
ELSE 1
END as day_coin -- 用户当天签到时应该获得的金币数
FROM
t1
)
SELECT
uid,DATE_FORMAT(dt,'%Y%m') `month`, sum(day_coin) coin -- 总金币数
FROM
t2
GROUP BY
uid,DATE_FORMAT(dt,'%Y%m')
ORDER BY
DATE_FORMAT(dt,'%Y%m'),uid;