Je ne sais pas comment transformer une entité variable en table relationnelle


9

INTRODUCTION ET INFORMATIONS PERTINENTES:

L'exemple suivant illustre le problème auquel je suis confronté:

L'animal a une race, qui peut être un chat ou un chien . Le chat peut être siamois ou persan . Le chien peut être un berger allemand ou un retriver du Labrador .

L'animal est une entité forte, tandis que sa race est un attribut qui peut avoir l'une des deux valeurs proposées (chat ou chien). Ces deux valeurs sont complexes (j'ai ajouté ici uniquement le type de chien / chat pour illustrer le problème, mais il peut également y avoir le nom du chat / chien et un tas d'autres choses).

PROBLÈME:

Je ne sais pas comment créer des tables relationnelles pour cet exemple.

MES EFFORTS POUR RÉSOUDRE LE PROBLÈME:

J'ai essayé de dessiner un diagramme ER, en utilisant la notation de Chen, qui représente le problème mais étant un débutant, je ne sais pas si je l'ai bien fait. Voici ce que j'ai:

entrez la description de l'image ici

Je m'excuse si j'ai dessiné quelque chose de mal, veuillez me corriger si c'est le cas. Je ne souhaite pas simplement obtenir une "solution gratuite" mais aussi apprendre à gérer ce problème afin de pouvoir le résoudre moi-même à l'avenir.

La seule chose qui me vient à l'esprit est de créer deux tables séparées, une pour les chats et une pour les chiens. De plus, l' attribut race dans la table Animal ne stockerait que la valeur cat ou dog . Quelque chose comme ça:

Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >

J'ai vraiment un mauvais pressentiment sur ma solution et je crains qu'elle ne soit mauvaise, d'où la question ci-dessous.

DES QUESTIONS:

  • Comment puis-je transformer mon exemple en diagramme ER?
  • Comment transformer ce diagramme ER en tables relationnelles?

Si des informations supplémentaires sont nécessaires, laissez un commentaire et je mettrai à jour mon message dès que possible. N'hésitez pas à ajouter des balises appropriées car je suis assez nouveau ici.

Je vous remercie.


1
La transformation des diagrammes EER en tableaux peut être trouvée dans cet article de 1986 de TJTeorey, D.Yang, JPFry: A Logical Design Methodology for Relational Databases Using the Extended Entity-Relationship Model . C'est simple et c'est l'un de mes papiers préférés.
miracle173

Réponses:


11

La structure appropriée pour ce scénario est un modèle de sous-classe / héritage, et est presque identique au concept que j'ai proposé dans cette réponse: Liste de valeur ordonnée hétérogène .

Le modèle proposé dans cette question est en fait assez proche en ce que l' Animalentité contient le type (ie race) et les propriétés qui sont communes à tous les types. Cependant, deux modifications mineures sont nécessaires:

  1. Supprimez les champs Cat_ID et Dog_ID de leurs entités respectives:

    Le concept clé ici est que tout est un Animalpeu importe, race: Cat, Dog, Elephantet ainsi de suite. Étant donné que le point de départ, tout particulièrement racede Animalne pas vraiment besoin d' un identifiant distinct depuis:

    1. le Animal_IDest unique
    2. les Cat, Doget toutes autres raceentités ajoutées à l'avenir ne représentent pas, en elles-mêmes, un élément particulier Animal; ils ont de sens que lorsqu'il est utilisé en combinaison avec les informations contenues dans l'entité mère, Animal.

    Par conséquent, la Animal_IDpropriété dans le Cat, Dog, etc entités est à la fois le PK et le dos FK à l' Animalentité.

  2. Différencier les types de breed:

    Le fait que deux propriétés partagent le même nom ne signifie pas nécessairement que ces propriétés sont identiques, même si le nom étant le même implique une telle relation. Dans ce cas, ce que vous avez vraiment est réellement CatBreedet en DogBreedtant que "types" séparés

