Внешние ключи MySQL - как применять один к одному в разных таблицах? - PullRequest
6 голосов
/ 22 сентября 2008

Если у меня есть таблица в MySQL, представляющая базовый класс, и у меня есть набор таблиц, представляющих поля в производных классах, каждая из которых ссылается на базовую таблицу с внешним ключом, есть ли способ заставить MySQL навязать взаимно-однозначное отношение между производной таблицей и базовой таблицей, или это должно быть сделано в коде?

На примере следующей быстрой 'n' грязной схемы, есть ли способ получить MySQL, чтобы гарантировать, что строки в product_cd и product_dvd не могут использовать один и тот же product_id? Есть ли лучший способ спроектировать схему, чтобы база данных могла обеспечить соблюдение этого отношения, или это просто невозможно?

CREATE TABLE IF NOT EXISTS `product` (
    `product_id` int(10) unsigned NOT NULL auto_increment,
    `product_name` varchar(50) NOT NULL,
    `description` text NOT NULL,
    PRIMARY KEY  (`product_id`)
) ENGINE = InnoDB;

CREATE TABLE `product_cd` (
    `product_cd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
    `product_id` INT UNSIGNED NOT NULL ,
    `artist_name` VARCHAR( 50 ) NOT NULL ,
    PRIMARY KEY ( `product_cd_id` ) ,
    INDEX ( `product_id` )
) ENGINE = InnoDB;

ALTER TABLE `product_cd` ADD FOREIGN KEY ( `product_id` ) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ;

CREATE TABLE `product_dvd` (
    `product_dvd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
    `product_id` INT UNSIGNED NOT NULL ,
    `director` VARCHAR( 50 ) NOT NULL ,
    PRIMARY KEY ( `product_dvd_id` ) ,
    INDEX ( `product_id` )
) ENGINE = InnoDB;

ALTER TABLE `product_dvd` ADD FOREIGN KEY ( `product_id` ) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ;

@ Skliwz , не могли бы вы предоставить более подробную информацию о том, как можно использовать триггеры для обеспечения соблюдения этого ограничения с помощью предоставленной схемы?

@ Боес , это звучит великолепно. Как это работает в ситуациях, когда у вас есть ребенок от ребенка? Например, если мы добавили product_movie и сделали product_dvd дочерним по отношению к product_movie? Будет ли кошмаром сопровождения сделать так, чтобы проверочное ограничение для product_dvd учитывало также все дочерние типы?

Ответы [ 6 ]

8 голосов
/ 22 сентября 2008

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

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

4 голосов
/ 22 сентября 2008

Если вы избавились от product-dvd-id и product-cd-id и использовали product-id в качестве первичного ключа для всех три таблицы, вы можете по крайней мере убедиться, что ни два DVD, ни два CD не используют один и тот же product-id . Плюс было бы меньше идентификаторов для отслеживания.

И вам может понадобиться какой-нибудь столбец type в таблице продуктов.

3 голосов
/ 22 сентября 2008

Чтобы убедиться, что продукт - это или компакт-диск, или DVD, я бы добавил столбец типа и сделал его частью первичного ключа. В производном столбце вы добавляете проверочное ограничение для типа. В этом примере я установил для cd значение 1, и вы можете сделать dvd = 2 и т. Д. Для каждой производной таблицы.

CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(10) unsigned NOT NULL auto_increment,
'product_type' int not null,
`product_name` varchar(50) NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`product_id`, 'product_type')
) ENGINE = InnoDB;

CREATE TABLE `product_cd` (
`product_id` INT UNSIGNED NOT NULL ,
'product_type' int not null default(1) check ('product_type' = 1)
`artist_name` VARCHAR( 50 ) NOT NULL ,
PRIMARY KEY ( `product_id`, 'product_type' ) ,
) ENGINE = InnoDB;

ALTER TABLE `product_cd` ADD FOREIGN KEY ( `product_id`, 'product_type' ) 
REFERENCES `product` (`product_id`, 'product_type') 
ON DELETE RESTRICT ON UPDATE RESTRICT ;
3 голосов
/ 22 сентября 2008

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

1 голос
/ 22 июля 2011

Единственный эффективный способ поддерживать отношение «один к одному» - установить для столбца внешнего ключа значение UNIQUE ; Таким образом, мы уверены, что для него будет создана только одна запись. И если вы попытаетесь нарушить это ограничение, вы получите следующую ошибку (MySQL 5.1):

`1 error(s) saving changes to table testdb`.table4: INSERT INTO testdb.table4 (idtable4, label, table3_idtable3) VALUES (0, 'soso', 0) 1062: Duplicate entry '0' for key 'table3_idtable3_UNIQUE' Rollback complete
1 голос
/ 22 сентября 2008

Если вы используете MySQL 5.x, вы можете использовать триггеры для этих видов ограничений.

Еще один (неоптимальный) вариант - использовать столбец «тип» в родительской таблице, чтобы игнорировать дублирование и иметь возможность выбрать правильную «таблицу расширений».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...