MySQL选择多对多的位置

Posted

技术标签:

【中文标题】MySQL选择多对多的位置【英文标题】:MySQL Select Where In Many to Many 【发布时间】:2014-04-22 22:38:03 【问题描述】:

我遇到了 SQL 查询问题。我的架构描述了articles 表中的文章和categories 表中的类别之间的多对多关系——中间表article_category 具有idarticle_idcategory_id 字段。

我想选择所有只有 ID 为 12 的类别的文章。不幸的是,除了其他任何类别之外,此查询还将选择具有这些类别的任何文章。

例如,这是来自 SQL 的示例输出(出于描述目的显示类别)。您可以看到,虽然查询选择了 id 为 10 的文章,但它也选择了 id 为 11 的文章,尽管有一个额外的类别。

+-------+------------+
| id    | categories |
+-------+------------+
| 10    | 1,2        |
| 11    | 1,2,3      |
+-------+------------+

这是我希望通过选择仅包含12类别的文章来实现的输出。

+-------+------------+
| id    | categories |
+-------+------------+
| 10    | 1,2        |
+-------+------------+

同样,这是我希望通过选择仅包含 123 类别的文章来实现的输出。

+-------+------------+
| id    | categories |
+-------+------------+
| 11    | 1,2,3      |
+-------+------------+

这是我写的 SQL。为了实现上述目标,我缺少什么?

SELECT articles.id
FROM articles
WHERE EXISTS (
    SELECT 1
    FROM article_category
    WHERE articles.id = article_id AND category_id IN (1,2)
    GROUP BY article_id
)

非常感谢!

【问题讨论】:

看下面的问题。对我来说似乎是同样的问题***.com/questions/8468117/… 【参考方案1】:

假设您想要的不仅仅是文章的 id:

SELECT a.id
      ,a.other_stuff
  FROM articles a
  JOIN article_category ac
    ON ac.article_id = a.id
GROUP BY a.id
HAVING GROUP_CONCAT(DISTINCT ac.category_id ORDER BY ac.category_id SEPARATOR ',') = '1,2'

如果你想要的只是文章的 ID,那么试试这个:

SELECT article_id
  FROM article_category 
GROUP BY article_id
HAVING GROUP_CONCAT(DISTINCT category_id ORDER BY category_id SEPARATOR ',') = '1,2'

在http://sqlfiddle.com/#!2/9d213/4查看它的实际应用

还应该补充一点,这种方法的优点是它可以支持检查任意数量的类别,而无需更改查询。只需将 '1,2' 设为字符串变量并更改传递给查询的内容。因此,您可以通过传递 '1,2,7' 字符串轻松搜索类别 1、2 和 7 的文章。不需要额外的连接。

【讨论】:

我不想粗鲁,但这与我的回答有什么不同? 几个不同之处。 1) 不存在。 2)没有子选择。 3)不假设 category_id 不重复(可能是一个安全的假设,但 DISTINCT 消除了它)。 4) 不假设 category_id 将在数据库中按升序排列(ORDER BY 消除了该假设)。 5) 如果只需要文章 ID,则无需 JOIN。【参考方案2】:

您可以在 category.id 上留下加入 category_id 然后 GROUP_CONCAT 以获取所有类别,就像您在解释(第一个表)中写的那样,而不是使用 HAVING 与您喜欢的任何集合匹配('1,2 '来自示例)

同样使用这种方法,您可以轻松地使用 php 或任何其他语言使此查询动态化

SELECT articles.id
FROM articles
WHERE EXISTS (
    SELECT GROUP_CONCAT(c.id) AS grp
    FROM article_category
    LEFT OUTER JOIN categories AS c ON c.id = article_category.category_id
    WHERE articles.id = article_id
    GROUP BY article_id
    HAVING grp = '1,2'
)

【讨论】:

这可能需要一个解释:-)。【参考方案3】:

请使用下面的查询您可以使用简单查询来做这件事。

   SELECT a.id, a.name
    FROM articles a, categories c, articles_categories ac 
    WHERE
    a.id = ac.article_id AND c.id = ac.category_id 
    AND c.id = 1 OR c.id = 2;

