当前位置:网站首页>SQL语言(五)
SQL语言(五)
2022-07-25 10:17:00 【黑小板】
第5章:SQL高级处理
本次学习参考Datawhale开源学习wonderful-sql
本文整体内容参考上述链接,此外每步语句都将进行实现并展示结果,同时给出习题解。
5.1 窗口函数
5.1.1 窗口函数概念及基本的使用方法
窗口函数也称为OLAP函数。OLAP 是 OnLine AnalyticalProcessing 的简称,意思是对数据库数据进行实时分析处理。
为了便于理解,称之为 窗口函数。常规的SELECT语句都是对整张表进行查询,而窗口函数可以让我们有选择的去某一部分数据进行汇总、计算和排序。
窗口函数的通用形式:
<窗口函数> OVER ([PARTITION BY <列名>]
ORDER BY <排序用列名>)
[ ]中的内容可以省略。
窗口函数最关键的是搞明白关键字 PARTITON BY 和 ORDER BY 的作用。
PARTITON BY 是用来分组,即选择要看哪个窗口,类似于 GROUP BY 子句的分组功能,但是 PARTITION BY 子句并不具备 GROUP BY 子句的汇总功能,并不会改变原始表中记录的行数。
ORDER BY 是用来排序,即决定窗口内,是按那种规则(字段)来排序的。
举个栗子:
mysql> 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.04 sec)
PARTITION BY 能够设定窗口对象范围。本例中,为了按照商品种类进行排序,我们指定了product_type。即一个商品种类就是一个小的"窗口"。
ORDER BY 能够指定按照哪一列、何种顺序进行排序。为了按照销售单价的升序进行排列,我们指定了sale_price。此外,窗口函数中的ORDER BY与SELECT语句末尾的ORDER BY一样,可以通过关键字ASC/DESC来指定升序/降序。省略该关键字时会默认按照ASC,也就是升序进行排序。
5.2 窗口函数种类
大致来说,窗口函数可以分为两类。
一是 将SUM、MAX、MIN等聚合函数用在窗口函数中
二是 RANK、DENSE_RANK等排序用的专用窗口函数
5.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 位
运行以下代码:
mysql> 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.03 sec)
5.2.2 聚合函数在窗口函数上的使用
聚合函数在开窗函数中的使用方法和之前的专用窗口函数一样,只是出来的结果是一个累计的聚合函数值。
运行以下代码:
mysql> 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.08 sec)
可以看出,聚合函数结果是,按我们指定的排序,这里是product_id,当前所在行及之前所有的行的合计或均值。即累计到当前行的聚合。
5.3 窗口函数的的应用 - 计算移动平均
在上面提到,聚合函数在窗口函数使用时,计算的是累积到当前行的所有的数据的聚合。 实际上,还可以指定更加详细的汇总范围。该汇总范围成为框架(frame)。
语法
<窗口函数> OVER (ORDER BY <排序用列名>
ROWS n PRECEDING )
<窗口函数> OVER (ORDER BY <排序用列名>
ROWS BETWEEN n PRECEDING AND n FOLLOWING)
PRECEDING(“之前”), 将框架指定为 “截止到之前 n 行”,加上自身行
FOLLOWING(“之后”), 将框架指定为 “截止到之后 n 行”,加上自身行
BETWEEN 1 PRECEDING AND 1 FOLLOWING,将框架指定为 “之前1行” + “之后1行” + “自身”
执行以下代码:
mysql> 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.04 sec)
5.3.1 窗口函数适用范围和注意事项
- 原则上,窗口函数只能在SELECT子句中使用。
- 窗口函数OVER 中的ORDER BY 子句并不会影响最终结果的排序。其只是用来决定窗口函数按何种顺序计算。
5.4 GROUPING运算符
5.4.1 ROLLUP - 计算合计及小计
常规的GROUP BY 只能得到每个分类的小计,有时候还需要计算分类的合计,可以用 ROLLUP关键字。
mysql> 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.05 sec)
得到的结果为:
这里ROLLUP 对product_type, regist_date两列进行合计汇总。
练习题
5.1
请说出针对本章中使用的 product(商品)表执行如下 SELECT 语句所能得到的结果。
mysql> SELECT product_id
,product_name
,sale_price
,MAX(sale_price) OVER (ORDER BY product_id) AS Current_max_price
FROM product;
+------------+--------------+------------+-------------------+
| product_id | product_name | sale_price | Current_max_price |
+------------+--------------+------------+-------------------+
| 0001 | T恤 | 1000 | 1000 |
| 0002 | 打孔器 | 500 | 1000 |
| 0003 | 运动T恤 | 4000 | 4000 |
| 0004 | 菜刀 | 3000 | 4000 |
| 0005 | 高压锅 | 6800 | 6800 |
| 0006 | 叉子 | 500 | 6800 |
| 0007 | 擦菜板 | 880 | 6800 |
| 0008 | 圆珠笔 | 100 | 6800 |
+------------+--------------+------------+-------------------+
8 rows in set (0.08 sec)
5.2
继续使用product表,计算出按照登记日期(regist_date)升序进行排列的各日期的销售单价(sale_price)的总额。排序是需要将登记日期为NULL 的“运动 T 恤”记录排在第 1 位(也就是将其看作比其他日期都早)
mysql> select
product_id,
product_name,
regist_date,
sale_price,
sum(sale_price)over(ORDER BY regist_date) as current_sum_price
from product;
+------------+--------------+-------------+------------+-------------------+
| product_id | product_name | regist_date | sale_price | current_sum_price |
+------------+--------------+-------------+------------+-------------------+
| 0003 | 运动T恤 | NULL | 4000 | 4000 |
| 0007 | 擦菜板 | 2008-04-28 | 880 | 4880 |
| 0005 | 高压锅 | 2009-01-15 | 6800 | 11680 |
| 0002 | 打孔器 | 2009-09-11 | 500 | 12180 |
| 0001 | T恤 | 2009-09-20 | 1000 | 16680 |
| 0004 | 菜刀 | 2009-09-20 | 3000 | 16680 |
| 0006 | 叉子 | 2009-09-20 | 500 | 16680 |
| 0008 | 圆珠笔 | 2009-11-11 | 100 | 16780 |
+------------+--------------+-------------+------------+-------------------+
8 rows in set (0.10 sec)
5.3
思考题
① 窗口函数不指定PARTITION BY的效果是什么?
不指定窗口,默认窗口为整张表,例如:
--不指定窗口,ranking按整张表排序
mysql> SELECT product_name
,product_type
,sale_price
,RANK() OVER (ORDER BY sale_price) AS ranking
FROM product;
+--------------+--------------+------------+---------+
| product_name | product_type | sale_price | ranking |
+--------------+--------------+------------+---------+
| 圆珠笔 | 办公用品 | 100 | 1 |
| 打孔器 | 办公用品 | 500 | 2 |
| 叉子 | 厨房用具 | 500 | 2 |
| 擦菜板 | 厨房用具 | 880 | 4 |
| T恤 | 衣服 | 1000 | 5 |
| 菜刀 | 厨房用具 | 3000 | 6 |
| 运动T恤 | 衣服 | 4000 | 7 |
| 高压锅 | 厨房用具 | 6800 | 8 |
+--------------+--------------+------------+---------+
8 rows in set (0.05 sec)
--指定窗口,ranking按窗口排序
mysql> 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.07 sec)
② 为什么说窗口函数只能在SELECT子句中使用?实际上,在ORDER BY 子句使用系统并不会报错。
因为窗口函数是对where或者group by子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中
边栏推荐
- Acquisition and compilation of UE4 source code
- Using numpy for elevation statistics and visualization
- Flask framework - Message flash
- 2021 CEC written examination summary
- I wrote code for openharmony, and the second phase of "code" pioneer officially opened!
- Flask framework -- flask caching
- 【云享新鲜】社区周刊·Vol.72- 2022华为开发者大赛中国区首场开幕式启动;华为云KooMessage火热公测中…
- Flame framework - Flame WTF form: file upload, verification code
- HCIA实验(09)
- HCIA实验(10)NAT
猜你喜欢

