HABTM 查询帮助

发布于 2024-08-13 18:21:56 字数 441 浏览 1 评论 0原文

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

I have a HABTM relationship between 'articles' and 'tags'

Problem: I'm only looking for articles with BOTH the tag 'sports' and 'outdoors' but not articles with only one of these tags.

I tried this:

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

...but it gets me articles that are in only sports, only outdoors AND both sports + outdoors

Question what is the right query to use? (I'm using MySQL)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好多鱼好多余 2024-08-20 18:21:56

试试这个:

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

Try this:

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
独孤求败 2024-08-20 18:21:56

有两种常见的解决方案。

  • 第一个解决方案使用 GROUP BY 来计算每篇文章与“outdoors”或“sports”匹配的标签,然后仅返回具有这两个标签的组。

    SELECT a.id, a.name
    FROM 文章 AS a
    INNER JOINarticles_tags AS at ON (a.id = at.article_id)
    INNER JOIN 标签 AS t ON (t.id = at.tag_id)
    WHERE t.name IN ('户外', '运动')
    按 a.id 分组
    HAVING COUNT(DISTINCT t.name) = 2;
    

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

  • 另一个解决方案对每个不同的标签使用JOIN。通过使用内部联接,查询自然会限制为与您指定的所有标签相匹配的文章。

    SELECT a.id, a.name
    FROM 文章 AS a
    INNER JOINarticles_tags AS at1 ON (a.id = at1.article_id)
    INNER JOIN 标签 AS t1 ON (t1.id = at1.tag_id AND t1.name = 'outdoors')
    内连接articles_tags AS at2 ON (a.id = at2.article_id)
    INNER JOIN 标签 AS t2 ON (t2.id = at2.article_id AND t2.name = 'sports');
    

    假设 tags.namearticles_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()返回相应组中的值的逗号分隔列表。

There are two common solutions.

  • The first solution uses GROUP BY to count the tags per article that match 'outdoors' or 'sports' and then returns only the groups that have both tags.

    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;
    

    This solution appears more readable to some people, and adding values is more straightforward. But GROUP BY queries in MySQL tend to incur a temporary table which harms performance.

  • The other solution uses a JOIN per distinct tag. By using inner joins, the query naturally restricts to articles that match all the tags you specify.

    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');
    

    Assuming tags.name and articles_tags.(article_id,tag_id) both have UNIQUE constraints, you shouldn't need a DISTINCT query modifier.

    This type of query tends to optimize better on MySQL than the GROUP BY solution, assuming you have defined appropriate indexes.


Re your followup question in the comment, I would do something like this:

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;

This still only finds articles that have both tags 'outdoors' and 'sports', but then it further joins these articles to all its tags.

This will return multiple rows per article (one for each tag) so we then use GROUP BY to reduce down to a single row per article again. GROUP_CONCAT() returns a comma-separated list of the values in the respective group.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文