开窗函数滑动窗口统计

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

http://www.nowcoder.com/practice/f90ce4ee521f400db741486209914a11

时间:37ms 内存:6388KB

【需求解析】

原文:"统计2021年国庆头3天每类视频每天的近一周总点赞量和一周内最大单天转发量,结果按视频类别降序、日期升序排序。假设数据库中数据足够多,至少每个类别下国庆头3天及之前一周的每天都有播放记录。"

也就是---

统计每类视频,2021年10月1号到3号,这三天,每天往前7天的总点赞量,以及7天内【单天转发量】的最大值

【难点解析】

一、先按天进行聚合统计

因为原数据是以天为单位的统计数据,每一天都会有多条if_like和if_retweet记录,所以先要按照tag,date进行统计,得到每天的总点赞量like_cnt,和总转发量retweet_cnt

alt

二、滑动窗口的设置(ROWS BETWEEN CURRENT ROW AND 6 PRECEDING)

思路:在09.25-10.03这个区间内,按tag聚合,dt逆序,统计得到CURRENT ROW及后6行的点赞量统计sum_like_cnt_7d,和转发量sum_retweet_cnt_7d

alt

注意点:不可以按dt升序,ROW 6 PRECEDING输出,会报错。

SQL_ERROR_INFO: "Window 'w': frame start or end is negative, NULL or of non-integral type"

理由就像Error_Info所说的那样,【开窗函数进行滑动统计时,起点或终点必须非空】。在本题中,如果按照dt升序,第一行数据前面根本不存在前6行,所以会报错

alt

三、记录的筛选

到这一步,答案要求的数据基本已经统计出来了。

alt

最后,在外面再套一层SELECT,取出所有字段,按照tag, dt聚合,HAVING限定日期为10月1号到3号,按照题目要求排序就大功告成啦。

代码如下:

SELECT
  *
FROM (
  SELECT
    tag,
    dt,
    SUM(like_cnt) OVER w sum_like_cnt_7d,
    MAX(retweet_cnt) OVER w sum_retweet_cnt_7d
  FROM (
    SELECT
      tag,
      DATE(start_time) dt,
      SUM(if_like) like_cnt,
      SUM(if_retweet) retweet_cnt
    FROM tb_video_info
    LEFT JOIN tb_user_video_log USING(video_id)
    WHERE DATE(start_time) BETWEEN '2021-09-25' AND '2021-10-03'
    group by 1,2) t1
  WINDOW w AS (PARTITION BY tag ORDER BY dt DESC ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING)
) t2
GROUP BY 1, 2
HAVING dt BETWEEN '2021-10-01' AND '2021-10-03'
ORDER BY 1 DESC, 2

