【问题标题】:Get only available tags from a many to many relation with MySQL query仅从与 MySQL 查询的多对多关系中获取可用标签
【发布时间】:2020-01-01 06:35:45
【问题描述】:

我有三个具有多对多关系的表; mediarelationstags

      Media Table                      Relations Table                   Tags Table

| media-id | media-name |       | rel-id | media-id | tag-id |       | tag-id | tag-name|
-------------------------       ------------------------------       --------------------
| 1        | "Media 1"  |       | 1      | 1        | 1      |       | 1      | "tag 1" |
| 2        | "Media 2"  |       | 2      | 2        | 2      |       | 2      | "tag 2" |
                                | 3      | 1        | 3      |       | 3      | "tag 3" |
                                | 4      | 2        | 3      |       | 4      | "tag 4" |
                                | 5      | 1        | 4      |

我需要一个 MySQL 查询,当给出一个或多个标签时,它将返回所有其他可能的标签组合。这里有几个例子。

示例 1

鉴于: tag 1

返回: tag 1tag 3tag 4

因为: tag 1 正在标记 Media 1,因此它应该返回其所有标记(tag 1tag 3tag 4


示例 2

鉴于: tag 3

返回:返回tag 1tag 2tag 3tag 4

因为: tag 3 同时标记了Media 1Media 2,所以它们的所有标记都会返回(tag 1tag 2tag 3tag 4

示例 3

给定: tag 2tag 3

返回: tag 2tag 3

因为: tag 3 标签 两个 Media 1Media 2,但只有 Media 2tag 2,所以只是返回它的标签和 @987654355 @的标签被排除在外

我已经搜索了一段时间,但我没有找到任何与弧形答案相关的东西。我能得到的最接近的是这里的查询:

SELECT `tags`.`tag-name`
FROM `tags`
WHERE `tags`.`tag-id` IN (
    SELECT `relations`.`tag-id`
    FROM `relations`
    WHERE `relations`.`media-id` IN (
        SELECT `relations`.`media-id`
        FROM `relations`
        WHERE `relations`.`tag-id` IN (
            SELECT `tags`.`id`
            FROM `tags`
            WHERE `tags`.`tag-name` IN ('tag 1')
        )
    )
)

这很好用,也许不是最有效的,但我能理解。

唯一的问题是如果我在示例 3 上运行它,它将返回所有标签,而不是从 Media 1 中排除标签。

有没有办法改变我的 MySQL 查询来做到这一点?

【问题讨论】:

  • “媒体 1”也未使用“标签 4”进行标记。你的问题没有意义。
  • @GordonLinoff,对不起,我是按表格编辑的

标签: mysql sql


【解决方案1】:

您的问题的一般解决方案涉及表和聚合之间的多个连接,如下所示:

select t2.tag_name
from tags t
inner join relations r on r.tag_id = t.tag_id 
inner join media m on m.media_id = r.media_id 
inner join relations r2 on r2.media_id = m.media_id
inner join tags t2 on t2.tag_id = r2.tag_id
where t.tag_name in ('tag 2', 'tag 3')
group by t2.tag_name
having count(distinct t.tag_id) = 2

这个:

('tag 2', 'tag 3')

是您希望查询获取结果的标签列表。
以及HAVING子句中条件右侧的数字:

count(distinct t.tag_id) = 2

是该列表中的项目数。
请参阅demo
在只有 1 个查询标签的情况下,代码可以简化为:

select t2.tag_name
from tags t
inner join relations r on r.tag_id = t.tag_id 
inner join media m on m.media_id = r.media_id 
inner join relations r2 on r2.media_id = m.media_id
inner join tags t2 on t2.tag_id = r2.tag_id
where t.tag_name = 'tag 1'
group by t2.tag_name

【讨论】:

  • 是的,因为您必须重新加入关系和标签才能获得所需的结果。在我编辑的答案中查看演示。
【解决方案2】:

如果您想要不用于“media_id = 1”的标签,那么not exists就会出现:

select t.*
from tags t
where not exists (select 1
                  from relations r join
                       media m
                       on r.media_id = m.media_id
                  where m.media_name = 'Media 1' and
                        r.tag_id = t.tag_id
                 );

【讨论】:

  • 谢谢,你能解释一下trm吗? tagsrelationsmedia?
  • 所以如果trm 是缩写,这是否会过滤所有Media 1 的标签ID?如果是这样,我不确定如何在我的查询中实现它,因为我从不输入Media 1,而不是标签名称。
  • @JackStoller 。 . . trm 是表别名,是 SQL 语言的基本部分。至于“Media 1”,如果你不把它放在查询中,那我就不明白这个问题了。
  • 我从来不知道,我对 MySQL 还是很陌生。我可以看到很多应用程序。抱歉,如果不清楚。我已经编辑了我的问题并添加了一些应该有帮助的示例。
猜你喜欢
  • 1970-01-01
  • 2012-02-26
  • 2011-03-17
  • 1970-01-01
  • 2019-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多