MySQL选择多对多的地方

时间:2014-04-22 22:38:04

标签: mysql sql many-to-many

我遇到了SQL查询问题。我的模式描述了articles表中的文章与categories表中的类别之间的多对多关系 - 中间表article_category具有id,{{1 }和article_id字段。

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

例如,这是SQL的示例输出(,为了描述目的显示了类别)。您可以看到,当查询选择了ID为2的文章时,它还选择了ID为10的文章,尽管有一个额外的类别。

11

这是我要通过选择仅包含类别+-------+------------+ | id | categories | +-------+------------+ | 10 | 1,2 | | 11 | 1,2,3 | +-------+------------+ 1的文章来实现的输出。

2

同样,这是我想要选择仅包含类别+-------+------------+ | id | categories | +-------+------------+ | 10 | 1,2 | +-------+------------+ 12的文章的输出。

3

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

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

非常感谢!

10 个答案:

答案 0 :(得分:3)

假设您想要的不仅仅是文章的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 :(得分:1)

您可以在category_id然后GROUP_CONCAT上加入category.id以获取所有类别,就像您在解释中所写的那样(第1张表格),而不是使用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'
)

答案 2 :(得分:1)

请使用以下查询您可以使用简单查询执行操作。

   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创建复合主键。

谢谢。

答案 3 :(得分:0)

我希望使用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子句中的每个条件都在测试是否存在其中一个类别。

我发现这种方法最灵活,可以回答许多不同类型的" set-within-sets"问题。

编辑:

上述内容略有不同可能更容易生成:

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

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

答案 4 :(得分:0)

也许,比如:

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 :(得分:0)

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

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.

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

答案 6 :(得分:0)

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 :(得分:0)

帮助而不是非常改变你的查询,我认为逻辑有一个bug。你不想要存在categegory 1,2的文章。您需要的文章不存在不同于1和2的类别。 谢谢&问候

答案 8 :(得分:0)

在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))

答案 9 :(得分:0)

使用此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)
)