视图
导学
视图(View)是数据库中的"虚拟表"——它不存储实际数据,而是存储一条 SELECT 查询。通过视图,你可以封装复杂的查询逻辑、隐藏敏感列、简化用户权限管理。
定义
视图(View):基于 SQL 查询结果的虚拟表。视图本身不存储数据(除非使用物化视图,MySQL 5.7 不支持),每次查询视图时都会执行底层的 SELECT 语句。
核心语法
-- 创建视图
CREATE VIEW view_name AS
SELECT 列 FROM 表 WHERE 条件;
-- 查询视图(和查询表一样)
SELECT * FROM view_name WHERE ...;
-- 删除视图
DROP VIEW IF EXISTS view_name;
-- 查看视图定义
SHOW CREATE VIEW view_name;
SQL 示例
场景一:简化复杂查询
当前数据状态:创建基础表
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
dept VARCHAR(20),
score DECIMAL(5,2)
);
CREATE TABLE scores (
score_id INT PRIMARY KEY AUTO_INCREMENT,
emp_id INT,
score DECIMAL(5,2)
);
INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);
INSERT INTO scores (emp_id, score) VALUES
(1, 90),
(2, 85);
SELECT * FROM employees;
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
SELECT * FROM scores;
| score_id | emp_id | score |
|---|---|---|
| 1 | 1 | 90.00 |
| 2 | 2 | 85.00 |
操作语句:
-- 创建一个"员工成绩汇总"视图
CREATE VIEW emp_score_summary AS
SELECT
e.emp_id,
e.emp_name,
e.dept,
s.score AS exam_score
FROM employees e
JOIN scores s ON e.emp_id = s.emp_id;
-- 查询视图
SELECT * FROM emp_score_summary;
操作后数据状态:
| emp_id | emp_name | dept | exam_score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 90.00 |
| 2 | 白歌 | 技术部 | 85.00 |
-- 像查表一样使用视图,并添加额外条件
SELECT * FROM emp_score_summary WHERE dept = '技术部';
| emp_id | emp_name | dept | exam_score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 90.00 |
| 2 | 白歌 | 技术部 | 85.00 |
结果解读:视图 emp_score_summary 封装了 JOIN 的复杂逻辑。使用者无需关心底层实现,只需像查表一样 SELECT * FROM emp_score_summary。
场景二:隐藏敏感列
当前数据状态:employees 表有姓名、部门和评分信息。
操作语句:
-- 先给 employees 表添加敏感列
ALTER TABLE employees ADD phone VARCHAR(20);
ALTER TABLE employees ADD id_card VARCHAR(18);
UPDATE employees SET phone = '13800138001', id_card = '110101200001011234' WHERE emp_id = 1;
UPDATE employees SET phone = '13900139002', id_card = '110101200001021234' WHERE emp_id = 2;
-- 查看完整表数据
SELECT * FROM employees;
当前数据状态:
| emp_id | emp_name | dept | score | phone | id_card |
|---|---|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 | 13800138001 | 110101200001011234 |
| 2 | 白歌 | 技术部 | NULL | 13900139002 | 110101200001021234 |
操作语句:
-- 创建视图,只暴露非敏感信息
CREATE VIEW emp_public AS
SELECT emp_id, emp_name, dept FROM employees;
-- 用户只能看到这三列
SELECT * FROM emp_public;
操作后数据状态:
| emp_id | emp_name | dept |
|---|---|---|
| 1 | 大翔 | 技术部 |
| 2 | 白歌 | 技术部 |
结果解读:通过视图 emp_public,用户只能看到 emp_id、emp_name、dept 三列,无法访问底层的 phone 和 id_card 敏感数据。
场景三:通过视图限制数据访问
当前数据状态:employees 表有 dept 列。
操作语句:
-- 创建一个只能查看技术部员工的视图
CREATE VIEW tech_employees AS
SELECT emp_id, emp_name, dept, score FROM employees WHERE dept = '技术部';
SELECT * FROM tech_employees;
操作后数据状态:
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
结果解读:视图 tech_employees 封装了 WHERE dept = '技术部' 这个过滤条件。即使用户执行 SELECT * FROM tech_employees,也只会看到技术部的员工。
可更新视图
某些视图支持 INSERT、UPDATE、DELETE 操作:
当前数据状态:创建基于单表的简单视图
CREATE TABLE simple_employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
dept VARCHAR(20)
);
INSERT INTO simple_employees (emp_name, dept) VALUES
('大翔', '技术部'),
('白歌', '产品部');
SELECT * FROM simple_employees;
| emp_id | emp_name | dept |
|---|---|---|
| 1 | 大翔 | 技术部 |
| 2 | 白歌 | 产品部 |
操作语句:
-- 创建基于单表的视图
CREATE VIEW tech_view AS
SELECT * FROM simple_employees WHERE dept = '技术部';
-- 通过视图插入数据
INSERT INTO tech_view (emp_name, dept) VALUES ('小崔', '技术部');
SELECT * FROM simple_employees;
操作后数据状态:
| emp_id | emp_name | dept |
|---|---|---|
| 1 | 大翔 | 技术部 |
| 2 | 白歌 | 产品部 |
| 3 | 小崔 | 技术部 |
SELECT * FROM tech_view;
| emp_id | emp_name | dept |
|---|---|---|
| 1 | 大翔 | 技术部 |
| 3 | 小崔 | 技术部 |
结果解读:通过 tech_view 插入的数据实际上插入到了底层表 simple_employees 中。因为视图是基于单表的简单查询,所以支持 INSERT 操作。
可更新视图的限制
操作语句:
-- 尝试通过包含 JOIN 的视图插入数据
INSERT INTO emp_score_summary (emp_name, exam_score) VALUES ('测试', 100);
操作后数据状态:
ERROR 1288 (HY000): The target table emp_score_summary of the INSERT is not insertable-into
结果解读:包含 GROUP BY、JOIN、聚合函数 的视图不可更新。实际开发中,视图主要用于查询,不推荐通过视图进行数据修改。
限制条件:
- 不能包含
GROUP BY、DISTINCT、聚合函数、UNION - 不能包含子查询(某些情况允许)
- 必须是基于单表的简单查询
实际开发中,视图主要用于查询,不推荐通过视图进行数据修改。
视图的性能考量
MySQL 5.7 处理视图的两种算法:
- MERGE:将视图定义合并到外层查询中,优化后统一执行(性能更好)
- TEMPTABLE:将视图结果物化为临时表,再对外层查询执行(某些复杂视图必须用)
可通过 ALGORITHM = MERGE 或 ALGORITHM = TEMPTABLE 显式指定。
查看视图使用的算法
SHOW CREATE VIEW emp_score_summary;
CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER
VIEW `emp_score_summary` AS
SELECT e.emp_id, e.emp_name, e.dept, s.score AS exam_score
FROM employees e JOIN scores s ON e.emp_id = s.emp_id
结果解读:ALGORITHM=UNDEFINED 表示由 MySQL 自行选择算法。对于包含 JOIN 的视图,MySQL 通常会选择 MERGE 算法,将视图定义合并到外层查询中优化执行。
常见误区
| 误区 | 正解 |
|---|---|
| 视图存储数据 | 不存储(MySQL 5.7 无物化视图)。每次查询都执行底层 SELECT。 |
| 视图查询比直接查表快 | 通常一样,甚至稍慢(多一层解析)。视图的价值在于简化和安全,而非性能。 |
| 所有视图都能更新 | 只有基于单表、无聚合/分组/DISTINCT 的简单视图才能更新。 |
面试考点
Q:视图和表的区别?
表存储实际数据;视图是虚拟表,基于 SELECT 查询,不存储数据(MySQL 5.7)。视图可以封装复杂查询、隐藏列、简化权限管理。
Q:视图的优点和缺点?
优点:简化复杂查询、隐藏敏感数据、统一数据访问接口。缺点:可能隐藏性能问题(复杂的视图定义让用户 unaware)、不支持物化(每次查询都重新执行)。
Q:MySQL 5.7 支持物化视图吗?
不支持。MySQL 5.7 的视图都是"虚拟视图",每次查询都执行底层 SQL。如需物化视图效果,需手动维护汇总表(如用触发器或定时任务更新)。
小结
- 视图是虚拟表,基于 SELECT 查询定义,不存储数据
- 用于简化查询、隐藏敏感列、控制数据访问
- 可更新视图有严格限制,实际开发中视图主要用于查询
- MySQL 5.7 不支持物化视图
下一章引子:视图封装了查询逻辑,存储过程则能封装更复杂的业务逻辑——包括变量、流程控制和多条 SQL 语句。