我想知道什么是最好的方式是实施一种标记系统,就像一个使用上。我想这样但我可以不来了一个良好的可扩展的解决方案。

我想有一个基本表3方案:具有一个 tags 表, articles 桌子和一个 tag_to_articles 表。

这是最好的解决这个问题,或者是否有替代方案?使用这种方法表将得到极大的时间,并为搜寻这是不是太有效。另一方面它是不是不重要的是,执行查询快。

有帮助吗?

解决方案

我相信你会发现这篇博文有趣:标签:数据库模式

  

问题:您希望拥有一个可以标记a的数据库架构   书签(或博客文章或其他任何内容),包含任意数量的标签。   之后,您希望运行查询以将书签限制为   联合或标记的交集。你也想排除(比如说:减去)   搜索结果中的一些标签。

“ MySQLicious”溶液

在此解决方案中,架构只有一个表,它是非规范化的。这种类型被称为“ MySQLicious解决方案”因为MySQLicious将del.icio.us数据导入到具有这种结构的表中。

交叉点(AND) 查询“搜索+网络服务+ semweb”:

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"

联盟(OR) 查询“搜索| webservice | semweb”:

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"

<强>减 查询&#8220;搜索+ webservice-semweb&#8221;

SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"

&#8220;天窗&#8221;溶液

Scuttle 将其数据组织在两个表格中。那张桌子&#8220; scCategories&#8221;是&#8220;标签&#8221; -table并且有一个外键到&#8220;书签&#8221; -table。

交叉点(AND) 查询&#8220;书签+网络服务+ semweb&#8221;:

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId
HAVING COUNT( b.bId )=3

首先,搜索所有书签 - 标签组合,其中标签是&#8220;书签&#8221;&#8220; webservice&#8221;或&#8220; semweb&#8221; (c.category IN('bookmark','webservice','semweb')),然后只考虑搜索了所有三个标签的书签(HAVING COUNT(b.bId)= 3)。

联盟(OR) 查询&#8220;书签| webservice | semweb&#8221;: 只要省略HAVING子句就可以了:

SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN ('bookmark', 'webservice', 'semweb'))
GROUP BY b.bId

减(排除) 查询&#8220;书签+ webservice-semweb&#8221;,即:书签和网络服务AND NOT semweb。

SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN ('bookmark', 'webservice'))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = 'semweb')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2

离开HAVING COUNT会导致查询&#8220;书签| webservice-semweb&#8221;


&#8220; TOXI&#8221;溶液

Toxi 提出了一个三表结构。通过表格&#8220; tagmap&#8221;书签和标签是n-to-m相关的。每个标签可以与不同的书签一起使用,反之亦然。 wordpress也使用这个DB模式。 查询与&#8220; scuttle&#8221;中的查询完全相同。溶液

交叉点(AND) 查询&#8220;书签+网络服务+ semweb&#8221;

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3

联盟(OR) 查询&#8220;书签| webservice | semweb&#8221;

SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN ('bookmark', 'webservice', 'semweb'))
AND b.id = bt.bookmark_id
GROUP BY b.id

减(排除) 查询&#8220;书签+ webservice-semweb&#8221;,即:书签和网络服务AND NOT semweb。

SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN ('Programming', 'Algorithms'))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = 'Python')
GROUP BY b.id
HAVING COUNT( b.id ) =2

离开HAVING COUNT会导致查询&#8220;书签| webservice-semweb&#8221;

其他提示

你的三桌解决方案没有错。

另一种选择是限制可以应用于文章的标签数量(如SO中的5)并将这些标签直接添加到文章表中。

规范化数据库有其优点和缺点,就像硬连接到一个表中有利有弊。

没有什么说你不能做到这两点。它反对关系数据库范式重复信息,但如果目标是性能,你可能不得不打破范式。

您建议的三个表格实施将用于标记。

然而,

堆栈溢出使用不同的实现。它们以明文形式将标记存储到posts表中的varchar列,并使用全文索引来获取与标记匹配的帖子。例如 posts.tags =&quot; algorithm system tagging best-practices&quot; 。我确信Jeff已经在某个地方提到了这个但我忘记了。

建议的解决方案是最好的 - 如果不是唯一可行的方法,我可以想到解决标签和文章之间的多对多关系。所以我的投票是“是的,它仍然是最好的。”我会对任何替代方案感兴趣。

如果您的数据库支持可索引数组(例如PostgreSQL),我建议使用完全非规范化的解决方案 - 将标记存储为同一个表上的字符串数组。如果不是,则将对象映射到标记的辅助表是最佳解决方案。如果您需要针对标记存储额外信息,则可以使用单独的标记表,但是为每个标记查找引入第二个连接没有意义。

我想建议优化MySQLicious以获得更好的性能。 在此之前,Toxi(3表)解决方案的缺点是

如果您有数百万个问题,并且每个问题都有5个标签,那么tagmap表中将有500万个条目。因此,首先我们必须根据标签搜索过滤出1万个标签图条目,然后再次过滤出那些万标记的匹配问题。因此,当过滤掉artical id是简单的数字时,它就可以了,但是如果它是UUID(32 varchar),那么过滤掉需要更大的比较,尽管它被索引。

我的解决方案:

每当创建新标记时,都有计数器++(基数为10),并将该计数器转换为base64。现在每个标签名称都有base64 id。并将此ID与名称一起传递给UI。 这样,在我们的系统中创建了4095个标签之前,您将拥有最多两个字符。现在将这些多个标记连接到每个问题表标记列中。添加分隔符并对其进行排序。

所以表格看起来像这样

在查询时,查询id而不是真实标签名称。 由于 SORTED ,标记上的条件将更有效( LIKE'%| a |%| c |%| f |% )。

请注意,单个空格分隔符是不够的,我们需要使用双分隔符来区分 sql mysql 之类的标记,因为 LIKE&quot;%sql%&quot; 也将返回 mysql 结果。应该是 LIKE&quot;%| sql |%&quot;

我知道搜索没有编入索引但仍然可能已经编入了与作者/ dateTime等文章相关的其他列的索引,否则将导致全表扫描。

最后使用此解决方案,不需要内部连接,其中必须将百万条记录与连接条件下的500万条记录进行比较。

CREATE TABLE Tags (
    tag VARHAR(...) NOT NULL,
    bid INT ... NOT NULL,
    PRIMARY KEY(tag, bid),
    INDEX(bid, tag)
)

注:

  • 这比毒在,它并不通过一个额外的许多许多表,优化使得困难。
  • 当然,我的做法可能会稍微更大(超过毒)由于多余的标记,但这是一个很小的百分比 整个 数据库,并且性能的改进,可以显着。
  • 它是高度的可伸缩性。
  • 它不具(因为它不需要)一个代理 AUTO_INCREMENT PK。因此,它是更好的比天窗.
  • MySQLicious糟糕,因为它无法使用索引(LIKE领先 野卡;假命上子)
  • MySQL,一定要使用的选项,以便得到'集群'的影响。

相关的讨论(MySQL):
许多许多映表优化
下令名单

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top