25.11 MySQL 视图

2024-04-03_175716

1. 常见的数据库对象

对象描述
表(TABLE)存储数据的逻辑单元, 以行和列的形式存在, 列就是字段, 行就是记录.
数据字典系统表, 存放数据库相关信息的表. 数据通常由数据库系统维护, 程序员通常不可修改, 只可查看.
约束(CONSTRAINT)执行数据校验的规则, 用于保证数据完整性的规则.
视图(VIEW)一个或多个数据表里的数据的逻辑显示. 视图并不存储数据, 而是提供一个定制化的数据展示方式.
索引(INDEX)用于提高查询性能, 相当于书的目录. 通过索引, 数据库系统可以快速定位到所需的数据.
存储过程(PROCEDURE)用于完成一次完整的业务处理, 没有返回值, 但可通过传出参数将多个值传给调用环境.
存储函数(FUNCTION)用于完成一次特定的计算, 具有一个返回值.
触发器(TRIGGER)相当于一个事件监听器. 当数据库发生特定事件(如插入, 更新或删除操作)后, 触发器被触发, 完成相应的处理.

2. 视图

2.1 简介

在MySQL中, 视图(View)是一个虚拟的表, 其内容由查询定义.
视图并不存储数据, 它只包含定义视图的SQL语句, 也因此视图占用的内存空间相对较少.
当查询视图时, MySQL会执行这些SQL语句并返回结果.

视图在多种场景下都非常有用, 以下是一些建议何时使用视图的情形:
* 1. 简化复杂的SQL查询: 当你有一个复杂的SQL查询, 并且这个查询被多个地方重复使用, 你可以将这个查询定义为一个视图.
     这样, 每次需要这个查询的结果时, 只需要简单地查询这个视图即可.

* 2. 抽象数据: 视图可以隐藏数据的复杂性, 只展示用户需要的部分数据.
     例如, 你可能有一个包含大量字段的表, 但某些用户或应用程序只需要其中的几个字段.
     通过创建一个只包含这些字段的视图, 你可以简化对这些用户的数据访问.

* 3. 安全性: 通过视图, 你可以限制用户对基础数据的访问.
     例如, 你可以创建一个视图, 该视图只显示某些用户有权查看的数据行或列.
     这样, 即使用户尝试直接查询基础表, 他们也只能看到视图所允许的数据.

* 4. 逻辑数据独立性: 当基础表的结构发生变化时(例如, 添加, 删除或修改列), 
     如果有很多地方引用了这些表, 那么修改这些引用可能会很繁琐.
     通过使用视图, 你可以将这些引用指向视图而不是基础表.
     这样, 当基础表结构发生变化时, 你只需要更新视图的定义, 而无需修改所有引用.

* 5. 合并数据: 视图可以用于合并来自多个表的数据, 以提供一个统一的视图.
     这对于需要跨多个表查询数据的场景非常有用.

* 6. 遵守业务规则: 你可以通过视图来实施业务规则, 确保用户只能看到或修改符合规则的数据.
     例如, 你可以创建一个视图, 该视图只显示库存量大于零的产品.

* 7. 历史数据或计算字段: 视图可以用于展示基于基础表计算得出的字段, 或者用于展示历史数据的快照.

需要注意的是, 虽然视图有很多优点, 但它们也有一些限制和潜在的性能问题.
例如, 对视图进行插入, 更新或删除操作可能会受到限制, 具体取决于视图的定义和基础表的结构.
此外, 如果视图基于复杂的查询, 那么查询视图本身可能会比直接查询基础表更慢.
因此, 在决定使用视图之前, 最好先评估其适用性和潜在影响.

image-20240402141843168

视图一方面可以帮我们使用表的一部分而不是所有的表,
另一方面也可以针对不同的用户制定不同的查询视图.
比如, 针对一个公司的销售人员, 我们只想给他看部分数据, 而某些特殊的数据, 比如采购的价格, 则不会提供给他.
再比如, 人员薪酬是个敏感的字段, 那么只给某个级别以上的人员开放, 其他人的查询视图中则不提供这个字段.

2.2 视图的理解

视图是SQL中的一个重要概念, 它允许用户通过预定义的查询语句来访问数据, 而不需要每次都编写复杂的查询.
视图基于已有的表(称为基表或基础表)创建, 并且可以根据需要引用一个或多个基表.

image-20240402163454199

视图的创建和删除仅对视图本身产生影响, 并不会改变其依赖的基表结构或数据.
然而, 当对视图执行插入, 删除或修改操作时, 这些变化会反映到基表中, 反之亦然.
这是因为视图本质上是一个基于SQL查询的虚拟表, 它并不存储实际数据, 而是根据查询语句动态生成数据.

向视图提供数据内容的核心语句是SELECT语句, 因此, 可以将视图视为一个预定义的, 存储起来的SELECT查询.
数据库中的视图并不保存数据本身, 真正的数据存储在数据表中.
因此, 当通过视图对数据进行增删改操作时, 这些变更会直接作用在基表上, 确保数据的完整性和一致性.

