Applicazione dei vincoli "a due tavoli di distanza"


10

Ho riscontrato qualche problema a modellare uno schema elettrico in SQL. La struttura che vorrei catturare è

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

dove "inst" è l'abbreviazione di "istanza".

Ad esempio, potrei avere un partamplificatore operazionale LM358 con pins 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT e V CC . Potrei quindi posizionare questa parte su uno schema, creando a part_inste 8 pin_insts.

Ignorando i campi di dati, il mio tentativo iniziale di uno schema è stato

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

Il problema principale con questo schema è che a pin_instpotrebbe essere legato a a part_instcon part_id=1ma pinha part_id=2.

Vorrei evitare questo problema a livello di database anziché a livello di applicazione. Quindi, ho modificato le mie chiavi primarie per imporlo. Ho contrassegnato le righe modificate con --.

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

La mia lamentela con questo metodo è che inquina le chiavi primarie: dovunque mi riferisca a part_inst, devo tenere traccia sia di part_inst_idche di part_id. Esiste un altro modo per applicare il vincolo pin_inst.part_inst.part_id = pin_inst.pin.part_idsenza essere eccessivamente prolisso?


È possibile rimuovere anche la pin_inst_idridondanza. È possibile utilizzare la (part_inst_id, part_id, pin_id)chiave primaria.
ypercubeᵀᴹ

Due cose: (a) 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT e VCC non generano istanze a 11 pin? (b) Non ho il tuo schema iniziale. Non è possibile utilizzare un pin in più di una parte? È necessaria una relazione NN tra pin e parte non 1-N.
Marcus Junius Brutus,

@ user34332: (a) I numeri fanno parte dei nomi. Ad esempio, "2OUT" è un singolo pin. Ecco un disegno schematico del chip di cui sto parlando nella domanda. (b) Non sono d'accordo. Certamente due parti potrebbero avere un pin VCC (tensione di alimentazione positiva, "tensione [at] collettore comune"), ma sono pin logicamente diversi. Ad esempio, un pin VCC in genere potrebbe assorbire 500 µA e uno diverso 250 µA.
Snowball,

@Snowball Aiuterebbe gli altri a capire il tuo schema se hai aggiunto un SQL-Fiddle con dati di esempio.
ypercubeᵀᴹ

Risposte:


13

Soluzione minima

Una soluzione radicale potrebbe essere quella di rimuovere pin_instcompletamente:

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

Non c'è nulla nella tua domanda che suggerisca che in realtà hai bisogno della tabella ridondante. Per le pins associate a a part_inst, guarda le pins degli associati part.

Ciò semplificherebbe il codice per:

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

Ma il tuo commento ha chiarito che non riusciremo a cavarcela ...

Alternativa se pin_instè necessaria

Includere part_idcome hai fatto è la soluzione più semplice con vincoli di chiave esterna. Non è possibile fare riferimento a una tabella "a due tabelle di distanza" con vincoli di chiave esterna .

Ma puoi almeno accontentarti senza "inquinare" le chiavi primarie. Aggiungi UNIQUEvincoli .

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

Ho messo per part_idprimo i vincoli unici. Ciò è irrilevante per l'integrità referenziale, ma è importante per le prestazioni. Le chiavi primarie implementano già indici per le colonne pk. È meglio avere prima l' altra colonna negli indici a più colonne che implementano i vincoli univoci. Dettagli sotto queste domande correlate:

Domande correlate su SO:

Alternativa con trigger

Potresti ricorrere a funzioni di trigger, che sono più flessibili, ma un po 'più complicate e soggette a errori e un po' meno rigorose. Il vantaggio: potresti fare a meno part_inst.part_ide pin.part_id...


Ci sono alcune colonne aggiuntive in pin_insts, ma le ho omesse nell'interesse della leggibilità ("Ignora i campi dati, [...]"). Ad esempio, a pin_instpuò essere contrassegnato come input o output.
Snowball,

@Snowball: Sarebbe stato facile essere veri. Ho ampliato un po 'la tua soluzione.
Erwin Brandstetter,

2
Il tuo secondo suggerimento funziona bene per la mia situazione. Non sapevo che una chiave esterna potesse fare riferimento a qualcosa di diverso dalla chiave primaria.
Snowball,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.