SQL进阶day11——窗口函数

2024-06-07 02:44

本文主要是介绍SQL进阶day11——窗口函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1专用窗口函数

1.1 每类试卷得分前3名

 1.2第二快/慢用时之差大于试卷时长一半的试卷

1.3连续两次作答试卷的最大时间窗

1.4近三个月未完成试卷数为0的用户完成情况

1.5未完成率较高的50%用户近三个月答卷情况

2聚合窗口函数

2.1 对试卷得分做min-max归一化

2.2每份试卷每月作答数和截止当月的作答总数。

2.3 每月及截止当月的答题情况

1专用窗口函数

1.1 每类试卷得分前3名

我的代码:筛选好难,不懂啥意思

select tag tid,uid,rank()over(partition by tag order by score desc ) ranking
from examination_info ei join exam_record er
on ei.exam_id = er.exam_id
limit 3

正确代码:

select *
from (select tag tid,
uid,
rank()over(partition by tag order by max(score) desc,min(score) desc,max(uid) desc) ranking
from examination_info ei join exam_record er
on ei.exam_id = er.exam_id
group by tag,uid)t
where ranking<=3

复盘:

(1)排序:如果两人最大分数相同,选择最小分数大者,如果还相同,选择uid大者

ORDER BY MAX(score) desc ,MIN(score) desc,uid desc

(2)窗口函数

【排序窗口函数】

●   rank()over()——1,1,3,4

●   dense_rank()over()——1,1,2,3

●   row_number()over()——1,2,3,4

 1.2第二快/慢用时之差大于试卷时长一半的试卷

我的代码:没搞出来,好久没有弄窗口了,这个题好难

方法1:max(if)

select a.exam_id,b.duration,b.release_time  
from
(select exam_id,
row_number() over(partition by exam_id order by timestampdiff(second,start_time,submit_time) desc) rn1,
row_number() over(partition by exam_id order by timestampdiff(second,start_time,submit_time) asc ) rn2,
timestampdiff(second,start_time,submit_time) timex
from exam_record 
where score is not null) ainner join examination_info b on a.exam_id=b.exam_id
group by a.exam_id
#if(rn1=2,a.timex,0)后最大值肯定是第二位的a.timex了
having (max(if(rn1=2,a.timex,0))-max(if(rn2=2,a.timex,0)))/60>b.duration/2 
order by a.exam_id desc

方法2:分析(窗口)函数:NTH_VALUE

select distinct c.exam_id,duration,release_time from 
(select a.exam_id, 
nth_value(TIMESTAMPDIFF(minute,start_time,submit_time),2) over (partition by exam_id order by TIMESTAMPDIFF(minute,start_time,submit_time) desc ) as low_2,
nth_value(TIMESTAMPDIFF(minute,start_time,submit_time),2) over (partition by exam_id order by TIMESTAMPDIFF(minute,start_time,submit_time) asc) as fast_2,
duration,release_time
from exam_record a left join examination_info b on a.exam_id = b.exam_id) c 
where low_2-fast_2>duration*0.5
order by exam_id desc;

复盘:

(1)时间差函数:timestampdiff,如计算差多少分钟,timestampdiff(minute,时间1,时间2),是时间2-时间1,单位是minute

(2)如何取次最大和次最小呢:分析(窗口)函数:NTH_VALUE

NTH_VALUE (measure_expr, n) [ FROM { FIRST | LAST } ][ { RESPECT | IGNORE } NULLS ] OVER (analytic_clause)

(3)关于窗口函数,才发现我本地的数据库连接版本是5,只有MySQL8以上才能用窗口函数好像,所以不能在本地演练推导了。(我一点也不想升级,安装都很麻烦,升级的话肯定各种报错)

1.3连续两次作答试卷的最大时间窗

我的思路:(写不出来)

(1)先把每个用户作答时间用dateformat求出来

(2)在作差,应该可以用偏移分析函数:

【偏移分析函数】

●   lag(字段名,偏移量[,默认值])over()——当前行向取值“偏移量”行

●   lead(字段名,偏移量[,默认值])over()——当前行向取值“偏移量”行

例:

●      ,confirmed 当天截至时间累计确诊人数

●      ,lag(confirmed,1)over(partition by name order by whn) 昨天截至时间累计确诊人数

●      ,(confirmed - lag(confirmed,1)over(partition by name order by whn)) 每天新增确诊人数

 (3)然后选取最大的这个差值

正确代码:

select uid,max(datediff(next_time,start_time))+1 as days_window,round(count(start_time)/(datediff(max(start_time),min(start_time))+1)*(max(datediff(next_time,start_time))+1),2)as avg_exam_cnt
from(select uid,start_time,lead(start_time,1) over(partition by uid order by start_time) as next_timefrom exam_recordwhere year(start_time) = '2021')a
group by uid
having count(distinct date(start_time)) > 1
order by days_window desc,avg_exam_cnt desc

复盘:

(1)先找出uid, 开始时间,下次开始时间。条件是2021创建子表

下次开始时间用偏移分析函数:

●   lead(字段名,偏移量[,默认值])over()——当前行向取值“偏移量”行

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'