Notes initiales

  1. Le SQL est spécifique à Microsoft SQL Server (c'est-à-dire T-SQL). Cela signifie que vous devez faire attention aux types de données car ils ne sont pas identiques sur tous les SGBDR. Par exemple, j'utilise VARCHARmais si vous avez besoin de stocker quoi que ce soit en dehors de l'ensemble ASCII standard, vous devriez vraiment l'utiliser NVARCHAR.
  2. Les champs d'identification des tables « de type » ( Race, CatBreedet DogBreed) sont pas auto-incrémentée (c. -à- IDENTITÉ en termes de T-SQL) , car ils sont des constantes d'application (ils font partie de l'application) qui sont des valeurs de recherche statiques dans la et sont représentés sous forme de enums en C # (ou dans d'autres langages). Si des valeurs sont ajoutées, elles sont ajoutées dans des situations contrôlées. Je réserve l'utilisation de champs d'incrémentation automatique pour les données utilisateur qui arrivent via l'application.
  3. La convention de dénomination que j'utilise consiste à nommer chaque table de sous-classe en commençant par le nom de la classe principale suivi du nom de la sous-classe. Cela permet d'organiser les tables et indique clairement (sans regarder les FK) la relation entre la table de sous-classe et la table d'entité principale.
  4. Veuillez consulter la section "Édition finale" à la fin pour une note concernant les vues.

"Race" comme "Race" - Approche spécifique

Schéma spécifique à la race
Ce premier ensemble de tables sont les tables de recherche / types:

CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE CatBreed
(
  CatBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  CatBreedAttribute1 INT,
  CatBreedAttribute2 VARCHAR(10)
  -- other "CatBreed"-specific properties as needed
);

CREATE TABLE DogBreed
(
  DogBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  DogBreedAttribute1 TINYINT
  -- other "DogBreed"-specific properties as needed
);

Cette deuxième inscription est la principale entité «animale»:

CREATE TABLE Animal
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  Name VARCHAR(50)
  -- other "Animal" properties that are shared across "Race" types
);

ALTER TABLE Animal
  ADD CONSTRAINT [FK_Animal_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

Cette troisième série de tableaux sont les entités sous-classe complémentaires qui complètent la définition de chacun Racede Animal:

CREATE TABLE AnimalCat
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  CatBreedID INT NOT NULL, -- FK to CatBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Cat"-specific properties as needed
);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_CatBreed]
  FOREIGN KEY (CatBreedID)
  REFERENCES CatBreed (CatBreedID);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);


CREATE TABLE AnimalDog
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  DogBreedID INT NOT NULL, -- FK to DogBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Dog"-specific properties as needed
);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_DogBreed]
  FOREIGN KEY (DogBreedID)
  REFERENCES DogBreed (DogBreedID);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);

Le modèle utilisant un breedtype partagé est affiché après la section "Notes supplémentaires".

Notes complémentaires

  1. Le concept de breedsemble être un foyer de confusion. Il a été suggéré par jcolebrand (dans un commentaire sur la question) qui breedest une propriété partagée entre les différents races, et les deux autres réponses l'ont intégré comme tel dans leurs modèles. Il s'agit toutefois d'une erreur, car les valeurs de breedne sont pas partagées entre les différentes valeurs de race. Oui, je suis conscient que les deux autres modèles proposés tentent de résoudre ce problème en faisant raceun parent de breed. Bien que cela résout techniquement le problème des relations, cela n'aide pas à résoudre la question globale de la modélisation de ce qu'il faut faire des propriétés non communes, ni comment gérer un racequi n'en a pas breed. Mais, dans le cas où une telle propriété était garantie d'exister dans tous lesAnimals, je vais également inclure une option pour cela (ci-dessous).
  2. Les modèles proposés par vijayp et DavidN (qui semblent identiques) ne fonctionnent pas car:
    1. Ils non plus
      1. ne pas autoriser le stockage de propriétés non communes (du moins pas pour des instances individuelles Animal), ou
      2. exiger que toutes les propriétés de tous les races soient stockées dans l' Animalentité, ce qui est une façon très plate (et presque non relationnelle) de représenter ces données. Oui, les gens le font tout le temps, mais cela signifie avoir de nombreux champs NULL par ligne pour les propriétés qui ne sont pas destinées à ce particulier raceET savoir quels champs par ligne sont associés au particulier racede cet enregistrement.
    2. Ils ne permettent pas d'ajouter un racede Animaldans le futur qui n'a pas breedde propriété. Et même si TOUS Animalont un breed, cela ne changerait pas la structure en raison de ce qui a été noté précédemment breed: cela breeddépend du race(c'est- breedà- dire pour Catn'est pas la même chose que breedpour Dog).

