17 MySQL 视图

MySQL 视图

1. 视图是什么

视图就是 虚拟表,本身不存数据,它只是把一条或多条 SQL 查询封装起来,同真实的表一样包含一系列带有名称的列和行数据,用它就像操作表一样,简单理解:视图就是个存好的 SELECT 语句。 举个例子:有个员工表 employees,只想经常查销售部的人,可以建个视图 sales_staff

1
CREATE VIEW sales_staff AS SELECT id, name, department FROM employees WHERE department = 'Sales';

这样一来,查 sales_staff 就只会看到销售部员工。

2. 视图的作用

  1. 简化复杂查询: 可以将多表关联、复杂的过滤条件封装成一个视图,用户只需查询视图即可,无需重复编写复杂的 SQL。
  2. 安全性: 可以通过视图限制用户访问表中的敏感列(如密码、薪资)或特定行。用户只能看到视图定义中允许的数据,起到了字段级和行级的权限控制作用。
  3. 逻辑独立性: 如果应用层直接通过视图获取数据,当底层表结构发生变化时(如字段名修改、表拆分),只需要修改视图定义,不用修改应用代码。
  4. 数据一致性: 可以强制实施特定的业务逻辑视图,确保不同用户看到的数据格式或筛选逻辑是一致的。

3. 创建视图

语法:

1
2
3
4
5
6
7
8
9
10
11
CREATE VIEW 视图名 AS SELECT 语句;
CREATE [OR REPLACE] VIEW 视图名 [(列名列表)] AS SELECT ... [WITH [CASCADED | LOCAL] CHECK OPTION];

# 例1
CREATE VIEW active_users AS SELECT id, name FROM users WHERE status = 'active' WITH CHECK OPTION;
# 例2
CREATE VIEW v_dept_emp_cross AS
SELECT
d.deptno AS d_deptno, d.dname, d.loc,
e.empno, e.ename, e.job, e.mgr, e.hiredate, e.sal, e.comm, e.deptno AS e_deptno
FROM dept d, emp e;
  • OR REPLACE:如果视图存在就覆盖。
  • WITH CHECK OPTION:保证通过视图插入/更新的数据仍符合视图条件。

4. 视图的查看和使用

查看已有的视图:

1
2
3
4
5
6
7
SELECT TABLE_NAME FROM information_schema.views WHERE TABLE_SCHEMA = '你的数据库名';	# 查某个库里有哪些视图
mysql> SHOW FULL TABLES WHERE TABLE_TYPE = 'VIEW'; # 注意:先 USE 库名; 选对数据库
+--------------------------+------------+
| Tables_in_Compound_query | Table_type |
+--------------------------+------------+
| v_dept_emp_cross | VIEW |
+--------------------------+------------+

查看视图定义:

1
2
SHOW CREATE VIEW 视图名;		# 能看到当初写的 SELECT 语句
mysql> show create view v_dept_emp_cross;

查看字段结构:

1
2
DESC 视图名;
mysql> desc v_dept_emp_cross;

运行视图查数据:就当它是个表,想加 WHERE、ORDER BY 随便加。

1
2
SELECT * FROM 视图名;
mysql> SELECT * FROM v_dept_emp_cross;

5. 修改视图(定义)

MySQL 里 没专门的 ALTER VIEW 改 SELECT,一般用 CREATE OR REPLACE VIEW 来改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE OR REPLACE VIEW active_users AS
SELECT ...
FROM users WHERE status = 'active';

mysql> CREATE OR REPLACE VIEW v_dept_emp_cross AS
-> SELECT
-> d.deptno AS d_deptno,
-> d.dname,
-> d.loc,
-> e.empno,
-> e.ename,
-> e.job,
-> e.mgr,
-> e.hiredate,
-> e.sal,
-> e.comm,
-> e.deptno AS e_deptno
-> FROM dept d
-> JOIN emp e ON d.deptno = e.deptno;

1. 修改视图会不会影响基表?

  • 基表(Base Table):实际存数据的表。比如 employeesusers 这些表才有真正的数据。
  • 视图(View):虚拟表,不存数据,只是一个查询包装。它像个窗口,通过它看到基表的数据或者一部分数据。