视图是向用户展示基表数据的一种灵活且高效的方式.
在小型项目中, 可能不需要频繁使用视图, 但在大型项目或数据表结构复杂的情况下, 视图的价值就显得尤为重要.
它可以将经常需要查询的结果集封装成一个虚拟表, 从而提高查询效率, 简化复杂操作, 并为用户提供更加清晰和定制化的数据视图.
理解和使用视图对于数据库管理和应用开发都是非常有益的.

3. 创建视图

3.1 语法格式

在MySQL中, 创建视图的完整语法如下:

CREATE VIEW view_name AS  
SELECT column1, column2, ...  
FROM table_name  
WHERE condition;

这里, view_name是你要创建的视图的名称, SELECT语句定义了视图的内容, 它指定了从哪个表中选择哪些列, 以及任何可能的过滤条件.

注意事项:
创建视图需要具有足够的权限, 并且视图所依赖的基表在视图创建后不能随意删除或修改, 否则可能会导致视图失效或查询错误.
如果基表结构发生变化, 可能需要相应地更新视图定义.
以下是一个具体的例子, 假设我们有一个名为employees的表,
包含id, name, department_id, 和salary列, 我们想要创建一个视图, 该视图只显示薪资超过某个特定值的员工:

CREATE VIEW high_salary_employees AS  
SELECT id, name, salary  
FROM employees  
WHERE salary > 50000;

在这个例子中, 我们创建了一个名为 high_salary_employees的视图,
它包含了employees表中薪资超过50000的员工的id, name, 和salary列.

3.2 示例

-- 使用数据库:
mysql> USE db0;
Database changed

-- 创建视图:
mysql> CREATE VIEW high_salary_employees
AS
SELECT employee_id, first_name, salary
FROM  atguigudb.employees
WHERE  salary > 5000;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图的数据:
mysql> SELECT * FROM high_salary_employees;
+-------------+-------------+----------+
| employee_id | first_name  | salary   |
+-------------+-------------+----------+
|         100 | Steven      | 24000.00 |
|         ... | ...         | ...      |  -- 省略
|         204 | Hermann     | 10000.00 |
|         205 | Shelley     | 12000.00 |
|         206 | William     |  8300.00 |
+-------------+-------------+----------+
58 rows in set (0.00 sec)
实际上就是我们在 SQL 查询语句的基础上封装了视图 VIEW, 这样就会基于 SQL 语句的结果集形成一张虚拟表.

image-20240402214029426

-- 创建年薪视图:
mysql> CREATE VIEW annual_salary
AS
SELECT 
employee_id AS `emp_id`,
first_name AS `name`,
salary * 12 * (1 + IFNULL(commission_pct, 0)) AS `year_salary`
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec)

-- 查看年薪视图:
mysql> SELECT * FROM annual_salary;
+--------+-------------+-------------+
| emp_id | name        | year_salary |
+--------+-------------+-------------+
|    100 | Steven      |   288000.00 |
|    101 | Neena       |   204000.00 |
|    102 | Lex         |   204000.00 |
|    103 | Alexander   |   108000.00 |
|    104 | Bruce       |    72000.00 |
|    ... | ...         |         ... |  -- 省略
+--------+-------------+-------------+
107 rows in set (0.00 sec)

3.3 设置字段名称

在创建视图时, 没有在视图名后面指定字段列表, 则视图中字段列表默认和SELECT语句中的字段列表一致.
如果SELECT语句中给字段取了别名, 那么视图中的字段名和别名相同.

在MySQL中, 设置视图字段名称的方式:
* 1. 方式一: 在SELECT语句中使用AS关键字为字段设置别名.
* 2. 方式二: 在CREATE VIEW语句的列定义部分直接指定字段名称.
-- 创建年薪视图, 并为字段设置名称:
mysql> CREATE VIEW annual_salary2
AS
SELECT employee_id AS `ID_NUMBER`, 
first_name AS `NAME`,
salary * 12 * (1 + IFNULL(commission_pct, 0)) AS `ANN_SALARY`
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec
                           
-- 查看年薪视图:       
mysql> SELECT * FROM annual_salary2;
+-----------+-------------+------------+
| ID_NUMBER | NAME        | ANN_SALARY |
+-----------+-------------+------------+
|       100 | Steven      |  288000.00 |
|       101 | Neena       |  204000.00 |
|       102 | Lex         |  204000.00 |
|       103 | Alexander   |  108000.00 |
|       104 | Bruce       |   72000.00 |
|       105 | David       |   57600.00 |
|       ... | ...         |   ...      |   -- 省略       
+-----------+-------------+------------+
107 rows in set (0.00 sec)
-- 方式二:
mysql> CREATE VIEW annual_salary3
(ID_NUMBER, NAME, ANN_SALARY)  -- 设置别名
AS
SELECT employee_id, first_name,
salary * 12 * (1 + IFNULL(commission_pct, 0))
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec)

-- 查看年薪视图:       
mysql> SELECT * FROM annual_salary3;
+-----------+-------------+------------+
| ID_NUMBER | NAME        | ANN_SALARY |
+-----------+-------------+------------+
|       100 | Steven      |  288000.00 |
|       101 | Neena       |  204000.00 |
|       102 | Lex         |  204000.00 |
|       103 | Alexander   |  108000.00 |
|       ... | ...         |   ...      |   -- 省略       
+-----------+-------------+------------+
107 rows in set (0.00 sec)

