讲解
视图(VIEW)是保存在数据库里的命名查询:CREATE VIEW v AS SELECT ...,之后可以像表一样 SELECT * FROM v。视图本身不存数据,每次查询视图都实时执行背后的 SELECT。它的价值主要有二:一是简化——把五张表连接加聚合的复杂查询封装成一个简单名字,业务代码只管查 v_user_spending;二是收口——给外部系统或低权限账号只开放视图,把敏感列(手机号、身份证)和敏感行挡在视图定义之外。
满足一定条件的视图可以直接 INSERT/UPDATE/DELETE(叫可更新视图):基于单表、不含聚合、不含 DISTINCT/GROUP BY/HAVING/子查询等。WITH CHECK OPTION 子句给可更新视图加一道闸:通过视图写入的行必须满足视图的 WHERE 条件,否则报错——「北京用户视图」里不允许插入上海的用户,防止数据从视图里「漏出去」。
注意 MySQL 没有物化视图(PostgreSQL、Oracle 有)。需要「查询结果落盘、定时刷新」的场景,工程上的替代方案是建一张汇总表,再用事件调度器(后面的章节会讲)定时重算写入。对报表类需求这是标准做法。
示例
两张基础表上加一个「用户消费汇总」视图,复杂连接和聚合被封装成一次简单查询:
CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), city VARCHAR(20));
CREATE TABLE orders (id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), status VARCHAR(20));
INSERT INTO users (name, city) VALUES ('李明', '北京'), ('王芳', '上海'), ('张伟', '北京');
INSERT INTO orders (user_id, amount, status) VALUES (1, 99.00, 'paid'), (1, 29.00, 'paid'), (2, 59.00, 'pending'), (3, 199.00, 'paid');
CREATE VIEW v_user_spending AS
SELECT u.id, u.name, u.city, COUNT(o.id) AS paid_orders, COALESCE(SUM(o.amount), 0) AS total_spent
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id AND o.status = 'paid'
GROUP BY u.id, u.name, u.city;
SELECT * FROM v_user_spending ORDER BY total_spent DESC;
带 WITH CHECK OPTION 的可更新视图:通过它插入北京用户成功;视图清单可以用 SHOW FULL TABLES 查看:
CREATE VIEW v_beijing_users AS
SELECT id, name, city FROM users WHERE city = '北京'
WITH CHECK OPTION;
INSERT INTO v_beijing_users (name, city) VALUES ('赵磊', '北京');
SELECT * FROM v_beijing_users;
SHOW FULL TABLES WHERE Table_type = 'VIEW';
清理视图,并从 information_schema 确认视图已删除:
DROP VIEW IF EXISTS v_beijing_users;
DROP VIEW IF EXISTS v_user_spending;
SELECT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA = DATABASE();
常见坑
- 视图层层嵌套:视图套视图再套视图,排查问题时像剥洋葱,优化器也未必能展开,性能越来越差。视图引用视图要克制。
- 以为视图存数据:视图是查询不是快照,基表删了行视图立刻就查不到。需要「定时的快照」用汇总表方案,别指望视图。
- 对不可更新视图写入:含聚合、DISTINCT、GROUP BY 的视图直接 UPDATE 会报 1288。先确认视图定义是否满足可更新条件。
- 忘记 CHECK OPTION 的语义:通过视图插入不满足 WHERE 条件的行会报 1369,加了这个选项的程序要捕获这个错误而不是当系统故障。
小结
视图是命名查询,用于简化复杂 SQL 和按列按行收口权限;可更新视图配 WITH CHECK OPTION 防止越界写入;MySQL 无物化视图,用汇总表替代。下一章是库内逻辑的重头戏:存储过程与函数。