视图
一、视图概述
视图(View)本质上是一个存储在数据库中的查询语句。视图本身不包含数据,也被称为虚拟表。我们在创建视图时给它指定了一个名称,然后可以像表一样对其进行查询。

合理使用的视图可以给我们带来以下好处:
- 替代复杂查询,减少复杂性。将复杂的查询语句定义为视图,然后使用视图进行查询,可以隐藏具体的实现;
- 提供一致性接口,实现业务规则。在视图的定义中增加业务逻辑,对外提供统一的接口;当底层表结构发生变化时,只需要修改视图接口,而不需要修改外部应用,可以简化代码的维护并减少错误;
- 控制对于表的访问,提高安全性。通过视图为用户提供数据访问,而不是直接访问表;同时可以限制允许访问某些敏感信息,例如身份证号、工资等。
二、创建视图
PostgreSQL 使用 CREATE VIEW 语句创建视图:
CREATE VIEW view_name AS query;
其中,view_name 是视图的名称;AS 之后是视图的查询语句,可以是简单查询或者复杂的查询。以下语句创建了一个包含员工详细信息的视图:
CREATE VIEW emp_details_view
AS SELECT
e.employee_id,
e.job_id,
e.manager_id,
e.department_id,
d.location_id,
e.first_name,
e.last_name,
e.salary,
e.commission_pct,
d.department_name,
j.job_title
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN jobs j ON (j.job_id = e.job_id);
该视图使用了连接查询从 3 个表中获取信息。
接下来,我们可以直接从视图中查询数据,不需要每次编写复杂的连接查询:
SELECT * FROM emp_details_view
WHERE department_name = 'IT';

该语句返回了 IT 部门的员工信息。
三、修改视图
如果需要修改视图定义中的查询,可以使用CREATE OR REPLACE 语句:
CREATE OR REPLACE VIEW view_name
AS
query
PostgreSQL 目前只支持追加视图定义中的字段,不支持减少字段或者修改字段的名称或顺序。例如,我们可以为视图 emp_details_view 增加一个字段 hire_date:
CREATE OR REPLACE VIEW emp_details_view
AS SELECT
e.employee_id,
e.job_id,
e.manager_id,
e.department_id,
d.location_id,
e.first_name,
e.last_name,
e.salary,
e.commission_pct,
d.department_name,
j.job_title,
e.hire_date
FROM employees e
JOIN departments d ON (e.department_id = d.department_id)
JOIN jobs j ON (j.job_id = e.job_id);
另外,PostgreSQL 还提供了 ALTER VIEW 语句修改视图的属性。例如以下语句用于修改视图的名称:
ALTER VIEW emp_details_view RENAME TO emp_info_view;
该语句将视图 emp_details_view 重命名为emp_info_view。
四、删除视图
使用DROP VIEW 语句删除一个已有的视图:
DROP VIEW [ IF EXISTS ] name [ CASCADE | RESTRICT ];
其中,IF EXISTS 可以避免删除一个不存在的视图时产生错误;CASCADE 表示级联删除依赖于该视图的对象;RESTRICT 表示如果存在依赖对象则提示错误信息,这是默认值。
我们将视图 emp_info_view 删除:
DROP VIEW emp_info_view;
五、递归视图
通用表表达式(CTE),包括使用 RECURSIVE 关键字实现递归查询。与此类似,视图的定义中也可以使用RECURSIVE:
CREATE RECURSIVE VIEW view_name (column_names) AS query;
需要注意的是,递归视图需要指定字段的名称column_names。以上语句实际上等价于:
CREATE VIEW view_name AS
WITH RECURSIVE cte_name (column_names) AS (query)
SELECT column_names FROM cte_name;
通过递归 CTE 遍历组织结构的查询语句定义为一个递归视图:
CREATE RECURSIVE VIEW employee_path(employee_id, employee_name, path) AS
SELECT employee_id,
CONCAT(first_name, ',', last_name),
CONCAT(first_name, ',', last_name) AS path
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.employee_id,
CONCAT(e.first_name, ',', e.last_name),
CONCAT(ep.path, '->', e.first_name, ',', e.last_name)
FROM employee_path ep
JOIN employees e ON ep.employee_id = e.manager_id;
六、可更新视图
如果一个视图满足以下条件:
- 视图定义的FROM 子句中只包含一个表或者可更新视图;
- 视图定义的最顶层查询语句中不包含以下子句: GROUP BY、 HAVING、 LIMIT、 OFFSET、DISTINCT、WITH、UNION、INTERSECT 以及EXCEPT;
- SELECT 列表中不包含窗口函数、集合函数或者聚合函数(例如 SUM、COUNT、AVG等)。
那么该视图被称为可更新视图(updatable view),意味着我们可以对其执行 INSERT、UPDATE 以及 DELETE 语句,PostgreSQL 会将这些操作转换为对底层表的操作。
我们创建一个视图 employees_it:
CREATE VIEW employees_it AS
SELECT employee_id,
first_name,
last_name,
email,
phone_number,
hire_date,
job_id,
manager_id,
department_id
FROM employees
WHERE department_id = 60;
视图 employees_it 只包含了 IT 部门的员工信息,并且不包含员工的月薪字段。该视图目前只能查询到 5 条数据:
SELECT employee_id,first_name, last_name
FROM employees_it;

