継承

PostgreSQL 8.4.1文書
前のページ | 巻戻し | 第 5章データ定義 | 早送り | 次のページ

# 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`以下の継承の階層にあるテーブルは対象としないことを意味します。 これまで議論したコマンドの多く—`SELECT`、`UPDATE`そして`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`は、いつも指定されたテーブルに対してデータを挿入します。 ルール（詳細は第36章を参照してください）を使用して挿入をリダイレクトが可能な場合もあります。しかし、ルールを使用しても上記のような場合には適用できません。 なぜなら、`cities`テーブルには`state`列は含んでいませんし、ルールが適用される前にコマンドは拒否されてしまうからです。

親テーブル上のチェック制約と非NULL制約はその子テーブルに自動的に継承されます。 他の種類の制約（一意性制約、プライマリキー、外部キー制約）は継承されません。

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

テーブル継承は、通常、CREATE TABLE文の`INHERITS`句を使用して、子テーブルを作成する時に確立します。 他にも、互換性を持つ方法で定義済みのテーブルに新しく親子関係を付けることも可能です。 これにはALTER TABLEの`INHERIT`形式を使用します。 このためには、新しい子テーブルは親テーブルと同じ名前の列を持ち、その列の型は同じデータ型でなければなりません。 また、親テーブルと同じ名前、同じ式の検査制約を持っていなければなりません。 `ALTER TABLE`の`NO INHERIT`形式を使用して、同様に継承関係を子テーブルから取り除くことも可能です。 このような継承関係の動的追加、動的削除は、継承関係をテーブル分割（項5.9を参照）に使用している場合に有用です。

後で子テーブルとする予定の、互換性を持つテーブルを簡単に作成する方法の1つは、`CREATE TABLE`で`LIKE`句を使用することです。 これは、元としたテーブルと同じ列を持つテーブルを新しく作成します。 新しい子テーブルが必ず親テーブルと一致する制約を持ち、互換性があるものとみなされるように、元となるテーブルで`CHECK`制約が存在する場合は、`LIKE`に`INCLUDING CONSTRAINTS`オプションを指定すべきです。

子テーブルが存在する場合親テーブルを削除することはできません。 また、子テーブルでは、親テーブルから継承した列、または検査制約を削除することも変更することもできません。 テーブルとそのすべての子孫テーブルを削除したければ、`CASCADE`オプションを付けて親テーブルを削除することが簡単な方法です。

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

## 5.8.1. 警告

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

より一般的には、すべてのSQLコマンドが継承階層に対して動作できるとは限らないことに注意してください。 データの検索、データの変更、スキーマの変更のために使用されるコマンド（例えば`SELECT`、`UPDATE`、`DELETE`、`ALTER TABLE`のほとんどの構文です。しかし`INSERT`および`ALTER TABLE ... RENAME`は除きます。）は通常、デフォルトで子テーブルを含み、また、それを除外するための`ONLY`記法をサポートします。 データベース保守およびチューニング（例えば`REINDEX`、`VACUUM`）を行うコマンドは通常、個々の物理テーブルに対してのみ動作し、継承階層に対する再帰をサポートしません。 個々のコマンドのそれぞれの動作はそのマニュアルページ（参照I, SQLコマンド）に記載されています。

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

- もし、`cities`.`name`を`UNIQUE`または`PRIMARY KEY`と宣言しても、 `cities`テーブルの行を複製した行を、`capitals`テーブル内に持つことを禁止することにはなりません。 さらに、これらの複製された行はデフォルトで`cities`テーブルへの問い合わせで現れるでしょう。 事実として、`capitals`テーブルはデフォルトで一意性制約を持っていませんし、同一の名前の複数の行を持つことがあり得ます。 `capitals`テーブルに一意性制約を追加できますが、これは`cities`テーブルと比較して複製を禁止することにはなりません。

- 同じように、もし`cities`.`name` `REFERENCES`他のテーブルとしても、 この制約は自動的に`capitals`に引き継がれるわけではありません。この場合は`capitals`テーブルに 同一の`REFERENCES`制約を手動で追加しなければいけません。

- 他のテーブルの列`REFERENCES cities(name)`を指定することは、他のテーブルが市の名前を持つことを 許可しますが、州都の名前を持つことを許可しません。この場合は次善策はありません。

これらの機能の不足は、今後のリリースでおそらく改善されるでしょう。しかし当面 継承がアプリケーションにとって有用であるかどうかを判断する際に十分注意してください。

> 推奨しない設定: 7.1より以前のリリースのPostgreSQLでは、問い合わせにおいて子テーブルを含まないことがデフォルトでした。 これはエラーとなりやすく、またSQL標準にも抵触しています。 7.1以前の古い動作を行うようにするには、sql_inheritance設定オプションを無効にしてください。

前のページ | ホーム | 次のページ
スキーマ | 上に戻る | パーティショニング