【问题标题】:MySQL Select Where In Many to ManyMySQL选择多对多的位置
【发布时间】: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
)

非常感谢!

【问题讨论】:

标签: mysql sql many-to-many


【解决方案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)
              )
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-11-14
                • 2021-10-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-12-24
                • 1970-01-01
                • 2011-03-01
                相关资源
                最近更新 更多