3.4 多表联合视图

多表联合视图(也称为多表视图)是一个虚拟的表, 它由多个表通过连接操作组合而成.
这个视图为用户提供了一个统一的, 简化的接口来查询多个表中的数据, 而无需重写的连接查询.
-- 内连接, 查看所有员工id, 员工姓名, 部门名称(没有部门不显示):
mysql> CREATE VIEW emp_depname
AS
SELECT emp.employee_id AS `emp_id`, emp.first_name AS `name`, dep.department_name
FROM atguigudb.employees AS `emp`
INNER JOIN atguigudb.departments AS `dep`
ON emp.department_id = dep.department_id;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:  
mysql> SELECT * FROM emp_depname2;
+--------+-------------+------------------+
| emp_id | name        | department_name  |
+--------+-------------+------------------+
|    200 | Jennifer    | Administration   |
|    201 | Michael     | Marketing        |
|    202 | Pat         | Marketing        |
|    114 | Den         | Purchasing       |
|    115 | Alexander   | Purchasing       |
|    116 | Shelli      | Purchasing       |
|    117 | Sigal       | Purchasing       |
|    ... | ...         | ...              |   -- 省略       
+--------+-------------+------------------+
106 rows in set (0.00 sec)
-- 右连接, 查看所有员工id, 员工姓名, 部门名称(没有部门也显示):
mysql> CREATE VIEW emp_depname2
AS
SELECT emp.employee_id AS `emp_id`, emp.first_name AS `name`, dep.department_name
FROM atguigudb.employees AS `emp`
LEFT JOIN atguigudb.departments AS `dep`
ON emp.department_id = dep.department_id;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:  
mysql> SELECT * FROM emp_depname2;
+--------+-------------+------------------+
| emp_id | name        | department_name  |
+--------+-------------+------------------+
|    100 | Steven      | Executive        |
|    101 | Neena       | Executive        |
|    102 | Lex         | Executive        |
|    103 | Alexander   | IT               |
|    104 | Bruce       | IT               |
|    ... | ...         | ...              |   -- 省略       
+--------+-------------+------------------+
107 rows in set (0.00 sec)
-- 获取部门的最高工资, 最低工资, 平均工资:
mysql> CREATE VIEW emp_min_max_avg
(name, minsal, maxsal, avgsal)
AS
SELECT dep.department_name, MIN(emp.salary), MAX(emp.salary), AVG(emp.salary)
FROM atguigudb.employees AS `emp`
INNER JOIN atguigudb.departments AS `dep`
ON emp.department_id = dep.department_id
GROUP BY dep.department_name;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:  
mysql> SELECT * FROM emp_min_max_avg;
+------------------+----------+----------+--------------+
| name             | minsal   | maxsal   | avgsal       |
+------------------+----------+----------+--------------+
| Executive        | 17000.00 | 24000.00 | 19333.333333 |
| IT               |  4200.00 |  9000.00 |  5760.000000 |
| Finance          |  6900.00 | 12000.00 |  8600.000000 |
| Purchasing       |  2500.00 | 11000.00 |  4150.000000 |
| Shipping         |  2100.00 |  8200.00 |  3475.555556 |
| Sales            |  6100.00 | 14000.00 |  8955.882353 |
| Administration   |  4400.00 |  4400.00 |  4400.000000 |
| Marketing        |  6000.00 | 13000.00 |  9500.000000 |
| Human Resources  |  6500.00 |  6500.00 |  6500.000000 |
| Public Relations | 10000.00 | 10000.00 | 10000.000000 |
| Accounting       |  8300.00 | 12000.00 | 10150.000000 |
+------------------+----------+----------+--------------+
11 rows in set (0.00 sec)

3.4 格式化数据

在创建视图时, 可以使用SQL的字符串函数来格式化数据, 以便在视图中以特定的格式展示.
可以使用各种内置函数来操作数据, 以满足特定的输出需求.

比如, 我们想输出员工姓名和对应的部门名, 对应格式为emp_name(department_name),
就可以使用视图来完成数据格式化的操作:
-- 创建视图:
mysql> CREATE VIEW emp_depname3
AS
SELECT CONCAT(emp.first_name,'(', dep.department_name,')' ) AS emp_depname
FROM atguigudb.employees AS `emp`
INNER JOIN atguigudb.departments AS `dep`
ON emp.department_id = dep.department_id;
Query OK, 0 rows affected (0.00 sec)

-- 查看视图:  
mysql> SELECT * FROM emp_depname;
+---------------------------+
| emp_depname               |
+---------------------------+
| Jennifer(Administration)  |
| Michael(Marketing)        |
| ...                       |  -- 省略       
+---------------------------+
106 rows in set (0.00 sec)

3.5 基于视图创建视图

在SQL中, 可以基于一个已经存在的视图来创建另一个视图.
通过组合多个视图或者在一个视图的基础上进一步处理数据来创建更复杂的视图.

