假设我有以下表格:
____________________ ____________________
| 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中定义的约束,同时保持足够的规范化?
答案 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) |
|.... |
|______________________|
这确实意味着您将创建许多表,但请将此视为权衡,以正确的方式存储属性的许多实际好处:
有关此设计的更多信息,请参阅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
,而是SpeciesId
和OrganismSubId
的组合(你仍然可以拥有OrganismId
,而不是SpeciesId
来识别生物体,关键 - 为简洁而不在此处显示。)
一旦你这样做,你的模型可以看起来像这样:
这里要注意的关键是SpeciesName
在这个菱形图的两个边下“传播”。这就是为您提供所需的限制,即不能为给定物种“声明”的属性“赋值”。
BTW,在命名表时使用单数。此外,考虑使用自然主键(例如SpeciesId
而不是{{1}}作为PK) - 如果做得正确,它可以显着提高JOIN的速度(特别是与群集一起使用)。 < / p>
答案 3 :(得分:1)
实现这些约束的另一种方法是通过删除Organism
并添加OrganismId
来更改No
表的PK。然后PK化合物(SpeciesId, No)
。因此,"Bob"
为(Human, 1)
,"Rufus"
为(Dog, 1)
等。
然后,添加OrganismPropsValues
表,SpeciesId
和No
(删除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) | |
|______________________| |
∞ |
| |
-------------------------------