2008-10-02 54 views
10

假设我有一个带两列的“标签”表:tagidcontentid。每一行代表分配给一段内容的标签。我想要一个查询,这将使我的每一条内容被标记为tagids 334,338的内容识别,和342SQL查询:在几行而不是子查询中模拟“AND”

“易”的方式来做到这将是():

select contentid from tags where tagid = 334 and contentid in (
    select contentid from tags where tagid = 338 and contentid in (
     select contentid from tags where tagid = 342 
    ) 
) 

但是,我的直觉告诉我,有一个更好,更快,更可扩展的方法来做到这一点。例如,如果我需要找到12个标签的交集?这很快就会变得可怕。有任何想法吗?

编辑:原来这也包括在this excellent blog post

回答

24
SELECT contentID 
FROM tags 
WHERE tagID in (334, 338, 342) 
GROUP BY contentID 
HAVING COUNT(DISTINCT tagID) = 3 


--In general 
SELECT contentID 
FROM tags 
WHERE tagID in (...) --taglist 
GROUP BY contentID 
HAVING COUNT(DISTINCT tagID) = ... --tagcount 
+0

就是这样。虽然如果tagid,contentid是唯一的,那么COUNT中可能不需要DISTINCT(DISTINCT tagID) – tzot 2008-10-02 18:44:04

+0

非常聪明的解决方案,但它需要对我们安装的SQL2000进行调整。如果没有select子句中的聚合,我们不能使用“having”。很容易解决,虽然。 – 2008-10-02 18:49:48

+0

这将工作在MySQL和Oracle? – 2008-10-02 19:00:50

1

唯一的替代办法,我能想到的是:

select a.contentid from tags a 
inner join tags b on a.contentid = b.contentid and b.tagid=334 
inner join tags c on a.contentid = c.contentid and c.tagid=342 
where a.tagid=338 
-1

SQL的什么类型的? MS SQL Server,Oracle,MySQL?

在SQL Server不会将此等同于:

select contentid from tags where tagid IN (334,338,342) 
0

我不知道这是更好,但它可能是更容易维护

select contentid from tags where tagid = 334 
intersect 
select contentid from tags where tagid = 338 
intersect 
select contentid from tags where tagid = 342 

你必须建立它动态的,这不会像你原来的解决方案一样糟糕。

2

这是一个解决方案,它比我在一个非常大的对象和标签数据库上的工作速度快得多。这是一个三标签交叉点的例子。这只是众多连锁加盟的对象变量表(objtags)来表示同一个对象,并规定了WHERE子句中的标签ID:

SELECT w0.objid 

FROM  objtags t0 
INNER JOIN objtags t1 ON t1.objid=t0.objid 
INNER JOIN objtags t2 ON t2.objid=t1.objid 

WHERE t0.tagid=512 
    AND t1.tagid=256 
    AND t2.tagid=128 

我不知道为什么这样运行速度更快。它受到MusicBrainz服务器搜索代码的启发。在Postgres中做这件事,我通常比HAVING COUNT(...)解决方案的速度快8-10倍。