文章目录
-
- 视图:虚拟表的妙用
- 一、前言
- 二、什么是视图
-
- [2.1 视图的定义](#2.1 视图的定义)
- [2.2 视图 vs 表](#2.2 视图 vs 表)
- [2.3 为什么需要视图](#2.3 为什么需要视图)
- 三、视图的基本操作
-
- [3.1 创建视图](#3.1 创建视图)
- [3.2 查询视图](#3.2 查询视图)
- [3.3 修改视图](#3.3 修改视图)
-
- [方式一:CREATE OR REPLACE](#方式一:CREATE OR REPLACE)
- [方式二:ALTER VIEW](#方式二:ALTER VIEW)
- [3.4 删除视图](#3.4 删除视图)
- [3.5 查看视图定义](#3.5 查看视图定义)
- 四、视图与基表的关系
- 五、视图的应用场景
-
- [5.1 简化复杂查询](#5.1 简化复杂查询)
- [5.2 权限控制](#5.2 权限控制)
- [5.3 数据格式化](#5.3 数据格式化)
- [5.4 数据汇总](#5.4 数据汇总)
- 六、视图的规则和限制
-
- [6.1 命名规则](#6.1 命名规则)
- [6.2 性能考虑](#6.2 性能考虑)
- [6.3 ORDER BY 的使用](#6.3 ORDER BY 的使用)
- [6.4 其他限制](#6.4 其他限制)
- 七、视图的最佳实践
-
- [7.1 何时使用视图](#7.1 何时使用视图)
- [7.2 视图命名规范](#7.2 视图命名规范)
- [7.3 视图文档化](#7.3 视图文档化)
- [7.4 定期维护](#7.4 定期维护)
- 八、总结
-
- [8.1 快速决策表](#8.1 快速决策表)
- [8.2 核心要点](#8.2 核心要点)
视图:虚拟表的妙用
一、前言
💬 这一篇讲什么:MySQL 视图的概念和应用
🚀 核心内容:
- 什么是视图?
- 如何创建和使用视图?
- 视图与基表的关系?
- 视图的应用场景?
视图是数据库中的一个重要概念。它像是一个"虚拟表",本身不存储数据,而是通过查询从基表中动态获取数据。视图可以简化复杂查询、提高安全性、保持数据独立性。
二、什么是视图
2.1 视图的定义
视图(View) 是一个虚拟表,其内容由查询定义。
关键特点:
- 视图不存储实际数据,只存储查询语句。
- 查询视图时,MySQL 执行定义视图的 SELECT 语句。
- 视图的数据来自一个或多个基表。
2.2 视图 vs 表
| 特性 | 表(Table) | 视图(View) |
|---|---|---|
| 存储数据 | ✅ 存储实际数据 | ❌ 不存储数据 |
| 占用空间 | 占用磁盘空间 | 只存储定义(SQL) |
| 查询方式 | 直接查询 | 执行定义的 SQL |
| 修改数据 | 直接修改 | 修改会影响基表 |
| 性能 | 快 | 取决于查询复杂度 |
2.3 为什么需要视图
场景一:简化复杂查询
假设经常需要查询员工及其部门信息:
sql
-- 每次都要写这么长的 SQL
SELECT e.ename, d.dname, d.loc
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE d.loc = '北京';
有了视图:
sql
-- 创建视图
CREATE VIEW v_emp_dept AS
SELECT e.ename, d.dname, d.loc
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
-- 使用视图,简单多了
SELECT * FROM v_emp_dept WHERE loc = '北京';
场景二:提高安全性
假设有一个员工表,包含敏感信息(工资、身份证号):
sql
-- 员工表
CREATE TABLE emp (
id INT,
name VARCHAR(20),
salary DECIMAL(10,2), -- 敏感
id_card CHAR(18) -- 敏感
);
-- 创建视图,只暴露非敏感信息
CREATE VIEW v_emp_public AS
SELECT id, name FROM emp;
-- 给普通用户授权,只能访问视图
GRANT SELECT ON v_emp_public TO 'user1'@'localhost';
场景三:保持数据独立性
当表结构改变时,视图可以保持接口不变:
sql
-- 原表
CREATE TABLE user (id INT, full_name VARCHAR(50));
-- 创建视图
CREATE VIEW v_user AS
SELECT id, full_name AS name FROM user;
-- 后来表结构改变
ALTER TABLE user CHANGE full_name user_name VARCHAR(50);
-- 修改视图定义,外部使用不受影响
CREATE OR REPLACE VIEW v_user AS
SELECT id, user_name AS name FROM user;
三、视图的基本操作
3.1 创建视图
基本语法
sql
CREATE VIEW 视图名 AS SELECT语句;
准备测试数据
sql
-- 部门表
CREATE TABLE dept (
deptno INT PRIMARY KEY,
dname VARCHAR(20),
loc VARCHAR(20)
);
INSERT INTO dept VALUES
(10, '财务部', '北京'),
(20, '研发部', '上海'),
(30, '销售部', '深圳');
-- 员工表
CREATE TABLE emp (
empno INT PRIMARY KEY,
ename VARCHAR(20),
job VARCHAR(20),
salary DECIMAL(10,2),
deptno INT
);
INSERT INTO emp VALUES
(1001, '张三', 'MANAGER', 5000, 10),
(1002, '李四', 'ANALYST', 4000, 20),
(1003, '王五', 'SALESMAN', 3000, 30),
(1004, '赵六', 'CLERK', 2000, 10),
(1005, '孙七', 'ANALYST', 4500, 20);
示例一:简单视图
sql
-- 创建视图:员工姓名和部门名称
CREATE VIEW v_emp_dept AS
SELECT e.ename, d.dname
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
-- 查询视图
SELECT * FROM v_emp_dept;
输出:
bash
+------+------+
| ename| dname|
+------+------+
| 张三 | 财务部|
| 李四 | 研发部|
| 王五 | 销售部|
| 赵六 | 财务部|
| 孙七 | 研发部|
+------+------+
示例二:带条件的视图
sql
-- 创建视图:只显示研发部的员工
CREATE VIEW v_dev_emp AS
SELECT e.ename, e.job, e.salary
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE d.dname = '研发部';
-- 查询视图
SELECT * FROM v_dev_emp;
输出:
bash
+------+---------+--------+
| ename| job | salary |
+------+---------+--------+
| 李四 | ANALYST | 4000.00|
| 孙七 | ANALYST | 4500.00|
+------+---------+--------+
示例三:带聚合的视图
sql
-- 创建视图:每个部门的平均工资
CREATE VIEW v_dept_avg_salary AS
SELECT d.dname, AVG(e.salary) AS avg_salary
FROM emp e
JOIN dept d ON e.deptno = d.deptno
GROUP BY d.deptno, d.dname;
-- 查询视图
SELECT * FROM v_dept_avg_salary;
输出:
bash
+------+------------+
| dname| avg_salary |
+------+------------+
| 财务部| 3500.00 |
| 研发部| 4250.00 |
| 销售部| 3000.00 |
+------+------------+
3.2 查询视图
视图的查询方式与表完全相同:
sql
-- 基本查询
SELECT * FROM v_emp_dept;
-- 带条件查询
SELECT * FROM v_emp_dept WHERE dname = '财务部';
-- 排序
SELECT * FROM v_emp_dept ORDER BY ename;
-- 连接其他表
SELECT v.ename, v.dname, e.salary
FROM v_emp_dept v
JOIN emp e ON v.ename = e.ename;
3.3 修改视图
方式一:CREATE OR REPLACE
sql
-- 如果视图存在则替换,不存在则创建
CREATE OR REPLACE VIEW v_emp_dept AS
SELECT e.ename, d.dname, d.loc
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
方式二:ALTER VIEW
sql
ALTER VIEW v_emp_dept AS
SELECT e.ename, d.dname, e.salary
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
3.4 删除视图
sql
DROP VIEW v_emp_dept;
-- 如果存在则删除
DROP VIEW IF EXISTS v_emp_dept;
3.5 查看视图定义
sql
-- 查看视图创建语句
SHOW CREATE VIEW v_emp_dept\G
-- 查看所有视图
SHOW TABLES; -- 视图也会显示在这里
-- 查看视图结构
DESC v_emp_dept;
四、视图与基表的关系
4.1 修改视图影响基表
重要:对视图的修改会直接影响基表。
示例
sql
-- 创建简单视图
CREATE VIEW v_emp AS
SELECT empno, ename, salary FROM emp;
-- 查看原始数据
SELECT * FROM v_emp WHERE ename = '张三';
输出:
bash
+-------+------+--------+
| empno | ename| salary |
+-------+------+--------+
| 1001 | 张三 | 5000.00|
+-------+------+--------+
通过视图修改数据:
sql
UPDATE v_emp SET salary = 6000 WHERE ename = '张三';
-- 查看视图
SELECT * FROM v_emp WHERE ename = '张三';
输出:
bash
+-------+------+--------+
| empno | ename| salary |
+-------+------+--------+
| 1001 | 张三 | 6000.00| -- 已修改
+-------+------+--------+
查看基表:
sql
SELECT * FROM emp WHERE ename = '张三';
输出:
bash
+-------+------+---------+--------+--------+
| empno | ename| job | salary | deptno |
+-------+------+---------+--------+--------+
| 1001 | 张三 | MANAGER | 6000.00| 10 | -- 基表也被修改
+-------+------+---------+--------+--------+
4.2 修改基表影响视图
sql
-- 修改基表
UPDATE emp SET salary = 7000 WHERE ename = '张三';
-- 查看视图
SELECT * FROM v_emp WHERE ename = '张三';
输出:
bash
+-------+------+--------+
| empno | ename| salary |
+-------+------+--------+
| 1001 | 张三 | 7000.00| -- 视图数据也变了
+-------+------+--------+
4.3 可更新视图 vs 不可更新视图
可更新视图:可以通过视图进行 INSERT、UPDATE、DELETE 操作。
不可更新视图:只能查询,不能修改。
不可更新的情况
| 情况 | 原因 |
|---|---|
| 包含聚合函数 | SUM(), AVG(), COUNT() 等 |
| 包含 DISTINCT | 去重后无法对应到具体行 |
| 包含 GROUP BY | 分组后无法对应到具体行 |
| 包含 UNION | 合并多个查询结果 |
| 包含子查询 | 复杂逻辑无法反向操作 |
| FROM 多个表 | 无法确定修改哪个表 |
示例:
sql
-- 不可更新的视图(包含聚合函数)
CREATE VIEW v_dept_count AS
SELECT deptno, COUNT(*) AS emp_count
FROM emp
GROUP BY deptno;
-- 尝试更新(会失败)
UPDATE v_dept_count SET emp_count = 10 WHERE deptno = 10;
输出:
bash
ERROR: The target table v_dept_count of the UPDATE is not updatable
五、视图的应用场景
5.1 简化复杂查询
场景:经常需要查询员工、部门、工资等级的综合信息。
sql
-- 创建视图
CREATE VIEW v_emp_full_info AS
SELECT
e.empno,
e.ename,
e.job,
e.salary,
d.dname,
d.loc,
CASE
WHEN e.salary >= 5000 THEN '高'
WHEN e.salary >= 3000 THEN '中'
ELSE '低'
END AS salary_level
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
-- 使用视图
SELECT * FROM v_emp_full_info WHERE salary_level = '高';
5.2 权限控制
场景:不同用户看到不同的数据。
sql
-- 管理员视图(所有信息)
CREATE VIEW v_emp_admin AS
SELECT * FROM emp;
-- 普通员工视图(不包含工资)
CREATE VIEW v_emp_public AS
SELECT empno, ename, job, deptno FROM emp;
-- 授权
GRANT SELECT ON v_emp_admin TO 'admin'@'localhost';
GRANT SELECT ON v_emp_public TO 'user'@'localhost';
5.3 数据格式化
场景:统一数据展示格式。
sql
-- 创建视图:格式化日期和金额
CREATE VIEW v_emp_formatted AS
SELECT
empno,
ename,
CONCAT('¥', FORMAT(salary, 2)) AS salary_formatted,
DATE_FORMAT(hiredate, '%Y年%m月%d日') AS hire_date_formatted
FROM emp;
-- 查询
SELECT * FROM v_emp_formatted;
输出:
bash
+-------+------+------------------+---------------------+
| empno | ename| salary_formatted | hire_date_formatted |
+-------+------+------------------+---------------------+
| 1001 | 张三 | ¥5,000.00 | 2020年01月15日 |
+-------+------+------------------+---------------------+
5.4 数据汇总
场景:为报表系统提供汇总数据。
sql
-- 创建视图:部门统计
CREATE VIEW v_dept_statistics AS
SELECT
d.dname AS 部门名称,
COUNT(e.empno) AS 员工数,
AVG(e.salary) AS 平均工资,
MAX(e.salary) AS 最高工资,
MIN(e.salary) AS 最低工资
FROM dept d
LEFT JOIN emp e ON d.deptno = e.deptno
GROUP BY d.deptno, d.dname;
-- 查询
SELECT * FROM v_dept_statistics;
输出:
bash
+--------+------+--------+--------+--------+
| 部门名称| 员工数| 平均工资| 最高工资| 最低工资|
+--------+------+--------+--------+--------+
| 财务部 | 2 | 3500.00| 5000.00| 2000.00|
| 研发部 | 2 | 4250.00| 4500.00| 4000.00|
| 销售部 | 1 | 3000.00| 3000.00| 3000.00|
+--------+------+--------+--------+--------+
六、视图的规则和限制
6.1 命名规则
- 视图名必须唯一,不能与表名重复。
- 建议使用
v_前缀,便于区分。
sql
-- 好的命名
CREATE VIEW v_emp_dept AS ...
CREATE VIEW v_sales_summary AS ...
-- 不好的命名
CREATE VIEW emp AS ... -- 与表名冲突
CREATE VIEW view1 AS ... -- 不够清晰
6.2 性能考虑
- 视图不存储数据,每次查询都执行定义的 SQL。
- 复杂视图(多表连接、子查询)会影响性能。
- 视图不能添加索引。
建议:
- 避免在视图中使用复杂的子查询。
- 对于频繁查询的复杂视图,考虑使用物化视图(MySQL 8.0+)。
6.3 ORDER BY 的使用
- 视图定义中可以包含 ORDER BY。
- 但如果查询视图时也使用 ORDER BY,视图中的 ORDER BY 会被覆盖。
sql
-- 视图定义中有 ORDER BY
CREATE VIEW v_emp_sorted AS
SELECT * FROM emp ORDER BY salary DESC;
-- 查询时使用 ORDER BY,视图中的排序被覆盖
SELECT * FROM v_emp_sorted ORDER BY ename;
6.4 其他限制
| 限制 | 说明 |
|---|---|
| 不能添加索引 | 视图是虚拟表,无法创建索引 |
| 不能有触发器 | 视图不支持触发器 |
| 不能有默认值 | 视图列不能设置默认值 |
| 数量无限制 | 但要考虑性能影响 |
| 需要权限 | 创建视图需要 CREATE VIEW 权限 |
七、视图的最佳实践
7.1 何时使用视图
✅ 适合使用视图:
- 简化复杂查询。
- 提供不同的数据视角。
- 保护敏感数据。
- 保持接口稳定。
❌ 不适合使用视图:
- 需要高性能的查询。
- 需要频繁修改数据。
- 查询逻辑简单。
7.2 视图命名规范
sql
-- 推荐的命名方式
v_emp_dept -- 员工部门视图
v_sales_summary -- 销售汇总视图
v_user_public -- 用户公开信息视图
7.3 视图文档化
sql
-- 在视图定义中添加注释
CREATE VIEW v_emp_dept AS
-- 查询员工及其部门信息
-- 用于简化常用的员工-部门关联查询
SELECT
e.empno,
e.ename,
d.dname,
d.loc
FROM emp e
JOIN dept d ON e.deptno = d.deptno;
7.4 定期维护
sql
-- 查看所有视图
SELECT TABLE_NAME, VIEW_DEFINITION
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA = 'your_database';
-- 删除不再使用的视图
DROP VIEW IF EXISTS v_old_view;
八、总结
现在你已经掌握了:
✅ 视图的概念:虚拟表,不存储数据
✅ 视图的创建:CREATE VIEW、CREATE OR REPLACE VIEW
✅ 视图的查询:与表的查询方式相同
✅ 视图的修改:ALTER VIEW、DROP VIEW
✅ 视图与基表的关系:双向影响
✅ 可更新视图:哪些视图可以修改,哪些不可以
✅ 应用场景:简化查询、权限控制、数据格式化、数据汇总
✅ 规则和限制:命名、性能、ORDER BY、其他限制
✅ 最佳实践:何时使用、命名规范、文档化、维护
8.1 快速决策表
| 场景 | 是否使用视图 | 原因 |
|---|---|---|
| 复杂的多表连接 | ✅ 使用 | 简化查询 |
| 需要隐藏敏感数据 | ✅ 使用 | 提高安全性 |
| 频繁修改的数据 | ❌ 不使用 | 性能问题 |
| 简单的单表查询 | ❌ 不使用 | 没有必要 |
| 需要索引的查询 | ❌ 不使用 | 视图不能加索引 |
8.2 核心要点
视图的本质:
- 视图是存储的 SELECT 语句。
- 查询视图时,MySQL 执行这个 SELECT。
- 视图不占用数据存储空间。
视图的优势:
- 简化复杂查询。
- 提高数据安全性。
- 保持接口稳定性。
视图的劣势:
- 性能可能较差。
- 不能添加索引。
- 有些视图不可更新。