视图是什么

MySQL 视图(View) 是一种虚拟存在的表,同真实表一样,视图也由列和行构成,但视图并不实际存在于数据库中 。行和列的数据来自于定义视图的查询中所使用的表( 基表 ),并且还是在使用视图时动态生成的。数据库中只存放了视图的定义(.frm文件),并没有存放视图中的数据 ,这些数据都存放在定义视图查询所引用的基表中。使用视图查询数据时,数据库会从基表表中取出对应的数据。因此,视图中的数据是依赖于基表表中的数据的。一旦基表中的数据发生改变,显示在视图中的数据也会发生改变;在视图中修改数据,基表中的数据也会发生改变。
从下图可见,创建视图后,只生成了视图的 .frm 文件,没有 .ibd 文件,这是因为 customer_info.frmcustomer_view.frm 的数据都是由 customer_info.ibd 提供。

视图的作用

保密
看这样一个需求:公司职员表的信息很多(姓名、薪水、部门、上级、工号、电话等),而其中有些信息属于个人隐私(薪水、电话),我们希望将此表下放到某管理员时,他只能看到其中的部分信息(姓名、部门、上级、工号),此时,就需要生成原表的一张视图,视图中只包含这一部分信息,然后再将此视图下放给该管理员。不恰当地说,“视图相对于对原表的封装”,对用户有部分不可见性。
性能
关系数据库的数据常常会分表存储,使用外键建立这些表的之间关系。这时,数据库查询通常会用到连接(JOIN)。这样做不但麻烦,效率相对也比较低。如果建立一个视图,将相关的表和字段组合在一起,就可以避免使用JOIN查询数据。
灵活性
如果系统中有一张旧的表,这张表由于设计的问题,即将被废弃。然而,很多应用都是基于这张表,不易修改。这时就可以建立一张视图,视图中的数据直接映射到新建的表。这样,就可以少做很多改动,也达到了升级数据表的目的。

值得一提的是,长期以来,大多数互联网公司的《MySQL开发规范》中都有一条规范:在MySQL中禁止(或建议不要)使用视图。究其原因,主要是由于在MySQL中视图的查询性能不好,同时带来了管理维护上的高成本。

视图的用法

创建视图

1
CREATE VIEW <视图名> AS <SELECT语句>

创建视图前,先给出下面基本数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
mysql> SELECT * FROM room_info;
+--------------+----------+-------+---------+
| type | location | state | room_id |
+--------------+----------+-------+---------+
| 标准间 | 10-1 | 1 | 001 |
| 标准间 | 10-2 | 1 | 002 |
| 标准间 | 10-3 | 0 | 003 |
| 标准间 | 10-4 | 0 | 004 |
| 情侣房 | 10-5 | 0 | 005 |
| 情侣房 | 11-6 | 0 | 006 |
| 大床房 | 11-7 | 0 | 007 |
| 大床房 | 12-8 | 0 | 008 |
| 家庭套间 | 12-9 | 0 | 009 |
| 家庭套间 | 13-10 | 0 | 010 |
| 总统套房 | 10-11 | 1 | 011 |
+--------------+----------+-------+---------+
11 rows in set (0.00 sec)

mysql> SELECT * FROM type_info;
+------+--------------+-------+----------+
| id | type | price | capacity |
+------+--------------+-------+----------+
| 1 | 标准间 | 150 | 2 |
| 2 | 大床房 | 200 | 2 |
| 3 | 情侣房 | 210 | 2 |
| 4 | 家庭套间 | 400 | 4 |
| 5 | 总统套房 | 600 | 4 |
+------+--------------+-------+----------+
5 rows in set (0.00 sec)

mysql> SELECT * FROM customer_info;
+--------+--------+------+-------------+---------+
| name | ID | age | tel | room_id |
+--------+--------+------+-------------+---------+
| 李华 | 511323 | 18 | 18990889826 | 002 |
| 周建 | 511345 | 20 | 17390199497 | 011 |
| 王霸 | 532435 | 45 | 18932981949 | 002 |
| 李斯 | 343234 | 34 | 14342324323 | 001 |
| 巴嘎 | 343323 | 23 | 32232123343 | 002 |
+--------+--------+------+-------------+---------+
5 rows in set (0.00 sec)

1)创建基于单表的视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> CREATE VIEW customer_view
-> AS
-> SELECT `name`, room_id FROM customer_info;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM customer_view;
+--------+---------+
| name | room_id |
+--------+---------+
| 李华 | 002 |
| 周建 | 011 |
| 王霸 | 002 |
| 李斯 | 001 |
| 巴嘎 | 002 |
+--------+---------+
5 rows in set (0.00 sec)

2)创建基于多表的视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> CREATE VIEW customer_view2
-> AS
-> SELECT name, room_info.room_id, room_info.type
-> FROM customer_info, room_info
-> WHERE customer_info.room_id=room_info.room_id;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM customer_view2;
+--------+---------+--------------+
| name | room_id | type |
+--------+---------+--------------+
| 李斯 | 001 | 标准间 |
| 李华 | 002 | 标准间 |
| 王霸 | 002 | 标准间 |
| 巴嘎 | 002 | 标准间 |
| 周建 | 011 | 总统套房 |
+--------+---------+--------------+
5 rows in set (0.00 sec)

3)基于视图创建视图(视图嵌套)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> CREATE VIEW customer_view3
-> AS
-> SELECT name
-> FROM customer_view2;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM customer_view3;
+--------+
| name |
+--------+
| 李斯 |
| 李华 |
| 王霸 |
| 巴嘎 |
| 周建 |
+--------+

