牛客SQL大厂真题——某音短视频

x33g5p2x  于2022-02-28 转载在 其他  
字(8.8k)|赞(0)|评价(0)|浏览(298)

☀️这几天复习了一下SQL的基本语法,于是昨天晚上一连做了20多道题,发现题目比较简单,于是便找到了牛客SQL大厂真题——某音短视频的题来做做。说一下我复习的SQL的文章都是我自己以前总结的,链接如下👇:

  • 重要部分1: MySQL之函数和谓词.
  • 重要部分2: SQL进阶篇.
  • 其他的内容: SQL的专栏.

🐯某音短视频的题总共6道,完成这6道题的代码逻辑都是独立完成,只是过程中查了一些忘记了的函数,本博客的最后,我会将这些函数都总结起来,多看多记多理解。让我们开始今日份的学习。

先给大家看一下题目难度和通过情况。平均通过率在32%吧。

题目介绍

表1(用户-视频互动表tb_user_video_log)

iduidvideo_idstart_timeend_timeif_followif_likeif_retweetcomment_id
110120012021-10-01 10:00:002021-10-01 10:00:30011NULL
210220012021-10-01 10:00:002021-10-01 10:00:24001NULL
310320012021-10-01 11:00:002021-10-01 11:00:340101732526
410120022021-09-01 10:00:002021-9-01 10:00:42101NULL
510220022021-10-01 11:00:002021-10-01 10:00:30101NULL

uid:用户ID
video_id:视频ID
start_time:开始观看时间
end_time:结束观看时间
if_follow:是否关注 (0不关注、1)
if_like:是否点赞
if_retweet:是否转发
comment_id:评论ID

表2(短视频信息表tb_video_info)

idvideo_idauthortagdurationrelease_time
12001901影视302021-01-01 07:00:00
22002901美食602021-01-01 07:00:00
32003902旅游902021-01-01 07:00:00

video_id:视频ID
author:创作者ID
tag:类别标签
duration:视频时长(秒)
release_time:发布时间

1. 各个视频的平均完播率

题目:计算2021年里有播放记录的每个视频的完播率(结果保留三位小数),并按完播率降序排序
注:视频完播率是指完成播放次数占总播放次数的比例。简单起见,结束观看时间与开始播放时间的差>=视频时长时,视为完成播放。
输出结果:

解题思路:我们先确定计算指标,完播率,而完播率是根据用户播放视频时长和视频本身时长比较得出的,有了这些思路,我们就可以开始编程了。

-- 最外层查计算完播率
select video_id,round(sum(tmp2.tag)/count(tmp2.tag),3) rk
from (
-- 第二个子查询计算看完视频的用户人数
select video_id, case when tmp1.see_time>=tmp1.duration then 1 else 0 end tag
from(
-- 第一个子查询计算出用户观看时长see_time
select t1.video_id video_id,TIMESTAMPDIFF(second,t1.start_time,t1.end_time) see_time,t2.duration duration
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id=t2.video_id
WHERE year(t1.start_time)='2021'

)tmp1)tmp2
group by video_id 
ORDER by rk DESC

在第一题中,我分段展示了每一步我要求的指标,然后一层一层进行求解,其实在这过程中,有很多值得优化的地方,比如可以将中间两个字查询合并为一个,并且case when也可以用if()来代替,可以更加简洁。

select t1.video_id video_id,if(TIMESTAMPDIFF(second,t1.start_time,t1.end_time)>=t2.duration,1,0) tag,
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id=t2.video_id
WHERE year(t1.start_time)='2021'

2. 平均播放进度大于60%的视频类别

问题:计算各类视频的平均播放进度,将进度大于60%的类别输出。
注:
播放进度=播放时长÷视频时长*100%,当播放时长大于视频时长时,播放进度均记为100%。
结果保留两位小数,并按播放进度倒序排序。

输出结果:

解题思路:这道题的指标计算的是视频的平均播放进度,播放进度就是播放时长/视频时长,和上题比较相似。

