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é de poser des questions)?

(je suis très intéressé par SQL Server et .NET, mais je veux aussi comprendre comment d'autres plateformes traitent ce problème.)

108
demandé sur Even Mien 2008-10-10 10:03:27

9 réponses

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

Table-Per-Type (TPT)

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

ainsi par exemple:

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

class Employee : Person {
    public DateTime StartDate;
}

résulterait en tableaux comme:

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

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

Table Par Hiérarchie (TPH)

il y a un seul tableau qui représente toute la hiérarchie de l'héritage, ce qui signifie que plusieurs des colonnes seront probablement clairsemées. Une colonne discriminante est ajoutée qui indique au système quel type de ligne il s'agit.

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

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

pour toutes les lignes qui sont rowtype 0 (Person), la date de démarrage sera toujours nulle.

Table Par le 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 tableaux:

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

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate
125
répondu Brad Wilson 2008-10-10 06:11:28

la bonne conception de la base de données n'a rien à voir avec la bonne conception de l'objet.

si vous prévoyez d'utiliser la base de données pour autre chose que de simplement sérialiser vos objets (tels que des rapports, interrogation, utilisation multi-application, intelligence d'affaires, etc.) alors je ne recommande aucune sorte de simple mapping des objets aux tables.

beaucoup de gens pensent à une rangée dans une table de base de données comme une entité( j'ai passé de nombreuses années à penser en ces termes), mais une rangée n'est pas une entité. C'est une proposition. Une relation de base de données (c.-à-d. une table) représente un certain énoncé de fait au sujet du monde. La présence de la rangée 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é à travers 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) peut être partiellement stockés dans un seul rapport.

il est préférable de se demander, quels faits voulez-vous stocker, quelles questions allez-vous vouloir des réponses, quels rapports voulez-vous générer.

une fois que le design DB approprié est créé, il est alors simple de créer des requêtes/vues qui vous permettent de sérialiser vos objets à ces relations.

exemple:

Dans un système de réservation d'hôtel, vous pouvez besoin de stocker le fait que Jane Doe a une réservation pour une chambre au Seaview Inn pour le 10-12 avril. C'est qu'un attribut de l'entité client? Est-il un attribut de l'hôtel entité? Est-ce une entité de réservation avec des propriétés qui comprennent le client et l'hôtel? Cela pourrait être n'importe laquelle ou toutes ces choses dans un système orienté objet. Dans une base de données, ce n'est rien de tout ça. C'est tout simplement un simple fait.

pour voir la différence, considérez les deux requêtes suivantes. (1) Comment de nombreuses réservations d'hôtel n'Jane Doe 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, query (1) est un attribut de l'entité client, et query (2) est un attribut de l'entité hôtel. Ce sont les objets qui exposeraient 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 relationnelles, les deux requêtes examineraient la relation de réservation pour obtenir leurs numéros, et conceptuellement il n'y a pas besoin de se soucier d'une autre "entité".

ainsi, c'est en essayant de stocker des faits sur le monde-plutôt que d'essayer de stocker des entités avec des attributs-qu'une base de données relationnelle appropriée est construite. Et une fois qu'il est correctement conçu, les requêtes utiles qui n'ont pas été imaginées pendant la phase de conception peuvent être facilement construites, puisque tous les faits nécessaires pour remplir ces requêtes sont à leur place.

99
répondu Jeffrey L Whitledge 2017-11-29 21:11:08

brève réponse: 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 d'une manière relationnelle (en faisant attention à ce que vous stockez, et en prêtant attention à ce que Jeffrey l Whitledge vient de dire), pas une affectée par votre conception d'objet.

8
répondu Marcin 2008-10-10 22:26:10

TPT, TPH et TPC modèles sont les moyens que vous allez, comme mentionné par Brad Wilson. Mais quelques notes:

  • les classes d'enfants héritant d'une classe de base peuvent être considérées comme des entités faibles à la définition de classe de base dans la base de données, ce qui signifie qu'elles sont dépendantes de leur classe de base et ne peuvent exister sans elle. J'ai vu le nombre de fois, que les ID uniques sont stockés pour chaque table d'enfant tout en gardant le FK à la table de parent. Un FK est juste assez et son encore mieux d'avoir ON-delete cascade permettre pour la relation FK entre l'enfant et les tables de base.

  • dans TPT, en ne voyant que les enregistrements de la table de base, vous n'êtes pas en mesure de trouver quelle classe d'enfant l'enregistrement représente. Cela est parfois nécessaire, lorsque vous voulez charger une liste de tous les enregistrements (sans faire select sur chaque table enfant). Une façon de gérer cela, c'est d'avoir une colonne représentant le type de l' classe enfant (similaire au champ rowType dans le TPH), donc mélange le TPT et le TPH en quelque sorte.

disons que nous voulons concevoir une base de données qui contient le diagramme de classe de forme suivant:

public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}

