Как смоделировать наследование двух таблиц MySQL

14

У меня есть несколько таблиц, в которых я храню данные, и в зависимости от типа человека (рабочий, гражданский), который выполнил задание, я хочу сохранить его в eventтаблице, теперь эти ребята спасают животное (есть animalтаблица).

Наконец, я хочу иметь таблицу для хранения события, когда парень (рабочий, гражданский) спас животное, но поклон, должен ли я добавить внешний ключ или как узнать idзначение гражданского или рабочего, который выполнил работу?

Теперь, в этом проекте я не знаю, как определить, какой человек выполнял работу, если бы у меня был только какой-то человек (он же гражданский), я бы только civil_idсохранял вейл в personстолбце в этой последней таблице ... но как знаю, если это был гражданский или рабочий, мне нужны другие "промежуточные" таблицы?

Как отразить дизайн следующей диаграммы в MySQL?

введите описание изображения здесь

Дополнительные детали

Я смоделировал это следующим образом:

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

Однако есть ли способ избавиться от нуля?

У меня есть следующие вопросы:

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 ;

Вот обновленный sqlfiddle .

cMinor
источник
Какова цель таблицы, TYPE_PERSONкогда она содержит только один столбец?
JW 웃
1
Продолжение этого: stackoverflow.com/questions/15128222/… ?
@cMinor - Вы спрашиваете, «как узнать идентификатор гражданина или работника, который сделал работу?» Вы действительно знаете, кто выполнял работу в реальной жизни (или воображаемой, если это домашняя работа)? Достаточно ли у вас исходных данных?
Я привыкаю к ​​наследованию, поэтому я создал табличного человека, который будет содержать типы людей (рабочий, гражданский), а затем в таблице событий. Как ссылаться на человека в зависимости от того, как выполнялась работа (гражданский или рабочий)?
cMinor
1
Я полагаю, что вы получите лучший совет в администраторах баз данных
Питер Гиркенс

Ответы:

13

Поскольку я сделал диаграмму, я лучше отвечу;)

К сожалению, современные реляционные базы данных не поддерживают наследование напрямую, поэтому вам необходимо преобразовать его в «простые» таблицы. Обычно для этого есть 3 стратегии:

  1. Все классы 1 в одной таблице с NULL-способными нечастыми полями.
  2. Конкретные классы 2 в отдельных таблицах. Абстрактные классы не имеют собственных таблиц.
  3. Все занятия в отдельных таблицах.

Для получения дополнительной информации о том, что это на самом деле означает, а также о некоторых плюсах и минусах, см. Ссылки, приведенные в моем исходном сообщении , но в двух словах (3), вероятно, должно быть вашим значением по умолчанию, если у вас нет конкретной причины для одного из двух других. Вы можете представить (3) в базе данных просто так:

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

К сожалению, эта структура позволит вам иметь , personчто не является ни civilни worker(т.е. вы можете создать экземпляр абстрактного класса), а также позволит вам создать personэто как civil и worker. Существуют способы применения первого на уровне базы данных, и в СУБД, которая поддерживает отложенные ограничения 3, даже последние могут быть применены в базе данных, но это один из немногих случаев, когда использование целостности на уровне приложения может оказаться предпочтительным. ,


1 person , civilи workerв этом случае.

2 civil и workerв этом случае ( personявляется «абстрактным»).

3 Что MySQL не делает.

Бранко Димитриевич
источник
Как последнее может быть применено в СУБД, которые поддерживают отложенные ограничения? (запретив человеку быть и тем civilи другим worker)
Гима
@Gima Пожалуйста, перейдите по ссылке, которую я указал в ответе.
Бранко Димитриевич
Вы утверждаете, что текущие реляционные базы данных не поддерживают наследование. Что насчет postgresql? postgresql.org/docs/9.6/static/ddl-inherit.html
Климакс
@Climax Я знаю о PostgreSQL, но его реализация является лишь частичной. По вашей ссылке: «Другие типы ограничений (уникальный, первичный ключ и ограничения внешнего ключа) не наследуются».
Бранко Димитриевич
1
@naaz Внешние ключи существуют как в, так civilи в worker. Возможно, вы пропустили сокращенный синтаксис (только REFERENCESбез FOREIGN KEY)?
Бранко Димитриевич
5

Нет необходимости в различном Civil_ID и Worker_ID; просто продолжайте использовать Person-ID в качестве ключа для всех трех таблиц: Person, Civil и Worker. Добавьте столбец PersonType к Person с двумя значениями "Civil" и "Worker".

Теперь он представляет два подкласса CivilClass и WorkerClass абстрактного базового класса PersonClass в качестве дочерних объектов Civil и Worker базового объекта Person. Вы получаете хорошее соответствие между моделью данных в БД и объектной моделью в приложении.

Питер Гиркенс
источник
Я сделал sqlfiddle sqlfiddle.com/#!2/1f6a4/1, но я не знаю, как присоединиться к другой таблице, не могли бы вы указать свой ответ здесь, в sqlfiddle?
cMinor
Здесь нет «отличных» civil_idи worker_id- это то же самое, что person_idи просто названные по-разному - посмотрите на FK1маркер (внешний ключ) перед ними.
Бранко Димитриевич
4

Ваш случай является примером моделирования класса / подкласса. Или, как вы наметили это в ER, обобщение / специализация.

Есть три метода, которые помогут вам разработать таблицы mysql для решения этой проблемы. Они называются наследованием отдельных таблиц, наследованием таблиц классов и общим первичным ключом. Вы можете прочитать о них на информационной вкладке из соответствующего тега в SO.

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

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

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

Наследование отдельных таблиц полезно для простых случаев, когда наличие NULL не вызывает проблем. Наследование таблиц классов лучше для более сложных случаев. Общий первичный ключ - это хороший способ реализации отношений один-к-одному и ускорения соединений.

Уолтер Митти
источник
1

Вы можете создать таблицу типов лиц и добавить поле ко всем таблицам, нуждающимся в применении типа. Затем создайте внешние ключи. Вот пример, полученный из вашего ...

    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
источник