MySQL:视图【详解】
1、视图
1.1 视图的定义
视图是在数据库中定义的虚拟表。它是一个基于一个或多个实际表的查询结果集,可以像实际表一样被查询和操作。视图可以看作是一个动态生成的数据表,其内容是从其他表中选择、过滤和计算得到的。
视图通过使用SQL查询语句来定义,这些查询语句可以包括与一个或多个表的连接、条件过滤、列计算、聚合函数等操作。在视图定义中,我们可以指定要在视图中包含的列和行,以及对这些列进行何种计算和处理。
1.2 为什么要使用视图
视图的使用有两大因素:
1.保护敏感数据:当我们使用 select+表名 的形式执行查询操作时,查询出的数据是不受约束的,当数据中含有用户的隐私数据如:密码、地址、身份证号码时,那么使用 select+表名 的形式,这些隐私数据将暴露无遗,而视图可以有效防止隐私的暴露,隐藏关键字段,避免别有用心之人的利用。
2.降低开发成本:当开发人员需要联合多张表查询时,那么这时的查询语句将会非常的复杂,而每次查询都需要重新编写这些SQL语句,这将非常影响工作效率,那么这时数据库开发人员就可以将这些SQL语句抽象到视图中,开发人员查询时就可以通过视图进行查询。
- 例如:查询用户的所有信息和考试成绩(四表联合查询)
-- 查询用户的所有信息和考试成绩(四表联合查询)
SELECT
st.student_id, st.`name`,
cl.class_id, cl.`name`,
co.course_id, co.name,
sc.score
FROM
student st, class cl, score sc, course co
WHERE
st.class_id = cl.class_id AND
sc.student_id = st.student_id AND
co.course_id = sc.course_id
ORDER BY
st.student_id;
每次查询时都需要编写这样复杂的SQL语句,这是非常影响效率的,并且可以任意指定列查看隐私数据,此时我们就可以将这些语句抽象到视图中,既提高了开发效率,又保护了用户隐私。
2、 视图的创建
上文已经讲到,视图的使用可以降低开发成本,我们只需创建视图,将相关SQL语句抽象到视图中,通过视图来查询即可。
2.1 不指定列名的创建
创建视图时,我们可以不指定视图中的列名,仅仅通过AS后SQL语句的结果集中的列名来当做视图中的列。
而当我们通过视图查询时,结果如下:
为什么会出现列名重复的错误提示呢?
—— 这是由于我们在定义视图时没有指定列名,这时视图中的列是由结果集中的列决定的,而结果集中的列出现了多个重复的列‘name’,所以出现了列名重复的报错信息。
此时,我们只需将结果集中的列起上不同的别名,让这些列名不重复即可:
-- 定义视图
-- 不指定列名的视图的定义
CREATE VIEW v_student_score AS (
SELECT
st.student_id,
st.`name` student_name, #结果集中列名重复时要起别名
cl.class_id,
cl.`name` class_name,
co.course_id,
co.`name` course_name,
sc.score
FROM
student st, class cl, score sc, course co
WHERE
st.class_id = cl.class_id AND
sc.student_id = st.student_id AND
co.course_id = sc.course_id
ORDER BY
st.student_id
);
创建成功后,我们可以使用 show tables; #查看视图
我们也可以通过,show create view 视图名; #查看创建视图的SQL语句
-- 查看试图
SHOW TABLES;
-- 查看创建视图的SQL语句
SHOW CREATE VIEW v_student_score;
2.2 指定列名的创建
我们也可以在创建视图时指定列名,此时有一个好处:指定列名之后,视图会根据指定的列名创建,查询结果集中是否有重复的列名并不影响。
也就是说,此时我们不需要关注结果集中是否有重复的列名,也不需要给重复的列名起别名。
例如:(注意,本示例创建的视图并未使用order by子句)
-- 定义视图
-- 指定列名的视图的定义
CREATE VIEW v_student_score_v1 (
id, name, class_id, class_name, course_id, course_name, score
) AS (
SELECT
st.student_id,
st.`name`,
cl.class_id,
cl.`name`,
co.course_id,
co.`name`, #不需要给结果集中重复的列起别名
sc.score
FROM
student st, class cl, score sc, course co
WHERE
st.class_id = cl.class_id AND
sc.student_id = st.student_id AND
co.course_id = sc.course_id
);
注意:上述的两种创建方式都是可以的,推荐使用第二种,即指定列名的创建
3、 视图的使用
我们可以通过封装的视图来进行查询操作:
注意,上文已经提到 视图v_student_v1 是未进行排序的视图,在查询时,我们可以给视图中的结果集指定排序规则:
-- 使用视图
SELECT * FROM v_student_score_v1 ;
-- 使用视图 --> 对score进行升序排序
SELECT * FROM v_student_score_v1 ORDER BY score ;
4、 视图与基础表的相互影响
接下来让我们一起探索视图与基础表间数据修改之间的关系。
4.1 更新基础表数据
我们先来探索在基础表中修改的数据是否能影响到视图。
先上总结:在基本表中修改的数据会影响到视图中。
接下来让我们一步一步验证:
第一步:我们现在先来修改基础表中的数据:
第二步:使用视图查询,查看修改的数据是否影响到视图:
综上:在基本表中修改的数据会影响到视图中。
-- 查看修改前的基础表
SELECT * FROM score WHERE student_id = 1 AND course_id = 1;
-- 更新基础表数据
-- 将id = 1 的学生 course_id = 1 的课程成绩修改为100
UPDATE
score
SET score = 100
WHERE
student_id = 1 AND
course_id = 1;
-- 查看修改后的基础表的成绩
SELECT * FROM score WHERE student_id = 1 AND course_id = 1;
-- 查看视图中的数据是否收到影响
select * FROM v_student_score_v1;
4.2 更新视图数据
4.2.1 视图是否可进行更新操作
我们先来更新 视图v_student_score,即上文创建时没有指定列名的但指定了order by排序规则的视图。
第一步:我们在v_student_score视图中将刚才修改为100的成绩修改为80,:
出现了上图的报错,提示我们:视图中如果使用了group by子句,则视图不能进行更新update操作。
OK,我们还有一个没有使用order by的视图,即:v_student_score_v1,我们继续在这个视图中进行update操作。
第二步:我们继续在v_student_score_v1视图中进行update操作。
注意:若视图指定了列名,则修改数据时,一定要使用视图中指定的列名,因为视图创建后,与基础表间已实现断藕。
发现,在该视图中成功更新了数据。
故综上:当在视图中使用order by 时,不允许在视图中进行更新update操作。
但在视图中,不仅仅是只有使用了order by语句才不允许进行更新操作,
使用了以下SQL,不允许在视图中进行update操作:
- 使用聚合函数
- 使用distinct
- 使用order by
- 使用group by以及having子句
- 使用union或union all
- 查询列表时,使用子查询
- 在from子句中引用不可更新的视图
4.2.2 视图更新对基础表的影响
同样,先上结论:更新视图中的数据同样会影响到基础表。
通过进行视图和基础表间数据更新的实验,得出以下结论:
- 不论更新了视图还是基础表,都会相互影响,查询处来的数据都是最新结果。
- 即:修改基础表会影响视图,修改视图也会影响基础表。
-- 更新视图
UPDATE v_student_score_v1 SET score = 80 WHERE id = 1 AND course_id = 1;
-- 查看基础表数据 --> 发现基础表受影响
SELECT * FROM score WHERE student_id = 1 and course_id = 1;
5、视图的删除
语法:drop view 视图名;
6、视图的优点
-
简单性:不用编写复杂的SQL,只通过使用公开对外提供的视图来查询数据。
-
安全性:通过视图,可以隐藏表中的敏感数据。
-
逻辑数据独立性:视图可以指定列名,所以不论底层表结构如何变化,底层列名如何变化,只需修改视图的定义即可,视图指定的列名仍然为原来的名称,这样实现了应用程序与数据库的解耦,不管应用程序改了什么内容,只认数据库的视图,不用"牵一发而动全身"。
-
重命名列:视图允许用户重命名列名,以增强数据可读性。
END