web-dev-qa-db-ja.com

テーブルをマッピングする代わりにテーブル継承を使用する

これはかなり一般的なシナリオのように見えます。すべてが同じ子タイプを構成するいくつかのタイプです。

これは通常、次のようになります。

-- 'name' is unique per parent record
CREATE TABLE sometype (
  sometype_id serial PRIMARY KEY,
  name        text
);

CREATE TABLE foo (foo_id serial PK);
CREATE TABLE bar (bar_id serial PK);

CREATE TABLE foo_sometype (
  foo_id      int4,
  sometype_id int4
);

CREATE TABLE bar_sometype (
  bar_id      int4,
  sometype_id int4
);

これは問題ありませんが、クエリするのが面倒です。これはもっときれいになると思います:

-- 'name' is unique per parent record
CREATE TABLE sometype (
  name        text
);

CREATE TABLE foo_sometype (
  foo_id      int4
) INHERITS(sometype);

CREATE TABLE bar_sometype (
  bar_id      int4,
) INHERITS(sometype);

これについて私が好きなこと:

  • 参加が簡単(USINGを使用)
  • 'sometype'に代理キーを追加する必要はありません。これは明示的に 'foo'および 'bar'のコンポーネントです

しかし、継承の非典型的な使用のようです。

これを行う理由ではありません


継承の「欠陥」について

Pg継承の標準的な警告は、クラス継承を直接モデル化するためにテーブル継承が使用される場合にのみ関係することに注意してください。これは、ここでは行っていません。実際、これが機能するためには、継承が継承を行う必要があります

動作は非常に論理的であり、「欠点」は1つのユースケースにのみ関連するため、私は彼らが「継承」以外のものをそれを呼んだことを望みます。


手動で複製されたテーブル構造に対する利点

Evanが指摘するように、私が説明したとおりの「foo_sometype」と「bar_sometype」を手動で作成することもできますが、継承された構造にはいくつかの大きな利点があると思います。

  • 継承関係では、 'foo_sometype'と 'bar_sometype'は、たまたま同じ列を持つ2つのテーブルだけでなく、同じtypeとして定義されています。
  • 親テーブルを介して将来スキーマを変更すると、偶発的な相違の可能性が少なくなります(わずかな作業で、これは実際に適用される可能性があります)。
  • さらに重要なのは、クライアントコードを親テーブルに対して生成し、テーブル名を変更するだけで子に適用できることです。この場合も、テーブル構造が確実に適用されます。

したがって、不自然な例として、FooとBarはHasSomeTypeListトレイトを持つことができます。これは、すべての「sometype」操作を抽象化し、両方のテーブルをSomeTypeクラスにマップできることを認識しています。

Foo/Bar関係の表現は、それが特性としてモデル化されているか、継承としてモデル化されているかにかかわらず、ここでの最終的な目標です。

ちなみに、素朴なユーザー/クエリライター(スキーマの変更を期待されていない人)にとっては、これら2つの方法は同じに見えます。

5
Dmitri

継承は、私が触れない機能の1つです。私の知る限り、それは内部的にレプリケーション およびパーティショニング のためにある程度の容量で使用されています。エンドユーザーが使用することを意図して設計されているかどうかはわかりません。

具体的な技術的欠点

UNIQUEとREFERENCESの欠点

ドキュメントは CAVEATセクション の欠点のいくつかをカバーしています(以下が重要です)。

  • Parent.nameをUNIQUEまたはPRIMARY KEYとして宣言した場合、これは、子テーブルが親の行と重複する名前を持つ行を持つことを阻止しません。そして、それらの重複する行は、デフォルトで親からのクエリに表示されます。実際、デフォルトでは子には一意の制約がまったくないため、同じ名前の複数の行を含めることができます。一意の制約を子に追加することもできますが、これは親と比較して重複を防ぐことはできません。
  • 同様に、parent.nameが他のいくつかのテーブルを参照するように指定した場合、この制約は自動的に子に伝播しません。この場合、同じREFERENCES制約を子に手動で追加することで回避できます。
  • 別のテーブルの列REFERENCES parent(name)を指定すると、他のテーブルに親の名前を含めることができますが、子の名前を含めることはできません。この場合の適切な回避策はありません。

INHERITの開発が遅い

これらの欠陥は、1996年にリリースされた docsから7.3 で最初に言及されましたが、継承が実装されて以来存在していました

この欠陥は、おそらく将来のリリースで修正されるでしょう。

唯一の変更は、2010年にリリースされた docsから8.0 への欠陥をより明確かつ詳細にすることでした。

これらの欠陥はおそらく将来のリリースで修正される予定ですが、当面は、継承が問題に役立つかどうかを判断する際にはかなりの注意が必要です。

