2

我在“文章”和“标签”之间有 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)

4

2 回答 2

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
于 2009-12-09T01:00:11.810 回答
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;
    

    这个解决方案对某些人来说似乎更具可读性,并且添加值更直接。但是GROUP BYMySQL 中的查询往往会产生一个损害性能的临时表。

  • 另一种解决方案使用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()返回相应组中值的逗号分隔列表。

于 2009-12-09T17:58:53.057 回答