【MySQL】视图:虚拟表的妙用

文章目录

视图:虚拟表的妙用

一、前言

💬 这一篇讲什么: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。
  • 视图不占用数据存储空间。

视图的优势

  • 简化复杂查询。
  • 提高数据安全性。
  • 保持接口稳定性。

视图的劣势

  • 性能可能较差。
  • 不能添加索引。
  • 有些视图不可更新。
相关推荐
Cosolar1 小时前
2026年向量数据库选型指南:Qdrant、Pinecone、Milvus、Weaviate 与 Chroma 深度解析
数据库·面试·llm
m0_747854522 小时前
如何为禁用按钮点击添加提示文案
jvm·数据库·python
谁怕平生太急2 小时前
面试题记录:在线数据迁移
java·数据库·spring
aXin_ya2 小时前
Redis 原理篇 (数据结构)
数据库·redis·缓存
2301_803538952 小时前
CSS如何设计简洁的移动端底部固定导航_利用position-fixed实现
jvm·数据库·python
vegetablec2 小时前
CSS如何制作卡片翻开呈现另一面的翻牌动画
jvm·数据库·python
吕源林2 小时前
Golang怎么Redis发布订阅_Golang如何用Publish和Subscribe收发消息【实战】
jvm·数据库·python
redreamSo2 小时前
Turso:用 Rust 重写 SQLite,让数据库跑在每一个边缘节点
数据库·rust·sqlite
2301_764150562 小时前
Golang colly爬虫框架如何用_Golang colly教程【进阶】
jvm·数据库·python