156——计算完播率
用户-视频互动表tb_user_video_log
id | uid | video_id | start_time | end_time | if_follow | if_like | if_retweet | comment_id |
---|---|---|---|---|---|---|---|---|
1 | 101 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:30 | 0 | 1 | 1 | NULL |
2 | 102 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:24 | 0 | 0 | 1 | NULL |
3 | 103 | 2001 | 2021-10-01 11:00:00 | 2021-10-01 11:00:34 | 0 | 1 | 0 | 1732526 |
4 | 101 | 2002 | 2021-09-01 10:00:00 | 2021-9-01 10:00:42 | 1 | 0 | 1 | NULL |
5 | 102 | 2002 | 2021-10-01 11:00:00 | 2021-10-01 10:00:30 | 1 | 0 | 1 | NULL |
(uid-用户ID, video_id-视频ID, start_time-开始观看时间, end_time-结束观看时间, if_follow-是否关注, if_like-是否点赞, if_retweet-是否转发, comment_id-评论ID)
短视频信息表tb_video_info
id | video_id | author | tag | duration | release_time |
---|---|---|---|---|---|
1 | 2001 | 901 | 影视 | 30 | 2021-01-01 07:00:00 |
2 | 2002 | 901 | 美食 | 60 | 2021-01-01 07:00:00 |
3 | 2003 | 902 | 旅游 | 90 | 2021-01-01 07:00:00 |
(video_id-视频ID, author-创作者ID, tag-类别标签, duration-视频时长(秒), release_time-发布时间)
问题:计算2021年里有播放记录的每个视频的完播率(结果保留三位小数),并按完播率降序排序
注:视频完播率是指完成播放次数占总播放次数的比例。简单起见,结束观看时间与开始播放时间的差>=视频时长时,视为完成播放。
输出示例:
示例数据的结果如下:
video_id | avg_comp_play_rate |
---|---|
2001 | 0.667 |
2002 | 0.000 |
解释:
视频2001在2021年10月有3次播放记录,观看时长分别为30秒、24秒、34秒,视频时长30秒,因此有两次是被认为完成播放了的,故完播率为0.667;
视频2002在2021年9月和10月共2次播放记录,观看时长分别为42秒、30秒,视频时长60秒,故完播率为0.000。
select
a.video_id,
round(
sum(
if ((end_time - start_time - b.duration) >= 0, 1, 0)
) / count(a.video_id),
3
) as avg_comp_play_rate
from
tb_user_video_log a
left join tb_video_info b on a.video_id = b.video_id
where
year (start_time) = 2021
group by
a.video_id
order by
avg_comp_play_rate desc
round函数
round(value,n):用于对某个数值(字段)保留指定小数位数(四舍五入)
SQL执行顺序
一般的执行顺序:
- from
- join
- on
- where
- group by
- avg, sum等聚合函数
- having
- select
- distinct
- order by
- limit
157——计算各类视频平均播放速度
用户-视频互动表tb_user_video_log
id | uid | video_id | start_time | end_time | if_follow | if_like | if_retweet | comment_id |
---|---|---|---|---|---|---|---|---|
1 | 101 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:30 | 0 | 1 | 1 | NULL |
2 | 102 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:21 | 0 | 0 | 1 | NULL |
3 | 103 | 2001 | 2021-10-01 11:00:50 | 2021-10-01 11:01:20 | 0 | 1 | 0 | 1732526 |
4 | 102 | 2002 | 2021-10-01 11:00:00 | 2021-10-01 11:00:30 | 1 | 0 | 1 | NULL |
5 | 103 | 2002 | 2021-10-01 10:59:05 | 2021-10-01 11:00:05 | 1 | 0 | 1 | NULL |
(uid-用户ID, video_id-视频ID, start_time-开始观看时间, end_time-结束观看时间, if_follow-是否关注, if_like-是否点赞, if_retweet-是否转发, comment_id-评论ID)
短视频信息表tb_video_info
id | video_id | author | tag | duration | release_time |
---|---|---|---|---|---|
1 | 2001 | 901 | 影视 | 30 | 2021-01-01 07:00:00 |
2 | 2002 | 901 | 美食 | 60 | 2021-01-01 07:00:00 |
3 | 2003 | 902 | 旅游 | 90 | 2021-01-01 07:00:00 |
(video_id-视频ID, author-创作者ID, tag-类别标签, duration-视频时长, release_time-发布时间)
问题:计算各类视频的平均播放进度,将进度大于60%的类别输出。
注:
- 播放进度=播放时长÷视频时长*100%,当播放时长大于视频时长时,播放进度均记为100%。
- 结果保留两位小数,并按播放进度倒序排序。
输出示例:
示例数据的输出结果如下:
tag | avg_play_progress |
---|---|
影视 | 90.00% |
美食 | 75.00% |
解释:
影视类视频2001被用户101、102、103看过,播放进度分别为:30秒(100%)、21秒(70%)、30秒(100%),平均播放进度为90.00%(保留两位小数);
美食类视频2002被用户102、103看过,播放进度分别为:30秒(50%)、60秒(100%),平均播放进度为75.00%(保留两位小数);
select tag,concat(avg_play_progress,"%") as avg_play_progress
from(
select tag,round(avg(if(TIMESTAMPDIFF(second,start_time,end_time)>duration,100,TIMESTAMPDIFF(second,start_time,end_time)/duration*100)),2) as avg_play_progress
from tb_user_video_log a
left join tb_video_info b on a.video_id = b.video_id
group by tag
having avg_play_progress > 60
order by avg_play_progress desc)t
-- 运行结果一样,但是提交结果没通过
select tag,concat(avg_play_progress,"%") as avg_play_progress
from(
select tag,round(avg(if((end_time-start_time)/duration>=1,100,(end_time-start_time)/duration*100)),2) as avg_play_progress
from tb_user_video_log a
left join tb_video_info b on a.video_id = b.video_id
group by tag
having avg_play_progress > 60
order by avg_play_progress desc)t
小数点转化为百分比的方法:concat(value,“%”)
TIMESTAMPDIFF函数:
TIMESTAMPDIFF(unit,begin,end)
TIMESTAMPDIFF函数返回begin-end的结果,其中begin和end是DATE或DATETIME表达式。
TIMESTAMPDIFF函数允许其参数具有混合类型,例如,begin是DATE值,end可以是DATETIME值。 如果使用DATE值,则TIMESTAMPDIFF函数将其视为时间部分为“00:00:00”的DATETIME值。
unit参数是确定(end-begin)的结果的单位,表示为整数。 以下是有效单位:
MICROSECOND 微秒
SECOND 秒
MINUTE 分钟
HOUR 小时
DAY 天
WEEK 周
MONTH 月份
QUARTER
YEAR 年份
158——统计在有用户互动的最近一个月每类视频转发率
用户-视频互动表tb_user_video_log
id | uid | video_id | start_time | end_time | if_follow | if_like | if_retweet | comment_id |
---|---|---|---|---|---|---|---|---|
1 | 101 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:20 | 0 | 1 | 1 | NULL |
2 | 102 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:15 | 0 | 0 | 1 | NULL |
3 | 103 | 2001 | 2021-10-01 11:00:50 | 2021-10-01 11:01:15 | 0 | 1 | 0 | 1732526 |
4 | 102 | 2002 | 2021-09-10 11:00:00 | 2021-09-10 11:00:30 | 1 | 0 | 1 | NULL |
5 | 103 | 2002 | 2021-10-01 10:59:05 | 2021-10-01 11:00:05 | 1 | 0 | 0 | NULL |
(uid-用户ID, video_id-视频ID, start_time-开始观看时间, end_time-结束观看时间, if_follow-是否关注, if_like-是否点赞, if_retweet-是否转发, comment_id-评论ID)
短视频信息表tb_video_info
id | video_id | author | tag | duration | release_time |
---|---|---|---|---|---|
1 | 2001 | 901 | 影视 | 30 | 2021-01-01 07:00:00 |
2 | 2002 | 901 | 美食 | 60 | 2021-01-01 07:00:00 |
3 | 2003 | 902 | 旅游 | 90 | 2020-01-01 07:00:00 |
(video_id-视频ID, author-创作者ID, tag-类别标签, duration-视频时长, release_time-发布时间)
问题:统计在有用户互动的最近一个月(按包含当天在内的近30天算,比如10月31日的近30天为10.2~10.31之间的数据)中,每类视频的转发量和转发率(保留3位小数)。(意思应该是最后有用户互动的最近一个月内)
注:转发率=转发量÷播放量。结果按转发率降序排序。
输出示例:
示例数据的输出结果如下
tag | retweet_cut | retweet_rate |
---|---|---|
影视 | 2 | 0.667 |
美食 | 1 | 0.500 |
解释:
由表tb_user_video_log的数据可得,数据转储当天为2021年10月1日。近30天内,影视类视频2001共有3次播放记录,被转发2次,转发率为0.667;美食类视频2002共有2次播放记录,1次被转发,转发率为0.500。
select tag,sum(if_retweet) retweet_cut,round((sum(if_retweet)/count(a.video_id)),3) retweet_rate
from tb_user_video_log a
left join tb_video_info b on a.video_id=b.video_id
where datediff(date((select max(start_time) from tb_user_video_log)),date(a.start_time))<=29
group by b.tag
order by retweet_rate desc
-- 用video_id分组结果提交不通过
select tag,sum(if_retweet) retweet_cut,round((sum(if_retweet)/count(a.video_id)),3) retweet_rate
from tb_user_video_log a
left join tb_video_info b on a.video_id=b.video_id
where datediff(date((select max(start_time) from tb_user_video_log)),date(a.start_time))<=29
group by a.video_id
order by retweet_rate desc
难点:如何计算30天内
-- 方法一:先取出最大的开始观看时间,转换成date格式,然后跟每一行的开始时间作对比
datediff(date((select max(start_time) from tb_user_video_log)),date(a.start_time))<=29
159——每个创作者每月的涨粉率及截止当前的总粉丝量
用户-视频互动表tb_user_video_log
id | uid | video_id | start_time | end_time | if_follow | if_like | if_retweet | comment_id |
---|---|---|---|---|---|---|---|---|
1 | 101 | 2001 | 2021-09-01 10:00:00 | 2021-09-01 10:00:20 | 0 | 1 | 1 | NULL |
2 | 105 | 2002 | 2021-09-10 11:00:00 | 2021-09-10 11:00:30 | 1 | 0 | 1 | NULL |
3 | 101 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:20 | 1 | 1 | 1 | NULL |
4 | 102 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:15 | 0 | 0 | 1 | NULL |
5 | 103 | 2001 | 2021-10-01 11:00:50 | 2021-10-01 11:01:15 | 1 | 1 | 0 | 1732526 |
6 | 106 | 2002 | 2021-10-01 10:59:05 | 2021-10-01 11:00:05 | 2 | 0 | 0 | NULL |
(uid-用户ID, video_id-视频ID, start_time-开始观看时间, end_time-结束观看时间, if_follow-是否关注, if_like-是否点赞, if_retweet-是否转发, comment_id-评论ID)
短视频信息表tb_video_info
id | video_id | author | tag | duration | release_time |
---|---|---|---|---|---|
1 | 2001 | 901 | 影视 | 30 | 2021-01-01 07:00:00 |
2 | 2002 | 901 | 美食 | 60 | 2021-01-01 07:00:00 |
3 | 2003 | 902 | 旅游 | 90 | 2020-01-01 07:00:00 |
4 | 2004 | 902 | 美女 | 90 | 2020-01-01 08:00:00 |
(video_id-视频ID, author-创作者ID, tag-类别标签, duration-视频时长, release_time-发布时间)
问题:计算2021年里每个创作者每月的涨粉率及截止当月的总粉丝量
注:
- 涨粉率=(加粉量 - 掉粉量) / 播放量。结果按创作者ID、总粉丝量升序排序。
- if_follow-是否关注为1表示用户观看视频中关注了视频创作者,为0表示此次互动前后关注状态未发生变化,为2表示本次观看过程中取消了关注。
输出示例:
示例数据的输出结果如下
author | month | fans_growth_rate | total_fans |
---|---|---|---|
901 | 2021-09 | 0.500 | 1 |
901 | 2021-10 | 0.250 | 2 |
解释:
示例数据中表tb_user_video_log里只有视频2001和2002的播放记录,都来自创作者901,播放时间在2021年9月和10月;其中9月里加粉量为1,掉粉量为0,播放量为2,因此涨粉率为0.500(保留3位小数);其中10月里加粉量为2,掉份量为1,播放量为4,因此涨粉率为0.250,截止当前总粉丝数为2。
(ps:total_fans是截止当前的总粉丝数,是要加上前面9月份的,要看清楚题目,所以需要用到开窗函数计算累积粉丝数)
select author, date_format(start_time,'%Y-%m') month,
round(sum(case when if_follow = 2 then -1 when if_follow = 1 then 1 else 0 end)/count(a.video_id),3) as fans_growth_rate,
sum(sum(case when if_follow = 2 then -1 when if_follow = 1 then 1 else 0 end)) over (partition by author order by date_format(start_time,'%Y-%m')) as total_fans
from tb_user_video_log a
left join tb_video_info b on a.video_id = b.video_id
where year(start_time) = 2021
group by author,month
order by b.author,total_fans
case when的使用方法
Case具有两种格式。简单Case函数和Case搜索函数。
- 第一种 格式 : 简单Case函数 :
格式说明
case 列名
when 条件值1 then 选项1
when 条件值2 then 选项2…
else 默认值 end
- 第二种 格式 :Case搜索函数
格式说明
case
when 列名= 条件值1 then 选项1
when 列名=条件值2 then 选项2…
else 默认值 end
date_format(date,format)函数
- 1、date:代表具体时间字段,也可以为now()查询当前时间;
- 2、format:DATE_FORMAT将传来的Date类型数据转为自己需要的格式,如%Y-%m-%d %H:%i:%s会将传来的Time数据转为"yyyy-MM-dd HH:mm:ss"格式
160——国庆期间每类视频点赞量和转发量
用户-视频互动表tb_user_video_log
id | uid | video_id | start_time | end_time | if_follow | if_like | if_retweet | comment_id |
---|---|---|---|---|---|---|---|---|
1 | 101 | 2001 | 2021-09-24 10:00:00 | 2021-09-24 10:00:20 | 1 | 1 | 0 | NULL |
2 | 105 | 2002 | 2021-09-25 11:00:00 | 2021-09-25 11:00:30 | 0 | 0 | 1 | NULL |
3 | 102 | 2002 | 2021-09-25 11:00:00 | 2021-09-25 11:00:30 | 1 | 1 | 1 | NULL |
4 | 101 | 2002 | 2021-09-26 11:00:00 | 2021-09-26 11:00:30 | 1 | 0 | 1 | NULL |
5 | 101 | 2002 | 2021-09-27 11:00:00 | 2021-09-27 11:00:30 | 1 | 1 | 0 | NULL |
6 | 102 | 2002 | 2021-09-28 11:00:00 | 2021-09-28 11:00:30 | 1 | 0 | 1 | NULL |
7 | 103 | 2002 | 2021-09-29 11:00:00 | 2021-10-02 11:00:30 | 1 | 0 | 1 | NULL |
8 | 102 | 2002 | 2021-09-30 11:00:00 | 2021-09-30 11:00:30 | 1 | 1 | 1 | NULL |
9 | 101 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:20 | 1 | 1 | 0 | NULL |
10 | 102 | 2001 | 2021-10-01 10:00:00 | 2021-10-01 10:00:15 | 0 | 0 | 1 | NULL |
11 | 103 | 2001 | 2021-10-01 11:00:50 | 2021-10-01 11:01:15 | 1 | 1 | 0 | 1732526 |
12 | 106 | 2002 | 2021-10-02 10:59:05 | 2021-10-02 11:00:05 | 2 | 0 | 1 | NULL |
13 | 107 | 2002 | 2021-10-02 10:59:05 | 2021-10-02 11:00:05 | 1 | 0 | 1 | NULL |
14 | 108 | 2002 | 2021-10-02 10:59:05 | 2021-10-02 11:00:05 | 1 | 1 | 1 | NULL |
15 | 109 | 2002 | 2021-10-03 10:59:05 | 2021-10-03 11:00:05 | 0 | 1 | 0 | NULL |
(uid-用户ID, video_id-视频ID, start_time-开始观看时间, end_time-结束观看时间, if_follow-是否关注, if_like-是否点赞, if_retweet-是否转发, comment_id-评论ID)
短视频信息表tb_video_info
id | video_id | author | tag | duration | release_time |
---|---|---|---|---|---|
1 | 2001 | 901 | 旅游 | 30 | 2020-01-01 07:00:00 |
2 | 2002 | 901 | 旅游 | 60 | 2021-01-01 07:00:00 |
3 | 2003 | 902 | 影视 | 90 | 2020-01-01 07:00:00 |
4 | 2004 | 902 | 美女 | 90 | 2020-01-01 08:00:00 |
(video_id-视频ID, author-创作者ID, tag-类别标签, duration-视频时长, release_time-发布时间)
问题:统计2021年国庆头3天每类视频每天的近一周总点赞量和一周内最大单天转发量,结果按视频类别降序、日期升序排序。假设数据库中数据足够多,至少每个类别下国庆头3天及之前一周的每天都有播放记录。
输出示例:
示例数据的输出结果如下
tag | dt | sum_like_cnt_7d | max_retweet_cnt_7d |
---|---|---|---|
旅游 | 2021-10-01 | 5 | 2 |
旅游 | 2021-10-02 | 5 | 3 |
旅游 | 2021-10-03 | 6 | 3 |
解释:
由表tb_user_video_log里的数据可得只有旅游类视频的播放,2021年9月25到10月3日每天的点赞量和转发量如下:
tag | dt | like_cnt | retweet_cnt |
---|---|---|---|
旅游 | 2021-09-25 | 1 | 2 |
旅游 | 2021-09-26 | 0 | 1 |
旅游 | 2021-09-27 | 1 | 0 |
旅游 | 2021-09-28 | 0 | 1 |
旅游 | 2021-09-29 | 0 | 1 |
旅游 | 2021-09-30 | 1 | 1 |
旅游 | 2021-10-01 | 2 | 1 |
旅游 | 2021-10-02 | 1 | 3 |
旅游 | 2021-10-03 | 1 | 0 |
因此国庆头3天(10.0110.03)里10.01的近7天(9.2510.01)总点赞量为5次,单天最大转发量为2次(9月25那天最大);同理可得10.02和10.03的两个指标。
with t1 as(
select tag,date_format(start_time,'%Y-%m-%d') dt,
sum(sum(if_like)) over (partition by tag order by date_format(start_time,'%Y-%m-%d') rows 6 preceding ) sum_like_cnt_7d,
max(sum(if_retweet)) over (partition by tag order by date_format(start_time,'%Y-%m-%d') rows 6 preceding) max_retweet_cnt_7d
from tb_user_video_log a
left join tb_video_info b on a.video_id = b.video_id
WHERE DATEDIFF('2021-10-03',DATE_FORMAT(start_time,'%Y-%m-%d'))<9
group by dt,tag)
select * from t1 where dt between '2021-10-01' and '2021-10-03' order by tag desc ,dt asc;
with as
with as 也叫子查询,用来定义一个sql片段,且该片段会被整个sql语句反复使用很多次,这个sql片段就相当于是一个公用临时表
如:with tmp as (select * from B)
select * from tmp
先执行select * from B拿到一个结果,将这个结果记录为tmp,再执行 select * from tmp
窗口函数
函数名称([字段名]) OVER([PARTITION BY <分组字段>][ORDER BY <排序字段> [DESC]] [<FRAME字句>])
partition by字句:按照指定字段进行分区,两个分区由边界分割,即所谓的"窗口"。如果没有指定,那么它把整个结果集作为一个分组。窗口函数在不同的分区内分别执行,在跨越分区边界时重新初始化。
order by子句:按照指定字段进行排序,开窗函数将按照排序后的记录顺序进行编号。可以和PARTITION BY子句配合使用,也可以单独使用。默认为升序排列。
FRAME子句:当前分区的一个子集,用来定义子集的规则,通常用来作为滑动窗口使用。
ROWS语法:
ROWS BETWEEN (expr 为指定数值)
UNBOUNDED PRECEDING边界是分区中的第一行
PRECEDING边界是当前行减去expr的值
FOLLOWING边界是当前行加上expr的值
CURRENT ROW边界是当前行
UNBOUNDED FOLLOWING边界是分区中的最后一行
默认:ROWS BETWEEN UNBOUNDED PRECENDING AND CURRENT ROW