5.5. 継承

2つのテーブルを作ってみましょう。 capitalsテーブルで保持される州都は、市(cities)でもあります。 capitalsテーブルはcitiesテーブルから継承されるべきだというのが自然な流れでしょう。

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- (単位feet)
);

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

この場合、capitalsの行はすべての属性(name(名前)、population(人口)、altitude(高度))を親テーブルのcitiesから継承します。 name属性の型はPostgreSQL固有の可変長文字列用のtext型です。 population属性の型はPostgreSQL固有の倍精度浮動小数点数用のfloat型です。 州都にはさらに、stateという州を示す属性が追加されます。 PostgreSQLでは、テーブルは0個以上のテーブルから継承することができ、問い合わせは1つのテーブルのすべての行を参照することも、テーブルとその子孫すべての行を参照することも可能です。

注意: 継承の階層は非循環有向グラフです。

たとえば、次の問い合わせは、州都を含む、高度が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"という構文をサポートしています。

場合によっては、ある特定の行がどのテーブルからきたものか知りたいということがあるかもしれません。 それぞれのテーブルには"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

改善のために仕様変更された部分: PostgreSQLの以前のバージョンでは、デフォルトでは子テーブルにアクセスすることができませんでした。 これは間違いのもとであり、また標準SQL99にも違反しています。 古い構文では、サブテーブルを得るためには*をテーブル名に付加していました。 たとえば下記のようになります。

SELECT * from cities*;

現在でも*を付加することで子テーブルのスキャンを明確に指定したり、"ONLY"を書くことで子テーブルをスキャンしないことを明確にすることができます。 しかしバージョン7.1からは、何も指定されていないテーブル名のデフォルト動作では子テーブルもスキャンします。 逆に、以前のデフォルトはそうしないことでした。 以前のデフォルト動作で作業をしたい場合はSQL_Inheritance設定オプションを無効にして下さい。 以下に例を示します。

SET SQL_Inheritance TO OFF;

または、postgresql.confファイルに1行追加して下さい。

継承機能の制限として、(一意性制約を含む)インデックス、および、外部キーは、そのテーブルのみに適用され、それを継承した子テーブルには適用されないことがあります。 従って、上の例で、他のテーブルの列にREFERENCES cities(name)を指定すると、そのテーブルは市の名前を持つことはできますが、州都の名前を持つことはできません。 この欠陥は将来のリリースでおそらく修正されます。