目录
- 1 什么是窗口函数?
- 2 窗口函数种类
-
- 2.1 专用窗口函数
-
- RANK函数
- DENSE_RANK函数
- ROW_NUMBER函数
- 2.2 将聚合函数用于窗口函数
- 2.3 窗口函数的的应用 - 计算移动平均
- 3 GROUPING运算符
- 练习题
1 什么是窗口函数?
窗口函数也称为OLAP函数,即online analyticall processing
常规的SELECT语句都是对整张表进行查询 而窗口函数可以让我们有选择的去某一部分数据进行汇总、计算和排序
<窗口函数> OVER ([PARTITION BY <列名>]ORDER BY <排序用列名>)
- []中的内容可以省略
- PARTITON BY是用来分组,类似于GROUP BY 子句的分组功能,即选择要看哪个窗口,但是它并不具备GROUP BY 子句的汇总功能,并不会改变原始表中记录的行数
- ORDER BY是用来排序,即决定窗口内是按哪一列(字段)哪种规则(升序降序)排列的
SELECT product_name,product_type,sale_price,RANK() OVER (PARTITION BY product_typeORDER BY sale_price) AS ranking
FROM product;
去掉PARTITION BY看一下
SELECT product_name,product_type,sale_price,RANK() OVER (ORDER BY sale_price) AS ranking
FROM product;
通过对比可以看到,窗口函数其实就是首先执行PARTITION BY
对原数据分组,然后对组内数据按照ORDER BY
排序,然后对数据挨个应用窗口函数。如果没有PARTITION BY
,就是整个原数据是一组。
- 应用sum其实是从头到尾累加
SELECT product_id,product_name,sale_price,SUM(sale_price) OVER (ORDER BY sale_price) AS current_sum
FROM product;
2 窗口函数种类
窗口函数可以分为两类,
-
一是 将SUM、MAX、MIN等聚合函数用在窗口函数中
-
二是 RANK、DENSE_RANK、ROW_NUMBER等排序用的专用窗口函数
2.1 专用窗口函数
RANK函数
计算排序时,如果存在相同位次的记录,则会跳过之后的位次
例)有 3 条记录排在第 1 位时:1 位、1 位、1 位、4 位……
DENSE_RANK函数
计算排序时,即使存在相同位次的记录,也不会跳过之后的位次。
例)有 3 条记录排在第 1 位时:1 位、1 位、1 位、2 位……
ROW_NUMBER函数
计算排序时,赋予唯一的连续位次。
例)有 3 条记录排在第 1 位时:1 位、2 位、3 位、4 位
SELECT product_name,product_type,sale_price,RANK() OVER (ORDER BY sale_price) as ranking,DENSE_RANK() OVER (ORDER BY sale_price) as dense_ranking,ROW_NUMBER() OVER (ORDER BY sale_price) as row_ranking
FROM product;
2.2 将聚合函数用于窗口函数
聚合函数在窗口函数中的使用方法和之前的专用窗口函数一样,只是出来的结果是一个累计的聚合函数值。
SELECT product_name,product_type,sale_price,sum(sale_price) OVER (ORDER BY sale_price) as current_sum,avg(sale_price) OVER (ORDER BY sale_price) as current_avg
FROM product;
聚合函数结果是,按我们指定的排序,当前所在行及之前所有的行的合计或均值。即累计到当前行的聚合。
注意到,这里由于叉子和打孔器价格一样都是500,导致累加时,对用于打孔器和叉子的累加和累加求平均都是一样的,且都是算了2次500的结果。
2.3 窗口函数的的应用 - 计算移动平均
聚合函数在窗口函数使用时,计算的是累积到当前行的所有的数据的聚合。 实际上,还可以指定更加详细的汇总范围。该汇总范围称为框架(frame)。
<窗口函数> OVER (ORDER BY <排序用列名>ROWS n PRECEDING ) <窗口函数> OVER (ORDER BY <排序用列名>ROWS BETWEEN n PRECEDING AND n FOLLOWING)
PRECEDING(“之前”), 将frame指定为 “截止到之前 n 行”,加上自身行
FOLLOWING(“之后”), 将frame指定为 “截止到之后 n 行”,加上自身行
SELECT product_name,product_type,sale_price,SUM(sale_price) OVER (ORDER BY sale_price ROWS 2 PRECEDING) as moving_sum,AVG(sale_price) OVER (ORDER BY sale_price ROWS 2 PRECEDING) as moving_avg
FROM product;
SELECT product_name,product_type,sale_price,AVG(sale_price) OVER (ORDER BY sale_price ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) as moving_avg1,AVG(sale_price) OVER (ORDER BY sale_price ROWS 2 PRECEDING) as moving_avg
FROM product;
3 GROUPING运算符
常规的GROUP BY 只能得到每个分类的小计,有时候还需要计算分类的合计,可以用 ROLLUP关键字。
SELECT product_type,regist_date,SUM(sale_price) AS sum_priceFROM productGROUP BY product_type, regist_date WITH ROLLUP
ROLLUP 对product_type, regist_date两列进行合计汇总。结果实际上有三层聚合,如上图 模块3是常规的 GROUP BY 的结果,需要注意的是衣服 有个注册日期为空的,这是本来数据就存在日期为空的,不是对衣服类别的合计; 模块2和1是 ROLLUP 带来的合计,模块2是对产品种类的合计,模块1是对全部数据的总计
练习题
- 5.1
执行结果是,current_max_price列是根据product_id排序的结果找出的基于本行和前面所有行的最大价格 - 5.2
继续使用product表,计算出按照登记日期(regist_date)升序进行排列的各日期的销售单价(sale_price)的总额。排序是需要将登记日期为NULL 的“运动 T 恤”记录排在第 1 位(也就是将其看作比其他日期都早)
SELECT product_name,regist_date,sale_price,SUM(sale_price) OVER (ORDER BY regist_date) AS sum_priceFROM product;
- 5.3
思考题
① 窗口函数不指定PARTITION BY的效果是什么?
不指定PARTITION BY时,就是全体数据是一组
② 为什么说窗口函数只能在SELECT子句中使用?实际上,在ORDER BY 子句使用系统并不会报错。
窗口函数就是在order by基础上做的进一步运算,所以直接order by 即可
Task 5:SQL高级处理