数据库标签devise

你将如何devise一个数据库来支持下面的标签function:

  • 项目可以有大量的标签
  • search所有用一组给定标签标记的项目必须是快速的(项目必须具有所有标签,所以这是一个ANDsearch,而不是ORsearch)
  • 创build/写入项目可能会较慢,以便快速查找/阅读

理想情况下,所有用(至less)一组n个给定标签标记的项目的查找应该使用单个SQL语句完成。 由于要search的标签的数量以及任何项目上的标签的数量是未知的,并且可能很高,所以使用JOIN是不切实际的。

有任何想法吗?


感谢所有迄今为止的答案。

如果我没有弄错,给出的答案显示了如何对标签进行ORsearch。 (select所有具有一个或多个n标签的项目)。 我正在寻找一个有效的ANDsearch。 (select所有具有全部n个标签的项目 – 可能更多。)

ANDING:这听起来就像是在寻找“关系部门”的操作。 本文涵盖了关系分工的简洁而难以理解的方式。

关于性能:基于位图的方法直观地听起来将会很好地适应这种情况。 然而,我不相信这是一个好主意,像digiguru一样,“手动”实现位图索引:这听起来像一个复杂的情况,每当添加新的标签(?)但是一些DBMS(包括Oracle)提供位图索引,可能以某种方式是有用的,因为内置索引系统消除了索引维护的潜在复杂性; 另外,在执行查询计划时,提供位图索引的DBMS应能够适当地考虑它们。

这里有一个关于标记数据库模式的好文章:

http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/

以及性能testing:

http://howto.philippkeller.com/2005/06/19/Tagsystems-performance-tests/

请注意,这里的结论对于MySQL来说是非常特殊的,至less在2005年写的时候,它的全文索引特性很差。

我没有看到一个简单的解决scheme的问题:项目表,标签表,标签交叉表“

交叉表上的指标应该足够优化。 select适当的项目将是

 SELECT * FROM items WHERE id IN (SELECT DISTINCT item_id FROM item_tag WHERE tag_id = tag1 OR tag_id = tag2 OR ...) 

和标签将是

 SELECT * FROM items WHERE EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag1) AND EXISTS (SELECT 1 FROM item_tag WHERE id = item_id AND tag_id = tag2) AND ... 

不可否认,对于大量的比较标签来说效率不高。 如果要在内存中维护标记计数,则可以使用不常用的标记进行查询,以便更快地评估AND序列。 根据要匹配的标签的期望数量以及匹配任何单个标签的期望值,这可能是好的解决scheme,如果要匹配20个标签,并且期望随机项目将匹配其中的15个,那么这仍然是重的在数据库上。

