在标记博客帖子时,我真的应该使用关系表吗?

时间:2009-01-26 16:05:52

标签: mysql database tags tagging denormalization

在试图弄清楚如何使用单个sql语句here来标记博客文章时,我想到了以下想法:使用关键表tag2post按ID引用标签,如下所示:

tags
+-------+-----------+
| tagid | tag       |
+-------+-----------+
|     1 | news      | 
|     2 | top-story | 
+-------+-----------+

tag2post
+----+--------+-------+
| id | postid | tagid |     
+----+--------+-------+
|  0 |    322 |     1 |
+----+--------+-------+

为什么不只使用以下模型,将标记本身编入索引,如下所示?认为标签永远不会被重命名,但是添加和删除,这可能有意义,对吗?你觉得怎么样?

tag2post
+----+--------+-------+
| id | postid | tag   |     
+----+--------+-------+
|  1 |    322 | sun   |
+----+--------+-------+
|  2 |    322 | moon  |
+----+--------+-------+
|  3 |   4443 | sun   |
+----+--------+-------+
|  4 |   2567 | love  |
+----+--------+-------+

PS:我保留了 ID ,为了轻松显示添加的最后 n 标签...

5 个答案:

答案 0 :(得分:7)

它可以工作,但它没有标准化,因为标签中有冗余。你也失去了使用“相同”标签来标记除帖子之外的东西的能力。对于小N,优化并不重要,所以如果你使用它,我没有问题。

实际上,您的索引会更大(假设您要对标记进行索引以进行搜索,您现在正在索引重复项和索引字符串)。在规范化版本中,标签表上的索引将更小,不会有重复,并且tagid上tag2post表上的索引将更小。此外,固定大小的int列对索引非常有效,您可能还会根据您的群集选择避免一些碎片。

我知道你说过没有重命名,但总的来说,在这两种情况下,你可能仍然需要考虑重命名(甚至删除)标签的语义 - 所有条目都需要更改,或者标签是否以某种方式分裂。因为在最坏的情况下这是事务中的批处理操作(所有tag2post都必须重命名),所以从设计的角度来看,我并没有真正将它归类为重要的。

答案 1 :(得分:2)

这对我来说听起来不错,当你有不同的东西,比如用户的名字或其他什么时,使用ID来引用你委托给另一个表的东西是有意义的,因为你不想在每个地方改变它的名字在他的数据库中,当他改变它。但是在这种情况下,标签名称本身不会发生变化,因此我看到的唯一潜在缺点是文本索引可能比要搜索的数字索引稍慢。

答案 2 :(得分:2)

您的提案相对于包含ID的关系表的真正优势在哪里?

从技术上讲,它们解决了同样的问题,但是您提出的解决方案是以冗余的,非规范化的方式实现的,这似乎只是满足了直接从关系表中读取数据的本能冲动。

数据库服务器非常擅长连接表,如果连接位于带有索引的INT字段上,则更是如此。当您将另一个表(例如:INT id, VARCHAR(50) TagName)加入查询时,我认为您不会面临破坏性的性能问题。

但是你失去了轻松重命名标签的能力(即使你不打算这样做),并且你不必要地用冗余数据夸大你的关系表。随着时间的推移,这可能会比标准化解决方案花费更多的性能。

答案 3 :(得分:2)

取决于您的应用,非规范化方法可能没问题。 您可能会发现由于搜索大量VARCHAR数据而导致性能下降。

搜索标记为“sun *”的内容时(例如太阳,晴天,日出) 你不需要加入。但是,您需要对更大的VARCHAR数据集进行类似的比较。正确的索引可能会缓解此问题,但只有测试才能告诉您数据集的哪种方法更快。

您还可以选择添加预先加入规范化表格的VIEW。这为您提供了更简单的查询,同时仍然允许您拥有高度标准化的数据。

我的建议是使用规范化结构(并添加非规范化视图以便于使用),直到遇到使数据模式修复程序不规范化的问题。

答案 4 :(得分:0)

我也在考虑这个问题。想要数据库中的标签列表,只需从tag2post中选择不同的标签即可。有人告诉我,因为我想优化select语句,所以最好使用整数键,因为它比使用字符串要快得多。