【问题标题】:Normalize or Denormalize: Store Contact Details (Phone Numbers) in separate Table? Search Performance?规范化或非规范化:将联系人详细信息(电话号码)存储在单独的表中?搜索性能?
【发布时间】:2009-07-28 23:30:30
【问题描述】:

我正在设计一个存储简单联系信息(名字/姓氏等)的数据库应用程序,并且我还必须存储电话号码。除了电话号码之外,我还必须存储它们的用途(移动、商业等),并可能为每个电话号码附加评论。

我的第一个方法是规范化电话号码并将其保存在单独的表格中,这样我就有了“联系人”和“电话号码”表格。 PhoneNumbers 表如下所示:

Id int PK
ContactId int FK<->Contacts.Id
PhoneNumber nvarchar(22)
Description nvarchar(100)

但是,如果我只是将此信息存储为每个联系人记录的一部分(假设我限制可以存储的电话号码总数,例如 4总数)。

但是,我最终得到了这样一个“丑陋”的结构:

PhoneNumber1 nvarchar(22)
Description1 nvarchar(100)
PhoneNumber2 nvarchar(22)
Description2 nvarchar(100)

等等。等等

这对我来说看起来很业余,但我看到了以下优点:

1) 在 ASP.NET MVC 中,我可以简单地将输入文本框附加到我的 LINQ 对象的属性中,然后完成记录添加和更新的连接。

2) 检索信息无需 SQL 连接。

不幸的是,我对表格宽度问题等问题不是很了解(我读到如果它变得太大/太多列并且出现性能问题,这可能会导致问题?)然后这也意味着当我搜索电话号码如果我将其保存在单独的表格中,我将不得不查看 4 个字段而不是 1 个字段。

我的应用程序有大约 80% 的搜索/数据检索活动,因此搜索性能是一个重要因素。

感谢您帮助我们找到正确的方法。单独的桌子还是把它放在一张桌子上?谢谢!

【问题讨论】:

  • 我在下面给出了答案,但还有一条建议是监控初始性能并阅读有关创建适当索引以适合您的 LINQ to SQL 设置的内容。监控数据库性能也是一项持续的事情,随着表的增长和可能的功能变化,最好定期检查您的数据库性能,而不是等待用户抱怨他们的体验受到影响。
  • 这是一个很好的解决方案...直到您需要 5 个电话号码而不是 4 个。标准化以进行维护。

标签: sql asp.net-mvc linq database-design


【解决方案1】:

像这样对数据进行非规范化不太可能导致问题,但我不建议这样做。尽管查询可能更复杂,但最好拥有可以通过多种方式操作的格式良好的数据。我会建议这样的数据库架构:

Contacts:
  ID (Primary Key)
  Name
  Job Title

Phone Number Categories:
  ID (Primary key)
  Name

Phone Numbers:
  ID (Primary Key)
  Category_ID (Foreign Key -> Phone Number Categories.ID)
  Contact_ID (Foreign Key -> Contacts.ID)
  Phone Number

这让您在允许的电话号码数量上有很大的灵活性,并让您能够对它们进行分类。

【讨论】:

  • 我同意@Mike,如下所述,但会更进一步,使他的答案中建议的电话号码类别表通用,以便您可以将其重新用于其他代码/描述字段应用程序中的其他位置。
  • 嗨@Mike Trpcic 我在MYSQL 中使用了一个类似的表。我正在尝试检索说 Contact ID = 1 的数据我将如何将多个电话号码非规范化为列。能否请您提供一个 Select 语句示例?
  • @Mike Trpcic 您将如何为此添加隐私设置?另一张桌子?
  • 您在寻找什么样的隐私设置?
【解决方案2】:

现在可能没问题,但是当有人想要第五个电话号码时会发生什么?您是否继续添加越来越多的字段?

要考虑的另一件事是,您将如何运行查询以说出“给我所有人及其手机号码”或“给我所有没有电话号码的人”?使用单独的表格,这很容易,但使用一张表格,手机号码可能位于四个字段中的任何一个字段中,因此变得更加复杂。

如果您采用标准化方法,并且将来您想添加有关电话号码的其他数据,您可以简单地在电话号码表中添加另一列,而不是在联系人表中添加 4 列。

回到关于将来添加更多电话号码的第一点 - 如果您确实添加了更多号码,您可能必须修改适用于与电话号码有关的数据的每个查询/逻辑/表单。

