有两种常见的解决方案。
第一个解决方案用于GROUP BY
计算每篇文章匹配“户外”或“运动”的标签,然后仅返回具有这两个标签的组。
SELECT a.id, a.name
FROM articles AS a
INNER JOIN articles_tags AS at ON (a.id = at.article_id)
INNER JOIN tags AS t ON (t.id = at.tag_id)
WHERE t.name IN ('outdoors', 'sports')
GROUP BY a.id
HAVING COUNT(DISTINCT t.name) = 2;
这个解决方案对某些人来说似乎更具可读性,并且添加值更直接。但是GROUP BY
MySQL 中的查询往往会产生一个损害性能的临时表。
另一种解决方案使用JOIN
每个不同的标签。通过使用内连接,查询自然会限制为与您指定的所有标签匹配的文章。
SELECT a.id, a.name
FROM articles AS a
INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id)
INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors')
INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id)
INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports');
假设tags.name
两者articles_tags.(article_id,tag_id)
都有UNIQUE
约束,则不需要DISTINCT
查询修饰符。
GROUP BY
假设您定义了适当的索引,这种类型的查询在 MySQL 上的优化往往比解决方案更好。
在评论中重新提出您的后续问题,我会做这样的事情:
SELECT a.id, a.name, GROUP_CONCAT(t3.tag) AS all_tags
FROM articles AS a
INNER JOIN articles_tags AS at1 ON (a.id = at1.article_id)
INNER JOIN tags AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors')
INNER JOIN articles_tags AS at2 ON (a.id = at2.article_id)
INNER JOIN tags AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports');
INNER JOIN articles_tags AS at3 ON (a.id = at3.article_id)
INNER JOIN tags AS t3 ON (t3.id = at3.article_id);
GROUP BY a.id;
这仍然只找到同时具有标签“户外”和“运动”的文章,但它会进一步将这些文章加入其所有标签。
这将返回每篇文章的多行(每个标签一个),因此我们然后使用GROUP BY
它再次减少到每篇文章的一行。 GROUP_CONCAT()
返回相应组中值的逗号分隔列表。