举例: 联合'emp_depname''annual_salary'视图查询员工姓名, 部门名称, 年薪信息创建emp_depname_anasal视图:
-- 创建视图:
mysql> CREATE VIEW emp_depname_anasal
AS
SELECT emp.emp_id, emp.name, ann.year_salary, emp.department_name
FROM emp_depname AS `emp`
INNER JOIN annual_salary AS `ann`
ON emp.emp_id = ann.emp_id;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp_depname_anasal;
+--------+-------------+-------------+------------------+
| emp_id | name        | year_salary | department_name  |
+--------+-------------+-------------+------------------+
|    100 | Steven      |   288000.00 | Executive        |
|    101 | Neena       |   204000.00 | Executive        |
|    102 | Lex         |   204000.00 | Executive        |
|    103 | Alexander   |   108000.00 | IT               |
|    104 | Bruce       |    72000.00 | IT               |
|    ... | ...         |         ... | ...              |  -- 省略     
+--------+-------------+-------------+------------------+
106 rows in set (0.00 sec)

4. 查看视图

语法1: 查看数据库的表对象, 视图对象: SHOW TABLES;
-- 查看视图对象:
mysql> SHOW TABLES;
+--------------------+
| Tables_in_db0      |
+--------------------+
| annual_salary      |
| emp_depname        |
| emp_depname2       |
| ...                |
+--------------------+
语法2: 查看视图的结构: DESC / DESCRIBE view_name;
-- 查看视图结构:
mysql> DESC annual_salary;
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| emp_id      | int          | NO   |     | 0       |       |
| name        | varchar(20)  | YES  |     | NULL    |       |
| year_salary | double(22,2) | YES  |     | NULL    |       |
+-------------+--------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
语法3: 查看视图的属性信息(数据表的存储引擎, 版本, 数据行数和数据大小等): SHOW TABLE STATUS LIKE 'view_name'\G
-- 执行结果显示, 注释Comment为VIEW, 说明该表为视图, 其他的信息为NULL, 说明这是一个虚表:
mysql> SHOW TABLE STATUS LIKE 'annual_salary';

image-20240403124046457

语法4: 查看视图的详细定义信息: SHOW CREATE VIEW view_name;
mysql> SHOW CREATE VIEW annual_salary;

image-20240403125454863

5. 更新视图的数据

MySQL支持使用INSERT, UPDATE和DELETE语句对视图中的数据进行插入, 更新和删除操作.
当视图中的数据发生变化时, 数据表中的数据也会发生变化, 反之亦然.

5.1 更行示例

-- 创建表格:
CREATE TABLE emp0(
   id INT,
   name VARCHAR(20),
   phone INT
);
Query OK, 0 rows affected (0.02 sec)

-- 插入数据:
mysql> INSERT INTO emp0 VALUES(1, 'kid', 123456);
Query OK, 1 row affected (0.00 sec)

-- 创建视图:
mysql> CREATE VIEW emp0_view
AS 
SELECT * FROM emp0;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp0_view;
+------+------+--------+
| id   | name | phone  |
+------+------+--------+
|    1 | kid  | 123456 |
+------+------+--------+
1 row in set (0.00 sec)

-- 修改视图:
mysql> UPDATE emp0_view SET phone = 10086 WHERE name = 'kid';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 查看视图:
mysql> SELECT * FROM emp0_view;
+------+------+-------+
| id   | name | phone |
+------+------+-------+
|    1 | kid  | 10086 |
+------+------+-------+
1 row in set (0.00 sec)

-- 查看表格:
mysql> SELECT * FROM emp0;
+------+------+-------+
| id   | name | phone |
+------+------+-------+
|    1 | kid  | 10086 |
+------+------+-------+
1 row in set (0.00 sec)

5.2 删除示例

mysql>  DELETE FROM emp0_view  WHERE name = 'kid';
Query OK, 1 row affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp0_view;
Empty set (0.00 sec)

-- 查看表格:
mysql> SELECT * FROM emp0;
Empty set (0.00 sec)

5.3 不可更新的视图

要使视图保持可更新性, 必须确保视图中的每一行与底层基本表中的行之间存在一对一的映射关系.
然而, 在多种情况下, 视图可能不支持更新操作.
以下是这些情况的详细解释和润色后的表述:
* 1. 使用临时表算法: 当在定义视图时指定了'ALGORITHM = TEMPTABLE', 这表示视图将使用临时表来存储查询结果.
     由于临时表不支持对原始数据的直接更新, 因此这样的视图将不支持INSERT和DELETE操作.

* 2. 缺少非空列: 如果视图中没有包含基本表中所有被定义为非空且未指定默认值的列, 
     那么当尝试向视图中插入数据时, 可能会因为缺少这些必要的列而无法完成, 因此这样的视图不支持INSERT操作.
     
* 3. 使用JOIN操作: 当定义视图的SELECT语句中使用了JOIN联合查询时, 由于JOIN操作可能涉及多个表的数据组合,
     这种组合可能导致无法直接定位到基本表中的具体行进行更新或删除, 因此这样的视图不支持INSERT和DELETE操作.

