Skip to content

视图

虚拟表 / 封装复杂查询 / 数据安全 / 可更新视图

什么是视图

一句话:视图是一个保存起来的 SELECT 语句,看起来像一张表,用起来也像一张表

比如你经常要查"北京员工的姓名、工资、以及他们的订单产品":

sql
SELECT u.name, u.salary, o.product, o.order_date
FROM users AS u
INNER JOIN orders AS o ON u.id = o.user_id
WHERE u.city = '北京';

每次都要写这么长的 SQL,很烦。可以把它包装成一个视图:

sql
CREATE VIEW beijing_orders AS
SELECT u.name, u.salary, o.product, o.order_date
FROM users AS u
INNER JOIN orders AS o ON u.id = o.user_id
WHERE u.city = '北京';

然后直接用:

sql
SELECT * FROM beijing_orders;

一行搞定。视图把复杂的联结、过滤都藏在了背后。

视图本质:

  复杂 SQL                   视图                      你看到的
 ┌──────────────┐        ┌────────────┐           ┌──────────────┐
 │ JOIN + WHERE │  ──▶   │ beijing_   │   ──▶    │ 像一张普通的 │
 │ + GROUP ...  │  包装   │ orders     │   使用    │ 表,直接 SELECT │
 └──────────────┘        └────────────┘           └──────────────┘

视图用来干嘛

1. 简化复杂查询

把常用的联结、子查询、聚合逻辑封装成视图,以后一句话调用:

sql
-- 把"每人下单统计"封装起来
CREATE VIEW user_order_stats AS
SELECT u.id, u.name, COUNT(o.id) AS order_count
FROM users AS u
LEFT JOIN orders AS o ON u.id = o.user_id
GROUP BY u.id, u.name;

-- 用的时候
SELECT * FROM user_order_stats WHERE order_count > 2;

2. 隐藏敏感列

有些列不能给所有人看(比如工资),可以建一个"不含敏感信息"的视图:

sql
CREATE VIEW user_public AS
SELECT id, name, age, city FROM users;   -- 没包含 salary

-- 给别人这个视图的权限,而不是整张 users 表

3. 屏蔽表结构变化

表结构变了?改视图定义就行,使用视图的应用代码不用动:

sql
-- 原来的 users 表拆成了 users 和 user_details
-- 改视图定义适配,外面的人无感知
CREATE VIEW users_full AS
SELECT u.id, u.name, d.phone, d.address
FROM users AS u
LEFT JOIN user_details AS d ON u.id = d.user_id;

视图的规则

  • 视图名在同一库里不能和表重名
  • 视图不能有索引、触发器
  • 视图里套视图是允许的(但别套太多层,性能堪忧)
  • ORDER BY 在视图里可能被忽略(MySQL 视图中的 ORDER BY 不保证生效,除非有 LIMIT)

管理视图

查看视图

sql
SHOW TABLES;             -- 视图也会列出来
SHOW CREATE VIEW 视图名;  -- 看视图定义

删除视图

sql
DROP VIEW beijing_orders;

修改视图

sql
-- 方法一:重新建
DROP VIEW beijing_orders;
CREATE VIEW beijing_orders AS ...;

-- 方法二:替换(不存在就建,存在就覆盖)
CREATE OR REPLACE VIEW beijing_orders AS
SELECT u.name, u.salary, o.product, o.order_date, o.quantity
FROM users AS u
INNER JOIN orders AS o ON u.id = o.user_id
WHERE u.city = '北京';

通常用 CREATE OR REPLACE VIEW 最省事。

可更新视图:能通过视图改数据吗?

如果视图满足条件(只查一张表、没有聚合、没有 DISTINCT 等),可以像操作普通表一样 UPDATE / DELETE:

sql
-- 视图
CREATE VIEW users_shanghai AS
SELECT id, name, age, city FROM users WHERE city = '上海';

-- 通过视图更新
UPDATE users_shanghai SET age = 31 WHERE name = '李四';
-- 实际更新的是 users 表里对应的行

但以下情况不可更新

  • 视图里有 JOINUNIONDISTINCT
  • 视图里有聚合函数(SUMAVG 等)
  • 视图里有 GROUP BYHAVING
  • 视图引用了子查询

💡 视图主要用来简化查询,不推荐通过视图改数据。搞清楚哪些能改哪些不能改的成本,不如直接操作原表。

实战:一步步封装

假设你常需要查这样一个报表:

"各城市下单最多的产品是什么?(附带城市人数和总订单数)"

这个查询写一次就行,封装成视图:

sql
CREATE VIEW city_report AS
SELECT u.city,
       COUNT(DISTINCT u.id) AS people_count,
       COUNT(o.id) AS order_count,
       COUNT(o.id) / COUNT(DISTINCT u.id) AS avg_order
FROM users AS u
LEFT JOIN orders AS o ON u.id = o.user_id
GROUP BY u.city;

以后:

sql
SELECT * FROM city_report ORDER BY avg_order DESC;

三秒出报表。

小结

视图 = 保存起来的 SELECT,一次定义,反复调用:

  1. 简化复杂查询 — 把多表联查、聚合统计封装成视图,以后一句 SELECT * FROM 视图 搞定
  2. 隐藏敏感列 — 建不含工资等敏感字段的视图,给不同人分不同权限
  3. 屏蔽表结构变化 — 表拆了、改名了?改视图定义就好,上层代码无感知
  4. 视图不是真表 — 不存数据,只是存了 SELECT 语句的定义,查的时候实时执行
  5. 可更新视图有条件 — 涉及 JOIN、聚合、DISTINCT 的视图不能 UPDATE/DELETE

💡 视图是"偷懒神器"——复杂查询写一次,封装成视图,以后一句话调用。

自主练习

  1. 创建一个视图 high_salary(工资 > 10000 的人,只显示 name 和 city)
  2. 创建刚才的 user_order_stats 视图(每人名字和订单数),查询订单数最多的前 3 人
  3. SHOW CREATE VIEW 看看第 1 题创建的视图定义
  4. 试试通过 high_salary 视图更新某人的工资,能成功吗?

视图是"偷懒神器"——一次定义,反复使用。复杂查询写一次,封装成视图,以后一句话调用。


评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8