多对多查询

时间:2010-04-27 20:37:57

标签: sql mysql database-design many-to-many

我有一个问题,我不知道什么是更好的解决方案。 好的,我有2个表:帖子(id,title),posts_tags(post_id,tag_id)。 我有下一个任务:必须选择带有标签ID的帖子,例如4,10和11。 不完全是,post可以同时拥有任何其他标签。 那么,我怎么能更优化呢?在每个查询中创建临时表?或者可能是某种存储过程? 将来,用户可以要求脚本选择任何标签数量的帖子(它可能只有1个标签或10个同时),我必须确保我选择的方法是解决我问题的最佳方法。 对不起我的英文,请注意。

5 个答案:

答案 0 :(得分:3)

此解决方案假定post_tags中的(post_id,tag_id)强制为UNIQUE:

 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术语。

答案 1 :(得分:1)

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

答案 2 :(得分: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 :(得分: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])

答案 4 :(得分: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;

动态变化的部分现在位于第二个语句(插入)中。