【讨论】:

  • 电话号码的数量不是问题。无论我做 4 还是 6,问题都是一样的,我可以限制在合理的范围内。您的第二条评论也正是我的想法(请参阅我的问题中关于搜索的部分) - 我必须查看 4 个字段。我想知道当我这样做时性能如何受到影响。特别是因为它们必须以某种方式被全文索引(我不能将数字限制为美国格式或类似的格式。)
  • 我不确定这是否会是一个性能问题,我会更关心执行简单数据过滤所需的查询的复杂性。
  • 所有人及其手机号码都将通过使用我在下面的回答中建议的代码方法来解决。您可以根据您打算提供的搜索优化您的索引。查看sqlblog.com/blogs/jonathan_kehayias/archive/2009/02/17/… 和此页面上的链接以获取指南。
【解决方案3】:

我赞成标准化方法。如果您决定要为企业电话号码添加“分机”列怎么办?您必须创建“Extension1”、“Extension2”、“Extension3”等列。这在某些时候可能会变得非常繁琐。

再说一次,我认为无论哪种方式你都不会出错。如果您决定改用另一种方法,则规范化/非规范化不会花费那么多时间。

【讨论】:

    【解决方案4】:

    去规范化的一个重要原则是它不会牺牲规范化的数据。您应该始终从准确描述数据的模式开始。因此,您应该将不同种类的信息放在不同种类的表格中。您还应该在您认为合理的情况下对您的数据施加尽可能多的限制。

    所有这些目标往往会使查询时间稍微长一点,因为您必须连接不同的表才能获得所需的信息,但是如果表和列的名称正确,这不应该是一个负担可读性的观点。

    更重要的是,这些目标会影响性能。您应该监控您的实际负载以查看您的数据库是否运行良好。如果几乎所有查询都快速返回,并且您有大量 CPU 空间来处理更多查询,那么您就完成了。

    如果您发现写入查询需要很长时间,请确保不要对数据进行非规范化。您将使数据库更加努力地保持一致性,因为它必须先进行多次读取,然后再进行多次写入。相反,您想查看您的索引。您对很少查询的列有索引吗?您是否有验证更新完整性所需的索引?

    如果读取查询是您的瓶颈,那么再一次,您希望从查看索引开始。您是否需要添加一个或两个索引以避免表扫描?如果您无法避免表扫描,是否可以采取任何措施来缩小每一行,例如减少 varchar 列中的字符数,或者将很少查询的列拆分到另一个表中,以便在它们是需要的。

    如果有一个特定的慢查询总是使用相同的连接,那么该查询可能会受益于非规范化。首先验证这些表上的读取数量是否远远超过写入。确定需要从一个表中添加到另一个表的列。您可能希望对这些列使用稍微不同的名称,以便更明显地看出它们来自非规范化。更改您的写入逻辑以更新连接中使用的原始表和非规范化字段。

    请务必注意,您并未删除旧表。非规范化数据的问题在于,虽然它加速了它所设计的特定查询,但它往往会使其他查询复杂化。特别是,写查询必须做更多的工作来确保数据保持一致,或者通过将数据从表复制到表,通过执行附加的子选择以确保数据有效,或者跳过其他类型的障碍。通过保留原始表,您可以保留所有旧约束,因此至少这些原始列始终有效。如果由于某种原因发现非规范化的列不同步,您可以切换回原来的较慢的查询并且一切正常,然后您可以研究重建非规范化数据的方法。

    【讨论】:

      【解决方案5】:

      我同意@Tom 的观点,即规范化更有意义并提供了灵活性。如果你的索引是正确的,你不应该因为在表之间进行连接而受苦。

      至于您的规范化表,我将添加一个类型或代码字段,以便您可以识别为 Home、Home 1、Home 2、Business、Bus 1、Bus 2、Mobile、Mob1 等...

      Id int PK
      ContactId int FK<->Contacts.Id
      Code char(5)
      PhoneNumber nvarchar(22)
      Description nvarchar(100)
      

      并将此类型与其他代码/代码描述信息一起存储在单独的表中

      我们倾向于有一个代码组表,其中包含诸如

      之类的信息
      CODE_GROUP, CODE DESC
      ST          State
      PH          Phone Number
      AD          Address Type
      

      还有一个 CODE 表

      CODE_ID, CODE_GROUP, DESCRIPTION
      MB1      PH          Mobile One
      MB2      PH          Mobile Two
      NSW      ST          New South Wales   
      etc...
      

      您可以将其扩展为长描述、短描述、排序、过滤等

      【讨论】:

        【解决方案6】:

        Contacts 表中的 XML 字段怎么样?它消除了另一个表的复杂性。

        (如果这是一个坏主意,请纠正我,我以前从未使用过 XML 字段)

        【讨论】:

        • 通常您希望任何单个列只跟踪一个属性。
        猜你喜欢
        • 2012-10-08
        • 2013-08-21
        • 2012-04-24
        • 2018-12-01
        • 2020-04-06
        • 1970-01-01
        • 2015-09-04
        • 2010-09-20
        • 1970-01-01
        相关资源
        最近更新 更多