2010-04-27 136 views
3

我有一個問題,我不知道什麼是更好的解決方案。 好吧,我有2個表格:帖子(id,title),posts_tags(post_id,tag_id)。 我有下一個任務:必須選擇具有標記id的帖子,例如4,10和11. 不完全一樣,帖子可以同時具有任何其他標籤。 那麼,我怎麼能做到更優化?在每個查詢中創建臨時表?或者可能是某種存儲過程? 將來,用戶可以要求腳本選擇任意數量的標籤(它可以是1個標籤,也可以是10個),並且我必須確定我選擇的方法對於我的問題是最好的方法。 對不起,我的英語,thx的注意力。多對多查詢

回答

1
select id, title 
from posts p, tags t 
where p.id = t.post_id 
and tag_id in (4,10,11) ; 

+0

它可以用標籤4或標籤10或標籤11返回帖子。但是我需要在一篇文章中完全列出所有這三個標籤。 問題在這裏:) – user52005 2010-04-28 04:11:03

0

這是行不通的?

select * 
from posts 
where post.post_id in 
    (select post_id 
    from post_tags 
    where tag_id = 4 
    and post_id in (select post_id 
        from post_tags 
        where tag_id = 10 
        and post_id in (select post_id 
            from post_tags 
            where tag_id = 11))) 
3

此解決方案假定(POST_ID,TAG_ID)在post_tags強制執行是唯一的:

SELECT id, title FROM posts 
    INNER JOIN post_tag ON post_tag.post_id = posts.id 
    WHERE tag_id IN (4, 6, 10) 
    GROUP BY id, title 
    HAVING COUNT(*) = 3 

雖然它不適合所有可能的標籤組合的解決方案,可以很容易地創建動態SQL。要更改其他標記集,請將IN()列表更改爲具有所有標記,並更改COUNT(*)=以檢查指定的標記數。這種解決方案將一些JOIN級聯在一起的優點是,當您更改請求時,不必添加JOIN,甚至不需要額外的WHERE條件。

+0

+1對於使用GROUP BY與HAVING。 – Joop 2010-04-28 13:58:34

0

您可以通過存儲按字母順序排列的帖子標籤名稱的單向散列進行時間存儲折衷。

當帖子被標記時,執行select t.name from tags t inner join post_tags pt where pt.post_id = [ID_of_tagged_post] order by t.name。連接所有標記名稱,使用MD5算法創建一個散列,並將該值插入到帖子旁邊的列中(或者如果您願意,可將其插入另一個由外鍵連接的表中)。

當您想要搜索特定的標籤組合時,只需執行(記住對標籤名稱進行排序)select from posts p where p.taghash = MD5([concatenated_tag_string])

0

這將選擇具有標籤(4,10,11)的任何所有帖子:

select distinct id, title from posts 
where exists ( 
    select * from posts_tags 
    where 
    post_id = id and 
    tag_id in (4, 10, 11)) 

或者您可以使用此:

select distinct id, title from posts 
join posts_tags on post_id = id 
where tag_id in (4, 10, 11) 

(兩者都會優化同樣的方式)。

這將選擇具有所有標籤(4,10,11)的所有帖子:

select distinct id, title from posts 
where not exists ( 
    select * from posts_tags t1 
    where 
    t1.tag_id in (4, 10, 11) and 
    not exists (
     select * from posts_tags as t2 
     where 
     t1.tag_id = t2.tag_id and 
     id = t2.post_id)) 

in子句中的標籤列表就是動態改變(在所有情況下)。

不過,這最後詢問是不是真快,所以你可以使用這樣的事情,而不是:

create temporary table target_tags (tag_id int); 
insert into target_tags values(4),(10),(11); 
select id, title from posts 
    join posts_tags on post_id = id 
    join target_tags on target_tags.tag_id = posts_tags.tag_id 
    group by id, title 
    having count(*) = (select count(*) from target_tags); 
drop table target_tags; 

改變動態現在是在第二個語句(插入)的一部分。

+0

這將選擇帶有1,2或3個期望標籤的帖子,而不是全部三個。如果以JOIN表示,它會更清晰地寫入(並且執行得更快)。 – 2010-04-28 15:31:22

+0

我添加了代碼來選擇包含所有標籤的帖子。 – 2010-04-28 15:56:04

+0

我還添加了第一個案例的連接代碼。雖然,一個體面的查詢優化器會將它與帶有exists子句的查詢相同對待。 – 2010-04-28 16:24:12