MySQL 视图:把复杂查询封装成表,并且还能控权限、做解耦

MySQL 视图一次讲透:把复杂查询"封装成表",并且还能控权限、做解耦

视图(View)最像什么?最像"把一段 SELECT 查询起个名字",然后像查表一样去查它。这样做的好处是:复杂 SQL 变简单、敏感字段能隐藏、底层表结构变化时应用更不容易被波及。


1)先把目标对齐:用视图解决什么问题

来看这三件事:

  • 明确视图的使用场景
  • 掌握视图的创建与使用
  • 理解视图在简化、权限与解耦中的价值

一个最直观的落地动作是:先把复杂查询写出来,再把它封装进视图里,后续直接 SELECT * FROM 视图名;

sql 复制代码
SELECT * FROM v_student_total_points;

2)视图是什么:虚拟表,不存数据,只存"查询逻辑"

来看定义:视图是一个虚拟表 ,基于一个或多个基本表(或其他视图)的查询结果集。视图本身不存储数据,每次访问时通过执行定义它的查询来动态生成结果;物理上依赖基础表数据,占用的不是"数据空间",而是"逻辑表示"。并且视图可以像普通表一样用于查询、更新和管理(但更新有条件限制,后面会列出哪些视图不可更新)。

来看这段代码:视图就是把 SELECT "命名"。

sql 复制代码
CREATE VIEW v_only_name AS
SELECT id, name FROM student;

3)创建视图:两种常用写法(别名重命名 / 显式列名列表)

3.1 基本语法长这样

sql 复制代码
CREATE VIEW view_name [(column_list)] AS select_statement;

语法是骨架,但关键在于:列名到底由谁决定 ------由 SELECT 里的别名决定,或者由 column_list 显式指定。

3.2 写法一:在 SELECT 里用别名,直接把结果列改成想要的名字

来看这段代码:把班级、课程、成绩相关列统一改成更清晰的列名。

sql 复制代码
CREATE VIEW v_student_socre AS
SELECT
  s.id, s.name, s.sno, s.age, s.gender, s.enroll_date,
  c.id  AS class_id,  c.name  AS class_name,
  co.id AS course_id, co.name AS course_name,
  sc.id AS score_id,  sc.score
FROM student s, class c, course co, score sc
WHERE s.class_id = c.id
  AND sc.student_id = s.id
  AND sc.course_id  = co.id
ORDER BY s.id;

3.3 写法二:在 VIEW 名后显式指定结果列名(column_list)

来看这段代码:不依赖 SELECT 的默认列名或别名,直接规定视图输出列名。

sql 复制代码
CREATE VIEW v_student_socre_v1
(id, name, sno, age, gender, enroll_date,
 class_id, class_name,
 course_id, course_name,
 score_id, score) AS
SELECT
  s.id, s.name, s.sno, s.age, s.gender, s.enroll_date,
  c.id, c.name,
  co.id, co.name,
  sc.id, sc.score
FROM student s, class c, course co, score sc
WHERE s.class_id = c.id
  AND sc.student_id = s.id
  AND sc.course_id  = co.id;

4)使用视图:复杂多表查询"降维成一张表"

4.1 先看不用视图时,多表连接查询会写得很长

来看这段代码:一次性查"用户信息 + 班级 + 课程 + 成绩",SQL 很容易越写越长。

sql 复制代码
SELECT
  s.id, s.name, s.sno, s.age, s.gender, s.enroll_date,
  c.id, c.name,
  co.id, co.name,
  sc.id, sc.score
FROM student s, class c, course co, score sc
WHERE s.class_id = c.id
  AND sc.student_id = s.id
  AND sc.course_id  = co.id
ORDER BY s.id;

4.2 再看用视图:查询直接变成"一句查表"

视图建好后,复杂 join 细节被封装起来,后续查询只剩:

sql 复制代码
SELECT * FROM v_student_socre;
SELECT * FROM v_student_socre_v1;

5)视图的典型用途:隐藏字段,只暴露"允许被看到的结果"

一个很经典的需求:只想展示学生姓名与总分,学号和各科明细不想暴露。

5.1 不用视图时:查询列表里随时能被"顺手加字段"

来看这段代码:本来只查 name + total,但完全可以临时加上 sno 等字段。

sql 复制代码
SELECT s.name, SUM(sc.score) AS total
FROM student s, score sc
WHERE s.id = sc.student_id
GROUP BY sc.student_id
ORDER BY s.id;

5.2 用视图封装后:查询者只能看到视图定义暴露的列

来看这段代码:把"姓名 + 总分"固定成一个视图,后续 SELECT * 就只会返回这两类信息(以及视图定义中选出的列)。

sql 复制代码
CREATE VIEW v_student_total_points AS
SELECT s.id, s.name, SUM(sc.score) AS total
FROM student s, score sc
WHERE s.id = sc.student_id
GROUP BY s.id
ORDER BY s.id;

SELECT * FROM v_student_total_points;

这种写法的效果是:视图变成"固定接口",字段能被统一控制。


6)视图也能参与联表:视图 + 真实表一起 join