それを待っている幸運いくつかの将来のリリース。そして、あなたが話しているいくつかのことfeaturesは、作曲に固有のものではありません、

「鍵」を保存するのは意味がない

  • 'sometype'に代理キーがありません。明示的にコンポジションです

sometypeを属性リストにして、それに直接リンクするのとどう違うのですか?

CREATE TABLE sometype (sometype_name text PRIMARY KEY);
CREATE TABLE foo (foo_id serial PRIMARY KEY);
CREATE TABLE foo_sometype (
  foo_id int REFERENCES foo,
  sometype_name text REFERENCES sometype,
  PRIMARY KEY ( foo_id, sometype_name )
);

今、あなたは参加する必要さえありませんfoo_sometype to sometype to get sometype.sometype_name

テーブルの分割

これらすべての問題は別として、テーブルパーティショニングの今後の PostgreSQL 10リリースでさらに悪化します

多重継承は許可されておらず、分割と継承を混在させることはできません

それであなたは継承が欲しいのですか? Forgoパーティショニング。これには、実際にプランナーの利点があります。

他の机

ああ、 ALTER TABLE には、その注記にもリストされているかなりの数の欠点があります。

テーブルに子孫テーブルがある場合、列のタイプを追加、名前変更、または変更したり、子孫に対して同じ操作を行わずに親テーブルで継承された制約の名前を変更したりすることはできません。つまり、ALTER TABLE ONLYは拒否されます。これにより、子孫は常に親と一致する列を持つようになります。 [...]再帰的なDROP COLUMN操作は、子孫が他の親からその列を継承せず、列の独立した定義を持たなかった場合にのみ、子孫テーブルの列を削除します。非再帰的なDROP COLUMN(つまり、ALTER TABLE ONLY ... DROP COLUMN)は、子孫の列を削除することはなく、継承ではなく独立して定義された列としてマークします。 [...] TRIGGER、CLUSTER、OWNER、およびTABLESPACEアクションが子孫テーブルに再帰することはありません。つまり、それらは常に指定されたものとしてのみ機能します。制約の追加は、NO INHERITとマークされていないCHECK制約に対してのみ再帰します。

結論

継承を使う人は少ないと思います。野生では見たことがありません。 dbの継承は学習曲線に追加され、一部の機能はそのままにしておく方がよいでしょう。それらのためのアプリケーションを見つける必要はありません。

Stack Overflowのこの投稿「PostgreSQLで継承されたテーブルをいつ使用するか?」が役立つかもしれません。

8
Evan Carroll

言及された欠陥は、継承を使用しない理由ではありません!ここでの継承は、独立したオブジェクトによるクラス継承と同様に機能します。あなたはクラス/テーブル「果物」とクラス/テーブル「リンゴ」と「オレンジ」を持っているかもしれません。リンゴとオレンジは果物からメタ定義を継承するため、果物を介してそれらを取得できます。ただし、これらは独立した列挙型の独立したクラスであり、他に何を期待できますか....

衝突を本当に防ぐ必要がある場合:トリガーを定義するか、メインテーブルで(NOTを使用して)チェック/外部キーを定義します。

しかし、どうか、PostgreSQLでの継承の動作に不満があるからといって、人々がそれを使用するのを思いとどまらせないでください!独立したサブクラスまたはテーブルは素晴らしいです!依存関係が必要な場合は、必要な方法で実装してください。公式ドキュメントを含め、多くの優れたチュートリアルがあります。次に、別の例を示します。 継承– PostgreSQLを愛するもう1つの理由

主キーの衝突の可能性があるにもかかわらず、私のプロジェクトの1つでも継承を使用しています(親テーブルには主キーが定義されておらず、モデルはそれを必要としません)。

ORMだけでなく、継承を使用してパフォーマンスを向上させる方法もあります。

難しいことは悪いことと同じではありません。

4

避けるべき多くの理由、そしてすべては関係モデルの原則に違反することになります。最も明白なのは、子テーブルがリレーションではなくバッグであり、外部キーの欠如が情報の原則に違反しているため、すべての情報がタプルの属性値の形式である必要があるということです。

最も重大な影響は、子テーブルのIDの整合性が失われることです(重複には予想外のときに挿入される方法があります)、モデルの透明性が失われます。これを次のユーザーに説明する必要がある場合は、非常に後悔しますモデル、そしていいえ、あなたがあなたが人生の唯一のユーザーになると確信することは決してできません。

リレーショナルコンストラクト(外部キーを含む)はクエリが面倒ですが、そうではありません。まず、いつでもビュー(派生関係)を作成できます。 2番目に、自動魔法の振る舞いよりも、ユーザーが照会しているものを理解している方が良いです。

2
Leandro