🔥 本文专栏:MySQL
🌸作者主页:努力努力再努力wz



💪 今日博客励志语录 :
你不用一开始就赢过所有人,你只要每天都别输给昨天那个想摆烂的自己。
引入
在此前的学习中,我们已经掌握了数据库表的基本操作,并进一步学习了索引和事务。而在实际使用数据库时,我们经常需要对表执行各种复杂的查询操作,也就是 SELECT 查询。
SELECT 查询得到的结果集并不一定是原始表的简单子集,它可能包含连接后的字段、表达式计算出的字段,甚至是聚合函数生成的派生列。也就是说,结果集是经过查询逻辑加工后得到的一张"临时结果表"。虽然它不是真正持久化存储的数据表,但在表现形式上仍然具有表的结构:由行和列组成,并且可以继续参与查询、筛选和连接。
但是在实际业务场景中,某些查询需求往往会反复出现。比如一个复杂查询可能涉及多张表的连接、多个条件的筛选,甚至还要配合分组、聚合、排序等操作。如果每次使用这个查询结果时,都要重新手动编写一遍完整的 SQL 语句,不仅操作繁琐,而且会降低代码的可读性和维护性。
为了解决这类问题,视图 便登场了。视图的核心作用,就是将一条复杂的 SELECT 查询语句封装起来,并为它起一个名字。之后我们就可以像查询普通表一样,通过视图名来使用这条查询逻辑,从而提高 SQL 的复用性、可读性和维护性。
视图
所谓视图 ,首先需要澄清一个关键点:视图存在的意义,是为了让我们能够复用某一条复杂的 SELECT 查询逻辑。
在实际开发中,某些查询需求可能会反复出现。比如,我们需要多次引用某个 SELECT 语句得到的查询结果,并在这个结果之上继续进行筛选、连接、分组或者统计。如果没有视图,那么每次使用这个查询结果时,我们都需要手动重新编写一遍完整的 SQL 语句。随着查询逻辑变得复杂,这种方式不仅繁琐,而且会降低 SQL 的可读性和维护性。
但是这里很多初学者容易产生一个误区:认为视图就是把某个 SELECT 查询得到的临时结果集保存了一份,或者说把这个结果集持久化存储到了磁盘中。实际上,MySQL 中的普通视图并不是这样工作的。
首先,从设计上看,如果视图直接保存查询结果集本身,就会带来很多问题。因为一个复杂查询得到的结果集可能非常大,可能包含成千上万条记录,甚至更多。如果每创建一个视图都把查询结果完整拷贝一份并持久化存储,那么就会带来额外的磁盘空间消耗。更重要的是,视图依赖的底层表数据可能随时发生变化,如果视图保存的是一份结果副本,那么还需要考虑这份副本如何和原始表保持同步,否则视图中的数据就可能变成过期数据。
因此,MySQL 中普通视图的核心思想,并不是保存 SELECT 查询得到的结果数据,而是保存生成这个结果集的那条 SELECT 查询语句。也就是说,视图本质上是对一条查询语句的封装。之后我们查询视图时,MySQL 会根据视图中保存的查询定义,结合当前底层表中的最新数据,动态生成对应的查询结果。
这个思想有点类似于使用 mysqldump 进行逻辑备份。逻辑备份并不是简单地复制数据库底层的数据文件,而是把数据库中的表结构和数据内容转换成一系列 SQL 语句,例如 CREATE TABLE 和 INSERT 语句。之后只需要重新执行这些 SQL 语句,就可以动态重建出数据库。相比直接复制底层物理文件,保存 SQL 语句是一种更加轻量、更加可移植的表达方式。
视图也是类似的思想:它并不直接持久化保存临时结果集,而是保存生成这个结果集的查询语句。用户后续访问视图时,就相当于通过视图名间接执行了这条被封装好的 SELECT 语句。这样既避免了重复书写复杂 SQL,又提高了查询逻辑的复用性、可读性和维护性。
生成视图的核心语法就是:
sql
CREATE VIEW 视图名 AS
SELECT 查询语句;
也就是说,视图名本质上就是给一条 SELECT 查询语句起了一个名字。
比如现在有一张学生表:
sql
CREATE TABLE students (
id INT PRIMARY KEY,
name VARCHAR(20),
age INT,
class_id INT
);
如果我们经常需要查询年龄大于 18 岁的学生:
sql
SELECT id, name, age
FROM students
WHERE age > 18;
那么每次都写这条 SQL 就比较麻烦,所以可以把它封装成一个视图:
sql
CREATE VIEW adult_students AS
SELECT id, name, age
FROM students
WHERE age > 18;
以后查询这个视图时,就可以像查询普通表一样:
sql
SELECT * FROM adult_students;
表面上看,我们是在查 adult_students 这张"表",但实际上 MySQL 会根据视图中保存的那条 SELECT 语句去底层表 students 中动态查询数据。
所以这里可以理解为:
sql
SELECT * FROM adult_students;

大致等价于:
sql
SELECT *
FROM (
SELECT id, name, age
FROM students
WHERE age > 18
) AS adult_students;
只不过这个查询逻辑被 MySQL 保存起来了,我们不需要每次手动写完整 SQL。
如果是多表查询,也一样可以封装成视图。
比如有学生表和班级表:
sql
CREATE VIEW student_class_view AS
SELECT
s.id,
s.name,
s.age,
c.class_name
FROM students AS s
JOIN classes AS c
ON s.class_id = c.id;
之后就可以直接查询:
sql
SELECT * FROM student_class_view;
甚至还可以在视图的基础上继续筛选:
sql
SELECT *
FROM student_class_view
WHERE age > 18;
这就体现出视图的价值了:
原本复杂的连接查询被封装起来,后续只需要面向视图继续查询即可。
查看已经创建的视图:
sql
SHOW TABLES;
注意,视图也会出现在 SHOW TABLES 的结果中。
如果想查看视图的创建语句:
sql
SHOW CREATE VIEW adult_students\G
删除视图:
sql
DROP VIEW adult_students;
这里还需要补充一点:视图虽然不是一张真实存储数据的表,但它作为一种数据库对象,也需要被 MySQL 记录下来。
在 MySQL 8.0 之前,表和视图的部分元数据主要依赖 .frm 文件保存;而从 MySQL 8.0 开始,.frm 文件被移除,原本存放在 .frm 文件中的表和视图元数据被统一存储到 MySQL 的数据字典中。
也就是说,视图保存的并不是查询结果集本身,而是视图这个数据库对象的元数据,例如视图名、所属数据库、列信息,以及最核心的 SELECT 查询定义。用户后续访问视图时,MySQL 会根据数据字典中保存的视图定义,再结合底层表中的当前数据动态生成结果集。
因此,视图保存的是"如何查询出这张虚拟表"的规则,而不是虚拟表当前对应的数据副本。
结语
那么这就是本篇文章的全部内容,下一期我会更新MySQL的用户管理,我会持续更新,希望你能够多多关注,如果本文有帮助到你的话,还请三连加关注,你的支持就是我创作的最大动力!感谢各位大佬对我的支持!
