【问题标题】:Flexibility tradeoff in database schema design数据库模式设计中的灵活性权衡
【发布时间】:2012-03-22 00:17:42
【问题描述】:

我们想在数据库中存储一个user 表。对于每个用户,都有许多属性,例如年龄、性别、出生日期等。

我们当然可以将这些常用的共享属性建模为 user 表中的列,但是,稍后当我们想要添加新属性时会变得不方便,因为我们必须修改表以添加额外的列,并且这个user 表可能很大。尤其是如果我们有不止一种类型的用户,并且每种用户类型都可能有一些独特的属性,那就更糟了。

或者,我们可以采用更灵活的设计,使用具有以下架构的单独表:

user_id | attribute_id | attribute_name | attribute_value

这样每个用户可能占据几行,每行包含特定属性的数据。这样,我们就不必担心向用户(或用户类型)添加新属性。但是,还有另一个问题:每个属性的数据类型可能彼此不同,例如有些可能是 int,有些可能是 float,有些可能是 string。作为回应,我们可以有另一个表用于将数据类型映射到属性,例如:

attribute_id | data_type

或者,我们可以简单地将所有内容存储为字符串,但不知何故,我认为就所需的空间量而言这是一个坏主意。

因此,在架构的灵活性和架构的复杂性/碎片化之间存在权衡,我想知道哪一个总体上比另一个更有利,或者,如果有第三种更好的方法这样做。

欢迎任何评论/建议!谢谢。

【问题讨论】:

  • 如果您在教科书或网络上查找有关数据库规范化的信息,您会找到很多关于这个问题的答案。没有正确的答案,因为每个解决方案都是一种权衡,正如您已经说过的那样。只有您可以回答的问题是关于添加新属性的频率。如果属性是相对静态的并且大部分都在使用中,我会采用您的第一种方法。
  • 我已经看到在几个大型数据库项目中实现了“user_id|attribute_id”方法。在每种情况下,开发人员都对其进行了很多思考,并且一开始效果很好。但在投入生产几个月后,性能开始走下坡路。这张桌子成了一个热点。

标签: database database-design relational-database database-schema


【解决方案1】:

听起来您可能正在考虑动态数据库TM。

在我看来,这是错误的做法;主要是你列举的原因。

第二个选项:

您的attribute 表必须将所有内容存储为字符串,然后如您所说将其映射回来。这不必要地使事情复杂化,并且意味着无论何时从数据库中选择或进行比较,都必须使用 attribute_data_types 表将每个值转换回其原始数据类型。或者您可能会选择另一种方法,即每种数据类型都有一个列,并根据您使用的数据类型以某种方式动态选择要更新或选择的列。

如果您确实走这条路,我建议您将数据类型添加到该表中,并通过检查约束而不是attribute_data_types 强制执行。数据类型的数量不会经常变化,您可以通过使用这种类型的约束停止向数据集中添加无效类型。

这里是previous question,关于您在使用动态数据库时可能遇到的问题。

第一个选项:

通常,我会将数据存储在同一级别,最好是在同一个表中,作为与之关联的唯一 ID。因此,如果您有一个专门与用户关联的属性,我会接受点击并将该列添加到users 表中。您没有指定您可能希望用户拥有多少属性,因此从长远来看这可能会变得很荒谬,但您无需担心很长时间。

如果您担心添加额外的列,您可以在开始时做一个可怕的 hack,添加 20 个额外的列,new_attribute1, ..., new_attribute20,然后在使用它们时重命名它们。

第三个选项:

您提到您正在考虑实施用户类型...您需要存储一些数据,无论用户、出生日期、姓名等不会消失。

虽然我不喜欢将所有内容存储在user 表中,但您可以将所有静态属性(出生日期等)保留在user 表中,然后创建第二个表@987654330 @,在 user_id 上是唯一的,用于存储不太通用的查询,您只需在不太通用的查询中加入即可。它会减小 users 表的大小。


归根结底,正如Richard A 所评论的那样,没有正确的方法可以走;你必须做适合你的事情。如果可能,请先进行一些测试,看看哪种方法最适合您自己的情况。

顺便说一句,永远不要像你提到的那样存储年龄。它每年都在变化,您必须不断更新您的表格;只需存储出生数据并在获取数据时进行计算。

【讨论】:

    【解决方案2】:

    我同意这个问题没有一个答案。

    恐怖故事时间:
    我必须实现由一位大学教授根据所谓的“指示性信息”开发的设计。这个概念基本上是一个 80/20 规则。在设计数据库表时,如果数据列将在 80% 的时间内被使用/填充到基表中。否则它进入“IndiciativeInfo”表。

    “IndicativeInfo”表变成了所有类型杂项数据的怪物大杂烩集合。什么样的恶梦。您无法内部连接此表,因此您必须对可能在此表中的 20% 数据类型中的每一个进行单独的辅助查找。

    该设计花了将近 6 个月的时间才在纸上完成。当它最终投入开发时,设计师(大学教授先生)被解雇了。

    这个故事的道德...永远不要为杂项数据制作一个包罗万象的表格。

    如果您打算拥有不同的用户类型,那么您可以尝试实现超级/子类型设计。

    这个概念很简单。一张基表说 Users 包含所有用户共有的所有数据。这是SuperType 表。每个用户组都有自己独立的SubType 表。

    我将此设计用于基于 Web 的房地产数据库系统。我创建了一个名为 Properties 的 SuperType 表和几个 SubType 表(Residential、Condo、Commercial、MultiTenant、Land) . SubType 表包含每个子类型通用的值。这让我可以查询所有 Properties,然后深入了解 SubTypes。

    【讨论】:

      【解决方案3】:

      我想提出另一个关于设计的思考点:性能。

      有几种方法可以设计满足您要求的架构。问题不仅在于编程的优缺点,还在于性能。您可以拆分表,但如果大多数屏幕或查询总是需要某个属性,那么将来会出现性能瓶颈。反之,如果您将属性放在一个表中,但这些属性只在几个屏幕或查询中需要,那么在一些更突出的屏幕或查询中就会出现性能瓶颈。

      除了编程的想法外,我曾经在数据库中有大量数据的情况下考虑性能。

      例如,如果我需要使用来自属性的条件进行查询,表示为数字数据,我想将其用作数据库中的整数类型。这样的策略可以让数据库有机会在不费力的情况下建立索引,并利用索引的优势进行查询。

      虽然很难预见未来会发生什么,但计划总比什么都不做要好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-17
        • 2021-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多