データベース設計:「(多対多)対多」関係の正規化


13

短縮版

既存の多対多結合の各ペアに、一定数の追加プロパティを追加する必要があります。基本ケースを拡張してこれを達成するために、利点と欠点の観点から、オプション1〜4のどれが最良の方法であるかを下の図にスキップしますか?または、ここで検討していないより良い代替手段がありますか?

長いバージョン

現在、中間結合テーブルを介して、多対多の関係にある2つのテーブルがあります。既存のオブジェクトのペアに属するプロパティにリンクを追加する必要があります。プロパティテーブルの1つのエントリが複数のペアに適用される場合があります(または1つのペアに対して複数回使用される場合もあります)が、各ペアにはこれらのプロパティが固定されています。私はこれを行うための最良の方法を決定しようとしていますが、状況をどう考えるかを整理するのに苦労しています。意味的には、次のいずれかと同等にうまく説明できるようです。

  1. 固定数の追加プロパティの1つのセットにリンクされた1つのペア
  2. 多くの追加プロパティにリンクされた1つのペア
  3. 1つのプロパティセットにリンクされた多くの(2つの)オブジェクト
  4. 多くのプロパティにリンクされた多くのオブジェクト

XとYの2つのオブジェクトタイプがあり、それぞれに一意のIDがあり、リンクテーブルのobjx_objyx_idとがありy_id、これらが一緒にリンクの主キーを形成します。各Xは多くのYに関連付けることができ、その逆も可能です。これは、既存の多対多の関係のセットアップです。

規範事例

規範事例

さらに、別のテーブルで定義された一連のプロパティと、特定の(X、Y)ペアがプロパティPを持つ必要がある一連の条件があります。条件の数は固定され、すべてのペアで同じです。基本的に、「状況C1では、ペア(X1、Y1)にプロパティP1があります」、「状況C2では、ペア(X1、Y1)にプロパティP2があります」など、結合の各ペアの3つのシチュエーション/条件についてテーブル。

オプション1

私の現在の状況であり、正確にこのような3つの条件があり、一つの可能性は、列を追加することですので、私は、それが増加することを期待する理由がないc1_p_idc2_p_idc3_p_idfeatx_featy、与えられたために指定するx_idy_id、どのプロパティp_id3例ごとに使用します。

オプション1

これは、SQLが機能に適用されるすべてのプロパティを選択するのを複雑にし、より多くの条件に容易にスケーリングできないため、私にとって素晴らしいアイデアのようには見えません。ただし、(X、Y)ペアごとに一定数の条件の要件を強制します。実際、それはここで唯一のオプションです。

オプション2

条件テーブルを作成し、条件テーブルcondの主キーに条件IDを追加します。

オプション2

これの1つの欠点は、各ペアの条件の数を指定しないことです。もう1つは、最初の関係だけを考えているとき、たとえば

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

次に、DISTINCTエントリの重複を避けるために句を追加する必要があります。これは、各ペアが一度しか存在しないという事実を失ったようです。

オプション3

結合テーブルに新しい「ペアID」を作成し、最初のテーブルとプロパティと条件の間に2番目のリンクテーブルを作成します。

オプション3

これには、各ペアに一定数の条件を強制しないこと以外に、最も不利な点があるようです。ただし、既存のIDのみを識別する新しいIDを作成することは理にかなっていますか?

オプション4(3b)

基本的にオプション3と同じですが、追加のIDフィールドは作成されません。これは、新しい結合テーブルに両方の元のIDを配置することで実現されるため、の代わりにx_idy_idフィールドが含まれますxy_id

オプション4

この形式のもう1つの利点は、既存のテーブルを変更しないことです(まだ運用されていません)。ただし、基本的にテーブル全体を複数回複製する(または、とにかくそのように感じる)ので、理想的とも思えません。

概要

私の感じでは、オプション3と4は十分に似ており、どちらでも使用できます。プロパティへの少数の固定数のリンクの要件がなければ、オプション1が他の場合よりも合理的に見えるようになるでしょう。いくつかの非常に限られたテストに基づいDISTINCTて、クエリに句を追加してもこの状況でのパフォーマンスに影響はないようですが、オプション2が他の状況と同様に状況を表すかどうかはわかりません。リンクテーブルの複数の行の同じ(X、Y)ペア。

