I’ve got a client that wants me to make a tagging system. I’ve got three tables: items, tags, and item_tag_assoc – the last one only serves to associate item_ids and tag_ids. Here’s the problem I’m having:
If the user requests included tags [1, 2, 3] and excluded tags [4, 5, 6], the result should be all items that have EVERY tag in [1, 2, 3] (not just one) and NO tags in [4, 5, 6]. How do I write a query to accomplish this?
I researched enough to figure out inner joins for the tag inclusion:
SELECT i.item_id, i.item_title FROM items AS i INNER JOIN tag_item_assoc AS tia1 ON (tia1.item_id = i.item_id AND tia1.tag_id = 1)
…and just chain on the same inner join pattern for as many tags as you want to include. It may be a little bulky, but users won’t be choosing more than 4 or 5 tags before they move on, so it’ll do.
I was really hoping that I could exclude the same way, and wrap everything into one query:
INNER JOIN tag_item_assoc AS tia2 ON (tia2.item_id = i.item_id AND tia2.tag_id!= 2)
But it became obvious very quickly that wasn’t going to work. I read a couple of articles that said LEFT OUTER JOINs could let me exclude while I include, but I couldn’t figure them out, mostly because of the stray WHERE clauses. Any permutation of LEFT OUTER JOINs and INNER JOINs I tried either yielded an error or very confusing results.
All that to say – does anyone here know how I can accomplish this? I apologize for not having any useful code examples to provide. I’m ok with starting from scratch if the INNER JOINs are an obstacle – I just need a way to accomplish multiple association inclusion and exclusion at the same time. Thanks in advance for the help and expertise!
Found the answer. Instead of adding
INNER JOINs like so:INNER JOIN tag_item_assoc AS tia2 ON (tia2.item_id = i.item_id AND tia2.tag_id!= 2)I added a sub-query with a single inner join:
WHERE i.item_id NOT IN ( SELECT i.item_id FROM items AS s INNER JOIN tag_item_assoc AS tia1 ON (tia1.item_id = i.item_id AND tia1.tag_id IN (4, 5, 6)))The sub-query creates a set of all
items that have any of the tags in [4, 5, 6], andWHERE i.iten_id NOT INeliminates any records that match that set.Thanks everyone for the effort! Hopefully this will help someone down the road.