注意- 如果两个表之间有多对多关系从 article_category 表中删除 ID 并使用 article_id 和 category_id 制作复合主键。

谢谢。

【讨论】:

【参考方案4】:

也许是这样的:

select distinct article_id from article_cathegory 
      where category_id in (1,2)
minus 
select distinct article_id from article_cathegory 
      where category_id not in (1,2)

【讨论】:

【参考方案5】:

看起来简单的解决方案可能如下:

SELECT 
    ac.article_id
    , SUM(ac.category_id IN (1, 2)) AS nb_categories
    , COUNT(ac.category_id) AS nb_all_categories
FROM
    article_categories ac
GROUP BY
    ac.article_id
HAVING
    nb_categories=2 AND nb_all_categories=2

在这里,我计算了我们有多少个必需的类别,也计算了我们总共有多少个类别。我们只需要 2 个类别,因此 required 和 total 都应该等于 2。

这非常灵活,添加更多类别只需更改 HAVING 语句中的类别列表和数字。

【讨论】:

【参考方案6】:
SELECT articles.id
FROM articles
INNER JOIN articles_category ac ON articles.id = ac.article_id
WHERE articles.id IN (
     SELECT ac1.article_id
     FROM article_category ac1
     WHERE ac1.category_id = 1;
     )
AND ac.article_id = 2;
AND articles.id NOT IN (
     SELECT ac2.article_id
     FROM article_category ac2
     WHERE ac2.category_id NOT IN (1, 2)
     )

远非我写的最漂亮的。

基本上,它首先限制类别 id 为 1 的 ID,然后确保记录的类别也为 2,最后,它确保它没有任何其他类别

【讨论】:

【参考方案7】:

我喜欢使用group byhaving 来处理这些查询。这是一个例子:

select ac.article_id
from article_category ac
group by ac.article_id
having sum(case when category_id = 1 then 1 else 0 end) > 0 and
       sum(case when category_id = 1 then 2 else 0 end) > 0;

having 子句中的每个条件都在测试其中一个类别是否存在。

我发现这种方法对于回答许多不同类型的“集合内集合”问题最为灵活。

编辑:

上面的细微变化可能更容易生成:

having sum(category_id in (1, 2, 3)) = count(*) and
       count(*) = 3

假设数据中没有重复项,这将起作用。您需要将3 更新为in 列表中的项目数。

【讨论】:

对于两个以上类别的实例来说,这有多容易?我需要能够使用脚本动态生成此查询。 @GeorgeRobinson 。 . .您只需在having 子句中添加另一个条件。这种方法的优点是可以灵活地描述纳入和排除标准。【参考方案8】:

在不改变查询的情况下提供帮助,我认为逻辑存在错误。您不想要存在类别 1,2 的文章。您需要不存在不同于 1 和 2 的类别的文章。 谢谢和问候

【讨论】:

【参考方案9】:

在 SQL Server 中,我会使用 INTERSECT 和 EXCEPT。在 mysql 中,试试这个:

SELECT DISTINCT article_id
  FROM article_category
  WHERE category_id=1
AND article_id IN
(SELECT article_id
  FROM article_category
  WHERE category_id=2)
AND article_id NOT IN
(SELECT article_id
  FROM article_category
  WHERE category_id NOT IN (1,2))

【讨论】:

【参考方案10】:

使用此 SQL 查询。

SELECT articles.id
FROM articles
WHERE articles.id in (
    SELECT *
    FROM article_category,articles
    WHERE article_category.articles.id = articles.article_id AND article_category.category_id IN (1,2)
)

【讨论】:

以上是关于MySQL选择多对多的位置的主要内容,如果未能解决你的问题,请参考以下文章

MySql 学习三(多对多)

选择多对多的关系

MYSQL - 多个多对多选择

如何编写多对多的sql脚本

查询多对多关系 - mySQL

JPQL 多对多 Dto 选择失败