MySQL入门系列:视图

上集回顾

上集唠叨了表中数据操作的一些语句,包括用于插入数据的INSERT语句,用于删除数据的DELETE语句,用于更新数据的UPDATE语句。不过我们之前说到的增删改查的语句都是一次性的,这些请求被包装成请求被客户端发到服务器,服务器处理后把结果返回给客户端之后就没有用了,本集将聚焦于如何让这些一次性的语句变得可以很容易的被重复使用。

视图

我们之前唠叨过连接表的查询,比方说下边这个:

mysql> SELECT s1.number, s1.name, s1.major, s2.subject, s2.score FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男';
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子腾    | 计算机科学与工程         | 母猪的产后护理              |    78 |
| 20180101 | 杜子腾    | 计算机科学与工程         | 论萨达姆的战争准备          |    88 |
| 20180103 | 范统      | 软件工程                 | 母猪的产后护理              |    59 |
| 20180103 | 范统      | 软件工程                 | 论萨达姆的战争准备          |    61 |
+----------+-----------+--------------------------+-----------------------------+-------+
4 rows in set (0.00 sec)

mysql>
复制代码

我们查询出了一些男学生的基本信息和成绩信息,如果下次还想得到这些信息,我们就不得不把这个又臭又长的查询语句再敲一遍,所以MySQL提供了视图(英文名VIEW)来帮助我们用很容易的方式去复用这些查询语句。

创建视图

一个视图可以理解为一个查询语句的别名,创建视图的语句如下:

CREATE VIEW 视图名 AS 查询语句
复制代码

比如我们想根据上边那个又臭又长的查询语句来创建一个视图可以这么写:

mysql> CREATE VIEW male_student_info AS SELECT s1.number, s1.name, s1.major, s2.subject, s2.score FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男';
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

这样,这个名称为male_student_info的视图就代表了那一串又臭又长的查询语句了。

使用视图

视图也被称为虚拟表,因为我们可以对视图进行一些类似表的增删改查操作,只不过我们对视图的相关操作都会被映射到那个又臭又长的查询语句对应的底层的表上。那一串又臭又长的查询语句的查询列表可以被当作视图虚拟列,比方说male_student_info这个视图对应的查询语句中的查询列表是numbernamemajorsubjectscore,它们也是male_student_info视图的虚拟列

比如我们可以使用平常的查询语句从视图中查询我们需要的信息可以这么写:

mysql> SELECT * FROM male_student_info;
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子腾    | 计算机科学与工程         | 母猪的产后护理              |    78 |
| 20180101 | 杜子腾    | 计算机科学与工程         | 论萨达姆的战争准备          |    88 |
| 20180103 | 范统      | 软件工程                 | 母猪的产后护理              |    59 |
| 20180103 | 范统      | 软件工程                 | 论萨达姆的战争准备          |    61 |
+----------+-----------+--------------------------+-----------------------------+-------+
4 rows in set (0.00 sec)

mysql>
复制代码

这里我们的查询列表是*,这也就意味着male_student_info所代表的查询语句的结果集将作为整个查询的结果集返回。从这个例子中我们也可以看到,我们不再需要使用那句又臭又长的连接查询语句了,只需要从它对应的视图中查询即可。

除此之外,我们在真实表中使用的那些查询语句都可以被用到视图这个虚拟表中,比方说这个查询语句:

mysql> SELECT subject, AVG(score) FROM male_student_info WHERE score > 60 GROUP BY subject HAVING AVG(score) > 75 LIMIT 1;
+-----------------------+------------+
| subject               | AVG(score) |
+-----------------------+------------+
| 母猪的产后护理        |    78.0000 |
+-----------------------+------------+
1 row in set (0.00 sec)

mysql>
复制代码

我们再次强调一遍,视图其实就是某个查询的别名,而不是某个查询的结果集,换句话说就是,创建视图的时候并不会把那个又臭又长的查询语句的结果集维护在硬盘或者内存里!在对视图进行查询时,MySQL服务器将会帮助我们把对视图的查询语句转换为对底层表的查询语句然后再执行,所以上边这个查询其实会被转换成下边这个查询语句去执行:

SELECT subject, AVG(score) FROM student_info AS s1 INNER JOIN student_score AS s2 WHERE s1.number = s2.number AND s1.sex = '男' AND score > 60 GROUP BY subject HAVING AVG(score) > 75;
复制代码

只不过这个转换的过程我们并不能看到,所以主观上认为硬盘或内存里真的维护了一个视图对应的表而已~ 更复杂的一些查询语句,比如子查询、连接查询什么的,都可以被用到视图上,我们这里就不举例子了。

有一点比较有趣的是,在查询时,视图可以和表一起使用,包括子查询和连接查询,比如这样:

mysql> SELECT * FROM male_student_info WHERE number IN (SELECT number FROM student_info WHERE major = '计算机科学与工程');
+----------+-----------+--------------------------+-----------------------------+-------+
| number   | name      | major                    | subject                     | score |
+----------+-----------+--------------------------+-----------------------------+-------+
| 20180101 | 杜子腾    | 计算机科学与工程         | 母猪的产后护理              |    78 |
| 20180101 | 杜子腾    | 计算机科学与工程         | 论萨达姆的战争准备          |    88 |
+----------+-----------+--------------------------+-----------------------------+-------+
2 rows in set (0.00 sec)

mysql>
复制代码

所以在使用层面,我们完全可以把视图当作一个表去使用,但是它的实现原理却是在执行语句时转换为对底层表的操作。使用视图的好处也是显而易见的,我们可以复用某个查询语句,从而简化了查询操作,避免了每次查询时都要写一遍又臭又长的语句;对视图的操作更加直观,而不用考虑它底层的查询细节

利用视图来创建新视图

我们前边说视图是某个查询语句的别名,其实这个查询语句不仅可以从普通的表中查询数据,也可以从另一个视图中查询数据,只要是个合法的查询语句就好了。比方说我们利用male_student_info视图来创建另一个新视图可以这么写:

mysql> CREATE VIEW by_view AS SELECT number, name, score FROM male_student_info;
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

我们查询一下这个从另一个视图中生成的视图:

mysql> SELECT * FROM by_view;
+----------+-----------+-------+
| number   | name      | score |
+----------+-----------+-------+
| 20180101 | 杜子腾    |    78 |
| 20180101 | 杜子腾    |    88 |
| 20180103 | 范统      |    59 |
| 20180103 | 范统      |    61 |
+----------+-----------+-------+
4 rows in set (0.00 sec)

mysql>
复制代码

这种利用其他的视图来生成的新视图也被称为嵌套视图,在对某个嵌套视图执行查询时,查询语句会先被转换成对它依赖的视图的查询,再转换成对底层表的查询。

创建视图时指定自定义列名

我们前边说过视图虚拟列其实是这个视图对应的查询语句的查询列表,我们也可以在创建列表的时候为这些虚拟列自定义列名,这些自定义列名写到视图名后边,用逗号,分隔就好了,不过需要注意的是,自定义列名一定要和查询列表中的查询对象一一对应。比如我们新创建一个自定义列名的视图:

mysql> CREATE VIEW student_info_view(no, n, m) AS SELECT number, name, major FROM student_info;
Query OK, 0 rows affected (0.02 sec)

mysql>
复制代码

我们的自定义列名列表是no, n, m,分别对应查询列表中的number, name, major。有了自定义列名之后,我们之后对视图的查询语句都要基于这些自定义列名,比如我们可以这么查询:

mysql> SELECT no, n, m FROM student_info_view;
+----------+-----------+--------------------------+
| no       | n         | m                        |
+----------+-----------+--------------------------+
| 20180101 | 杜子腾    | 计算机科学与工程         |
| 20180102 | 杜琦燕    | 计算机科学与工程         |
| 20180103 | 范统      | 软件工程                 |
| 20180104 | 史珍香    | 软件工程                 |
| 20180105 | 范剑      | 飞行器设计               |
| 20180106 | 朱逸群    | 电子信息                 |
+----------+-----------+--------------------------+
6 rows in set (0.00 sec)

mysql>
复制代码

如果仍旧使用与视图对应的查询语句的查询列表中的列名就会报错,比如这样:

