视图

视图(View)是把一条 SELECT 语句存成"虚拟表"——它本身不存数据,查询时动态执行底层 SQL。视图是 SQL 复用查询逻辑、控制权限、抽象表结构变化的利器,在真实项目里非常常用。

1. CREATE VIEW 创建视图

视图本质上就是"命名的 SELECT"。创建后,你可以像查普通表一样查它:

-- 视图(View):把一条 SELECT 语句存成"虚拟表"
-- 视图本身【不存数据】,只在查询时动态执行底层 SQL

-- 基础语法
CREATE VIEW view_name AS
SELECT ...;

-- 示例:创建一个"成年学生"视图
CREATE VIEW adult_students AS
SELECT id, name, age, email
FROM students
WHERE age >= 18;

-- 用法和普通表一样
SELECT * FROM adult_students;
SELECT name, age FROM adult_students WHERE age > 20;

-- 视图可以再被其他视图引用(但层数多了会变慢)
CREATE VIEW adult_active AS
SELECT * FROM adult_students WHERE status = 'active';

关键认知:视图不存数据,每次查询都重新执行底层 SELECT。这意味着视图永远反映最新的底层数据——但也意味着大表上的复杂视图每次查询都很慢。

2. 删除与修改视图

视图的 DDL 操作很安全——删视图不影响底层数据:

-- 删除视图(不影响底层数据)
DROP VIEW adult_students;
DROP VIEW IF EXISTS adult_students;     -- 推荐加 IF EXISTS

-- 修改视图(用 CREATE OR REPLACE 替换,MySQL / PostgreSQL)
CREATE OR REPLACE VIEW adult_students AS
SELECT id, name, age, email, city
FROM students
WHERE age >= 18;

-- MySQL 也有 ALTER VIEW 语法
ALTER VIEW adult_students AS
SELECT id, name FROM students WHERE age >= 18;

-- 查看视图定义(MySQL)
SHOW CREATE VIEW adult_students;

实际项目里推荐用 CREATE OR REPLACE,一条语句搞定创建+修改,不用先 DROP 再 CREATE(避免短暂时间内视图不存在导致业务报错)。

3. 视图的三大用途

视图不是"装饰品",它在真实项目里有明确的应用场景:

-- 视图的核心用途

-- 1. 简化复杂查询(最常见的用途)
-- 团队里常用的"用户订单汇总"查询很复杂,封装成视图
CREATE VIEW user_order_summary AS
SELECT
    u.id,
    u.name,
    COUNT(o.id) AS order_count,
    COALESCE(SUM(o.amount), 0) AS total_spent,
    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.name;

-- 业务方查起来就像查普通表一样简单
SELECT * FROM user_order_summary WHERE total_spent > 1000;

-- 2. 权限控制(只暴露部分列)
GRANT SELECT ON adult_students TO 'public_user';
-- public_user 只能看到成年学生,看不到未成年人

-- 3. 抽象底层表结构变化
-- 表拆分 / 重命名时,只要视图逻辑不变,应用层 SQL 不用改

三大用途速记:(1) 简化复杂查询(把 50 行 SQL 包成一个视图,业务方查起来像查普通表);(2) 权限控制(只暴露部分列给某些用户);(3) 抽象底层表结构变化(表重命名/拆分时,只要视图逻辑不变,应用层 SQL 不用改)。

4. 可更新视图

部分视图可以执行 INSERT / UPDATE / DELETE,操作会反映到底层表。规则是:视图必须直接对应单张表的行(不能含聚合 / JOIN / DISTINCT):

-- 可更新视图(Updatable View)
-- 某些视图可以执行 INSERT / UPDATE / DELETE,会反映到底层表
-- 规则:视图必须【直接对应】单张表的行,不能有聚合/JOIN/ DISTINCT

CREATE VIEW active_students AS
SELECT id, name, age FROM students WHERE status = 'active';

-- 这种视图可以更新(直接映射单表)
INSERT INTO active_students (id, name, age) VALUES (10, '小明', 20);
-- 等价于 INSERT INTO students ...(status 会被设为默认值)

UPDATE active_students SET age = 21 WHERE id = 10;
-- 等价于 UPDATE students SET age = 21 WHERE id = 10