redis 哨兵,高可用的执行者

Learn NLP with Transformer (Chapter 5)

HCIA实验(08)

HCIP实验(01)
![TPS calculation in performance test [Hangzhou multi tester] [Hangzhou multi tester _ Wang Sir]](/img/b2/7a6b99f0ec907b83ac58ed44b23062.png)
TPS calculation in performance test [Hangzhou multi tester] [Hangzhou multi tester _ Wang Sir]

HCIA实验(07)综合实验

Modify MySQL group error expression 1 of select list is not in group

ESP8266 使用 DRV8833驱动板驱动N20电机

Using px2rem does not take effect

MySQL master-slave replication and read-write separation
随机推荐
Openstack Skyline 组件安装
微波技术大作业课设-分立电容电感+微带单枝短截线+微带双枝短截线
UE4 collision
Disabled and readonly and focus issues
Differences between redis and mongodb
2021 CEC written examination summary
Learn NLP with Transformer (Chapter 7)
我为OpenHarmony 写代码,战“码”先锋第二期正式开启!
HCIA实验(09)
MySQL master-slave replication and read-write separation
JS bidirectional linked list 02
推荐系统-协同过滤在Spark中的实现
ONNX(Open Neural Network Exchange)介绍
Visual thematic map of American airport go style: ArcGIS Pro version
Flask框架——Flask-WTF表单:数据验证、CSRF保护
Druid 查询超时配置的探究 → DataSource 和 JdbcTemplate 的 queryTimeout 到底谁生效?
Probe into Druid query timeout configuration → who is the querytimeout of datasource and jdbctemplate effective?
The idea has been perfectly verified again! The interest rate hike is approaching, and the trend is clear. Take advantage of this wave of market!
使用Three.js实现炫酷的赛博朋克风格3D数字地球大屏
What is the meaning of ordinary people's life?