适当的数据库设计

时间:2011-10-26 15:32:57

标签: mysql sql database

我目前正在开发一个包含许多不同表的项目,这些表使用规范化来有效地最小化重复和NULL单元格值。我的问题涉及最小化正在使用的表格数量的正确方法以及我所采用的方法是否糟糕以及我可能采取的方法是否存在可预见的问题。

我很快就制定了一个与我正在做的相似的例子,但更简化了。

请帮助我了解哪种方法更好,或者有更好的方法显示?

Image http://img225.imageshack.us/img225/6972/helpdb.jpg

指向较大图片的链接http://img225.imageshack.us/img225/6972/helpdb.jpg

编辑:我要感谢大家的绝佳回答/评论。 我能够将我的初始数据结构表削减大约40%;答案也帮助我改进了我目前的数据结构,使其更加可扩展。我希望在未来2-3个月内完成这个项目。然后你可以看看你帮助我学习和建立的东西!非常感谢你,我希望在我的知识和经验增长的同时,向我现在所处的社区做出贡献。再次感谢你!

8 个答案:

答案 0 :(得分:6)

为什么甚至为不同的实体类型都有单独的表?你可以这样做:

实体:id_entity,name,entity_type(即人,狗,鱼等)

Illness_resilience:entity_id,sick_id等......

答案 1 :(得分:4)

我一直很喜欢在数据模型中显示继承的做法,我认为你对方法2有正确的想法,因为你试图以类似的方式对待类似的实体。但是,我同意这两个空值是丑陋和不正确的。以下是我对数据建模的方法:

ENTITY
entity_id PK
entity_type

PERSON
entity_id PK FK REFERENCES ENTITY(entity_id)
name

DOG
entity_id PK FK REFERENCES ENTITY(entity_id)
name

FISH
entity_id PK FK REFERENCES ENTITY(entity_id)
name

我们这里有一个“抽象”表实体和三个“具体”表(这里使用面向对象的编程术语)。具体表与ENTITY表的主键共享其主键,表示给定记录是该实体,并且由于实体患有疾病,人/狗/鱼也有这种疾病。

ENTITY上的“entity_type”列说明记录来自哪个表(可能是ENUM(“P”,“D”或“F”),或拼出整个名称)。如果您需要从ENTITY表转到具体表,这更方便,但从具体表到ENTITY时显然不需要。

这个数据模型很好地映射到使用Hibernate的Web应用程序和准确描述继承关系的对象模型。我已多次实施此类解决方案并取得了巨大成功。

修改

对于使用“entity_type”和单个表的一些先前答案的方法,这种方法的好处在于,此模型允许您附加特定于具体表的数据 - 不仅在表本身上,而且甚至是关系

例如,让我们说:

  • 所有DOG和FISH拥有PERSON所有者
  • PERSON参加比赛
  • DOGs和FISHes有一个物种

您的架构可能如下所示:

ENTITY
entity_id PK
name
entity_type

PERSON
entity_id PK FK REFERENCES ENTITY(entity_id)
race_id FK REFERENCES RACE(race_id)

DOG
entity_id PK FK REFERENCES ENTITY(entity_id)
owner_id FK REFERENCES PERSON(entity_id)
species

FISH
entity_id PK FK REFERENCES ENTITY(entity_id)
owner_id FK REFERENCES PERSON(entity_id)
species

请注意,正如Alix指出的那样,您绝对可以将name移动到超级表,因为它在所有实体中都很常见。

答案 2 :(得分:2)

使用超类型/子类型结构

你需要问的第一个问题是......实体(人,狗,鱼)是否会共享同样的疾病。如果答案是否定的,那么就没有必要在实体和疾病之间建立一对多的关系。

如果上述答案为否,那么您在实体和疾病之间存在1比1的关系。现在,您可以创建所谓的超类型/子类型数据结构。 SuperType 包含所有常用元素, SubType 包含所有唯一的元素。

在您引用的示例中,实体表将包含People,Dogs,Fish和您稍后添加的任何其他实体之间通用的所有内容。然后为每个SubType创建一个单独的表,链接回SuperType表。

[tableEntity]
EntityId,
EntityTypeId,
Name,
Weight,
LifeExpectancy,
Etc

[tablePeople]     [tableDogs]     [tableFish]
PeopleId,         DogId,          FishId,
EntityTypeId,     EntityTypeId,   EntityTypeId,
UniquePeople1,    UniqueDog1,     UniqueFish1,
UniquePeople2,    UniqueDog2,     UniqueFish2
UniquePeople3,    UniqueDog3,     UniqueFish3
Etc...            Etc...          Etc...

您可以在SuperType表和SubType表之间创建单独的连接,以获取每个关系的总信息:

