2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3

5.11. 繼承 #

PostgreSQL 實現表繼承,這對於資料庫設計者來說是一個有用的工具。(SQL:1999 及更高版本定義了一個型別繼承特性,該特性在許多方面與此處描述的特性不同。)

讓我們從一個例子開始:假設我們正在嘗試構建一個城市資料模型。每個州都有許多城市,但只有一個首府。我們希望能夠快速檢索任何特定州的州首府。這可以透過建立兩個表來完成,一個用於州首府,另一個用於非首府城市。但是,當我們想查詢關於某個城市的資料,而不管它是否是首府時,會發生什麼?繼承特性可以幫助解決這個問題。我們定義 capitals 表,使其繼承自 cities

CREATE TABLE cities (
    name            text,
    population      float,
    elevation       int     -- in feet
);

CREATE TABLE capitals (
    state           char(2)
) INHERITS (cities);

在這種情況下,capitals繼承了其父表 cities 的所有列。州首府還有一個額外的列 state,用於顯示其所屬的州。

PostgreSQL 中,一個表可以繼承自零個或多個其他表,並且查詢可以引用表的所有行,或者表的所有行及其所有後代表。後一種行為是預設的。例如,以下查詢查詢所有城市(包括州首府)的名稱,這些城市的海拔高度超過 500 英尺

SELECT name, elevation
    FROM cities
    WHERE elevation > 500;

使用 PostgreSQL 教程中的示例資料(參見 第 2.1 節),這將返回

   name    | elevation
-----------+-----------
 Las Vegas |      2174
 Mariposa  |      1953
 Madison   |       845

另一方面,以下查詢查詢所有非州首府且海拔高度超過 500 英尺的城市

SELECT name, elevation
    FROM ONLY cities
    WHERE elevation > 500;

   name    | elevation
-----------+-----------
 Las Vegas |      2174
 Mariposa  |      1953

這裡的 ONLY 關鍵字表示查詢僅適用於 cities,而不適用於繼承層次結構中 cities 下方的任何表。我們已經討論過的許多命令(如 SELECTUPDATEDELETE)都支援 ONLY 關鍵字。

您也可以在表名後加上 * 來顯式指定包含後代表

SELECT name, elevation
    FROM cities*
    WHERE elevation > 500;

新增 * 不是必需的,因為這是預設行為。但是,為了與舊版本(其預設設定可能已更改)相容,仍然支援此語法。

在某些情況下,您可能想知道某一行來自哪個表。每個表都有一個名為 tableoid 的系統列,它可以告訴您原始表

SELECT c.tableoid, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;

這將返回

 tableoid |   name    | elevation
----------+-----------+-----------
   139793 | Las Vegas |      2174
   139793 | Mariposa  |      1953
   139798 | Madison   |       845

(如果您嘗試重現此示例,可能會得到不同的數字 OID。)透過與 pg_class 進行連線,您可以檢視實際的表名

SELECT p.relname, c.name, c.elevation
FROM cities c, pg_class p
WHERE c.elevation > 500 AND c.tableoid = p.oid;

這將返回

 relname  |   name    | elevation
----------+-----------+-----------
 cities   | Las Vegas |      2174
 cities   | Mariposa  |      1953
 capitals | Madison   |       845

另一種獲得相同效果的方法是使用 regclass 別名型別,它會以符號化方式打印表 OID

SELECT c.tableoid::regclass, c.name, c.elevation
FROM cities c
WHERE c.elevation > 500;

繼承不會自動將 INSERTCOPY 命令的資料傳播到繼承層次結構中的其他表中。在我們的示例中,以下 INSERT 語句將失敗

INSERT INTO cities (name, population, elevation, state)
VALUES ('Albany', NULL, NULL, 'NY');

我們可能會希望資料以某種方式被路由到 capitals 表,但這並沒有發生:INSERT 總是精確地插入到指定的表中。在某些情況下,可以使用規則(請參見 第 39 章)來重定向插入。但是,這對於上述情況沒有幫助,因為 cities 表不包含 state 列,因此在規則應用之前命令就會被拒絕。

父表上的所有檢查約束和非空約束都會被其子表自動繼承,除非使用 NO INHERIT 子句明確指定。其他型別的約束(唯一約束、主鍵約束和外部索引鍵約束)不會被繼承。

一個表可以繼承自多個父表,在這種情況下,它擁有所有父表定義的列的並集。子表定義中宣告的任何列都會新增到這些列中。如果同一列名出現在多個父表中,或者出現在父表和子表的定義中,則這些列會被合併,以便子表中只有一個這樣的列。要合併,列必須具有相同的資料型別,否則會引發錯誤。可繼承的檢查約束和非空約束也會以類似的方式合併。因此,例如,如果它來自的某個列定義被標記為非空,那麼合併後的列將被標記為非空。如果檢查約束具有相同的名稱,它們就會被合併,如果它們的條件不同,合併將失敗。

