题解 138 | #连续两次作答试卷的最大时间窗#

【场景】:时间窗

【分类】:专用窗口函数、嵌套子查询

分析思路

难点:

1.如何求窗口期:使用lag()或者lead(),再分组求最大值

2.作答试卷不去重

(1)统计2021年至少有两天作答过试卷的人

  • [条件]:where year(start_time) = 2021 group by uid having count(date(start_time)) >= 2

  • [使用]:count()、max()、min()

(2)得到平均每天作答试卷数、当前行日期和当前行的前一行日期

  • [使用]:timestampdiff(day,min_date,max_date);

当前行的前一行日期

  • [使用]:lag(date,1) over(partition by uid order by date) 对应 timestampdiff(day,pre_date,date)

当前行的后一行日期

  • [使用]:lead(date,1) over(partition by uid order by date) 对应timestampdiff(day,date,latter_date)

(3)计算avg_exam_cnt,按最大时间窗和平均做答试卷套数倒序排序

得到平均每天作答试卷数、当前行日期和当前行的前一行日期的差求最大值,得到days_window和平均每天作答试卷数

  • [使用]: max(timestampdiff(day,pre_date,date))+1

  • [使用]:order by days_window desc,avg_exam_cnt desc

最终结果

select 查询结果 [用户ID;最大时间窗;平均做答试卷套数]
from 从哪张表中查询数据[嵌套from子查询]
where 查询条件 [空窗期不为空]
order by 按照指定条件排序 [按最大时间窗和平均做答试卷套数倒序排序]

扩展

前往查看: MySQL 窗口函数

求解代码

方法一

with子句+一步步拆解

with
    temp as(
        #2021年至少有两天作答过试卷的人
        select 
            uid,
            count(date(start_time)) as cnt_date,
            max(date(start_time)) as max_date,
            min(date(start_time)) as min_date
        from exam_record
        where year(start_time) = 2021
        group by uid having count(date(start_time)) >= 2
    )
    ,temp1 as(
        #得到平均每天作答试卷数、当前行日期和当前行的前一行日期
        select 
            uid,
            cnt_date/(timestampdiff(day,min_date,max_date)+1) as avg_exam,
            date,
            lag(date,1) over(partition by uid order by date) as pre_date
        from temp
        join(
            select distinct
                uid,
                date(start_time) as date
            from exam_record
            where year(start_time) = 2021
            ) attr using(uid)
    )
    ,temp2 as(
        #统计平均每天试卷数、最大时间窗
        #当前行日期和当前行的前一行日期求差的最大值为days_window
        select
            uid,
            max(avg_exam) as avg_exam,
            max(timestampdiff(day,pre_date,date))+1 as days_window
        from temp1
        group by uid
    )

#计算avg_exam_cnt,按最大时间窗和平均做答试卷套数倒序排序。
select
    uid,
    days_window,
    round(avg_exam*days_window,2) as avg_exam_cnt
from temp2
where days_window is not null
order by days_window desc,avg_exam_cnt desc

优化代码

with
    main as(
        #2021年至少有两天作答过试卷的人
        select
            uid,
            count(date(start_time)) as cnt_date,
            max(date(start_time)) as max_date,
            min(date(start_time)) as min_date
        from exam_record
        where year(start_time) = 2021
        group by uid having count(date(start_time)) >= 2
    )
    ,main1 as(
        #得到平均每天作答试卷数、当前行日期和当前行的前一行日期
        select
            uid,
            cnt_date/(timestampdiff(day,min_date,max_date)+1) as avg_exam,
            date,
            lag(date,1) over(partition by uid order by date) as pre_date
        from main
        join(
            select distinct
                uid,
                date(start_time) as date
            from exam_record
            where year(start_time) = 2021
        ) attr using(uid)
    )