mysql> SELECT number, name, major FROM student_info_view;
ERROR 1054 (42S22): Unknown column 'number' in 'field list'
mysql>
复制代码

查看和删除视图

查看有哪些视图

我们想查看当前数据库中有哪些视图的话,其实和查看有哪些表的命令是一样的:

mysql> SHOW TABLES;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| by_view             |
| first_table         |
| male_student_info   |
| second_table        |
| student_info        |
| student_info_view   |
| student_score       |
| t                   |
| t1                  |
| t2                  |
| t3                  |
| zero_table          |
+---------------------+
12 rows in set (0.00 sec)

mysql>
复制代码

可以看到,我们创建的几个视图,包括by_viewmale_student_infostudent_info_view就都显示出来了。需要注意的是,因为视图是一张虚拟表,所以新创建的视图的名称不能和当前数据库中的其他视图或者表的名称冲突

查看视图的定义

因为视图是一张虚拟表,所以用来查看表结构的语句都可以用来查看视图的结构,不过我们经常使用的查看视图定义语句是这个:

SHOW CREATE VIEW 视图名
复制代码

我们来查看一下student_info_view视图的定义:

mysql> SHOW CREATE VIEW student_info_view\G
*************************** 1. row ***************************
                View: student_info_view
         Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `student_info_view` AS select `student_info`.`number` AS `no`,`student_info`.`name` AS `n`,`student_info`.`major` AS `m` from `student_info`
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)

mysql>
复制代码

删除视图

如果某个视图我们不想要了,可以使用这个语句来删除掉它:

DROP VIEW 视图名
复制代码

比如我们把by_view视图删掉可以这么写:

mysql> DROP VIEW by_view;
Query OK, 0 rows affected (0.00 sec)

mysql>
复制代码

然后再查看当前数据库中的表:

mysql> SHOW TABLES;
+---------------------+
| Tables_in_xiaohaizi |
+---------------------+
| first_table         |
| male_student_info   |
| second_table        |
| student_info        |
| student_info_view   |
| student_score       |
| t                   |
| t1                  |
| t2                  |
| t3                  |
| zero_table          |
+---------------------+
11 rows in set (0.00 sec)

mysql>
复制代码

这个视图就不见了!

更新视图

我们前边进行的都是对视图的查询操作,其实也可以对视图进行更新,也就是在视图上执行INSERTDELETEUPDATE语句。对视图执行更新语句的本质上是对该视图对应的底层表进行更新。比方说视图student_info_view的底层表是student_info,所以如果我们对student_info_view执行更新语句就相当于对student_info表进行更新,比方说我们执行这个语句:

mysql> UPDATE student_info_view SET n = '111' WHERE no = 20180101;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>
复制代码

我们再到student_info表中看一下这个学生的名称是否被改了:

mysql> SELECT name FROM student_info WHERE number = 20180101;
+------+
| name |
+------+
| 111  |
+------+
1 row in set (0.00 sec)

mysql>
复制代码

名称的确被更改成功了!

不过并不是可以在所有的视图上执行更新语句的,在生成视图的时候使用了下边这些语句的都不能进行更新:

  • 分组
  • 连接查询
  • 子查询
  • 组合查询
  • 聚集函数
  • DISTINCT
  • 查询列表上是非列名的表达式

虽然有这么多限制,但是需要我们注意的是,一般情况下,我们只在视图上执行查询操作而不进行更新操作!这里介绍对视图的更新只是为了语法的完整性,并不是建议大家在实际使用过程中使用对视图的更新功能。

小册

本系列专栏都是MySQL入门知识,想看进阶知识可以到小册中查看:MySQL是怎样运行的链接 。小册的内容主要是从小白的角度出发,用比较通俗的语言讲解关于MySQL进阶的一些核心概念,比如记录、索引、页面、表空间、查询优化、事务和锁等,总共的字数大约是三四十万字,配有上百幅原创插图。主要是想降低普通程序员学习MySQL进阶的难度,让学习曲线更平滑一点~

猜你喜欢

转载自juejin.im/post/5c8efdabf265da67e93c1063