我正在尝试根据是否在另一个表中有多行来从posts表中加载行。采用下表结构:
帖子
post_id post_title
-------------------
1 My Post
2 Another Post
post_tags
post_tag_id post_tag_name
--------------------------
1 My Tag
2 Another Tag
postTags
postTag_id postTag_tag_id postTag_post_id
------------------------------------------
1 1 1
2 2 1
不出所料,post和post_tags存储帖子和标签,postTags加入哪些帖子有哪些标签。
我通常会加入这些表格:
SELECT * FROM (`posts`)
JOIN `postTags` ON (`postTag_post_id` = `post_id`)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)
然后我会有关于标记的信息,并且可以在查询后面添加其他内容来搜索搜索字词等的标记名称,然后在我有匹配搜索字词的帖子后再使用GROUP。
我要做的只是从帖子中同时包含标签1和标签2的帖子中选择,而我无法为其计算出SQL。我认为它需要在实际的JOIN中完成,而不是为它设置一个WHERE子句,因为当我运行上面的连接时,我显然得到两行,所以我不能有像
这样的东西WHERE post_tag_id = 1 AND post_tag_id = 2
因为每行只有一个post_tag_id,我无法在一行中检查同一列的不同值。
我试图做的是这样的事情:
SELECT * FROM (`posts`)
JOIN `postTags` ON (postTag_tag_id = 1 AND postTag_tag_id = 2)
JOIN `post_tags` ON (`post_tag_id` = `postTag_tag_id`)
但是当我运行它时会返回0结果;我之前在类似的事情上已经把这样的条件放在了JOINS中,我确信它已经接近了,但是如果不起作用的话,还不能解决该怎么做。
我至少走在正确的轨道上吗?希望我不会错过一些明显的东西。
感谢。
答案 0 :(得分:2)
你试图让postTags行同时出现一件事。
你需要对post_tags和postTags进行两次连接,这样你才能得到两者。或者你可以说帖子可以在这两个之间有任何标签,标签总数必须等于2(假设一个帖子不能多次与同一个标签相关)。
第一种方法:
SELECT *
FROM `posts` as p
WHERE p.`post_id` IN (SELECT pt.`postTag_post_id`
FROM `postTags` as pt
WHERE pt.`postTag_tag_id` = 1)
AND p.`post_id` IN (SELECT pt.`postTag_post_id`
FROM `postTags` as pt
WHERE pt.`postTag_tag_id` = 2);
第二种方法:
SELECT *
FROM posts as p
WHERE p.post_id IN (SELECT pt.postTag_post_id
FROM (SELECT count(0) as c, pt.postTag_post_id
FROM postTags as pt
WHERE pt.postTag_tag_id IN (1, 2)
GROUP BY pt.postTag_post_id
HAVING c = 2) as pt);
我还想补充一点,如果你在第一种方法中使用IN或EXISTS,那么你就不会因同一个帖子行而有多行,因为你有多个标签。这样您以后可以保存一个DISTINCT,这会使您的查询变慢。 我在第二种方法中使用了IN作为我使用的经验法则:如果你不需要显示数据,则不需要在FROM部分中进行JOIN。
答案 1 :(得分:1)
SELECT p.*, t1.*, t2.* FROM posts p
INNER JOIN postTags pt1 ON pt1.postTag_post_id = p.id AND pt1.postTag_tag_id = 1
INNER JOIN postTags pt2 ON pt2.postTag_post_id = p.id AND pt2.postTag_tag_id = 2
INNER JOIN post_tags t1 ON t1.post_tag_id = pt1.postTag_tag_id
INNER JOIN post_tags t2 ON t2.post_tag_id = pt2.postTag_tag_id
答案 2 :(得分:0)
如果没有实际构建与您的数据库相同的数据库,则很难验证,但应该工作。
首先我要说的是,在支持分析查询的数据库(Oracle,MS SQL Server)中,这种类型的查询更容易,性能也更高。因此,在MySQL中,你必须采用旧的,糟糕的,聚合的方式。
我还想说,在post_tags中有一个存储标签名称的表,然后将post标签映射到postTags中的帖子是令人困惑的。如果是我,我会将映射表的名称更改为post_tags_map或post_tags_to_post_map。所以你的帖子有post_id,post_tags有post_tags_id,post_tags_map有post_tags_map_id。并且这些id列在每个表中都被命名为相同。具有在其他表中以不同方式命名的相同列也令人困惑。
无论如何,让我们解决你的问题。 首先,您需要一个每行1个帖子ID的结果集,并且只有具有标签1&的帖子。 2。
select postTag_post_id, count(1) cnt from (
select postTag_post_id from postTags where postTag_tag_id in (1, 2)
) group by postTag_post_id;`
这应该会给你这样的数据:
postTag_post_id | cnt
1 | 2
然后,您可以将该结果集加入您的帖子表。
select * from posts p,
(
select postTag_post_id, count(1) cnt from (
select postTag_post_id from postTags where postTag_tag_id in (1, 2)
) group by postTag_post_id;
) t
where p.post_id = t.postTag_post_id
and t.cnt >= 2;
如果你需要另外加入post_tags表以便从post_tag_name获取postTag_tag_id,那么你最内层的查询会改变如下:
select postTag_post_id
from postTags a,
post_tags b
where a.postTag_tag_id = b.post_tag_id
and b.post_tag_name in ('tag 1', 'tag 2');
这应该可以解决问题。
答案 3 :(得分:0)
假设您已经知道标记ID(1
和2
),您可以执行以下操作:
SELECT post_id, post_title
FROM posts JOIN postTags ON (postTag_post_id = post_id)
WHERE postTag_tag_id IN (1, 2)
GROUP BY post_id, post_title
HAVING COUNT(DISTINCT postTag_tag_id) = 2
注意:如果postTags {postTag_tag_id, postTag_post_id}
上有替代密钥,则不需要DISTINCT。
注意:如果您没有标签ID(并且只有标签名称),则需要另一个JOIN(朝post_tags
表)。
postTags.postTag_id
)中抛弃代理PK并且只有自然的PK {postTag_tag_id, postTag_post_id}
。 InnoDB tables are clustered,并且群集表中的二级索引比基于堆的表更胖且更慢。此外,一些查询可以受益于将相同标签标记的帖子物理地靠近在一起(或者如果您反转PK,则将相同帖子的标签紧密存储在一起)。