问题
I'm having trouble with a SQL query. My schema describes a many to many relationship between articles in the articles
table and categories in the categories
table - with the intermediate table article_category
which has an id
, article_id
and category_id
field.
I want to select all articles which only have the categories with id 1
and 2
. Unfortunately this query will also select any articles which have those categories in addition to any others.
For example, this is a sample output from the SQL (with categories shown for descriptive purposes). You can see that while the query selected the article with id of 10
, it also selected the article with an id of 11
despite having one extra category.
+-------+------------+
| id | categories |
+-------+------------+
| 10 | 1,2 |
| 11 | 1,2,3 |
+-------+------------+
This is the output that I want to achieve from selecting articles with only categories 1
and 2
.
+-------+------------+
| id | categories |
+-------+------------+
| 10 | 1,2 |
+-------+------------+
Likewise, this is the output what I want to achieve from selecting articles with only categories 1
, 2
and 3
.
+-------+------------+
| id | categories |
+-------+------------+
| 11 | 1,2,3 |
+-------+------------+
This is the SQL I have written. What am I missing to achieve the above?
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
)
Many thanks!
回答1:
Assuming you want more than just the article's 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'
If all you want is the article's id then try this:
SELECT article_id
FROM article_category
GROUP BY article_id
HAVING GROUP_CONCAT(DISTINCT category_id ORDER BY category_id SEPARATOR ',') = '1,2'
See it in action at http://sqlfiddle.com/#!2/9d213/4
Should also add that the advantage of this approach is that it can support the checking of any number of categories without having to change the query. Just make '1,2' a string variable and change what gets passed into the query. So, you could just as easily search for articles with categories 1, 2, and 7 by passing a string of '1,2,7'. No additional joins are needed.
回答2:
You can left join category_id
on category.id
and then GROUP_CONCAT to get all categories, like you wrote in explanation (1st table) and than using HAVING
match with any set you like ( '1,2' from example)
also with that approach you can easily make this query dynamic with php or any other language
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:
Please Use Below Query You can do the Thing by Using Simple Query.
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;
NOTE- If You have Many to many Relationship between two Tables Remove ID from the article_category table and make composite primary key Using article_id and category_id.
Thank you.
回答4:
Maybe, something like:
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:
Looks like simple solution for this could be the following:
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
Here I count how many required categories we have and also count how many categories we have in total. We only need exactly 2 categories, so both required and total should be equal to 2.
This is quite flexible and for adding more categories just change categories list and numbers in HAVING statement.
回答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)
)
Far from the prettiest one I have written.
Basically, it limits first by ID that have a category id of 1, then it makes sure the records have a category of 2 as well, finally, it makes sure that it does not have any other categories
回答7:
I like to approach these queries using group by
and having
. Here is an example:
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;
Each condition in the having
clause is testing for the presence of one of the categories.
I find that this approach is the most flexible for answering many different types of "set-within-sets" problems.
EDIT:
A slight variation on the above might be easier to generate:
having sum(category_id in (1, 2, 3)) = count(*) and
count(*) = 3
This will work assuming there are no duplicates in the data. You need to update the 3
to be the number of items in the in
list.
回答8:
to help just without changing your query very much, i think the logic has a bug. you dont want articles where a categegory 1,2 exists. You need articles where does not exist categories different than 1 and 2. thanks & regards
回答9:
In SQL Server I would do it with an INTERSECT and an EXCEPT. In MySQL, try this:
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:
Use this SQL query.
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)
)
来源:https://stackoverflow.com/questions/23231549/mysql-select-where-in-many-to-many