これらのオプションの1つが私の最善の方法ですか、または考慮すべき別の構造がありますか?


全体的に1と4が最良の選択肢のようです、私は同意します。オプション4でプロパティの固定数(3)を強制するのは簡単ではありませんが、実現可能だと思います。
ypercubeᵀᴹ

DISTINCT句、私はリンク#2の端に1つ、のようなクエリを考えていたxy経由xycを参照していないけどc、私がしている場合...だから(x_id, y_id, c_id)制約UNIQUE行と(1,1,1)し、(1,1,2)その後、SELECT x.id, y.id FROM x JOIN xyc JOIN y私は戻って、同一の2を取得します行、、(1,1)および(1,1)
マイケルアンダーウッド14

1
わかった とにかくオプション2を却下します。私は1または4のいずれかで行きます。
ypercubeᵀᴹ14年

考えれば考えるほど、プロパティの数を厳密に3つに制限することは私の要件の中で最も重要ではないと感じます。そのため、今後少しの建設的なフィードバックがなければ、この時点でおそらく4番に進みます。ご意見をお寄せいただきありがとうございます、@ ypercube!
マイケルアンダーウッド14

回答:


7
  • オプション1

    *これは、SQLが機能に適用されるすべてのプロパティを選択するのを複雑にするので、私には素晴らしいアイデアのようには見えません…

    必ずしもクエリSQLを複雑にするわけではありません(以下の結論を参照)。

    …そして、より多くの条件に容易に対応できません…

    条件の数が固定されており、数十または数百ではない限り、より多くの条件に容易にスケーリングします。

    ただし、(X、Y)ペアごとに一定数の条件の要件を強制します。実際、これを行う唯一のオプションです。*

    それはそうであり、あなたはこれが「私の要件の中で最も重要ではない」とコメントで述べていますが、あなたはそれが全く問題ではないと言っていません。

  • オプション2

    これの1つの欠点は、各ペアの条件の数を指定しないことです。もう1つは、初期の関係のみを検討している場合、重複エントリを避けるためにDISTINCT句を追加する必要があるということです...

    あなたが言及する複雑さのために、このオプションを却下できると思います。このobjx_objyテーブルは、一部のクエリの駆動テーブルになる可能性があります(たとえば、「機能に適用されるすべてのプロパティを選択する」。これは、objxまたはに適用されるすべてのプロパティを意味しますobjy)。ビューを使用して事前に適用することができるため、DISTINCTクエリを複雑にする必要はありませんが、それは非常にわずかなゲインでパフォーマンスに関して非常にひどくスケーリングします。

  • オプション3

    ただし、既存のIDのみを識別する新しいIDを作成することは理にかなっていますか?

    いいえ、ありません—オプション4はあらゆる点で優れています。

  • オプション4

    …基本的にテーブル全体を複数回複製する(または、とにかくそのように感じる)ので、理想的とも思えません。

    このオプションは問題ありません—プロパティの数が可変であるか、変更される可能性がある場合、関係を設定する明らかな方法です

結論

あたりのプロパティの数objx_objyが安定している可能性が高く、ほんの少しだけ余分に追加することを想像できない場合、私の好みはオプション1です。また、「プロパティの数= 3」制約を適用する唯一のオプションです。オプション4に同様の制約を適用するとc1_p_id、とにかくxyテーブルに列を追加することになります*。

本当にその状態にあまり関心がなく、プロパティ数の状態が安定することを疑う理由がある場合は、オプション4を選択します。

どれがわからない場合は、オプション1を選択してください。他の人が言っているように、オプションがあればもっと簡単です。オプション1を延期する場合、「…SQLが機能に適用されるすべてのプロパティを選択するのを複雑にするため…」オプション4の追加テーブルと同じデータを提供するビューを作成することをお勧めします。

オプション1の表:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

「エミュレート」オプション4を表示:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

「機能に適用されるすべてのプロパティを選択する」:

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

ここに dbfiddle


-3

これらのオプションはいずれも機能すると考えていますが、条件の数が本当に3に固定されている場合はオプション1を使用し、そうでない場合はオプション2を使用します。Occamのカミソリは、データベースの設計にも使用できます。他のすべての要素は、最も単純な設計と同等であることが通常は最適です。

厳密なデータベース正規化ルールに従う場合は、条件の数が固定されているかどうかに関係なく、2にする必要があると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.