Como modelar herança de duas tabelas MySQL

14

Eu tenho algumas tabelas onde eu armazeno dados e dependendo do tipo de pessoa (trabalhador, civil) que fez um trabalho que eu quero armazenar em uma eventtabela, agora esses caras resgatam um animal (existe uma animaltabela).

Finalmente, quero ter uma mesa para armazenar o evento em que um homem (trabalhador, civil) salvou um animal, mas, se eu acrescentasse uma chave estrangeira, ou como saber o idvalor do civil ou trabalhador que fez o trabalho?

Agora, nesse design, eu não sei como relacionar qual pessoa fez o trabalho se, eu tivesse apenas um tipo de pessoa (aka civil), que apenas armazenaria o civil_idvale em uma personcoluna nesta última tabela ... mas como saber se era civil ou trabalhador, preciso de outra tabela "intermediária"?

Como refletir o design do diagrama a seguir no MySQL?

insira a descrição da imagem aqui

Detalhes adicionais

Eu modelei da seguinte maneira:

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

No entanto, existe uma maneira de se livrar dos nulos?

As consultas que tenho são:

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 ;

Aqui está um sqlfiddle atualizado .

cMinor
fonte
qual é o objetivo da tabela TYPE_PERSONquando ela contém apenas uma coluna?
JW # 11/13
1
Seguimento para isso: stackoverflow.com/questions/15128222/… ?
@cMinor - Você está perguntando "como saber a identificação do civil ou trabalhador que fez o trabalho?" Você realmente sabe quem fez o trabalho na vida real (ou imaginário, se isso é um dever de casa)? Você tem dados de origem suficientes?
Como estou me acostumando com a herança, criei uma pessoa da tabela que conteria os tipos de pessoas (trabalhador, civil) e, em seguida, na tabela de eventos: Como fazer referência a uma pessoa dependendo de como o trabalho (civil ou trabalhador)?
Cminor
1
Eu acredito que você iria ficar melhor conselho em administradores de banco de dados
Pieter Geerkens

Respostas:

13

Desde que fiz o diagrama, é melhor eu responder;)

Infelizmente, os bancos de dados relacionais atuais não suportam a herança diretamente, portanto, você precisa transformá-la em tabelas "simples". Geralmente, existem três estratégias para fazer isso:

  1. Todas as classes 1 em uma única tabela com campos não comuns compatíveis com NULL.
  2. Classes de concreto 2 em tabelas separadas. As aulas abstratas não possuem tabelas próprias.
  3. Todas as classes em tabelas separadas.

Para saber mais sobre o que isso realmente significa e alguns prós e contras, consulte os links fornecidos no meu post original , mas em poucas palavras o (3) provavelmente deve ser o seu padrão, a menos que você tenha um motivo específico para um dos outros dois. Você pode representar o (3) no banco de dados da seguinte maneira:

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

Infelizmente, essa estrutura permitirá que você tenha um personque não seja civilnem worker(ou seja, você pode instanciar a classe abstrata) e também criará um personque seja ambos civil e worker. Existem maneiras de aplicar o primeiro no nível do banco de dados e, em um DBMS que suporta restrições diferidas 3, até o último pode ser aplicado no banco de dados, mas este é um dos poucos casos em que o uso da integridade no nível do aplicativo pode ser realmente preferível .


1 person , civile workerneste caso.

2 civil e workerneste caso ( personé "abstrato").

3 O MySQL não.

Branko Dimitrijevic
fonte
Como o último pode ser aplicado no DBMS que suporta restrições adiadas? (impedindo que uma pessoa seja ambos civile worker)
Gima
@ Gima Por favor, siga o link que forneci na resposta.
Branko Dimitrijevic
Você alega que os bancos de dados relacionais atuais não suportam herança. E o postgresql? postgresql.org/docs/9.6/static/ddl-inherit.html
Climax
@ Climax Estou ciente do PostgreSQL, mas sua implementação é apenas parcial. No seu link: "Outros tipos de restrições (únicas, chave primária e chave estrangeira) não são herdadas".
Branko Dimitrijevic
1
@naaz Existem chaves estrangeiras em ambos civile worker. Talvez você tenha perdido a sintaxe da mão curta (apenas REFERENCESsem FOREIGN KEY)?
Branko Dimitrijevic
5

Não há necessidade de Civil_ID e Worker_ID distintos; apenas continue usando o Person-ID como a chave para todas as três tabelas: Person, Civil e Worker. Adicione uma coluna PersonType a Person com os dois valores "Civil" e "Worker".

Agora, isso representa as duas subclasses CivilClass e WorkerClass da classe base abstrata PersonClass como sub-entidades Civil e Worker da entidade base Person. Você obtém uma boa correspondência entre o modelo de dados no banco de dados e o modelo de objeto no aplicativo.

Pieter Geerkens
fonte
Eu fiz um sqlfiddle sqlfiddle.com/#!2/1f6a4/1, mas não sei como ingressar em outra tabela, você poderia apontar sua resposta aqui no sqlfiddle?
Cminor
Não existem "distintos" civil_ide worker_id- eles são a mesma coisa que person_id, apenas nomeados de forma diferente - olham para o FK1marcador (chave estrangeira) na frente deles.
Branko Dimitrijevic
4

Seu caso é uma instância de modelagem de classe / subclasse. Ou, conforme o diagrama em ER, generalização / especialização.

Existem três técnicas que ajudarão você a criar tabelas mysql para cobrir este caso. Eles são chamados de herança de tabela única, herança de tabela de classe e chave primária compartilhada. Você pode lê-los na guia Informações da tag correspondente em SO.

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

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

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

A herança de tabela única é útil para casos simples em que a presença de NULLs não causa problemas. A herança da tabela de classes é melhor para casos mais complicados. A chave primária compartilhada é uma boa maneira de impor relacionamentos individuais e acelerar junções.

Walter Mitty
fonte
1

Você pode criar uma tabela de tipos de pessoas e adicionar um campo a todas as tabelas que precisam da imposição de tipos. Em seguida, crie chaves estrangeiras. Aqui está um exemplo derivado do seu ...

    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
fonte