我只是想强调一下@Jeff Atwood链接到的文章( http://howto.philippkeller.com/2005/04/24/Tags-Database-schemas/ )是非常彻底的(它讨论了3种不同模式的优点方法),并且对于通常performance比目前为止所提到的更好的AND查询(即,对于每个术语不使用相关的子查询)具有很好的解决scheme。 评论中还有很多好东西。

ps – 这里所有人都在谈论的方法在文章中被称为“Toxi”解决scheme。

您可能想要尝试一个非严格数据库的解决scheme,比如Java Content Repository实现(例如Apache Jackrabbit ),并使用像Apache Lucene一样构build的search引擎。

采用适当caching机制的解决scheme可能比本地解决scheme产生更好的性能。

但是,我并不认为在一个中小型应用程序中,需要一个比先前的文章中提到的规范化数据库更复杂的实现。

编辑:与您的澄清,似乎更引人注目的是与search引擎一起使用类似JCR的解决scheme。 这将大大简化您的程序从长远来看。

最简单的方法是创build一个标签表。
Target_Type – 如果您正在标记多个表
Target – 被标记logging的关键
TagTag的文本

查询数据会是这样的:

 Select distinct target from tags where tag in ([your list of tags to search for here]) and target_type = [the table you're searching] 

UPDATE
根据你的要求和条件,上面的查询会变成这样的东西

 select target from ( select target, count(*) cnt from tags where tag in ([your list of tags to search for here]) and target_type = [the table you're searching] ) where cnt = [number of tags being searched] 

我会第二@Zizzencsbuild议,你可能需要一些不完全(R)数据库为中心

不知何故,我相信使用简单的nvarchar字段来存储具有适当的caching/索引的标签可能会产生更快的结果。 但那只是我。

我已经实现了使用3个表来表示多对多关系(标签ItemTags)的标签系统,但是我想你会在很多地方处理标签,我可以告诉你,3个表必须同时被操纵/查询,一定会使你的代码更加复杂。

您可能要考虑增加的复杂性是否值得。

你将无法避免join,仍然有点正常化。

我的方法是有一个标签表。

  TagId (PK)| TagName (Indexed) 

然后,您的项目表中有一个TagXREFID列。

这个TagXREFID列是第三个表的FK,我将它称为TagXREF:

  TagXrefID | ItemID | TagId 

所以,要获得一个项目的所有标签将是这样的:

 SELECT Tags.TagId,Tags.TagName FROM Tags,TagXref WHERE TagXref.TagId = Tags.TagId AND TagXref.ItemID = @ItemID 

为了得到一个标签的所有项目,我会使用这样的东西:

 SELECT * FROM Items, TagXref WHERE TagXref.TagId IN ( SELECT Tags.TagId FROM Tags WHERE Tags.TagName = @TagName; ) AND Items.ItemId = TagXref.ItemId; 

为了和一堆标签放在一起,你可以稍微修改上面的语句来添加AND Tags.TagName = @ TagName1 AND Tags.TagName = @ TagName2 etc …并dynamic构build查询。

我喜欢做的是有一些表格代表原始数据,所以在这种情况下,你会有

 Items (ID pk, Name, <properties>) Tags (ID pk, Name) TagItems (TagID fk, ItemID fk) 

这对于写入时间是快速的,并且保持一切正常化,但是您也可能注意到,对于每个标签,您需要为每个想要AND的标签连接两次表,因此读取速度很慢。

提高读取的解决scheme是通过build立一个存储过程来创build一个命令caching表,这个存储过程本质上是创build一个新的表格来表示数据以一种扁平化的格式。

 CachedTagItems(ID, Name, <properties>, tag1, tag2, ... tagN) 

然后,您可以考虑“标记项目”表格需要保持最新状态的频率(如果它位于每个插入项上),然后在游标插入事件中调用存储过程。 如果这是一个小时的任务,然后设置一个小时工作来运行它。

现在为了在数据检索中非常聪明,你需要创build一个存储过程来从标签中获取数据。 您不想在一个大的case语句中使用嵌套查询,而是想传入一个包含要从数据库中select的标签列表的参数,并返回一个logging集的Items。 这将是最好的二进制格式,使用按位运算符。

二进制格式,很容易解释。 假设有四个标签要分配给一个项目,用二进制可以表示这个标签

 0000 

如果所有四个标签都分配给一个对象,该对象将看起来像这样…

 1111 

如果只是前两个…

 1100 

那么这只是在你想要的列中find1和0的二进制值的情况。 使用SQL Server的Bitwise运算符,可以使用非常简单的查询来检查第一列中是否有1。

检查此链接了解更多 。

为了解释其他人所说的话:诀窍不在模式中 ,它在查询中

实体/标签/标签的天真架构是正确的路要走。 但正如你所看到的,现在还不清楚如何用很多标签来执行AND查询。

优化该查询的最佳方式将取决于平台,因此我build议您使用RDBS重新标记您的问题,并将标题更改为“在标记数据库上执行和查询的最佳方式”。

我有一些关于MS SQL的build议,但是如果这不是你正在使用的平台的话,将会避免。

上面的答案的一个变种是采取标签id,sorting,结合为一个分离的string,并散列它们。 然后,只需将散列关联到该项目。 每个标签组合产生一个新的密钥。 要进行ANDsearch,只需使用给定的标签ID和search重新创build哈希。 更改项目上的标签将导致哈希被重新创build。 具有相同标记集的项目共享相同的散列键。

如果你有一个数组types,你可以预先聚合所需的数据。 在一个单独的线程中看到这个答案:

数组types的用途是什么?