MySQL 视图管理

从零开始:创建、查看、修改、删除、更新 —— 视图的"五项全能"

🎯 学习目标
  • 能够使用 CREATE VIEW 在单表/多表/视图上创建视图
  • 能够使用 DESCRIBE 和 SHOW CREATE VIEW 查看视图
  • 能够使用 ALTER VIEW 修改、DROP VIEW 删除视图
  • 理解通过视图更新基本表数据的机制与限制

🎬 上一节我们理解了视图是什么,这一节就要 真正动手 了。视图的管理就像普通表一样,离不开这五件事:创建、查看、修改、删除、更新

本节所有例子都基于学生选课系统的三张表:s(学生)、c(课程)、sc(选课)。请先按下面的"准备工作"在自己的 MySQL 里把环境搭起来,然后跟着每个例子动手敲一遍 —— 视图这个东西,看十遍不如自己写一遍。

📦 准备工作:搭建练习环境

请打开你的 MySQL 客户端(命令行 / Workbench / Navicat 都可以),把下面整段代码复制粘贴 进去执行一次。这会创建一个名为 school 的数据库,里面有 3 张表和测试数据。本节后面所有例子都基于这套数据。

SQL · 完整可执行
-- ① 创建并使用数据库
CREATE DATABASE IF NOT EXISTS school CHARSET=utf8mb4;
USE school;

-- ② 学生表 s
CREATE TABLE s (
  sno  VARCHAR(10) PRIMARY KEY,    -- 学号
  sn   VARCHAR(20) NOT NULL,       -- 姓名
  sex  CHAR(2),                  -- 性别
  age  INT,                      -- 年龄
  maj  VARCHAR(20),              -- 专业
  dept VARCHAR(20)               -- 学院
);

-- ③ 课程表 c
CREATE TABLE c (
  cno VARCHAR(10) PRIMARY KEY,     -- 课程号
  cn  VARCHAR(40) NOT NULL,        -- 课程名
  ct  INT                          -- 学时
);

-- ④ 选课表 sc
CREATE TABLE sc (
  sno   VARCHAR(10),
  cno   VARCHAR(10),
  score INT,
  PRIMARY KEY(sno, cno)
);

-- ⑤ 插入测试数据
INSERT INTO s VALUES
('s01','张三','男',20,'计算机','信息学院'),
('s02','李四','女',19,'软件工程','信息学院'),
('s03','王五','男',21,'物理','理学院'),
('s04','赵六','女',20,'数学','理学院'),
('s05','钱七','男',22,'计算机','信息学院');

INSERT INTO c VALUES
('c01','数据库原理',64),
('c02','操作系统',48),
('c03','高等数学',80);

INSERT INTO sc VALUES
('s01','c01',85),
('s01','c02',76),
('s02','c01',92),
('s03','c03',88),
('s04','c03',79);

-- ⑥ 验证一下
SELECT * FROM s;
📊 验证 SELECT * FROM s 的结果
snosnsexagemajdept
s01张三20计算机信息学院
s02李四19软件工程信息学院
s03王五21物理理学院
s04赵六20数学理学院
s05钱七22计算机信息学院
💡 提示

看到 5 行学生数据就说明环境搭好了。如果某一步报错,最常见的原因是 没有先 USE school 或者 表名已存在(之前敲过一次了)—— 此时可以先 DROP DATABASE school; 然后重来。

① 创建视图:CREATE VIEW

完整语法

语法格式
CREATE [OR REPLACE] [ALGORITHM={UNDEFINED | MERGE | TEMPTABLE}]
  [DEFINER = {user | CURRENT_USER}]
  VIEW 视图名 [(视图字段列表)]
  AS 查询语句
  [WITH [CASCADED | LOCAL] CHECK OPTION];
💬 语法读法(先记住核心,可选项后看)

方括号 [] 里的都是可选的。把可选项全删掉,最简形式就这一行:

CREATE VIEW 视图名 AS 查询语句;

翻译过来就是:"创建一个叫 XX 的视图,它的内容就是后面这条 SELECT 查出来的东西。"

语法关键字详解

关键字作用说明
OR REPLACE如果数据库中已有同名视图,直接替换,否则报错。
ALGORITHM视图的执行算法。UNDEFINED(默认)让 MySQL 自己决定;MERGE 把视图查询和外层查询合并执行(效率高);TEMPTABLE 把视图结果先存进临时表(无法做更新)。
DEFINER指定视图的"创建者/所有者",影响权限检查。多数场景用默认值即可。
视图字段列表给视图的列起新名字(如 (sno, sname))。省略时,沿用查询语句中字段的原名。
AS 查询语句视图的核心 —— 一条完整的 SELECT 语句,决定了视图能查出什么数据。
WITH CHECK OPTION当通过视图执行 INSERT/UPDATE 时,只允许满足视图 WHERE 条件 的数据通过;不满足的会被拒绝。

