SQL多对多匹配

时间:2008-08-24 00:09:35

标签: sql many-to-many tagging

我正在为网站实施标记系统。每个对象有多个标签,每个标签有多个对象。这是通过维护一个每个记录有两个值的表来完成的,一个表用于对象的ID和标记。

我正在寻找一个查询来查找与给定标记集匹配的对象。假设我有以下数据(在[object] - > [tags] * format)

apple -> fruit red food
banana -> fruit yellow food
cheese -> yellow food
firetruck -> vehicle red

如果我想匹配(红色),我应该得到苹果和救火车。如果我想匹配(水果,食物)我应该得到(苹果,香蕉)。

如何编写SQL查询呢?做我想做的事情?

@Jeremy Ruten,

感谢您的回答。使用的符号用于提供一些示例数据 - 我的数据库确实有一个表,每个记录有1个对象ID和1个标记。

其次,我的问题是我需要获得与所有标签匹配的所有对象。用你的OR代替AND,如下:

SELECT object WHERE tag = 'fruit' AND tag = 'food';

运行时不会产生任何结果。

6 个答案:

答案 0 :(得分:4)

假设:

  • 对象表(主键ID)
  • objecttags表(外键objectId,tagid)
  • 标签表(主键ID)

    SELECT distinct o.*
      from object o join objecttags ot on o.Id = ot.objectid
                    join tags t on ot.tagid = t.id
     where t.Name = 'fruit' or t.name = 'food';
    

这似乎是倒退,因为你想要和,但问题是,2个标签不在同一行,因此,a并且不产生任何结果,因为1个单行不能同时是水果和食物。 此查询通常会产生重复,因为每个标记将为每个对象提供1行。

如果您希望真的这样做,在这种情况下,您的查询中需要group byhaving count = <number of ors>

  SELECT distinct o.name, count(*) as count
    from object o join objecttags ot on o.Id = ot.objectid
                  join tags t on ot.tagid = t.id
   where t.Name = 'fruit' or t.name = 'food'
group by o.name
  having count = 2;

答案 1 :(得分:4)

天哪,我可能误解了你原来的评论。

在SQL中执行此操作的最简单方法是使用三个表:

1) Tags ( tag_id, name )
2) Objects (whatever that is)
3) Object_Tag( tag_id, object_id )

然后,您可以快速,轻松,高效地询问您想要的数据问题(只要您正确索引)。如果你想获得想象力,你也可以允许多字标签(有一种优雅的方式,而且我可以想到一种不那么优雅的方式)。

我认为这就是你所拥有的,所以下面的SQL将起作用:

文字方式:

    SELECT obj 
      FROM object
     WHERE EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'fruit' 
                      AND oid = object_id ) 
       AND EXISTS( SELECT * 
                     FROM tags 
                    WHERE tag = 'Apple'
                      AND oid = object_id )

还有其他方法可以做到,例如:

SELECT oid
  FROM tags
 WHERE tag = 'Apple'
INTERSECT
SELECT oid
  FROM tags
 WHERE tag = 'Fruit'

答案 2 :(得分:0)

@Kyle:您的查询应该更像:

SELECT object WHERE tag IN ('fruit', 'food');

您的查询正在寻找标签既是水果又是食物的行,由于该字段只能有一个值,而不是同时具有一个值,因此无法看到。

答案 3 :(得分:0)

将史蒂夫M.的建议与杰里米相结合,你会得到一份你正在寻找的记录:

select object
from tblTags
where tag = @firstMatch
and (
       @secondMatch is null 
       or 
       (object in (select object from tblTags where tag = @secondMatch)
     )

现在,它不能很好地扩展,但它会得到你想要的东西。我认为有一个更好的方法可以做到这一点,这样你可以很容易地拥有N个匹配的项目而不会对代码产生很大的影响,但它目前让我感到厌烦。

答案 4 :(得分:0)

我推荐以下架构。

Objects: objectID, objectName
Tags: tagID, tagName
ObjectTag: objectID,tagID

使用以下查询。

select distinct
    objectName
from
    ObjectTab ot
    join object o
        on o.objectID = ot.objectID
    join tabs t
        on t.tagID = ot.tagID
where
    tagName in ('red','fruit')

答案 5 :(得分:-2)

我建议你的桌子每条记录有1个标签,如下所示:

 apple -> fruit
 apple -> red
 apple -> food
 banana -> fruit
 banana -> yellow
 banana -> food

然后你可以

 SELECT object WHERE tag = 'fruit' OR tag = 'food';

如果你真的想按照自己的方式去做,你可以这样做:

 SELECT object WHERE tag LIKE 'red' OR tag LIKE '% red' OR tag LIKE 'red %' OR tag LIKE '% red %';