I have this database schema. I’m calling the stuff I’m tagging objects just for the sake of making it simple:
Users
- user_id
- login
User to Object table (many to many)
- user_id
- object_id
Object table
- object_id
- object_stuff
Object to tag with user_id table (many to many with extra column)
- user_id
- object_id
- tag_id
Tag table
- tag_id
- tag_name
I have been using a query like this in order to get the objects of a particular user as well as the objects’ tags (if the user tagged any). If you think you can optimize this, feel free to let me know.
@user_id = 'whatever user_id I want';
SELECT
o.object_id AS object_id,
o.object_stuff AS object_stuff,
IF (tags.tag_name IS NOT NULL, GROUP_CONCAT(tags.tag_name SEPARATOR '|'), 'N/A') AS tags
FROM object_table AS o
LEFT OUTER JOIN obj_to_users_table AS o2u ON o.object_id = o2u.object_id
LEFT OUTER JOIN users AS u ON u.user_id = o2u.user_id
LEFT OUTER JOIN obj_to_tag_users AS o2tu ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
LEFT OUTER JOIN tags AS t ON t.tag_id = o2t.tag_id
WHERE u.user_id = @user_id GROUP BY o.object_id
Which would yield results like this:
object_id object_stuff tags
1 stuff1... tag1|tag2|tag3
2 stuff2... tag4|tag6|tag1
3 stuff3... tag7|tag2|tag5
My problem is that I would like to, for example, search for objects that are tagged as ‘tag2’ and I should get:
object_id object_stuff tags
1 stuff1... tag1|tag2|tag3
3 stuff3... tag4|tag2|tag1
But instead, I lose the other tags that accompany the object:
object_id object_stuff tags
1 stuff1... tag2
3 stuff3... tag2
How do I modify my WHERE clause so that I can get the objects tagged as ‘tag2’ but also keeping the other tags the object has in the result set?
... WHERE u.user_id = @user_id AND t.tag_name LIKE '%tag2%' ...
First, calculate the concatenated results.
Next, perform your search against those concatenated results to preserve the groupings of the tags:
EDIT:
Here’s an example that uses an
EXISTS. I also switched theLEFT JOINs toINNER JOINs, since searching for a specific tag should eliminate anyobjectswithouttagsanyways.While this query looks more complicated, it should have a better chance of using any available indexes.
I had also mentioned doing the same type of query using an additional set of
INNER JOINs, but that one would look even more complex since you’d still have to use a sub-query to useDISTINCTto get rid any possible duplicates introduced by the additionalJOINs… so I’d suggest usingEXISTSinstead…