首页 / MySQL 入门教程 / 视图 VIEW

MySQL 入门教程

视图 VIEW

本教程共 46 篇 · 第 34 篇 · 更新于 2026-07-30 · 约 10 分钟阅读

MySQLMySQL 入门教程视图VIEWCREATE VIEW可更新视图CHECK OPTION

34. 视图 VIEW

本节目标:理解视图是什么、为什么用它,掌握 CREATE/REPLACE/DROP VIEW 的语法,知道可更新视图的条件和 WITH CHECK OPTION 的作用。

34.1 视图是什么

视图(View) 是一张虚拟表。它本身不存数据,数据来自底层的真实表(称为基表)。视图把一条 SELECT 查询”存”起来,给它起个名字,之后可以像查表一样查它。

-- 创建视图:查出成年用户
CREATE VIEW adult_users AS
SELECT id, username, email, age
FROM users
WHERE age >= 18;

-- 像查表一样查视图
SELECT * FROM adult_users;
Note

视图不占存储空间(除了视图定义本身),每次查视图时,MySQL 把视图展开成底层 SELECT 再执行。所以视图的数据永远是实时的,和基表同步。
来源:MySQL 官方文档 - CREATE VIEW

34.2 为什么要用视图

视图有三个典型用途:

1. 简化查询

复杂的 JOIN 和子查询写一遍就够了,封装成视图后直接 SELECT * FROM 视图名

-- 经常要查"每个用户的订单数量和总金额"
CREATE VIEW user_order_stats AS
SELECT
    u.id,
    u.username,
    COUNT(o.id) AS order_count,
    COALESCE(SUM(o.amount), 0) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;

-- 以后直接查视图就行
SELECT * FROM user_order_stats WHERE total_amount > 1000;

2. 控制权限

不想让某个用户看到完整数据,可以建一个只暴露部分列的视图,然后只给视图授权。

-- 只暴露用户名和年龄,隐藏邮箱
CREATE VIEW user_public AS
SELECT id, username, age FROM users;

-- 给某个账号只授权查这个视图
GRANT SELECT ON user_public TO 'app_read'@'localhost';

3. 提供数据一致性

底层表结构变了(比如加列、改列名),只要视图的查询逻辑不变,上层应用无需改代码。

34.3 创建视图

基本语法:

CREATE [OR REPLACE] VIEW 视图名 [(列名列表)] AS
SELECT 查询语句;
-- 创建视图并指定列名
CREATE VIEW active_orders (订单ID, 用户ID, 金额) AS
SELECT id, user_id, amount
FROM orders
WHERE status = 'active';
-- 查询结果
SELECT 订单ID, 金额 FROM active_orders;
Tip

CREATE OR REPLACE VIEWCREATE VIEW 好用:如果视图已存在就替换,不存在就创建,省得先 DROP 再 CREATE。

34.4 修改视图

CREATE OR REPLACE VIEWALTER VIEW

-- 方式一:OR REPLACE(推荐)
CREATE OR REPLACE VIEW adult_users AS
SELECT id, username, email, age, created_at
FROM users
WHERE age >= 18;

-- 方式二:ALTER VIEW
ALTER VIEW adult_users AS
SELECT id, username, age
FROM users
WHERE age >= 18;

两者效果差不多,OR REPLACE 更简洁,一行搞定创建和修改。

34.5 删除视图

DROP VIEW IF EXISTS adult_users;

IF EXISTS 防止视图不存在时报错,养成习惯加上。

Warning

删除视图不影响底层的基表数据。视图只是个”查询别名”,删了别名,数据还在。

34.6 查看视图定义

-- 查看建视图语句
SHOW CREATE VIEW adult_users\G

-- 查看视图列信息(和查表一样)
DESCRIBE adult_users;

-- 查所有视图
SELECT TABLE_NAME FROM information_schema.VIEWS
WHERE TABLE_SCHEMA = '你的库名';

