您的同事实施了一个名为多态关联的设计。也就是说,“外键”指的是两个不同的父表之一。大多数人添加另一列parent_type 或类似的东西,以便您可以知道给定行引用哪个父表。在您的同事的情况下,他反而细分了 id 的范围。这是一个脆弱的设计,因为您无法在数据库级别强制执行它。如果您插入的部门编号 > 100,您将无法知道您的文章是否适用于部门或部门。
鉴于您开发了一个类似于Single Table Inheritance 的设计,您将多个相关类型存储在一个表中,因此主键可以确保保持唯一,并且文章可以引用任何相关类型的任何实例.
这是另一种选择:
想想面向对象的设计。如果你想让两个不同的类有文章,你可以为这两个类创建一个公共超类或一个公共接口。你可以在 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 制作。每个部门或部门都是 ArticleProducer。
另见Why can you not have a foreign key in a polymorphic association?
有关多态关联的更多信息,请参阅我的演示文稿Practical Object-Oriented Models in SQL,或我的书,SQL Antipatterns: Avoiding the Pitfalls of Database Programming。
来自 Erwin Smout 的回复:
你是对的,试图从所有子类型表中强制执行不超过 一个 行有点棘手。不幸的是,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 中的每个给定行都可以被 Department 或 Division 引用,但不能同时被两者引用。
如果我们想添加一个新的生产者类型,我们在 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 中给定行的引用;即我们不能做出强制在其中一个依赖表中创建行的约束。我对此没有解决方案。