表繼承通常在建立子表時建立,使用 CREATE TABLE 語句的 INHERITS 子句。或者,可以使用 ALTER TABLEINHERIT 變體,將一個已定義的兼容表新增新的父表關係。為此,新子表必須已經包含與父表列名稱和型別相同的列。它還必須包含與父表具有相同名稱和檢查表示式的檢查約束。類似地,可以使用 ALTER TABLENO INHERIT 變體從子表中刪除繼承連結。像這樣動態新增和刪除繼承連結可能很有用,當繼承關係用於表分割槽時(請參見 第 5.12 節)。

建立兼容表以便以後成為新子表的一種便捷方法是在 CREATE TABLE 中使用 LIKE 子句。這會建立一個具有與源表相同列的新表。如果源表上定義了任何 CHECK 約束,則應指定 LIKEINCLUDING CONSTRAINTS 選項,因為新子表必須具有與父表相容的約束才能被視為相容。

在任何子表仍然存在的情況下,無法刪除父表。也不能刪除或更改子表的列或檢查約束,如果它們是從任何父表繼承的。如果您想刪除一個表及其所有後代,一種簡單的方法是使用 CASCADE 選項刪除父表(請參見 第 5.15 節)。

ALTER TABLE 會將列資料定義和檢查約束的任何更改向下傳播到繼承層次結構。同樣,只有在使用 CASCADE 選項時,才能刪除被其他表依賴的列。ALTER TABLE 遵循與 CREATE TABLE 相同的重複列合併和拒絕規則。

繼承查詢僅對父表執行訪問許可權檢查。因此,例如,授予對 cities 表的 UPDATE 許可權,意味著當透過 cities 訪問 capitals 表中的行時,也具有更新這些行的許可權。這保留了資料(也)位於父表中的外觀。但是,如果沒有額外的授權,則無法直接更新 capitals 表。以類似的方式,在繼承查詢中,父表的行安全策略(請參見 第 5.9 節)適用於來自子表的行。子表的策略(如果存在)僅在它作為查詢中明確命名的表時應用;在這種情況下,其父表上的任何策略都會被忽略。

外部表(請參見 第 5.13 節)也可以是繼承層次結構的一部分,無論是作為父表還是子表,就像常規表一樣。如果外部表是繼承層次結構的一部分,那麼任何外部表不支援的操作在整個層次結構上也不支援。

5.11.1. 注意事項 #

請注意,並非所有 SQL 命令都能在繼承層次結構上工作。用於資料查詢、資料修改或模式修改的命令(例如,SELECTUPDATEDELETE、大多數 ALTER TABLE 的變體,但不包括 INSERTALTER TABLE ... RENAME)通常預設包含子表,並支援 ONLY 表示法來排除它們。大多數執行資料庫維護和調優的命令(例如,REINDEX)僅在單個物理表上工作,並且不支援遍歷繼承層次結構。VACUUMANALYZE 命令預設包含子表,並且支援 ONLY 表示法以允許它們被排除。每個命令的具體行為在其參考頁(SQL 命令)中有記錄。

繼承特性的一個嚴重限制是,索引(包括唯一約束)和外部索引鍵約束僅適用於單個表,而不適用於它們的繼承子表。在引用方和被引用方外部索引鍵約束上都是如此。因此,根據上面的例子

  • 如果我們宣告 cities.nameUNIQUEPRIMARY KEY,這不會阻止 capitals 表中出現與 cities 表中行重複的名稱。而且,預設情況下,這些重複的行會出現在從 cities 發起的查詢中。實際上,預設情況下 capitals 將沒有任何唯一約束,因此可能包含多個名稱相同的行。您可以為 capitals 新增一個唯一約束,但這不會阻止與 cities 相比的重複。

  • 同樣,如果我們指定 cities.name REFERENCES 另一個表,此約束不會自動傳播到 capitals。在這種情況下,您可以透過手動為 capitals 新增相同的 REFERENCES 約束來解決。

  • 指定另一個表的列 REFERENCES cities(name) 將允許另一個表包含城市名稱,但不能包含首府名稱。在這種情況下沒有好的解決方法。

為繼承層次結構未實現的一些功能已在宣告式分割槽中實現。在決定是否為應用程式使用遺留繼承分割槽時需要非常謹慎。

提交更正

如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步說明的內容,請使用 此表單 報告文件問題。