同一业务规则的多个外键
Multiple Foreign Keys for the same business rule
让我们跳到一个例子,说明一个 table 引用多个 tables :
CREATE TABLE Corses
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Questions
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Answers
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Files
(
ID INT PRIMARY KEY,
Corse_ID INT,
Question_ID INT,
Answer_ID INT,
FOREIGN KEY (Corse_ID) REFERENCES Corses(ID),
FOREIGN KEY (Question_ID) REFERENCES Questions(ID),
FOREIGN KEY (Answer_ID) REFERENCES Answers(ID)
)
上面的例子说明了一个学习应用程序中的文件与其他对象(课程,问题和答案)的关系,业务规则对所有对象都是相同的,如下所示:
- 文件必须附加到单个对象,并且只能附加到单个对象
- 一个对象可以附加 none 个或多个文件
这使它成为一对多关系并如上所示。
我的问题:
1。
当业务规则为 1-Many 时,这会使文件出现的其他 Forign Key 列过时,例如,如果文件附加到问题(如屏幕截图),则它仅附加到该问题,而不是答案而不是科西斯。
每次出现实际上只使用一个外键。
必须有更好的方法来模拟这种情况。
还有其他方法可以实现更好的设计吗?
2。
当添加多个基于相同业务规则的一对多关系时,子 table 必须依赖于父 table 中的一行(文件必须附加到对象)我无法添加"NOT NULL" 限制执行此规则,因为我不知道我的文件将附加到哪个对象。
如何实现?
这里有一个没有这些问题的替代设计:
CREATE TABLE Objects
(
Id int PRIMARY KEY
);
CREATE TABLE Courses
(
CourseId int PRIMARY KEY,
CONSTRAINT FK_Courses_Objects FOREIGN KEY (CourseId) REFERENCES Objects(Id)
)
CREATE TABLE Questions
(
QuestionId int PRIMARY KEY,
CONSTRAINT FK_Questions_Objects FOREIGN KEY (QuestionId) REFERENCES Objects(Id)
)
CREATE TABLE Answers
(
AnswerId int PRIMARY KEY,
CONSTRAINT FK_Answers_Objects FOREIGN KEY (AnswerId) REFERENCES Objects(Id)
)
CREATE TABLE Files
(
FileId int PRIMARY KEY,
ObjectId int NOT NULL CONSTRAINT FK_Files_Objects REFERENCES Objects(Id)
)
但是你可以解决第二个问题保持原来的设计:
CREATE TABLE Files
(
FileId int PRIMARY KEY,
CourseId int REFERENCES Courses(CourseId),
QuestionId int REFERENCES Questions(QuestionId),
AnswerId int REFERENCES Answers(AnswerId),
CONSTRAINT CHK_JustOneObjectReferenced CHECK (
CourseId IS NOT NULL AND QuestionId IS NULL AND AnswerId IS NULL
OR CourseId IS NULL AND QuestionId IS NOT NULL AND AnswerId IS NULL
OR CourseId IS NULL AND QuestionId IS NULL AND AnswerId IS NOT NULL
)
)
这个问题可能有多个答案,但我在 #4 下面的答案是我认为对这种多态关联的更好解决方案。
首先以免通过其他可能的选项:
基于基数的设计:
由于对象(Corse、问题或答案)和文件之间的关系是一对多,这意味着文件将托管引用对象 table 的 PK 的 FK。
此设计存在以下问题:
- 每个文件出现仅使用一个 FK,其余已过时。
不能使用 - NOT NULL 约束,必须用 CHECK 约束来检查是否至少填充了一个 FK。
基本 table 设计:
创建一个带有 ID 列的基本抽象对象 table,并在引用抽象对象 table 和最终引用的所有对象 tables(Corse、问题和答案)中添加 FK文件 table 中抽象对象 table 的 ID。
此设计存在以下问题:
- 当一个对象被创建时,它意味着代表一个科西斯、一个问题或一个答案(一个对象和一个对象)但是使用这个设计我可以创建一个假设是一个问题的对象并使用它相同的对象来表示科西嘉。 触发器 或 CHECK 必须使用函数约束来避免这种情况。
基于对象类型的设计:
创建一个对象类型 table,ID 列作为 PK,并在文件 table 中引用它,然后在文件 table 中创建一个没有 FK 的 Object_ID 列,最后添加UNIQUE 对 ObjectType_ID 和 Object_ID 列的约束。
此设计存在以下问题:
- 文件可以附加到甚至不存在的(Corses、问题或答案)。 触发器 或 CHECK 必须使用函数约束来避免这种情况。
基于多对多的设计:
尽管对象(Corse、Question 或 Answer)和文件之间的关系是一对多关系,但它等同于修改关系 table 的 PK 的多对多关系。
首先,我在每个对象的文件和对象之间创建关系 table,然后我使用 File_ID 列作为 PK。
这是 File-Corse 关系的 DDL,对于问题和答案也是一样的:
CREATE TABLE Files
(
ID INT PRIMARY KEY,
.....
)
CREATE TABLE Corses
(
ID INT PRIMARY KEY,
.....
)
CREATE TABLE Files_Corses
(
File_ID INT PRIMARY KEY,
Corse_ID INT NOT NULL,
FOREIGN KEY (File_ID) REFERENCES Files(ID),
FOREIGN KEY (Corse_ID) REFERENCES Corses(ID)
)
让我们跳到一个例子,说明一个 table 引用多个 tables :
CREATE TABLE Corses
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Questions
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Answers
(
ID int PRIMARY KEY,
.....
)
CREATE TABLE Files
(
ID INT PRIMARY KEY,
Corse_ID INT,
Question_ID INT,
Answer_ID INT,
FOREIGN KEY (Corse_ID) REFERENCES Corses(ID),
FOREIGN KEY (Question_ID) REFERENCES Questions(ID),
FOREIGN KEY (Answer_ID) REFERENCES Answers(ID)
)
上面的例子说明了一个学习应用程序中的文件与其他对象(课程,问题和答案)的关系,业务规则对所有对象都是相同的,如下所示:
- 文件必须附加到单个对象,并且只能附加到单个对象
- 一个对象可以附加 none 个或多个文件 这使它成为一对多关系并如上所示。
我的问题:
1。 当业务规则为 1-Many 时,这会使文件出现的其他 Forign Key 列过时,例如,如果文件附加到问题(如屏幕截图),则它仅附加到该问题,而不是答案而不是科西斯。 每次出现实际上只使用一个外键。 必须有更好的方法来模拟这种情况。 还有其他方法可以实现更好的设计吗?
2。 当添加多个基于相同业务规则的一对多关系时,子 table 必须依赖于父 table 中的一行(文件必须附加到对象)我无法添加"NOT NULL" 限制执行此规则,因为我不知道我的文件将附加到哪个对象。 如何实现?
这里有一个没有这些问题的替代设计:
CREATE TABLE Objects
(
Id int PRIMARY KEY
);
CREATE TABLE Courses
(
CourseId int PRIMARY KEY,
CONSTRAINT FK_Courses_Objects FOREIGN KEY (CourseId) REFERENCES Objects(Id)
)
CREATE TABLE Questions
(
QuestionId int PRIMARY KEY,
CONSTRAINT FK_Questions_Objects FOREIGN KEY (QuestionId) REFERENCES Objects(Id)
)
CREATE TABLE Answers
(
AnswerId int PRIMARY KEY,
CONSTRAINT FK_Answers_Objects FOREIGN KEY (AnswerId) REFERENCES Objects(Id)
)
CREATE TABLE Files
(
FileId int PRIMARY KEY,
ObjectId int NOT NULL CONSTRAINT FK_Files_Objects REFERENCES Objects(Id)
)
但是你可以解决第二个问题保持原来的设计:
CREATE TABLE Files
(
FileId int PRIMARY KEY,
CourseId int REFERENCES Courses(CourseId),
QuestionId int REFERENCES Questions(QuestionId),
AnswerId int REFERENCES Answers(AnswerId),
CONSTRAINT CHK_JustOneObjectReferenced CHECK (
CourseId IS NOT NULL AND QuestionId IS NULL AND AnswerId IS NULL
OR CourseId IS NULL AND QuestionId IS NOT NULL AND AnswerId IS NULL
OR CourseId IS NULL AND QuestionId IS NULL AND AnswerId IS NOT NULL
)
)
这个问题可能有多个答案,但我在 #4 下面的答案是我认为对这种多态关联的更好解决方案。
首先以免通过其他可能的选项:
基于基数的设计: 由于对象(Corse、问题或答案)和文件之间的关系是一对多,这意味着文件将托管引用对象 table 的 PK 的 FK。 此设计存在以下问题:
- 每个文件出现仅使用一个 FK,其余已过时。 不能使用
- NOT NULL 约束,必须用 CHECK 约束来检查是否至少填充了一个 FK。
基本 table 设计: 创建一个带有 ID 列的基本抽象对象 table,并在引用抽象对象 table 和最终引用的所有对象 tables(Corse、问题和答案)中添加 FK文件 table 中抽象对象 table 的 ID。 此设计存在以下问题:
- 当一个对象被创建时,它意味着代表一个科西斯、一个问题或一个答案(一个对象和一个对象)但是使用这个设计我可以创建一个假设是一个问题的对象并使用它相同的对象来表示科西嘉。 触发器 或 CHECK 必须使用函数约束来避免这种情况。
基于对象类型的设计: 创建一个对象类型 table,ID 列作为 PK,并在文件 table 中引用它,然后在文件 table 中创建一个没有 FK 的 Object_ID 列,最后添加UNIQUE 对 ObjectType_ID 和 Object_ID 列的约束。 此设计存在以下问题:
- 文件可以附加到甚至不存在的(Corses、问题或答案)。 触发器 或 CHECK 必须使用函数约束来避免这种情况。
基于多对多的设计: 尽管对象(Corse、Question 或 Answer)和文件之间的关系是一对多关系,但它等同于修改关系 table 的 PK 的多对多关系。 首先,我在每个对象的文件和对象之间创建关系 table,然后我使用 File_ID 列作为 PK。 这是 File-Corse 关系的 DDL,对于问题和答案也是一样的:
CREATE TABLE Files
(
ID INT PRIMARY KEY,
.....
)
CREATE TABLE Corses
(
ID INT PRIMARY KEY,
.....
)
CREATE TABLE Files_Corses
(
File_ID INT PRIMARY KEY,
Corse_ID INT NOT NULL,
FOREIGN KEY (File_ID) REFERENCES Files(ID),
FOREIGN KEY (Corse_ID) REFERENCES Corses(ID)
)