-- ⚠️ 含聚合 / JOIN / DISTINCT / GROUP BY 的视图【不可更新】
CREATE VIEW student_stats AS
SELECT class_id, COUNT(*) AS cnt FROM students GROUP BY class_id;

UPDATE student_stats SET cnt = 30 WHERE class_id = 1;   -- ❌ 报错
-- 数据库无法把"改 cnt"映射到底层表

-- WITH CHECK OPTION:防止插入不符合视图 WHERE 的数据
CREATE VIEW adult_students AS
SELECT * FROM students WHERE age >= 18
WITH CHECK OPTION;
-- INSERT age=15 会报错(不符合视图条件)

含聚合 / JOIN 的视图不可更新——因为数据库无法把"修改一个 COUNT(*)"映射到底层表。WITH CHECK OPTION 防止通过视图插入不符合 WHERE 条件的行。

5. 物化视图(Materialized View)

普通视图每次查询都重算,大表上很慢。物化视图是另一种存在——它真正存储数据,查询时直接读取,性能极高:

-- 物化视图(Materialized View)
-- 和普通视图不同,物化视图【真正存储数据】
-- 适合不经常变化、查询频繁的统计场景

-- PostgreSQL 原生支持
CREATE MATERIALIZED VIEW daily_sales AS
SELECT
    DATE(created_at) AS day,
    SUM(amount) AS total
FROM orders
GROUP BY DATE(created_at);

-- 查询时直接读物化数据,不用重算(秒级)
SELECT * FROM daily_sales WHERE day = '2024-08-05';

-- 缺点:数据不会自动更新,需要手动刷新
REFRESH MATERIALIZED VIEW daily_sales;

-- MySQL 不直接支持物化视图,常见替代方案:
-- 1. 创建实体的"汇总表",用定时任务(CRON / Event)刷新
CREATE TABLE daily_sales_cache (
    day DATE PRIMARY KEY,
    total DECIMAL(10,2)
);
-- 每晚跑:
INSERT INTO daily_sales_cache
SELECT DATE(created_at), SUM(amount) FROM orders
WHERE DATE(created_at) = CURDATE() - INTERVAL 1 DAY
ON DUPLICATE KEY UPDATE total = VALUES(total);

-- 2. 用触发器实时维护(写入多则慎用,影响写性能)

物化视图的代价是数据不实时——需要手动 REFRESH,或定时任务刷新。它适合"统计结果变化慢、查询频繁"的场景(日报、周报、排行榜)。MySQL 不直接支持,常见替代是建实体汇总表 + 定时任务维护。

6. 视图 vs 表:什么时候用哪个?

选视图还是选表,看需求性质:

-- 视图 vs 表:什么时候用哪个?

-- 用【表】的场景:
-- 1. 存储真实业务数据
-- 2. 需要建索引优化查询
-- 3. 高频写入(INSERT / UPDATE)
-- 4. 数据量大且查询复杂

-- 用【视图】的场景:
-- 1. 简化团队常用的复杂查询
-- 2. 给不同角色暴露不同列(权限)
-- 3. 底层表结构变化时保护应用层
-- 4. 报表查询(配合物化视图或缓存表)

-- ⚠️ 视图的性能陷阱
-- 1. 视图是"语法糖",每次查询都重算
-- 2. 嵌套视图(视图套视图)可能极慢,优化困难
-- 3. 大表上的聚合视图,每次查都全表扫描
-- 这种情况改用【物化视图】或【汇总表】

一个经验法则:能用视图就用视图(零数据冗余、永远最新),但性能不够时改用物化视图或汇总表。两者配合使用是日常。

视图的优缺点总结

恭喜,SQL 系列完结!

这是 SQL 系列 12 篇的最后一篇。回过头看,你已经走完了完整的学习路径:

掌握了这些,你已经能独立设计数据库表结构、写出复杂的多表统计查询、用索引优化慢查询、用事务保证数据一致性。SQL 是为数不多"学了能用一辈子"的技能,核心概念四十多年几乎没变——无论编程语言怎么迭代,你的 SQL 能力都会持续增值。

下一步建议:多刷题(LeetCode Database、SQLZOO)、读源码(看公司内部的复杂 SQL)、实战优化(找慢查询用 EXPLAIN 分析)。SQL 是肌肉记忆,光看不写学不会。祝你在数据世界里探索愉快!

← 上一篇 事务

返回 SQL 教程目录

✈️💬