一、知识点汇总
把有用的知识写在前面,以方便自个儿复习观看😊
1)lead的窗口函数用法
- lead(字段名,n) over () :取值向后偏移n行(空间的理解就是直接将一列数据往前推n个位置,后面的位置就空出来了,具体配合图片理解);
- lag(字段名,n) over () :取值向前偏移n行(空间的理解就是直接将一列数据往前后n个位置,前面的位置就空出来了,具体配合图片理解);
- lag(字段名,n,x) over () :取值向前偏移n行,并将空值填充为数字x(空间的理解就是直接将一列数据往前后n个位置,前面的空出来的位置用X填充上,具体配合图片理解) 。
SELECT id,score,Lead(score,2) over(order by id) lead_score,-- score数列向前推动2位,后面就腾空了2个位置 Lag(score,2) over(order by id) lag_score, -- score数列向后推2位,腾空2个位置 lag(score,2,666) over(order by id) lag_score_3 -- score数列向后推动2位,空值被填充为666 FROM exam_record;
2)datediff 函数
- datediff(时间1,时间2):计算两个日期之间间隔的天数,单位为日
- timestampdiff(时间单位,开始时间,结束时间):两个日期的时间差,返回的时间差形式由时间单位决定(日,周,月,年)
- date_add(日期,INTERVAL n 时间单位) :返回加上n个时间单位后的日期
- date_sub(日期,INTERVAL n 时间单位 ):返回减去n个时间单位后的日期
- date_format(时间,‘%Y-%m-%d’):强制转换时间为所需要的格式
3)datediff和timestampdiff函数的区别
这个知识点的补充源于"刷题匠"同学的问题。这是一个非常细致且容易出错的问题~值得我再次进行补充。
- datediff()函数的作用是求日期差,也就是把一个时间的日期部分取出来求差。例如:'2021-09-05 12:00:00'和'2021-09-04 11:00:00'这两个日期,datediff只取2021-09-05和2021-09-04求日期差,并不会管后面的时间部分。
- timestampdiff()函数的作用则是求时间戳的差,例如:'2021-09-05 12:00:00'和'2021-09-04 11:00:00'这两个日期,datediff只会先求出这个日期的时分秒差,之后再转换成天数来求日期差。
直接说可能有点懵,看完差别后,具体来看下面这个例子:


