不必要的规范化

时间:2010-09-10 15:13:57

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

我和我的朋友正在建立一个网站并且存在重大分歧。该网站的核心是一个关于“人”的评论数据库。基本上人们可以输入评论,他们可以进入评论的人。然后,查看者可以在数据库中搜索注释中的单词或人名的部分内容。它完全由用户生成。例如,如果有人想对一个人姓名的拼错版本发表评论,他们可以,那就没问题。因此,可能有多个不同人的拼写被列为几个不同的条目(一些具有中间名,一些具有昵称,一些具有错误,等等),但这一切都可以。我们不在乎人们是否对随机人或想象的人做出评论。

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

评论ID - 评论 - 人

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

2 - “臭女孩” - 珍妮

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

4 - “欠我20美元” - Jennyyyyyyyyy

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

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

谁是对的?

11 个答案:

答案 0 :(得分:9)

在我看来,你的朋友是对的。

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

从长远来看,你可能想要对你的网站做更多的事情,比如说你想把多个文件附加到一个人(即图片),你会非常感激,然后进行规范化。

答案 1 :(得分:8)

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

答案 2 :(得分:3)

我会投票给你的朋友。我喜欢规范化并为未来做好计划,即使你从未需要它,这种规范化也很容易实现,从字面上看,它不需要时间。您可以创建一个查询的视图,以使您的SQL更清洁,并且无需自己加入表。

答案 3 :(得分:2)

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

如果您计划添加更多内容,即允许人们拥有帐户或其他任何内容,我认为将您的数据分成人员,评论表可能是明智之举。它并不难,使您的功能扩展更容易。

答案 4 :(得分:2)

你是对的。

Person可能是一般情况,但不在您的模型中。如果你打算让人们正确地识别他们正在谈论的人,那么就需要Person表。例如,如果评论仅涉及已在数据库中注册的人员。

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

标准化完全是关于功能依赖性(FD)。您需要确定所有 在数据模型的属性可以完全标准化之前存在的FD。

让我们回顾一下你的所作所为:

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

此处的所有内容都是CommentIdCommentId的依赖属性 仅CommentId。这可能会让您相信一个包含所有或一部分的关系(表) 以上属性必须标准化。

首先要问自己,为什么要创建CommentId属性?严格来说, 这是一个制造属性 - 它与任何“真实”无关。评论是 通常被称为代理键。代理键只是一个弥补的价值 对于与其他一组属性相对应的唯一值集。那么什么属性组是Comment 代理人?我们可以想到这一点 通过询问以下问题并在模型中添加新的FD:

  • 1)评论必须是唯一的吗?如果是这样FD:CommentId - > Person必须是真的。
  • 2)只要是关于不同的人,可以多次进行相同的评论吗?如果是的话,那么 FD:Comment + CommentId - > Person必须为true,上面1中的FD为false。
  • 3)同一个评论可以多次针对同一个人提出 不同的UserId?如果是这样,1和2中的FD不可能是真的,但是 FD:Comment + UserId + CommentId - > Person可能属实。
  • 4)同一个UserId可以对同一个人进行多次相同的注释但是 有不同的分数?这意味着FD:Comment + UserId + Score'+ CommentId - > Person是真的,其他都是假的。

上述4个FD中的一个必须是真的。无论哪种因素都会影响数据模型的规范化。

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

  • Comment + UserId + CommentIdScore用作Score
  • 的等效键
  • CommentId应该与一个但不是两个密钥建立关系(以避免传递依赖性)。 显而易见的选择是CommentId,因为它是专门创建的替代品。
  • 需要包含以下内容的关系:PersonCommentUserIdCommentId 其代理的关键。

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

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

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

无论您对值如何操作,分配给代理键的值都不会更改 与其确定的属性相关联。现在更新依赖属性 限于定义代理键的一个表。这具有重大的现实意义。

现在回到是否应该为Person创建代理。 Person是否有效 在许多或任何FD的左侧?如果确实如此,它的价值将通过你的价值传播 数据库,并有一个案例为它创建一个代理。 Person是text还是numeric属性与创建代理键的选择无关。

根据你所说的,充其量只是一个弱论点来创建一个 代理{{1}}。这个论点的基础是怀疑它的价值在某些时候可能成为未来某个时刻关键或关键的一部分。

答案 7 :(得分:1)

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

答案 8 :(得分:0)

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

虽然过度规范化肯定是有害的,但我个人会创建另一个更大的表来保存具有其他字段的人,这样我将来可以轻松添加新功能。

答案 9 :(得分:0)

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

user -> id | username | password | email

comment -> id | user_id | content

将评论加入用户的SQL:

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

如果您想查找有关该特定用户的信息,将来会更容易。额外努力的数量可以忽略不计。

关于每条评论的“得分”,这也应该是一个单独的表格。这样,您就可以将用户连接到“喜欢”或“不喜欢”。

答案 10 :(得分:0)

使用此数据库,您可能会觉得它没问题,但是如果您希望用户从数据库中了解更多信息,将来可能会出现一些问题。假设您想了解对某个人发表的评论数量name ='abc'。在这种情况下,你将不得不浏览整个评论表并继续计数。代替这一点,你可以为每个人都有一个名为'count'的属性,并在评论时增加对那个人做的。
就标准化而言,拥有标准化数据库总是更好,因为它减少了冗余并使数据库直观易懂。如果您希望将来数据库变大,那么必须存在规范化。