Progettazione del database: normalizzazione di una relazione "(da molti a molti) a molti"


13

Versione breve

Devo aggiungere un numero fisso di proprietà aggiuntive a ciascuna coppia in un join molti-a-molti esistente. Saltando ai diagrammi seguenti, quale delle opzioni 1-4 è il modo migliore, in termini di vantaggi e svantaggi, per raggiungere questo obiettivo estendendo il caso di base? Oppure, c'è un'alternativa migliore che non ho considerato qui?

Versione più lunga

Al momento ho due tabelle in una relazione molti-a-molti, tramite una tabella di join intermedia. Ora devo aggiungere ulteriori collegamenti alle proprietà che appartengono alla coppia di oggetti esistenti. Ho un numero fisso di queste proprietà per ogni coppia, anche se una voce nella tabella delle proprietà può essere applicata a più coppie (o addirittura essere utilizzata più volte per una coppia). Sto cercando di determinare il modo migliore per farlo e ho problemi a capire come pensare alla situazione. Semanticamente sembra che possa descriverlo ugualmente bene in uno dei seguenti modi:

  1. Una coppia collegata a un set di un numero fisso di proprietà aggiuntive
  2. Una coppia collegata a molte proprietà aggiuntive
  3. Molti (due) oggetti collegati a un insieme di proprietà
  4. Molti oggetti collegati a molte proprietà

Esempio

Ho due tipi di oggetti, X e Y, ognuno con ID univoci, e una tabella di collegamento objx_objycon colonne x_ide y_id, che insieme formano la chiave primaria per il collegamento. Ogni X può essere correlata a molte Y e viceversa. Questa è l'impostazione per la mia relazione molti-a-molti esistente.

Caso base

Caso base

Ora ho anche una serie di proprietà definite in un'altra tabella e una serie di condizioni in cui una data coppia (X, Y) dovrebbe avere la proprietà P. Il numero di condizioni è fisso e lo stesso per tutte le coppie. Sostanzialmente dicono "Nella situazione C1, la coppia (X1, Y1) ha la proprietà P1", "Nella situazione C2, la coppia (X1, Y1) ha la proprietà P2" e così via, per tre situazioni / condizioni per ogni coppia nel join tavolo.

opzione 1

Nella mia situazione attuale non ci sono esattamente tre queste condizioni, e non ho alcun motivo di aspettarsi che per aumentare, quindi una possibilità è quella di aggiungere colonne c1_p_id, c2_p_ide c3_p_idper featx_featy, specificando per una data x_ide y_id, quale proprietà p_idad uso in ciascuno dei tre casi .

opzione 1

Questa non mi sembra un'ottima idea, perché complica l'SQL a selezionare tutte le proprietà applicate a una funzionalità e non si adatta facilmente a più condizioni. Tuttavia, applica il requisito di un certo numero di condizioni per coppia (X, Y). In effetti, è l'unica opzione qui che lo fa.

opzione 2

Creare una tabella delle condizioni conde aggiungere l'ID condizione alla chiave primaria della tabella dei join.

opzione 2

Un aspetto negativo di questo è che non specifica il numero di condizioni per ciascuna coppia. Un altro è che quando sto solo considerando la relazione iniziale, con qualcosa come

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Devo quindi aggiungere una DISTINCTclausola per evitare voci duplicate. Questo sembra aver perso il fatto che ogni coppia dovrebbe esistere una sola volta.

Opzione 3

Creare un nuovo "ID coppia" nella tabella dei join, quindi disporre di una seconda tabella dei collegamenti tra la prima e le proprietà e le condizioni.

Opzione 3

Questo sembra avere il minor numero di svantaggi, oltre alla mancanza di far rispettare un numero fisso di condizioni per ciascuna coppia. Ha senso però creare un nuovo ID che non identifichi altro che ID esistenti?

Opzione 4 (3b)

Sostanzialmente uguale all'opzione 3, ma senza la creazione del campo ID aggiuntivo. Ciò si ottiene inserendo entrambi gli ID originali nella nuova tabella di join, quindi contiene x_ide y_idcampi, anziché xy_id.

Opzione 4

Un ulteriore vantaggio di questo modulo è che non altera le tabelle esistenti (anche se non sono ancora in produzione). Tuttavia, fondamentalmente duplica un'intera tabella più volte (o comunque si sente così) quindi non sembra l'ideale.

Sommario

La mia sensazione è che le opzioni 3 e 4 siano abbastanza simili da poterle scegliere con entrambe. Probabilmente avrei ormai se non fosse per il requisito di un piccolo numero fisso di collegamenti a proprietà, il che fa sembrare l'opzione 1 più ragionevole di quanto non sarebbe altrimenti. Sulla base di alcuni test molto limitati, l'aggiunta di una DISTINCTclausola alle mie query non sembra influire sulle prestazioni in questa situazione, ma non sono sicuro che l'opzione 2 rappresenti la situazione così come le altre, a causa della duplicazione intrinseca causata dal posizionamento le stesse coppie (X, Y) in più righe della tabella dei collegamenti.

