Quelles sont les meilleures pratiques pour modéliser l'héritage dans les bases de données?
Quels sont les compromis (par exemple, queriability)?
(Je suis particulièrement intéressé par SQL Server et .NET, mais je souhaite également comprendre comment d'autres plates-formes résolvent ce problème.)
.net
sql-server
oop
inheritance
database-design
Même Mien
la source
la source
Réponses:
Il existe plusieurs façons de modéliser l'héritage dans une base de données. Ce que vous choisissez dépend de vos besoins. Voici quelques options:
Table par type (TPT)
Chaque classe a sa propre table. La classe de base contient tous les éléments de la classe de base, et chaque classe qui en dérive a sa propre table, avec une clé primaire qui est également une clé étrangère de la table de classe de base; la classe de la table dérivée contient uniquement les différents éléments.
Donc par exemple:
Cela donnerait des tableaux comme:
Table par hiérarchie (TPH)
Il existe une table unique qui représente toute la hiérarchie d'héritage, ce qui signifie que plusieurs colonnes seront probablement rares. Une colonne discriminante est ajoutée qui indique au système de quel type de ligne il s'agit.
Compte tenu des classes ci-dessus, vous vous retrouvez avec ce tableau:
Pour toutes les lignes de type 0 (Person), la date de début sera toujours nulle.
Table par béton (TPC)
Chaque classe a sa propre table entièrement formée sans aucune référence à d'autres tables.
Compte tenu des classes ci-dessus, vous vous retrouvez avec ces tables:
la source
Une bonne conception de base de données n'a rien à voir avec une conception d'objet appropriée.
Si vous envisagez d'utiliser la base de données pour autre chose que la simple sérialisation de vos objets (tels que des rapports, des requêtes, une utilisation multi-applications, une veille stratégique, etc.), je ne recommande aucun type de simple mappage d'objets vers des tables.
Beaucoup de gens considèrent une ligne dans une table de base de données comme une entité (j'ai passé de nombreuses années à réfléchir en ces termes), mais une ligne n'est pas une entité. C'est une proposition. Une relation de base de données (c.-à-d., Table) représente un énoncé de fait sur le monde. La présence de la ligne indique que le fait est vrai (et inversement, son absence indique que le fait est faux).
Avec cette compréhension, vous pouvez voir qu'un seul type dans un programme orienté objet peut être stocké dans une douzaine de relations différentes. Et une variété de types (unis par héritage, association, agrégation ou complètement non affiliés) peuvent être partiellement stockés dans une seule relation.
Il est préférable de vous demander quels faits voulez-vous stocker, à quelles questions voulez-vous des réponses, quels rapports voulez-vous générer.
Une fois la conception de base de données appropriée créée, il suffit de créer des requêtes / vues qui vous permettent de sérialiser vos objets dans ces relations.
Exemple:
Dans un système de réservation d'hôtel, vous devrez peut-être mémoriser le fait que Jane Doe a réservé une chambre au Seaview Inn du 10 au 12 avril. Est-ce un attribut de l'entité cliente? Est-ce un attribut de l'entité hôtelière? S'agit-il d'une entité de réservation avec des propriétés qui incluent le client et l'hôtel? Il peut s'agir de tout ou partie de ces éléments dans un système orienté objet. Dans une base de données, ce n'est rien de tout cela. C'est simplement un simple fait.
Pour voir la différence, considérez les deux requêtes suivantes. (1) Combien de réservations d'hôtel Jane Doe a-t-elle pour l'année prochaine? (2) Combien de chambres sont réservées pour le 10 avril au Seaview Inn?
Dans un système orienté objet, la requête (1) est un attribut de l'entité client et la requête (2) est un attribut de l'entité hôtelière. Ce sont les objets qui exposeraient ces propriétés dans leurs API. (Cependant, de toute évidence, les mécanismes internes par lesquels ces valeurs sont obtenues peuvent impliquer des références à d'autres objets.)
Dans un système de base de données relationnelle, les deux requêtes examineraient la relation de réservation pour obtenir leurs numéros et, conceptuellement, il n'est pas nécessaire de s'embêter avec une autre «entité».
C'est donc en essayant de stocker des faits sur le monde - plutôt qu'en essayant de stocker des entités avec des attributs - qu'une base de données relationnelle appropriée est construite. Et une fois qu'elle est correctement conçue, les requêtes utiles qui n'étaient pas envisagées pendant la phase de conception peuvent être facilement construites, car tous les faits nécessaires pour répondre à ces requêtes sont à leur place.
la source
Employment
tableau qui recueille tous les emplois avec leurs dates de début. Donc, s'ilEmployer
est important de connaître la date de début d'emploi actuelle d'un an , cela pourrait être un cas d'utilisation approprié pour aView
, qui inclut cette propriété en interrogeant? (note: il sembleRéponse courte: vous ne le faites pas.
Si vous avez besoin de sérialiser vos objets, utilisez un ORM, ou mieux quelque chose comme activerecord ou prevaylence.
Si vous avez besoin de stocker des données, stockez-les de manière relationnelle (en faisant attention à ce que vous stockez et en faisant attention à ce que Jeffrey L. Whitledge vient de dire), pas affectée par la conception de votre objet.
la source
Les modèles TPT, TPH et TPC sont la voie à suivre, comme mentionné par Brad Wilson. Mais quelques notes:
les classes enfants héritant d'une classe de base peuvent être considérées comme des entités faibles par rapport à la définition de classe de base dans la base de données, ce qui signifie qu'elles dépendent de leur classe de base et ne peuvent exister sans elle. J'ai vu un certain nombre de fois que des identifiants uniques sont stockés pour chaque table enfant tout en conservant le FK dans la table parent. Un FK est juste suffisant et il est encore mieux d'avoir une activation en cascade lors de la suppression pour la relation FK entre les tables enfant et de base.
Dans TPT, en ne voyant que les enregistrements de la table de base, vous ne pouvez pas trouver la classe enfant représentée par l'enregistrement. Cela est parfois nécessaire, lorsque vous souhaitez charger une liste de tous les enregistrements (sans faire
select
sur chaque table enfant). Une façon de gérer cela est d'avoir une colonne représentant le type de la classe enfant (similaire au champ rowType dans le TPH), mélangeant ainsi en quelque sorte le TPT et le TPH.Supposons que nous souhaitons concevoir une base de données contenant le diagramme de classes de formes suivant:
La conception de la base de données pour les classes ci-dessus peut être comme ceci:
la source
Il existe deux principaux types d'héritage que vous pouvez configurer dans une base de données, une table par entité et une table par hiérarchie.
La table par entité est l'endroit où vous avez une table d'entité de base qui a les propriétés partagées de toutes les classes enfants. Vous avez ensuite par classe enfant une autre table, chacune avec uniquement des propriétés applicables à cette classe. Ils sont liés 1: 1 par leurs PK
La table par hiérarchie est l'endroit où toutes les classes ont partagé une table et les propriétés facultatives peuvent être nulles. Leur est également un champ discriminateur qui est un nombre qui indique le type que l'enregistrement détient actuellement
SessionTypeID est un discriminateur
La cible par hiérarchie est plus rapide à interroger car vous n'avez pas besoin de jointures (uniquement la valeur du discriminateur), tandis que la cible par entité, vous devez effectuer des jointures complexes afin de détecter le type de quelque chose et de récupérer toutes ses données.
Edit: Les images que je montre ici sont des captures d'écran d'un projet sur lequel je travaille. L'image Asset n'est pas complète, d'où le vide de celle-ci, mais c'était surtout pour montrer comment sa configuration, pas quoi mettre à l'intérieur de vos tables. Cela dépend de toi ;). La table de session contient des informations sur la session de collaboration virtuelle et peut être de plusieurs types de sessions selon le type de collaboration impliqué.
la source
Vous normaliseriez votre base de données et cela refléterait en fait votre héritage. Cela peut avoir une dégradation des performances, mais c'est comme ça avec la normalisation. Vous devrez probablement faire preuve de bon sens pour trouver l'équilibre.
la source
répétition d'une réponse similaire
dans le mappage OR, l'héritage est mappé à une table parent où les tables parent et enfant utilisent le même identifiant
par exemple
SubObject a une relation de clé étrangère avec Object. lorsque vous créez une ligne de sous-objet, vous devez d'abord créer une ligne d'objet et utiliser l'ID dans les deux lignes
EDIT: si vous cherchez à modéliser également le comportement, vous aurez besoin d'une table Type qui répertorie les relations d'héritage entre les tables et spécifie l'assembly et le nom de classe qui implémentent le comportement de chaque table
Cela semble exagéré, mais tout dépend de l'utilisation que vous en faites!
la source
En utilisant SQL ALchemy (Python ORM), vous pouvez effectuer deux types d'héritage.
Celle que j'ai eue utilise une table unique et une colonne discriminante. Par exemple, une base de données Sheep (sans blague!) Stockait tous les moutons dans une seule table, et les béliers et les brebis étaient traités en utilisant une colonne de genre dans cette table.
Ainsi, vous pouvez rechercher tous les moutons et obtenir tous les moutons. Ou vous pouvez interroger uniquement par Ram, et il n'obtiendra que des Rams. Vous pouvez également faire des choses comme avoir une relation qui ne peut être qu'un bélier (c'est-à-dire, le père d'un mouton), etc.
la source
Notez que certains moteurs de base de données fournissent déjà des mécanismes d'héritage nativement comme Postgres . Regardez la documentation .
Pour un exemple, vous interrogez le système Personne / Employé décrit dans une réponse ci-dessus comme ceci:
Dans ce choix de votre base de données, vous n'avez pas besoin d'être particulièrement intelligent!
la source