#先看第一组时间差是23小时,日期差(9月5日-9月4日)是1天 select DATEDIFF('2021-09-05 12:00:00','2021-09-04 11:00:00') datediff_1, TIMESTAMPDIFF(hour,'2021-09-04 12:00:00','2021-09-05 11:00:00') hourdiff_1, TIMESTAMPDIFF(day,'2021-09-04 12:00:00','2021-09-05 11:00:00')timediff_1;
#再看第二组时间差是28小时,日期差(9月5日-9月4日)是1天 select DATEDIFF('2021-09-05 16:00:00','2021-09-04 11:00:00') datediff_2, TIMESTAMPDIFF(hour,'2021-09-04 12:00:00','2021-09-05 16:00:00') hourdiff_2, TIMESTAMPDIFF(day,'2021-09-04 12:00:00','2021-09-05 16:00:00')timediff_2;
看完这个是不是就很容易理解啦~😊😊😊
二、题目解读与解题步骤拆分
1、题目解读
求:请计算在2021年至少有两天作答过试卷的人中,计算该年连续两次作答试卷的最大时间窗days_window,那么根据该年的历史规律他在days_window天里平均会做多少套试卷,按最大时间窗和平均做答试卷套数倒序排序,保留两位小数。
题目中隐藏的坑
坑1:“解释”中有句话“连续两次作答最大时间窗为6天(1号到6号)”,时间窗的计算方法为:6号-1号+1。我第一次解题的时候没有+1。
坑1:“解释”中有句话“连续两次作答最大时间窗为6天(1号到6号)”,时间窗的计算方法为:6号-1号+1。我第一次解题的时候没有+1。
计算公式为:(这些用户总共作答的次数/头尾作答时间窗)* 最大间隔天数
- 这些用户作答的次数 命名为 total
- 头尾作答时间窗 :最早一天作答-最晚一天作答+1 命名为diff_time
- 最大间隔天数:days_window
- (total / diff_time)* days_window 命名为avg_exam_cnt
- 同时要求小数点保留2位小数 round(,2)。
2、解题步骤
step1:在2021年至少有两天作答过试卷的人
也就是头尾作答时间窗>1的用户(根据“解释”的算法,日期间隔的计算需要用日期差+1)两次作答的时间进行相减错行排序,使用lead()over来进行排序。
😁 tips:连续的数据进行对比一般使用lead()over()窗口函数进行处理。
step3:用户在2021年days_window天里平均会做多少套试卷
由上个步骤可得:(total / diff_time)* days_window 命名为 avg_exam_cnt
step4:按最大时间窗和平均做答试卷套数倒序排序。
需求字段:uid,days_window,avg_exam_cnt
三、步骤代码
1)最大时间间隔计算方法DATEDIFF(MAX(start_time),MIN(start_time))+1 diff_time2)连续两次作答试卷的最大时间窗
- 第一次作答时间:start_time
- 第二次作答时间:LEAD(start_time,1)OVER(PARTITION BY uid ORDER BY start_time) AS next_time
先处理出第一个表格t1
SELECT uid,start_time, LEAD(start_time,1)OVER(PARTITION BY uid ORDER BY start_time) AS next_time -- 第二次作答时间 FROM exam_record WHERE YEAR(start_time)=2021 -- 2021年的数据
- 最大连续时间间隔:MAX(DATEDIFF(next_time,start_time))+1
SELECT uid, MAX(DATEDIFF(next_time,start_time))+1 days_window -- 最大连续时间窗 FROM ( SELECT uid,start_time, LEAD(start_time,1)OVER(PARTITION BY uid ORDER BY start_time) AS next_time -- 第二次作答时间 FROM exam_record WHERE YEAR(start_time)=2021 -- 2021年的数据 ) t1 GROUP BY uid
(这些用户总共作答的次数/(最后一天作答的日期-第一天作答的日期))*最大连续间隔天数
- 总作答次数: COUNT(start_time) total
- 头尾时间间隔: DATEDIFF(MAX(start_time),MIN(start_time))+1 diff_time
- 最大连续时间间隔:MAX(DATEDIFF(next_time,start_time))+1 days_window
SELECT uid, COUNT(start_time) total, -- 用户2021年作答的次数 DATEDIFF(MAX(start_time),MIN(start_time))+1 diff_time, -- 头尾作答时间窗 MAX(DATEDIFF(next_time,start_time))+1 days_window -- 最大间隔天数 FROM ( SELECT uid,start_time, LEAD(start_time,1)OVER(PARTITION BY uid ORDER BY start_time) AS next_time -- 第二次作答时间 FROM exam_record WHERE YEAR(start_time)=2021 -- 2021年的数据 ) t1 GROUP BY uid;
- 保留2位小数点:ROUND(,2)
四、完整代码组装
WITH t2 AS ( SELECT uid, COUNT(start_time) total, -- 用户2021年作答的次数 DATEDIFF(MAX(start_time),MIN(start_time))+1 diff_time, -- 头尾作答时间窗 MAX(DATEDIFF(next_time,start_time))+1 days_window -- 最大间隔天数 FROM ( SELECT uid,start_time, LEAD(start_time,1)OVER(PARTITION BY uid ORDER BY start_time) AS next_time -- 第二次作答时间 FROM exam_record WHERE YEAR(start_time)=2021 -- 2021年的数据 ) t1 GROUP BY uid ) SELECT uid,days_window,ROUND(total* days_window/diff_time,2) avg_exam_cnt FROM t2 WHERE diff_time>1 ORDER BY days_window DESC,avg_exam_cnt DESC ;