Join Entity to People on EntityTypeId for EntityPeople
Join Entity to Dogs   on EntityTypeId for EntityDogs
Join Entity to Fish   on EntityTypeId for EntityFish

答案 3 :(得分:1)

你的第二种方法比第一种方法更好,但我建议稍作修改:

实体

  • id_entity
  • id_animal
  • animal_type(enum = person,dog,fish)

您也明白了这一点,请务必查看以下链接:

答案 4 :(得分:1)

您在疾病和人,狗,鱼的单独表格中复制字段和值。您必须为每个新表编写不同的insert,update和delete语句。这有很多开销。

方法二更好,但由于人,狗,鱼在不同的桌子中,它也会有很多添加的插入/更新。这些共享属性应合并到一个表中,其他属性可以在单独的表中。

然而,如果已经定义了人,狗,鱼等,那么你必须忍受

您的#2

的变化

不要将钥匙放在人,鱼等上。将钥匙放在实体上,并将外键放在人,鱼等上。它消除了你所看到的零点。认为你必须拥有一个实体才能拥有一个人,鱼等......因为这些是实体的属性所以Entity_id永远不会重复,并且在所有人,鱼,狗等中都是独一无二的...... IT消除了在人,狗等上需要PK,或者它们可以保留,而entity_ID只是成为那些表的FK

或者,如果架构尚未锁定,您可以执行此操作并在任何方向上提供灵活性。

6个表格可以扩展到任何规模的

它消除了您要求的null问题,并且在发现新的fish或person属性时不需要更改结构。例如,当新实体添加“Cats”时,结构也不会发生变化。然而,数据类型转换和更多编码会带来性能损失的代价;但是一旦完成,几乎不需要额外的维护工作(除了更改数据;但没有代码!)

缺点:

  1. 更难概念化/做
  2. SQL可能难以编写
  3. 如果在添加新属性时,如果在最初编写时未考虑现有记录,则会导致问题。
  4. 您必须编写动态控件并迭代所有属性才能在运行时真正实现数据驱动。
  5. 优点:

    • 扩展到任意数量的实体
    • 保留任意数量的狗,猫或鱼属性
    • 消除您指出的所有NULL问题
    • 非常OO esque您为实体数据创建了一组属性,而不是有一行......

    Entity_Type (人,鱼,狗等等。可以增长到任意大小的IT数据)

    • Entity_Type_ID(PK)
    • Entity_Type_name(string)

    实体 (特定的人,鱼,狗等)

    • Entity_ID(PK)
    • Entity_Type_ID(FK to Entity_Type)
    • Entity_Name(String)
    • 所有entity_Types共有的任何其他字段(现在和未来或您有空)

    Attribute_Type 任何entity_type

    的可用属性的完整列表
    • Attribute_Type_ID(PK)
    • Attribute_Name(String)
    • Attribute_Data_Type(用于演员目的)(字符串,日期,数字等)
    • isRequired(bit)

    Entity_Attribute_Types (定义特定实体可用的属性)

    • Attribute_Type_ID(FK)(将FK组合成唯一键)
    • Entity_Type_ID(FK)

    Entity_Attribute (存储为输入的属性提供的值用户)

    • Entity_ID(FK)(将FK组合成唯一键)
    • Attribute_Type_Id(FK)
    • 价值(字符串)

    Entity_Illness_Resilience

    • Entity_ID(FK)(将FK组合成唯一键)
    • Illness_ID(FK)
    • 您的其他字段

    Entity_Attribute表基本上包含 ALL 实体属性数据,除了那些对所有实体和其他相关数据(sick_resilience等)通用的实体属性数据

答案 5 :(得分:0)

设计方面,我认为方法2很糟糕,因为它不允许未来扩展设计(例如添加更多生物)而不改变现有结构。也许这对你来说是一个问题,也许不是。另外,方法1很糟糕,因为必须对每个弹性表进行弹性表的更改。

我希望每个生物类型都有一个resilience表和一个关系表,它将生物表与弹性表相关联。

也许引入creature_type表来回答有关特定生物类型的弹性的具体问题也是有益的。弹性表将引用此表。

<强>更新

您还可以与creature_idcreature_type建立一个关系表。即使你无法为creature_id设置外键,我似乎在实践中使用了这个效果。它还使设计更加灵活。

答案 6 :(得分:0)

您的结构存在的问题是实体可以启用多个实体类型。

我建议这是一种更好的方法......

人:personId,illnessId,name

鱼:fishId,sickId,name

狗:dogId,sickId,name

答案 7 :(得分:0)

我认为你有#2的正确方法,也许你的实体表可能包含一个实体类型ENUM('Person','Dog','Cat')和一个id列。虽然这对外国钥匙不太好意。

您不希望设计此内容,因此每次添加新实体时都必须创建新的弹性表。如果你有特定属性只适用于一种类型的实体,它可以存储在人,狗,猫桌上。