【问题标题】:unnecessary normalization不必要的标准化
【发布时间】:2011-04-10 19:00:59
【问题描述】:

我和我的朋友正在建立一个网站,但存在重大分歧。该网站的核心是一个关于“人”的 cmets 数据库。基本上人们可以输入评论,他们可以输入评论所涉及的人。然后查看者可以在数据库中搜索评论中的单词或人名的一部分。它完全是用户生成的。例如,如果有人想对拼写错误的人名发表评论,他们可以,这没关系。所以可能会有不同人的多个拼写被列为几个不同的条目(一些带有中间名,一些带有昵称,一些拼写错误等),但这一切都可以。我们不在乎人们是否对随机的人或虚构的人进行 cmet。

无论如何,问题在于我们如何构建数据库。现在它只是一个以评论 ID 作为主键的表,然后有一个字段用于评论的“人”:

评论 ID - 评论 - 人

1 - “他很奇怪” - 约翰·史密斯

2 - “臭女孩” - 珍妮

3 - “同性恋” - 约翰·史密斯

4 - “欠我 20 美元” - Jennyyyyyyyyy

一切正常。使用数据库,我能够创建列出特定“人”的所有“cmets”的页面。然而,他对数据库没有规范化很着迷。我阅读了规范化并得知他错了。该表目前已规范化,因为评论 ID 是唯一的,并且规定了“评论”和“人”。现在他坚持认为“人”应该拥有自己的表,因为它是“事物”。我认为没有必要,因为即使“人”确实是更大的容器(一个“人”可以有很多关于他们的“cmets”),数据库似乎运行得很好,“人”是评论 ID。我对不同的 SQL 选择使用各种 PHP 调用,以使其在输出和用户搜索和查看结果的不同方式上神奇地显得更加复杂,但实际上,设置非常简单。我现在让用户用竖起大拇指和大拇指向下的方式对 cme​​ts 进行排名,并且我在同一张桌子上保留一个“分数”作为另一个字段。

我觉得目前没有必要为唯一的“人”条目单独设置一个表,因为“人”没有自己的“分数”或任何自己的属性。只有 cmets 可以。我的朋友如此坚持,以至于它是提高效率的必要条件。最后我说,“好吧,如果你要我创建一个单独的表,让'person'作为它自己的字段,那么第二个字段会是什么?因为如果一个表只有一个列,这似乎没有意义。我同意我们以后可能会需要给“人”它自己的桌子,但我们可以在那时处理。”然后他说字符串不能是主键,我们将当前表中的“人”转换为数字,数字将成为新“人”表中的主键。对我来说,这似乎没有必要,它会使当前表格更难阅读。他还认为以后创建第二个表是不可能的,我们现在需要预见到以后可能会需要它。

谁是对的?