不建议视图嵌套创建,不仅影响效率,也会提高后期维护成本。 视图定义中不能引用 TEMPORARY 表(临时表),不能创建 TEMPORARY 视图。

另外,对于创建视图中的 SELECT 语句的指定存在以下限制:

  • 用户除了拥有 CREATE VIEW 权限外,还具有操作中涉及的基表和其他视图的相关权限。
  • SELECT 语句不能引用系统或用户变量。
  • SELECT 语句不能包含 FROM 子句中的子查询。
  • SELECT 语句不能引用预处理语句参数。

删除视图

1
DROP VIEW <视图名1> [ , <视图名2> …]
1
2
mysql> DROP VIEW customer_view2;
Query OK, 0 rows affected (0.00 sec)

注意,删除视图后,建立在本视图上的视图也将不可用;比如将 customer_view2 删掉,customer_view3 将不可用:

1
2
mysql> SELECT * FROM customer_view3;
ERROR 1356 (HY000): View 'custom_info.customer_view3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them

可以使用 CHECK TABLE 来检查此类问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> CHECK TABLE customer_view3 \G;
*************************** 1. row ***************************
Table: custom_info.customer_view3
Op: check
Msg_type: Error
Msg_text: Table 'custom_info.customer_view2' doesn't exist
*************************** 2. row ***************************
Table: custom_info.customer_view3
Op: check
Msg_type: Error
Msg_text: View 'custom_info.customer_view3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
*************************** 3. row ***************************
Table: custom_info.customer_view3
Op: check
Msg_type: error
Msg_text: Corrupt
3 rows in set (0.00 sec)

第 6 行表明,其基表 customer_view2 不存在。

查看视图

1)查看结构

1
2
3
4
5
6
7
8
mysql> DESC demo_view1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(32) | YES | | NULL | |
| age | tinyint(4) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

2)查看定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> SHOW CREATE TABLE demo_view1 \G;
*************************** 1. row ***************************
View: demo_view1
Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `demo_view1` AS select `stuinfo`.`name` AS `name`,`stuinfo`.`age` AS `age` from `stuinfo` order by `stuinfo`.`age`
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)
######或者
mysql> SHOW CREATE VIEW demo_view1 \G;
*************************** 1. row ***************************
View: demo_view1
Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `demo_view1` AS select `stuinfo`.`name` AS `name`,`stuinfo`.`age` AS `age` from `stuinfo` order by `stuinfo`.`age`
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)

3)查看状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mysql> SHOW TABLE STATUS \G;
*************************** 2. row ***************************
Name: demo_view1
Engine: NULL
Version: NULL
Row_format: NULL
Rows: NULL
Avg_row_length: NULL
Data_length: NULL
Max_data_length: NULL
Index_length: NULL
Data_free: NULL
Auto_increment: NULL
Create_time: NULL
Update_time: NULL
Check_time: NULL
Collation: NULL
Checksum: NULL
Create_options: NULL
Comment: VIEW

修改视图

修改结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> DESC demo_view1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| name | varchar(32) | YES | | NULL | |
| age | tinyint(4) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> ALTER VIEW demo_view1
-> AS
-> SELECT age
-> FROM stuinfo;
Query OK, 0 rows affected (0.00 sec)

mysql> DESC demo_view1;
+-------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| age | tinyint(4) | YES | | NULL | |
+-------+------------+------+-----+---------+-------+
1 row 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
24
25
26
27
28
29
mysql> SELECT * FROM customer_view;
+--------+---------+
| name | room_id |
+--------+---------+
| 李华 | 002 |
| 周建 | 011 |
| 王霸 | 002 |
| 李斯 | 001 |
| 巴嘎 | 002 |
+--------+---------+
5 rows in set (0.00 sec)

mysql> UPDATE customer_view
-> SET room_id='011'
-> WHERE name='李华';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> SELECT * FROM customer_view;
+--------+---------+
| name | room_id |
+--------+---------+
| 李华 | 011 |
| 周建 | 011 |
| 王霸 | 002 |
| 李斯 | 001 |
| 巴嘎 | 002 |
+--------+---------+
5 rows in set (0.00 sec)

注意:插入、修改和删除操作更新视图中的数据,实质上是在更新视图所引用的基本表的数据。
某些结构会使得视图不可更新 。如果视图包含以下结构中的任何一种,它就是不可更新的:

  • 聚合函数 SUM()、MIN()、MAX()、COUNT() 等。
  • DISTINCT 关键字。
  • GROUP BY 子句。
  • HAVING 子句。
  • UNION 或 UNION ALL 运算符。
  • 位于选择列表中的子查询。
  • FROM 子句中的不可更新视图或包含多个表。
  • WHERE 子句中的子查询,引用 FROM 子句中的表。
  • ALGORITHM 选项为 TEMPTABLE(使用临时表总会使视图成为不可更新的)的时候。
  • 若视图是由两个以上基本表导出的,则此视图不允许更新

使用视图的时候,还应该注意以下两点:

  • 视图不能索引,也不能有关联的触发器、默认值或规则。
  • 视图不包含数据,所以每次使用视图时,都必须执行查询中所需的任何一个检索操作 。如果用多个连接和过滤条件创建了复杂的视图或嵌套了视图,可能会发现系统运行性能下降得十分严重。因此,在部署大量视图应用时,应该进行系统测试。