推荐的用于标记或标记的SQL数据库设计

推荐的用于标记或标记的SQL数据库设计

Recommended SQL database design for tags or tagging

我听说过几种实施标记的方法; 使用TagID和ItemID之间的映射表(对我来说有意义,但是可以缩放吗?),向ItemID添加固定数量的可能的TagID列(似乎是个坏主意),将标签保持在逗号分隔的文本列中(声音 疯狂但可以工作)。 我什至听说有人建议使用稀疏矩阵,但是标签名称又如何优雅地增长呢?

我是否错过了标签的最佳做法?


三个表(一个用于存储所有项目,一个用于所有标签,一个用于两者之间的关系)已正确索引,并且在适当的数据库上运行了外键,这些表应该可以正常工作并可以适当扩展。

1
2
3
4
5
6
7
8
TABLE: Item
COLUMNS: ItemID, Title, Content

TABLE: Tag
COLUMNS: TagID, Title

TABLE: ItemTag
COLUMNS: ItemID, TagID

通常,我会同意Yaakov Ellis的观点,但是在这种特殊情况下,还有另一个可行的解决方案:

使用两个表:

1
2
3
4
5
6
7
TABLE: Item
COLUMNS: ItemID, Title, Content
Indexes: ItemID

TABLE: Tag
COLUMNS: ItemID, Title
Indexes: ItemId, Title

这具有一些主要优点:

首先,它使开发变得更加简单:在用于item的插入和更新的三表解决方案中,您必须查找Tag表以查看是否已经有条目。然后,您必须与新成员一起加入。这不是小事。

然后,它使查询更简单(也许更快)。您将执行三个主要的数据库查询:输出所有Tags到一个item,绘制一个标签云,并为一个标签标题选择所有项目。

一个项目的所有标签:

3桌:

1
2
3
4
SELECT Tag.Title
  FROM Tag
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 WHERE ItemTag.ItemID = :id

2表:

1
2
3
SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id

标签云:

3桌:

1
2
3
4
SELECT Tag.Title, COUNT(*)
  FROM Tag
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 GROUP BY Tag.Title

2表:

1
2
3
SELECT Tag.Title, COUNT(*)
  FROM Tag
 GROUP BY Tag.Title

一个标签的项目:

3桌:

1
2
3
4
5
SELECT Item.*
  FROM Item
  JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
  JOIN Tag ON ItemTag.TagID = Tag.TagID
 WHERE Tag.Title = :title

2表:

1
2
3
4
SELECT Item.*
  FROM Item
  JOIN Tag ON Item.ItemID = Tag.ItemID
 WHERE Tag.Title = :title

但是也有一些缺点:它可能会占用数据库中更多的空间(这可能会导致更多的磁盘操作,速度变慢),并且未规范化这可能会导致不一致。

size参数不是很强,因为标签的本质是标签通常很小,因此尺寸增加不是很大。有人可能会说,在一个只包含每个标签一次的小表中,对标签标题的查询要快得多,这当然是正确的。但是考虑到不必加入而节省下来的钱,以及可以在它们上建立良好索引的事实,很容易就能弥补这一点。当然,这在很大程度上取决于您所使用的数据库的大小。

不一致的论点也有一点争议。标签是自由文本字段,没有预期的操作,例如"将所有标签" foo"重命名为" bar""。

tldr:我会寻求两张桌子的解决方案。 (实际上,我要去。我找到了这篇文章,以查看是否有反对它的有效论点。)


如果您使用的是支持map-reduce的数据库(例如,couchdb),则将标签存储在纯文本字段或列表字段中确实是最好的方法。例:

1
2
3
4
5
6
7
8
9
10
tagcloud: {
  map: FUNCTION(doc){
    FOR(tag IN doc.tags){
      emit(doc.tags[tag],1)
    }
  }
  reduce: FUNCTION(KEYS,VALUES){
    RETURN VALUES.length
  }
}

使用group = true运行此命令将按标签名称对结果进行分组,甚至返回遇到该标签的次数的计数。这与计算文本中单词的出现非常相似。


使用单个格式化的文本列[1]来存储标签,并使用功能强大的全文本搜索引擎对此进行索引。否则,在尝试实现布尔查询时,您将遇到扩展问题。

如果需要有关标签的详细信息,则可以在增量维护的表中跟踪它,也可以运行批处理作业以提取信息。

[1]一些RDBMS甚至提供了本机数组类型,由于不需要解析步骤,它甚至可能更适合存储,但是可能会导致全文搜索出现问题。


我一直将标签放在单独的表中,然后有一个映射表。当然,我也从来没有做过任何大规模的事情。

拥有一个"标签"表和一个映射表使得生成标签云变得相当简单,因为您可以轻松地将SQL组合在一起以获取一个标签列表,其中包含每个标签使用频率的计数。


我建议以下设计:
项目表:
Itemid,taglist1,taglist2
这样很快,并且可以轻松地在项目级别保存和检索数据。

并行构建另一个表:
标签
标签
不要使标签成为唯一标识符,如果第二列中的空间用完了,假设第二列包含100个项目,则会创建另一行。

现在,在搜索标签商品时,它将会非常快。


推荐阅读