web-dev-qa-db-fra.com

Appliquer des contraintes «deux tables»

J'ai rencontré des problèmes de modélisation d'un schéma électrique dans SQL. La structure que j'aimerais capturer est

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

où "inst" est court pour "instance".

Par exemple, j'aurais peut-être en tant que part un ampli OP-LM358 avec pins 1out, 1in- 1in +, GND, 2in +, 2in, 2OUT et VCc. Je pourrais alors placer cette partie sur un schéma, créant un part_inst et 8 pin_insts.

Ignorer les champs de données, ma tentative initiale d'un schéma était

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
);

Le principal problème avec ce schéma est qu'un pin_inst peut être attaché à un part_inst avec part_id=1 mais son pin a part_id=2.

J'aimerais éviter ce problème sur le niveau de la base de données plutôt que le niveau d'application. Donc, j'ai modifié mes principales clés pour appliquer cela. J'ai marqué les lignes changées avec --.

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              --
);

Ma gripe avec cette méthode est qu'elle pollue les clés primaires: partout où je me réfère à un part_inst, J'ai besoin de garder une trace de l'part_inst_id et le part_id. Y a-t-il une autre façon de faire de l'application de la contrainte pin_inst.part_inst.part_id = pin_inst.pin.part_id Sans être trop verbeux?

9
Snowball

Solution minimale

Une solution radicale peut être de supprimer pin_inst complètement:

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

Vous n'avez rien dans votre question pour vous suggérer que vous avez réellement besoin de la table redondante. Pour pins associé à un part_inst, regarde le pins de l'associé part.

Cela simplifierait le code à:

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
);

Mais votre commentaire a clairement précisé que nous ne vous échapperons pas avec ça ...

Alternative si pin_inst est nécessaire

Comprenant part_id Comme vous l'avez fait est la solution la plus simple avec des contraintes de clé étrangère. Vous ne pouvez pas faire référence à une table "Deux tables" "avec contraintes de clé étrangère .

Mais vous pouvez au moins faire faire sans "polluer" les clés primaires. Ajouter UNIQUE contraintes .

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
);

Je mets part_id Tout d'abord dans les contraintes uniques. Cela n'est pas pertinent pour l'intégrité référentielle, mais cela compte pour la performance. Les touches principales appliquent déjà des index pour les colonnes PK. Il vaut mieux avoir le Autre colonne d'abord dans les index multicolumnistes mettant en œuvre les contraintes uniques. Détails dans ces questions connexes:

Questions connexes sur SO:

Alternative avec des déclencheurs

Vous pourriez avoir recours aux fonctions des déclencheurs, qui sont plus flexibles, mais un peu plus compliqué et sujet aux erreurs et un peu moins strict. L'avantage: vous pourriez faire sans part_inst.part_id et pin.part_id ...

12
Erwin Brandstetter