Sql 多对多查询
我有一个问题,我不知道什么是更好的解决办法。 好的,我有两个表:posts(id,title),posts\u标签(post\u id,tag\u id)。 我有下一个任务:必须选择带有标记ID的帖子,例如4、10和11。 不完全是这样,post可以同时具有任何其他标记。 那么,我怎样才能做得更优化呢?在每个查询中创建临时表?或者可能是某种存储过程? 将来,用户可以要求脚本选择带有任意数量标签的帖子(可以是1个标签,也可以是10个标签),我必须确保我选择的方法是解决问题的最佳方法。 对不起,我的英语,谢谢关注Sql 多对多查询,sql,mysql,database-design,many-to-many,Sql,Mysql,Database Design,Many To Many,我有一个问题,我不知道什么是更好的解决办法。 好的,我有两个表:posts(id,title),posts\u标签(post\u id,tag\u id)。 我有下一个任务:必须选择带有标记ID的帖子,例如4、10和11。 不完全是这样,post可以同时具有任何其他标记。 那么,我怎样才能做得更优化呢?在每个查询中创建临时表?或者可能是某种存储过程? 将来,用户可以要求脚本选择带有任意数量标签的帖子(可以是1个标签,也可以是10个标签),我必须确保我选择的方法是解决问题的最佳方法。 对不起,我的
select id, title
from posts p, tags t
where p.id = t.post_id
and tag_id in ( 4,10,11 ) ;
这行吗
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)))
此解决方案假定post_标记中的(post_id,tag_id)是唯一的:
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(*)更改为检查指定的标记数。与将一组联接级联在一起相比,此解决方案的优点是,当您更改请求时,不必添加联接,甚至不必添加额外的WHERE术语。您可以通过存储按字母顺序排序的帖子标记名的单向散列来进行时间存储权衡 当贴子被标记时,执行
从标签t内部连接贴子标签pt中选择t.name,其中pt.post\u id=[id\u of\u taged\u post]按t.name排序
。连接所有标记名,使用MD5算法创建一个散列,并将该值插入post旁边的一列(或者插入另一个由外键连接的表,如果愿意的话)
当您想要搜索特定的标记组合时,只需执行(记住对标记名称进行排序)
从posts p中选择,其中p.taghash=MD5([concatenated_tag_string])
这将选择具有任何标记(4、10、11)的所有posts:
或者,您可以使用:
select distinct id, title from posts
join posts_tags on post_id = id
where tag_id in (4, 10, 11)
(两者将以相同的方式进行优化)
这将选择具有所有标记(4、10、11)的所有帖子:
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;
动态更改的部分现在位于第二条语句(insert)中。它可以返回带有标记4、标记10或标记11的帖子。但我需要在一篇文章中包含这三个标签。问题是:)这将选择带有1、2或3个所需标记的帖子,而不是全部三个。如果将其表示为联接,则会编写得更清晰(执行速度更快)。我还为第一种情况添加了联接代码。尽管如此,一个合适的查询优化器会将其与带有exists子句的查询进行相同的处理。
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))
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;