三种典型创建方式

需求:创建"信息学院学生"视图 s_view

把"WHERE dept='信息学院'"封装起来,以后查信息学院的学生只需 SELECT * FROM s_view

SQL · 单表视图
CREATE VIEW s_view
AS SELECT * FROM s
   WHERE dept = '信息学院';

-- 创建后立刻验证:像查表一样查视图
SELECT * FROM s_view;
📊 SELECT * FROM s_view 的结果
snosnsexagemajdept
s01张三20计算机信息学院
s02李四19软件工程信息学院
s05钱七22计算机信息学院
💡 读法提醒

AS SELECT * 中的 AS"作为" 的意思 —— "把这个视图作为这个 SELECT 查询的结果"。它不是别名,是引出视图定义的关键字。

需求:创建"学生选课情况"视图 s_sc_c_view

把学生姓名(s 表)+ 课程名(c 表)+ 成绩(sc 表)拼起来,封装成一个视图,以后看选课情况一句话搞定。

SQL · 多表视图(含字段重命名)
CREATE VIEW s_sc_c_view(sno, sname, cname, score)
AS SELECT s.sno, s.sn, c.cn, sc.score
    FROM s, c, sc
    WHERE s.sno = sc.sno
      AND sc.cno = c.cno;

SELECT * FROM s_sc_c_view;
📊 SELECT * FROM s_sc_c_view 的结果
snosnamecnamescore
s01张三数据库原理85
s01张三操作系统76
s02李四数据库原理92
s03王五高等数学88
s04赵六高等数学79
💬 重点:视图字段列表的作用

视图名后面的 (sno, sname, cname, score)视图字段列表

原表中字段叫 sn(不直观),但通过视图字段列表,我们对外展示的列名是更易读的 sname位置一一对应 —— 第 1 个字段名对应 SELECT 中第 1 列,以此类推。

如果省略这个列表,视图列名就用 SELECT 后面的原字段名 sno, sn, cn, score

⚠️ 多表 JOIN 的写法

例子里用的是 逗号分隔多表 + WHERE 写连接条件 的旧写法(教材风格)。在实际项目中更推荐用 JOIN ... ON 显式连接,可读性更好:

FROM s JOIN sc ON s.sno = sc.sno JOIN c ON sc.cno = c.cno

两种写法效果一样,但 JOIN 写法把"连接"和"过滤"分得更清楚。

需求:在 s_view 基础上,再筛出"计算机专业"的学生

视图本身就可以当成一张"表"来用,所以也可以基于已有视图再创建视图 —— 这就是"视图的视图"。

SQL · 基于视图创建视图
-- 前提:已有 s_view(信息学院学生)
CREATE VIEW s_maj_view
AS SELECT * FROM s_view
   WHERE maj = '计算机';

SELECT * FROM s_maj_view;
📊 SELECT * FROM s_maj_view 的结果
snosnsexagemajdept
s01张三20计算机信息学院
s05钱七22计算机信息学院
💡 思维提升:视图层叠

这相当于给筛选条件叠加了两层:第一层(s_view)筛"信息学院",第二层(s_maj_view)筛"计算机专业"。这种 分层封装 的思路在工程上很有用 —— 把复杂的筛选拆成多个简单视图,每一层都好理解、好维护。

创建视图的注意事项

❌ 易错点
  • SELECT 中 不能包含系统变量、用户变量,也不能包含处理语句的参数。
  • FROM 子句中 不能包含子查询(即不能 FROM (SELECT...))。
  • 视图依赖的基本表如果被删除,视图就废了 —— 用 CHECK TABLE 表名 检查表的状态。
  • 不能为 临时表(TEMPORARY TABLE)创建视图。
  • ALGORITHM 参数会影响部分查询结果(特别是 TEMPTABLE 模式下,视图变成只读)。
📌 补充:用 MySQL Workbench 图形化创建视图

步骤:

  1. 在 Navigator 窗格的 Schemas 选项卡下,找到目标数据库;
  2. 右键点击该数据库下的 Views 节点,选择 Create View...
  3. 在右侧打开的 DDL 文本框里输入完整的 CREATE VIEW ... 语句;
  4. Workbench 会根据你的 SQL 自动调整 Name 文本框的内容;
  5. 检查无误后,点击 Apply 按钮提交。

本质上,Workbench 也只是把你的 SQL 发给 MySQL 服务器执行 —— 所以学好 SQL 永远是第一位的。

