参考来自某乎
数据分析|SQL窗口函数最全使用指南 https://zhuanlan.zhihu.com/p/120269203
通俗易懂的学会:SQL窗口函数 https://zhuanlan.zhihu.com/p/92654574
一.窗口函数有什么用?
在日常工作中,经常会遇到需要在每组内排名,比如下面的业务需求:
排名问题:每个部门按业绩来排名
topN问题:找出每个部门排名前N的员工进行奖励
面对这类需求,就需要使用sql的高级功能窗口函数了。
二.什么是窗口函数?
窗口函数,也叫OLAP函数(Online Anallytical Processing,联机分析处理),可以对数据库数据进行实时分析处理。
窗口函数的基本语法如下:
<窗口函数> over (partition by <用于分组的列名>
order by <用于排序的列名>)
每个班内按成绩排名:partition by 班 order by 成绩
给每个学生的课程成绩排名:partition by 学生 order by 课程成绩
那么语法中的<窗口函数>都有哪些呢?
1)一些专用的窗口函数
序号函数:row_number() / rank() / dense_rank()
分布函数:percent_rank() / cume_dist()
前后函数:lag() / lead()
头尾函数:first_val() / last_val()
其他函数:nth_value() / 用途:将分区中的有序数据分为n个桶,记录桶号。
2)原有的聚合函数也可用作窗口函数,如sum(),avg(),count(),max(),min()
三.如何使用窗口函数
接下来,就结合实例,给大家介绍几种窗口函数的用法。
1.专用窗口函数rank
例如下图,是班级表中的内容
如果我们想在每个班级内按成绩排名,得到下面的结果。
以班级“1”为例,这个班级的成绩“95”排在第1位,这个班级的“83”排在第4位。上面这个结果确实按我们的要求在每个班级内,按成绩排名了。
得到上面结果的sql语句代码如下:
select *,
rank() over (partition by 班级
order by 成绩 desc) as ranking
from 班级表
我们来解释下这个sql语句里的select子句。rank是排序的函数。要求是“每个班级内按成绩排名”,这句话可以分为两部分:
1)每个班级内:按班级分组
partition by用来对表分组。在这个例子中,所以我们指定了按“班级”分组(partition by 班级)
2)按成绩排名
order by子句的功能是对分组后的结果进行排序,默认是按照升序(asc)排列。在本例中(order by 成绩 desc)是按成绩这一列排序,加了desc关键词表示降序排列。
通过下图,我们就可以理解partiition by(分组)和order by(在组内排序)的作用了。
窗口函数具备了我们之前学过的group by子句分组的功能和order by子句排序的功能。那么,为什么还要用窗口函数呢?这是因为,group by分组汇总后改变了表的行数,一行只有一个类别。而partiition by和rank函数不会减少原表中的行数。例如下面统计每个班级的人数。
相信通过这个例子,你已经明白了这个窗口函数的使用:
select *,
rank() over (partition by 班级
order by 成绩 desc) as ranking
from 班级表
现在我们说回来,为什么叫“窗口”函数呢?这是因为partition by分组后的结果称为“窗口”,这里的窗口不是我们家里的门窗,而是表示“范围”的意思。
简单来说,窗口函数有以下功能:
1)同时具有分组和排序的功能
2)不减少原表的行数
3)语法如下:
<窗口函数> over (partition by <用于分组的列名>
order by <用于排序的列名>)
2.其他专用窗口函数
rank函数:这个例子中是5位,5位,5位,8位,也就是如果有并列名次的行,会占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,4。
dense_rank函数:这个例子中是5位,5位,5位,6位,也就是如果有并列名次的行,不占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,2。
row_number函数:这个例子中是5位,6位,7位,8位,也就是不考虑并列名次的情况。比如前3名是并列的名次,排名是正常的1,2,3,4。
这三个函数的区别如下:
最后,需要强调的一点是:在上述的这三个专用窗口函数中,函数后面的括号不需要任何参数,保持()空着就可以。
3.聚合函数
用途:在窗口中每条记录动态应用聚合函数(sum/avg/max/min/count),可以动态计算在指定的窗口内的各种聚合函数值。
应用场景:截至到当前课程,每个学生的总成绩/平均成绩/最高成绩/最低成绩/课程数是多少?
由于每个窗口函数的条件相同,所以这里将其起个别名w,每个窗口函数只要调用其别名w就好,如下:
SELECT s.sname, c.cname, sc.score,
SUM(sc.score) OVER w as sumVal,
AVG(sc.score) OVER w as avgVal,
MAX(sc.score) OVER w as maxVal,
MIN(sc.score) OVER w as minVal,
COUNT(sc.score) OVER w as countgVal
FROM student s INNER JOIN score sc ON s.sid = sc.sid
INNER JOIN course c ON sc.cid = c.cid
WINDOW w as (PARTITION BY s.sname ORDER BY sc.score DESC)