【教奶奶学SQL】(task5)SQL高级处理(窗口函数 | ROLLUP)

举报
野猪佩奇996 发表于 2022/01/23 00:24:10 2022/01/23
【摘要】 学习总结 (1)PARTITON BY 是用来分组,即选择要看哪个窗口,类似于 GROUP BY 子句的分组功能,但是 PARTITION BY 子句并不具备 GROUP BY 子句的汇总功能,并不会改...

学习总结

(1)PARTITON BY 是用来分组,即选择要看哪个窗口,类似于 GROUP BY 子句的分组功能,但是 PARTITION BY 子句并不具备 GROUP BY 子句的汇总功能,并不会改变原始表中记录的行数。
ORDER BY 是用来排序,即决定窗口内,是按那种规则(字段)来排序的。

一、窗口函数

在这里插入图片描述

1.1 窗口函数概念及基本的使用方法

窗口函数也称为OLAP函数。OLAP 是 OnLine AnalyticalProcessing 的简称,意思是对数据库数据进行实时分析处理。

为了便于理解,称之为 窗口函数。常规的SELECT语句都是对整张表进行查询,而窗口函数可以让我们有选择的去某一部分数据进行汇总、计算和排序。

窗口函数的通用形式:

<窗口函数> OVER ([PARTITION BY <列名>]
                     ORDER BY <排序用列名>)  

  
 
  • 1
  • 2

[ ]中的内容可以省略。

窗口函数最关键的是搞明白关键字 PARTITON BYORDER BY 的作用。

PARTITON BY 是用来分组,即选择要看哪个窗口,类似于 GROUP BY 子句的分组功能,但是 PARTITION BY 子句并不具备 GROUP BY 子句的汇总功能,并不会改变原始表中记录的行数。

ORDER BY 是用来排序,即决定窗口内,是按那种规则(字段)来排序的。

举个栗子:

SELECT product_name
       ,product_type
       ,sale_price
       ,RANK() OVER (PARTITION BY product_type
                         ORDER BY sale_price) AS ranking
  FROM product;  

+--------------+--------------+------------+---------+
| product_name | product_type | sale_price | ranking |
+--------------+--------------+------------+---------+
| 圆珠笔       | 办公用品     |        100 |       1 |
| 打孔器       | 办公用品     |        500 |       2 |
| 叉子         | 厨房用具     |        500 |       1 |
| 擦菜板       | 厨房用具     |        880 |       2 |
| 菜刀         | 厨房用具     |       3000 |       3 |
| 高压锅       | 厨房用具     |       6800 |       4 |
| T恤          | 衣服         |       1000 |       1 |
| 运动T恤      | 衣服         |       4000 |       2 |
+--------------+--------------+------------+---------+
8 rows in set (0.00 sec)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

我们先忽略生成的新列 - [ranking], 看下原始数据在PARTITION BYORDER BY 关键字的作用下发生了什么变化。

PARTITION BY 能够设定窗口对象范围。本例中,为了按照商品种类进行排序,我们指定了product_type。即一个商品种类就是一个小的"窗口"。

ORDER BY 能够指定按照哪一列、何种顺序进行排序。为了按照销售单价的升序进行排列,我们指定了sale_price。此外,窗口函数中的ORDER BYSELECT语句末尾的ORDER BY一样,可以通过关键字ASC/DESC来指定升序/降序。省略该关键字时会默认按照ASC,也就是
升序进行排序。本例中就省略了上述关键字 。

在这里插入图片描述

二、窗口函数种类

大致来说,窗口函数可以分为两类。

一是 将SUM、MAX、MIN等聚合函数用在窗口函数中

二是 RANK、DENSE_RANK等排序用的专用窗口函数

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 位

运行以下代码:
因为不用像上面栗子进行分类商品中的排序,所以不需要用PARTITION BY

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_num
  FROM product;  