② 查看视图的定义

创建完视图,怎么知道它长什么样?MySQL 提供了两条命令:

方式 1:DESCRIBE 视图名 —— 看"结构"

SQL
DESCRIBE s_view;
-- 也可以缩写为 DESC s_view;
📊 输出(视图的字段结构)
FieldTypeNullKeyDefault
snovarchar(10)NONULL
snvarchar(20)NONULL
sexchar(2)YESNULL
ageintYESNULL
majvarchar(20)YESNULL
deptvarchar(20)YESNULL
💬 解读

DESCRIBE 输出的是"字段层面"的结构信息:每一列叫什么、是什么类型、能不能为空。它把视图当成一张普通表来描述 —— 因为对使用者来说,视图就是表。

方式 2:SHOW CREATE VIEW —— 看"原始定义"

SQL
SHOW CREATE VIEW s_view;
📊 输出(视图的创建语句)
ViewCreate View(节选)
s_viewCREATE ALGORITHM=UNDEFINED ... VIEW `s_view` AS SELECT ... FROM `s` WHERE `dept`='信息学院'
💡 两条命令的区别

DESCRIBE:看视图"有哪些字段" → 关心使用层面。

SHOW CREATE VIEW:看视图"是怎么定义的" → 关心实现层面,能看到完整 SQL。

③ 修改视图的定义

视图建好后想改?有两种办法:ALTER VIEWCREATE OR REPLACE VIEW

方式 1:ALTER VIEW(推荐)

语法
ALTER [ALGORITHM={UNDEFINED | MERGE | TEMPTABLE}]
  [DEFINER = {user | CURRENT_USER}]
  VIEW 视图名 [(视图字段列表)]
  AS 查询语句
  [WITH [CASCADED | LOCAL] CHECK OPTION];

例子:把 s_view 改成显示"理学院"的学生,并把字段重命名

SQL
ALTER VIEW s_view(s_id, s_name, s_maj)
AS SELECT sno, sn, maj
   FROM s
   WHERE dept = '理学院';

SELECT * FROM s_view;
📊 修改后查询结果(注意列名变了)
s_ids_names_maj
s03王五物理
s04赵六数学

方式 2:CREATE OR REPLACE VIEW

用上一节学过的 OR REPLACE:写一遍 CREATE VIEW,加上 OR REPLACE 关键字,有同名视图就替换,没有就新建

SQL
CREATE OR REPLACE VIEW s_view
AS SELECT sno, sn, age
   FROM s
   WHERE dept = '信息学院';
❌ 使用 CREATE OR REPLACE 时的大坑

务必 仔细核对视图名! 如果你 把名字打错了(例如想改 s_view 却写成了 s_veiw),它会:

① 找不到 s_veiw,所以不会"替换";
② 然后按你给的"错误名字"新建一个视图

结果是:原来的 s_view 没动,多了个错误名字的 s_veiwALTER VIEW 没有这个风险 —— 名字不存在直接报错,更安全。

④ 删除视图:DROP VIEW

语法

语法
DROP VIEW [IF EXISTS] 视图名1 [, 视图名2, ...] [RESTRICT | CASCADED];
💬 语法读法

IF EXISTS:如果视图存在才删除。强烈建议加上,因为删除一个不存在的视图会报错,加上这个就只是给个警告,脚本不中断。

视图名后面可以 用逗号写多个,一次删除多个视图。

例子:删除视图

SQL
-- 删除单个视图
DROP VIEW s_view;

-- 安全删除(不存在也不报错)
DROP VIEW IF EXISTS s_view;

-- 一次删除多个
DROP VIEW IF EXISTS s_view, s_maj_view, s_sc_c_view;
💡 安全建议

删除是不可逆操作。在生产环境删视图前,先用 SHOW CREATE VIEW 视图名 把定义打印出来保存好 —— 万一删错了还能照着原样重建。

⑤ 通过视图更新数据

📖 核心概念

视图本身不存数据,所以 对视图的 INSERT/UPDATE/DELETE 操作,最终会被转换为对基本表的相应操作

💬 人话翻译

你以为你在改视图,其实改的是视图背后的基本表。视图只是"中转站"。所以视图改完,去查基本表,会发现基本表的数据真的变了。

建立一个可更新的视图

SQL
-- 这个视图等于把整张 s 表搬过来,最简单的"可更新视图"
CREATE VIEW s_update_view
AS SELECT * FROM s;

三种 DML 操作演示

通过视图插入一条新学生

SQL
INSERT INTO s_update_view
VALUES('s10', '韩义', '男', 19, '计算机', '信息学院');

