Ваш коллега реализовал проект под названием Полиморфные ассоциации . То есть «внешний ключ» относится к одной из двух разных родительских таблиц. Большинство людей добавляют еще один столбец parent_type
или что-то в этом роде, чтобы вы могли определить, к какой родительской таблице относится данная строка. В случае вашего коллеги он вместо этого разделил диапазон идентификаторов. Это хрупкий дизайн, потому что вы не можете применить его на уровне базы данных. Если вы когда-нибудь введете номер отдела> 100, вы не сможете узнать, относятся ли ваши статьи к отделу или отделу.
В то время как вы разработали дизайн, который выглядит как Наследование одной таблицы , где вы храните несколько связанных типов в одной таблице, поэтому первичные ключи гарантированно остаются уникальными, а Статьи могут ссылаться на любой экземпляр любого из связанных типов.
Вот еще одна альтернатива:
Подумайте об объектно-ориентированном дизайне. Если вы хотите, чтобы два разных класса имели статьи, вы можете создать общий суперкласс или общий интерфейс для этих двух классов. Вы можете сделать то же самое в SQL:
ArticleProducer
---------------
ProducerID (PK) int NOT NULL
Department
----------
DepartmentID (PK) int NOT NULL, (FK)->ArticleProducer
DepartmentName varchar(50) NOT NULL
Division
--------
DivisionID (PK) int NOT NULL, (FK)->ArticleProducer
DepartmentID (FK) int NOT NULL
DivisonName varchar(50) NOT NULL
Article
-------
ArticleID (PK) int NOT NULL, (FK)->ArticleProducer
UniqueID int NOT NULL
ArticleName varchar(50) NOT NULL
Таким образом, статья должна быть произведена одним ArticleProducer
. Каждый департамент или отдел является производителем товара.
См. Также Почему вы не можете иметь внешний ключ в полиморфной ассоциации?
Подробнее о полиморфных ассоциациях см. Мою презентацию Практические объектно-ориентированные модели в SQL или мою книгу Антипаттерны SQL: предотвращение ловушек программирования баз данных .
Комментарии от Эрвина Смута:
Вы правы, пытаясь добиться того, чтобы не более одна строка из всех таблиц подтипов была немного хитрой. К сожалению, MySQL не поддерживает ограничения CHECK ни в одном из механизмов хранения. Вы можете достичь чего-то похожего с помощью таблиц поиска:
CREATE TABLE ArticleProducerTypes (ProducerType TINYINT UNSIGNED PRIMARY KEY);
INSERT INTO ArticleProducerTypes VALUES (1), (2);
CREATE TABLE ArticleProducer (
ProducerID INT UNSIGNED NOT NULL PRIMARY KEY,
ProducerType TINYINT UNSIGNED NOT NULL,
UNIQUE KEY (ProducerID,ProducerType),
FOREIGN KEY (ProducerType)
REFERENCES ArticleProducerTypes(ProducerType)
) ENGINE=InnoDB;
CREATE TABLE DepartmentProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY);
INSERT INTO DepartmentProducerType VALUES (1);
CREATE TABLE Department (
DepartmentID INT UNSIGNED NOT NULL PRIMARY KEY,
DepartmentName VARCHAR(50) NOT NULL,
ProducerType TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (DepartmentID, ProducerType)
REFERENCES ArticleProducer(ProducerID, ProducerType),
FOREIGN KEY (ProducerType)
REFERENCES DepartmentProducerType(ProducerType) -- restricted to '1'
) ENGINE=InnODB;
CREATE TABLE DivisionProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY);
INSERT INTO DivisionProducerType VALUES (2);
CREATE TABLE Division (
DivisionID INT UNSIGNED NOT NULL PRIMARY KEY,
ProducerType TINYINT UNSIGNED NOT NULL,
DepartmentID INT UNSIGNED NOT NULL,
FOREIGN KEY (DivisionID, ProducerType)
REFERENCES ArticleProducer(ProducerID, ProducerType),
FOREIGN KEY (ProducerType)
REFERENCES DivisionProducerType(ProducerType), -- restricted to '2'
FOREIGN KEY (DepartmentID)
REFERENCES Department(DepartmentID)
) ENGINE=InnODB;
CREATE TABLE Article (
ArticleID INT UNSIGNED NOT NULL PRIMARY KEY,
ArticleName VARCHAR(50) NOT NULL,
FOREIGN KEY (ArticleID)
REFERENCES ArticleProducer(ProducerID)
);
Теперь на каждую данную строку в ArticleProducer могут ссылаться либо отдел, либо отдел, но не оба.
Если мы хотим добавить новый тип производителя, мы добавляем одну строку в таблицу поиска ArticleProducerTypes и создаем пару новых таблиц для нового типа. Например:
INSERT INTO ArticleProducerTypes VALUES (3);
CREATE TABLE PartnerProducerType (ProducerType TINYINT UNSIGNED PRIMARY KEY);
INSERT INTO PartnerProducerType VALUES (3);
CREATE TABLE Partner (
PartnerID INT UNSIGNED NOT NULL PRIMARY KEY,
ProducerType TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (PartnerID, ProducerType)
REFERENCES ArticleProducer(ProducerID, ProducerType),
FOREIGN KEY (ProducerType)
REFERENCES PartnerProducerType(ProducerType) -- restricted to '3'
) ENGINE=InnODB;
Но у нас все еще есть возможность, что ни не содержит ссылку на данную строку в ArticleProducer; то есть мы не можем создать ограничение, которое вынуждает создать строку в одной из зависимых таблиц. У меня нет решения для этого.