この記事では、継承の関連内容を紹介します。
概要
PostgreSQLはテーブル継承を実装します。これはデータベースデザイナーにとって便利なツールです。
例から始めましょう。都市のデータモデルを構築しようとしているとします。 各州には多くの都市がありますが、首都は1つだけです。 特定の州の首都をすばやく取得できるようにしたいと考えています。 これは、州都用と首都ではない都市用の2つのテーブルを作成することで実行できます。 しかし、首都であるかどうかに関係なく、都市に関するデータを求めたい場合はどうなりますか? 継承機能は、この問題の解決に役立ちます。 都市から継承するように大文字のテーブルを定義します。
テーブルの都市を作成する (
テキスト名、
人口フロート、
elevation int -- フィートで
);
CREATE TABLEの大文字 (
state char(2)
) 相続人 (都市); この場合、大文字テーブルは親テーブルのすべての列citiesを継承します。 州都には、州を示す追加の列Stateもあります。
PostgreSQLでは、テーブルは0個以上の他のテーブルから継承でき、クエリはテーブルのすべての行またはテーブルのすべての行とその子孫テーブルのすべてを参照できます。 後者の動作がデフォルトです。 たとえば、次のクエリでは、500フィート以上の標高にある州都を含むすべての都市の名前を検索します。
SELECT名、標高
都市から
WHERE elevation > 500; PostgreSQLチュートリアルのサンプルデータを考えると、次のようになります。
名 | 標高
-----------+-----------
ラスベガス | 2174
マリポサ | 1953
マディソン | 845 一方、次のクエリでは、州都ではなく、500フィートを超える標高にあるすべての都市が見つかります。
SELECT名、標高
都市のみから
どこ標高> 500;
名前 | 標高
-----------+-----------
ラスベガス | 2174
マリポサ | 1953 ここで、ONLYキーワードは、クエリが都市にのみ適用され、継承階層内の都市の下のテーブルには適用されないことを示します。 すでに説明したコマンドの多く (SELECT、UPDATE、DELETE) は、ONLYキーワードをサポートしています。
テーブル名を末尾に * を付けて記述し、子孫テーブルが含まれることを明示的に指定することもできます。
SELECT名、標高
都市から *
WHERE elevation > 500; この動作は常にデフォルトであるため、* を書く必要はありません。 ただし、この構文は、デフォルトを変更できる古いリリースとの互換性については引き続きサポートされています。
場合によっては、特定の行がどのテーブルから発生したかを知りたい場合があります。 各テーブルには、元のテーブルを伝えることができるtableoidというシステム列があります。
SELECT c.tableoid, c.name, c.elevation
都市からc
WHERE c.elevation > 500; を返します。
tableoid | 名前 | 標高
----------+-----------+-----------
139793 | ラスベガス | 2174
139793 | マリポサ | 1953
139798 | マディソン | 845 (この例を再現しようとすると、おそらく異なる数値のOIDが得られます。) pg_classで結合を行うと、実際のテーブル名が表示されます。
SELECT p.relname, c.name, c.elevation
都市c、pg_class pから
どこc. 標高> 500とc.tableoid = p.oid; を返します。
relname | name | 標高
----------+-----------+-----------
都市 | ラスベガス | 2174
都市 | マリポサ | 1953
首都 | マディソン | 845 同じ効果を得るもう1つの方法は、regclassエイリアス型を使用することです。
SELECT c.tableoid::regclass, c.name, c.elevation
都市からc
WHERE c.elevation > 500; 継承は、INSERTまたはCOPYコマンドから継承階層内の他のテーブルにデータを自動的に伝播しません。 この例では、次のINSERTステートメントは失敗します。
INSERT INTO都市 (名前、人口、標高、州)
値 ('Albany' 、NULL、NULL、'NY'); データがどういうわけか大文字のテーブルにルーティングされることを望むかもしれませんが、これは起こりません。INSERTは常に指定されたテーブルに正確に挿入されます。 場合によっては、ルールを使用して挿入をリダイレクトすることが可能です。 ただし、citiesテーブルに列の状態が含まれていないため、ルールが適用される前にコマンドが拒否されるため、上記のケースでは役に立ちません。
NO INHERIT句で明示的に指定されていない限り、親テーブルのすべてのチェック制約とnullでない制約は、その子によって自動的に継承されます。 他のタイプの制約 (一意、主キー、および外部キーの制約) は継承されません。
テーブルは複数の親テーブルから継承することができ、その場合、親テーブルによって定義された列の和集合を有する。 子テーブルの定義で宣言された列はすべてこれらに追加されます。 同じ列名が複数の親テーブル、または親テーブルと子の定義の両方に表示される場合、これらの列は「マージ」され、子テーブルにそのような列が1つだけ存在するようになります。 マージするには、列のデータ型が同じである必要があります。そうしないと、エラーが発生します。 継承可能なチェック制約および非ヌル制約は、同様の方法でマージされる。 したがって、たとえば、マージされた列は、それが由来する列定義のいずれか1つがnullでないとマークされている場合、nullでないとマークされます。 チェック制約が同じ名前の場合はマージされ、条件が異なる場合はマージに失敗します。
テーブルの継承は、通常、CREATE TableステートメントのINHERITS句を使用して、子テーブルが作成されたときに確立されます。 または、互換性のある方法で既に定義されているテーブルに、ALTER tableのINHERITバリアントを使用して新しい親関係を追加できます。 これを行うには、新しい子テーブルに、親の列と同じ名前と型の列がすでに含まれている必要があります。 また、親と同じ名前とチェック式のチェック制約を含める必要があります。 同様に、継承リンクは、ALTER TABLEのNO INHERITバリアントを使用して子から削除できます。 このような継承リンクを動的に追加および削除することは、継承関係がテーブルのパーティション分割に使用されている場合に便利です。
後で新しい子になる互換性のあるテーブルを作成する便利な方法の1つは、create tableのLIKE句を使用することです。 これにより、ソーステーブルと同じ列を持つ新しいテーブルが作成されます。 ソーステーブルにCHECK制約が定義されている場合、互換性があると見なされるには、新しい子が親に一致する制約を持つ必要があるため、LIKEにINCLUDING constraintsオプションを指定する必要があります。
親テーブルは、子が残っている間は削除できません。 子テーブルの列またはチェック制約は、親テーブルから継承されている場合、削除または変更することもできません。 テーブルとそのすべての子孫を削除する場合は、CASCADEオプションを指定して親テーブルを削除するのが簡単です。
ALTER TABLEは、列データ定義の変更を反映し、制約を継承階層の下に確認します。 繰り返しますが、他のテーブルが依存する列を削除することは、CASCADEオプションを使用する場合にのみ可能です。ALTER TABLEは、CREATE TABLE中に適用される重複列マージおよび拒否の場合と同じ規則に従います。
継承されたクエリは、親テーブルに対してのみアクセス権限チェックを実行します。 したがって、たとえば、都市テーブルに対するUPDATE許可の付与は、大文字テーブルの行が都市を介してアクセスされた場合に、それらの行を更新する許可を意味する。 これにより、データが親テーブルに (も) 存在するような外観が保持されます。 しかし、大文字のテーブルは、追加の助成金なしでは直接更新できませんでした。 同様に、親テーブルの行セキュリティポリシーは、継承されたクエリ中に子テーブルから来る行に適用されます。 子テーブルのポリシーがある場合は、それがクエリで明示的に名前が付けられたテーブルである場合にのみ適用されます。その場合、その親にアタッチされたポリシーは無視されます。
外部テーブルは、通常のテーブルと同様に、親テーブルまたは子テーブルとして継承階層の一部にすることもできます。 外部テーブルが継承階層の一部である場合、外部テーブルによってサポートされない操作は、階層全体でもサポートされません。
ノート
すべてのSQLコマンドが継承階層で動作できるわけではないことに注意してください。 データの照会、データの変更、スキーマの変更に使用されるコマンド (例: SELECT、UPDATE、DELETE、ALTER TABLEのほとんどのバリアントですが、INSERTまたはALTER TABLE... 通常、RENAMEはデフォルトで子テーブルを含め、それらを除外する表記のみをサポートします。 データベースのメンテナンスとチューニングを行うコマンド (REINDEX、VACUUMなど) は、通常、個々の物理テーブルでのみ機能し、継承階層に対する再帰はサポートされません。 個々のコマンドのそれぞれの動作は、参照ページに記載されています。
継承機能の重大な制限は、インデックス (一意の制約を含む) と外部キーの制約が単一のテーブルにのみ適用され、継承の子には適用されないことです。 これは、外部キー制約の参照側と参照側の両方に当てはまります。 したがって、上記の例では:
都市を宣言した場合。nameをUNIQUEまたはPRIMARY KEYにすると、大文字のテーブルがcityの行を複製する名前を持つ行を持つことはできません。 そして、それらの重複した行は、デフォルトで都市からのクエリに表示されます。 実際、デフォルトでは、大文字には一意の制約がまったくないため、同じ名前の複数の行を含めることができます。大文字に固有の制約を追加できますが、都市と比較して重複を防ぐことはできません。同様に、その
都市を指定する場合。nameREFERENCES他のテーブルの場合、この制約は自動的に大文字に伝播しません。 この場合、同じREFERENCES制約を大文字に手動で追加することで回避できます。別のテーブルの列
REFERENCES cities(name)を指定すると、他のテーブルには都市名を含めることができますが、大文字の名前は含めません。 この場合の良い回避策はありません。
継承階層に実装されていない機能の一部は、宣言型パーティション分割に実装されます。 レガシー継承によるパーティション分割がアプリケーションに役立つかどうかを判断するには、かなりの注意が必要です。