Una di queste opzioni è il mio modo migliore di procedere o esiste un'altra struttura che dovrei prendere in considerazione?


Complessivamente 1 e 4 sembrano le migliori opzioni, sono d'accordo. Non sarebbe facile imporre il numero fisso (3) di proprietà con l'opzione 4, ma penso che sia fattibile.
ypercubeᵀᴹ

Per la DISTINCTclausola, stavo pensando a una query come quella alla fine del n. 2, che collega xe yattraversa xycma non fa riferimento a c... Quindi se ho (x_id, y_id, c_id)vincolato UNIQUEcon le righe (1,1,1)e (1,1,2), quindi SELECT x.id, y.id FROM x JOIN xyc JOIN y, ne restituirò due identici righe (1,1), e (1,1).
Michael Underwood,

1
Ah ok. Respingerei comunque l'opzione 2.
Andrei

Più ci penso, più sento che limitare il numero di proprietà esattamente a tre è il meno importante dei miei requisiti. Quindi, escludendo ulteriori feedback costruttivi nei prossimi minuti, probabilmente andrò con il n. 4 a questo punto. Grazie per il tuo contributo, @ypercube!
Michael Underwood,

Risposte:


7
  • opzione 1

    * Questa non mi sembra un'ottima idea, perché complica l'SQL per selezionare tutte le proprietà applicate a una funzione ...

    Non complica necessariamente la query SQL (vedere la conclusione di seguito).

    ... e non si adatta facilmente a più condizioni ...

    Si adatta rapidamente a più condizioni, purché ci sia ancora un numero fisso di condizioni e non ce ne siano dozzine o centinaia.

    Tuttavia, applica il requisito di un certo numero di condizioni per coppia (X, Y). In effetti, è l'unica opzione qui che lo fa. *

    Sì, e sebbene tu dica in un commento che questo è "il meno importante dei miei requisiti", non hai detto che non importa affatto.

  • opzione 2

    Un aspetto negativo di questo è che non specifica il numero di condizioni per ciascuna coppia. Un altro è che quando sto solo considerando la relazione iniziale ... devo quindi aggiungere una clausola DISTINCT per evitare voci duplicate ...

    Penso che tu possa rifiutare questa opzione a causa delle complicazioni che menzioni. Il objx_objytavolo è probabile che sia il tavolo di guida per alcune delle vostre domande (ad esempio, "selezionare tutte le proprietà applicate a una funzione", che sto prendendo per indicare tutte le proprietà applicate a una objxo objy). È possibile utilizzare una vista per pre-applicare, DISTINCTquindi non si tratta di complicare le query, ma si ridurrà in modo molto peggiore dal punto di vista delle prestazioni per un guadagno molto ridotto.

  • Opzione 3

    Ha senso però creare un nuovo ID che non identifichi altro che ID esistenti?

    No, non è così: l'opzione 4 è migliore sotto tutti gli aspetti.

  • Opzione 4

    ... praticamente duplica un intero tavolo più volte (o comunque si sente così) quindi non sembra l'ideale.

    Questa opzione è perfetta: è il modo ovvio di impostare le relazioni se il numero di proprietà è variabile o soggetto a modifiche

Conclusione

La mia preferenza sarebbe l'opzione 1 se il numero di proprietà per objx_objyè probabile che sia stabile e se non riesci a immaginare di aggiungere mai più di una manciata in più. È anche l'unica opzione che impone il vincolo 'numero di proprietà = 3' - imporre un vincolo simile sull'opzione 4 implicherebbe probabilmente l'aggiunta di c1_p_id... colonne alla tabella xy *.

Se davvero non ti interessa molto di quella condizione, e hai anche motivo di dubitare che il numero di proprietà sia stabile, allora scegli l'opzione 4.

Se non sei sicuro di quale, scegli l'opzione 1: è più semplice e sicuramente meglio se hai l'opzione, come altri hanno già detto. Se si rimanda all'opzione 1 "... perché complica l'SQL per selezionare tutte le proprietà applicate a una funzione ..." Suggerisco di creare una vista per fornire gli stessi dati della tabella aggiuntiva nell'opzione 4:

tabelle opzione 1:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

vista per 'emulare' l'opzione 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"seleziona tutte le proprietà applicate a una funzione":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle qui


-3

Credo che una di queste opzioni potrebbe funzionare, ma andrei con l'opzione 1 se il numero di condizioni è veramente fissato a 3 e l'opzione 2 se non lo è. Il rasoio di Occam funziona anche per la progettazione di database, a parità di tutti gli altri fattori il design più semplice è in genere il migliore.

Anche se se vuoi seguire rigide regole di normalizzazione del database, credo che dovresti andare con 2 indipendentemente dal fatto che il numero di condizioni sia fisso.

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.