(2)最大时间窗口 = max(datediff(next_time,start_time))+1

(3)平均做答试卷套数=作答的试卷数 / 作答期间 *最大时间窗口

= 3/7*6

= count(start_time)/

(datediff(max(start_time),min(start_time))+1)

*(max(datediff(next_time,start_time))+1)

= round(count(start_time)/

(datediff(max(start_time),min(start_time))+1)

*(max(datediff(next_time,start_time))+1),2) #保留两位小数

(4)时间作差要用时间差函数datediff,不能直接相减:结果会是不一样的

(5)datediff()函数 与 timestampdiff()函数的区别

//语法
DATEDIFF(datepart,startdate,enddate)

SELECT DATEDIFF('2018-05-09 08:00:00','2018-05-09') AS DiffDate;//结果 0 ; 表示 2018-05-09 与 2018-05-09之间没有日期差。这里是不比较时分秒的。下面验证带上时分秒有没有差别。SELECT DATEDIFF('2018-05-09 00:00:00','2018-05-09 23:59:59') AS DiffDate;//结果 0 ;SELECT DATEDIFF('2018-05-08 23:59:59','2018-05-09 00:00:00') AS DiffDate;//结果 -1;SELECT DATEDIFF('2018-05-09 00:00:00','2018-05-08 23:59:59') AS DiffDate;
//结果 1;

 

1.4近三个月未完成试卷数为0的用户完成情况

我的代码:思路是这样,报错是必然的

# 先按照uid划分,找出都完成了的,
select uid,
rank()over(partition by uid order by start_time) exam_complete
from exam_record
group by uid
having count(start_time) = count(submit_time) #不对,这样不是每个uid的count# 再按照时间划分,找出3个以上的
select uid,
count(exam_complete) exam_complete_cnt
from 
(select uid,
rank()over(partition by uid order by start_time) exam_complete
from exam_record
group by uid
having count(start_time) = count(submit_time))a
where exam_complete_cnt>3

大佬代码:发现这个答案和我的好像,我再改改

select uid,count(start_time) as exam_complete_cnt
from(select *,dense_rank() over(partition by uid order by date_format(start_time,'%Y%m') desc) as rankingfrom exam_record) a
where ranking <= 3    -- 这里也不能用where ranking <= 3 and submit_time is not null,而要将用户分组后,用having判断
group by uid
having count(score) =  count(uid)
order by exam_complete_cnt desc, uid desc

我的代码改正:

select uid,
count(start_time) as exam_complete_cnt
from
(select *, #后面要用到start_time和submit_time,select也要用到uid,用*全部返回吧
dense_rank()over(partition by uid order by date_format(start_time,"%Y%m") desc) ranking
from exam_record)a
where ranking <=3 #把前面3个月的都要进行计数
group by uid
having count(start_time) = count(submit_time)
order by exam_complete_cnt desc, uid desc

复盘:

(1)这里不能用rank,加引号也不行,难道是和函数名重复了?改为ranking就好了

(2)窗口函数,等着二刷吧,有点小难

1.5未完成率较高的50%用户近三个月答卷情况

我的代码:思路是这样,报错是必然的

# 先筛选出SQL试卷上,未完成率较高的50%用户,6级和7级用户
select *,count(er.submit_time)/count(er.start_time) complete_rate,
rank()over(partition by u.uid order by complete_rate) ranking
from examination_info ei join exam_record er
on ei.exam_id = er.exam_id
join user_info u on u.uid = er.uid
group by u.uid
having ei.tag = 'SQL' and u.level in (6,7) and ranking<0.5
# 子表用户在有试卷作答记录的近三个月中,每个月的答卷数目和完成数目
# 完整代码:
select
uid,
start_month,
count(start_time) total_cnt,
count(submit_time) complete_cnt
from(select *,count(er.submit_time)/count(er.start_time) complete_rate,
rank()over(partition by u.uid order by complete_rate) ranking,
dense_rank()over(partition by uid order by date_format(submit_time,"%Y%m") desc) rankingmonth
from examination_info ei join exam_record er
on ei.exam_id = er.exam_id
join user_info u on u.uid = er.uid
group by u.uid
having ei.tag = 'SQL' and u.level in (6,7) and ranking<0.5)a
where rankingmonth <=3
group by date_format(submit_time,"%Y%m")

大佬代码:好牛,我啥时候能这个水平

