相同业务规则的多个外键

时间:2018-07-07 17:56:28

标签: sql-server database-design foreign-keys relational-database entity-relationship

让我们跳到一个示例,该示例说明一个表引用多个表:

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

上面的示例说明了学习型App中针对其他对象(问题,答案和答案)的文件真实性,所有对象的业务规则均相同,如下所示:

  • 文件必须附加到单个对象,并且只能附加到单个对象
  • 对象可以不附加任何文件,也可以附加许多文件 使其成为1-Many关系,并如上所述进行了精化。

我的问题:

1。 当业务规则为1时,这会使文件的其他“外键”列过时,例如,如果文件附加到一个问题(如屏幕截图),则仅附加到该问题,而不附加到答案和科西斯 每次出现实际上仅使用一个外键。 必须有更好的方法来对这种情况进行建模。 还有另一种方法可以实现更好的设计吗?

2。 当添加基于同一业务规则的多个1-Many关系并且子表必须依赖于父表中的行(文件必须附加到对象)时,我无法添加“ NOT NULL”约束来强制执行此操作规则,因为我不知道我的文件将附加到哪个对象。 如何实现?

2 个答案:

答案 0 :(得分:1)

这里您有一个没有这些问题的替代设计:

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

答案 1 :(得分:0)

这个问题可能有多个答案,但是我在#4以下的答案是对我的观点中这种多态关联的更好解决方案。

首先免除其他可能的选择:

  1. 基于基数的设计: 由于关系是对象(粗略,问题或答案)与文件之间的1对多关系,因此这意味着文件将托管引用对象表PK的FK。 此设计存在以下问题:

    • 每次出现文件仅使用一个FK,其余的已过时。
    • NOT NULL 约束不能使用,必须用 CHECK 约束替换,以检查是否至少填充了一个FK。
  2. 基本表设计: 创建一个具有ID列的基本抽象对象表,并在所有引用抽象对象表ID的对象表(Corse,Question和Answer)中添加FK,最后引用文件表中抽象对象表的ID。 此设计存在以下问题:

    • 创建对象时,它代表了一个科西斯,一个问题或一个答案(仅一个对象和一个对象),但是使用这种设计,我可以创建一个假设为问题的对象,并使用相同的对象代表科西嘉必须使用具有功能的 Triggers CHECK 约束来避免这种情况。
  3. 基于对象类型的设计: 创建一个ID列为PK的对象类型表,并在File表中引用它,然后在不带FK的文件表中创建一个Object_ID列,最后在ObjectType_ID和Object_ID上添加一个 UNIQUE 约束列。 此设计存在以下问题:

    • 文件可以附加到(甚至不存在)(问题,问题或答案)。必须使用具有功能的 Triggers CHECK 约束来避免这种情况。
  4. 基于许多设计: 即使该关系是一个对象(Corse,Question或Answer)与一个文件之间的1-Many关系,也等效于对关系表PK进行修改的Many-Many关系。 首先,我在每个对象的文件和对象之间创建一个关系表,然后将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)
)
相关问题