我们通过视图 employees_it 为employees 表增加一个员工:
INSERT INTO employees_it (employee_id, first_name, last_name, email, phone_number, hire_date, job_id,
manager_id, department_id)
VALUES(207, 'Tony', 'Dong', 'DONG', '590.423.5568', '2020-05-06', 'IT_PROG',
103, 60);
SELECT employee_id,first_name, last_name FROM employees_it;

需要注意,不在视图定义中的字段不能通过视图进行修改。以下语句尝试通过视图employees_it 修改员工的月薪:
UPDATE employees_it
SET salary = 5000
WHERE employee_id = 207;

不在视图定义中的字段不能通过视图进行操作。我们将新增的员工从 employees 表中删除:
DELETE FROM employees_it
WHERE employee_id = 207;
6.1、WITH CHECK OPTION
我们再次通过视图 employees_it 为 employees 表增加一个员工,此时将部门编号改为 80:
INSERT INTO employees_it(employee_id, first_name, last_name, email, phone_number, hire_date, job_id,
manager_id, department_id)
VALUES(207, 'Tony', 'Dong', 'DONG', '590.423.5568', '2020-05-06', 'IT_PROG',
149, 80);
SELECT employee_id,first_name, last_name
FROM employees_it
WHERE employee_id = 207;

SELECT employee_id,first_name, last_name
FROM employees
WHERE employee_id = 207;

新增加的员工不属于 IT 部门,增加之后无法通过视图查询,但是可以通过 employees 表查询。
为了防止通过视图插入或者修改视图不可见的数据,可以使用WITH CHECK OPTION 选项:
CREATE OR REPLACE VIEW employees_it AS
SELECT employee_id,
first_name,
last_name,
email,
phone_number,
hire_date,
job_id,
manager_id,
department_id
FROM employees
WHERE department_id = 60
WITH CHECK OPTION;
再次通过视图 employees_it 为employees 表增加一个员工:
DELETE FROM employees
WHERE employee_id = 207;
INSERT INTO employees_it(employee_id, first_name, last_name, email, phone_number, hire_date, job_id,
manager_id, department_id)
VALUES(207, 'Tony', 'Dong', 'DONG', '590.423.5568', '2020-05-06', 'IT_PROG',
149, 80);

