他のバージョンの文書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 | 7.2

5.8. 継承

PostgreSQLは、データベース設計者にとって便利なテーブルの継承を実装しています(SQL:1999以降は型の継承を定義していますが、ここで述べられている継承とは多くの点で異なっています)。

まず例から始めましょう。市(cities)のデータモデルを作成しようとしていると仮定して下さい。それぞれの州にはたくさんの市がありますが、州都は1つのみです。 特定の州から州都を素早く検索したいとします。これは、2つのテーブルを作成することにより可能です。1つは州都のテーブルで、もう1つは州都ではないテーブルです。 しかし、州都であるか否かに関わらない市に対するデータを問い合わせたときには何が起こるでしょうか?継承のはこの問題を解決できます。 citiesから継承されるcapitalsテーブルを定義するのです。

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

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

この場合、capitalsテーブルは、その親テーブルであるcitiesテーブルの列をすべて継承します。 州都は1つの余分な列stateを持ち、州を表現します。

PostgreSQLでは、1つのテーブルは、0以上のテーブルから継承することが可能です。 また、問い合わせはテーブルのすべての行、またはテーブルのすべての行と継承されたテーブルのすべての行を参照します。 後者がデフォルトの動作になります。 例えば次の問い合わせは、500フィートより上に位置している州都を含んだ、すべての市の名前を検索します。

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

PostgreSQLチュートリアルからサンプルデータです(詳細は項2.1を参照してください)。 この問い合わせは、以下の結果を出力します。

   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

ここでONLYキーワードは、問い合わせがcitiesテーブルのみを対象にしcities以下の継承の階層にあるテーブルは対象としないことを意味します。 これまで議論したコマンドの多く—SELECTUPDATEそしてDELETE —が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

継承はINSERTまたはCOPYにより、継承の階層にある他のテーブルにデータを自動的に伝播することはありません。 この例では、次のINSERT文は失敗します。

INSERT INTO cities (name, population, altitude, state)
VALUES ('New York', NULL, NULL, 'NY');

データが、どうにかしてcapitalsテーブルまで到達できればいいのですが、そのようにはなりません。 INSERTは、いつも指定されたテーブルに対してデータを挿入します。 ルール(詳細は第34章を参照してください)を使用して挿入をリダイレクトが可能な場合もあります。しかし、ルールを使用しても上記のような場合には適用できません。 なぜなら、citiesテーブルにはstate列は含んでいませんし、ルールが適用される前にコマンドは拒否されてしまうからです。

チェック制約は、継承の階層にあるテーブルに対して定義可能です。親テーブルのすべてのチェック制約は、自動的にすべての子テーブルに対して継承されます。しかしながら、他の制約は継承されません。

テーブルは1つ以上の親テーブルから継承可能です。この場合、テーブルは親テーブルで定義された列の和になります。子テーブルで宣言された列は、これらの列に追加されることになります。 もし親テーブルに同じ名前の列がある場合もしくは、親テーブルと子テーブルに同じ名前の列がある場合は、列が"マージ"されて子テーブルではただ1つの列となります。 マージされるには列は同じデータ型を持っている必要があります。別のデータ型の場合にはエラーとなります。 マージされた列は、元となったテーブルで定義されていたすべてのチェック制約のコピーを持ちます。

テーブル継承は、現時点でCREATE TABLE文のみを使用して定義されます。 関連のあるCREATE TABLE ASでは継承は指定できません。既に定義されているテーブルに対して子テーブルとして継承させるような方法はありません。 同様に、子テーブルに対して継承関係を削除する方法は、テーブルを完全に削除する以外にはありません。子テーブルが存在している間は、親テーブルは削除できません。 親テーブルとその子テーブルすべてを削除する簡単な方法は、CASCADEオプションをつけることです。

ALTER TABLEは、列データ定義とチェック制約の変更を継承の階層にあるテーブルに伝えます。 繰り返しになりますが、親テーブルの列や制約の削除はCASCADEオプションを使用したときのみ可能となります。 ALTER TABLEは、重複列のマージ時に適用されるルールとCREATE TABLE時に適用される拒否のルールに従います。

5.8.1. 警告

テーブルに対するアクセス許可は自動的には継承されません。よって親テーブルにアクセスするユーザは、そのすべての子テーブルに対する操作の許可も同様に持っていなくてはいけません。 またはONLY宣言をする必要があります。新規に子テーブルを既存の継承の階層に追加する場合は、必要なすべての許可を子テーブルに与えることに気をつけてください。

継承機能の厳しい制限として、(一意性制約を含む)インデックス、および外部キーは、そのテーブルのみに適用され、それを継承した子テーブルには適用されないことがあります。これは外部キーの参照側、被参照側でも同様に適用されません。 したがって、上の例では

これらの機能の不足は、今後のリリースでおそらく改善されるでしょう。しかし 継承が有用であるかどうかは、十分注意して決定してください。

推奨しない設定: PostgreSQLの前のバージョンでは、問い合わせにおいて子テーブルを含まないことがデフォルトでした。 これはエラーとなりやすく、またSQL標準にも抵触しています。古いシンタックスでは子テーブルを含むには、テーブル名に*を追加していました。例えば

SELECT * from cities*;

ONLYと指定することにより子テーブルのスキャンを明示的に指定しない方法と同様に、*を追加して子テーブルのスキャンを明示的に指定することが可能です。 しかし7.1版の初期で、修飾のないテーブル名に対するデフォルトの動作は、子テーブルに対するスキャンも含めることになりました(それ以前は含んでいませんでした)。 古いデフォルトの動作を行うようにするには、sql_inheritance設定を無効にしてください。