-- 验证:去基本表 s 中查 s10
SELECT * FROM s WHERE sno = 's10';
📊 验证结果(基本表 s 也有了 s10)
snosnsexagemajdept
s10韩义19计算机信息学院

通过视图修改 s10 的年龄

SQL
UPDATE s_update_view
SET age = 20
WHERE sno = 's10';

SELECT sno, sn, age FROM s WHERE sno = 's10';
📊 验证结果(age 由 19 改为 20)
snosnage
s10韩义20

通过视图删除 s10

SQL
DELETE FROM s_update_view
WHERE sno = 's10';

SELECT * FROM s WHERE sno = 's10';
📊 验证结果

Empty set (0 行)—— s10 已经从基本表 s 中真正删除了

哪些视图 不能 被更新?

⚠️ 视图更新的 7 大限制

视图本质是"虚拟"的,有些情况下 MySQL 没办法把"对视图的修改"翻译回"对基本表的修改",这时视图就 变成只读的。下列任意一种情况,视图都不可更新:

  1. 视图字段或查询语句中包含 聚合函数(SUM、COUNT、AVG、MAX、MIN);
  2. 视图字段是通过 表达式或计算 得到的(如 price * 0.8 AS discount);
  3. 视图定义中包含 DISTINCTGROUP BYORDER BYHAVING 等子句;
  4. 查询语句中使用了集合运算 UNIONUNION ALL
  5. 视图的列来自子查询的列;
  6. 创建视图时指定了 ALGORITHM = TEMPTABLE(MySQL 把视图结果先存进临时表,无法反向更新);
  7. 视图依赖的"上游视图"本身就是不可更新的。
💬 怎么记?一句话总结

能让"视图的一行"明确对应到"基本表的一行" 的视图才能更新。一旦做了聚合、去重、计算、合并 —— 一行视图数据可能由多行基本表数据"加工"出来 —— MySQL 就没法反推回去,视图就只能读不能写。

❌ 多表视图的更新更复杂

即便视图本身满足上述限制都不触犯,多张基本表 JOIN 出来的视图,更新也常常受限:单条 INSERT 可能要往两张表里写数据,MySQL 不会自动帮你拆。所以工程实践中:用于更新的视图通常基于单表。 多表视图主要用于查询。

📋 本节小结

📋 五项操作速查
  • 创建CREATE [OR REPLACE] VIEW 名 AS SELECT... —— 单表/多表/视图层叠都支持。
  • 查看DESCRIBE 名 看结构;SHOW CREATE VIEW 名 看完整定义。
  • 修改ALTER VIEW 名 AS SELECT...(推荐);或 CREATE OR REPLACE VIEW(注意名字别打错)。
  • 删除DROP VIEW [IF EXISTS] 名1, 名2, ...
  • 更新数据:INSERT/UPDATE/DELETE 都可,本质是改基本表;但聚合、计算、DISTINCT、GROUP BY 等会让视图变只读。
💡 学完后你应该能
  • 看到一段需求("查 XX 学院的学生"),能立刻写出对应的 CREATE VIEW;
  • 给定一个视图,知道它能不能被 INSERT/UPDATE/DELETE;
  • 写脚本删视图前,会主动加上 IF EXISTS

✏️ 综合练习

以下题目都基于本节的 s、c、sc 三张表。建议先在自己脑子里写答案,再点选项验证。

第 1 题 · 语法理解

下列创建视图的语句中,语法错误 的是?

A. CREATE VIEW v AS SELECT * FROM s;
B. CREATE OR REPLACE VIEW v AS SELECT sno FROM s;
C. CREATE VIEW v SELECT * FROM s;
D. CREATE VIEW v(id, name) AS SELECT sno, sn FROM s;

✅ 正确答案:C

C 选项 缺少 AS 关键字AS 是连接"视图名"和"查询语句"的必需关键字,不能省略。其他三项语法都正确:A 是最简形式;B 用了 OR REPLACE;D 用了视图字段列表。

第 2 题 · 视图字段列表

执行 CREATE VIEW v(a, b, c) AS SELECT sno, sn, dept FROM s; 后,下列哪条语句能正确查出数据?

A. SELECT sno, sn, dept FROM v;
B. SELECT a, b, c FROM v;
C. SELECT * FROM v WHERE sno = 's01';
D. 三个都不对

✅ 正确答案:B

视图字段列表 (a, b, c) 给视图的列重命名了 —— 对外只能看到 a、b、c 这三个列名。原表的 sno、sn、dept 在视图层面已经"不存在"了,所以 A 和 C 都会报错。SELECT * 没问题,但 WHERE sno = ... 中的 sno 在视图里已经叫 a 了,所以 C 错。

