加入多行

时间:2012-08-26 11:07:13

标签: mysql sql join

我正在尝试根据是否在另一个表中有多行来从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中,我确信它已经接近了,但是如果不起作用的话,还不能解决该怎么做。

我至少走在正确的轨道上吗?希望我不会错过一些明显的东西。

感谢。

4 个答案:

答案 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(12),您可以执行以下操作:

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,则将相同帖子的标签紧密存储在一起)。