web-dev-qa-db-ja.com

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

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

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

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

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

11
jpmc26

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

これは達成することができます* ブリッジテーブルに自己参照制約があります。

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

したがって、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)に挿入

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

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

x_id1の低いIDとx_id2列の高い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
);
9
ypercubeᵀᴹ