PostgreSQL 中的 VIEW 视图和 MATERIALIZED VIEW
FreeGuideOnline
最新
2026-07-07
sql CREATE VIEW dept_employee_count AS SELECT d.department_name, COUNT(e.employee_id) AS total_employees FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id GROUP BY d.department_name;
之后就可以像查普通表一样使用:
```sql
SELECT * FROM dept_employee_count;
修改视图
如果需要修改视图定义,可以使用 CREATE OR REPLACE VIEW:
CREATE OR REPLACE VIEW dept_employee_count AS
SELECT
d.department_name,
d.location,
COUNT(e.employee_id) AS total_employees
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_name, d.location;
删除视图
DROP VIEW IF EXISTS dept_employee_count;
可更新视图
简单视图(基于单表,没有聚合、DISTINCT、GROUP BY 等)可以被用来执行 INSERT、UPDATE、DELETE 操作,PostgreSQL 会将该操作转换为对基表的操作。例如:
CREATE VIEW active_employees AS
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE is_active = true;
-- 更新视图数据,实际更新 employees 表
UPDATE active_employees
SET salary = salary * 1.1
WHERE employee_id = 101;
对于复杂视图,可以使用
INSTEAD OF触发器来实现更新,这超出了入门范围,但你需要知道这种可能性。
MATERIALIZED VIEW 深入
创建物化视图
语法与普通视图类似,只是关键字不同:
CREATE MATERIALIZED VIEW sales_summary AS
SELECT
product_id,
DATE_TRUNC('month', sale_date) AS month,
SUM(amount) AS total_sales,
COUNT(*) AS sale_count
FROM sales
GROUP BY product_id, DATE_TRUNC('month', sale_date);
此时,sales_summary 表存储了查询结果的物理副本。直接查询速度非常快。
刷新物化视图
数据不会自动变化。为了同步基表的最新数据,需要执行刷新命令:
REFRESH MATERIALIZED VIEW sales_summary;
默认情况下,刷新会锁定物化视图,期间无法查询。如果要允许并发查询,可以添加 CONCURRENTLY 选项(需要物化视图有唯一索引):
-- 首先创建唯一索引
CREATE UNIQUE INDEX idx_sales_summary ON sales_summary (product_id, month);
-- 并发刷新,不阻塞读操作
REFRESH MATERIALIZED VIEW CONCURRENTLY sales_summary;
删除物化视图
DROP MATERIALIZED VIEW IF EXISTS sales_summary;
查看物化视图状态
你可以检查所有物化视图及其最新刷新时间:
SELECT
schemaname,
matviewname,
ispopulated,
last_refresh
FROM pg_matviews;
何时选择 VIEW 还是 MATERIALIZED VIEW
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 需要实时数据,基表频繁更新 | VIEW | 每次查询都反映最新数据 |
| 查询逻辑复杂,执行耗时 | MATERIALIZED VIEW | 预计算并存储结果,提升查询速度 |
| 报表系统、历史数据快照 | MATERIALIZED VIEW + 定时刷新 | 对实时性要求不高,可接受延迟 |
| 限制用户只能访问部分行/列 | VIEW | 安全封装,隐藏底层表结构 |
| 数据仓库 ETL 中间结果 | MATERIALIZED VIEW | 物理存储,可被下游直接使用 |
| 移动端或 API 需要快速响应 | MATERIALIZED VIEW | 亚秒级响应,避免复杂 Join |
实践技巧与注意事项
对 VIEW 的建议
- 避免嵌套过深:视图多层嵌套会导致查询计划极难优化,性能下降明显。
- 可更新视图要谨慎:对复杂视图的自动更新可能失败,建议只用在简单场景。
- 善用物化视图替代视图:当发现某个视图查询频繁且慢时,考虑改为物化视图,并设计合理的刷新策略。
对 MATERIALIZED VIEW 的建议
- 建立合适的索引:在刷新后分析表并创建索引,可以大幅提升查询性能。
- 规划刷新策略:根据业务容忍度,安排在业务低峰期(如每天深夜)刷新,或使用触发器事件驱动刷新(需要借助外部工具或 cron 任务)。
- 并发刷新的限制:
CONCURRENTLY需要至少一个唯一索引,刷新速度稍慢,但可避免锁表。适合 7×24 服务。 - 存储成本:物化视图会占用额外磁盘空间,注意监控。
使用 EXPLAIN 检查性能
无论是视图还是物化视图,分析查询计划都是优化利器:
EXPLAIN SELECT * FROM dept_employee_count;
EXPLAIN SELECT * FROM sales_summary;
物化视图的查询计划通常显示为对物理表的简单扫描,而视图会展示完整的执行计划。
动手任务:创建你的第一个物化视图
- 在测试库中创建两张表:
CREATE TABLE products (
id serial PRIMARY KEY,
name text,
price numeric
);
CREATE TABLE orders (
id serial PRIMARY KEY,
product_id int REFERENCES products(id),
quantity int,
order_date date
);