跳转到主要内容

48 CREATE VIEW

通过视图提供可复用的查询,并了解视图何时支持更新。

视图是一个有名称且保存下来的 SELECT。你可以像查询表一样查询它,但每次都会即时计算结果行——不会存储任何数据。视图非常适合隐藏复杂性(将大型连接隐藏在简短名称之后)、实施列级访问控制(只授予对视图的 SELECT 权限,而不授予底层表权限),以及在底层模式发生演变时为应用提供稳定接口。PostgreSQL 还支持物化视图,它会存储行;这是下一课的内容。

本章要点

  • 使用 CREATE OR REPLACE VIEW 创建和替换视图
  • 可更新视图及其必须遵循的规则
  • 使用 WITH CHECK OPTION 确保更新仍处于视图范围内
  • 授予视图级访问权限而不暴露底层表
  • 列出和删除视图

示例

示例 48.1 创建视图
-- A simple view: customer + their order count
CREATE OR REPLACE VIEW customer_orders AS
SELECT c.id, c.name, count(o.id) AS order_count, COALESCE(sum(o.total), 0) AS total_spent
FROM customers_example c
LEFT JOIN orders_example o ON o.customer_id = c.id
GROUP BY c.id, c.name;

-- Use it like a table
SELECT * FROM customer_orders WHERE total_spent > 0 ORDER BY total_spent DESC;

-- Simple updatable view (no joins, no aggregates, no DISTINCT)
CREATE OR REPLACE VIEW alice_orders AS
SELECT * FROM orders_example WHERE customer_id = 1;

-- You can INSERT through an updatable view
INSERT INTO alice_orders (id, customer_id, total) VALUES (10, 1, 99.00);

-- WITH CHECK OPTION: refuse INSERTs/UPDATEs that wouldn't satisfy the view's WHERE
CREATE OR REPLACE VIEW alice_orders_strict AS
SELECT * FROM orders_example WHERE customer_id = 1
WITH CHECK OPTION;

-- This would fail because customer_id = 2 is not in the view's scope
-- INSERT INTO alice_orders_strict (id, customer_id, total) VALUES (11, 2, 1.00);

-- List views
SELECT viewname, definition
FROM pg_views
WHERE schemaname = 'public'
ORDER BY viewname;

-- Drop a view
DROP VIEW IF EXISTS alice_orders;
DROP VIEW IF EXISTS alice_orders_strict;
DROP VIEW IF EXISTS customer_orders;

视图是虚拟的:对视图执行的每个 SELECT 都会重新运行底层查询。视图没有缓存——本质上是规划器会内联的宏。这使视图不会产生过时数据,但也意味着视图本身不会带来性能收益;视图解决的是抽象问题,而不是速度问题。

可更新视图——那些能直接映射到单个底层表,且不包含聚合、连接、DISTINCT 或集合运算的视图——可以接受 INSERT、UPDATE 和 DELETE。这些操作会作用于底层表。WITH CHECK OPTION 是安全带,可防止 UPDATE 将行移出视图的谓词范围。

如果视图已存在,CREATE OR REPLACE VIEW 会更新其定义。列列表和类型必须保持兼容——不能通过这种方式删除列或更改其类型。使用 ALTER VIEW ... RENAME COLUMN 重命名列;其他不兼容的更改可能需要先删除再重新创建。

运行示例

运行方式(需要 customers_example 和 orders_example):

$ psql -f source/create-view.sql postgres
CREATE VIEW
 id | name  | order_count | total_spent
----+-------+-------------+-------------
  1 | Alice |           2 |       80.00
(1 row)
 ...

常见陷阱

  • 视图不会自动带来性能提升——视图定义会被内联到每个查询中。对于反复执行的繁重工作,可以考虑物化视图。
  • 可更新视图的规则很严格:任何聚合、DISTINCT、GROUP BY、集合运算或多个基表,通常都会使视图不可更新。你可以编写 INSTEAD OF 触发器来允许写入,但那属于高级用法。
  • CREATE OR REPLACE VIEW 必须保留现有的列名、顺序和类型,即使没有依赖对象也是如此。它可以追加列;重命名请使用 ALTER VIEW。
  • 视图可能隐藏代价高昂的连接,因此对视图执行的一行查询可能会运行数秒。使用 EXPLAIN 检查。
提示

视图是应用安全中常被低估的工具。只授予应用角色对某个视图的 SELECT 权限,该视图只返回它应该看到的列(例如排除 password_hash)。底层表仍受保护。

动手练习

创建一个列出没有订单的客户的视图(HAVING count(o.id) = 0)。在 psql 中使用 \d customer_orders 检查视图。然后创建一个限定到单个客户、支持 UPDATE 的视图,并通过它执行更新——观察底层表的变化。

下载

SQL 文件:create-view.sql · 全部示例文件 · 准备工作