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

时间:2015-09-30 20:47:27

标签: sql django sqlite database-design foreign-key-relationship

我正在开发一个网络应用程序,我必须设计它的数据库。有一部分对我来说并不是那么简单,所以经过一些思考和研究后,我得到了多个想法。似乎仍然不合适,所以我不确定实施哪一个以及为什么。

简化问题如下: 我有一张桌子老师。根据与其领域和主题的关系,有两种类型的教师:

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

    我最初的想法是拥有两个可以为空的外键,一个用于表Field,另一个用于表Category。但在这种情况下,我怎样才能确保一个是空的,而另一个不是?

    另一个想法是创建一个层次结构,有两种类型的教师表派生自表Teacher(is-a relation),但我找不到任何有用的教程。

    我正在使用带有SQLite数据库的Django开发应用程序

1 个答案:

答案 0 :(得分:1)

好的,你的评论让它更加清晰:

如果教师只属于一个类别,您应该直接将其保存在教师的表格中:

其次,每个教师属于"一个或零"领域。如果这是肯定的,你应该使用可空的FieldID列。这已设置或保持为空。

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

备注:这与我最后一个答案的映射表几乎相同。唯一的区别是,您对"完全一个"有严格的限制。或者"完全没有或一个" ...根据我的经验,我仍然更喜欢开放的方法。使用包括TeacherID列的唯一索引可以轻松实施规则。迟早你可能不得不重新构建这个...

当你继续时,一个类别与"零或更多"相关。领域。有两种方法:

将CategoryID列添加到Field-table(NOT NULL FK)。这样,您可以使用不同的CategoryID(组合唯一索引!)多次定义字段。您可以通过向Field-table询问具有给定CategoryID的所有字段来获取类别的字段列表。

在我眼里更好的是一个映射表CategoryField。如果您强制执行唯一的FieldID,您将确保无法将字段映射两次。并在CategoryID和FieldID的组合上添加唯一索引...

SELECT可能是这样的(SQL Server语法,未经测试):

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, ...

你说,在所有情况下,这些字段都绑定到一个类别。如果在所有情况下都是相同的类别,则应在“字段 - 表”中将类别设置为FK列。如果有一点机会,那么一个领域的类别可能因环境而异,你不应该...

与教师相同:如果教师被绑定到一个单独的类别,则在教师表中设置一个FK列,否则不要。

最灵活的是,您将至少拥有一个映射表:

(SQL Server语法)

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

将映射表FieldCategory和此表通过外键映射到上面的映射表可能是一个更好的概念。这样做可以避免无效的字段类别组合。

希望这会有所帮助......