全部评论
如果有些日期是没有数据的呢
11 回复 分享
发布于 2021-12-17 15:07
最后的1,2代表什么呀
8 回复 分享
发布于 2022-03-05 23:58
第一次sum是求每天的,第二次才是求累加的
6 回复 分享
发布于 2021-12-03 18:47
楼主,我测试的降序和升序都可以啊,降序用ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING,升序用ROWS 6 PRECEDING AND CURRENT ROW,错误信息SQL_ERROR_INFO: "Window 'w': frame start or end is negative, NULL or of non-integral type"出现的原因是rows between a and b中a和b的位置颠倒了
4 回复 分享
发布于 2022-07-31 20:54
按照 dt 升序,可以这样写:PARTITION BY tag ORDER BY dt asc ROWS BETWEEN 6 preceding AND current row 亲测没有报错
4 回复 分享
发布于 2022-02-20 23:07
这个方案最后一步,不要再用group by having,直接用where即可。 where dt BETWEEN '2021-10-01' AND '2021-10-03' ORDER BY tag DESC, dt asc
2 回复 分享
发布于 2022-08-03 17:40
如果某个tag某一天没有播放量不能用这个方法,你理所当然每天都有播放量了
2 回复 分享
发布于 2022-06-08 10:45
亲测可以用6 preceding. select * from ( select tag,dt, sum(like_cnt) over (partition by tag order by dt rows between 6 preceding and current row) as sum_like_cnt_7d, max(retweet_cnt) over (partition by tag order by dt rows between 6 preceding and current row) as max_retweet_cnt_7d from ( select tag,date(start_time) as dt,sum(if_like) as like_cnt, sum(if_retweet) as retweet_cnt from tb_user_video_log t join tb_video_info v using(video_id) where date(start_time)<='2021-10-03' and date(start_time)>='2021-09-25' group by tag,dt) a group by tag,dt order by tag desc,dt ) b where dt in('2021-10-01','2021-10-02','2021-10-03')
2 回复 分享
发布于 2022-05-20 09:58
答主没有筛选7天内都有播放啊,可能有的tag没有7天都有播放
2 回复 分享
发布于 2022-03-10 21:27
GROUP BY 1, 2 HAVING dt BETWEEN '2021-10-01' AND '2021-10-03' ORDER BY 1 DESC, 2 这里不需要再分组,直接where筛选日期即可
2 回复 分享
发布于 2022-03-04 19:55
请问个问题,我尝试了在上一层直接having dt...,不套最后一层select *,是不可以的。按理论不是先计算select再筛选having吗,为什么最后直接having不行呢?感谢
2 回复 分享
发布于 2022-02-11 11:47
省去WHERE DATE(start_time) BETWEEN '2021-09-25' AND '2021-10-03' 也是可以通过的
1 回复 分享
发布于 2022-05-29 09:26
很多内容都是高级玩法,第一次看到,不错,学习了。
1 回复 分享
发布于 2022-04-26 15:43
为什么最外层还要select *呢?我试了把这一层去掉 就报错了
点赞 回复 分享
发布于 2023-03-27 12:29 天津
MySQL8.0.21没有出现 窗口函数往前取若取不到则报错的情况。往前取6行取不到的话,就得0。比如,要求往前取两行,第一行数据前边没有任何数据,查询结果为0。附上SQL: with p as( SELECT b.tag, date(a.start_time) 日期, sum(if_like) 当日喜欢数, sum(if_retweet) 当日转发数 FROM 160_tb_user_video_log a LEFT JOIN 160_tb_video_info b on a.video_id = b.video_id GROUP BY b.tag, date(a.start_time) ) SELECT tag,日期,最近一周喜欢数,最近一周单日最大转发数 from( SELECT p.*, sum(当日喜欢数) over(ORDER BY 日期 rows between 6 preceding and current row) 最近一周喜欢数, max(当日转发数) over(ORDER BY 日期 rows between 6 preceding and current row) 最近一周单日最大转发数 from p ) a WHERE 日期 in ('2021-10-01','2021-10-02','2021-10-03') 不知道是否为MySQL版本问题。
点赞 回复 分享
发布于 2022-11-24 18:58 山东
select * from ( select tag, date_format(start_time,'%Y-%m-%d') dt, sum(sum(if_like)) over w sum_like_cnt_7d, max(sum(if_retweet)) over w max_retweet_cnt_7d from tb_user_video_log join tb_video_info using(video_id) group by tag, date_format(start_time,'%Y-%m-%d') window w as (partition by tag order by date_format(start_time,'%Y-%m-%d') ROWS 6 PRECEDING) ) tmp where dt >= '2021-10-01' and dt < '2021-10-04' order by tag desc, dt asc;
点赞 回复 分享
发布于 2022-10-09 11:54 湖北
可以按dt升序,ROWS BETWEEN frame_start AND frame_end,frame_start必须在frame_end前面才可以
点赞 回复 分享
发布于 2022-09-22 20:11 浙江
关于SQL_ERROR_INFO: "Window 'w': frame start or end is negative, NULL or of non-integral type"这个解释有问题吧 就算是倒序,往下取值,在某一行数据后面也会根本不存在后6行的情况,按道理一样会报错
点赞 回复 分享
发布于 2022-09-22 17:02 湖南
假如9月30号没有数据呢
点赞 回复 分享
发布于 2022-07-20 22:27
preceding 和 following
点赞 回复 分享
发布于 2022-06-08 21:31

相关推荐

点赞 评论 收藏
分享
求面试求offer啊啊啊啊:把华北改为华南再试一试,应该就没啥问题了。改完可能都不用投,别人主动联系了。
点赞 评论 收藏
分享
评论
110
17
分享

创作者周榜

更多
牛客网
牛客企业服务