12. 视图【由浅入深-MySQL】

文章目录

  • [MySQL 视图深度解析:从虚拟表原理到实战约束](#MySQL 视图深度解析:从虚拟表原理到实战约束)
  • [第一章 初识视图:定义与底层存储哲学](#第一章 初识视图:定义与底层存储哲学)
  • [第二章 视图的基本操作与实战演示](#第二章 视图的基本操作与实战演示)
    • [2.1 创建视图:封装多表连接查询](#2.1 创建视图:封装多表连接查询)
    • [2.2 查询视图:像操作普通表一样](#2.2 查询视图:像操作普通表一样)
    • [2.3 删除视图](#2.3 删除视图)
  • [第三章 数据变更的连锁反应:视图与基表的双向影响](#第三章 数据变更的连锁反应:视图与基表的双向影响)
    • [3.1 修改视图数据,基表随之改变](#3.1 修改视图数据,基表随之改变)
    • [3.2 修改基表数据,视图实时可见](#3.2 修改基表数据,视图实时可见)
  • [第四章 视图的核心规则与性能陷阱](#第四章 视图的核心规则与性能陷阱)
    • [4.1 命名空间独占性](#4.1 命名空间独占性)
    • [4.2 性能考量(无索引、无触发器、无默认值)](#4.2 性能考量(无索引、无触发器、无默认值))
    • [4.3 权限控制与安全性](#4.3 权限控制与安全性)
    • [4.4 ORDER BY 的覆盖机制](#4.4 ORDER BY 的覆盖机制)
    • [4.5 视图与普通表混合使用](#4.5 视图与普通表混合使用)
  • [第五章 进阶法则:视图的可更新性与 WITH CHECK OPTION](#第五章 进阶法则:视图的可更新性与 WITH CHECK OPTION)
    • [5.1 什么是可更新视图?](#5.1 什么是可更新视图?)
    • [5.2 WITH CHECK OPTION 的守护意义](#5.2 WITH CHECK OPTION 的守护意义)

MySQL 视图深度解析:从虚拟表原理到实战约束

在数据库开发的日常工作中,我们经常需要封装复杂的查询逻辑、隐藏敏感字段或简化业务层的数据访问。MySQL 提供的 视图(View) 功能正是解决这些场景的利器。很多开发者将视图简单理解为"一张虚表",但若仅停留在这个层面,很容易在性能调优和数据更新时踩坑。

本篇博客将摒弃零散的笔记式记录,从底层数据结构、基本操作、双向影响机制到核心限制规则,层层递进地为你梳理 MySQL 视图的完整知识图谱。

第一章 初识视图:定义与底层存储哲学

1.1 什么是视图?

从 SQL 标准的角度定义,视图是一个虚拟表 ,其内容由一条 SELECT 查询语句定义。它在逻辑上包含了一系列带有名称的列和行数据,但在物理层面上,绝大多数视图并不在磁盘上以独立的数据结构持久化存储记录。

核心误区澄清:视图数据到底存不存在磁盘上?在内存中吗?

这个问题的本质,是搞清楚 "执行 SELECT * FROM 视图 时,MySQL 内部到底在干什么"。我们分三个层次来理解,保证通透。

第一层:磁盘上绝对没有视图的数据文件

执行 CREATE VIEW v_emp AS SELECT ... 时,MySQL 在磁盘上只做了一件事:往数据字典(系统表)里插入了一条文本记录,这条记录长什么样?大致如下:

"有一个叫 v_emp 的东西,它的定义是一段 SQL 字符串:SELECT ename, dname FROM emp JOIN dept ..."

仅此而已。MySQL 不会为视图在磁盘上新建任何 .ibd 或 .frm 文件来存放数据行。所以,只要你的电脑关机断电,视图的"数据"就凭空消失了------因为它压根就没存过。

第二层:查询视图时,取决于"现场拼装"还是"中途拍照"

当我们输入 SELECT * FROM v_emp WHERE ename = 'JAMES' 时,MySQL 的优化器面对这个请求,会采取两种截然不同的策略。用两个生活场景来比喻:

策略 A:现场拼装(推荐,也是默认倾向)

想象你定义视图时,只是把一张"购物清单"(SQL 语句)贴在了冰箱上。当你真正要买东西时(执行查询),MySQL 说:"我不需要跑去打印这张清单的副本,我直接把你的要求(WHERE ename='JAMES')一起写进这张旧清单里,拿着合并后的新清单直接去超市货架(基表)拿东西。"

  • 实际执行 :MySQL 将视图的 SQL 和外部查询的 SQL 合并成一条全新的 SQL ,直接去读取 EMP 和 DEPT 这两个物理硬盘上的真实数据文件。
  • 内存占用 :此时视图本身占用什么内存吗?完全不占用 。它只是一段改写逻辑。查询过程中占用的内存,是 InnoDB 存储引擎从磁盘加载 EMP 和 DEPT 表的数据页到 Buffer Pool(共享缓冲池) 时产生的。视图就像一个"免费的快捷方式",直接复用基表的缓存,没有自己的副本。

策略 B:中途拍照(遇到复杂逻辑时使用)

假设你的视图定义特别复杂,比如包含 GROUP BY 分组汇总,或者用了 DISTINCT 去重。此时 MySQL 觉得:"这玩意儿没法直接拼接到你的查询里,太乱了。"

于是 MySQL 换一种做法:它先不管你的外部条件(WHERE),先把视图内部的 SQL 独立执行一遍 ,把查出来的所有结果,写入一张当前会话私有的临时小表里。

  • 这个小表在哪? 优先在 MySQL 服务器的 内存 中(使用 Memory 引擎)。如果结果集太大,内存塞不下,就会自动溢出转存到磁盘上的临时文件(#sql-xxx 格式)。
  • 关键点来了 :这个临时小表,就是视图数据在这一刻的 独立副本 。它和基表 EMP 不是同一块内存,而是把基表的数据复制了一份出来。
  • 后续动作 :临时表建好后,MySQL 再针对这个临时小表执行你外部写的 WHERE ename='JAMES' 过滤,最后把结果返回给你。查询结束后,这张临时表 立即被销毁 ,内存或磁盘空间随即释放。
第三层:用一句话终结你的疑惑

回到问题:"他和基表是同一内存还是两个副本?"

  • 如果采用策略 A(现场拼装) :没有所谓"他和基表"的区别,视图就是基表的一个别名,查询时共用同一份 Buffer Pool 内存,视图本身无内存开销。
  • 如果采用策略 B(中途拍照) :视图的数据是基表数据的临时独立副本,存放在当前会话的私有内存(或临时磁盘)中,查询结束即刻消失,不会持久化。

MySQL 优化器会依据视图定义是否包含 GROUP BY、DISTINCT、聚合函数、子查询 等元素,自动决定采用策略 A 还是策略 B。绝大多数简单的连表查询(如我们例子中的 INNER JOIN)都会走策略 A,所以你平时几乎感觉不到视图的存在感------它本来就是个"逻辑壳子"。

结论 :视图与基表并非"同一块内存",也非"两个持久副本"。视图是一套查询重写规则 或临时结果集的生成器,其数据要么实时计算(MERGE),要么短暂驻留于临时空间(TEMPTABLE)。

1.2 创建视图的基础语法

创建视图的基本语法结构如下,这是所有高级操作的地基:

sql 复制代码
CREATE [OR REPLACE] VIEW 视图名 [(列别名列表)] AS select语句 [WITH [CASCADED | LOCAL] CHECK OPTION];
  • CREATE VIEW:最基础的创建命令。
  • OR REPLACE:如果视图已存在,则替换其定义(MySQL 5.7+ 支持),避免删除重建的繁琐。
  • 列别名列表 :可选,若 SELECT 中的列含有表达式或聚合函数,建议在此显式定义别名。

第二章 视图的基本操作与实战演示

我们以经典的 EMP(员工表)和 DEPT(部门表)为例,完成视图的创建、查询与删除全流程。

2.1 创建视图:封装多表连接查询

假设我们需要频繁查询员工姓名及其所属部门名称,若不使用视图,每次都需要写内连接。通过视图固化这一逻辑:

sql 复制代码
-- 创建视图(标准内连接写法)
CREATE VIEW v_ename_dname AS
SELECT e.ename, d.dname
FROM EMP e
INNER JOIN DEPT d ON e.deptno = d.deptno;

注意 :上面这条语句执行后,如果你再次执行完全相同的 CREATE VIEW 语句,MySQL 会抛出 ERROR 1050 (42S01): Table 'v_ename_dname' already exists 错误(类似截图中遇到的情况)。此时有两种处理方案:

  • 方案一:先删除后创建(下文会讲)。
  • 方案二 :使用 CREATE OR REPLACE VIEW 直接覆盖。
sql 复制代码
-- 安全的覆盖创建
CREATE OR REPLACE VIEW v_ename_dname AS
SELECT e.ename, d.dname
FROM EMP e
INNER JOIN DEPT d ON e.deptno = d.deptno;

2.2 查询视图:像操作普通表一样

创建完成后,查询视图与查询普通表无异:

sql 复制代码
SELECT * FROM v_ename_dname ORDER BY dname;

执行结果(以 SCOTT 样例数据为准):

ename dname
CLARK ACCOUNTING
KING ACCOUNTING
MILLER ACCOUNTING
SMITH RESEARCH
JONES RESEARCH
... ...
JAMES SALES

你会发现,这里的 ORDER BY dname 作用于视图结果集。但请注意:如果一个视图定义中已经写了排序规则,但是查询这个视图时,外层 SELECT 又重新指定了排序规则,那么最终结果按照外层的 ORDER BY 排序,视图内部的排序不再生效。

2.3 删除视图

当视图不再需要时,使用 DROP VIEW 移除其定义:

sql 复制代码
DROP VIEW v_ename_dname;

该操作不会 影响基表 EMP 和 DEPT 的任何数据,因为视图只是逻辑定义。

第三章 数据变更的连锁反应:视图与基表的双向影响

视图与基表之间并不是"单向只读"的关系。在满足特定条件(WITH CHECK OPTION 及非聚合、非去重、非分组等)时,对视图的增删改会直接反映到基表,反之亦然。这是视图容易被误用的高危地带。

3.1 修改视图数据,基表随之改变

假设我们试图将视图中的 CLARK 改名为 TEST:

sql 复制代码
-- 更新视图中的员工姓名
UPDATE v_ename_dname SET ename = 'TEST' WHERE ename = 'CLARK';

执行此语句后,MySQL 并不会在视图中修改任何"虚拟行",而是将修改操作穿透到底层的 EMP 表:

sql 复制代码
-- 查询基表验证
SELECT * FROM EMP WHERE ename = 'CLARK';
-- 结果为 Empty set,因为 CLARK 已被修改

SELECT * FROM EMP WHERE ename = 'TEST';
-- 结果展示:EMPNO=7782, ENAME='TEST', JOB='MANAGER'...

结论:只要视图定义满足可更新条件(即视图与基表行能建立一一映射),对视图的 DML 操作等价于对基表的操作。

3.2 修改基表数据,视图实时可见

视图不存储数据,每次查询都动态读取基表。若我们修改基表,视图会立即感知:

sql 复制代码
-- 修改基表:将 JAMES 的部门编号从 30(SALES)改为 10(ACCOUNTING)
UPDATE EMP SET deptno = 10 WHERE ename = 'JAMES';

重新查询视图:

sql 复制代码
SELECT * FROM v_ename_dname WHERE ename = 'JAMES';

返回结果:

ename dname
JAMES ACCOUNTING

第四章 视图的核心规则与性能陷阱

MySQL 对视图的使用有诸多隐形约束,理解这些规则能帮你避开生产环境中的大坑。

4.1 命名空间独占性

规则 :视图与表(包括临时表)共享同一个命名空间。数据库中不允许存在同名的表和视图。

上述的截图中执行 create view myview_as ... 时报错 Table 'myview' already exists,正是触发了此规则------因为之前已经有一个名为 myview 的视图(或表)存在。即使是 myview 和 myview_as 也会因不同名称而允许共存,但若混淆,会导致误判。

4.2 性能考量(无索引、无触发器、无默认值)

  • 无法建立索引 :视图是虚拟结构,CREATE INDEX 语句不支持视图。若对视图查询性能不满,只能优化其底层的 SELECT 语句,或针对基表加索引。
  • 不能关联触发器 :不能为视图创建 BEFORE INSERT、AFTER UPDATE 等触发器。
  • 不能设定默认值:向视图插入数据时,若未指定某列,MySQL 无法像表一样应用默认值(除非基表该列有默认值且视图直接映射)。

性能建议 :创建视图的 SELECT 语句越复杂(多表 JOIN、子查询、排序),查询视图时开销越大。MySQL 不会为视图自动生成优化执行计划缓存,每次调用都会重新解析并优化。

4.3 权限控制与安全性

视图是精细化权限管理的利器。你可以:

  • 通过视图隐藏基表的敏感字段(如工资、身份证号),只授予用户访问视图的 SELECT 权限,而不授予基表权限。
  • 用户必须拥有 CREATE VIEW 权限才能创建,且对视图引用的基表至少拥有 SELECT 权限。

4.4 ORDER BY 的覆盖机制

这是一个极易被忽略的细节 。如果视图定义中包含 ORDER BY,而外部查询也包含 ORDER BY,MySQL 会完全忽略 视图内部的 ORDER BY,以外部排序为准。

示例:

sql 复制代码
-- 视图内部按 ename 降序
CREATE VIEW v_order_test AS SELECT ename FROM EMP ORDER BY ename DESC;

-- 外部查询按 ename 升序
SELECT * FROM v_order_test ORDER BY ename ASC; -- 最终结果按升序,视图内的 DESC 无效

因此,不建议在视图定义中使用 ORDER BY,除非你明确配合 LIMIT 使用(在子查询或特定算法下有效),否则徒增解析开销而无实际效果。

4.5 视图与普通表混合使用

视图在 FROM 子句中可与表、其他视图自由组合。例如:

sql 复制代码
SELECT v.ename, s.grade
FROM v_ename_dname v
JOIN SALGRADE s ON v.ename = ... -- 实际业务关联

MySQL 会尝试将视图合并到整体执行计划中,但这可能引发巨大的中间结果集,需谨慎评估。

第五章 进阶法则:视图的可更新性与 WITH CHECK OPTION

5.1 什么是可更新视图?

并非所有视图都支持 INSERT、UPDATE、DELETE。如果视图定义包含以下元素之一,则视为不可更新视图:

  • 聚合函数(SUM、COUNT、AVG 等)
  • DISTINCT
  • GROUP BY / HAVING
  • 子查询(某些场景)
  • UNION 或 UNION ALL
  • FROM 子句中的不可更新视图

实战验证 :我们创建的 v_ename_dname 不含上述元素,因此可更新。

5.2 WITH CHECK OPTION 的守护意义

若希望确保通过视图插入或更新的数据依然符合视图的过滤条件,务必加上 WITH CHECK OPTION。

示例:

sql 复制代码
CREATE VIEW v_sales_emp AS
SELECT ename, deptno FROM EMP WHERE deptno = 30
WITH CHECK OPTION;

此时,若执行 UPDATE v_sales_emp SET deptno = 10 WHERE ename = 'ALLEN',MySQL 会报错,因为修改后该行不再满足 deptno=30 的视图定义条件,WITH CHECK OPTION 阻止了该操作,保证了视图数据逻辑的一致性。

相关推荐
treesforest1 小时前
游戏账号安全风险分层方案:IP信号分层与人机复核实践
tcp/ip·安全·游戏·1024程序员节
Evand J1 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang1 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节
Initialize-le1 小时前
Docker 容器网络不通?五步排查法
1024程序员节
天马37981 小时前
微信小程序swiper实现滚动渐变效果案例1
1024程序员节·微信小程序swiper·实现滚动渐变效果案例
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言学生信息管理系统 — 知识点详解(18)
c语言·学习·1024程序员节
ha_lydms1 小时前
AnalyticDB MySQL 技术架构
大数据·hadoop·mysql·adb·spark·dataworks·1024程序员节
zh路西法1 小时前
【四足强化学习经典算法】(一):RMA——盲视运动控制与快速适应模块
pytorch·强化学习·1024程序员节·四足机器人·dagger·pyhon·rma
太理摆烂哥1 小时前
MySQL索引
1024程序员节