I have trivial tables post
, tag
and post_tags
in a trivial Many-To-Many relationship. I want to select some posts by including and excluding some tags. I tried many variations of SQL queries, but none of them works for excluding tags.
I started from a query like this:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
AND tag.name NOT IN ('War', 'Crime')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
But, unfortunately, this does not work. I see posts with tag "War" in result set. Then I tried to move the NOT IN
condition to a separate subquery on post_tags
and join to it:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
INNER JOIN (SELECT * FROM tag WHERE name NOT IN ('War', 'Crime')) AS tags
ON post_tags.tag_id = tags.id
WHERE tags.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
Even tried to exclude some posts in first JOIN
like this:
SELECT post.* FROM post
INNER JOIN post_tags ON post.id = post_tags.post_id
AND post_tags.tag_id NOT IN (SELECT id FROM tag WHERE name IN ('War', 'Crime'))
INNER JOIN tag ON post_tags.tag_id = tag.id
WHERE tag.name IN ('Science','Culture')
GROUP BY post.id
HAVING COUNT(post_tags.id) > 1
ORDER BY post.rating DESC
LIMIT 50;
But none of this works. I am especially confused about second query (joining with filtered result set instead of table).
Using PostgreSQL version 9.3, OS Ubuntu 14.04.
Any thoughts?