public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}

public class Circle : Shape {
Point center;
int radius;
}

la conception de base de données pour les classes ci-dessus peut être comme ceci:

table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)

table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;

table Circle
----------
int ShapeID; (FK on delete cascade)  
int centerX;
int center;
int radius;
5
répondu imang 2014-08-22 17:37:17

il y a deux types principaux d'héritage que vous pouvez configurer dans un DB, table par entité et table par hiérarchie.

Table par entité est où vous avez une table d'entité de base qui a des propriétés partagées de toutes les classes d'enfants. Vous avez alors par classe d'enfant une autre table chacun avec seulement des propriétés applicables à cette classe. Ils sont liés 1:1 par leurs PK

alt texte http://mattlant.com/ent.png

Table per hierarchy est l'endroit où toutes les classes partagent une table, et les propriétés optionnelles sont nulles. Leur est aussi un champ discriminateur qui est un nombre qui indique le type que l'enregistrement détient actuellement

alt texte http://mattlant.com/hier.png SessionTypeID is discriminator

cible par hiérarchie est plus rapide à interroger car vous n'avez pas besoin de jointures (seulement la valeur discriminatrice), alors que la cible par entité que vous devez faire complexe jointures afin de détecter quel type quelque chose est ainsi que retriuve toutes ses données..

Edit: les images que je montre ici sont des captures d'écran d'un projet sur lequel je travaille. L'Actif de l'image n'est pas complète, donc le vide, mais c'était surtout de montrer comment sa configuration, pas de quoi mettre à l'intérieur de vos tables. C'est à vous de choisir ;). La table des séances contient de L'information sur les séances de collaboration virtuelle et peut comprendre plusieurs types de séances, selon le type de séance. la collaboration est impliquée.

4
répondu mattlant 2008-10-10 07:21:00

vous normaliseriez votre base de données et cela refléterait votre héritage. Il peut avoir des performances dégradantes, mais c'est comme ça avec la normalisation. Vous aurez probablement à utiliser le bon sens pour trouver l'équilibre.

1
répondu Per Hornshøj-Schierbeck 2008-10-10 06:06:01

répétition d'un fil similaire réponse

dans O-R mapping, les cartes d'héritage vers une table parent où les tables parent et enfant utilisent le même identificateur

par exemple

create table Object (
    Id int NOT NULL --primary key, auto-increment
    Name varchar(32)
)
create table SubObject (
    Id int NOT NULL  --primary key and also foreign key to Object
    Description varchar(32)
)
Le sous-objet

a une relation clé étrangère avec L'objet. 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 behavior aussi, vous auriez besoin d'une table de Type qui a énuméré les relations d'héritage entre les tables, et a spécifié l'assemblage et le nom de classe qui a mis en œuvre le comportement de chaque table

semble exagéré, mais tout dépend de ce pour quoi vous voulez l'utiliser!

1
répondu Steven A. Lowe 2017-05-23 12:02:34

en utilisant L'Alchimie SQL (Python ORM), vous pouvez faire deux types d'héritage.

celui que j'ai eu l'expérience est l'utilisation d'une table simple, et avoir une colonne discriminante. Pour les cas, une base de données de moutons (pas de blague!) stockaient tous les moutons dans la même table, et les béliers et les brebis étaient manipulés en utilisant une colonne de genre dans cette table.

ainsi, vous pouvez interroger pour tous les moutons, et obtenir tous les moutons. Ou vous pouvez interroger par Ram seulement, et il n'obtiendra Rams. Vous pouvez aussi le faire les choses comme avoir une relation qui ne peut être Qu'un bélier (c.-à-d., le Père D'un mouton), et ainsi de suite.

1
répondu Matthew Schinckel 2008-10-10 08:28:53

noter que certains moteurs de base de données fournit déjà des mécanismes d'héritage nativement comme Postgres . Regardez la documentation .

pour un exemple, vous interrogeriez le système personne/employé décrit dans une réponse ci-dessus comme ceci:

  /* This shows the first name of all persons or employees */
  SELECT firstname FROM Person ; 

  /* This shows the start date of all employees only */
  SELECT startdate FROM Employee ;

C'est le choix de votre base de données, vous n'avez pas besoin d'être particulièrement intelligent !

1
répondu Pierre 2008-10-10 09:10:54