+--------------+--------------+------------+---------+---------------+---------+
| product_name | product_type | sale_price | ranking | dense_ranking | row_num |
+--------------+--------------+------------+---------+---------------+---------+
| 圆珠笔       | 办公用品     |        100 |       1 |             1 |       1 |
| 打孔器       | 办公用品     |        500 |       2 |             2 |       2 |
| 叉子         | 厨房用具     |        500 |       2 |             2 |       3 |
| 擦菜板       | 厨房用具     |        880 |       4 |             3 |       4 |
| T恤          | 衣服         |       1000 |       5 |             4 |       5 |
| 菜刀         | 厨房用具     |       3000 |       6 |             5 |       6 |
| 运动T恤      | 衣服         |       4000 |       7 |             6 |       7 |
| 高压锅       | 厨房用具     |       6800 |       8 |             7 |       8 |
+--------------+--------------+------------+---------+---------------+---------+
8 rows in set (0.00 sec)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.2 聚合函数在窗口函数上的使用

聚合函数在窗口函数中的使用方法和之前的专用窗口函数一样,只是出来的结果是一个累计的聚合函数值。

运行以下代码:

SELECT  product_id
       ,product_name
       ,sale_price
       ,SUM(sale_price) OVER (ORDER BY product_id) AS current_sum
       ,AVG(sale_price) OVER (ORDER BY product_id) AS current_avg  
  FROM product;  

+------------+--------------+------------+-------------+-------------+
| product_id | product_name | sale_price | current_sum | current_avg |
+------------+--------------+------------+-------------+-------------+
| 0001       | T恤          |       1000 |        1000 |   1000.0000 |
| 0002       | 打孔器       |        500 |        1500 |    750.0000 |
| 0003       | 运动T恤      |       4000 |        5500 |   1833.3333 |
| 0004       | 菜刀         |       3000 |        8500 |   2125.0000 |
| 0005       | 高压锅       |       6800 |       15300 |   3060.0000 |
| 0006       | 叉子         |        500 |       15800 |   2633.3333 |
| 0007       | 擦菜板       |        880 |       16680 |   2382.8571 |
| 0008       | 圆珠笔       |        100 |       16780 |   2097.5000 |
+------------+--------------+------------+-------------+-------------+
8 rows in set (0.00 sec)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

在这里插入图片描述
在这里插入图片描述
可以看出,聚合函数结果是,按我们指定的排序,这里是product_id当前所在行及之前所有的行的合计或均值。即累计到当前行的聚合。

三、窗口函数的的应用 - 计算移动平均

在上面提到,聚合函数在窗口函数使用时,计算的是累积到当前行的所有的数据的聚合。 实际上,还可以指定更加详细的汇总范围。该汇总范围成为 框架 (frame)。

语法

<窗口函数> OVER (ORDER BY <排序用列名>
                 ROWS n PRECEDING )  
                 
<窗口函数> OVER (ORDER BY <排序用列名>
                 ROWS BETWEEN n PRECEDING AND n FOLLOWING)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

PRECEDING(“之前”), 将框架指定为 “截止到之前 n 行”,加上自身行

FOLLOWING(“之后”), 将框架指定为 “截止到之后 n 行”,加上自身行

BETWEEN 1 PRECEDING AND 1 FOLLOWING,将框架指定为 “之前1行” + “之后1行” + “自身”

执行以下代码:

SELECT  product_id
       ,product_name
       ,sale_price
       ,AVG(sale_price) OVER (ORDER BY product_id
                               ROWS 2 PRECEDING) AS moving_avg
       ,AVG(sale_price) OVER (ORDER BY product_id
                               ROWS BETWEEN 1 PRECEDING 
                                        AND 1 FOLLOWING) AS moving_avg  
  FROM product;  

+------------+--------------+------------+------------+------------+
| product_id | product_name | sale_price | moving_avg | moving_avg |
+------------+--------------+------------+------------+------------+
| 0001       | T恤          |       1000 |  1000.0000 |   750.0000 |
| 0002       | 打孔器       |        500 |   750.0000 |  1833.3333 |
| 0003       | 运动T恤      |       4000 |  1833.3333 |  2500.0000 |
| 0004       | 菜刀         |       3000 |  2500.0000 |  4600.0000 |
| 0005       | 高压锅       |       6800 |  4600.0000 |  3433.3333 |
| 0006       | 叉子         |        500 |  3433.3333 |  2726.6667 |
| 0007       | 擦菜板       |        880 |  2726.6667 |   493.3333 |
| 0008       | 圆珠笔       |        100 |   493.3333 |   490.0000 |
+------------+--------------+------------+------------+------------+
8 rows in set (0.00 sec)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

执行结果:

注意观察框架的范围。