# 第一步,先找出未完成率前50%高的用户ID,注意这里需要的sql试卷
with rote_tab as 
(select t.uid,t.f_rote,row_number()over(order by t.f_rote desc,uid) as rank2
,count(t.uid)over(partition by t.tag)as cnt
from (select er.uid,ef.tag,(sum(if(submit_time is null,1,0))/count(start_time)) as f_rote
from exam_record er left join examination_info ef 
on ef.exam_id=er.exam_id 
where tag='SQL' 
group by uid ) t)select  #第四步,分用户和月份进行数据统计;同时需要注意,统计的试卷数是所有类型的,不是之前仅有SQL类型uid,start_month,count(start_time) as total_cnt,count(submit_time) as complete_cnt
from 
(
select # 第三步,利用窗口函数对每个用户的月份进行降序排序,以便找出最近的三个月;uid,start_time,submit_time,date_format(start_time,'%Y%m') as start_month,dense_rank()over(partition by uid order by date_format(start_time,'%Y%m') desc) as rank3
from exam_record 
where uid in (select distinct er.uidfrom exam_record er left join user_info uf on uf.uid=er.uidwhere er.uid in (select uid from rote_tab #引用公用表 rote_tabwhere rank2<=round(cnt/2,0))and uf.level in (6,7))  # 第二步,进一步找出满足等级为6或7的用户ID
) t2
where rank3<=3
group by uid,start_month
order by uid,start_month

2聚合窗口函数

2.1 对试卷得分做min-max归一化

我的报错代码:(得分区间默认为[0,100],如果某个试卷作答记录中只有一个得分,那么无需使用公式,归一化并缩放后分数仍为原分数)这个怎么筛选出去呀

select er.uid,er.exam_id,
(score-min(score))/(max(score)-min(score)) avg_new_score
from examination_info ei join exam_record er
using(exam_id)
where difficulty = 'hard'
group by er.uid,er.exam_id
order by er.uid desc,avg_new_score desc

 大佬代码:

# 第一步先求出高难度试卷的最值max_min_tab
with max_min_tab as 
(select  er.uid,er.exam_id,er.score,max(er.score)over(partition by er.exam_id) as max_score,min(er.score)over(partition by er.exam_id) as min_score
from exam_record er 
left join examination_info ef on er.exam_id=ef.exam_id
where score is not null and difficulty='hard')select uid,exam_id, #第三步进行取平均值和排序
round(avg(new_score)) as avg_new_score
from 
(select uid,exam_id
,if(max_score!=min_score,(score-min_score)/(max_score-min_score)*100,score) as new_score
from max_min_tab) t  # 第二步在max_min_tab中进行归一化计算,并用if排除只有一个分数的
group by exam_id,uid
order by exam_id,avg_new_score desc

复盘:

(1)最值窗口函数:不是直接max,min再后面分组

max(er.score)over(partition by er.exam_id) as max_score,

min(er.score)over(partition by er.exam_id) as min_score

(2)用if来排除只有一个分数的情况

if(max_score!=min_score,(score-min_score)/(max_score-min_score)*100,score

2.2每份试卷每月作答数和截止当月的作答总数。

我的代码:

select exam_id,
date_format(submit_time,"%Y%m") start_month,
count(submit_time)over(partition by exam_id) month_cnt,
count(submit_time)over(partition by exam_id) cum_exam_cnt #应该要用偏移分析函数
from exam_record

 大佬代码:

select distinct exam_id,
date_format(start_time,'%Y%m') start_month,
count(start_time)over(partition by exam_id,date_format(start_time,"%Y%m")) month_cnt,
count(start_time)over(partition by exam_id order by date_format(start_time,'%Y%m')) cum_exam_cnt 
from exam_record
order by exam_id,start_month

复盘:

(1)要distinct exam_id,如果不去重 exam_id,那么同 exam_id和同月会被输出原文件中exam_id和同月配套出现那么多次。

如:

又如:

(2)是start_time而不是submit_time,start_time有记录才表明有作答

2.3 每月及截止当月的答题情况

我的代码:后面三个没有整出来

select 
distinct date_format(start_time,'%Y%m') start_month,
count(uid)over(partition by date_format(start_time,'%Y%m')) mau,
# if(count>0,count,0) month_add_uv,
# max(month_add_uv)over(partition by date_format(start_time,'%Y%m')) max_month_add_uv,
# max(mau)over() cum_sum_uv
from exam_record
group by uid,start_month
order by start_month

大佬代码:

select start_month
, count(distinct uid) as mau
, count(if(rn=1, uid, null)) as month_add_uv
, max(count(if(rn=1, uid, null))) over(order by start_month) as max_month_add_uv
, sum(count(if(rn=1, uid, null))) over(order by start_month) as cum_sum_uv
from (selectuid, date_format(start_time, '%Y%m') as start_month, row_number() over(partition by uid order by start_time) as rnfrom exam_record
) t
group by start_month
;

复盘:

(1)【排序窗口函数】

●   rank()over()——1,1,3,4

●   dense_rank()over()——1,1,2,3

●   row_number()over()——1,2,3,4

这里使用 row_number()over()就只有一个1,那么如果uid有排名为1的,就表示是这个月的新用户。

(2)

  • SQL查询语句语法结构和运行顺序
    • 运行顺序:from--where--group by--having--order by--limit--select
    • 语法结构:select--from--where--group by--having--order by--limit

这篇关于SQL进阶day11——窗口函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1037956

相关文章

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分

MySQL 定时新增分区的实现示例

《MySQL定时新增分区的实现示例》本文主要介绍了通过存储过程和定时任务实现MySQL分区的自动创建,解决大数据量下手动维护的繁琐问题,具有一定的参考价值,感兴趣的可以了解一下... mysql创建好分区之后,有时候会需要自动创建分区。比如,一些表数据量非常大,有些数据是热点数据,按照日期分区MululbU

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优