MySQL选择多对多的位置
Posted
技术标签:
【中文标题】MySQL选择多对多的位置【英文标题】:MySQL Select Where In Many to Many 【发布时间】:2014-04-22 22:38:03 【问题描述】:我遇到了 SQL 查询问题。我的架构描述了articles
表中的文章和categories
表中的类别之间的多对多关系——中间表article_category
具有id
、article_id
和category_id
字段。
我想选择所有只有 ID 为 1
和 2
的类别的文章。不幸的是,除了其他任何类别之外,此查询还将选择具有这些类别的任何文章。
例如,这是来自 SQL 的示例输出(出于描述目的显示类别)。您可以看到,虽然查询选择了 id 为 10
的文章,但它也选择了 id 为 11
的文章,尽管有一个额外的类别。
+-------+------------+
| id | categories |
+-------+------------+
| 10 | 1,2 |
| 11 | 1,2,3 |
+-------+------------+
这是我希望通过选择仅包含1
和2
类别的文章来实现的输出。
+-------+------------+
| id | categories |
+-------+------------+
| 10 | 1,2 |
+-------+------------+
同样,这是我希望通过选择仅包含 1
、2
和 3
类别的文章来实现的输出。
+-------+------------+
| 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 by
和having
来处理这些查询。这是一个例子:
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选择多对多的位置的主要内容,如果未能解决你的问题,请参考以下文章