【问题标题】:Onetomany with parent database designOnetomany 与父数据库设计
【发布时间】:2013-02-01 18:33:50
【问题描述】:

以下是代表我的问题的数据库设计(这不是我的实际数据库设计)。对于每个城市,我需要知道有哪些餐厅、酒吧和酒店可用。我认为这两种设计不言自明,但是:

第一个设计:在城市与餐厅、酒吧和酒店之间建立一对多的关系。

第二种设计:只在城市和地点之间建立一对多的关系。

哪种设计是最佳实践?第二种设计的关系较少,但我能否获得一个城市的所有餐厅、酒吧和酒店并有自己的数据(property_x/y/z)?

更新:这个问题出了问题,可能是我没有说清楚。

  • 餐厅/酒吧/酒店类是“地方”的子类(在这两个 设计)。
  • 餐厅/酒吧/酒店类必须有父“地方”
  • 餐厅/酒吧/酒店类有自己的特定数据 (property_X/Y/X)

【问题讨论】:

  • 那是 MySQL Workbench 吗?
  • 关于赏金:我决定把它给安德鲁,因为它似乎是最完整的答案。其他答案也很有帮助,谢谢。

标签: database entity-relationship one-to-many


【解决方案1】:

好的设计优先

您的数据以及 SQL 和 ERD 的可读性/可理解性是最重要的考虑因素。为便于阅读:

  • city_id 放入place。原因:地点在城市中。酒店并不是一个碰巧因为是酒店而在城市中的地方。

其他需要考虑的设计点是这种结构将来如何扩展。让我们比较一下添加一个新的子类型:

  • 在设计一中,您需要添加一个新表、与“地点”的关系以及与city 的关系
  • 在设计二中,您只需向“地点”添加一个新表和关系。

我会再次选择第二种设计。

性能第二

现在,我猜,但将city_id 放在子类型中的原因可能是您预计它在某些特定用例中更有效或更快,这可能是忽略可读性/可理解性的一个很好的理由.但是,在您测量将要部署的实际硬件上的性能之前,您并不知道:

  • 哪种设计更快
  • 性能差异是否会真正降低整个系统的性能
  • 其他优化方法(调整 SQL 或数据库参数)实际上是否是更好的处理方法。

我认为设计一是尝试在 ERD 上对数据库进行物理建模,这是一种不好的做法。

过早的优化是软件工程中许多邪恶的根源。

子类型方法

在 ERD 上实现子类型有两种解决方案:

  1. 一个通用属性表,每个子类型一个表,(这是您的第二个模型)
  2. 一个表,其中包含用于子类型属性的附加列。

在单表方法中,您将:

  • 子类型列TYPE INT NOT NULL。这指定该行是餐厅、酒吧还是酒店
  • place 上的额外列 property_Xproperty_Yproperty_Z

以下是利弊的快速表:

单表方法的缺点:

  • 在单表方法中,扩展列(X、Y、Z)不能为 NOT NULL。您可以实现行级约束,但您失去了简单 NOT NULL 的简单性和可见性
  • 单个表非常宽且稀疏,尤其是当您添加其他子类型时。你可能会达到最大值。某些数据库上的列数。这会使这种设计非常浪费。
  • 要查询特定子类型的列表,您必须使用WHERE TYPE = ? 子句进行过滤,而每个子类型的表是更自然的“FROM HOTEL INNER JOIN PLACE ON HOTEL.PLACE_ID = PLACE.ID”
  • 恕我直言,映射到面向对象语言中的类更加困难且不那么明显。考虑避免这个 DB 是否将被 Hibernate、Entity Beans 或类似的映射

单表方法的优点:

  • 通过合并到一个表中,没有连接,因此查询和 CRUD 操作更加高效(但是这种微小的差异会导致问题吗?)
  • 不同类型的查询是参数化的 (WHERE TYPE = ?),因此在代码中而不是在 SQL 本身中更可控 (FROM PLACE INNER JOIN HOTEL ON PLACE.ID = HOTEL.PLACE_ID)。

没有最好的设计,您必须根据您最常执行的 SQL 和 CRUD 操作的类型以及可能的性能来选择(但请参阅上面的一般警告)。

建议

在所有条件相同的情况下,我建议默认选项是您的第二个设计。但是,如果您有我上面列出的那些最重要的问题,请选择其他实现。但不要过早优化。

【讨论】:

  • 是的。我也会选择第二种设计。另外,我建议将您的数据库带入第 3 范式(数据库规范化)。那么事情就不会出错了,看这里:en.wikipedia.org/wiki/Database_normalization#Normal_forms
  • 我使用 JPA 创建映射。我对第二种设计的担忧是“城市”与“地点”有“一对多”的映射关系。如果我想要一个城市的所有酒店,我需要将(一些)地方投射到酒店。或者我需要创建一个(JPQL)查询来查找该城市的所有酒店。在第一个设计中,“城市”直接映射到“酒店”。
  • @BigJ:看看stackoverflow.com/questions/4265454/…@Inheritance(strategy = InheritanceType.JOINED) 可能有助于解决您的 JPA 问题。
  • 是的,城市中的一对多地点映射使用了多态性。但是要从该映射中获取所有酒店,我仍然需要检查类型(instanceof Hotel)并进行转换。例如调用hotel.getProperty_Z()。
  • @BigJ:不,比这要好得多。您在 Hotel 上有 '@Entity',因此您可以运行 JPA 查询 Query q = em.createQuery("SELECT h FROM Hotel h WHERE h.city.name = 'London'");列表 londonHotels = q.getResultList();我还缺少什么吗?
