Progettazione del database: da 2 a molte relazioni con la stessa tabella


20

Devo modellare una situazione in cui ho una tabella Chequing_Account (che contiene budget, numero iban e altri dettagli dell'account) che deve essere correlata a due diverse tabelle Person e Corporation che possono avere entrambi 0, 1 o molti account chequing.

In altre parole, ho due relazioni da 1 a molte con lo stesso account di Chequing tabella

Vorrei ascoltare soluzioni per questo problema che rispettano i requisiti di normalizzazione. La maggior parte delle soluzioni che ho sentito in giro sono:

1) trova un'entità comune di cui appartengono sia Person che Corporation e crea una tabella di collegamenti tra questa e la tabella Chequing_Account, questo non è possibile nel mio caso e anche se lo fosse, voglio risolvere il problema generale e non questa specifica istanza.

2) Creare due tabelle di collegamenti PersonToChequingAccount e CorporationToChequingAccount che mettono in relazione le due entità con gli Account Chequing. Tuttavia, non voglio che due persone abbiano lo stesso account di chequing, e non voglio che una persona fisica e una società condividano un account di chequing! vedi questa immagine

http://i41.tinypic.com/35i6kbk.png

3) Creare due chiavi esterne nel Conto Chequing che rimandano a Corporazione e Persona fisica, tuttavia imporrei quindi che una Persona e una Società possano avere molti conti Chequing, ma dovrei assicurarmi manualmente che per ogni riga del Conto Chequing non entrambe le relazioni rimandino a Società e persona fisica perché un account di checquing è di una società o di una persona fisica. vedi questa immagine

http://i40.tinypic.com/1rpv9z.png

Esiste un'altra soluzione più pulita a questo problema?


Avete mai pensato di avere per esempio una OwnerTypeIDnel ChecquingAccounttavolo, con 1=Corporatione 2=NaturalPerson? In questo modo ne hai bisogno solo uno OwnerIDnella ChecquingAccounttabella, che puoi indicizzare insieme a OwnerTypeID.
RoKa,

Ho bisogno non solo di sapere se si tratta di una società o di una persona fisica, ma anche di conoscere il rispettivo ID, quindi ho bisogno di un numero ID e non solo di 1 o 2! La soluzione 3 è quella che ho scoperto qui che ho due colonne con ID per corporazione o persona fisica
dendini,

2
Sì, la soluzione è un'opzione valida. Nella maggior parte dei DBMS è possibile imporre che solo uno dei due FK sia "attivo" con un vincolo di controllo: CHECK (CorporationID IS NOT NULL AND NaturalPersonID IS NULL OR CorporationID IS NULL AND NaturalPersonID IS NOT NULL)preferisco di gran lunga la soluzione 1 (ma sono solo io). È molto "più pulito".
ypercubeᵀᴹ

Sì, ho capito, ma potresti avere ChecquingAccountun record nella tabella OwnerTypeID=1e OwnerID=123, indicando che è un tipo Corporation, quindi ID 123nella Corporationtabella. OwnerTypeID ti dice quale tabella e OwnerID ti dice l'ID in quella tabella.
RoKa,

1
Come è impossibile l'opzione n. 1? La parola "Corporation" significa fondamentalmente "un'azienda legalmente una persona", dopo tutto. Chiamalo un Customerstavolo.
Jon of All Trades,

Risposte:


15

I database relazionali non sono creati per gestire perfettamente questa situazione. Devi decidere cosa è più importante per te e poi fare i tuoi compromessi. Hai diversi obiettivi:

  • Mantieni la terza forma normale
  • Mantenere l'integrità referenziale
  • Mantenere il vincolo che ogni account appartiene a una società o una persona fisica.
  • Preservare la capacità di recuperare i dati in modo semplice e diretto

Il problema è che alcuni di questi obiettivi competono tra loro.

Soluzione di
sottotipo Puoi scegliere una soluzione di sottotipo in cui crei un super-tipo che incorpori sia le società che le persone. Questo super-tipo avrebbe probabilmente una chiave composta dalla chiave naturale del sottotipo più un attributo di partizionamento (ad es customer_type.). Questo va bene per quanto riguarda la normalizzazione e ti permette di imporre l'integrità referenziale così come il vincolo che le società e le persone si escludono a vicenda. Il problema è che ciò rende più difficile il recupero dei dati, perché devi sempre diramarti in base a customer_typequando unisci l'account al titolare dell'account. Questo probabilmente significa usare UNIONe avere un sacco di SQL ripetitivo nella tua query.

Soluzione con
due chiavi esterne È possibile scegliere una soluzione in cui tenere due chiavi esterne nella tabella del proprio account, una alla società e una alla persona. Questa soluzione consente inoltre di mantenere l'integrità referenziale, la normalizzazione e l'esclusività reciproca. Ha anche lo stesso inconveniente di recupero dei dati della soluzione di sotto-digitazione. In effetti, questa soluzione è proprio come la soluzione di sotto-digitazione, tranne per il fatto che si arriva al problema di ramificare la logica di giunzione "prima".