【问题讨论】:

    标签: mysql sql database database-design database-normalization


    【解决方案1】:

    我认为你的朋友是对的。

    人应该住在不同的桌子上,你应该尝试规范化。不过,不要过度。

    从长远来看,您可能希望对您的网站做更多的事情,比如您想将多个文件附加到一个人(即图片)上,然后您会非常感谢标准化。

    【讨论】:

    • 我同意弗兰基/你朋友的观点。以后再做这种改变,虽然不是不可能,但很尴尬,而且容易出错。
    • 任何人都可以解释如何为没有出现在任何功能依赖关系左侧的属性创建代理键来规范数据库吗?正如 OP 所说, Person 什么都不决定(永远不会)。您会为名为“Stuff”的属性提供相同的建议吗?这里可能存在规范化问题,但不涉及 Person。
    • @NealB 我作为老师的经历和提问的方式让我相信 OP 是有偏见的。该字段被称为人员而不是文本的简单事实在 IMO 中是相关的。
    • 很难说 OP 是否有偏见或只是给他的模型中的某些属性起不幸的名字。当我看到一个名为 Person 的东西时,我自然希望它不仅仅是几个随机字母,但鉴于他对应用程序的描述,Person 似乎只是一个不能确定其他事实的标签。如果这改变了为它创建代理键和 Person 的单独关系的论点就会焕然一新。
    【解决方案2】:

    为 person 创建一个新表并使用该表的键代替 person 属性与规范化无关。出于其他原因,这可能是一个好主意,但这样做并不会使数据库“更规范化”而不是不这样做。所以你是对的:就规范化而言,创建另一个表是不必要的。

    【讨论】:

      【解决方案3】:

      我会投票给你的朋友。我喜欢规范化并为未来做计划,即使你从不需要它,这种规范化也很容易做到,实际上不需要时间。您可以创建一个您查询的视图,以使您的 SQL 更清晰,并消除您自己连接表的需要。

      【讨论】:

        【解决方案4】:

        如果您已经达到了所有能力并且没有扩展能力的计划,我认为您可以保持原样。

        如果您打算添加更多内容,即允许人们拥有帐户或其他任何东西,我认为将您的数据分成 Person、Comments 表可能是明智之举。它并不难,并且可以更轻松地扩展您的功能。

        【讨论】:

          【解决方案5】:

          你是对的。

          Person 可能是一般事物,但不在您的模型中。如果您要让人们正确识别他们正在谈论的人,那么Person 表将是必要的。例如,如果 cmets 只是关于已在数据库中注册的人。

          但是这里看起来你有一个非结构化数据,没有身份;并且没有人/没有人有兴趣确定“jenny”和“jennyyy”是否实际上是同一个人,更不用说“jenny doe”和“我的堂兄”......

          【讨论】:

            【解决方案6】:

            嗯,有两种思想流派。有人说,尽可能以最规范化的方式创建数据模型,然后在需要更高效率时反规范化。另一个基本上是“做这项工作所需的最少工作,然后随着你的需求变化而改变它”。也称为 YAGNI(你不会需要它)。

            这完全取决于您看到的情况。如果这就是全部,那么您的方法可能很好。如果您打算随着时间的推移使用新功能对其进行改进,那么您的朋友是对的。

            【讨论】:

              【解决方案7】:

              如果您从不打算将人员列与用户或其他任何内容相关联,并且数据显然不需要一致性或数据完整性检查,那么为什么要在关系数据库中这样做呢?这不是 nosql 数据库的用例吗?还是我错过了什么?

              【讨论】:

                【解决方案8】:

                规范化是关于函数依赖(FD)的。您需要确定所有 在完全规范化之前存在于数据模型属性中的 FD。

                让我们回顾一下你有什么:

                • CommentId 的任何给定实例在功能上决定了 Person(FD:CommentId -> Person
                • CommentId 的任何给定实例在功能上决定了 Comment(FD:CommentId -> Comment
                • CommentId 的任何给定实例在功能上决定了 UserId(FD:CommentId -> UserId
                • CommentId 的任何给定实例在功能上决定了 Score(FD:CommentId -> Score

                这里的一切都是CommentId 的依赖属性和 CommentId 一个人。这可能会导致您相信包含所有或子集的关系(表) 上述属性必须标准化。

                首先要问自己,为什么要创建CommentId 属性?严格来讲, 这是一个制造属性 - 它与任何“真实”无关。评论 ID 是 通常称为代理键。代理键只是一个组成的值,代表 对于与其他一些属性组相对应的唯一值集。那么CommentId是哪组属性 替代品?我们可以认为 通过提出以下问题并向模型添加新的 FD:

                • 1) 评论必须是唯一的吗?如果是这样,FD:Comment -> CommentId 必须为真。
                • 2) 同一评论可以多次发表,只要它是关于不同的人的吗?如果是这样,那么 FD:Person + Comment -> CommentId 必须为真,上面1中的FD为假。
                • 3) 可以对同一个人多次发表相同的评论,前提是该评论是由 不同的UserId?如果是这样,则 1 和 2 中的 FD 不能为真,但 FD:Person + Comment + UserId -> CommentId 可能是真的。
                • 4) 同一 UserId 是否可以对同一个人多次发表相同的评论,但 有不同的分数?这意味着 FD:Person + Comment + UserId' + Score -> CommentId 为真,其他为假。

                以上 4 个 FD 中的一个必须为真。无论哪种方式都会影响您的数据模型的规范化方式。

                假设 FD:Person + Comment + UserId -> CommentId 是真的。合乎逻辑的 后果是:

                • Person + Comment + UserIdCommentId 用作相对于 Score 的等效键
                • Score 应该与其一个但不是两个键(以避免传递依赖)建立关系。 显而易见的选择是 CommentId,因为它是专门作为代理创建的。
                • 需要一个由以下组成的关系:CommentIdPersonCommentUserId 来绑定 它的替代品的关键。

                从理论上看,代理键CommentId 不是 需要使您的数据模型或数据库工作。但是,它的存在可能会影响关系的构建方式。

                代理键的创建是一个具有一定重要性的实际问题。 考虑如果您选择不使用代理键而是使用完整的代理键可能会发生什么 属性集Person + Comment + UserId 在它的位置,特别是如果它是必需的 在多个表上作为外键或主键:

                • 注释可能会增加大量空间开销 到您的数据库,因为它在多个表中重复。它可能不止几个字符。
                • 如果有人选择编辑评论会怎样?这种变化需要传播 到 Comment 是键的一部分的所有表。不是一个漂亮的景象!
                • 为长而复杂的键编制索引可能会占用大量空间和/或降低更新性能

                分配给代理键的值永远不会改变,无论您对这些值做什么 关联到它确定的属性。现在更新依赖属性 仅限于定义代理键的一个表。这具有巨大的现实意义。

                现在回到您是否应该为Person 创建代理项。 Person 直播吗 在许多或任何 FD 的左侧?如果是这样,它的价值将通过你的 数据库,并且有一个为它创建代理的案例。 Person 是文本属性还是数字属性与创建代理键的选择无关。

                根据你所说的,创建一个 Person 的代理。这个论点是基于怀疑它的值可能会在未来的某个时刻成为键或键的一部分。

                【讨论】:

                  【解决方案9】:

                  这是交易。每当您创建某些东西时,您都希望确保它有成长的空间。您想尝试为您的计划预测未来的项目和未来的进步。在这种情况下,您说得对,目前不需要添加仅包含 1 个字段的人员表(不计算 ID,假设您有一个 int ID 字段和一个人名)。但是,将来,您可能希望为这些人设置其他属性,例如名字、姓氏、电子邮件地址、添加日期等。

                  虽然过度规范化肯定是有害的,但我个人会创建另一个更大的表来容纳具有额外字段的人,以便我将来可以轻松添加新功能。

                  【讨论】:

                  • 以后从“person”中提取所有“unique”条目并使其成为新表中的列真的那么难吗?使用字符串作为主键有问题吗?
                  • 使用字符串作为 PK 只会让事情变得更加困难。很多 DBMS 允许您使用自动递增的数字 ID,因此您不必担心发生错误的可能性。与字符串相比,使用数字 ID 更有意义。
                  【解决方案10】:

                  每当您与用户打交道时,都应该有一张专门的桌子。然后你就可以加入表格并引用该用户的 ID。

                  user -> id | username | password | email
                  
                  comment -> id | user_id | content
                  

                  将 cmets 加入用户的 SQL:

                  SELECT user.username, comment.content FROM user JOIN comment WHERE user.id = comment.user_id;
                  

                  将来当您想要查找有关该特定用户的信息时,它会变得更加容易。额外的努力可以忽略不计。

                  关于每条评论的“分数”,这也应该是一个单独的表格。这样您就可以将用户与“喜欢”或“不喜欢”联系起来。

                  【讨论】:

                    【解决方案11】:

                    有了这个数据库,你可能觉得还可以,但是以后你想让用户从数据库中了解更多的时候可能会出现一些问题。假设你想知道一个人的 cmets 数量name='abc'。在这种情况下,您将不得不遍历整个 cmets 表并继续计数。取而代之的是,您可以为每个人设置一个名为“count”的属性,并在有评论时将其递增在那个人身上制作的。
                    就规范化而言,拥有规范化的数据库总是更好,因为它减少了冗余并使数据库直观易懂。如果您期望您的数据库将来会变大,那么规范化必须存在。

                    【讨论】:

                      猜你喜欢
                      • 2021-04-22
                      • 2018-11-06
                      • 2014-03-01
                      • 2014-01-15
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-04-05
                      • 2018-02-28
                      • 1970-01-01
                      相关资源
                      最近更新 更多