La «race» comme approche de propriété commune / partagée

entrez la description de l'image ici
Notez s'il vous plaît:

  1. Le SQL ci-dessous peut être exécuté dans la même base de données que le modèle présenté ci-dessus:

    1. Le Racetableau est le même
    2. La Breedtable est neuve
    3. Les trois Animaltableaux sont accompagnés d'un2
  2. Même s'il s'agit d'une Breedpropriété désormais commune, il ne semble pas juste de ne pas l'avoir Racenoté dans l'entité principale / parent (même si elle est techniquement correcte sur le plan relationnel). Ainsi, les deux RaceIDet BreedIDsont représentés dans Animal2. Afin d'éviter une incompatibilité entre le RaceIDnoté dans Animal2et un BreedIDpour un autre RaceID, j'ai ajouté un FK sur les deux RaceID, BreedIDqui fait référence à une CONTRAINTE UNIQUE de ces champs dans la Breedtable. Je méprise généralement le fait de pointer un FK vers une CONTRAINTE UNIQUE, mais voici l'une des rares raisons valables de le faire. UNE CONTRAINTE UNIQUE est logiquement une «Clé alternative», ce qui la rend valable pour cette utilisation. Veuillez également noter que la Breedtable a toujours un PK juste BreedID.
    1. La raison de ne pas aller avec juste un PK sur les champs combinés et sans CONTRAINTE UNIQUE est que cela permettrait de BreedIDrépéter le même sur différentes valeurs de RaceID.
    2. La raison de ne pas commuter autour de laquelle PK et UNIQUE CONSTRAINT est que cela pourrait ne pas être la seule utilisation de BreedID, il devrait donc être possible de référencer une valeur spécifique de Breedsans avoir la RaceIDdisponibilité.
  3. Bien que le modèle suivant fonctionne, il présente deux failles potentielles concernant le concept de partage Breed(et c'est pourquoi je préfère les tableaux Racespécifiques Breed).
    1. Il y a une supposition implicite que TOUTES les valeurs de Breedont les mêmes propriétés. Il n'y a pas de moyen facile dans ce modèle d'avoir des propriétés disparates entre les Dog"races" et les Elephant"races". Cependant, il existe toujours un moyen de le faire, ce qui est noté dans la section "Final Edit".
    2. Il n'y a aucun moyen de partager une Breedcourse sur plusieurs courses. Je ne sais pas si c'est souhaitable de le faire (ou peut-être pas dans le concept d'animaux mais peut-être dans d'autres situations qui utiliseraient ce type de modèle), mais ce n'est pas possible ici.
CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY,
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE Breed
(
  BreedID INT NOT NULL PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  BreedName VARCHAR(50)
);

ALTER TABLE Breed
  ADD CONSTRAINT [UQ_Breed]
  UNIQUE (RaceID, BreedID);

ALTER TABLE Breed
  ADD CONSTRAINT [FK_Breed_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

CREATE TABLE Animal2
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race, FK to Breed
  BreedID INT NOT NULL, -- FK to Breed
  Name VARCHAR(50)
  -- other properties common to all "Animal" types
);

ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Breed]
  FOREIGN KEY (RaceID, BreedID)
  REFERENCES Breed (RaceID, BreedID);


CREATE TABLE AnimalCat2
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalCat2
  ADD CONSTRAINT [FK_AnimalCat2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);

