2009-11-13 3 views
11

Qual è il modo migliore per emulare Tagged union nei database? Sto parlando di qualcosa di simile:Come emulare l'unione con tag in un database?

create table t1 { 
    vehicle_id INTEGER NOT NULL REFERENCES car(id) OR motor(id) -- not valid 
    ... 
} 

dove vehicle_id sarebbe id nella tabella auto o tavolo del motore, e sarebbe sapere quale.

(supporre che i tavoli del motore e di automobili non hanno nulla in common0

risposta

9

Alcune persone usano un design chiamato polimorfi Associazioni per fare questo, permettendo vehicle_id per contenere un valore che esiste sia in car o motor tabelle. Poi aggiungere un vehicle_type che i nomi tavolo che la riga data in t1 riferimenti.

il guaio è che non è possibile dichiarare un vero SQL vincolo di chiave esterna se fate questo. non c'è alcun supporto in SQL per una chiave esterna che ha multipl e obiettivi di riferimento. Ci sono anche altri problemi, ma la mancanza di integrità referenziale è già un rompicapo.

Un disegno migliore è prendere in prestito un concetto dalla progettazione OO di un supertipo comune sia car e motor:

CREATE TABLE Identifiable (
id SERIAL PRIMARY KEY 
); 

Poi fare riferimento t1 questa tabella supertype:

CREATE TABLE t1 (
    vehicle_id INTEGER NOT NULL, 
    FOREIGN KEY (vehicle_id) REFERENCES identifiable(id) 
    ... 
); 

E anche fare in modo che i sottotipi facciano riferimento al loro supertipo genitore. Si noti che la chiave primaria dei sottotipi è non autoincremento. Il supertipo genitore si occupa di allocare un nuovo valore id, e i bambini fanno riferimento solo a quel valore.

CREATE TABLE car (
    id INTEGER NOT NULL, 
    FOREIGN KEY (id) REFERENCES identifiable(id) 
    ... 
); 

CREATE TABLE motor (
    id INTEGER NOT NULL, 
    FOREIGN KEY (id) REFERENCES identifiable(id) 
    ... 
); 

Ora è possibile avere una vera integrità referenziale, ma anche supportare più tabelle di sottotipi con i propri attributi.


La risposta di @Quassnoi mostra pure un metodo per applicare sottotipi disgiunti. Cioè, si desidera impedire sia a car e a motor di fare riferimento alla stessa riga nella tabella dei supertipi padre. Quando eseguo questa operazione, utilizzo una chiave primaria a singola colonna per Identifiable.id ma dichiaro anche una chiave UNIQUE su Identifiable.(id, type). Le chiavi esterne in car e motor possono fare riferimento alla chiave univoca a due colonne anziché alla chiave primaria.

+0

La chiave surrogata per 'identificabile 'è valida solo quando ci sono attributi in' identificabile 'che la query deve selezionare. Se 'identificabile 'serve solo a far rispettare i vincoli, l'uso di una chiave composita consente di eliminarlo in modo competente nelle query. – Quassnoi

+1

Ho ideato e utilizzato l'approccio "comune supertipo" e l'ho usato con successo nei principali progetti di migrazione/sviluppo del sistema. (Governo NZ, SPOT25 per MoE) –

5
CREATE TABLE vehicle (type INT NOT NULL, id INT NOT NULL, 
      PRIMARY KEY (type, id) 
) 

CREATE TABLE car (type INT NOT NULL DEFAULT 1, id INT NOT NULL PRIMARY KEY, 
      CHECK(type = 1), 
      FOREIGN KEY (type, id) REFERENCES vehicle 
) 

CREATE TABLE motorcycle (type INT NOT NULL DEFAULT 2, id INT NOT NULL PRIMARY KEY, 
      CHECK(type = 2), 
      FOREIGN KEY (type, id) REFERENCES vehicle 
) 

CREATE TABLE t1 (
    ... 
    vehicle_type INT NOT NULL, 
    vehicle_id INT NOT NULL, 
    FOREIGN KEY (vehicle_type, vehicle_id) REFERENCES vehicle 
    ... 
) 
+0

Sarebbe fare la vita è più semplice se hai definito "VEHICLE.VEHICLE_ID" come chiave primaria in modo da non dover fare riferimento a una chiave composita e avere colonne type & id che utilizzano un vincolo univoco. –

+0

'Ponts @OMG:' Con questo layout, non è necessario fare riferimento a 'vehicle' affatto.Puoi semplicemente unirti a 'cars' o' motocicli', a seconda del 'tipo'. 'Veicolo' qui serve solo a controllare le relazioni. – Quassnoi

+0

Con questo metodo, c'è un modo per garantire che non ci saranno veicoli "orfani" che non hanno una riga corrispondente in "auto" o "moto"? –

3

Penso che si possa modellare tale riferimento usando table inheritance in PostgreSQL.

Se avete veramente bisogno di sapere dove una fila proviene da in una query, è possibile utilizzare un semplice UNION ALL statment come (questa possibilità non ha nulla a che fare con l'ereditarietà delle tabelle):

SELECT car.*, 'car' table_name 
UNION ALL 
SELECT motor.*, 'motor' table_name