web-dev-qa-db-ja.com

「2つのテーブルから離れた」制約の適用

SQLで電気回路図をモデリングする際に問題が発生しました。キャプチャしたい構造は

  part ←────────── pin
   ↑                ↑
part_inst ←───── pin_inst

ここで、「inst」は「instance」の略です。

たとえば、partとしてpins 1OUT、1IN-、1IN +、GND、2IN +、2IN-、2OUT、およびVを備えたLM358オペアンプがあるとします。CC。次に、このパーツを回路図に配置して、part_instおよび8 pin_instsを作成します。

データフィールドを無視して、スキーマでの最初の試みは

create table parts (
    part_id bigserial primary key
);
create table pins (
    pin_id bigserial primary key,
    part_id bigint not null references parts
);
create table part_insts (
    part_inst_id bigserial primary key,
    part_id bigint not null references parts
);
create table pin_insts (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null references part_insts,
    pin_id bigint not null references pins
);

このスキーマの主な問題は、pin_instpart_instpart_id=1に関連付けられている可能性があることですが、そのpinにはpart_id=2があります。

アプリケーションレベルではなくデータベースレベルでこの問題を回避したいと思います。それで、私はそれを強制するために私の主キーを修正しました。変更された行を--でマークしました。

create table parts (
    part_id bigserial primary key
);
create table pins (
    pin_id bigserial,                                          --
    part_id bigint not null references parts,
    primary key (pin_id, part_id)                              --
);
create table part_insts (
    part_inst_id bigserial,                                    --
    part_id bigint not null references parts,
    primary key (part_inst_id, part_id)                        --
);
create table pin_insts (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null,                              --
    pin_id bigint not null,                                    --
    part_id bigint not null references parts,                  --
    foreign key (part_inst_id, part_id) references part_insts, --
    foreign key (pin_id, part_id) references pins              --
);

この方法の不満は、主キーを汚染することです。part_instを参照するすべての場所で、part_inst_idpart_idの両方を追跡する必要があります。過度に冗長にせずにpin_inst.part_inst.part_id = pin_inst.pin.part_id制約を適用する別の方法はありますか?

9
Snowball

最小限のソリューション

根本的な解決策の1つは、pin_instを完全に削除することです。

  part ←────────── pin
    ↑               
part_inst ←───── pin_inst

あなたの質問には、実際に冗長なテーブルが必要であることを示唆するものは何もありません。 part_instに関連付けられているpinsについては、関連付けられているpinpartsを確認してください。

これにより、コードが次のように簡略化されます。

create table part (    -- using singular terms for table names
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part
);

しかし、あなたのコメントは私たちがそれで逃げられないことを明らかにしました...

pin_instが必要な場合の代替

あなたがしたようにpart_idを含めることは、外部キー制約を持つ最も単純なソリューションです。 外部キー制約 を使用して、「2テーブル離れた」テーブルを参照することはできません。

しかし、少なくとも主キーを「汚染」することなく済ますことができます。 UNIQUE constraint を追加します。

create table part (
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, pin_id)         -- note sequence of columns
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, part_inst_id)
);
create table pin_inst (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null,
    pin_id bigint not null,
    part_id bigint not,
    foreign key (part_id, pin_id) references pin,
    foreign key (part_id, part_inst_id) references part_inst
);

一意の制約の最初にpart_idを置きます。これは参照整合性には関係ありませんが、パフォーマンスにとって重要です。主キーはすでにpk列のインデックスを実装しています。一意の制約を実装する複数列インデックスの最初にother列を置くことをお勧めします。これらの関連質問の下の詳細:

SOに関する質問:

トリガー付きの代替

より柔軟なトリガー関数を使用することもできますが、少し複雑でエラーが発生しやすく、少し厳密ではありません。利点:part_inst.part_idおよびpin.part_idなしで実行できる...

12