MySQL 视图
视图(View)是一个虚拟表,其内容由查询定义。本章将详细介绍视图的创建、使用和最佳实践。
什么是视图?
视图是一个虚拟表,它基于 SQL 查询结果构建,不存储实际数据:
┌─────────────────────────────────────────────────────────────┐
│ 视图的概念 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 物理表 视图 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ users │ │ v_users │ │
│ │─────────────│ │─────────────│ │
│ │ id │ ────> │ id │ │
│ │ name │ 查询 │ name │ │
│ │ email │ │ email │ │
│ │ password │ │ status │ │
│ │ status │ └─────────────┘ │
│ └─────────────┘ (不包含密码) │
│ │
│ 特点: │
│ 1. 视图不存储数据,每次查询时动态生成 │
│ 2. 视图可以简化复杂查询 │
│ 3. 视图可以隐藏敏感数据 │
│ 4. 视图可以提供数据独立性 │
│ │
└─────────────────────────────────────────────────────────────┘
视图的优点
| 优点 | 说明 |
|---|---|
| 简化查询 | 封装复杂 SQL,简化调用 |
| 数据安全 | 隐藏敏感列,控制数据访问 |
| 逻辑独立 | 表结构变化不影响应用 |
| 数据一致性 | 统一的数据访问入口 |
视图的缺点
| 缺点 | 说明 |
|---|---|
| 性能影响 | 每次查询都需要执行定义的 SQL |
| 更新限制 | 不是所有视图都支持更新 |
| 调试困难 | 视图嵌套时难以排查问题 |
创建视图
基本语法
CREATE VIEW 视图名 AS
SELECT 语句;
创建简单视图
-- 创建用户基本信息视图(隐藏密码等敏感信息)
CREATE VIEW v_user_info AS
SELECT
id,
username,
email,
status,
created_at
FROM users;
-- 使用视图
SELECT * FROM v_user_info;
-- 带条件的查询
SELECT * FROM v_user_info WHERE status = 'active';
创建带别名的视图
CREATE VIEW v_user_summary (
user_id,
user_name,
order_count,
total_amount
) AS
SELECT
u.id,
u.username,
COUNT(o.id),
COALESCE(SUM(o.total_amount), 0)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;
SELECT * FROM v_user_summary;
创建多表关联视图
-- 创建订单详情视图
CREATE VIEW v_order_detail AS
SELECT
o.id AS order_id,
o.order_no,
u.username,
u.email,
o.product_name,
o.quantity,
o.price,
o.total_amount,
o.status,
o.created_at
FROM orders o
INNER JOIN users u ON o.user_id = u.id;
-- 使用视图查询
SELECT * FROM v_order_detail WHERE status = 'completed';
创建计算视图
CREATE VIEW v_user_statistics AS
SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent,
AVG(o.total_amount) AS avg_order_amount,
MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;
SELECT * FROM v_user_statistics WHERE order_count > 5;
创建条件视图
-- VIP 用户视图
CREATE VIEW v_vip_users AS
SELECT
id,
username,
email,
salary
FROM users
WHERE salary > 15000
WITH CHECK OPTION; -- 确保插入/更新符合条件
-- 活跃订单视图
CREATE VIEW v_active_orders AS
SELECT * FROM orders
WHERE status IN ('pending', 'paid', 'shipped');
OR REPLACE 选项
-- 如果视图存在则替换
CREATE OR REPLACE VIEW v_user_info AS
SELECT
id,
username,
email,
age,
status,
created_at
FROM users;
查看视图
查看视图定义
-- 查看视图结构
DESC v_user_info;
-- 查看视图定义
SHOW CREATE VIEW v_user_info;
-- 从 information_schema 查看
SELECT
TABLE_NAME,
VIEW_DEFINITION,
CHECK_OPTION,
IS_UPDATABLE
FROM information_schema.VIEWS
WHERE TABLE_SCHEMA = 'your_database';
查看所有视图
-- 查看数据库中的所有视图
SELECT
TABLE_NAME,
TABLE_TYPE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database'
AND TABLE_TYPE = 'VIEW';
-- 使用 SHOW 命令
SHOW FULL TABLES WHERE Table_type = 'VIEW';
修改视图
使用 ALTER VIEW
ALTER VIEW v_user_info AS
SELECT
id,
username,
email,
phone,
status,
created_at,
updated_at
FROM users;
使用 CREATE OR REPLACE
CREATE OR REPLACE VIEW v_user_info AS
SELECT
id,
username,
email,
phone,
status,
salary,
created_at
FROM users;
删除视图
-- 删除视图
DROP VIEW IF EXISTS v_user_info;
-- 删除多个视图
DROP VIEW IF EXISTS v_user_info, v_order_detail;
可更新视图
可更新视图的条件
视图必须满足以下条件才能进行 INSERT、UPDATE、DELETE 操作:
- 没有聚合函数(SUM、COUNT、AVG 等)
- 没有 DISTINCT、GROUP BY、HAVING
- 没有 UNION、UNION ALL
- 没有子查询(FROM 子句中)
- 没有常量列
- 视图中的列都来自一个表
更新视图示例
-- 创建可更新视图
CREATE VIEW v_active_users AS
SELECT id, username, email, status
FROM users
WHERE status = 'active';
-- 通过视图更新数据
UPDATE v_active_users
SET email = '[email protected]'
WHERE id = 1;
-- 通过视图插入数据
INSERT INTO v_active_users (id, username, email, status)
VALUES (100, '新用户', '[email protected]', 'active');
-- 通过视图删除数据
DELETE FROM v_active_users WHERE id = 100;
WITH CHECK OPTION
确保通过视图修改的数据仍然满足视图条件:
-- 创建带检查选项的视图
CREATE VIEW v_high_salary_users AS
SELECT id, username, email, salary
FROM users
WHERE salary > 10000
WITH CHECK OPTION;
-- 更新成功(满足条件)
UPDATE v_high_salary_users
SET salary = 15000
WHERE id = 1;
-- 更新失败(不满足条件)
UPDATE v_high_salary_users
SET salary = 5000
WHERE id = 1;
-- ERROR: CHECK OPTION failed
-- 插入失败(不满足条件)
INSERT INTO v_high_salary_users (id, username, email, salary)
VALUES (101, 'test', '[email protected]', 5000);
-- ERROR: CHECK OPTION failed
检查选项类型
-- CASCADED:检查所有底层视图条件(默认)
CREATE VIEW v_view1 AS
SELECT * FROM users WHERE salary > 5000
WITH CHECK OPTION;
CREATE VIEW v_view2 AS
SELECT * FROM v_view1 WHERE salary < 20000
WITH CHECK OPTION; -- 同时检查两个条件
-- LOCAL:只检查当前视图条件
CREATE VIEW v_view3 AS
SELECT * FROM v_view1 WHERE salary < 20000
WITH LOCAL CHECK OPTION; -- 只检查当前条件
视图应用场景
1. 简化复杂查询
-- 复杂的多表查询
CREATE VIEW v_product_sales AS
SELECT
p.id AS product_id,
p.name AS product_name,
c.name AS category_name,
COUNT(o.id) AS sales_count,
SUM(o.quantity) AS total_quantity,
SUM(o.total_amount) AS total_revenue
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
LEFT JOIN order_items oi ON p.id = oi.product_id
LEFT JOIN orders o ON oi.order_id = o.id AND o.status = 'completed'
GROUP BY p.id, p.name, c.name;
-- 简单查询
SELECT * FROM v_product_sales
WHERE total_revenue > 10000
ORDER BY total_revenue DESC;
2. 数据安全
-- 只暴露部分数据给特定用户
CREATE VIEW v_public_users AS
SELECT
id,
username,
-- 不包含 email 和手机号
status,
created_at
FROM users;
-- 给只读用户授权视图访问
GRANT SELECT ON mydb.v_public_users TO 'readonly'@'%';
-- 不授权原始表访问
3. 数据抽象
-- 隐藏表结构变化
-- 假设原来 users 表拆分为 users_profile 和 users_auth
CREATE VIEW v_users AS
SELECT
p.id,
p.username,
p.email,
p.phone,
a.status,
a.last_login,
p.created_at
FROM users_profile p
INNER JOIN users_auth a ON p.id = a.user_id;
-- 应用程序无需修改,继续使用 v_users
4. 报表视图
-- 日报表视图
CREATE VIEW v_daily_report AS
SELECT
DATE(created_at) AS report_date,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales,
COUNT(DISTINCT user_id) AS unique_customers
FROM orders
WHERE status = 'completed'
GROUP BY DATE(created_at);
SELECT * FROM v_daily_report
WHERE report_date >= '2024-01-01'
ORDER BY report_date DESC;
5. 分区数据视图
-- 假设有按月分表
-- orders_202401, orders_202402, ...
CREATE VIEW v_all_orders AS
SELECT * FROM orders_202401
UNION ALL
SELECT * FROM orders_202402
UNION ALL
SELECT * FROM orders_202403;
SELECT * FROM v_all_orders WHERE user_id = 100;
视图性能优化
视图执行原理
查询流程:
SELECT * FROM v_user_info WHERE id = 1;
1. 解析视图定义
└─> SELECT id, username, email FROM users
2. 合并查询条件
└─> SELECT id, username, email FROM users WHERE id = 1
3. 执行优化后的查询
性能优化建议
-- 1. 在基础表上建立合适的索引
CREATE INDEX idx_users_status ON users(status);
CREATE VIEW v_active_users AS
SELECT id, username, email FROM users WHERE status = 'active';
-- 2. 避免视图嵌套过深
-- 不好
CREATE VIEW v_view1 AS SELECT ...;
CREATE VIEW v_view2 AS SELECT * FROM v_view1;
CREATE VIEW v_view3 AS SELECT * FROM v_view2; -- 嵌套太深
-- 好:直接定义
CREATE VIEW v_final AS SELECT ...;
-- 3. 使用 EXPLAIN 分析视图查询
EXPLAIN SELECT * FROM v_user_info WHERE id = 1;
-- 4. 对于复杂视图,考虑物化视图(MySQL 不直接支持,可用定时任务更新实体表)
CREATE TABLE mv_user_statistics AS
SELECT ...;
-- 定时更新
-- 使用事件或定时任务
视图与临时表
| 特性 | 视图 | 临时表 |
|---|---|---|
| 存储 | 不存储数据 | 存储数据 |
| 更新 | 自动反映源表变化 | 需手动更新 |
| 性能 | 每次查询执行 | 查询快速 |
| 生命周期 | 永久 | 会话结束删除 |
| 索引 | 依赖源表索引 | 可独立建索引 |
小结
本章我们学习了:
- 视图概念:虚拟表,不存储实际数据
- 创建视图:CREATE VIEW、OR REPLACE
- 查看视图:DESC、SHOW CREATE VIEW
- 修改删除:ALTER VIEW、DROP VIEW
- 可更新视图:条件和 WITH CHECK OPTION
- 应用场景:简化查询、数据安全、数据抽象
- 性能优化:索引、避免嵌套、EXPLAIN 分析
练习
- 创建一个视图,展示用户及其订单统计信息
- 创建一个带 WITH CHECK OPTION 的视图,测试更新限制
- 创建一个报表视图,统计每日销售数据
- 分析一个复杂视图的执行计划