Cómo modelar la herencia de dos tablas MySQL

14

Tengo algunas tablas donde almaceno datos y, dependiendo del tipo de persona (trabajador, civil) que hizo un trabajo, quiero almacenarlo en una eventtabla, ahora estos tipos rescatan a un animal (hay una animaltabla).

Finalmente, quiero tener una mesa para almacenar el evento de que un hombre (trabajador, civil), salvó un animal, pero ¿debería agregar una clave foránea o cómo saber el idvalor del civil o trabajador que hizo el trabajo?

Ahora, en este diseño, no sé cómo relacionar qué persona hizo el trabajo si, solo tuviera un tipo de persona (también conocido como civil), solo almacenaría el civil_idvalle en una personcolumna en esta última tabla ... pero cómo saber si era civil o trabajador, ¿necesito otra tabla "intermedia"?

¿Cómo reflejar el diseño del siguiente diagrama en MySQL?

ingrese la descripción de la imagen aquí

Detalles adicionales

Lo he modelado de la siguiente manera:

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' );

Sin embargo, ¿hay alguna manera de deshacerse de los nulos?

Las consultas que tengo son:

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 ;

Aquí hay un sqlfiddle actualizado .

cMinor
fuente
¿Cuál es el propósito de la tabla TYPE_PERSONcuando solo contiene una columna?
JW 웃
1
Seguimiento de esto: stackoverflow.com/questions/15128222/… ?
@cMinor - Usted pregunta "¿cómo saber la identificación del civil o trabajador que hizo el trabajo?" ¿Realmente sabes quién hizo el trabajo en la vida real (o imaginario, si esto es una tarea)? ¿Tiene suficientes datos de origen?
Me estoy acostumbrando a la herencia, así que creé una persona de mesa que contendría los tipos de personas (trabajador, civil), luego en la tabla de eventos, ¿Cómo hacer referencia a una persona dependiendo de cómo funcionó el trabajo (civil o trabajador)?
cMoror
1
Creo que obtendría mejores consejos en Administradores de bases de datos
Pieter Geerkens

Respuestas:

13

Desde que hice el diagrama, mejor respondo;)

Desafortunadamente, las bases de datos relacionales actuales no admiten la herencia directamente, por lo tanto, debe transformarla en tablas "simples". Generalmente hay 3 estrategias para hacerlo:

  1. Todas las clases 1 en una sola tabla con campos no comunes con capacidad NULL.
  2. Clases concretas 2 en mesas separadas. Las clases abstractas no tienen sus propias tablas.
  3. Todas las clases en mesas separadas.

Para obtener más información sobre lo que esto realmente significa y algunos pros y contras, consulte los enlaces provistos en mi publicación original , pero en pocas palabras, el (3) probablemente debería ser su valor predeterminado a menos que tenga una razón específica para uno de los otros dos. Puede representar el (3) en la base de datos simplemente así:

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...
);

Desafortunadamente, esta estructura le permitirá tener un personque no es ni civiltampoco worker(es decir, puede crear una instancia de la clase abstracta), y también le permitirá crear un personque sea ambos civil y worker. Hay formas de hacer cumplir la primera en el nivel de la base de datos, y en un DBMS que admite restricciones diferidas 3, incluso la última puede aplicarse en la base de datos, pero este es uno de los pocos casos en los que podría ser preferible utilizar la integridad en el nivel de la aplicación. .


1 person , civily workeren este caso.

2 civil y workeren este caso ( persones "abstracto").

3 Lo que MySQL no tiene.

Branko Dimitrijevic
fuente
¿Cómo se puede aplicar esto último en DBMS que admite restricciones diferidas? (no permitir a una persona de ser a la vez civily worker)
Gima
@Gima Por favor, siga el enlace que he proporcionado en la respuesta.
Branko Dimitrijevic
Afirma que las bases de datos relacionales actuales no admiten la herencia. ¿Qué pasa con postgresql? postgresql.org/docs/9.6/static/ddl-inherit.html
Climax
@Climax Soy consciente de PostgreSQL, pero su implementación es solo parcial. Desde su enlace: "Otros tipos de restricciones (restricciones únicas, de clave principal y de clave externa) no se heredan".
Branko Dimitrijevic
1
@naaz Las claves externas existen en ambos civily worker. ¿Quizás te perdiste la sintaxis de mano corta (solo REFERENCESsin FOREIGN KEY)?
Branko Dimitrijevic el
5

No hay necesidad de Civil_ID y Worker_ID distintos; simplemente continúe usando la identificación de persona como clave para las tres tablas: Persona, Civil y Trabajador. Agregue una columna PersonType a Person con los dos valores "Civil" y "Worker".

Esto ahora representa las dos subclases CivilClass y WorkerClass de la clase base abstracta PersonClass como sub-entidades Civil y Worker de la entidad base Person. Obtiene una buena correspondencia entre el modelo de datos en la base de datos con el modelo de objetos en la aplicación.

Pieter Geerkens
fuente
He hecho un sqlfiddle sqlfiddle.com/#!2/1f6a4/1 pero no sé cómo unirme a otra tabla, ¿podría indicar su respuesta aquí en el sqlfiddle?
cMoror
No hay "distintos" civil_idy worker_id, son lo mismo que person_id, simplemente nombrados de manera diferente, miren el FK1marcador (clave externa) frente a ellos.
Branko Dimitrijevic
4

Su caso es una instancia de modelado de clase / subclase. O, como lo ha diagramado en ER, generalización / especialización.

Existen tres técnicas que lo ayudarán a diseñar tablas mysql para cubrir este caso. Se denominan herencia de tabla única, herencia de tabla de clase y clave primaria compartida. Puede leerlos en la pestaña de información de la etiqueta correspondiente en SO.

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

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

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

La herencia de tabla única es útil para casos simples donde la presencia de NULL no causa problemas. La herencia de la tabla de clase es mejor para casos más complicados. La clave primaria compartida es una buena manera de reforzar las relaciones uno a uno y acelerar las uniones.

Walter Mitty
fuente
1

Puede crear una tabla de tipo de persona y agregar un campo a todas las tablas que necesitan la aplicación de tipo. Luego cree claves foráneas. Aquí hay un ejemplo derivado del tuyo ...

    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
fuente