第一列的moving_avg:ROWS 2 PRECEDING:
在这里插入图片描述

第二列的moving_avg:ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:
在这里插入图片描述

3.1 窗口函数适用范围和注意事项

  • 原则上,窗口函数只能在SELECT子句中使用。
  • 窗口函数OVER 中的ORDER BY 子句并不会影响最终结果的排序。其只是用来决定窗口函数按何种顺序计算

四、GROUPING运算符

4.1 ROLLUP - 计算合计及小计

常规的GROUP BY 只能得到每个分类的小计,有时候还需要计算分类的合计,可以用 ROLLUP关键字。

SELECT  product_type
       ,regist_date
       ,SUM(sale_price) AS sum_price
  FROM product
 GROUP BY product_type, regist_date WITH ROLLUP;  

+--------------+-------------+-----------+
| product_type | regist_date | sum_price |
+--------------+-------------+-----------+
| 办公用品     | 2009-09-11  |       500 |
| 办公用品     | 2009-11-11  |       100 |
| 办公用品     | NULL        |       600 |
| 厨房用具     | 2008-04-28  |       880 |
| 厨房用具     | 2009-01-15  |      6800 |
| 厨房用具     | 2009-09-20  |      3500 |
| 厨房用具     | NULL        |     11180 |
| 衣服         | NULL        |      4000 |
| 衣服         | 2009-09-20  |      1000 |
| 衣服         | NULL        |      5000 |
| NULL         | NULL        |     16780 |
+--------------+-------------+-----------+
11 rows in set (0.01 sec)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

这里ROLLUPproduct_type, regist_date两列进行合计汇总。结果实际上有三层聚合,如:

  • 下图 模块3是常规的 GROUP BY 的结果,需要注意的是衣服 有个注册日期为空的,这是本来数据就存在日期为空的,不是对衣服类别的合计;
  • 模块2和1是 ROLLUP 带来的合计,模块2是对产品种类的合计,模块1是对全部数据的总计。

ROLLUP 可以对多列进行汇总求小计和合计。
在这里插入图片描述

五、练习题

5.1 结果题

请说出针对本章中使用的 product(商品)表执行如下 SELECT 语句所能得到的结果。

SELECT  product_id
       ,product_name
       ,sale_price
       ,MAX(sale_price) OVER (ORDER BY product_id) AS Current_max_price
  FROM product;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

利用窗口函数OVER,计算按照product_id升序排序后,遍历到当前时候的最大商品价格:
在这里插入图片描述

5.2 排序题

继续使用product表,计算出按照登记日期(regist_date)升序进行排列的各日期的销售单价(sale_price)的总额。排序是需要将登记日期为NULL 的“运动 T 恤”记录排在第 1 位(也就是将其看作比其他日期都早)

一开始想着如下这样做(计算出每个日期的当天的),但是是要有显示每个商品的,从题目说的排序规则也可以看出来。

SELECT regist_date,
	   SUM(sale_price) OVER(ORDER BY regist_date) AS sum_sale_price
FROM product;

  
 
  • 1
  • 2
  • 3

在这里插入图片描述
所以就改成如下这样,以每个日期为窗口(利用PARTITION BY),进行计算当天的销售总额,注意这里的销售总额不会累加之前日期的销售总额。

SELECT product_name,
       product_id,sale_price,regist_date,
       SUM(sale_price) OVER (PARTITION BY regist_date
					         ORDER BY regist_date) AS current_sum_price
FROM product;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

5.3 思考题

① 窗口函数不指定PARTITION BY的效果是什么?
【答】木有划分窗口,整个表是一个窗口,拿刚才的5.2举栗子,如果去掉PARTITION BY,结果变为按照日期排序,然后计算累加单价:

SELECT product_name,
       product_id,sale_price,regist_date,
       SUM(sale_price) OVER (ORDER BY regist_date) AS current_sum_price
FROM product;

  
 
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述
② 为什么说窗口函数只能在SELECT子句中使用?实际上,在ORDER BY 子句使用系统并不会报错。
【答】SQL执行顺序:
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。

Reference

datawhale notebook

文章来源: andyguo.blog.csdn.net,作者:山顶夕景,版权归原作者所有,如需转载,请联系作者。

原文链接:andyguo.blog.csdn.net/article/details/121425049

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。