--最后将进度转化为百分比形式
SELECT tmp2.tag,concat(round(tmp2.avg_play_progress_rate*100,2),'%') avg_play_progress
FROM(
--第二个子查询分组计算平均播放进度,筛选出播放进度大于60%的视频类型
SELECT tmp1.tag tag,sum(tmp1.rate)/count(tmp1.tag) avg_play_progress_rate
FROM(
--第一个字查询用来查询每个用户的视频播放进度
SELECT t1.uid uid ,t1.video_id video_id ,t2.tag tag,if(TIMESTAMPDIFF(second,t1.start_time,t1.end_time)>=t2.duration,t2.duration,TIMESTAMPDIFF(second,t1.start_time,t1.end_time))/t2.duration rate
FROM tb_user_video_log t1 LEFT JOIN tb_video_info t2
on t1.video_id = t2.video_id
)tmp1
GROUP BY tmp1.tag 
HAVING avg_play_progress_rate>0.6)tmp2
order by avg_play_progress DESC

这道题也有值得优化的地方,第二个字查询和进度转化为百分比形式可以进行合并,更复杂一点,也可以将这三个查询合并为一个查询,但是不易读。

--第二个子查询分组计算平均播放进度,筛选出播放进度大于60%的视频类型
SELECT tmp1.tag tag,concat(round(sum(tmp1.rate)*100/count(tmp1.tag),2),'%')avg_play_progress
FROM(
--第一个字查询用来查询每个用户的视频播放进度
SELECT t1.uid uid ,t1.video_id video_id ,t2.tag tag,if(TIMESTAMPDIFF(second,t1.start_time,t1.end_time)>=t2.duration,t2.duration,TIMESTAMPDIFF(second,t1.start_time,t1.end_time))/t2.duration rate
FROM tb_user_video_log t1 LEFT JOIN tb_video_info t2
on t1.video_id = t2.video_id
)tmp1
GROUP BY tmp1.tag 
HAVING sum(tmp1.rate)/count(tmp1.tag)>0.6
order by avg_play_progress DESC

3. 每类视频近一个月的转发量/率

问题:统计在有用户互动的最近一个月(按包含当天在内的近30天算,比如10月31日的近30天为10.2~10.31之间的数据)中,每类视频的转发量和转发率(保留3位小数)。
注:转发率=转发量÷播放量。结果按转发率降序排序,用户互动的最近一个月是指(用户-视频互动表tb_user_video_log)表中,start_time的最大日期。
输出结果

解题思路:这道题首先确定纬度:最近一个月,指标是转发量、转发率,转发率是根据转发量计算。由题意可知,我们需要先求出最大日期。然后判断最近一个月的互动情况。

-- 分组计算转发量和转发率
SELECT tmp1.tag,sum(tmp1.if_retweet),round(sum(tmp1.if_retweet)/count(tmp1.if_retweet),3) retweet_rate
from(
-- 第一个字查询查询出近一个月以来的用户互动的转发量数据
select t1.uid,t1.video_id,t1.if_retweet,t2.tag,DATEDIFF(DATE((SELECT max(start_time) FROM tb_user_video_log)),DATE(t1.start_time)) days
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id = t2.video_id
WHERE DATEDIFF(DATE((SELECT max(start_time) FROM tb_user_video_log)),DATE(t1.start_time))<=29)tmp1
GROUP BY tmp1.tag
order by retweet_rate desc

这道题比较简单,有其他方法的同学可以自己试一试。

4. 每个创作者每月的涨粉率及截止当前的总粉丝量

问题:计算2021年里每个创作者每月的涨粉率及截止当月的总粉丝量
注:
涨粉率=(加粉量 - 掉粉量) / 播放量。结果按创作者ID、总粉丝量升序排序。
if_follow-是否关注为1表示用户观看视频中关注了视频创作者,为0表示此次互动前后关注状态未发生变化,为2表示本次观看过程中取消了关注。

输出结果:

解题思路:这道题的指标是涨粉率,加粉量,掉粉量,播放量,我们只要按照字段进行统计即可,主要的难度在总粉丝数上,解法是:我们可以统计出每个月的粉丝数,然后窗口函数计算当前行到以前所有行的粉丝数之和即可。