* 4. 使用数学表达式或子查询: 如果定义视图的SELECT语句后的字段列表中使用了数学表达式或子查询,
     这些操作通常会产生计算后的值或基于其他数据的派生值, 这些值并不直接对应基本表中的实际数据,
     因此这样的视图不支持INSERT操作, 也不支持UPDATE中对使用数学表达式或子查询字段值的修改.

* 5. 使用DISTINCT, 聚合函数等: 当SELECT语句后的字段列表中使用DISTINCT关键字进行去重, 或者使用聚合函数如SUM, COUNT等.
     以及使用GROUP BY, HAVING, UNION等操作时, 这些操作通常会对数据进行汇总或组合, 导致无法直接映射到基本表中的具体行.
     因此这样的视图不支持INSERT, UPDATE和DELETE操作.

* 6. 子查询引用FROM后的表: 如果定义视图的SELECT语句中包含了子查询, 并且子查询中引用了FROM子句后面指定的表,
     这种结构可能导致视图无法准确反映基本表中的数据变化, 因此这样的视图不支持INSERT, UPDATE和DELETE操作.

* 7. 基于不可更新视图: 如果视图的定义是基于另一个已经不可更新的视图,
     那么由于底层视图的限制, 这个视图也将继承其不可更新的特性.

* 8. 常量视图: 常量视图是只包含常量值或字面量的视图, 由于其不涉及任何基本表数据, 因此自然不支持任何更新操作.

虽然可以更新视图数据, 但总的来说, 视图作为虚拟表主要用于方便查询, 不建议更新视图的数据.
对视图数据的更改, 都是通过对实际数据表里数据的操作来完成的.

6. 修改视图

替换视图语法: CREATE OR REPLACE VIEW view_name;
如果同名的视图已经存在, 则替换它. 这个命令让你能够修改视图的定义, 而无需先删除现有的视图再重新创建它.

修改视图语法: ALTER VIEW view_name;
mysql> SHOW TABLES;
+--------------------+
| Tables_in_db0      |
+--------------------+
| annual_salary      |
| ...                |
+--------------------+

-- annual_salary视图已经存在, 替换掉:
mysql> CREATE OR REPLACE VIEW annual_salary  -- 不算提成版本:
AS
SELECT 
employee_id AS `emp_id`,
first_name AS `name`,
salary * 12 AS `year_salary`
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM annual_salary;
+--------+-------------+-------------+
| emp_id | name        | year_salary |
+--------+-------------+-------------+
|    100 | Steven      |   288000.00 |
|    101 | Neena       |   204000.00 |
|    102 | Lex         |   204000.00 |
|    103 | Alexander   |   108000.00 |
|    ... | ...         |   ...       |
+--------+-------------+-------------+
-- 计算半年薪资:
mysql> ALTER VIEW annual_salary  -- 不算提成版本:
AS
SELECT 
employee_id AS `emp_id`,
first_name AS `name`,
salary * 6 AS `salary_6`
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM annual_salary;
+--------+-------------+-----------+
| emp_id | name        | salary_6  |
+--------+-------------+-----------+
|    100 | Steven      | 144000.00 |
|    101 | Neena       | 102000.00 |
|    102 | Lex         | 102000.00 |
|    103 | Alexander   |  54000.00 |
|    104 | Bruce       |  36000.00 |
|    105 | David       |  28800.00 |
|    106 | Valli       |  28800.00 |
|    ... | ...         |   ...     |
+--------+-------------+-----------+

7. 删除视图

删除视图的语法: DROP VIEW IF EXISTS 视图名称;
删除视图只是删除视图的定义, 并不会删除基表的数据.

注意事项:
如果基于视图a, b创建了新的视图c, 将视图a或者视图b删除, 会导致视图c的查询失败.
这样的视图c需要手动删除或修改, 否则影响使用.
mysql> DROP VIEW IF EXISTS annual_salary;
Query OK, 0 rows affected (0.00 sec)

8. 总结

8.1 视图的优点

视图在数据库设计和应用中确实扮演了重要的角色, 主要的优点如下:
* 1. 操作简单: 视图可以隐藏数据的复杂性, 使得用户只需要关注他们关心的数据部分, 而不需要了解底层数据表的结构和关联关系.
     通过视图, 开发人员可以编写一次复杂的查询逻辑, 并在多个地方重复使用这个视图, 从而简化了开发过程.
* 2. 减少数据冗余: 视图本身不存储数据, 它只存储查询语句.
     因此, 视图不会占用额外的存储空间, 这有助于减少数据的冗余和存储成本.
     当底层数据表中的数据发生变化时, 视图会自动反映这些变化, 无需手动更新.
* 3. 数据安全: 通过视图, 可以限制用户对数据的访问权限.
     例如, 可以创建一个只包含部分字段或部分记录的视图, 然后只允许用户通过这个视图来访问数据.
     视图还可以隐藏敏感数据, 例如密码或敏感的业务逻辑, 从而提高数据的安全性.
* 4. 适应灵活多变的需求: 当业务需求发生变化时, 如果直接修改数据表的结构可能会影响到多个应用程序或查询.
     而通过使用视图, 可以在不修改底层数据表的情况下, 通过修改视图的定义来满足新的需求.
     视图提供了一种抽象层, 使得应用程序与底层数据表之间的耦合度降低, 从而更容易适应变化.