【解决方案2】:

两者都没有。

如果我需要选择一个,我会保留第二个,因为之后需要创建的外键和索引的数量。
但是,更好的方法是:创建一个包含各种地点(酒吧、餐馆等)的表格,并为每一行分配一个具有该地点类型值的列(应用 COMPRESS 子句与列中预期的类型)。它将提高结构的性能和可读性,并且更易于维护。

希望这会有所帮助。 :-)

【讨论】:

  • 我认为您的解决方案没有考虑到每种类型的地方都有自己的数据(property_x/y/z)。
【解决方案3】:

您不会在任何子表中显示备用列。我认为您不应该将类型数据拆分为“酒吧”、“餐厅”等表名 - 这些应该是位置表中的类型。

我进一步认为您应该有一个地址表 - 其中一列是城市。然后每个地方都有一个地址,您可以在需要时轻松按城市分组。 (或州或邮政编码或国家等)

【讨论】:

  • 我认为他在每种类型的地方都有不同类型的属性,如 property_x int 和 property_y varchar 等
  • 糟糕,我的错。该评论是针对 Jose F. 抱歉我的错误
  • 其实spathirana,你是完全正确的。 Randy 和 Jose F 都忽略了表中的 property_x/y/z 列。
【解决方案4】:

我认为最好的选择是第二个。在第一个设计中,可能会出现数据错误,因为一个地方可以分配给一个城市(例如 A)的特定餐厅(或任何其他类型),同时可以分配给不同城市的另一家餐厅(例如 B)。在第二种设计中,一个地方总是与一个特定的城市绑定。

【讨论】:

  • 一个地方没有分配给一个餐厅:一个餐厅是一个地方的子类。而且,每家餐厅都与 1 个城市相连。
  • 在这种情况下,最好删除 place 表并将 name 和 phone 列添加到这三个子类中的每一个中。这里的连接是开销。
  • 您能解释一下为什么您坚持认为places 表是必须的吗?因为据我了解,您在 db 设计中不一定需要父类和子类关系,就像在 java 类中那样。如果给定地点仅与一家餐厅/酒吧/酒店相关联,并且对于餐厅/酒吧/酒店来说 place_id 是强制性的,那么您不应该有单独的地点表。也许我在这里错过了一个重要的点
  • 一个地方不仅仅与一个餐厅相关联:一个餐厅就是一个地方。删除“地点”将违背数据库规范化:“餐厅/酒吧/酒店”表中的重复数据。另外,我可以在“place”中添加一个名为“rank”的列,它给出了一个地方的排名。
【解决方案5】:

第一:
两种设计都可以为您提供所有适当的数据。

第二:
如果所有扩展类都将实现该位置(这对您的实现来说听起来很明显),那么将它作为父对象的一部分包含在内将是一个更好的做法。这将建议选项 2。

Thingy:
问题是即使很难找到每个特定 PLACE 的类型,更容易知道类型(孩子)总是一个地方(父母)。您可以在可视化选项 2 的结果集时想到这一点。考虑到这一点,我推荐第一种方法。

注意:
第一个没有更多的关系,它只是拆分它们。

【讨论】:

    【解决方案6】:

    如果酒吧、餐厅和酒店具有不同的属性集,则它们是不同的实体,应由 3 个不同的表表示。但是为什么你需要地方表呢?我的建议是放弃它并为您的 3 个实体提供 3 个表,仅此而已。

    在代码中,将公共属性收集到父类中比在每个子类中重复它们更有组织和效率 - 当然。但是正如上面的spathirana cmets,数据库设计不像OOP。当然,您可以通过将地点的共同属性粘贴到“地点”表中来节省列名的重复。但它也会增加复杂性: - 每当您想提及酒吧、餐厅或酒店时,您都必须加入该表 - 每当您想添加新的酒吧、餐厅或酒店时,您都必须插入两张桌子 - 你必须更新两个表...等等。

    拥有 3 个没有 place 表的表也可能是性能最优化的设计。但这不是我的来历。我正在考虑干净、简单的数据库设计,其中单个实体意味着单个表中的单个行。关系数据库中没有“is-a”关系。外键关系是“has-a”。好的,我敢肯定有例外,但你的情况也不例外。

    【讨论】:

    • 删除“地方”会违反数据库规范化:“餐厅/酒吧/酒店”表中的重复数据。如果您使用 JPA,则插入/编辑 2 个表(父表和子表)无需额外工作。它确实会影响性能。
    • 它会重复列名,但不会重复数据本身。规范化是关于组织一对多的关系,与继承无关。与我建议的设计相比,您的位置表设计没有更多或更少标准化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-24
    • 1970-01-01
    相关资源
    最近更新 更多