-- 最后我根据sum()over()窗口聚合函数将前几个月的粉丝数相加,得到每月总粉丝数。
SELECT tmp2.author,tmp2.display_time,tmp2.fans_groupth_rate,sum(tmp2.total_fans)over(partition by tmp2.author order by tmp2.display_time) total_fans
FROM(
-- 第二个子查询,我计算了涨粉率和每月增长的粉丝数
select tmp1.author,tmp1.display_time,round((sum(case when if_follow=1 then 1 else 0 end)-sum(case when if_follow=2 then 1 else 0 end))/count(1),3) fans_groupth_rate,sum(case when if_follow=1 then 1 else 0 end)-sum(case when if_follow=2 then 1 else 0 end) total_fans
from (
-- 第一个字查询,我将时间转化为了日期形式
select t1.video_id,t2.author,date_format(t1.start_time,'%Y-%m') display_time,t1.if_follow
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id =t2.video_id 
where year(t1.start_time)='2021')tmp1
group by tmp1.author,tmp1.display_time)tmp2
order by tmp2.author,total_fans

这道题可以改进的地方就是转换时间的子查询可以和计算指标的子查询合并为一个,case when 也可以变为if,但最重要的是sum()over()窗口函数,sum窗口函数默认的范围是当前行之前,但也可以自己设定前几行。

-- 窗口聚合函数rows between unbounded preceding and current row
--前面所有行与当前行的累加(分区内)

SELECT tmp2.author,tmp2.display_time,tmp2.fans_groupth_rate,sum(tmp2.total_fans)over(partition by tmp2.author order by tmp2.display_time rows between unbounded preceding and current row) total_fans

5. 国庆期间每类视频点赞量和转发量

问题:统计2021年国庆头3天每类视频每天的近一周总点赞量和一周内最大单天转发量,结果按视频类别降序、日期升序排序。假设数据库中数据足够多,至少每个类别下国庆头3天及之前一周的每天都有播放记录。
注:前一周是指(比如:10月1日,前一周就是9月25到10月1日)
输出结果:

解题思路:需要计算的维度是近一周,指标是近一周总点赞量和近一周最大单天转发量,我们只要计算出每天到点赞量和转发量,再用窗口大小为7的窗口函数即可完成。

-- 按照排序
select tmp3.tag,tmp3.dt,tmp3.sum_like_cnt_7d,tmp3.max_retweet_cnt_7d
from(
-- 第三个子查询计算近7天的指标
SELECT tmp2.tag,tmp2.dt,sum(tmp2.like_num)over(partition by tmp2.tag order by tmp2.dt rows between 6 preceding and current row) sum_like_cnt_7d ,max(tmp2.retweet_num)over(partition by tmp2.tag order by tmp2.dt rows between 6 preceding and current row) max_retweet_cnt_7d 
FROM(
-- 第二个字查询进行了分组求和,统计每天的指标
SELECT tmp1.tag,tmp1.dt,sum(tmp1.if_like) like_num,sum(tmp1.if_retweet) retweet_num
from(
-- 第一个子查询依旧是时间类型转化,和找出自己需要的字段
select t1.video_id,t2.tag,DATE_FORMAT(t1.start_time,'%Y-%m-%d') dt,t1.if_like,t1.if_retweet
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id=t2.video_id
where year(t1.start_time)='2021')tmp1
GROUP BY tmp1.tag,tmp1.dt)tmp2)tmp3
where tmp3.dt in ('2021-10-01','2021-10-02','2021-10-03')
ORDER BY tmp3.tag DESC ,tmp3.dt

这里需要注意的就是我们的窗口函数用到了当前行和前6行求和,需要优化的地方,我认为在第一个字查询、第二个字查询可以进行合并,必要的话,第一个和第二、第三个都可以进行合并。窗口函数的执行是在where语句之后,所以必须先窗口函数再筛选日期,这就是为什么我最后一步也需要用一个子查询。

6. 近一个月发布的视频中热度最高的top3视频