视图并不是孤岛,它可以像表一样参与 join。

来看这段代码:把"总分视图"再与 student 表联表,用于补充其它信息或做二次过滤。

sql 复制代码
SELECT *
FROM v_student_total_points v, student s
WHERE v.id = s.id;

7)修改数据:改表会影响视图,改视图也可能影响表(但有坑)

7.1 通过真实表修改数据:视图结果会同步变化

来看这段代码:直接更新 score 表里某人的某科成绩,再查视图会发现对应行已经更新。

sql 复制代码
UPDATE score
SET score = 99
WHERE student_id = 1 AND course_id = 1;

SELECT * FROM v_student_socre;

7.2 通过视图修改数据:可能成功,也可能失败(取决于视图是否可更新)

来看这段代码:对带 ORDER BY 定义的视图直接 UPDATE,会报错(典型报错:Incorrect usage of UPDATE and ORDER BY)。

sql 复制代码
UPDATE v_student_socre
SET score = 99
WHERE score_id = 3;   -- 可能失败:视图定义里含 ORDER BY

换成不含 ORDER BY 的版本,就可以更新,并且基表会被真正改动:

sql 复制代码
UPDATE v_student_socre_v1
SET score = 99
WHERE score_id = 3;

SELECT * FROM score WHERE student_id = 1 AND course_id = 5;

结论很硬:视图不是只读的代名词,但可更新视图必须满足条件


8)注意事项:哪些视图"不可更新"(必须背熟的清单)

先记一句总原则:修改真实表会影响视图,修改视图也会影响真实表。

再看不可更新视图的典型特征(满足其一就要警惕):

  • 视图定义里使用聚合函数
  • 使用 DISTINCT
  • 使用 GROUP BY / HAVING
  • 使用 UNION / UNION ALL
  • 查询列表中包含子查询
  • FROM 子句引用了不可更新视图

来看这段代码:带聚合(GROUP BY)的视图通常不可更新,用 UPDATE 会直接走不通。

sql 复制代码
CREATE VIEW v_class_cnt AS
SELECT class_id, COUNT(*) AS cnt
FROM student
GROUP BY class_id;

UPDATE v_class_cnt SET cnt = 100 WHERE class_id = 1;  -- 通常不可更新

9)删除视图:一条 DROP VIEW 搞定

来看这段代码:

sql 复制代码
DROP VIEW v_student_socre;

10)视图的优点:把复杂度、权限与变化"隔离开"

10.1 简单性:复杂查询封装成简单接口

来看这段代码:多表 join 写一次,封装成视图,后续调用变简单。

sql 复制代码
CREATE VIEW v_student_socre_v1 AS
SELECT s.id, s.name, c.name AS class_name, co.name AS course_name, sc.score
FROM student s, class c, course co, score sc
WHERE s.class_id=c.id AND sc.student_id=s.id AND sc.course_id=co.id;

10.2 安全性:隐藏敏感字段,只暴露必要列

来看这段代码:用户表里隐藏 password,仅暴露公共信息。

sql 复制代码
CREATE VIEW v_user_public AS
SELECT id, username, email FROM user;

10.3 逻辑数据独立性:底层表变了,优先改视图定义,应用少改

核心意思是"应用与数据库解耦":让应用依赖视图这个稳定接口,而不是依赖底表的细节。

来看这段代码:应用侧仍查 v_user_public,底表字段变化时主要调整视图定义。

sql 复制代码
SELECT * FROM v_user_public;

10.4 重命名列:提升可读性

来看这段代码:列重命名后,返回结果更像业务语言而不是数据库语言。

sql 复制代码
CREATE VIEW v_student_total AS
SELECT s.name AS 姓名, SUM(sc.score) AS 总分
FROM student s JOIN score sc ON s.id = sc.student_id
GROUP BY s.id;

总结

一句话收束:视图是"把查询变成可复用、可控、可解耦的接口"。写一次复杂 SQL,后续就像查表一样调用;同时还能天然做字段裁剪与安全隔离。

相关推荐
l1t2 小时前
postgresql 18版bytea 类型转换的改进
数据库·postgresql
小蒜学长2 小时前
python餐厅点餐系统(代码+数据库+LW)
数据库·spring boot·后端·python
岳麓丹枫0012 小时前
PostgreSQL 中 create database 中的注意事项
数据库·postgresql
梦想画家2 小时前
告别关键词!PostgreSQL+pgvector 玩转语义和图像检索
数据库·postgresql
爱潜水的小L2 小时前
自学嵌入式day41,数据库
jvm·数据库
橙汁味的风2 小时前
《数据库系统概论》陈红、卢卫 - 9 - 关系数据库存储管理
数据库·数据库系统概论
最贪吃的虎2 小时前
Redis 除了缓存,还能干什么?
java·数据库·redis·后端·缓存
u0131635512 小时前
Oracle 报错:PLS-00201: 必须声明标识符‘DBMS_LOCK‘的解决方法
数据库·oracle
Awkwardx3 小时前
MySQL数据库—MySQL数据类型
数据库·mysql