5.5. 繼承

讓我們建立兩個資料表。首府資料表包含每個州的首府,它們也是城市。通常,首府資料表應該從城市資料表中繼承過來。

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- (單位:英尺)
);

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

在這種情況下,一行首府從它的父資料表,城市資料表中繼承所有屬性(名字,人口以及海拔)。 州首府有一個額外的屬性,state,顯示它們所在的州。在 PostgreSQL 裡, 一個資料表可以從零個或多個其它資料表中繼承屬性,而且一個查詢既可以引用一個資料表中的所有行, 也可以引用一個資料表的所有行加上所有其後代資料表的行。

注意: 繼承層次實際上是有向開環圖。

比如,下面的查詢查找所有海拔 500 英尺以上的所有城市的名字,包括州首府:

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

它返回:

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

另一方面,如果要找出不包括州首府在內的所有海拔超過500英尺的城市, 查詢應該是這樣的:

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

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

這裡的 cities 前面的 "ONLY" 資料表面該查詢應該只對 cities 進行查找而不包括繼承級別低於 cities 的資料表。 許多我們已經討論過的命令 -- SELECTUPDATEDELETE -- 支援這個 "ONLY" 符號。

廢棄: 以前版本的 PostgreSQL 裡,預設是不訪問子資料表。 我們發現這樣是容易出錯的而且違背 SQL:1999 標準。在舊語法裡面,要訪問子資料表,您需要附加一個 * 到資料表名後面。例如

SELECT * from cities*;

您仍然可以透過附加*明確聲明需要掃瞄子資料表, 也可以透過寫 "ONLY" 聲明明確聲明不掃瞄子資料表。 不過,從版本 7.1 開始,對那些不帶修飾的資料表名子的預設行為是同時掃瞄它的子資料表, 而以前的預設是正相反。要獲得老的預設行為, 把配置選項 SQL_Inheritance 關閉,也就是︰

SET SQL_Inheritance TO OFF;

或者向您的 postgresql.conf 文件裡面加一行。

有時候您可能想知道某條行版本來自哪個資料表。在每個資料表裡我們都有一個系統屬性叫 TABLEOID,它可以告訴您源資料表是誰:

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

它返回:

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

(如果您想復現這個例子,您可能會得到不同的數字 OID。) 透過和pg_class做一個連接,您可以看到實際的資料表名字︰

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

它返回:

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

一個資料表可以從多於一個父資料表中繼承,在這種情況下,它擁有它的父資料表們定義的字串的和 (加上任何為這個子資料表單獨定義的字串)。

繼承特性的一個嚴重的局限性是索引(包括唯一約束)和外鍵約束只施用於單個資料表, 而不包括它們的繼承的子資料表。這一點不管對引用資料表還是被引用資料表都是事實,因此,在上面的例子裡:

這些缺點很可能在將來的版本中修補,但同時您也需要考慮一下,繼承是否對您的問題真正有用。