2008-10-08 84 views
2

我有主題(id *)和標籤(id *,name)和鏈接表topic_tags(topicFk *,tagFk *)。MINUS在MySQL?

現在我想選擇每個主題,它包含所有好標籤(a,b,c),但沒有任何不好的標籤(d,e,f)。

我該怎麼做?

回答

0

我自己的解決方案使用Pauls和Bills的想法。

這個想法是內部連接主題與良好的標籤(扔掉沒有好標籤的主題),然後計算每個主題的唯一標籤(以驗證所有好標籤都存在)。

同時,帶有錯誤標籤的外連接應該沒有單個匹配項(所有字段都是NULL)。

SELECT topics.id 
FROM topics 
    INNER JOIN topic_tags topic_ptags 
    ON topics.id = topic_ptags.topicFk 
    INNER JOIN tags ptags 
    ON topic_ptags.tagFk = ptags.id 
     AND ptags.name IN ('a','b','c') 
    LEFT JOIN topic_tags topic_ntags 
    ON topics.id = topic_ntags.topicFk 
    LEFT JOIN tags ntags 
    ON topic_ntags.tagFk = ntags.id 
     AND ntags.name IN ('d','e','f') 
GROUP BY topics.id 
HAVING count(DISTINCT ptags.id) = 3 
    AND count(ntags.id) = 0 
3

這是一個可行的解決方案,但需要加入所需的每個標籤。

SELECT * 
FROM topics 
WHERE topic_id IN 
    (SELECT topic_id 
    FROM topic_tags a 
    INNER JOIN topic_tags b 
     on a.topic_id=b.topic_id 
     and b.tag = 'b' 
    INNER JOIN topic_tags c 
     on b.topic_id=c.topic_d 
     and c.tag = 'c' 
    WHERE a.tag = 'a') 
AND topic_id NOT IN 
    (SELECT topic_id 
    FROM topic_tags 
    WHERE tag = 'd' or tag = 'e' or tag = 'f') 
0

不能完全肯定我明白了,我希望有一個更好的方式做的好標籤的一部分,但:

select id from topic 
    inner join topic_tags tta on topic.id=tta.topicFk and tta.tagFk=a 
    inner join topic_tags ttb on topic.id=ttb.topicFk and ttb.tagFk=b 
    inner join topic_tags ttc on topic.id=ttc.topicFk and ttc.tagFk=c 
    left join topic_tags tt on topic.id=tt.topicFk and tt.tagFk in (d,e,f) 
    where tt.topicFk is null; 

更新:是這樣的:

select id from topic 
    left join topic_tags tt on topic.id=tt.topicFk and tt.tagFk in (d,e,f) 
    where tt.topicFk is null and 
     3=(select count(*) from topic_tags where topicFk=topic.id and tagFk in (a,b,c)); 

我看到一個答案,假設a,b,c,d,e,f是名稱,而不是id。如果是這樣,那麼這個:

select id from topic 
    left join topic_tags tt on topic.id=tt.topicFk 
     inner join tags on tt.tagFk=tags.id and tags.name in (d,e,f) 
    where tt.topicFk is null and 
     3=(select count(*) from tags inner join topic_tags on tags.id=topic_tags.tagFk and topic_tags.topicFk=topic.id where tags.name in (a,b,c)); 
1

由於寫了這3個其他答案進來,但這是不同的,所以無論如何,我會張貼。

這個想法是選擇具有a,b,c標籤的所有主題,然後用左連接標識也包含d,e,f的主題,然後用where子句搜索找到空那加入...

select distinct topics.id from topics 
inner join topic_tags as t1 
    on (t1.topicFK=topics.id) 
inner join tags as goodtags 
    on(goodtags.id=t1.tagFK and goodtags.name in ('a', 'b', 'c')) 
left join topic_tags as t2 
    on (t2.topicFK=topics.id) 
left join tags as badtags 
    on(badtags .id=t2.tagFK and batags.name in ('d', 'e', 'f')) 
where badtags.name is null; 

完全未經測試,但希望你看看邏輯來自哪裏。

+0

說回這個,我要補充這個答案「的主題具有至少一個,b或c標籤,卻沒有一個d,e或f標籤」 - 但問題是表述爲需要*全部* a,b,c標籤。我看到Marc發佈了一個解決方案,通過巧妙地計數好的標籤來整齊地糾正這個問題。 – 2008-10-09 06:21:11

0

您可以使用minus關鍵字過濾出帶有不需要的標籤的主題。

-- All topics with desired tags. 
select distinct T.* 
from Topics T inner join Topics_Tags R on T.id = R.topicFK 
       inner join Tags U on U.id = R.topic=FK 
where U.name in ('a', 'b', 'c') 

minus 

-- All topics with undesired tags. These are filtered out. 
select distinct T.* 
from Topics T inner join Topics_Tags R on T.id = R.topicFK 
       inner join Tags U on U.id = R.topic=FK 
where U.name in ('d', 'e', 'f') 
+1

MySQL 5.1沒有'minus'關鍵字 – defnull 2008-10-08 23:26:37

5

假設你Topic_Tags表是獨一無二的,這回答您的確切的問題 - 但可能不會推廣到您的實際問題:

SELECT 
    TopicId 
FROM Topic_Tags 
JOIN Tags ON 
    Topic_Tags.TagId = Tags.TagId 
WHERE 
    Tags.Name IN ('A', 'B', 'C', 'D', 'E', 'F') 
GROUP BY 
    TopicId 
HAVING 
    COUNT(*) = 3 
    AND MAX(Tags.Name) = 'C' 

一個更普遍的解決辦法是:

SELECT 
    * 
FROM (
    SELECT 
     TopicId 
    FROM Topic_Tags 
    JOIN Tags ON 
     Topic_Tags.TagId = Tags.TagId 
    WHERE 
     Tags.Name IN ('A', 'B', 'C') 
    GROUP BY 
     TopicId 
    HAVING 
     COUNT(*) = 3 
) as GoodTags 
LEFT JOIN (
    SELECT 
     TopicId 
    FROM Topic_Tags 
    JOIN Tags ON 
     Topic_Tags.TagId = Tags.TagId 
    WHERE 
     Tags.Name = 'D' 
     OR Tags.Name = 'E' 
     OR Tags.Name = 'F' 
) as BadTags ON 
    GoodTags.TopicId = BadTags.TopicId 
WHERE 
    BadTags.TopicId IS NULL 
3

這是另一個替代查詢。也許把好的和不好的標籤列在最上面會更清楚和方便。我在MySQL 5.0上測試了這個。

SELECT t.*, 
    SUM(CASE WHEN g.name IN ('a', 'b', 'c') THEN 1 ELSE 0 END) AS num_good_tags, 
    SUM(CASE WHEN g.name IN ('d', 'e', 'f') THEN 1 ELSE 0 END) AS num_bad_tags 
FROM topics AS t 
JOIN topic_tags AS tg ON (t.id = tg.topicFk) 
JOIN tags AS g ON (g.id = tg.tagFk) 
GROUP BY t.id 
HAVING num_good_tags = 3 AND num_bad_tags = 0; 
+0

更容易:sum(if('a','b','c'),1,0)) 更容易:sum(g.name ('a','b','c')) – ysth 2008-10-09 00:06:11