173 votes

Comment modéliser efficacement l'héritage dans une base de données ?

Quelles sont les meilleures pratiques pour modéliser l'héritage dans les bases de données ?

Quels sont les compromis (par exemple, la possibilité d'interrogation) ?

(Je suis surtout intéressé par SQL Server et .NET, mais je veux aussi comprendre comment les autres plateformes abordent cette question).

17 votes

Si vous vous intéressez aux "meilleures pratiques", la plupart des réponses sont tout simplement incorrectes. Selon les meilleures pratiques, la RDb et l'application sont indépendantes ; leurs critères de conception sont complètement différents. Par conséquent, "modéliser l'héritage" dans une base de données (ou modéliser la BDR pour l'adapter à une seule application ou à un seul langage d'application) est une très mauvaise pratique, mal informée, qui enfreint les règles de conception de base de la BDR et la paralyse.

0 votes

8 votes

@PerformanceDBA Quelle est votre suggestion pour éviter l'héritage dans le modèle de base de données ? Disons que nous avons 50 types différents d'enseignants, et que nous voulons connecter cet enseignant particulier avec la classe. Comment pouvez-vous y parvenir sans avoir recours à l'héritage ?

201voto

Brad Wilson Points 22910

Il existe plusieurs façons de modéliser l'héritage dans une base de données. Celle que vous choisissez dépend de vos besoins. Voici quelques options :

Table-Par-Type (TPT)

Chaque classe a son propre tableau. La classe de base contient tous les éléments de la classe de base, et chaque classe qui en dérive possède sa propre table, avec une clé primaire qui est également une clé étrangère à la table de la classe de base ; la classe de la table dérivée contient uniquement les différents éléments.

Donc, par exemple :

class Person {
    public int ID;
    public string FirstName;
    public string LastName;
}

class Employee : Person {
    public DateTime StartDate;
}

Cela donnerait des tableaux comme :

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK, FK)
datetime startdate

Table-Par-Hiérarchie (TPH)

Une seule table représente toute la hiérarchie de l'héritage, ce qui signifie que plusieurs des colonnes seront probablement clairsemées. Une colonne de discrimination est ajoutée pour indiquer au système de quel type de ligne il s'agit.

Compte tenu des classes ci-dessus, vous obtenez ce tableau :

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

Pour toutes les lignes dont le type de ligne est 0 (Personne), la date de début sera toujours nulle.

Table-Par-Béton (TPC)

Chaque classe a son propre tableau entièrement formé, sans référence à d'autres tableaux.

Compte tenu des classes ci-dessus, vous obtenez les tableaux suivants :

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate

36 votes

Le choix dépend de vos besoins" - veuillez préciser, car je pense que les raisons des choix sont au cœur de la question.

3 votes

Comme pour la plupart des choses dans une base de données, vous devez faire un compromis entre le coût du stockage et les performances. TPH stocke tout dans une seule table, les sélections sont donc rapides. TPT est plus compact que TPC, mais plus coûteux à cause des jointures. La recherche d'éléments dans TPC implique de vérifier plusieurs tables. Chacun de ces éléments est un compromis, et votre décision doit être basée sur ce qui est le plus important pour votre application.

17 votes

Voir mon commentaire sur la question. Utiliser de nouveaux noms amusants pour des termes techniques de Rdb qui ont déjà existé mène à la confusion. "TPT" est un sous-type-supérieur. "TPH" est non normalisé, une erreur grossière. "TPH" est encore moins normalisé, une autre erreur grossière.

180voto

Jeffrey L Whitledge Points 27574

Une bonne conception de base de données n'a rien à voir avec une bonne conception d'objet.

Si vous prévoyez d'utiliser la base de données pour autre chose que la simple sérialisation de vos objets (comme des rapports, des requêtes, une utilisation multi-applications, de la business intelligence, etc.), je ne recommande pas un simple mappage des objets aux tables.

De nombreuses personnes considèrent qu'une ligne d'une table de base de données est une entité (j'ai passé de nombreuses années à penser en ces termes), mais une ligne n'est pas une entité. C'est une proposition. Une relation de base de données (c'est-à-dire une table) représente une déclaration 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 l'héritage, l'association, l'agrégation, ou complètement non affiliés) peuvent être partiellement stockés dans une seule relation.

Il est préférable de se demander quels sont les faits que vous souhaitez conserver, les questions auxquelles vous souhaitez obtenir des réponses et les rapports que vous souhaitez générer.

