2010-04-27 109 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