#计算avg_exam_cnt,按最大时间窗和平均做答试卷套数倒序排序。
select
    uid,
    max(timestampdiff(day,pre_date,date))+1 as days_window,
    round(max(avg_exam)*(max(timestampdiff(day,pre_date,date))+1),2) as avg_exam_cnt
from main1
where pre_date is not null
group by uid
order by days_window desc,avg_exam_cnt desc

方法二

from嵌套子查询

#计算avg_exam_cnt,按最大时间窗和平均做答试卷套数倒序排序。
select
    uid,
    max(timestampdiff(day,pre_date,date))+1 as days_window,
    round(max(avg_exam)*(max(timestampdiff(day,pre_date,date))+1),2) as avg_exam_cnt
from(
    #得到平均每天作答试卷数、当前行日期和当前行的前一行日期
    select
        uid,
        cnt_date/(timestampdiff(day,min_date,max_date)+1) as avg_exam,
        date,
        lag(date,1) over(partition by uid order by date) as pre_date
    from(
        #2021年至少有两天作答过试卷的人
        select
            uid,
            count(date(start_time)) as cnt_date,
            max(date(start_time)) as max_date,
            min(date(start_time)) as min_date
        from exam_record
        where year(start_time) = 2021
        group by uid having count(date(start_time)) >= 2
    ) main
    join(
        select distinct
            uid,
            date(start_time) as date
        from exam_record
        where year(start_time) = 2021
    ) attr using(uid)
) main1
where pre_date is not null
group by uid
order by days_window desc,avg_exam_cnt desc
全部评论

相关推荐

02-07 12:06
已编辑
华侨大学 测试开发
最近看到很多 92 的,甚至是硕士,开始往测开赛道卷,说实话有点看不懂。先把话说清楚,大厂里的测开,绝大多数时间干的还是测试的活,只是写点自动化脚本、维护测试平台、接接流水线,真正像开发一样做系统、做架构、做核心平台的测开少得可怜,基本都集中在核心提效组,而且人很少,外面进去的大概率轮不到你,我想真正干过人都清楚。很多人被洗脑了,以为测开也是开,和后端差不多,只是更简单、更轻松、还高薪。现实情况是,测开和开发的职业路径完全不一样。开发的核心是业务和系统能力,测开的核心是稳定性和覆盖率,前者是往上走,后者天花板非常明显。你可以见到很多开发转测开,但你很少见到干了几年测开还能顺利转回开发的。更现实一点说,92 的高学历如果拿来做测开,大部分时间就是在做重复性很强的杂活,这种工作对个人能力的放大效应非常弱。三年下来,你和一个双非的,甚至本科的测开差距不会太大,但你和同龄的后端、平台开发差距会非常明显。这不是努不努力的问题,是赛道问题。所谓测开简单高薪,本质上是把极少数核心测开的上限,当成了整个岗位的常态来宣传。那些工资高、技术强的测开,本身就是开发水平,只是挂了个测开的名。普通人进去,99% 做的都是项目兜底型工作,而不是你想象中的平台开发。测开不是不能做,但它绝对不是开发的平替,也不是性价比最优解。如果你是真的不想做开发,追求稳定,那测开没问题。但如果你只是觉得测开比后端容易,还能进大厂,那我劝你冷静一点,这只是在用短期安全感换长期天花板。有92的学历,如果你连测开这些重复性工作都能心甘情愿接受,那你把时间精力用在真正的开发、系统、业务深度上,回报大概率比卷测开要高得多。想清楚再下场,别被岗位名和话术带偏了,就算去个前端客户端也是随便占坑的,测开是一个坑位很少赛道,反而大面积学历下放,不用想也能知道会是什么结果,我想各位在JAVA那里已经看到了
小浪_Coding:工作只是谋生的手段 而不是相互比较和歧视
点赞 评论 收藏
分享
2025-12-19 15:04
门头沟学院 Java
小肥罗:hr爱上你了,你负责吗哈哈
点赞 评论 收藏
分享
评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务