Due to boredom, I decided to setup a simple tagging system for my movies today. I basically have a list of movie titles and X number of tags for each movie. The database looks like this:
mydb=# select id, title from movies;
id | title
----+----------------------------------------
1 | first_movie
2 | second_movie
mydb=# select id, movie_id, tag_id from movie_tags;
id | movie_id | tag_id
----+----------+--------
1 | 1 | 716
2 | 1 | 787
3 | 1 | 322
4 | 2 | 716
5 | 2 | 787
6 | 2 | 323
mydb=# SELECT l.id, l.title, t.tag_id FROM movies l, movie_tags t WHERE t.movie_id = l.id AND t.tag_id IN(7开发者_JS百科16, 787, 323);
id | title | tag_id
----+--------------+--------
2 | second_movie | 787
2 | second_movie | 716
2 | second_movie | 323
1 | first_movie | 716
1 | first_movie | 787
It's the third query that's causing me problems. Firstly, it's showing duplicate rows. A new row for each tag_id
. I don't want this. I'd prefer it to show one row, for each movie, that matches the specified tags.
Which leads me to the second problem. As you can see, first_movie
isn't tagged with tag_id=323
. However, it still shows up in the results.
How can I not show duplicate rows and filter the results to movies that match all the given tags?
I planned on building a simple UI for this so I was hoping for a nice 'dynamic' query that I can drop a list of tag_ids into.
This query gives you all movies that have all 3 of the tags searched.
SELECT l.id, l.title
FROM movies l, movie_tags t
WHERE t.movie_id = l.id
AND t.tag_id IN (716, 787, 323)
GROUP BY l.id, l.title
HAVING COUNT(*) = 3
As you can see, the tag column is removed because it doesn't make sense to include one arbitrary tag.
SELECT l.id, l.title
FROM movies l
WHERE l.id IN (SELECT t.movie_id
FROM movie_tags t
WHERE t.tag_id IN (716, 787, 323));
精彩评论