* 5. 能够分解复杂的查询逻辑: 对于复杂的查询逻辑, 可以将其分解为多个简单的查询, 并将这些查询封装在视图中.
     然后, 可以通过组合这些视图来构建更复杂的查询, 从而简化查询逻辑的管理和维护.
     通过分解复杂的查询逻辑, 还可以提高查询的性能, 因为每个视图都可以针对其特定的查询进行优化.
     
总之, 视图提供了一种强大而灵活的工具来管理, 简化和优化数据库访问和查询逻辑, 
使得开发人员能够更高效地处理数据库操作, 同时保障数据的安全性和完整性.

image-20240403145913127

8.2 视图的缺点

视图的缺点主要包括以下几个方面:
* 1. 额外开销: 视图是基于SQL查询的, 它不存储实际的数据, 而是存储了查询数据的SQL语句.
     因此, 每次通过视图进行数据查询时, 都需要执行其背后的SQL语句, 
     这个执行过程包括解析视图定义, 优化查询计划以及执行实际的查询操作, 这些步骤都需要消耗一定的计算资源.
     相比之下, 一个简单的SQL查询可能更快.
* 2. 维护复杂: 当底层数据表的结构发生变化(如添加, 删除或修改字段), 相关的视图可能需要进行相应的更新.
     这是因为视图是基于底层数据表构建的, 如果底层数据表的结构发生变化, 视图可能就不再准确反映数据, 甚至可能变得无效.
     特别是在大型系统中, 视图数量众多, 维护起来可能相当繁琐.
     此外, 如果视图之间存在依赖关系(如嵌套视图), 修改一个视图可能会影响到其他多个视图, 进一步增加了维护的复杂性.
* 3. 可读性和可维护性挑战: 创建视图的SQL查询可能包含复杂的逻辑, 如字段重命名, 条件筛选, 聚合计算等.
     这些复杂的查询逻辑可能使得视图变得难以理解和维护.
     对于其他开发人员来说, 可能需要花费更多的时间和精力来理解视图的逻辑和功能.
     此外, 如果视图逻辑过于复杂, 也增加了出错的可能性, 进一步影响了系统的稳定性和可靠性.

为了优化视图的使用和管理, 可以采取以下措施:
* 1. 精简视图数量: 避免创建不必要的视图, 只针对那些经常使用的, 能够简化查询逻辑的数据创建视图.

* 2. 简化视图逻辑: 尽量保持视图逻辑的简洁性, 避免在视图中进行复杂的计算或处理.
     复杂的逻辑可以放在应用程序层面处理.

* 3. 定期审查和维护: 定期审查现有视图, 确保其与实际数据表保持同步, 并根据需要进行更新或优化.

* 4. 文档化视图: 为每个视图提供详细的文档说明, 包括其用途, 依赖关系, 查询逻辑等, 以便其他开发人员能够理解和维护.

* 5. 使用命名规范: 为视图制定统一的命名规范, 以便快速识别和理解视图的含义和用途.

* 6. 限制嵌套视图的使用: 尽量避免使用嵌套视图, 以减少维护的复杂性和潜在风险, 
    如果必须使用嵌套视图, 应确保其逻辑清晰, 易于理解.

通过综合考虑以上措施, 可以更好地利用视图的优点, 同时降低其可能带来的维护成本, 使系统整体达到最优状态.

9. 练习

9.1 练习1

