@Bill Karwin décrit trois modèles d'héritage dans son livre SQL Antipatterns, lorsqu'il propose des solutions à l'antipattern SQL Entity-Attribute-Value. Voici un bref aperçu :
Héritage de table unique (alias héritage de table par hiérarchie) :
L'utilisation d'une seule table comme dans votre première option est probablement la conception la plus simple. Comme vous l'avez mentionné, de nombreux attributs spécifiques au sous-type devront recevoir un NULL
valeur sur les lignes où ces attributs ne s'appliquent pas. Avec ce modèle, vous auriez une table de stratégies, qui ressemblerait à ceci :
+------+---------------------+----------+----------------+------------------+
| id | date_issued | type | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
| 1 | 2010-08-20 12:00:00 | MOTOR | 01-A-04004 | NULL |
| 2 | 2010-08-20 13:00:00 | MOTOR | 02-B-01010 | NULL |
| 3 | 2010-08-20 14:00:00 | PROPERTY | NULL | Oxford Street |
| 4 | 2010-08-20 15:00:00 | MOTOR | 03-C-02020 | NULL |
+------+---------------------+----------+----------------+------------------+
\------ COMMON FIELDS -------/ \----- SUBTYPE SPECIFIC FIELDS -----/
Garder le design simple est un plus, mais les principaux problèmes avec cette approche sont les suivants :
-
Lorsqu'il s'agit d'ajouter de nouveaux sous-types, vous devrez modifier le tableau pour tenir compte des attributs qui décrivent ces nouveaux objets. Cela peut rapidement devenir problématique lorsque vous avez de nombreux sous-types, ou si vous prévoyez d'en ajouter régulièrement.
-
La base de données ne pourra pas appliquer les attributs qui s'appliquent et ceux qui ne le sont pas, car il n'y a pas de métadonnées pour définir quels attributs appartiennent à quels sous-types.
-
Vous ne pouvez pas non plus appliquer
NOT NULL
sur les attributs d'un sous-type qui devraient être obligatoires. Vous auriez à gérer cela dans votre application, ce qui n'est généralement pas idéal.
Héritage de table concrète :
Une autre approche pour lutter contre l'héritage consiste à créer une nouvelle table pour chaque sous-type, en répétant tous les attributs communs dans chaque table. Par exemple :
--// Table: policies_motor
+------+---------------------+----------------+
| id | date_issued | vehicle_reg_no |
+------+---------------------+----------------+
| 1 | 2010-08-20 12:00:00 | 01-A-04004 |
| 2 | 2010-08-20 13:00:00 | 02-B-01010 |
| 3 | 2010-08-20 15:00:00 | 03-C-02020 |
+------+---------------------+----------------+
--// Table: policies_property
+------+---------------------+------------------+
| id | date_issued | property_address |
+------+---------------------+------------------+
| 1 | 2010-08-20 14:00:00 | Oxford Street |
+------+---------------------+------------------+
Cette conception résoudra essentiellement les problèmes identifiés pour la méthode de table unique :
-
Les attributs obligatoires peuvent désormais être appliqués avec
NOT NULL
. -
L'ajout d'un nouveau sous-type nécessite l'ajout d'une nouvelle table au lieu d'ajouter des colonnes à une table existante.
-
Il n'y a également aucun risque qu'un attribut inapproprié soit défini pour un sous-type particulier, tel que le
vehicle_reg_no
champ pour une politique de propriété. -
Il n'y a pas besoin du
type
attribut comme dans la méthode de table unique. Le type est maintenant défini par les métadonnées :le nom de la table.
Cependant, ce modèle présente également quelques inconvénients :
-
Les attributs communs sont mélangés avec les attributs spécifiques au sous-type, et il n'y a pas de moyen facile de les identifier. La base de données ne le saura pas non plus.
-
Lors de la définition des tables, vous devrez répéter les attributs communs pour chaque table de sous-type. Ce n'est certainement pas DRY.
-
La recherche de toutes les politiques, quel que soit le sous-type, devient difficile et nécessiterait un tas de
UNION
s.
Voici comment vous auriez à interroger toutes les politiques, quel que soit leur type :
SELECT date_issued, other_common_fields, 'MOTOR' AS type
FROM policies_motor
UNION ALL
SELECT date_issued, other_common_fields, 'PROPERTY' AS type
FROM policies_property;
Notez que l'ajout de nouveaux sous-types nécessiterait que la requête ci-dessus soit modifiée avec un UNION ALL
supplémentaire pour chaque sous-type. Cela peut facilement entraîner des bogues dans votre application si cette opération est oubliée.
Héritage de la table de classe (ou héritage de la table par type) :
C'est la solution que @David mentionne dans l'autre réponse. Vous créez une table unique pour votre classe de base, qui comprend tous les attributs communs. Ensuite, vous créez des tables spécifiques pour chaque sous-type, dont la clé primaire sert également de clé étrangère à la table de base. Exemple :
CREATE TABLE policies (
policy_id int,
date_issued datetime,
-- // other common attributes ...
);
CREATE TABLE policy_motor (
policy_id int,
vehicle_reg_no varchar(20),
-- // other attributes specific to motor insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
CREATE TABLE policy_property (
policy_id int,
property_address varchar(20),
-- // other attributes specific to property insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
Cette solution résout les problèmes identifiés dans les deux autres conceptions :
-
Les attributs obligatoires peuvent être appliqués avec
NOT NULL
. -
L'ajout d'un nouveau sous-type nécessite l'ajout d'une nouvelle table au lieu d'ajouter des colonnes à une table existante.
-
Aucun risque qu'un attribut inapproprié soit défini pour un sous-type particulier.
-
Pas besoin du
type
attribut. -
Désormais, les attributs communs ne sont plus mélangés avec les attributs spécifiques au sous-type.
-
On peut rester au SEC, enfin. Il n'est pas nécessaire de répéter les attributs communs pour chaque table de sous-type lors de la création des tables.
-
Gestion d'un
id
à incrémentation automatique pour les politiques devient plus facile, car cela peut être géré par la table de base, au lieu que chaque table de sous-type les génère indépendamment. -
La recherche de toutes les politiques quel que soit le sous-type devient désormais très simple :Non
UNION
s nécessaire - juste uneSELECT * FROM policies
.
Je considère l'approche de la table de classe comme la plus appropriée dans la plupart des situations.
Les noms de ces trois modèles proviennent du livre de Martin Fowler, Patterns of Enterprise Application Architecture.