我听说过一些实现标签的方法;使用TagID和ItemID之间的映射表(对我来说有意义,但它是否缩放?),向ItemID添加固定数量的可能的TagID列(似乎是个坏主意),在文本列中保留逗号分隔的标签(听起来很疯狂,但可以工作)。我甚至听说有人建议使用稀疏矩阵,但是标签名如何优雅地增长呢?

我是否错过了标签的最佳实践?


三个表(一个用于存储所有项,一个用于存储所有标记,一个用于存储两者之间的关系),在适当的数据库上正确地建立索引,并设置外键,应该能够正常工作并适当地扩展。

Table: Item
Columns: ItemID, Title, Content

Table: Tag
Columns: TagID, Title

Table: ItemTag
Columns: ItemID, TagID

我总是把标签放在一个单独的表中,然后有一个映射表。当然,我也从来没有做过真正大规模的事情。

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

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

如果需要关于所拥有的标记的详细信息,可以在增量维护的表中跟踪它,或者运行批处理作业来提取信息。

一些RDBMS甚至提供了原生数组类型,这种类型可能更适合存储,因为不需要解析步骤,但可能会导致全文搜索出现问题。

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

tagcloud: {
  map: function(doc){ 
    for(tag in doc.tags){ 
      emit(doc.tags[tag],1) 
    }
  }
  reduce: function(keys,values){
    return values.length
  }
}

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

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

使用两个表:

Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID

Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title

这有一些主要的优势:

首先,它使开发变得更加简单:在插入和更新项目的三表解决方案中,您必须查找Tag表,以查看是否已经有条目。然后你必须加入新的。这不是一项简单的任务。

然后,它使查询更简单(也许更快)。有三个主要的数据库查询,你将做:输出一个项目的所有标签,绘制标签云和选择一个标签标题的所有项目。

一个项目的所有标签:

3表:

SELECT Tag.Title 
  FROM Tag 
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 WHERE ItemTag.ItemID = :id

2的表格:

SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id

类似于:

3表:

SELECT Tag.Title, count(*)
  FROM Tag
  JOIN ItemTag ON Tag.TagID = ItemTag.TagID
 GROUP BY Tag.Title

2的表格:

SELECT Tag.Title, count(*)
  FROM Tag
 GROUP BY Tag.Title

一件物品

3表:

SELECT Item.*
  FROM Item
  JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
  JOIN Tag ON ItemTag.TagID = Tag.TagID
 WHERE Tag.Title = :title

2的表格:

SELECT Item.*
  FROM Item
  JOIN Tag ON Item.ItemID = Tag.ItemID
 WHERE Tag.Title = :title

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

The size argument is not that strong because the very nature of tags is that they are normally pretty small so the size increase is not a large one. One could argue that the query for the tag title is much faster in a small table which contains each tag only once and this certainly is true. But taking in regard the savings for not having to join and the fact that you can build a good index on them could easily compensate for this. This of course depends heavily on the size of the database you are using.

前后矛盾的论点也有点毫无意义。标签是自由文本字段,没有像“将所有标签“foo”重命名为“bar”这样的操作。

tldr:我会选择双表解决方案。(事实上,我正要去。我找到了这篇文章,看看是否有有效的理由反对它。)

我建议设计如下: 项目表: Itemid, taglist1, taglist2 这将是快速和容易的保存和检索数据的项目级别。

并行构建另一个表: 标签 标签 不要让标签唯一的标识符,如果你用完了第二列的空间,其中包含100个项目,创建另一行。

现在,当搜索项目的标签,这将是超级快。