【Mysql】简单窗口函数及例题

646 阅读3分钟

基本代码:

select *, (窗口函数) over (
partition by 列名1
order by 列名2) as xx
from xxx

也就是说,窗口函数兼备分组和排序的功能,而partition和group by的区别在于前者得到的结果不减少表的行数。

专用窗口函数

假设分数为10,10,10,9,8,7,6,5

rank():并列时排位一致,然后跳过重复排位:1,1,1,4,5,6,7,8;最后出来的ranking列会直接从1递增到n,所以调整排名方式(越大越高或是越低越高)应在order by中考虑desc的使用。

dense_rank():并列式排位一致,然后不跳过重复排位:1,1,1,2,3,4,5,6

row_number():直接递增排位:1,2,3,4,5,6,7,8

聚合函数作窗口函数

sum(列)/avg(列)/count(列)/max(列)/min(列):设列值分别为x1,x2...xn,则sum(列)n=sum(列)n-1 + xn;avg(列)n=sum(列)n / n.

ntile(n)函数

将数据分为n等分,用于筛选百分数相关的数据,具体例子见例1法2.

向上/向下窗口函数

lead(字段名,n,默认值)/lag(字段名,n,默认值),用于将某列的数值向上或向下移动n行并形成新的列,超出表的范围的视为默认值,不设则为null

微信截图_20210809161223.png

微信截图_20210809161146.png

相关例题

例1:剔除访问量前20%的用户后,求每类用户的平均访问次数

微信截图_20210806191211.png

1:子查询
select a.用户类型,avg(a.访问量)
from (select *, row_number() over(order by 访问量 desc) as 排名
from 访问次数表) as a
where 排名 > 0.2 * (select max(排名) 
from (select *, row_number() over(order by 访问量 desc) as 排名
from 访问次数表) as b)
group by 用户类型

法2:ntile函数
select 用户类型,avg(访问量)
from(select *,ntile(5) over(order by 访问量 desc) as level
from 访问次数表) as a
where level between 2 and 5
group by 用户类型

法2中,where子句并未在临时表中使用,原因是会报错显示找不到level,因为sql会先运行from和where再运行select,运行where的时候并没有level的字段名。

例2:查找出每门课程中成绩最好的学生的姓名和该学生的课程及成绩

微信截图_20210808164001.png

1:子查询
select a.姓名,c.课程名称,b.成绩
from 学生表 as a inner join 成绩表 as b on a.学号 = b.学号
inner join 课程表 as c on b.课程号 = c.课程号
where (b.课程号,b.成绩) in (
select 课程号,max(成绩)
from 成绩表
group by 课程号)

法2:窗口函数
select a.姓名,c.课程名称,d.成绩
from (select 学号,课程号,成绩,
rank() over(partition by 课程号 order by 成绩 desc) as 排位 from 成绩表) as d
inner join 学生表 as a on a.学号 = d.学号
inner join 课程表 as c on d.课程号 = c.课程号
where d.排位 = 1

法1注意where子句中的对应关系

例3:查询每个部门除去最高薪水和最低薪水后的平均薪水并保留整数

微信截图_20210809152452.png

1:窗口函数
select 部门编号,round(avg(薪水),0) as 平均薪水
from (select *,row_number() over (partition by 部门编号 order by 薪水 desc) as row1,
row_number() over (partition by 部门编号 order by 薪水) as row2
from 薪水表) as a
where row1 != 1 and row2 != 1
group by 部门编号

法2select 部门编号,round((sum(薪水)-max(薪水)-min(薪水)) / (count(薪水)-2),0) as 平均薪水
from 薪水表
group by 部门编号

例4:找出连续得分三次的球员

微信截图_20210809155354.png

select distinct 球员姓名
from (select *,
lead(球员姓名,1) over(partition by 球队 order by 得分时间) as lead1,
lead(球员姓名,2) over(partition by 球队 order by 得分时间) as lead2
from 分数表) as a
where 球员姓名 = lead1 and 球员姓名 = lead2