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は、いつも指定されたテーブルに対してデータを挿入します。 ルール(詳細は第35章を参照してください)を使用して挿入をリダイレクトが可能な場合もあります。しかし、ルールを使用しても上記のような場合には適用できません。 なぜなら、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時に適用される拒否のルールに従います。
テーブルに対するアクセス許可は自動的には継承されません。よって親テーブルにアクセスするユーザは、そのすべての子テーブルに対する操作の許可も同様に持っていなくてはいけません。 またはONLY宣言をする必要があります。新規に子テーブルを既存の継承の階層に追加する場合は、必要なすべての許可を子テーブルに与えることに気をつけてください。
継承機能の厳しい制限として、(一意性制約を含む)インデックス、および外部キーは、そのテーブルのみに適用され、それを継承した子テーブルには適用されないことがあります。これは外部キーの参照側、被参照側でも同様に適用されません。 したがって、上の例では
もし、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設定オプションを無効にしてください。