CREATE TABLE AnimalDog2
(
  AnimalID INT NOT NULL PRIMARY KEY,
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalDog2
  ADD CONSTRAINT [FK_AnimalDog2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);


Édition finale (j'espère ;-)

  1. En ce qui concerne la possibilité (et ensuite la difficulté) de gérer des propriétés disparates entre les types de Breed, il est possible d'employer le même concept de sous-classe / héritage mais avec Breedcomme entité principale. Dans cette configuration, le Breedtableau aurait les propriétés communes à tous les types de Breed(tout comme le Animaltableau) et RaceIDreprésenterait le type de Breed(comme il le fait dans le Animaltableau). Ensuite , vous avez des tables sous - classe tels que BreedCat, BreedDoget ainsi de suite. Pour les petits projets, cela pourrait être considéré comme une «ingénierie excessive», mais il est mentionné comme une option pour les situations qui en bénéficieraient.
  2. Pour les deux approches, il est parfois utile de créer des vues sous forme de raccourcis vers les entités complètes. Par exemple, considérez:

    CREATE VIEW Cats AS
       SELECT  an.AnimalID,
               an.RaceID,
               an.Name,
               -- other "Animal" properties that are shared across "Race" types
               cat.CatBreedID,
               cat.HairColor
               -- other "Cat"-specific properties as needed
       FROM    Animal an
       INNER JOIN  AnimalCat cat
               ON  cat.AnimalID = an.AnimalID
       -- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
    
  3. Bien qu'il ne fasse pas partie des entités logiques, il est assez courant d'avoir des champs d'audit dans les tables pour au moins avoir une idée du moment où les enregistrements sont insérés et mis à jour. Donc en termes pratiques:
    1. Un CreatedDatechamp serait ajouté à la Animaltable. Ce champ n'est nécessaire dans aucune des tables de sous-classe (par exemple AnimalCat) car les lignes insérées pour les deux tables doivent être effectuées en même temps dans une transaction.
    2. Un LastModifiedDatechamp serait ajouté à la Animaltable et à toutes les tables de sous-classe. Ce champ n'est mis à jour que si cette table particulière est mise à jour: si une mise à jour se produit dans AnimalCatmais pas dans Animalpour un particulier AnimalID, alors seul le LastModifiedDatechamp dans AnimalCatsera défini.

2
D'une certaine manière, j'ai l'impression que vous avez compris exactement quel est mon problème. Je vais donner un coup d'oeil à votre réponse liée et l'étudier attentivement. Une simple définition des tables serait également idéale (si les requêtes SQL sont trop lourdes à écrire pour le moment). Si vous décidez de mettre à jour votre message avec des requêtes SQL ou des définitions de table, laissez-moi un commentaire. Merci encore. Meilleures salutations.
AlwaysLearningNewStuff

1
J'essaie d'appliquer votre réponse à mon cas réel. Si je suis aveuglément vos instructions, je pense que je pourrais manquer l'occasion d'optimiser davantage ma conception. J'aimerais que vous jetiez un œil à ma dernière question, car vous avez parfaitement compris mes questions et apporté d'excellentes réponses. J'ai composé la question pour utiliser un modèle de données générique afin d'être également utile aux futurs lecteurs. Si vous avez du mal à le trouver, laissez-moi un commentaire. Merci et désolé d'avoir dérangé ...
AlwaysLearningNewStuff

@AlwaysLearningNewStuff Salut. Vous avez reçu ce message plus tôt, mais je n'ai pas eu le temps d'y accéder immédiatement. J'ai pu trouver la nouvelle question en cliquant sur votre nom ci-dessus et il montre toutes vos questions :-).
Solomon Rutzky

Je faisais référence à cette question . En un mot: j'ai 3 entités avec un attribut commun D, donc je voulais appliquer la méthode de votre réponse. Deux entités ont un attribut commun Equi n'est pas présent dans la troisième entité. Dois-je ignorer ce fait et appliquer une solution standard, ou existe-t-il un moyen d'optimiser davantage ma conception?
AlwaysLearningNewStuff

4

Tout d'abord, vous réussissez bien à faire la distinction entre la modélisation ER et la modélisation relationnelle. Beaucoup de débutants ne le font pas.

Voici quelques mots à la mode que vous pouvez utiliser pour rechercher des articles utiles sur le Web.

Votre cas est un cas classique de classe / sous-classe ou, si vous le souhaitez, de type / sous-type.

L'expression utilisée dans la modélisation ER est "généralisation / spécialisation". Et de nombreux articles le montrent sous quelque chose appelé modélisation EER (Enhanced Entity-Relationship). Ce n'était pas dans la présentation originale de Peter Chen de la modélisation ER. Il a été ajouté plus tard. Pour un assez bon résumé de gen / spec au format pdf, cliquez ici

Ensuite, lors de la conversion d'un cas de classe / sous-classe en modélisation relationnelle, vous concevez des tables. Il existe plusieurs approches. Les deux principales approches sont appelées héritage de table unique et héritage de table de classe. Chacun a ses avantages et ses inconvénients. La meilleure présentation de ces deux designs vient de Martin Fowler. Vous pouvez voir son plan ici et ici .

Le gros avantage de l'héritage d'une table unique est la simplicité. Tout est stocké dans une seule table. Le gros inconvénient est beaucoup de NULLS. Cela peut gaspiller de l'espace et du temps et entraîner une logique confuse.

L'héritage de table de classe nécessite des jointures, mais elles sont simples et rapides. Surtout si vous utilisez une technique appelée clé primaire partagée, dans laquelle le PK dans les tables de sous-classe est une copie du PK dans la table de superclasse. Vous pouvez créer des vues pour chaque sous-classe qui joignent des données de super-classe à des données de sous-classe.

Enfin, il y a une balise dans cette zone qui recueille des questions comme la vôtre ensemble.
Le voici: -


1
+1 Ce qui me déroute, c'est le manque de clés primaires dans les diagrammes des tableaux. Surtout dans le "classTableInheritance", je ne vois pas que toutes ces tables sont connectées par la même clé primaire.
miracle173

@ miracle173 un point valide. Pour une raison quelconque, Fowler n'inclut pas les PK et FK dans le diagramme. Il existe d'autres articles sous l'héritage de table de classe qui fournissent ce détail. Toutes les implémentations de l'héritage de table de classe ne le combinent pas avec la clé primaire partagée. Je le recommande. C'est un peu plus de travail au moment de l'insertion, mais plus facile et plus rapide au moment de la récupération jointe.
Walter Mitty

3

Je vois sur la conception possible

Table Race

RaceId- PK- Int
RaceName - Varchar(50)

Table Breed

BreedId - PK- Int
RaceId - FK - Int
BreedName - varchar(50)

Table Animal

AnimalId - PK- Int
BreedId - FK - Int
Other Columns....

Ces PK ci-dessus seraient une colonne à incrémentation automatique. Les autres colonnes du Animaltableau peuvent être nommées en conséquence.

entrez la description de l'image ici


De plus, j'ajouterais des champs avec des clés de race et de type (pourraient être des déclencheurs) dans la table des animaux afin de faciliter les index ultérieurs pour améliorer la vitesse.
Felipe Alcacibar

0

Votre méthode actuelle n'est pas mauvaise. Cependant, si vous allez ajouter plus de races plus tard (oiseau, poisson, etc.), la création d'une table distincte pour chacune pourrait être fastidieuse. Je recommanderais quelque chose comme ceci:

Animal < # Animal_ID, Breed_ID, other attributes >
Breed < # Breed_ID, Race_ID >
Race < # Race_ID >

À ma connaissance, une race ne devrait avoir qu'une seule race. Donc, si vous stockez la race dans la table des animaux, vous pourrez déterminer la race en vous joignant à la table des races. De toute évidence, ajoutez tout autre attribut (nom, description, etc.) aux tables de race et de race si nécessaire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.