Jak modelować dziedziczenie dwóch tabel MySQL

14

Mam kilka tabel, w których przechowuję dane i w zależności od rodzaju osoby (pracownik, pracownik służby cywilnej), która wykonała zadanie, chcę je przechowywać w eventtabeli, teraz ci faceci ratują zwierzę (jest tam animaltabela).

Na koniec chcę mieć stolik do przechowywania zdarzenia, w którym facet (pracownik, pracownik służby cywilnej) uratował zwierzę, ale ukłon powinienem dodać klucz obcy lub jak poznać idwartość pracownika służby cywilnej lub pracownika, który wykonał pracę?

Teraz, w tym projekcie nie wiem, jak powiązać, która osoba wykonała pracę, gdybym miał tylko rodzaj osoby (inaczej cywilnej), tylko przechowałbym civil_iddolinę w personkolumnie w tej ostatniej tabeli ... ale jak wiesz, czy był to pracownik cywilny czy pracownik, czy potrzebuję innego „pośredniego” stołu?

Jak odzwierciedlić projekt poniższego diagramu w MySQL?

wprowadź opis zdjęcia tutaj

Dodatkowe Szczegóły

Modelowałem to w następujący sposób:

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

Czy jest jednak sposób na pozbycie się zer?

Mam pytania:

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 ;

Oto zaktualizowane narzędzie sqlfiddle .

cMinor
źródło
jaki jest cel tabeli, TYPE_PERSONgdy zawiera ona tylko jedną kolumnę?
JW 웃
@ cMinor - pytasz „jak poznać identyfikator pracownika cywilnego lub pracownika, który wykonał pracę?” Czy naprawdę wiesz, kto wykonał tę pracę w prawdziwym życiu (lub wyobrażony, jeśli jest to praca domowa)? Czy masz wystarczające dane źródłowe?
Przyzwyczajam się do dziedziczenia, więc stworzyłem osobę z tabeli, która pomieściłaby typy ludzi (pracownik, pracownik cywilny), a następnie w tabeli zdarzeń, jak odwołać się do osoby w zależności od tego, jak dana praca (osoba cywilna lub pracownik)?
cMinor
1
Wierzę, że uzyskałbyś lepszą poradę od Administratorów baz danych
Pieter Geerkens

Odpowiedzi:

13

Odkąd wykonałem schemat, lepiej odpowiem;)

Obecne relacyjne bazy danych niestety nie obsługują bezpośrednio dziedziczenia, dlatego należy przekształcić je w „zwykłe” tabele. Istnieją na ogół 3 strategie:

  1. Wszystkie klasy 1 w jednej tabeli z polami nieobsługującymi NULL.
  2. Konkretne klasy 2 w osobnych tabelach. Klasy abstrakcyjne nie mają własnych tabel.
  3. Wszystkie klasy w osobnych tabelach.

Aby uzyskać więcej informacji na temat tego, co to właściwie oznacza, a także niektórych zalet i wad, zapoznaj się z linkami podanymi w moim oryginalnym poście , ale w skrócie (3) prawdopodobnie powinno być twoim domyślnym, chyba że masz konkretny powód jednego z dwóch pozostałych. Możesz reprezentować (3) w bazie danych w następujący sposób:

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

Niestety, ta struktura pozwoli ci mieć coś, personco nie jest civilani worker(tj. Możesz utworzyć instancję klasy abstrakcyjnej), a także pozwoli ci stworzyć coś, personco jest jednocześnie civil i worker. Istnieją sposoby egzekwowania tego pierwszego na poziomie bazy danych, aw systemie DBMS, który obsługuje odroczone ograniczenia 3, nawet ten drugi może być egzekwowany w bazie danych, ale jest to jeden z niewielu przypadków, w których stosowanie integralności na poziomie aplikacji może być rzeczywiście preferowane .


1 person , civili workerw tym przypadku.

2 civil iw workertym przypadku ( personjest „abstrakcyjny”).

3 Który MySQL nie.

Branko Dimitrijevic
źródło
Jak można egzekwować te ostatnie w DBMS, które obsługują odroczone ograniczenia? (nie pozwalając osobie być jednocześnie civili worker)
Gima,
@Gima Postępuj zgodnie z linkiem podanym w odpowiedzi.
Branko Dimitrijevic
Twierdzisz, że bieżące relacyjne bazy danych nie obsługują dziedziczenia. Co z postgresql? postgresql.org/docs/9.6/static/ddl-inherit.html
Climax
@Climax Znam PostgreSQL, ale jego implementacja jest tylko częściowa. Z linku: „Inne typy ograniczeń (ograniczenia unikatowe, klucz podstawowy i klucz obcy) nie są dziedziczone”.
Branko Dimitrijevic
1
@naaz Klucze obce istnieją zarówno w, jak civili w worker. Być może przegapiłeś składnię krótkiej ręki (tylko REFERENCESbez FOREIGN KEY)?
Branko Dimitrijevic
5

Nie ma potrzeby podawania odrębnego identyfikatora_ Civil_ID i identyfikatora_ Worker_ID; po prostu nadal używaj Person-ID jako klucza dla wszystkich trzech tabel: Person, Civil i Worker. Dodaj kolumnę PersonType do Person z dwiema wartościami „Civil” i „Worker”.

To reprezentuje teraz dwie podklasy CivilClass i WorkerClass abstrakcyjnej klasy bazowej PersonClass jako podelementy Civil i Pracownik bazowej osoby Person. Otrzymujesz ładną zgodność między modelem danych w DB a modelem obiektowym w aplikacji.

Pieter Geerkens
źródło
Zrobiłem sqlfiddle sqlfiddle.com/#!2/1f6a4/1, ale nie wiem, jak dołączyć do innego stołu, czy mógłbyś podać swoją odpowiedź tutaj w sqlfiddle?
cMinor
Nie ma „wyraźnego” civil_idi worker_id- są tym samym, co person_idinaczej nazwane - spójrz na znajdujący się FK1przed nimi znacznik (klucz obcy).
Branko Dimitrijevic
4

Twoja sprawa jest przykładem modelowania klas / podklas. Lub, tak jak to naszkicowałeś w ER, uogólnienie / specjalizacja.

Istnieją trzy techniki, które pomogą Ci zaprojektować tabele mysql na ten przypadek. Nazywa się je dziedziczeniem pojedynczej tabeli, dziedziczeniem tabeli klas i wspólnym kluczem podstawowym. Możesz przeczytać o nich na karcie informacyjnej z odpowiedniego tagu w SO.

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

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

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

Dziedziczenie pojedynczej tabeli jest przydatne w prostych przypadkach, w których obecność wartości NULL nie powoduje problemów. Dziedziczenie tabel klas jest lepsze w bardziej skomplikowanych przypadkach. Wspólny klucz podstawowy to dobry sposób na wymuszenie relacji jeden na jeden i przyspieszenie łączenia.

Walter Mitty
źródło
1

Możesz utworzyć tabelę typów osób i dodać pole do wszystkich tabel wymagających wymuszania typu. Następnie utwórz klucze obce. Oto przykład pochodzący od twojego ...

    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
źródło