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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/514252.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JMeter+Grafana+influxdb 配置出现transaction无数据情况解决办法

JMeterGrafanainfluxdb 配置出现transaction无数据情况解决办法 一、问题描述二、解决方法 一、问题描述 如下图所示出现application有数据但是transaction无数据情况 二、解决方法 需要做如下设置 打开变量设置如下图打开两个选项 然后再进行后端监听器的设置 如下图所…

AR/VR技术对制造业劳动力危机的影响

借助 AR/VR 的力量缩小现代制造业的技能差距 数字化转型仍然是企业的首要任务,其许多方面都需要人工干预。然而,推动此类举措所需的技术工人日益短缺。这就造成了我们所说的“制造业劳动力危机”。 制造业应当如何: 制造业用工危机正在影响…

基于单片机的汽车自动预警刹车系统汇编

**单片机设计介绍,基于单片机的汽车自动预警刹车系统汇编 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的汽车自动预警刹车系统汇编概要主要描述了通过单片机技术实现汽车自动预警和刹车控制的系统设计和…

Tinymce富文本编辑器二次开发电子病历时解决的bug

前言 本文是在Tinymce富文本编辑器添加自定义toolbar,二级菜单,自定义表单,签名的基础之上进行一些bug记录,功能添加,以及模版的应用和打印 项目描述 建立电子病历模版—录入(电子病历模版和电子病历打印…

微信小程序使用icon图标

原因: 微信小程序使用fontawesome库使用icon图标,网上有很多教程,按照网上说法制作,引入到微信小程序中,但是验证成功,只能使用部分图标,结果不尽如人意。后面使用阿里巴巴开源iconfont来使用ic…

【.NET全栈】ZedGraph图表库的介绍和应用

文章目录 一、ZedGraph介绍ZedGraph的特点ZedGraph的缺点使用注意事项 二、ZedGraph官网三、ZedGraph的应用四、ZedGraph的高端应用五、、总结 一、ZedGraph介绍 ZedGraph 是一个用于绘制图表和图形的开源.NET图表库。它提供了丰富的功能和灵活性,可以用于创建各种…

R语言数据挖掘:随机森林(1)

数据集heart_learning.csv与heart_test.csv是关于心脏病的数据集,heart_learning.csv是训练数据集,heart_test.csv是测试数据集。要求:target和target2为因变量,其他诸变量为自变量。用决策树模型对target和target2做预测&#xf…

使用Java拓展本地开源大模型的网络搜索问答能力

背景 开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的langChain框架已经集成了网络搜索的能力。但是作为一个倔强的Java程序员,还是想要用Java去实现。 注册SerpAPI Serpapi 提供了多种搜索引擎的搜索API接口。 访问 Ser…

Unity性能优化篇(十四) 其他优化细节以及UPR优化分析器

代码优化: 1. 使用AssetBundle作为资源加载方案。 而且经常一起使用的资源可以打在同一个AssetBundle包中。尽量避免同一个资源被打包进多个AB包中。压缩方式尽量使用LZ4,少用或不要用LZMA的压缩方式。如果确定后续开发不会升级Unity版本,则可…

微服务相关之Nacos

微服务相关之Nacos 一、基础概念1.什么是 Nacos2.Nacos 的关键特性包括3.Nacos 核心组件4.Nacos 业务大图 二、架构设计1.基本架构及概念 三、Nacos注册中心1.注册中心演变及其设计思想2.Nacos注册中心架构3.服务注册表结构 一、基础概念 1.什么是 Nacos Nacos /nɑ:kəʊs/ …

vlookup跨表使用

VLOOKUP(查找值,数据表,列序数,匹配条件)。打开两表格,输入查找值和表格数据格式一致,查找表格或数据范围是连续的。 跨表VLOOKUP使用步骤: 插函数单元格,输“VLOOKUP()…

AI学习-线性回归推导

线性回归 1.简单线性回归2.多元线性回归3.相关概念熟悉4.损失函数推导5.MSE损失函数 1.简单线性回归 ​ 线性回归:有监督机器学习下一种算法思想。用于预测一个或多个连续型目标变量y与数值型自变量x之间的关系,自变量x可以是连续、离散,但是目标变量y必…

【图】最小生成数—Kruskal算法

目录 1. 什么是最小生成树? 2. Kruskal算法 1. 什么是最小生成树? 最小生成树(Minimum Spanning Tree,简称 MST)是指在一个连通的无向图中,找到一个包含所有顶点的树,并且边的权值之和最小。…

简单的安全密码生成器PwGen

什么是 PwGen ? PwGen 是一个简单的 Docker Web 应用程序,旨在生成具有可自定义选项的安全密码或密码短语。用户可以选择生成具有特定标准的随机密码或由随机单词组成的密码。其他功能包括在密码中包含大写字母、数字和特殊字符的选项,或者将…

边缘计算盒子与云计算:谁更适合您的业务需求?

边缘计算盒子和云计算,这两个概念听起来可能有点复杂,但其实它们就是两种不同的数据处理方式。那谁更适合您的业务需求呢?咱们来详细说说。 边缘计算盒子,就像是个小型的数据处理中心,放在离你业务现场比较近的地方。它…

解决Flutter应用在苹果商店上架中常见的问题与挑战

引言 Flutter是一款由Google推出的跨平台移动应用开发框架,其强大的性能和流畅的用户体验使其备受开发者青睐。然而,开发一款应用只是第一步,将其成功上架到苹果商店才是实现商业目标的关键一步。本文将详细介绍如何使用Flutter将应用程序上…

【Unity每日一记】(Canvas的相机渲染模式) 如何将模型显示在UI之前

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

CSS——精灵图

CSS——精灵图 目录 CSS——精灵图什么是精灵图?导入精灵图裁剪精灵图使用精灵图方式1方式2 什么是精灵图? 精灵图(Spritesheet)是指将多个小图标、图像或动画合并到一个大图像中的技术。在网页设计和游戏开发中,精灵…

AlgorithmStar(AS机器学习与科学计算库) 实现 矩阵数据类型的计算函数汇总

AlgorithmStar 实现 矩阵 计算 AlgorithmStar 本文中将会演示通过 AS 机器学习库 实现 矩阵计算 目录 文章目录 AlgorithmStar 实现 矩阵 计算目录矩阵创建通过数组创建通过稀疏矩阵创建通过填充创建矩阵通过随机的方式创建矩阵 矩阵计算矩阵的基本运算矩阵的加法计算矩阵的减…

【Erlang】Linux(CentOS7)安装Erlang和RabbitMQ

一、系统环境 查版本对应,CentOS-7,选择Erlang 23.3.4,RabbitMQ 3.9.16 二、操作步骤 安装 Erlang repository curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash安装 Erlang package s…