2008-08-24 66 views
3

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

我正在寻找编写查询来查找匹配给定标记集的对象。假设我有如下数据(在[对象] - > [标签] *格式)

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'; 

运行时不产生任何结果。

+0

你用什么RDBMS? 如何你需要做的这个实施?一个存储过程?你想如何传递参数? 或者你会被动态构成SQL数据库管理系统之外?在这种情况下,使用什么编程语言? – van 2009-05-26 22:31:36

回答

4

考虑:

  • 对象表(主键ID)
  • objecttags表(外键对象ID,标签识别)
  • 标签表(主键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个标签不在同一行,并且t因此,一个并不会产生任何结果,因为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; 
+0

当数据库中有数百万行时,这是非常不理想的 – 2016-12-19 21:42:59

-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 %'; 
0

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

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

您的查询是寻找标签既是水果又是食物的行,这是不可能的,因为该字段只能有一个值,而不能同时存在。

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' 
0

结合史蒂夫M.的建议与Jeremy的,你会得到一个单一的纪录,你在找什么:

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

现在,这并不规模非常好,但它会让你在找什么。我认为有一个更好的方法可以做到这一点,所以你可以很容易地拥有N个匹配的项目,而不会对代码产生很大的影响,但它现在逃脱了我。

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')