[代码重构](master): 视图

2022年11月11日16:57:16
master
土豆兄弟 2 years ago
parent 3c410ee245
commit 2cbdb4297b

@ -426,7 +426,358 @@ mysql> SELECT COUNT(*) FROM demo.mytransdisk;
间不会受到影响。除此之外,临时表会在连接结束的时候自动删除,不会占用磁盘空间。 间不会受到影响。除此之外,临时表会在连接结束的时候自动删除,不会占用磁盘空间。
- 当然,临时表也有不足,比如会挤占空间。在使用临时表的时候,要从简化查询和挤占资源两个方面综合考虑,既不能过度加重系统的负担,同时又能够通过存储中间结果,最大限度地简化查询。 - 当然,临时表也有不足,比如会挤占空间。在使用临时表的时候,要从简化查询和挤占资源两个方面综合考虑,既不能过度加重系统的负担,同时又能够通过存储中间结果,最大限度地简化查询。
## 2. ## 2. 进阶
### 2.1 视图:如何简化查询?
- 视图是一种虚拟表,我们可以把一段查询语句作为视图存储在数据库中,在需要的时候,可以把视图看做一个表,对里面的数据进行查询。
- 让查询变得简单,而且,**视图没有实际存储数据,还避免了数据存储过程中可能产生的冗余,提高了存储的效率**。
- 视图的创建及其好处
- 创建视图的语法结构:
```sql
CREATE [OR REPLACE]
VIEW 视图名称 [(字段列表)]
AS 查询语句
```
- 现在假设我们要查询一下商品的每日销售明细这就要从销售流水表demo.trans和 商品信息表demo.goodsmaster中获取到销售数据和对应的商品信息数据。
- 销售流水表包含流水单号、商品编号、销售数量、销售金额和交易时间等信息:
- ![销售流水表](pic/销售流水表.png)
- 商品信息表包含商品编号、条码、名称和售价等信息:
- ![商品信息表](pic/视图商品信息表.png)
- 在不使用视图的情况下,我们可以通过对销售流水表和商品信息表进行关联查询,得到每天商品销售统计的结果,包括销售日期、商品名称、每天销售数量的合计和每天销售金额
的合计,如下所示:
```sql
mysql> SELECT
-> a.transdate,
-> a.itemnumber,
-> b.goodsname,
-> SUM(a.quantity) AS quantity, -- 统计销售数量
-> SUM(a.salesvalue) AS salesvalue -- 统计销售金额
-> FROM
-> demo.trans AS a
-> LEFT JOIN -- 连接查询
-> demo.goodsmaster AS b ON (a.itemnumber = b.itemnumber)
-> GROUP BY a.transdate , a.itemnumber;
+---------------------+------------+-----------+----------+------------+
| transdate | itemnumber | goodsname | quantity | salesvalue |
+---------------------+------------+-----------+----------+------------+
| 2020-12-01 00:00:00 | 1 | 本 | 1.000 | 89.00 |
| 2020-12-01 00:00:00 | 2 | 笔 | 1.000 | 5.00 |
| 2020-12-02 00:00:00 | 3 | 胶水 | 2.000 | 20.00 |
+---------------------+------------+-----------+----------+------------+
3 rows in set (0.00 sec)
```
- 在实际项目中,我们发现,每日商品销售查询使用的频次很高,而且经常需要以这个查询的结果为基础,进行更进一步的统计。
- 举个例子,超市经营者要查一下“每天商品的销售数量和当天库存数量的对比”,如果用一个 SQL 语句查询就会比较复杂。历史库存表demo.inventoryhist如下所示
- ![视图历史库存表](pic/视图历史库存表.png)
- 接下来我们的查询步骤会使用到子查询和派生表,很容易理解,你知道含义就行了。
- 子查询:就是嵌套在另一个查询中的查询。
- 派生表:如果我们在查询中把子查询的结果作为一个表来使用,这个表就是派生表。
- 这个查询的具体步骤是:
- 通过子查询获得单品销售统计的查询结果;
- 把第一步中的查询结果作为一个派生表,跟历史库存表进行连接,查询获得包括销售日期、商品名称、销售数量和历史库存数量在内的最终结果。
```sql
mysql> SELECT
-> a.transdate,
-> a.itemnumber,
-> a.goodsname,
-> a.quantity, -- 获取单品销售数量
-> b.invquantity -- 获取历史库存数量
-> FROM
-> (SELECT -- 子查询,统计单品销售
-> a.transdate,
-> a.itemnumber,
-> b.goodsname,
-> SUM(a.quantity) AS quantity,
-> SUM(a.salesvalue) AS salesvalue
-> FROM
-> demo.trans AS a
-> LEFT JOIN demo.goodsmaster AS b ON (a.itemnumber = b.itemnumber)
-> GROUP BY a.transdate , a.itemnumber
-> ) AS a -- 派生表,与历史库存进行连接
-> LEFT JOIN
-> demo.inventoryhist AS b
-> ON (a.transdate = b.invdate
-> AND a.itemnumber = b.itemnumber);
+---------------------+------------+-----------+----------+-------------+
| transdate | itemnumber | goodsname | quantity | invquantity |
+---------------------+------------+-----------+----------+-------------+
| 2020-12-01 00:00:00 | 1 | 本 | 1.000 | 100.000 |
| 2020-12-01 00:00:00 | 2 | 笔 | 1.000 | 99.000 |
| 2020-12-02 00:00:00 | 3 | 胶水 | 2.000 | 200.000 |
+---------------------+------------+-----------+----------+-------------+
3 rows in set (0.00 sec)
```
- 可以看到,这个查询语句是比较复杂的,可读性和可维护性都比较差。那该怎么办呢?其实,针对这种情况,我们就可以使用视图。
- 我们可以把商品的每日销售统计查询做成一个视图,存储在数据库里,代码如下所示:
```sql
mysql> CREATE VIEW demo.trans_goodsmaster AS -- 创建视图
-> SELECT
-> a.transdate,
-> a.itemnumber,
-> b.goodsname, -- 从商品信息表中获取名称
-> SUM(a.quantity) AS quantity, -- 统计销售数量
-> SUM(a.salesvalue) AS salesvalue -- 统计销售金额
-> FROM
-> demo.trans AS a
-> LEFT JOIN
-> demo.goodsmaster AS b ON (a.itemnumber = b.itemnumber) -- 与商品信息表关联
-> GROUP BY a.transdate , a.itemnumber; -- 按照销售日期和商品编号分组
Query OK, 0 rows affected (0.01 sec)
```
- 这样一来,我们每次需要查询每日商品销售数据的时候,就可以直接查询视图,不需要再写一个复杂的关联查询语句了。
- 我们来试试用一个查询语句直接从视图中进行查询:
```sql
mysql> SELECT * -- 直接查询
-> FROM demo.trans_goodsmaster; -- 视图
+---------------------+------------+-----------+----------+------------+
| transdate | itemnumber | goodsname | quantity | salesvalue |
+---------------------+------------+-----------+----------+------------+
| 2020-12-01 00:00:00 | 1 | 本 | 1.000 | 89.00 |
| 2020-12-01 00:00:00 | 2 | 笔 | 1.000 | 5.00 |
| 2020-12-02 00:00:00 | 3 | 胶水 | 2.000 | 20.00 |
+---------------------+------------+-----------+----------+------------+
3 rows in set (0.01 sec)
```
- 结果显示,这两种查询方式得到的结果是一样的。
- 如果我们要进一步查询“每日单品销售的数量与当日的库存数量的对比”,就可以把刚刚定义的视图作为一个数据表来使用。我们把它跟历史库存表连接起来,来获取销售数量和
历史库存数量。
```sql
mysql> SELECT
-> a.transdate, -- 从视图中获取销售日期
-> a.itemnumber, -- 从视图中获取商品编号
-> a.goodsname, -- 从视图中获取商品名称
-> a.quantity, -- 从视图中获取销售数量
-> b.invquantity -- 从历史库存表中获取历史库存数量
-> FROM
-> demo.trans_goodsmaster AS a -- 视图
-> LEFT JOIN
-> demo.inventoryhist AS b ON (a.transdate = b.invdate
-> AND a.itemnumber = b.itemnumber); -- 直接连接库存历史表
+---------------------+------------+-----------+----------+-------------+
| transdate | itemnumber | goodsname | quantity | invquantity |
+---------------------+------------+-----------+----------+-------------+
| 2020-12-01 00:00:00 | 1 | 本 | 1.000 | 100.000 |
| 2020-12-01 00:00:00 | 2 | 笔 | 1.000 | 99.000 |
| 2020-12-02 00:00:00 | 3 | 胶水 | 2.000 | 200.000 |
+---------------------+------------+-----------+----------+-------------+
3 rows in set (0.00 sec)
```
- 结果显示,这里的查询结果和我们刚刚使用派生表的查询结果是一样的。但是,使用视图的查询语句明显简单多了,可读性更好,也更容易维护。
- 如何操作视图和视图中的数据?
- 创建完了视图,我们还经常需要对视图进行一些操作,比如修改、查看和删除视图。
- 如何操作视图?
- 修改、查看、删除视图的操作比较简单,你只要掌握具体的语法就行了。
- 修改视图的语法如下所示:
```sql
ALTER VIEW 视图名
AS 查询语句;
```
- 查看视图的语法是:
```sql
查看视图:
DESCRIBE 视图名;
```
- 删除视图要使用 DROP 关键词,具体方法如下:
```sql
删除视图:
DROP VIEW 视图名;
```
- 如何操作视图中的数据?
- 视图本身是一个虚拟表,所以,对视图中的数据进行插入、修改和删除操作,实际都是通过对实际数据表的操作来实现的。
- 在视图中插入数据
- 借用刚刚的视图 demo.view_goodsmaster 来给你解释下。假设商品信息表中的规格字段specification被删除了当我们尝试用 INSERT INTO 语句向视图中插入一条记录
的时候,就会提示错误了:
```sql
mysql> INSERT INTO demo.view_goodsmaster
-> (itemnumber,barcode,goodsname,salesprice)
-> VALUES
-> (5,'0005','测试',100);
ERROR 1471 (HY000): The target table view_goodsmaster of the INSERT is not ins ...
```
- 这是因为,**只有视图中的字段跟实际数据表中的字段完全一样MySQL 才允许通过视图插入数据**。刚刚的视图中包含了实际数据表所没有的字段“specification”所以在插入
数据时,系统就会提示错误。
- 为了解决这个问题,我们来修改一下视图,让它只包含实际数据表中有的字段,也就是商品编号、条码、名称和售价。代码如下:
```sql
mysql> ALTER VIEW demo.view_goodsmaster
-> AS
-> SELECT itemnumber,barcode,goodsname,salesprice -- 只包含实际表中存在的字段
-> FROM demo.goodsmaster
-> WHERE salesprice > 50;
Query OK, 0 rows affected (0.01 sec)
```
- 对视图进行修改之后,我们重新尝试向视图中插入一条记录:
```sql
mysql> INSERT INTO demo.view_goodsmaster
-> (itemnumber,barcode,goodsname,salesprice)
-> VALUES
-> (5,'0005','测试',100);
Query OK, 1 row affected (0.02 sec)
```
- 结果显示,插入成功了。
- 实际数据表中的数据情况
```sql
mysql> SELECT *
-> FROM demo.goodsmaster;
+------------+---------+-----------+------------+
| itemnumber | barcode | goodsname | salesprice |
+------------+---------+-----------+------------+
| 1 | 0001 | 本 | 89.00 |
| 2 | 0002 | 笔 | 5.00 |
| 3 | 0003 | 胶水 | 10.00 |
| 5 | 0005 | 测试 | 100.00 | -- 通过视图插入的数据
+------------+---------+-----------+------------+
4 rows in set (0.00 sec)
```
- 可以看到,实际数据表 demo.goodsmaster 中,也已经包含通过视图插入的商品编号是 5 的商品数据了。
- **删除视图中的数据**
- 我们可以通过 DELETE 语句,删除视图中的数据:
```sql
mysql> DELETE FROM demo.view_goodsmaster -- 直接在视图中删除数据
-> WHERE itemnumber = 5;
Query OK, 1 row affected (0.02 sec)
```
- 现在我们来查看视图和实际数据表的内容,会发现商品编号是 5 的商品都已经被删除了。
```sql
mysql> SELECT *
-> FROM demo.view_goodsmaster;
+------------+---------+-----------+------------+
| itemnumber | barcode | goodsname | salesprice |
+------------+---------+-----------+------------+
| 1 | 0001 | 本 | 89.00 | -- 视图中已经没有商品编号是5的商品
+------------+---------+-----------+------------+
1 row in set (0.00 sec)
mysql> SELECT *
-> FROM demo.goodsmaster;
+------------+---------+-----------+------------+
| itemnumber | barcode | goodsname | salesprice |
+------------+---------+-----------+------------+
| 1 | 0001 | 本 | 89.00 |
| 2 | 0002 | 笔 | 5.00 |
| 3 | 0003 | 胶水 | 10.00 | -- 实际表中也已经没有商品编号是5的
+------------+---------+-----------+------------+
3 rows in set (0.00 sec)
```
- 修改视图中的数据
- 我们可以通过 UPDATE 语句对视图中的数据进行修改:
```sql
mysql> UPDATE demo.view_goodsmaster -- 更新视图中的数据
-> SET salesprice = 100
-> WHERE itemnumber = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
```
- 结果显示,更新成功了。现在我们来查看一下视图和实际数据表,代码如下所示:
```sql
mysql> SELECT *
-> FROM demo.view_goodsmaster;
+------------+---------+-----------+------------+
| itemnumber | barcode | goodsname | salesprice |
+------------+---------+-----------+------------+
| 1 | 0001 | 本 | 100.00 | -- 视图中的售价改过了
+------------+---------+-----------+------------+
1 row in set (0.01 sec)
mysql> SELECT *
-> FROM demo.goodsmaster;
+------------+---------+-----------+------------+
| itemnumber | barcode | goodsname | salesprice |
+------------+---------+-----------+------------+
| 1 | 0001 | 本 | 100.00 | -- 实际数据表中的售价也改过了
| 2 | 0002 | 笔 | 5.00 |
| 3 | 0003 | 胶水 | 10.00 |
+------------+---------+-----------+------------+
3 rows in set (0.00 sec)
```
- 可以发现,视图和原来的数据表都已经改过来了。
- 需要注意的是,**我不建议你对视图的数据进行更新操作**,因为 MySQL 允许用比较复杂的 SQL 查询语句来创建视图(比如 SQL 查询语句中使用了分组和聚合函数,或者是 UION
和 DISTINCT 关键字),所以,要通过对这个结果集的更新来更新实际数据表,有可能不被允许,因为 MySQL 没办法精确定位实际数据表中的记录。就比如刚刚讲到的那个“每
日销售统计查询”视图就没办法更改,因为创建视图的 SQL 语句是一个包含了分组函数GROUP BY的查询。
- 视图有哪些优缺点?
- 到这里,视图的操作我就讲完了,现在我们把视线拔高一点,来看看视图都有哪些优缺点。只有全面掌握视图的特点,我们才能充分享受它的高效,避免踩坑。
- 视图的优点。
- 第一,因为我们可以把视图看成一张表来进行查询,所以在使用视图的时候,我们不用考虑视图本身是如何获取数据的,里面有什么逻辑,包括了多少个表,有哪些关联操作,而
是可以直接使用这样一来,实际上就把查询模块化了,查询变得更加简单,提高了开发和维护的效率。所以,你可以把那些经常会用到的查询和复杂查询的子查询定义成视图,
存储到数据库中,这样可以为你以后的使用提供方便。
- 第二,视图跟实际数据表不一样,它存储的是查询语句。所以,在使用的时候,我们要通过定义视图的查询语句来获取结果集。而视图本身不存储数据,不占用数据存储的资源。
- 第三,视图具有隔离性。视图相当于在用户和实际的数据表之间加了一层虚拟表。也就是说,**用户不需要查询数据表,可以直接通过视图获取数据表中的信息**。这样既提高了数据
表的安全性,同时也通过视图把用户实际需要的信息汇总在了一起,查询起来很轻松。
- 第四,**视图的数据结构相对独立,即便实际数据表的结构发生变化,我们也可以通过修改定义视图的查询语句,让查询结果集里的字段保持不变**。这样一来,针对视图的查询就不
受实际数据表结构变化的影响了。
- 假设我们有一个实际的数据表demo.goodsmaster包括商品编号、条码、名称、规格和售价等信息
- ![视图实际的数据表](pic/视图实际的数据表.png)
- 在这个表的基础上,我们建一个视图,查询所有价格超过 50 元的商品:
```sql
mysql> CREATE VIEW demo.view_goodsmaster AS
-> SELECT *
-> FROM demo.goodsmaster
-> WHERE salesprice > 50;
Query OK, 0 rows affected (0.03 sec)
```
- 接着,我们在这个视图的基础上做一个查询,来验证一下视图的内容:
```sql
mysql> SELECT barcode,goodsname,specification
-> FROM demo.view_goodsmaster;
+---------+-----------+---------------+
| barcode | goodsname | specification |
+---------+-----------+---------------+
| 0001 | 本 | 16开 |
+---------+-----------+---------------+
1 row in set (0.00 sec)
```
- 结果显示,我们得到了商品信息表中售价大于 50 元的商品16 开)。
- 假设现在我们需要把数据表 demo.goodsmaster 中的字段“specification”删掉就可以用下面的代码
```sql
mysql> ALTER TABLE demo.goodsmaster DROP COLUMN specification;
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
```
- 这样一来,因为少了一个字段,而我们的语句又是直接查询数据表的,代码就会提示错误:
```sql
mysql> SELECT barcode,goodsname,specification
-> FROM demo.goodsmaster;
ERROR 1054 (42S22): Unknown column 'specification' in 'field list'
```
- 你看代码提示字段“specification”不存在。
- 但是,如果查询的是视图,就可以通过修改视图来规避这个问题。我们可以用下面的代码把刚才的视图修改一下:
```sql
mysql> ALTER VIEW demo.view_goodsmaster
-> AS
-> SELECT
-> itemnumber,
-> barcode,
-> goodsname,
-> '' as specification, -- 由于字段不存在插入一个长度是0的空字符串作为这个字段的值
-> salesprice
-> FROM demo.goodsmaster
-> WHERE salesprice > 50;
Query OK, 0 rows affected (0.02 sec)
```
- 你看虽然实际数据表中已经没有字段“specification”了但是视图中却保留了这个字段而且字段值始终是空字符串。所以我们不用修改原有视图的查询语句它也会正常
运行。下面的代码查询的结果中就包括了实际数据表没有的字段“specification”。
```sql
mysql> SELECT barcode,goodsname,specification
-> FROM demo.view_goodsmaster;
+---------+-----------+---------------+
| barcode | goodsname | specification |
+---------+-----------+---------------+
| 0001 | 本 | |
+---------+-----------+---------------+
1 row in set (0 00 sec)
```
- 结果显示,运行成功了。这个视图查询,就没有受到实际数据表中删除字段的影响。
- 视图有这么多好处,那我以后都用视图可以吗?其实不是的,视图也有自身的不足。
- 如果我们在实际数据表的基础上创建了视图,那么,**如果实际数据表的结构变更了,我们就需要及时对相关的视图进行相应的维护**。特别是当视图是由视图生成的时候,维护会变
得比较复杂。因为创建视图的 SQL 查询可能会对字段重命名,也可能包含复杂的逻辑,这些都会增加维护的成本。
- 一张图来汇总下视图的优缺点:
- ![视图的优缺点](pic/视图的优缺点.png)
- 虽然可以更新视图数据,但总的来说,视图作为虚拟表,主要用于方便查询。我不建议你更新视图的数据,因为对视图数据的更改,都是通过对实际
数据表里数据的操作来完成的,而且有很多限制条件。
- 视图虽然有很多优点。但是在创建视图、简化查询的同时,也要考虑到视图太多而导致的数据库维护成本的问题。
- 视图不是越多越好,特别是嵌套的视图(就是在视图的基础上创建视图),我不建议你使用,因为逻辑复杂,可读性不好,容易变成系统的潜在隐患。
### 2.2 存储过程:如何提高程序的性能和安全性?
## 3. 优化 ## 3. 优化

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Loading…
Cancel
Save