《PostgreSQL 開發指南》第 25 篇 視圖

視圖概述

視圖(View)本質上是一個存儲在數據庫中的查詢語句。視圖本身不包含數據,也被稱爲虛擬表;我們在創建視圖時給它指定了一個名稱,然後可以像表一樣對其進行查詢。

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_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。

ALTER VIEW語句還提供了其他的修改功能,例如設置字段的默認值、修改視圖所屬的模式等,具體可以參考官方文檔

刪除視圖

使用DROP VIEW語句刪除一個已有的視圖:

DROP VIEW [ IF EXISTS ] name  [ CASCADE | RESTRICT ];

其中,IF EXISTS 可以避免刪除一個不存在的視圖時產生錯誤;CASCADE 表示級聯刪除依賴於該視圖的對象;RESTRICT 表示如果存在依賴對象則提示錯誤信息,這是默認值。

我們將視圖 emp_info_view 刪除:

drop view emp_info_view;

遞歸視圖

在專欄的第 20 篇中,我們介紹了通用表表達式(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;

我們將第 20 篇中通過遞歸 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;
employee_id|first_name|last_name|
-----------|----------|---------|
        103|Alexander |Hunold   |
        104|Bruce     |Ernst    |
        105|David     |Austin   |
        106|Valli     |Pataballa|
        107|Diana     |Lorentz  |

我們通過視圖 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;
employee_id|first_name|last_name|
-----------|----------|---------|
        103|Alexander |Hunold   |
        104|Bruce     |Ernst    |
        105|David     |Austin   |
        106|Valli     |Pataballa|
        107|Diana     |Lorentz  |
        207|Tony      |Dong     |

需要注意,不在視圖定義中的字段不能通過視圖進行修改。以下語句嘗試通過視圖 employees_it 修改員工的月薪:

update employees_it
set salary = 5000
where employee_id = 207;
SQL Error [42703]: ERROR: column "salary" of relation "employees_it" does not exist
  Position: 26

不在視圖定義中的字段不能通過視圖進行操作。

我們將新增的員工從 employees 表中刪除:

delete from employees_it
where employee_id = 207;

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;
employee_id|first_name|last_name|
-----------|----------|---------|

select employee_id,first_name, last_name 
from employees 
where employee_id = 207;
employee_id|first_name|last_name|
-----------|----------|---------|
        207|Tony      |Dong     |

新增加的員工不屬於 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);
SQL Error [44000]: ERROR: new row violates check option for view "employees_it"
  Detail: Failing row contains (207, Tony, Dong, DONG, 590.423.5568, 2020-05-06, IT_PROG, null, null, 149, 80).

執行結果顯示違反檢查選項,無法插入數據。

進一步來說,WITH CASCADED CHECK OPTION 選項會對視圖以及它所依賴的其他視圖進行級聯檢查;WITH LOCAL CHECK OPTION 選項只對當前視圖進行檢查。默認爲 CASCADED。

歡迎點贊👍、評論📝、收藏❤️!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章