Qual è il modo migliore per gestire un "sindacato" in un database relazionale?

2

Mi imbatto in un fastidioso problema al lavoro, in cui il modello di dominio non si adatta bene al modello relazionale del nostro database Postgres.

Ho una serie di tabelle primarie A, B, C, ecc. Quindi c'è quest'altra tabella X, che funge fondamentalmente da una sotto-raccolta per ciascuna delle tabelle principali. Quindi, voglio una relazione uno-a-molti da ciascuna delle tabelle primarie a X. Normalmente, una relazione molti-a-uno viene modellata aggiungendo una chiave esterna da X alla tabella primaria. Tuttavia, in questo caso, credo che avrei bisogno di una chiave esterna da X a ogni delle tabelle A, B, C, ecc. Questo mi sembra janky, specialmente dal momento che esattamente una di queste chiavi dovrebbe essere non nullo in qualsiasi momento, ad es una tupla in X non dovrebbe appartenere ad A e B.

Avere a_id , b_id , c_id attributi in X la soluzione migliore oppure esiste un approccio alternativo?

    
posta gardenhead 14.07.2017 - 21:59
fonte

3 risposte

4

Se X ha una relazione 1: M con A, B e C (essendo X nel lato M) ma vuoi che l'FK indichi A e quello che punta a B si escluda a vicenda, devi aggiungere un assegno vincolo alla tabella.

 
ALTER TABLE A ADD CONSTRAINT check_001 CHECK 
    ((a_id IS NULL and b_id IS NOT NULL) OR 
    (a_id IS NOT NULL and b_id IS NULL));
    
risposta data 14.07.2017 - 22:27
fonte
3

La soluzione normalizzata standard "dal libro" consiste nell'aggiungere le tabelle di collegamento X_A, X_B, X_C ciascuna con due chiavi esterne. Ad esempio, in X_A:

  • a_id che fa riferimento a A
  • x_id che fa riferimento a X, che rappresenta una relazione 1: 1!

Tuttavia, se vale davvero la pena di creare queste tabelle aggiuntive, o se si può vivere con una soluzione meno normalizzata con a_id, b_id, c_id tutto in X, dipende molto dal caso.

    
risposta data 14.07.2017 - 22:14
fonte
2

La semplice risposta è di capovolgere la dipendenza dalla chiave.

La tabella X dovrebbe avere una singola chiave primaria, forse denominata xid , che è unica nell'intera tabella. Non sono necessarie chiavi esterne.

Ogni riga in A , B , C e D dovrebbe avere anche una colonna denominata xid . Se la relazione è veramente 1: 1, allora xid potrebbe anche servire come chiave primaria per A , B , C e D pure.

Per unirti a loro userai

SELECT *
FROM   X
LEFT JOIN A on A.xid = X.xid
LEFT JOIN B on B.xid = X.xid
LEFT JOIN C on C.xid = X.xid
LEFT JOIN D on D.xid = X.xid

Quanto sopra è solo un sottotipo / supertype implementazione del database , niente di particolarmente insolito a riguardo.

Se sei molto preoccupato per quei join extra che causano I / O non necessari, e solo uno tra {A, B, C, D} sarà mai valido per una data X, potresti anche aggiungere un attributo aggiuntivo per aiutarti query trova la tabella giusta. Basta aggiungere una colonna aggiuntiva a X , forse denominata xtype , contenente un singolo carattere ("A", "B", "C" o "D"), quindi scrivere i join in questo modo:

SELECT *
FROM   X
LEFT JOIN A on A.xid = X.xid AND x.xtype = 'A'
LEFT JOIN B on B.xid = X.xid AND x.xtype = 'B'
LEFT JOIN C on C.xid = X.xid AND x.xtype = 'C'
LEFT JOIN D on D.xid = X.xid AND x.xtype = 'D' 

Questo finirà per essere leggermente più efficiente, perché il motore di query non si preoccuperà nemmeno di provare a fare un join in A, B, C o D a seconda di xtype . Lo svantaggio in questo caso è che devi mantenere xtype aggiornato e non esiste alcun vincolo implicito che ti impedisca di impostare un xid che punta a A ma a xtype che punta a B . Dovresti implementare questo vincolo da solo.

    
risposta data 14.07.2017 - 22:34
fonte

Leggi altre domande sui tag