视图

0x01 视图简介

视图(View)是一个虚拟表,其内容由查询定义。视图不存储实际数据,而是存储一个查询定义,每次访问视图时执行该查询。

-- 视图的本质是一个保存的查询
-- 视图 = 存储的查询定义

0x02 创建视图

基本语法

-- MySQL / PostgreSQL
CREATE VIEW view_name AS
SELECT column1, column2, ...
FROM table_name
WHERE condition;

示例

-- 创建用户订单视图
CREATE VIEW user_orders_view AS
SELECT 
    u.id AS user_id,
    u.name AS user_name,
    o.order_id,
    o.total_amount,
    o.created_at
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed';

-- 创建销售统计视图
CREATE VIEW monthly_sales AS
SELECT 
    DATE_TRUNC('month', order_date) AS month,
    COUNT(*) AS order_count,
    SUM(total_amount) AS total_sales
FROM orders
GROUP BY DATE_TRUNC('month', order_date);

0x03 查询视图

-- 查询视图与查询表完全相同
SELECT * FROM user_orders_view;

-- 带条件查询
SELECT * FROM user_orders_view 
WHERE user_id = 1;

-- 视图可以与其他表 JOIN
SELECT v.*, p.product_name
FROM user_orders_view v
JOIN order_items oi ON v.order_id = oi.order_id
JOIN products p ON oi.product_id = p.id;

0x04 查看视图信息

-- MySQL: 查看视图定义
SHOW CREATE VIEW view_name;

-- PostgreSQL: 查看视图定义
SELECT pg_get_viewdef('view_name', true);

-- 查看所有视图
-- MySQL
SELECT TABLE_NAME 
FROM INFORMATION_SCHEMA.VIEWS 
WHERE TABLE_SCHEMA = 'database_name';

-- PostgreSQL
SELECT tablename 
FROM pg_tables 
WHERE schemaname = 'public' 
AND tablename LIKE '%\_view' ESCAPE '\';

0x05 修改视图

创建或替换视图

-- MySQL 8.0+
CREATE OR REPLACE VIEW view_name AS
SELECT ...
-- PostgreSQL
CREATE OR REPLACE VIEW view_name AS
SELECT ...

使用 ALTER 修改

-- PostgreSQL 支持 ALTER VIEW
ALTER VIEW view_name 
ALTER COLUMN column_name TYPE new_type;

0x06 删除视图

-- 删除视图
DROP VIEW view_name;

-- 如果存在则删除(MySQL 8.0+ / PostgreSQL)
DROP VIEW IF EXISTS view_name;

-- 删除多个视图
DROP VIEW view1, view2, view3;

0x07 视图的分类

简单视图

基于单个表创建的视图,不包含聚合函数或分组。

CREATE VIEW simple_users AS
SELECT id, name, email
FROM users
WHERE status = 'active';

复杂视图

包含聚合函数、JOIN、分组等复杂查询的视图。

CREATE VIEW complex_sales AS
SELECT 
    u.name,
    COUNT(o.id) AS order_count,
    SUM(o.total_amount) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

可更新视图

可以通过视图更新底层数据(需要满足特定条件)。

-- 可更新视图的条件:
-- 1. 不包含聚合函数
-- 2. 不包含 DISTINCT
-- 3. 不包含 GROUP BY / HAVING
-- 4. 不包含 UNION
-- 5. 视图的每列对应底层表的列

-- 通过视图更新数据
CREATE VIEW updatable_user AS
SELECT id, name, email
FROM users
WHERE status = 'active';

UPDATE updatable_user 
SET email = 'new@email.com' 
WHERE id = 1;

0x08 物化视图

物化视图(Materialized View)存储实际数据,定期刷新。

PostgreSQL 物化视图

-- 创建物化视图
CREATE MATERIALIZED VIEW monthly_sales_mv AS
SELECT 
    DATE_TRUNC('month', order_date) AS month,
    COUNT(*) AS order_count,
    SUM(total_amount) AS total_sales
FROM orders
GROUP BY DATE_TRUNC('month', order_date);

-- 刷新物化视图
REFRESH MATERIALIZED VIEW monthly_sales_mv;

-- 增量刷新(如果使用了 CONCURRENTLY)
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales_mv;

-- 查看物化视图
SELECT * FROM monthly_sales_mv;

MySQL 物化视图

MySQL 不原生支持物化视图,可通过触发器或定时任务模拟。

0x09 视图的优缺点

优点

-- 1. 简化复杂查询
-- 多次使用的复杂查询可以保存为视图
CREATE VIEW complex_report AS
SELECT ... FROM multiple_tables WHERE ...;

-- 2. 数据安全
-- 只暴露需要的列,隐藏敏感数据
CREATE VIEW safe_user_info AS
SELECT id, name, email
FROM users;  -- 不包含 password, phone 等敏感字段

-- 3. 数据抽象
-- 改变底层表结构不影响应用,只需修改视图定义

-- 4. 代码复用
-- 多个模块使用相同查询逻辑,通过视图统一管理

缺点

-- 1. 性能问题
-- 每次查询视图都会执行底层查询,大数据量时可能慢

-- 2. 更新限制
-- 并非所有视图都可更新

-- 3. 依赖关系
-- 底层表结构变更可能影响视图,需要同步维护

0x10 视图的最佳实践

-- 1. 为视图命名添加约定前缀
CREATE VIEW v_user_active AS ...;
CREATE VIEW mv_monthly_sales AS ...;  -- 物化视图

-- 2. 添加注释说明视图用途
COMMENT ON VIEW v_user_active IS '活跃用户列表,用于前端展示';

-- 3. 避免嵌套视图(视图依赖视图)
-- 嵌套视图会导致性能问题和维护困难

-- 4. 定期检查视图定义是否仍然需要
-- 清理不再使用的视图

-- 5. 物化视图用于报表和统计
-- 适合变化不频繁但查询频繁的场景

参考