2009-11-13 2 views
11

Quelle est la meilleure façon d'émuler Tagged union dans des bases de données? Je parle de quelque chose comme ceci:Comment émuler une union étiquetée dans une base de données?

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

où vehicle_id serait id dans la table de voiture ou une table de moteur, et il connaîtrait qui.

(en supposant que le moteur et les tables voiture ont rien dans common0

Répondre

9

Certaines personnes utilisent un design appelé Les associations polymorphes pour ce faire, ce qui permet vehicle_id contenir une valeur qui existe soit dans car ou motor tables. Ensuite, ajouter un vehicle_type que les noms de la table qui la ligne donnée dans t1 références.

le problème est que vous ne pouvez pas déclarer une véritable SQL contrainte de clé étrangère si vous faites cela. Il n'y a pas de support dans SQL pour une clé étrangère qui a multiplier les cibles de référence. Il y a aussi d'autres problèmes, mais le manque d'intégrité référentielle est déjà un problème.

Une meilleure conception est d'emprunter un concept de la conception orientée objet d'un supertype commun des deux car et motor:

CREATE TABLE Identifiable (
id SERIAL PRIMARY KEY 
); 

Ensuite, faire t1 référence ce tableau supertype:

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

Et faites également les sous-types référencer leur supertype parent. Notez que la clé primaire des sous-types est et non auto-incrémentée. Le supertype parent prend en charge l'allocation d'une nouvelle valeur d'identifiant, et les enfants ne font référence qu'à cette valeur. Maintenant, vous pouvez avoir une véritable intégrité référentielle, mais aussi prendre en charge plusieurs tables de sous-types avec leurs propres attributs.


La réponse par @Quassnoi montre également une méthode pour appliquer sous-types disjoints. Autrement dit, vous voulez empêcher à la fois car et motor de référencer la même ligne dans leur table de type supertype parent. Lorsque je fais cela, j'utilise une clé primaire à colonne unique pour Identifiable.id mais déclare également une clé UNIQUE sur Identifiable.(id, type). Les clés étrangères dans car et motor peuvent faire référence à la clé unique à deux colonnes au lieu de la clé primaire.

+0

La clé de substitution pour 'identifiable' n'est bonne que s'il y a des attributs dans' identifiable' que la requête doit sélectionner. Si 'identifiable 'ne sert qu'à imposer les contraintes, l'utilisation d'une clé composite permettra de s'en débarrasser dans les requêtes de manière compétente. – Quassnoi

+1

J'ai inventé et utilisé moi-même l'approche «common supertype», et je l'ai utilisée avec succès dans des projets majeurs de migration/redéveloppement de systèmes. (Gouvernement NZ, SPOT25 pour 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

Il ferait la vie est plus facile si vous avez défini 'VEHICLE.VEHICLE_ID' comme clé primaire, donc vous n'avez pas besoin de référencer une clé composite, et avez des colonnes type & id utilisant une contrainte unique. –

+0

'@OMG Ponies:' Avec cette mise en page, vous n'avez pas besoin de référencer 'véhicule'.Vous pouvez simplement vous joindre à «voitures» ou «motos», en fonction du «type». 'véhicule' ici sert uniquement à la police des relations. – Quassnoi

+0

Avec cette méthode, existe-t-il un moyen de garantir qu'il n'y aura pas de véhicules «orphelins» n'ayant pas de rangée correspondante dans «voiture» ou «motocyclette»? –

3

Je pense que vous pourriez modéliser une telle référence en utilisant table inheritance in PostgreSQL.

Si vous avez vraiment besoin de savoir où une ligne vient dans une requête, vous pouvez utiliser un simple UNION ALL statment comme (cette possibilité n'a rien à voir avec l'héritage de table):

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