Reputation: 23
I store some data in MySQL and want to filter out rows that match a given criteria.
Sounds easy, but it is not since there are some join criteria involved.
I do have the following tables:
items : id, ...
genres: id, name:varchar, item_id
Each item has multiple genres.
The query should filter out items if at least one genre does match a given genre name (or a set of names).
For example:
Item with id 1 has 3 genres
- genre name = 'foo'
- genre name = 'bar'
- genre name = 'baz'
Item 1 may not be part of the result set if the given genre name is 'bar', ['bar', 'baz', 'xyz'], etc.
I tried to left join the genres on the items and applied a WHERE statement with "genres.name NOT IN (?)". ? is the given set of genre names.
This (of course) only works for items with exactly one genre. The same could be achieved by multiple WHERE conditions: WHERE name <> 'a' AND name <> 'b' ...
Any ideas how to get this query done properly?
Thanks in advance!
Upvotes: 1
Views: 7705
Reputation: 1482
select * from items i where not exists
(select '' from genres gen where gen.item_id = i.id and gen.name in ('foo','pop') )
A shorter ans simpler query.
The inner query checks genre name in the keywords list , if the no row is returned then the current row of outer query is selected.
Verify the output
at here.
Thanks to Kyra ,i borrowed his schema from sql fiddle.
Upvotes: 0
Reputation: 147
Well hopefully I understand what you are asking for and I think MySQL and T-SQL will be similar in this respect. These are the sample tables I assumed you were using from your example.
Table1
ID NAME
1 Item 1
2 Item 2
3 Item 3
4 Item 4
5 Item 5
6 Item 6
Table2
ID NAME ITEM_ID
1 Genre 1 1
2 Genre 2 1
3 Genre 3 1
4 Genre 1 2
5 Genre 2 2
6 Genre 3 2
7 Genre 1 3
8 Genre 2 3
9 Genre 3 3
10 Genre 1 4
Here is the SQL to filter out items if they match one of your criteria.
SELECT *
FROM table1 a, table2 b
WHERE a.id = b.item_id
AND b.name NOT IN ('Genre 1', 'Genre 2')
Results returned from above query on sample tables.
ID NAME ID_1 NAME_1 ITEM_ID
1 Item 1 3 Genre 3 1
2 Item 2 6 Genre 3 2
3 Item 3 9 Genre 3 3
You could also filter out against the genre_id (table2.id) instead. If you plan to use a sub-query instead of hard coded values then you should switch the "NOT IN" to a "NOT EXISTS" and re-write the "AND" portion accordingly as "NOT IN" does not like null values.
This query also assumes that each "Item" has at least 1 "Genre." I am sure you can play with it if you want all "Items" excluding the ones that match your criteria.
Upvotes: 0
Reputation: 5407
You can do something like this (see sqlfiddle):
select i.name as item, g.name as genre
from items as i
left join genres as g
on i.id = g.item_id
where i.id not in
(select distinct g2.item_id from genres as g2
where FIND_IN_SET(g2.name,'foo,bar'));
And this way it works if you want to check against multiple genre names.
Upvotes: 1
Reputation: 169354
You can use a correlated subquery.
Example:
SELECT id
FROM items i
WHERE NOT EXISTS (SELECT 1
FROM genres g
WHERE g.`name` = 'bar'
AND i.id = g.item_id);
A correlated subquery differs from other subqueries in that it references the outer query.
Correlated subqueries are run for each row projected/selected by the outer query.
Upvotes: 0