执行结果显示违反检查选项,无法插入数据。
WITH CASCADED CHECK OPTION 选项会对视图以及它所依赖的其他视图进行级联检查;
WITH LOCAL CHECK OPTION 选项只对当前视图进行检查。默认为 CASCADED。
七、物化视图
物化视图(Materialized View)是PostgreSQL 提供的一个扩展功能,它是介于视图和表之间的一种对象。
物化视图和视图的最大区别是它不仅存储定义中的查询语句,而且可以像表一样存储数据。物化视图和表的最大区别是它不支持 INSERT、UPDATE、DELETE 以及 MERGE 语句,只能通过刷新物化视图进行数据的更新。
物化视图通过提前运行并存储查询结果,通常用于查询优化、数据仓库、数据集成等场景。
另外,PostgreSQL 目前不支持物化视图的实时更新,因此物化视图中的数据通常不是最新数据。
7.1、创建物化视图
PostgreSQL 使用 CREATE MATERIALIZED VIEW 语句创建视图:
CREATE MATERIALIZED VIEW [IF NOT EXISTS] name
AS query
[ WITH [NO] DATA];
其中,IF NOT EXISTS 可以避免重复创建一个已经存在的物化视图时产生错误;name 是物化视图的名称;query 是物化视图的查询语句。
WITH DATA 表示创建时填充物化视图中的数据,这个是默认选项;WITH NO DATA 表示创建时不填充数据,此时无法查询物化视图中的数据,需要执行 REFRESH MATERIALIZEDVIEW 命令刷新物化视图数据之后才能查询。
虽然物化视图定义中的查询语句支持 ORDER BY 子句,但是不推荐使用。如果想要以指定顺序显示数据,应该在查询数据时明确指定排序字段,而不应该依赖表中的数据存储顺序。以下语句创建了一个包含员工统计信息的物化视图:
CREATE MATERIALIZED VIEW emp_stat_mv
AS
SELECT d.department_name, j.job_title, count(*)
FROM employees e
JOIN departments d ON d.department_id = e.department_id
JOIN jobs j ON j.job_id = e.job_id
GROUP BY d.department_name, j.job_title;
物化视图 emp_stat_mv 包含了按照部门和职位统计的员工数量。
我们可以直接查询 emp_stat_mv,获取员工统计信息:
SELECT *
FROM emp_stat_mv
ORDER BY department_name;

物化视图可以像表一样支持索引,实现约束和查询优化。例如,以下语句为 emp_stat_mv 创建了一个唯一索引:
CREATE UNIQUE INDEX uk_emp_stat_mv
ON emp_stat_mv(department_name, job_title);
7.2、刷新物化视图
PostgreSQL 物化视图不会自动刷新数据,需要手动执行 REFRESH MATERIALIZED VIEW语句:
REFRESH MATERIALIZED VIEW [CONCURRENTLY] name
[ WITH [NO] DATA];
其中,CONCURRENTLY 可以支持刷新物化视图时其他连接的并发读取。使用该选项的前提是物化视图上至少存在一个唯一索引,而且不能是表达式索引或者部分索引。
WITH DATA 表示刷新时填充物化视图中的数据,这个是默认选项;WITH NO DATA 表示刷新时不填充数据,刷新后物化视图处于不可查询状态。
CONCURRENTLY 和WITH NO DATA 选项不能一起使用。未填充数据的物化视图刷新时不支持 CONCURRENTLY 选项。
如果在物化视图定义的查询语句中指定了 ORDER BY 子句,刷新物化视图数据时不会保证数据仍然按照指定顺序进行存储。
例如,我们可以定期执行以下语句刷新物化视图 emp_stat_mv 中的数据:
REFRESH MATERIALIZED VIEW CONCURRENTLY emp_stat_mv;
或者执行以下语句清空物化视图 emp_stat_mv 中的数据:
REFRESH MATERIALIZED VIEW emp_stat_mv
WITH NO DATA;
7.3、修改物化视图
ALTER MATERIALIZED VIEW 语句可以修改物化视图的一些属性,例如名称、字段名等:
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
RENAME [ COLUMN ] column_name TO new_column_name;
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
RENAME TO new_name;
ALTER MATERIALIZED VIEW [ IF EXISTS ] name
SET SCHEMA new_schema;
第一个语法用于修改物化视图的字段名称,第二个语法用于修改物化视图的名称,第三个语法用于修改物化视图所在的模式。
例如,以下语句将物化视图 emp_stat_mv 的名称修改为 emp_stat_mv2:
ALTER MATERIALIZED VIEW emp_stat_mv
RENAME TO emp_stat_mv2;
LTER MATERIALIZED VIEW 语句不能修改物化视图定义中的查询语句;如果想要修改物化视图定义中的查询语句,需要删除并重建物化视图。
7.4、删除物化视图
PostgreSQL 使用 DROP MATERIALIZED VIEW 语句删除物化视图:
DROP MATERIALIZED VIEW [ IF EXISTS ] name [ CASCADE | RESTRICT ];
其中,IF EXISTS 可以避免删除一个不存在的物化视图时产生错误;CASCADE 表示级联删除依赖于该物化视图的对象;RESTRICT 表示如果存在依赖对象则提示错误信息,这是默认值。
我们可以使用以下语句删除物化视图 emp_stat_mv2:
DROP MATERIALIZED VIEW emp_stat_mv2;
2475

被折叠的 条评论
为什么被折叠?



