【问题标题】:SQL one to one relationship vs. single tableSQL 一对一关系与单表
【发布时间】:2014-06-27 10:29:05
【问题描述】:

考虑如下的数据结构,其中用户有少量固定设置。

用户

[Id] INT IDENTITY NOT NULL,
[Name] NVARCHAR(MAX) NOT NULL,
[Email] VNARCHAR(2034) NOT NULL

用户设置

[SettingA],
[SettingB],
[SettingC]

将用户的设置移动到单独的表中,从而与用户表建立一对一的关系是否被认为是正确的?与将其与用户存储在同一行中相比,这是否提供了任何真正的优势(明显的劣势是性能)。

【问题讨论】:

  • 给定的 Id 是否总是有一个 SettingA、一个 SettingB 和一个 SettingC?或者它可以只有其中一些?当 Id 具有特定设置时,是否总是知道它是其中之一?或者它可以放在其中任何一个下面吗?你打算让它们可以为空吗? (您对“规范化”的使用是可疑的(见下文)。我不想对您对“1:1”的使用做出任何假设。)
  • 是的,所有用户的设置完全相同。
  • 所以我猜你的意思是:是的,对 or 没有,是的,对 or 没有,并且没有。那么,在哪方面,SettingA、SettingB 和 SettingC 都和 Name 和 Email 一样。
  • 与您的问题有些相关:Why use a 1-to-1 relationship in database design?.

标签: sql database-design rdbms


【解决方案1】:

当表格变得非常宽(即有很多列)时,您通常会将表格拆分为两个或多个 1:1 相关表格。程序员很难处理包含太多列的表。对于大公司来说,这样的表很容易有超过 100 列。

想象一下产品表。有一个售价,也可能是另一个仅用于计算和估算的价格。如果有两张表,一张用于实际值,一张用于规划阶段,不是很好吗?所以程序员永远不会混淆这两个价格。或者对产品进行物流设置。您想插入到 products 表中,但是其中包含所有这些物流属性,您是否需要设置其中一些?如果是两张表,你会插入到产品表中,另一个负责物流数据的程序员会关心物流表。没有更多的困惑。

多列表的另一件事是,对于具有 150 列的表,全表扫描当然比对于只有一半或更少列的表要慢。

最后一点是访问权限。使用单独的表,您可以对产品的主表和产品的后勤表授予不同的权限。

总而言之,1:1 的关系是相当罕见的,但它们可以更清晰地了解数据,甚至有助于解决性能问题和数据访问。

编辑:我正在接受 Mike Sherrill 的建议并(希望)澄清关于标准化的事情。

规范化主要是为了避免冗余和相关的缺乏一致性。是否将数据仅保存在一个表或多个 1:1 相关表中的决定与此无关。您可以决定将用户表拆分为一个表以获取个人信息,例如名字和姓氏,另一个表用于他的学校、毕业和工作。两个表都将保持与原始表一样的正常形式,因为没有比以前更多或更少冗余的数据。唯一使用两次的列是用户 ID,但这并不是多余的,因为在两个表中都需要它来标识一条记录。

所以问“将设置标准化到单独的表中是否被认为是正确的?”不是一个有效的问题,因为您不会通过将数据放入 1:1 相关的单独表中来规范化任何内容。

【讨论】:

  • 你说的大部分是对的,但我很不同意你关于让程序员更简单的观点——这通常是通过将“原始”表隐藏在某种 API 后面来完成的(例如视图和存储过程)。不得不担心如何正确连接两个表中的行,如果有的话,这会使程序员的事情变得复杂。
  • "当表格变得非常宽时,您通常会将表格拆分为两个或多个 1:1 相关表格......"也许。但是规范化并没有告诉你这样做。
  • OP 询问“将设置规范化到单独的表中是否被认为是正确的......”.
  • @Mike Sherrill 'Cat Recall':是的,但您不会通过拆分表格来规范任何内容。
  • 这就是我的观点。 OP 显然对什么规范化 以及什么规范化不是 感到困惑。如果它纠正了他的困惑,您的回答将对 OP 和一般 SO 更有帮助。
【解决方案2】:

创建具有 1-1 关系的新表不是一个合理的解决方案。有时您可能需要这样做,但通常没有理由拥有两个以用户 ID 为主键的表。

另一方面,将设置拆分到一个单独的表中,每个用户/设置组合有一行可能是一个非常好的主意。这将是一个三表解决方案。一个用于用户,一个用于所有可能的设置,一个用于它们之间的联结表。

联结表可能非常有用。例如,它可能包含设置的生效日期和结束日期。

但是,这假定设置在 SQL 意义上彼此“相似”。如果设置不同,例如:

  • 首选位置为纬度/经度
  • 一天中接收电子邮件的首选时间
  • 要从某些联系人中排除的标志

那么在将它们存储在表中时,您会遇到数据类型问题。所以,答案是“视情况而定”。很多答案取决于设置的外观、使用方式以及对它们的约束类型。

【讨论】:

    【解决方案3】:

    你们都错了 :) 开个玩笑。

    非常高负载、高容量、大量更新的系统上,按 1:1 拆分表有助于优化 I/O。

    例如,通过这种方式,您可以将大量读取的列放在单独的物理硬盘驱动器上,以加速并行读取(为此,1-1 表必须位于不同的“文件组”中) .或者您可以优化表级锁。等等等等。

    但是这种类型的优化通常不会发生,除非你有数百万行和巨大的读/写并发

    【讨论】:

      【解决方案4】:

      通常不实践将表拆分为具有 1:1 关系的不同表,因为:

      如果关系是真的 1:1,那么完整性强制归结为“在所有相关表中完成插入,或者根本不插入”。在服务器端实现这一点需要支持延迟约束检查的系统,而 AFAIK 是相当高端系统的一个特性。因此,在许多情况下,1:1 强制执行被推到应用程序端,这种方法有其明显的缺点。

      仍然建议拆分表的一种情况是存在安全方面的问题,即,当并非所有列都可以由一个用户更新时。但请注意,根据定义,在这种情况下,表之间的关系永远不会严格 1:1。

      (我还建议您仔细阅读 Thorsten/Mike 之间的讨论。您使用了“规范化”这个词,但规范化与您的场景几乎没有关系 - 除非您正在考虑 6NF,它我认为不太可能。)

      【讨论】:

        【解决方案5】:

        更有意义的是,您的设置不仅在单独的表中,而且在 ID 和设置之间使用一对多关系。这样,您就可以根据需要拥有尽可能多(或尽可能少)的设置。

        用户设置

        [Settings_ID]
        [User_ID]
        [Settings]
        

        事实上,可以为 [Email] 字段提出相同的论点。

        【讨论】:

        • 这是迈向 EAV 反模式的第一步。第一步的缺点是你失去了对 [Settings] 列/属性的类型检查。
        • 哦,我应该更清楚一点。我的意思是每一行都是一组设置,而不是单个属性。因此,您可以拥有多组相同类型的设置。请注意,它仍然确实首先取决于 OP 设置的性质。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-23
        • 1970-01-01
        • 1970-01-01
        • 2014-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多