问题:找出近一个月发布的视频中热度最高的top3视频。
**注:热 度 = ( 视 频 完 播 率 + b ∗ 点 赞 数 + c ∗ 评 论 数 + d ∗ 转 发 数 ) ∗ 新 鲜 度 热度=(视频完播率+b点赞数+c评论数+d转发数)新鲜度热度=(视频完播率+b∗点赞数+c∗评论数+d∗转发数)∗新鲜度
新 鲜 度 = 1 / ( 最 近 无 播 放 天 数 + 1 ) 新鲜度=1/(最近无播放天数+1)新鲜度=1/(最近无播放天数+1)
当前配置的参数a,b,c,d分别为100、5、3、2。
最近播放日期以end_time-结束观看时间为准,假设为T,则最近一个月按[T-29, T]闭区间统计。
结果中热度保留为整数,并按热度降序排序。

输出结果

解题思路:我们这里需要计算的指标:视频完播率、点赞数、评论数、转发数、新鲜度。这里难点在于新鲜度的指标的理解,是指最近没有被观看的天数(各类型视频最近无播放天数=所有视频中最后播放的时间-各类型视频最后播放的时间),这里还需要注意的是近一个月发布的限定,我们需要先筛选出满足近一个月发布的视频。

-- 最后直接根据公式进行计算
SELECT tmp1.video_id, round((tmp1.see_rate*100+tmp1.like_num*5+tmp1.commit_num*3+tmp1.ret_num*2)/(tmp1.diff_days+1),0) hot_index
FROM(
-- 第一个字查询,我筛选出来近一个月发布的视频,视频完播率、点赞数、评论数、转发数、最近无播放天数
select t1.video_id, DATEDIFF(date((select max(end_time) from tb_user_video_log)),DATE(max(t1.end_time))) diff_days,SUM(if(TIMESTAMPDIFF(second,t1.start_time,t1.end_time)>=duration,1,0))/COUNT(1) see_rate,SUM(t1.if_like) like_num,SUM(if(t1.comment_id IS NULL,0,1)) commit_num,SUM(t1.if_retweet) ret_num
from tb_user_video_log t1 left join tb_video_info t2
on t1.video_id=t2.video_id
WHERE date_sub((select max(end_time) from tb_user_video_log),INTERVAL 29 DAY)<=t2.release_time
group by t1.video_id)tmp1
order by hot_index DESC
limit 3

有了上面几道题的经验,这道题不算太难,只要理解各指标的计算口径即可。

7. 函数的总结

这一部分,我会对一些容易忘记的函数进行总结,如有遗忘,请大家补充。

7.1 时间相关函数

-- 日期和天数相减
DATE_SUB('2022-02-22',INTERVAL 2 day)
-- 日期和日期相减
DATEDIFF('2022-02-26','2022-02-22')
-- 时间相减 
TIMEDIFF('2022-02-26 07:00:00','2022-02-26 06:30:00')-- 结果00:30:00
TIMESTAMPDIFF(MINUTE/SECOND,'2022-02-26 07:00:00','2022-02-26 06:00:00')-- 结果可以转化以分钟/秒为单位 
-- 时间类型转化
DATE_FORMAT('2022-02-26 07:00:00','%Y-%m-%d') --结果为2022-02-26

7.2 窗口函数的范围为

窗口聚合函数的范围,rows between …… and ……一般写在partitin by …order by之后

-- 例如
SELECT author,display_time,fans_groupth_rate,sum(total_fans)over(partition by author order by display_time rows between unbounded preceding and current row) total_fans
--各变量含义
unbounded preceding --前面所有行
unbounded following --后面所有行
current row -- 当前行
n following  -- 后面n行
n preceding  -- 前面n行

-- 例子
-- 从当前行到最后的数据
sum(total_fans)over(partition by author order by display_time rows between current row and unbounded following)
-- 前面所有行与当前行的累加
sum(total_fans)over(partition by author order by display_time rows between unbounded preceding and current row)
-- 当前行与后2行
sum(total_fans)over(partition by author order by display_time rows between current row and 2 following)
-- 当前行、前几行、后几行
sum(total_fans)over(partition by author order by display_time rows between n preceding and n following)

8. 参考资料

相关文章