第 3 题 · 视图修改

想把已有视图 s_view 改成查询"理学院"的学生,最 安全 的语句是?

A. CREATE VIEW s_view AS SELECT * FROM s WHERE dept='理学院';
B. ALTER VIEW s_view AS SELECT * FROM s WHERE dept='理学院';
C. UPDATE s_view SET dept='理学院';
D. CREATE OR REPLACE VIEW s_veiw AS SELECT * FROM s WHERE dept='理学院';

✅ 正确答案:B

A 错,s_view 已存在,CREATE 会报错;C 错,UPDATE 是改数据不是改视图定义;D 错,名字打错了(s_veiw),结果会新建一个错的视图。ALTER VIEW 是最安全的:名字不存在直接报错提醒你,名字正确就稳稳替换定义。

第 4 题 · 视图可更新性

下列哪个视图 不能 通过 INSERT/UPDATE/DELETE 更新数据?

A. CREATE VIEW v AS SELECT * FROM s;
B. CREATE VIEW v AS SELECT sno, sn FROM s WHERE age > 18;
C. CREATE VIEW v AS SELECT dept, COUNT(*) FROM s GROUP BY dept;
D. CREATE VIEW v AS SELECT sno, age FROM s ORDER BY sno; (注意:MySQL 中 ORDER BY 在视图里不会真的影响可更新性,但下面的 GROUP BY 会)

✅ 正确答案:C

C 同时使用了 聚合函数 COUNT(*)GROUP BY 子句,违反了视图更新的两条限制。视图里"信息学院 3 个人"这一行是基本表里 3 行数据汇总出来的,MySQL 没法把"修改这一行"翻译回"修改基本表的哪 3 行",所以视图只读。A、B 是简单的单表选取,可更新;D 仅有 ORDER BY,不影响数据的"行对应"关系。

第 5 题 · 综合应用

想要创建一个名为 v_score 的视图,显示每个学生的"姓名 + 课程名 + 成绩",并允许在视图已存在时直接替换。下列 SQL 中 正确且最佳 的是?

A. CREATE VIEW v_score AS SELECT s.sn, c.cn, sc.score FROM s, c, sc;
B. CREATE OR REPLACE VIEW v_score AS SELECT s.sn, c.cn, sc.score FROM s, c, sc WHERE s.sno=sc.sno AND sc.cno=c.cno;
C. ALTER VIEW v_score AS SELECT s.sn, c.cn, sc.score FROM s, sc, c;
D. CREATE OR REPLACE VIEW v_score AS SELECT * FROM s, c, sc;

✅ 正确答案:B

A 错:少了表连接条件(缺 WHERE),会产生笛卡尔积导致结果爆炸;C 错:ALTER 在视图不存在时会报错,不满足"已存在则替换、不存在则新建"的需求;D 错:少了 JOIN 条件且 SELECT * 会返回无意义的所有字段(包含重复列)。B 同时满足"OR REPLACE"和"正确的连接条件",最佳。

🛠️ 实操任务(课后练习)

任务 1 · 基础

创建一个视图 v_male_stu,显示所有 男生 的学号、姓名、年龄。

查看参考答案
SQL
CREATE VIEW v_male_stu
AS SELECT sno, sn, age FROM s WHERE sex = '男';

任务 2 · 中等

创建一个视图 v_pass,显示 所有及格(成绩 ≥ 60) 的选课记录,包含字段:学生姓名、课程名、成绩。然后通过这个视图查询"数据库原理"课的所有及格学生。

查看参考答案
SQL
-- 创建视图
CREATE VIEW v_pass
AS SELECT s.sn, c.cn, sc.score
   FROM s, c, sc
   WHERE s.sno = sc.sno
     AND sc.cno = c.cno
     AND sc.score >= 60;

-- 通过视图查询
SELECT * FROM v_pass WHERE cn = '数据库原理';

任务 3 · 进阶(思考题)

有视图 v_avg_score 定义为 SELECT sno, AVG(score) FROM sc GROUP BY sno。判断:能否 通过这个视图执行 UPDATE v_avg_score SET ...?为什么?

查看参考答案

不能。原因是这个视图同时违反了两条限制:① 包含聚合函数 AVG();② 包含 GROUP BY 子句。

视图里的"一行"代表"某个学生的平均分",是由基本表 sc 中该学生的多行成绩聚合出来的。如果你想 UPDATE 平均分,MySQL 完全没法翻译回"修改基本表的哪几行" —— 是要全部成绩同时改?还是改一行?指令本身是不明确的,所以 MySQL 直接禁止。