SQL:在保留约束的同时规范化数据库

时间:2011-08-24 22:23:27

标签: sql sql-server-2008 database-design polymorphism entity-attribute-value

假设我有以下表格:

     ____________________             ____________________
    |     Organisms      |           |       Species      |
    |--------------------|           |--------------------|
    |OrganismId (int, PK)|           |SpeciesId (int, PK) |
    |SpeciesId (int, FK) |∞---------1|Name (varchar)      |
    |Name (varchar)      |           |____________________|
    |____________________|                      1
              1                                 |
              |                                 |
              |                                 |
              ∞                                 ∞
    ______________________        ____________________          _______________
   | OrganismPropsValues  |      |   SpeciesProps     |        |     Props     |
   |----------------------|      |--------------------|        |---------------|
   |OrganismId (int, FK)  |      |PropId (int,PK,FK)  | ∞-----1|PropId (int,PK)|
   |PropId (int, FK)      |      |SpeciesId(int,PK,FK)|        |Name (varchar) |
   |Value (varchar)       |      |____________________|        |_______________|
   |______________________|                                             1
              ∞                                                         |
              |                                                         |
              -----------------------------------------------------------

快速解释我在这里要表达的内容:假设我们有一个物种列表,例如猫,狗,人等。我们还有一组属性(缩写为Props,所以我可以更容易地适应它在图中)适用于某些但不一定适用于所有物种 - 例如,这可能是尾长(对于有尾巴的物种),眼睛颜色(对于有眼睛的人)等。

SpeciesProps是一个链接表,用于定义哪些属性适用于哪些物种 - 所以这里我们会有{人类,眼睛颜色},{狗,眼睛颜色},{猫,眼睛颜色},{狗,尾巴长度} ,{猫,尾长}。我们没有{Human,Tail Length},因为Tail Length显然不适用于人类。

有机体表包含物种的实际“实施” - 所以我们可能有{人类,鲍勃},{狗,鲁弗斯}和{猫,菲利克斯}。

现在我的问题是:在OrganismPropsValues表中,我想存储每个生物体的属性的“值” - 例如,对于Bob我想要存储{Bob,Eye Color,Blue}。对于Rufus,我想存储{Rufus,Eye Color,Brown}和{Rufus,Tail Length,20}(类似于Felix)。然而,我的问题是,在我详细描述的模式中,即使在SpeciesProps中不存在{Human,Tail Length}元组,也完全可以存储{Bob,Tail Length,10}。如何修改此模式,以便我可以强制执行OrganismPropsValues中SpeciesProps中定义的约束,同时保持足够的规范化?

4 个答案:

答案 0 :(得分:4)

您正在实施Entity-Attribute-Value反模式。这不是规范化的数据库设计,因为它不是关系型的。

我建议的是Class Table Inheritance设计模式:

  • 为生物创建一个表,其中包含所有物种共有的属性。
  • 为每个物种创建一个表,其中包含特定于该物种的属性。这些表中的每一个与Organisms都有一对一的关系,但每个属性都属于自己的列。

     ____________________             ____________________
    |     Organisms      |           |       Species      |
    |--------------------|           |--------------------|
    |OrganismId (int, PK)|           |SpeciesId (int, PK) |
    |SpeciesId (int, FK) |∞---------1|Name (varchar)      |
    |Name (varchar)      |           |____________________|
    |____________________|
              1
              |
              |
              1
     ______________________ 
    |    HumanOrganism     |
    |----------------------|
    |OrganismId (int, FK)  |
    |Sex      (enum)       |
    |Race     (int, FK)    |
    |EyeColor (int, FK)    |
    |....                  |
    |______________________|
    

这确实意味着您将创建许多表,但请将此视为权衡,以正确的方式存储属性的许多实际好处:

  • 您可以适当地使用SQL数据类型,而不是将所有内容都视为自由格式的varchar。
  • 您可以使用约束或查找表通过一组预定义的值限制某些属性。
  • 您可以强制使用属性(即NOT NULL)或使用其他约束。
  • 更有效地存储数据和索引。
  • 查询更容易编写,更容易让RDBMS执行。

有关此设计的更多信息,请参阅Martin Fowler的书籍Patterns of Enterprise Application Architecture,或我的演示文稿Practical Object-Oriented Models in SQL或我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming

答案 1 :(得分:2)

嗯...
这是一种方法:
将SpeciesPropsId添加到SpeciesProps表中 将ProtId替换为OrganismPropsValues表中的SpeciesPropsId 你需要稍微改变一下约束 需要将SpeciesProps添加到OrganismPropsValues约束 需要删除OrganismPropsValues到道具约束。

从技术上讲,您不必从OrganismPropsValues中删除PropId,但如果保留它将导致数据冗余。

答案 2 :(得分:2)

每当你有像这样的菱形依赖时,考虑更多地强调复合 PRIMARY KEYS。

具体来说,不仅仅是OrganismId,而是SpeciesIdOrganismSubId的组合(你仍然可以拥有OrganismId,而不是SpeciesId来识别生物体,关键 - 为简洁而不在此处显示。)

一旦你这样做,你的模型可以看起来像这样:

ER Model

这里要注意的关键是SpeciesName在这个菱形图的两个边下“传播”。这就是为您提供所需的限制,即不能为给定物种“声明”的属性“赋值”。

BTW,在命名表时使用单数。此外,考虑使用自然主键(例如SpeciesId而不是{{1}}作为PK) - 如果做得正确,它可以显着提高JOIN的速度(特别是与群集一起使用)。 < / p>

答案 3 :(得分:1)

实现这些约束的另一种方法是通过删除Organism并添加OrganismId来更改No表的PK。然后PK化合物(SpeciesId, No)。因此,"Bob"(Human, 1)"Rufus"(Dog, 1)等。

然后,添加OrganismPropsValues表,SpeciesIdNo(删除OrganismId。)

这样可以将FK从OrganismPropsValues更改为Props,改为参考SpeciesProps

     ____________________             ____________________
    |     Organisms      |           |       Species      |
    |--------------------|           |--------------------|
    |SpeciesId (int, FK) |           |SpeciesId (int, PK) |
    |No (int)            |∞---------1|Name (varchar)      |
    |Name (varchar)      |           |____________________|
    |PK (SpeciedId,No)   |                      1
    |____________________|                      |
              1                                 |
              |                                 |
              |                                 |
              ∞                                 ∞
    ______________________        ____________________          _______________
   | OrganismPropsValues  |      |   SpeciesProps     |        |     Props     |
   |----------------------|      |--------------------|        |---------------|
   |SpeciesId (int, PK)   |      |PropId (int,PK,FK)  | ∞-----1|PropId (int,PK)|
   |No (int, PK)          |      |SpeciesId(int,PK,FK)|        |Name (varchar) |
   |PropId (int, PK)      |      |____________________|        |_______________|
   |Value (varchar)       |                 1
   |FK (SpeciesId,No)     |                 |
   |FK (SpeciesId,PropId) |                 |
   |______________________|                 |
              ∞                             |
              |                             |
              -------------------------------