Comment modéliser l'héritage de deux tables MySQL

14

J'ai des tableaux où je stocke des données et selon le type de personne (travailleur, civil) qui a fait un travail, je veux le stocker dans une eventtable, maintenant ces gars sauvent un animal (il y a une animaltable).

Enfin, je veux avoir une table pour stocker l'événement qu'un gars (travailleur, civil), a sauvé un animal, mais s'incliner dois-je ajouter une clé étrangère ou comment connaître la idvaleur du civil ou du travailleur qui a fait le travail?

Maintenant, dans cette conception, je ne sais pas comment identifier la personne qui a fait le travail si, je n'avais qu'une sorte de personne (alias civile), je ne stockerais le civil_idval dans une personcolonne dans ce dernier tableau ... mais comment savoir si elle était civile ou ouvrière, ai-je besoin d'une autre table "intermédiaire"?

Comment refléter la conception du diagramme suivant dans MySQL?

entrez la description de l'image ici

Détails supplémentaires

Je l'ai modélisé de la manière suivante:

DROP    TABLE IF EXISTS `tbl_animal`; 
CREATE TABLE `tbl_animal` (
    id_animal       INTEGER     NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name            VARCHAR(25) NOT NULL DEFAULT "no name",
    specie          VARCHAR(10) NOT NULL DEFAULT "Other",
    sex             CHAR(1)     NOT NULL DEFAULT "M",
    size            VARCHAR(10) NOT NULL DEFAULT "Mini",
    edad            VARCHAR(10) NOT NULL DEFAULT "Lact",
    pelo            VARCHAR(5 ) NOT NULL DEFAULT "short",
    color           VARCHAR(25) NOT NULL DEFAULT "not defined",
    ra              VARCHAR(25) NOT NULL DEFAULT "not defined",
    CONSTRAINT `uc_Info_Animal` UNIQUE (`id_animal`)           
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;


INSERT INTO `tbl_animal` VALUES (1,'no name', 'dog', 'M','Mini','Lact','Long','black','Bobtail');
INSERT INTO `tbl_animal` VALUES (2,'peluchin', 'cat', 'M','Mini','Lact','Long','white','not defined');
INSERT INTO `tbl_animal` VALUES (3,'asechin', 'cat', 'M','Mini','Lact','Corto','orange','not defined');

DROP    TABLE IF EXISTS `tbl_person`;  
CREATE TABLE `tbl_person` (
    type_person  VARCHAR(50) NOT NULL primary key        
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
INSERT INTO `tbl_person` (type_person) VALUES ('Worker');
INSERT INTO `tbl_person` (type_person) VALUES ('Civil');



DROP    TABLE IF EXISTS `tbl_worker`;  
CREATE TABLE `tbl_worker`(
    id_worker           INTEGER  NOT NULL PRIMARY KEY,
    type_person         VARCHAR(50) NOT NULL , 
    name_worker         VARCHAR(50) NOT NULL ,    
    address_worker      VARCHAR(40) NOT NULL DEFAULT "not defined",     
    delegation          VARCHAR(40) NOT NULL DEFAULT "not defined",
    FOREIGN KEY (type_person)               REFERENCES `tbl_person` (type_person),
    CONSTRAINT `uc_Info_worker` UNIQUE (`id_worker`)           
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

INSERT INTO `tbl_worker` VALUES (1,'Worker','N_CEDENTE1', 'DIR Worker 1', 'DEL');
INSERT INTO `tbl_worker` VALUES (2,'Worker','N_worker1', 'DIR Worker 2', 'DEL');
INSERT INTO `tbl_worker` VALUES (3,'Worker','N_worker2', 'address worker','delegation worker'); 


DROP    TABLE IF EXISTS `tbl_civil`; 
CREATE TABLE `tbl_civil`(
    id_civil                        INTEGER  NOT NULL PRIMARY KEY,
    type_person         VARCHAR(50) NOT NULL ,
    name_civil                      VARCHAR(50)  ,
    procedence_civil                VARCHAR(40)  NOT NULL DEFAULT "Socorrism",    
  FOREIGN KEY (type_person)             REFERENCES `tbl_person` (type_person),
    CONSTRAINT `uc_Info_civil` UNIQUE (`id_civil`)           
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;


INSERT INTO `tbl_civil`  VALUES (1,'Civil','N_civil1' , 'Socorrism');


CREATE TABLE `tbl_event` (
    id_event     INTEGER NOT NULL,
    id_animal    INTEGER NOT NULL,
    type_person  VARCHAR(50) NOT NULL , 
    date_reception DATE DEFAULT '2000-01-01 01:01:01',
    FOREIGN KEY (id_animal)   REFERENCES `tbl_animal`    (id_animal),
    FOREIGN KEY (type_person )  REFERENCES `tbl_person`   (type_person ),
    CONSTRAINT `uc_Info_ficha_primer_ingreso` UNIQUE (`id_animal`,`id_event`)     
)ENGINE=InnoDB  DEFAULT CHARSET=utf8;

INSERT INTO `tbl_event` VALUES (1,1, 'Worker','2013-01-01 01:01:01' );
INSERT INTO `tbl_event` VALUES (2,2, 'Civil','2013-01-01 01:01:01' );

Cependant, existe-t-il un moyen de se débarrasser des null?

Les requêtes que j'ai sont:

SELECT  a.*,b.*,z.*
FROM    tbl_event a
        left JOIN tbl_worker b
            ON a.type_person = b.type_person
        left JOIN tbl_animal z
            ON   z.id_animal = a.id_animal ;

SELECT  a.*,b.*,z.*
FROM    tbl_event a
        left JOIN tbl_civil b
            ON a.type_person = b.type_person
        left JOIN tbl_animal z
            ON   z.id_animal = a.id_animal ;

Voici un sqlfiddle mis à jour .

cMinor
la source
quel est le but de la table TYPE_PERSONlorsqu'elle ne contient qu'une seule colonne?
JW
@cMinor - Vous demandez "comment connaître l'ID du civil ou du travailleur qui a fait le travail?" Savez-vous réellement qui a fait le travail dans la vraie vie (ou imaginaire, si c'est un devoir)? Avez-vous suffisamment de données source?
Je m'habitue à l'héritage, j'ai donc créé une personne de table qui contiendrait les types de personnes (travailleur, civil), puis dans la table d'événements, Comment référencer une personne en fonction de la façon dont le travail a été fait (civil ou travailleur)?
cMinor
1
Je pense que vous obtiendriez de meilleurs conseils
Pieter Geerkens

Réponses:

13

Depuis que j'ai fait le schéma, je ferais mieux de répondre;)

Les bases de données relationnelles actuelles ne prennent malheureusement pas directement en charge l'héritage, vous devez donc le transformer en tables "simples". Il existe généralement 3 stratégies pour le faire:

  1. Toutes les classes 1 dans une seule table avec des champs non communs compatibles NULL.
  2. Classes concrètes 2 dans des tableaux séparés. Les classes abstraites n'ont pas leurs propres tables.
  3. Toutes les classes dans des tableaux séparés.

Pour en savoir plus sur ce que cela signifie réellement et certains avantages et inconvénients, veuillez consulter les liens fournis dans mon article d' origine , mais en un mot, le (3) devrait probablement être votre valeur par défaut, sauf si vous avez une raison spécifique pour l'un des deux autres. Vous pouvez représenter le (3) dans la base de données simplement comme ceci:

CREATE TABLE person (
    person_id int PRIMARY KEY
    -- Other fields...
);

CREATE TABLE civil (
    civil_id int PRIMARY KEY REFERENCES person (person_id)
    -- Other fields...
);

CREATE TABLE worker (
    worker_id int PRIMARY KEY REFERENCES person (person_id)
    -- Other fields...
);

CREATE TABLE event (
    event_id int PRIMARY KEY,
    person_id int REFERENCES person (person_id)
    -- Other fields...
);

Malheureusement, cette structure vous permettra d'avoir un personqui n'est ni civilni worker(c'est-à-dire que vous pouvez instancier la classe abstraite), et vous permettra également de créer un personqui est à la fois civil et worker. Il existe des moyens d'appliquer les premiers au niveau de la base de données, et dans un SGBD qui prend en charge les contraintes différées 3, même les derniers peuvent être appliqués dans la base de données, mais c'est l'un des rares cas où l'utilisation de l'intégrité au niveau de l'application pourrait être préférable .


1 person , civilet workerdans ce cas.

2 civil et workerdans ce cas ( personest "abstrait").

3 Ce que MySQL ne fait pas.

Branko Dimitrijevic
la source
Comment ce dernier peut-il être appliqué dans un SGBD qui prend en charge les contraintes différées? (interdisant à une personne d'être à la fois civilet worker)
Gima
@Gima Veuillez suivre le lien que j'ai fourni dans la réponse.
Branko Dimitrijevic
Vous prétendez que les bases de données relationnelles actuelles ne prennent pas en charge l'héritage. Et postgresql? postgresql.org/docs/9.6/static/ddl-inherit.html
Climax
@Climax Je connais PostgreSQL, mais son implémentation n'est que partielle. À partir de votre lien: "Les autres types de contraintes (contraintes de clé unique, de clé primaire et de clé étrangère) ne sont pas héritées."
Branko Dimitrijevic
1
@naaz Des clés étrangères existent à la fois dans civilet worker. Peut-être avez-vous manqué la syntaxe abrégée (juste REFERENCESsans FOREIGN KEY)?
Branko Dimitrijevic
5

Il n'y a pas besoin de Civil_ID et Worker_ID distincts; continuez simplement à utiliser Person-ID comme clé pour les trois tables: Person, Civil et Worker. Ajoutez une colonne PersonType à Person avec les deux valeurs "Civil" et "Worker".

Cela représente désormais les deux sous-classes CivilClass et WorkerClass de la classe de base abstraite PersonClass en tant que sous-entités Civil et Worker de l'entité de base Person. Vous obtenez une belle correspondance entre le modèle de données dans la base de données avec le modèle d'objet dans l'application.

Pieter Geerkens
la source
J'ai fait un sqlfiddle sqlfiddle.com/#!2/1f6a4/1 mais je ne sais pas comment rejoindre une autre table, pourriez-vous s'il vous plaît pointer votre réponse ici dans le sqlfiddle?
cMinor
Il n'y a pas de "distinct" civil_idet worker_id- c'est la même chose que person_id, juste nommé différemment - regardez le FK1marqueur (clé étrangère) devant eux.
Branko Dimitrijevic
4

Votre cas est une instance de modélisation de classe / sous-classe. Ou, comme vous l'avez schématisé dans ER, généralisation / spécialisation.

Il existe trois techniques qui vous aideront à concevoir des tables mysql pour couvrir ce cas. Ils sont appelés héritage de table unique, héritage de table de classe et clé primaire partagée. Vous pouvez les lire dans l'onglet info de la balise correspondante dans SO.

/programming//tags/single-table-inheritance/info

/programming//tags/class-table-inheritance/info

/programming//tags/shared-primary-key/info

L'héritage de table unique est utile dans les cas simples où la présence de valeurs NULL ne pose pas de problème. L'héritage de table de classe est préférable pour les cas plus compliqués. La clé primaire partagée est un bon moyen d'imposer des relations un-à-un et d'accélérer les jointures.

Walter Mitty
la source
1

Vous pouvez créer une table de type de personne et ajouter un champ à toutes les tables nécessitant l'application du type. Créez ensuite des clés étrangères. Voici un exemple dérivant du vôtre ...

    CREATE TABLE person_type (
        person_type_id int PRIMARY KEY
        -- data: 1=civil, 2=worker
        -- Other fields (such as a label)...
    );

    CREATE TABLE person (
        person_id int PRIMARY KEY
        person_type_id int FOREIGN KEY REFERENCES person_type (person_type_id)
        -- Other fields...
    );

    CREATE TABLE civil (
        civil_id int PRIMARY KEY REFERENCES person (person_id)
        person_type_id int FOREIGN KEY REFERENCES person (person_type_id)
        -- Other fields...
    );

    CREATE TABLE worker (
        worker_id int PRIMARY KEY REFERENCES person (person_id)
        person_type_id int FOREIGN KEY REFERENCES person (person_type_id)
        -- Other fields...
    );

    CREATE TABLE event (
        event_id int PRIMARY KEY,
        person_id int REFERENCES person (person_id)
        -- Type is optional here, but you could enforce event for a particular type
        person_type_id int FOREIGN KEY REFERENCES person (person_type_id)
        -- Other fields...
    );
Isometriq
la source