同じテーブルの2つの行を関連付ける方法


11

行を相互に関連付けることができるテーブルがあり、論理的には、その関係は2つの行の間で双方向(基本的に、方向がない)になります。(そして、もし疑問に思っているなら、はい、これは実際には1つのテーブルでなければなりません。これは、まったく同じ論理エンティティ/タイプの2つのものです。)これを表す方法はいくつか考えられます。

  1. 関係とその逆を保存する
  2. リレーションシップを1つの方法で保存し、データベースに逆方向の保存を制限し、FKの順序が逆の2つのインデックスを作成します(1つのインデックスはPKインデックスです)。
  3. 2つのインデックスを使用して関係を一方向に保存し、いずれにしても2番目のインデックスを挿入できるようにします(ややこしいですが、ちょっと、完全性)
  4. ある種のグループ化テーブルを作成し、元のテーブルにFKを適用します。(多くの質問が発生します。グループ化テーブルには数しかありません。なぜテーブルさえ持っているのですか?FKをNULL可能にするか、単一の行が関連付けられたグループがあるのですか?)

これらの方法の主な長所と短所は何ですか?もちろん、私が考えていない方法はありますか?

以下は、使用するSQLFiddle です:http ://sqlfiddle.com/#!12/7ee1a/1/0 。(私が使用しているため、PostgreSQLであるように思われますが、この質問はPostgreSQLに固有のものではないと思います。)現在、例として、関係とその逆の両方を保存しています。


与えられた値を他の複数の値に関連付けることができますか?与えられた値は常に別のものと関連していますか?他の共通のデータを共有していますか?
フィロ2013

はい、他の複数の行に関連付けることができます。いいえ、必ずしも別の行に関連付けられているとは限りません。必ずしも共通のデータはありません。ありがとうございました。
jpmc26 2013

おっとっと。@Philを忘れました。また、私が思いついた潜在的な構造を追加するために編集されました。
jpmc26 2013

回答:


9

あなたがデザインしたものは良いです。追加する必要があるのは、関係を無方向にするための制約です。したがって、行を追加し(1,5)ないと(5,1)行を作成できません。

これを達成することができる*ブリッジテーブル上の自己参照制約を持ちます。

*:SQL標準で説明されているように外部キー制約を実装しているPostgres、Oracle、DB2、およびすべてのDBMSで実行できます(遅延、たとえばトランザクションの最後にチェックされます)。ステートメントの最後でそれらをチェックするサーバーであり、この構造は引き続き機能します。「InnoDBはUNIQUEおよびFOREIGN KEY制約を行ごとにチェックする」ため、これをMySQLで行うことはできません

したがって、Postgresでは、以下が要件に一致します。

CREATE TABLE x
(
  x_id SERIAL NOT NULL PRIMARY KEY,
  data VARCHAR(10) NOT NULL
);

CREATE TABLE bridge_x
(
  x_id1 INTEGER NOT NULL REFERENCES x (x_id),
  x_id2 INTEGER NOT NULL REFERENCES x (x_id),
  PRIMARY KEY(x_id1, x_id2),
  CONSTRAINT x_x_directionless
    FOREIGN KEY (x_id2, x_id1)
    REFERENCES bridge_x (x_id1, x_id2)
);

テスト場所:SQL-Fiddle

行を追加しようとすると(1,5)

INSERT INTO bridge_x VALUES
(1,5) ;

それは失敗します:

エラー:テーブル "bridge_x"の挿入または更新が外部キー制約 "x_x_directionless"に違反しています
詳細:キー(x_id2、x_id1)=(5、1)がテーブル "bridge_x"に存在しません。:
INSERT INTO bridge_x VALUES(1,5)

さらに、行CHECKを禁止する場合は、制約を追加でき(y,y)ます。

ALTER TABLE bridge_x
  ADD CONSTRAINT x_x_self_referencing_items_not_allowed
    CHECK (x_id1 <> x_id2) ;

あなたが言及するようにこれを実装する他の方法があります。x_id1たとえば、x_id2列の低いID と高いIDを強制することによって、関係の1方向のみを(2行ではなく1行に)格納します。実装は簡単に見えますが、通常は後でより複雑なクエリにつながります。

CREATE TABLE bridge_x
(
  x_id1 INTEGER NOT NULL REFERENCES x (x_id),
  x_id2 INTEGER NOT NULL REFERENCES x (x_id),
  PRIMARY KEY(x_id1, x_id2),
  CONSTRAINT x_x_directionless
    CHECK (x_id1 <= x_id2)                       -- or "<" to forbid `(y,y)` rows
);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.