多种实体类型的最佳数据库设计

Best database design for multiple entity types

我正在开发一个网络应用程序,我必须设计它的数据库。有一部分对我来说不是很直接,所以经过一些思考和研究,我有了多个想法。两者似乎都不完全符合table,所以我不确定要实施哪一个以及为什么。

简化后的问题如下所示: 我有一位 table 老师。有2种类型的教师,根据他们的领域和学科的关系:

  1. 与领域相关的教师,该领域必须与类别相关
  2. 与领域无关但直接与类别相关的教师

我最初的想法是有两个可为空的外键,一个指向 table 字段,另一个指向 table 类别。但是在这种情况下,如何才能确保恰好一个为空,另一个不为空呢?

另一个想法是创建一个层次结构,有两种类型的 Teacher table 派生自 table Teacher(是关系),但我找不到任何有用的教程关于这个。

我正在使用 Django 和 SQLite 数据库开发应用程序

好的,你的评论更清楚了:

如果一位教师恰好属于一个类别,您应该将其直接保存在教师的 table 中:

其次每个老师都属于"one or zero"个领域。如果这永远是确定的,您应该使用可为空的 FieldID 列。这是已设置或保持为空。

Category (CategoryID, Name, ...)
Field (FieldID,Name,...)
Teacher (TeacherID,FieldID [NULL FK],CategoryID [NOT NULL FK], FirstName, Lastname, ...)

备注:这和我上个回答的映射table几乎一样。唯一的区别是,您的 "exactly one" 或 "exactly none or one" 将受到严格限制...根据我的经验,我仍然更喜欢开放式方法。使用包括 TeacherID 列在内的唯一索引可以很容易地执行您的规则。迟早你可能不得不重新构建这个...

随着您的继续,一个类别与 "zero or more" 字段相关。有两种方法:

将 CategoryID 列添加到 Field-table(非空 FK)。通过这种方式,您可以多次使用不同的 CategoryID(组合唯一索引!)定义一个字段。只需向 Field-table 询问具有给定 CategoryID 的所有字段即可获得类别的字段列表。

在我看来更好的是映射 table CategoryField。如果您强制执行唯一的 FieldID,您可以肯定,没有字段被映射两次。并在CategoryID和FieldID的组合上添加唯一索引...

A SELECT 可能是这样的(SQL 服务器语法,未经测试):

SELECT Teacher.TeacherID
      ,Teacher.FieldID --might be NULL
      ,Teacher.CategoryID --never NULL
      ,Teacher.[... Other columns ...]
      ,Field.Name --might be NULL

      --The following columns you pick from the right source, 
      --depending on the return value of the LEFT JOIN to Field and the related "catField"
      --the directly joined "Category" (which is never NULL) is the "default"
      ,ISNULL(catField.CategoryID,Category.CategoryID) AS ResolvedCategoryID
      ,ISNULL(catField.Name,Category.Name) AS ResolvedCategoryName
      ,[... Other columns ...]

FROM Teacher
    INNER JOIN Category ON Teacher.CategoryID=Category.CategoryID --never NULL
    LEFT JOIN Field ON Teacher.FieldID=Field.FieldID --might be NULL
        LEFT JOIN Category AS catField ON Field.CategoryID=catField.CategoryID

这是编辑前的答案: 即使这个概念对我来说不是很清楚,我也会尽力帮助你

Teacher-Table: TeacherID, person's data (name, address...), ...
Category-Table: CategoryID, category title, ... 
Field-Tabls: FieldID, field title, ...

你说,字段在所有情况下都绑定到一个类别。如果这在所有情况下都是相同的类别,则应将类别设置为 Field-Table 中的 FK 列。如果有丝毫的机会,字段的类别可能与上下文不同,您不应该...

与教师相同:如果教师曾经绑定到一个类别,请在教师-table 中设置一个 FK 列,否则不要。

至少有一个映射是最灵活的 table:

(SQL 服务器语法)

CREATE TABLE TeacherFieldCategory
(
--A primary key to identify this row. This is not needed actually, but it will serve as clustered key index as a lookup index...
 TeacherFieldCategoryID INT IDENTITY NOT NULL CONSTRAINT PK_TeacherFieldCategory PRIMARY KEY
--Must be set
,TeacherID INT NOT NULL CONSTRAINT FK_TeacherFieldCategory_TeacherID FOREIGN KEY REFERENCES Teacher(TeacherID)
--Field may be left NULL
,FieldID INT NULL CONSTRAINT FK_TeacherFieldCategory_FieldID FOREIGN KEY REFERENCES Field(FieldID)
--Must be set. This makes sure, that a teacher ever has a category and - if the field is set - the field will have a category
,CategoryID INT NOT NULL CONSTRAINT FK_TeacherFieldCategory_CategoryID FOREING KEY REFERENCES Category(CategoryID)
);
--This unique index will ensure, that each combination will exist only once.
CREATE UNIQUE INDEX IX_TeacherFieldCategory_UniqueCombination ON TeacherFieldCategory(TeacherID,FieldID,CategoryID); 

有一个映射 table FieldCategory 并且这个 table 通过外键映射到上面的映射 table 可能是一个更好的概念。这样做可以避免无效的字段-类别组合。

希望这对您有所帮助...