Une fois que la conception appropriée de la base de données est créée, il est facile 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 enregistrer le fait que Jane Doe a réservé une chambre au Seaview Inn du 10 au 12 avril. S'agit-il d'un attribut de l'entité client ? Est-ce un attribut de l'entité hôtel ? S'agit-il d'une entité de réservation dont les propriétés incluent le client et l'hôtel ? Dans un système orienté objet, il peut s'agir de tout ou partie de ces éléments. Dans une base de données, ce n'est rien de tout cela. Il s'agit simplement d'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ôtel. Ce sont les objets qui exposent ces propriétés dans leurs API. (Même si, évidemment, 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, d'un point de vue conceptuel, il n'est pas nécessaire de se préoccuper d'une autre "entité".

Ainsi, c'est 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 correcte est construite. Et une fois qu'elle est correctement conçue, des requêtes utiles qui n'avaient pas été imaginées pendant la phase de conception peuvent être facilement construites, puisque tous les faits nécessaires pour répondre à ces requêtes sont à leur place.

19 votes

+1 Enfin, un îlot de savoir authentique dans une mer d'ignorance (et de refus d'apprendre quoi que ce soit en dehors de leur champ d'action). Je suis d'accord, ce n'est pas magique : si la RDb est conçue en utilisant les principes de la RDb, il n'y a aucun effort à faire pour "mapper" ou "projeter" n'importe quelle "classe". Forcer la RDb dans des exigences basées sur les classes est tout simplement incorrect.

3 votes

+1, excellente réponse. Quoi qu'il en soit, pourriez-vous donner quelques exemples de faits et expliquer en quoi ils ne sont pas des entités ? Il m'est encore difficile de trouver la différence entre les "faits" et les entités. Je vous remercie.

0 votes

@fra - OK, j'ai ajouté un exemple.

11voto

Marcin Points 25366

Réponse courte : vous ne le faites pas.

Si vous avez besoin de sérialiser vos objets, utilisez un ORM, ou encore 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 prêtant attention à ce que Jeffrey L Whitledge vient de dire), et non pas de manière affectée par votre conception d'objet.

4 votes

+1 Tenter de modéliser l'héritage dans une base de données est un gaspillage de bonnes ressources relationnelles.

4voto

mattlant Points 9136

Il existe deux principaux types d'héritage que vous pouvez configurer dans une BD, la table par entité et la table par hiérarchie.

La table par entité consiste à avoir une table d'entité de base qui a des propriétés communes à toutes les classes enfantines. Vous avez ensuite une autre table par classe enfant, chacune contenant uniquement les propriétés applicables à cette classe. Elles sont liées 1:1 par leurs PK.

alt text

La table par hiérarchie est celle où toutes les classes partagent une table, et les propriétés optionnelles sont nullables. Il existe également un champ discriminant qui est un nombre indiquant le type de l'enregistrement actuel.

alt text SessionTypeID est un discriminateur

La cible par hiérarchie est plus rapide à interroger car vous n'avez pas besoin de jointures (seulement la valeur du discriminateur), alors que la cible par entité vous devez faire des jointures complexes afin de détecter le type de quelque chose et de retrouver 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 de l'actif n'est pas complète, d'où sa vacuité, mais c'était principalement pour montrer comment il est configuré, pas ce qu'il faut mettre dans vos tables. C'est à vous de voir ;). La table de session contient les informations de la session de collaboration virtuelle, et peut être de plusieurs types de sessions selon le type de collaboration impliqué.

0 votes

Je considère également que la classe Target per Concrete ne modélise pas vraiment bien l'héritage et je ne l'ai donc pas montrée.

0 votes

Pourriez-vous ajouter une référence d'où provient l'illustration ?

0 votes

Où sont les images dont vous parlez à la fin de votre réponse ?

1voto

Vous normaliseriez votre base de données et cela refléterait votre héritage. Il pourrait y avoir une dégradation des performances, mais c'est ainsi que cela se passe avec la normalisation. Vous devrez probablement faire preuve de bon sens pour trouver l'équilibre.

2 votes

Pourquoi les gens pensent-ils que la normalisation d'une base de données dégrade les performances ? les gens pensent-ils également que le principe DRY dégrade les performances du code ? d'où vient cette perception erronée ?

1 votes

Peut-être parce que la dénormalisation peut améliorer les performances, alors que la normalisation les dégrade, relativement parlant. Je ne peux pas dire que je suis d'accord avec ça, mais c'est probablement comme ça que c'est arrivé.

2 votes

Au début, la normalisation peut avoir un léger effet sur les performances, mais au fil du temps, à mesure que le nombre de lignes augmente, les JOIN efficaces commenceront à être plus performants que les tables plus volumineuses. Bien entendu, la normalisation présente d'autres avantages plus importants : cohérence et absence de redondance, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X