34.7 可更新视图

有些视图不仅能查,还能通过它 INSERT/UPDATE/DELETE 底层表的数据,这叫可更新视图(Updatable View)

CREATE VIEW user_names AS
SELECT id, username FROM users;

-- 通过视图插入数据(email 等列没在视图里,需要有默认值或允许 NULL)
INSERT INTO user_names(id, username) VALUES(10, 'testuser');

-- 通过视图更新数据
UPDATE user_names SET username = 'newname' WHERE id = 10;

-- 通过视图删除数据
DELETE FROM user_names WHERE id = 10;

可更新视图的条件

视图要可更新,必须满足:

  • 视图的 SELECT 中没有聚合函数(SUM、COUNT 等);
  • 没有 DISTINCT、GROUP BY、HAVING、UNION;
  • 没有 JOIN(某些简单 JOIN 也可更新,但限制多);
  • 没有子查询在 FROM 子句中。

说白了,视图的数据和基表行是一一对应的,才能通过视图改基表。

特性可更新视图不可更新视图
简单单表视图可以-
带 JOIN 的视图部分可以多数不行
带聚合函数的视图-不行
带 GROUP BY 的视图-不行
带 DISTINCT 的视图-不行

34.8 WITH CHECK OPTION

通过视图插入或更新数据时,可能插入不符合视图 WHERE 条件的数据。比如 adult_users 视图要求 age >= 18,但你通过视图插入了 age = 15 的数据:

-- 没有 CHECK OPTION 时,能插入,但在视图里查不到
INSERT INTO adult_users(id, username, age) VALUES(20, 'kid', 15);
-- 插入成功,但 SELECT * FROM adult_users 查不到这条(因为 age < 18)

WITH CHECK OPTION 就能阻止这种情况:

CREATE OR REPLACE VIEW adult_users AS
SELECT id, username, email, age
FROM users
WHERE age >= 18
WITH CHECK OPTION;

-- 再试插入 age=15 的数据
INSERT INTO adult_users(id, username, age) VALUES(21, 'kid', 15);
-- ERROR 1369 (HY000): CHECK OPTION failed 'adult_users'
Note

WITH CHECK OPTION 确保通过视图插入或修改的数据,仍然满足视图的 WHERE 条件。这在数据权限控制场景中很有用:你给用户一个视图,就不怕他通过视图写入越界数据。

CASCADED vs LOCAL

WITH CASCADED CHECK OPTION;  -- 默认,检查本视图及所有依赖视图的条件
WITH LOCAL CHECK OPTION;     -- 只检查本视图的条件

视图基于另一个视图时才有区别。大多数场景用默认的 CASCADED 就行。

34.9 视图的局限性

视图很好用,但也有坑:

  • 性能不透明:视图本质是子查询,多层嵌套视图可能导致性能问题,且难以排查;
  • 不能建索引:视图本身没有索引(索引在基表上),复杂视图查询可能很慢;
  • 修改限制多:带 JOIN、聚合的视图不可更新;
  • 依赖链风险:删了基表,视图还在但查不了,报错信息不直观。
Tip

我之前接手过一个项目,视图套视图套了三层,最后排查一个慢查询花了半天。视图嵌套不要超过两层,复杂逻辑尽量用存储过程或在应用层处理。

34.10 小结

  • 视图是虚拟表,不存数据,每次查询展开为底层 SELECT;
  • 三大用途:简化查询、控制权限、隔离表结构变化;
  • CREATE OR REPLACE VIEW 一行搞定创建和修改;
  • 可更新视图要求视图与基表行一一对应,不能有聚合/分组/DISTINCT;
  • WITH CHECK OPTION 防止通过视图写入不符合条件的数据;
  • 视图嵌套别超过两层,避免性能和维护问题。

下一节学存储过程和自定义函数,把复杂 SQL 逻辑封装成可复用的程序。