Je réfléchis à la façon de représenter une structure complexe dans une base de données SQL Server.
Considérez une application qui doit stocker les détails d'une famille d'objets, qui partagent certains attributs, mais dont beaucoup d'autres ne sont pas communs. Par exemple, un paquet d'assurance commerciale peut inclure une couverture de responsabilité, automobile, propriété et indemnité dans le même dossier de police.
Il est trivial de l'implémenter en C #, etc., car vous pouvez créer une politique avec une collection de sections, où la section est héritée selon les besoins pour les différents types de couverture. Cependant, les bases de données relationnelles ne semblent pas permettre cela facilement.
Je peux voir qu'il y a deux choix principaux:
Créez une table Policy, puis une table Sections, avec tous les champs requis, pour toutes les variantes possibles, dont la plupart seraient nulles.
Créez un tableau des politiques et de nombreux tableaux de sections, un pour chaque type de couverture.
Ces deux alternatives semblent insatisfaisantes, d'autant plus qu'il est nécessaire d'écrire des requêtes dans toutes les sections, ce qui impliquerait de nombreuses jointures ou de nombreuses vérifications nulles.
Quelle est la meilleure pratique pour ce scénario?
Réponses:
@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 une
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:Garder la conception simple est un plus, mais les principaux problèmes de cette approche sont les suivants:
En ce qui concerne l'ajout de nouveaux sous-types, vous devez modifier le tableau pour tenir compte des attributs qui décrivent ces nouveaux objets. Cela peut rapidement devenir problématique lorsque vous disposez de nombreux sous-types ou si vous prévoyez d'ajouter régulièrement des sous-types.
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
les attributs d'un sous-type qui devraient être obligatoires. Vous devrez gérer cela dans votre application, ce qui n'est généralement pas idéal.Héritage de table en béton:
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 de chaque table. Par exemple:
Cette conception résoudra essentiellement les problèmes identifiés pour la méthode à 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 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 d'une stratégie de propriété.Il n'est pas nécessaire pour l'
type
attribut comme dans la méthode de table unique. Le type est désormais 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'existe aucun 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 SEC .
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 devez interroger toutes les stratégies quel que soit le type:
Notez que l'ajout de nouveaux sous-types nécessiterait la modification de la requête ci-dessus avec un supplément
UNION ALL
pour chaque sous-type. Cela peut facilement entraîner des bogues dans votre application si cette opération est oubliée.Héritage de table de classe (alias héritage de 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 devez créer des tables spécifiques pour chaque sous-type, dont la clé primaire sert également de clé étrangère à la table de base. Exemple:
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 existante.
Aucun risque qu'un attribut inapproprié soit défini pour un sous-type particulier.
Pas besoin de l'
type
attribut.Maintenant, les attributs communs ne sont plus mélangés avec les attributs spécifiques au sous-type.
Nous pouvons enfin rester SECS. 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.
La gestion d'une incrémentation automatique
id
pour les stratégies devient plus facile, car elle peut être gérée 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: pas
UNION
besoin de - juste aSELECT * 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 Patterns of Enterprise Application Architecture de Martin Fowler .
la source
La 3ème option consiste à créer une table "Policy", puis une table "SectionsMain" qui stocke tous les champs communs aux différents types de sections. Créez ensuite d'autres tables pour chaque type de section qui ne contiennent que les champs qui ne sont pas communs.
Le choix du meilleur dépend principalement du nombre de champs dont vous disposez et de la façon dont vous souhaitez écrire votre SQL. Ils fonctionneraient tous. Si vous n'avez que quelques champs, j'irais probablement avec # 1. Avec "beaucoup" de champs, je pencherais vers # 2 ou # 3.
la source
Avec les informations fournies, je modéliserais la base de données pour avoir les éléments suivants:
STRATÉGIES
PASSIFS
PROPRIÉTÉS
... et ainsi de suite, car je m'attends à ce qu'il y ait différents attributs associés à chaque section de la stratégie. Sinon, il pourrait y avoir une seule
SECTIONS
table et en plus dupolicy_id
, il y aurait unsection_type_code
...Quoi qu'il en soit, cela vous permettrait de prendre en charge les sections facultatives par politique ...
Je ne comprends pas ce que vous trouvez insatisfaisant dans cette approche - c'est ainsi que vous stockez les données tout en préservant l'intégrité référentielle et en ne dupliquant pas les données. Le terme est "normalisé" ...
Parce que SQL est basé sur SET, il est plutôt étranger aux concepts de programmation procédurale / OO et nécessite du code pour passer d'un domaine à l'autre. Les ORM sont souvent pris en compte, mais ils ne fonctionnent pas bien dans les systèmes complexes à volume élevé.
la source
De plus, dans la solution Daniel Vassallo, si vous utilisez SQL Server 2016+, il existe une autre solution que j'ai utilisée dans certains cas sans perte de performances considérable.
Vous pouvez créer juste une table avec uniquement le champ commun et ajouter une seule colonne avec le JSON chaîne qui contient tous les champs spécifiques au sous-type.
J'ai testé cette conception pour gérer l'héritage et je suis très heureux de la flexibilité que je peux utiliser dans l'application relative.
la source
Une autre façon de le faire est d'utiliser le
INHERITS
composant. Par exemple:Il est ainsi possible de définir un héritage entre les tables.
la source
INHERITS
outre PostgreSQL ? MySQL par exemple?Je me penche vers la méthode n ° 1 (une table de section unifiée), dans le but de récupérer efficacement des politiques entières avec toutes leurs sections (ce que je suppose que votre système fera beaucoup).
De plus, je ne sais pas quelle version de SQL Server vous utilisez, mais dans 2008+ Sparse Columns aident à optimiser les performances dans les situations où la plupart des valeurs d'une colonne seront NULL.
En fin de compte, vous devrez décider à quel point les sections de politique sont "similaires". À moins qu'ils ne diffèrent considérablement, je pense qu'une solution plus normalisée pourrait être plus problématique qu'elle ne vaut ... mais vous seul pouvez faire cet appel. :)
la source
Vous pouvez également envisager d'utiliser des bases de données de documents (telles que MongoDB) qui prennent en charge nativement les structures de données riches et l'imbrication.
la source
Découvrez la réponse que j'ai donnée ici
Mappage un-à-un NHibernate fluide avec des clés synthétiques
la source