深蓝黑背景封面:右侧大标题「视图:一张不存数据的表」,副标题「视图的作用、限制与可更新条件」;左侧原创示意——一张底表被一扇「视窗」裁掉两列敏感字段,读者只能透过窗口看到裁剪后的列;左上标注「面试经典 · 数据库」,右上标注「Day 05 · 10-23」 图 1:本文封面,视图是一扇裁剪过的窗,不是一张新表。

先说明属性:这是一篇经典题解析,题目出自本地历史面试资料,与任何公司的当前招聘无关。「视图的作用,视图可以更改么」是两连问:第一问考你知不知道它解决什么问题,第二问考你清不清楚它的边界。两个问题都答实了,才算会用视图。

第一步,先认清本质:视图是一条存起来的 SELECT。

视图本身不存任何数据,它只是一条被命名保存的查询定义。每次查视图,数据库把定义实时展开到底表上执行——MySQL 默认尽量用 MERGE 算法把视图定义合并进外层查询,合并不了(比如含聚合)才退到 TEMPTABLE,先把视图结果物化成临时表再查,这也是带 TEMPTABLE 的视图一律不可更新的原因。这一个事实决定了后面所有结论:视图是逻辑层,不是性能层——它不缓存结果,查一次算一次;复杂视图套视图,要警惕层层展开后的执行计划。

第二步,两个真正值钱的作用。

查询简化:把多表连接加过滤条件封装成一个名字,调用方像查单表一样用。报表、看板类查询反复用到同一段连接逻辑时,视图把复杂度收敛到一个地方,改定义一处生效。

权限隔离:这是更常被低估的用途。给标注团队、分析团队这类外部角色授权时,直接授底表权限意味着所有列都暴露;授一个裁剪过的视图权限,敏感列天然不可见——列级裁剪不靠应用层自觉,由数据库强制。比如用户表里有手机号、身份证列,给客服系统一个只含工单所需字段的视图,客服账号连底表权限都没有,却能把该查的都查到。配合「视图定义者权限」的语义,还能让没有底表权限的账号透过视图读到允许的部分。

第三步,能不能改:看行能不能唯一映射回底表。

改视图本质是改底表,所以 MySQL 官方文档给出的总判据是:视图里的每一行必须与底表的行一一对应。具体地,视图包含以下任何一项就不可更新:聚合或窗口函数、DISTINCT、GROUP BY、HAVING、UNION、选择列表里的子查询、引用不可更新的视图、只引用字面量,以及 ALGORITHM = TEMPTABLE。多表视图可以更窄地支持:必须用内连接,且一次 UPDATE 只能改其中一张表的列。想 INSERT 还有附加条件——视图列不能有重名、必须包含底表所有无默认值的列、列必须是简单引用不能是表达式。服务器在建视图时就算好了可更新标记,可以直接查:

-- 查视图的可更新标记:YES 表示 UPDATE/DELETE 合法
SELECT TABLE_NAME, IS_UPDATABLE
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_db';

-- 可更新的典型:单表、无聚合、行与底表一一对应
CREATE VIEW v_active_users AS
SELECT id, name, city FROM users WHERE status = 'active';

-- 不可更新的典型:聚合让多行合并成一行,无法映射回底表
CREATE VIEW v_city_cnt AS
SELECT city, COUNT(*) AS cnt FROM users GROUP BY city;

第一个视图里改一行 name,就是改 users 表里那一行;第二个视图里 cnt 是若干行压缩出来的数字,「把 cnt 改成 5」根本没有对应的底表操作。

作用加判定图:上方视图本质——视图是一条存起来的 SELECT 定义,本身不存数据,查询时实时展开到底表执行。中部左侧两个用途:查询简化——把多表连接与过滤封装成一个名字,调用方像查单表一样用;权限隔离——给外部团队只授视图权限,敏感列天然不可见,列级裁剪不靠应用自觉。中部右侧可更新判定路径:改视图本质是改底表,能改的条件是每行都能唯一映射回底表的一行——单表、无聚合与 DISTINCT、无 GROUP BY / HAVING、含底表主键列、无 UNION;任一不满足则视图只读,要写就直接写底表或拆成存储过程。底部提示:视图是逻辑层不是性能层——它不缓存结果,复杂视图套视图要警惕展开后的执行计划 图 2:视图的本质、两个用途与可更新判定路径。

可能的追问与易错点。

「不可更新的视图想写怎么办?」两条路:直接写底表,或者把写逻辑封装成存储过程、视图只负责读。「WITH CHECK OPTION 是什么?」加在可更新视图上,要求对视图的插入和更新结果必须仍满足视图的 WHERE 条件——防止透过视图把行改到「窗外」去,属于可更新视图的配套保险。易错点三个:把视图当缓存用,指望它提速——要缓存结果得用物化方案,普通视图做不到;权限设计时只授底表,靠应用层过滤敏感列,一旦绕过应用直接连库就全裸;在嵌套视图上做复杂查询不看执行计划,展开后的连接顺序可能完全出乎意料。回到答题现场:先一句「视图是存起来的 SELECT,不存数据」立住本质,再讲简化与权限两个作用,然后用「行能否唯一映射回底表」串起可更新条件,最后提一句 IS_UPDATABLE 可查、CHECK OPTION 兜底。你团队的数据库权限是按表授的还是按视图授的?欢迎聊聊。