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?
 
     
     
    