-- 1. 使用表employees创建视图employee_vu, 其中包括姓名(LAST_NAME), 员工号(EMPLOYEE_ID)部门号(DEPARTMENT_ID):
mysql> CREATE VIEW employee_vu
(LAST_NAME, EMPLOYEE_ID, DEPARTMENT_ID)
AS
SELECT employee_id, last_name, department_id
FROM atguigudb.employees;
Query OK, 0 rows affected (0.01 sec)
-- 2. 显示视图的结构:
mysql> DESC employee_vu;
+---------------+-------------+------+-----+---------+-------+
| Field         | Type        | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| LAST_NAME     | int         | NO   |     | 0       |       |
| EMPLOYEE_ID   | varchar(25) | NO   |     | NULL    |       |
| DEPARTMENT_ID | int         | YES  |     | NULL    |       |
+---------------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
-- 3. 查询视图中的全部内容:
mysql> SELECT * FROM employee_vu;
+-----------+-------------+---------------+
| LAST_NAME | EMPLOYEE_ID | DEPARTMENT_ID |
+-----------+-------------+---------------+
|       100 | King        |            90 |
|       101 | Kochhar     |            90 |
|       102 | De Haan     |            90 |
|       103 | Hunold      |            60 |
|       ... | ...         |           ... |  -- 省略
+-----------+-------------+---------------+
-- 4. 将视图中的数据限定在部门号是80的范围内:
mysql> ALTER VIEW employee_vu
(LAST_NAME, EMPLOYEE_ID, DEPARTMENT_ID)
AS
SELECT employee_id, last_name, department_id
FROM atguigudb.employees
WHERE department_id = 80;
Query OK, 0 rows affected (0.01 sec

9.2 练习2

-- 复制表格
mysql> CREATE TABLE emps
AS
SELECT * FROM atguigudb.employees;
Query OK, 107 rows affected, 2 warnings (0.02 sec)
Records: 107  Duplicates: 0  Warnings: 2
-- 1. 创建视图emp_v1,要求查询电话号码以'011'开头的员工姓名和工资, 邮箱:
mysql> CREATE VIEW emp_v1 
AS 
SELECT first_name, salary, email
FROM emps
WHERE phone_number LIKE '011%';
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp_v1;
+-------------+----------+----------+
| first_name  | salary   | email    |
+-------------+----------+----------+
| John        | 14000.00 | JRUSSEL  |
| Karen       | 13500.00 | KPARTNER |
| ...         | ...      | ...      |  -- 省略
| Jack        |  8400.00 | JLIVINGS |
| Kimberely   |  7000.00 | KGRANT   |
| Charles     |  6200.00 | CJOHNSON |
+-------------+----------+----------+
35 rows in set (0.00 sec)
-- 2. 要求将视图 emp_v1 修改为查询电话号码以‘011’开头的并且邮箱中包含e字符的员工姓名和邮箱, 电话号码:
mysql> ALTER VIEW emp_v1
AS 
SELECT first_name, email, phone_number
FROM emps
WHERE phone_number LIKE '011%' AND email LIKE '%e%';
Query OK, 0 rows affected (0.01 sec)

-- 查看视图: 
mysql> SELECT * FROM emp_v1;
+-------------+----------+--------------------+
| first_name  | email    | phone_number       |
+-------------+----------+--------------------+
| John        | JRUSSEL  | 011.44.1344.429268 |
| Karen       | KPARTNER | 011.44.1344.467268 |
| ...         | ...      | ...                |  -- 省略
| Lisa        | LOZER    | 011.44.1343.929268 |
| Elizabeth   | EBATES   | 011.44.1343.529268 |
| Ellen       | EABEL    | 011.44.1644.429267 |
+-------------+----------+--------------------+
16 rows in set (0.00 sec)
-- 3. 向 emp_v1 插入一条记录, 是否可以?
--  先查看emps表结构:
mysql> desc emps;
+-----------------+-------------+------+-----+---------+-------+
| Field           | Type        | Null | Key | Default | Extra |
+-----------------+-------------+------+-----+---------+-------+
| employee_id     | int         | NO   |     | 0       |       |
| first_name      | varchar(20) | YES  |     | NULL    |       |
| last_name       | varchar(25) | NO   |     | NULL    |       |
| email           | varchar(25) | NO   |     | NULL    |       |
| phone_number    | varchar(20) | YES  |     | NULL    |       |
| hire_date       | date        | NO   |     | NULL    |       |  -- 大量非空字段
| job_id          | varchar(10) | NO   |     | NULL    |       |
| salary          | double(8,2) | YES  |     | NULL    |       |
| commission_pct  | double(2,2) | YES  |     | NULL    |       |
| manager_id      | int         | YES  |     | NULL    |       |
| department_id   | int         | YES  |     | NULL    |       |
| department_name | varchar(24) | YES  |     | NULL    |       |
+-----------------+-------------+------+-----+---------+-------+
12 rows in set (0.00 sec)

-- 答: 还有非空字段且没有设置默认值, 不能插入数据.
-- 4. 修改emp_v1中员工的工资, 每人涨薪1000
mysql> ALTER VIEW emp_v1 
AS 
SELECT first_name, salary + 1000
FROM emps;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp_v1;
+-------------+---------------+
| first_name  | salary + 1000 |
+-------------+---------------+
| Steven      |      25000.00 |
| Neena       |      18000.00 |
| Lex         |      18000.00 |
| Alexander   |      10000.00 |
| ...         |           ... |
+-------------+---------------+
107 rows in set (0.00 sec)
-- 5. 删除emp_v1中姓名为Christopher的员工:
mysql> SELECT * FROM emp_v1 WHERE first_name = 'Christopher';
+-------------+---------------+
| first_name  | salary + 1000 |
+-------------+---------------+
| Christopher |       9000.00 |
+-------------+---------------+
1 rows in set (0.00 sec)

mysql> DELETE FROM emp_v1 WHERE first_name = 'Christopher';
Query OK, 1 row affected (0.00 sec)

-- 查看数据:
mysql> SELECT * FROM emp_v1 WHERE first_name = 'Christopher';
Empty set (0.00 sec)
-- 6.  创建视图emp_v2, 要求查询部门的最高工资 高于 12000 的部门id和其最高工资:
mysql> CREATE VIEW emp_v33
AS
SELECT department_id, max(salary) AS `max_sal`
FROM emps
-- WHERE salary > 1200  -- 这样更快
GROUP BY department_id
HAVING max_sal > 12000;
Query OK, 0 rows affected (0.01 sec)

-- 查看视图:
mysql> SELECT * FROM emp_v2;
+---------------+-------------+
| department_id | max(salary) |
+---------------+-------------+
|            90 |    24000.00 |
|            80 |    14000.00 |
|            20 |    13000.00 |
+---------------+-------------+
3 rows in set (0.01 sec)
-- 7. 向 emp_v2 中插入一条记录, 是否可以? 肯定不能啊!!! 谁出送分题???
-- 8. 删除刚才的emp_v2 和 emp_v1:
mysql> DROP VIEW IF EXISTS emp_v1, emp_v2;
Query OK, 0 rows affected (0.01 sec)
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是100道MySQL热门面试题: 1. 什么是MySQL? 2. MySQL有哪些优点和缺点? 3. MySQL支持的数据类型有哪些? 4. MySQL中的NULL值是什么意思? 5. 如何在MySQL中创建表? 6. 如何在MySQL中删除表? 7. 如何在MySQL中修改表? 8. 如何在MySQL中添加新的列? 9. 如何在MySQL中删除列? 10. 如何在MySQL中修改列? 11. 如何在MySQL中添加索引? 12. 如何在MySQL中删除索引? 13. 如何在MySQL中修改索引? 14. 如何在MySQL中添加主键? 15. 如何在MySQL中删除主键? 16. 如何在MySQL中修改主键? 17. 如何在MySQL中添加外键? 18. 如何在MySQL中删除外键? 19. 如何在MySQL中修改外键? 20. 如何在MySQL中插入数据? 21. 如何在MySQL中更新数据? 22. 如何在MySQL中删除数据? 23. 如何在MySQL中查询数据? 24. 如何在MySQL中使用SELECT语句? 25. 如何在MySQL中使用WHERE子句? 26. 如何在MySQL中使用ORDER BY子句? 27. 如何在MySQL中使用GROUP BY子句? 28. 如何在MySQL中使用HAVING子句? 29. 如何在MySQL中使用LIMIT子句? 30. 如何在MySQL中使用JOIN语句? 31. 如何在MySQL中使用UNION语句? 32. 如何在MySQL中使用子查询? 33. 如何在MySQL中使用视图? 34. 如何在MySQL中使用存储过程? 35. 如何在MySQL中使用触发器? 36. 如何在MySQL中使用游标? 37. 如何在MySQL中使用事务? 38. 如何在MySQL中使用备份和恢复? 39. 如何在MySQL中使用分区表? 40. 如何在MySQL中使用全文索引? 41. 如何在MySQL中使用正则表达式? 42. 如何在MySQL中使用时间和日期函数? 43. 如何在MySQL中使用数学函数? 44. 如何在MySQL中使用字符串函数? 45. 如何在MySQL中使用聚合函数? 46. 如何在MySQL中使用逻辑运算符? 47. 如何在MySQL中使用比较运算符? 48. 如何在MySQL中使用位运算符? 49. 如何在MySQL中使用流程控制语句? 50. 如何在MySQL中使用存储引擎? 51. MySQL的存储引擎有哪些? 52. MyISAM和InnoDB的区别是什么? 53. 如何在MySQL中优化查询? 54. 如何在MySQL中优化表? 55. 如何在MySQL中优化服务器? 56. 如何在MySQL中配置主从复制? 57. 如何在MySQL中配置集群? 58. 如何在MySQL中配置分布式? 59. 如何在MySQL中配置读写分离? 60. 如何在MySQL中配置高可用? 61. 如何在MySQL中配置监控? 62. 如何在MySQL中配置安全? 63. 如何在MySQL中配置备份和恢复? 64. 如何在MySQL中配置性能调优? 65. 如何在MySQL中配置日志? 66. 如何在MySQL中配置缓存? 67. 如何在MySQL中配置连接池? 68. 如何在MySQL中配置字符集? 69. 如何在MySQL中配置时区? 70. 如何在MySQL中配置分区? 71. 如何在MySQL中配置全文索引? 72. 如何在MySQL中配置正则表达式? 73. 如何在MySQL中配置时间和日期函数? 74. 如何在MySQL中配置数学函数? 75. 如何在MySQL中配置字符串函数? 76. 如何在MySQL中配置聚合函数? 77. 如何在MySQL中配置逻辑运算符? 78. 如何在MySQL中配置比较运算符? 79. 如何在MySQL中配置位运算符? 80. 如何在MySQL中配置流程控制语句? 81. 如何在MySQL中配置存储引擎? 82. MySQL的存储引擎有哪些? 83. MyISAM和InnoDB的区别是什么? 84. 如何在MySQL中优化查询? 85. 如何在MySQL中优化表? 86. 如何在MySQL中优化服务器? 87. 如何在MySQL中配置主从复制? 88. 如何在MySQL中配置集群? 89. 如何在MySQL中配置分布式? 90. 如何在MySQL中配置读写分离? 91. 如何在MySQL中配置高可用? 92. 如何在MySQL中配置监控? 93. 如何在MySQL中配置安全? 94. 如何在MySQL中配置备份和恢复? 95. 如何在MySQL中配置性能调优? 96. 如何在MySQL中配置日志? 97. 如何在MySQL中配置缓存? 98. 如何在MySQL中配置连接池? 99. 如何在MySQL中配置字符集? 100. 如何在MySQL中配置时区?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值