视图
虚拟表 / 封装复杂查询 / 数据安全 / 可更新视图
什么是视图
一句话:视图是一个保存起来的 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 表里对应的行但以下情况不可更新:
- 视图里有
JOIN、UNION、DISTINCT - 视图里有聚合函数(
SUM、AVG等) - 视图里有
GROUP BY、HAVING - 视图引用了子查询
💡 视图主要用来简化查询,不推荐通过视图改数据。搞清楚哪些能改哪些不能改的成本,不如直接操作原表。
实战:一步步封装
假设你常需要查这样一个报表:
"各城市下单最多的产品是什么?(附带城市人数和总订单数)"
这个查询写一次就行,封装成视图:
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,一次定义,反复调用:
- 简化复杂查询 — 把多表联查、聚合统计封装成视图,以后一句
SELECT * FROM 视图搞定 - 隐藏敏感列 — 建不含工资等敏感字段的视图,给不同人分不同权限
- 屏蔽表结构变化 — 表拆了、改名了?改视图定义就好,上层代码无感知
- 视图不是真表 — 不存数据,只是存了 SELECT 语句的定义,查的时候实时执行
- 可更新视图有条件 — 涉及 JOIN、聚合、DISTINCT 的视图不能 UPDATE/DELETE
💡 视图是"偷懒神器"——复杂查询写一次,封装成视图,以后一句话调用。
自主练习
- 创建一个视图
high_salary(工资 > 10000 的人,只显示 name 和 city) - 创建刚才的
user_order_stats视图(每人名字和订单数),查询订单数最多的前 3 人 - 用
SHOW CREATE VIEW看看第 1 题创建的视图定义 - 试试通过
high_salary视图更新某人的工资,能成功吗?
视图是"偷懒神器"——一次定义,反复使用。复杂查询写一次,封装成视图,以后一句话调用。