会,但有严格限制。只有“可更新视图”才允许通过视图修改基表数据。 可更新视图必须同时满足以下条件(缺一不可):

  • 基于单张基表(不能 JOIN、多表)
  • 视图定义中没有 DISTINCT、GROUP BY、HAVING、聚合函数(SUM/COUNT/AVG 等)、UNION/UNION ALL
  • 没有 LIMIT / OFFSET
  • 没有子查询出现在 FROM 子句(派生表)(但 WHERE 中的 IN/EXISTS 子查询通常允许)
  • 视图的每一列都直接对应基表中的某一列(不能是表达式、常量、函数计算结果、别名计算列等)
  • 没有使用 WITH 语句中的递归 CTE

满足以上条件 → 通过视图的 INSERT/UPDATE/DELETE 会直接翻译成对基表的对应操作。

2. 修改基表会影响视图吗?

会,而且是实时的、必然的。

  • 数据层面:基表数据增删改,视图查询结果立刻反映最新内容(视图不存数据,只是保存的 SELECT)
  • 结构层面:基表发生以下变化,视图大概率直接失效(查询时报错 ERROR 1356):
    • 删除视图中用到的列
    • 修改/重命名视图中用到的列名
    • 改变列的数据类型(有时可以,但不保证兼容)
    • 删表、改表结构导致 SELECT 无法执行

修复方式:DROP VIEW + 重新 CREATE,或用 CREATE OR REPLACE VIEW 重写。

3. WITH CHECK OPTION

WITH CHECK OPTION 的作用就是:既然通过这个视图看数据,那就不能把数据改成“自己看不见”的样子。

场景模拟:低薪员工视图

假设创建了一个视图 low_salary,专门用来管理“工资低于 5000”的员工。

1
2
3
CREATE VIEW low_salary AS
SELECT * FROM employees WHERE salary < 5000
WITH CHECK OPTION; # 注意这句,这就是“安全锁”

这个视图就像一个 “低薪员工房间”,只有工资 < 5000 的人才能住进去。

情况 1:没加 WITH CHECK OPTION(没上锁)

在这个房间里(视图里)想把员工张三的工资改成 8000。

  1. 执行UPDATE low_salary SET salary = 8000 WHERE name = '张三';
  2. MySQL 反应:好的,基表数据改成了 8000。
  3. 结果:改成功了,但是!因为张三工资变成了 8000,他不再符合“工资 < 5000”的条件,张三瞬间从这个视图里 消失 了!
  4. 这感觉就像自己在这个房间里给人发钱,发完之后他有钱了,直接穿墙走了,再也看不到他了。这很容易造成数据管理混乱(比如我们以为是给低薪员工涨工资,结果涨完他跑了,自己都没确认修改成功)。

情况 2:加了 WITH CHECK OPTION(上了锁)

在这个房间里想把员工张三的工资改成 8000。

  1. 执行UPDATE low_salary SET salary = 8000 WHERE name = '张三';
  2. MySQL 反应:等等!你要把他改成 8000?那他就不符合这个视图的条件了(8000 不小于 5000)。既然要在这个“低薪视图”里操作,就不能把人改成“非低薪”。
  3. 结果直接报错!操作被拒绝,基表数据没变,张三还在视图里。
  4. 就像自己在房间里想给人发钱,门口保安拦住我说:“这里只接待低薪员工,如果要给他涨到 8000,请去外面(基表或其他视图)操作,别在这里改,改了他就不属于这儿了。”

6. 删除视图

1
2
3
4
5
DROP VIEW [IF EXISTS] view_name [, view_name] ...;

mysql> drop view v_dept_emp_cross;
mysql> SHOW FULL TABLES WHERE TABLE_TYPE = 'VIEW';
Empty set (0.00 sec)

7. 视图规则和限制(小结)

视图就是个存好的 SELECT 语句,本身不存数据。简单视图能增删改(基表会跟着变),但复杂视图(带 JOIN、GROUP BY、聚合函数这些)只能查不能改。视图里列名不能重复,也不能搞循环依赖(A 视图用 B 视图,B 视图又用 A 视图)。基表要是删了列或者改了列名,视图直接报错。想限制通过视图改数据时别改出视图范围,就加个 WITH CHECK OPTION