Tuttavia, molti modellatori di dati considererebbero questa soluzione inferiore alla soluzione di sotto-tipizzazione a causa del modo in cui viene applicato il vincolo di mutua esclusività. Nella soluzione di digitazione secondaria si utilizzano i tasti per applicare l'esclusività reciproca. Nella soluzione con due chiavi esterne si utilizza un CHECKvincolo. Conosco alcune persone che hanno un pregiudizio ingiustificato contro i vincoli di controllo. Queste persone preferirebbero la soluzione che mantiene i vincoli nelle chiavi.

Soluzione di attributo di partizionamento "denormalizzato"
Esiste un'altra opzione in cui si mantiene una singola colonna di chiave esterna sulla tabella degli account chequing e si utilizza un'altra colonna per indicare come interpretare la colonna di chiave esterna (RoKa'sOwnerTypeIDcolonna). Questo essenzialmente elimina la tabella dei super-tipi nella soluzione di sotto-digitazione denormalizzando l'attributo di partizionamento nella tabella figlio. (Si noti che questa non è strettamente "denormalizzazione" secondo la definizione formale, perché l'attributo di partizionamento fa parte di una chiave primaria.) Questa soluzione sembra abbastanza semplice poiché evita di avere una tabella aggiuntiva per fare più o meno la stessa cosa e riduce il numero di colonne di chiave esterna a una. Il problema con questa soluzione è che non evita la ramificazione della logica di recupero e, inoltre, non consente di mantenere l' integrità referenziale dichiarativa . I database SQL non hanno la capacità di gestire una singola colonna di chiave esterna per una delle tabelle padre multiple.

Soluzione di dominio chiave primaria condivisa
Un modo in cui le persone a volte affrontano questo problema è utilizzare un singolo pool di ID in modo che non ci sia confusione per un determinato ID se appartiene a un sottotipo o a un altro. Questo probabilmente funzionerebbe in modo abbastanza naturale in uno scenario bancario, dal momento che non emetterete lo stesso numero di conto bancario sia a una società che a una persona fisica. Ciò ha il vantaggio di evitare la necessità di un attributo di partizionamento. Puoi farlo con o senza una tabella di super-tipo. L'uso di una tabella dei super-tipi consente di utilizzare i vincoli dichiarativi per applicare l'univocità. Altrimenti ciò dovrebbe essere applicato proceduralmente. Questa soluzione è normalizzata ma non consente di mantenere l'integrità referenziale dichiarativa se non si mantiene la tabella dei super-tipi. Non fa ancora nulla per evitare complesse logiche di recupero.

Potete quindi vedere che non è davvero possibile avere un design pulito che segua tutte le regole, mantenendo allo stesso tempo semplice il recupero dei dati. Devi decidere dove saranno i tuoi compromessi.


La mia soluzione n. 2 a quale dei tuoi quattro gruppi appartiene? La "soluzione di attributo di partizionamento denormalizzato" non è abbastanza chiara per me ..
dendini,

@dendini - La tua soluzione numero 2 non si adatta a nessuna delle soluzioni che ho delineato. Questo perché non corrisponde al requisito di un account appartenente a una persona giuridica. Nel modo in cui hai definito le chiavi primarie delle tabelle intermedie, sono intersezioni molti-a-molti. Se le chiavi primarie fossero giuste corporation_id e person_idallora avresti essenzialmente la soluzione di sotto-digitazione, tranne per il fatto che la tabella dei super-tipi sarebbe stata divisa in due e la chiave esterna sarebbe stata invertita, quindi le persone non potevano tenere più account. Questo tipo di sconfigge lo scopo.
Joel Brown,

Ottima spiegazione @JoelBrown, quali sono le implicazioni in termini di prestazioni della soluzione "Due chiavi esterne", in termini di query? Inoltre, considerando che invece di 2, potrebbero esserci 6 o più chiavi esterne: consiglieresti comunque questo approccio o preferiresti piuttosto un altro?
Amadeo Gallardo,

1
@AmadeoGallardo La risposta è "dipende". L'interrogazione su una chiave è sempre piuttosto efficiente, poiché in genere si può contare su una scansione dell'indice almeno, se non una ricerca, e queste sono operazioni rapide. Il problema si presenta quando si esegue una query su entrambe le chiavi nella soluzione di due chiavi esterne . Qui stai chiedendo a Query Optimizer di eseguire un'operazione / o. Nella migliore delle ipotesi, questo raddoppierà il costo della query, di solito un po 'peggio, poiché è necessario eseguire una query su una chiave, quindi sull'altra, quindi unire i risultati.
Joel Brown,

@JoelBrown Le future versioni SQL denormalizzate dovrebbero consentire questo approccio consentendo la definizione di una chiave esterna composta basata su due colonne RefIDe RefTabledove RefTableè un ID fisso che identifica la tabella di destinazione. Esistono molti casi d'uso per questo tipo di chiave ed è troppo per mantenere 10 o più tabelle di associazione / sottotipo per imporre l'integrità. Per quei casi l'ho creato da keysolo.
djmj,
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.