我在'articles'和'tags'之间有HABTM关系
问题:我只是在寻找带有“体育”和“户外”标签的文章,而不是仅包含其中一个标签的文章。
我试过了:
SELECT DISTINCT article.id, article.name FROM articles
inner JOIN tags ON (tags.name IN ('outdoors', 'sports')
inner JOIN articles_tags ON articles_tags.article_id = article.id AND articles_tags.tag_id = tags.id
...但它让我得到的文章只有运动,只有户外运动+户外运动
问题使用什么是正确的查询? (我正在使用MySQL)
答案 0 :(得分:1)
试试这个:
SELECT a1.id, a1.name FROM articles a1
JOIN tags t1 ON t1.name ='outdoors'
JOIN articles_tags at1 ON at1.article_id = a1.id AND at1.tag_id = t1.id
JOIN tags t2 ON t2.name = 'sports'
JOIN articles_tags at2 ON at2.article_id = a1.id AND at2.tag_id = t2.id
答案 1 :(得分:0)
有两种常见的解决方案。
第一个解决方案使用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;
此解决方案对某些人来说更具可读性,添加值更为直接。但是MySQL中的GROUP BY
查询往往会产生一个损害性能的临时表。
另一个解决方案使用每个不同标记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
个查询修饰符。
这种类型的查询倾向于在MySQL上优于GROUP BY
解决方案,假设您已经定义了适当的索引。
在评论中重新提出你的后续问题,我会做这样的事情:
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()
返回相应组中值的逗号分隔列表。