0

Say I have the following 3 tables:

users
id : int 11 primary autoinc
email : varchar 255

tags
id : int 11 primary autoinc
name : varchar 255

tag_union
id : int 11 primary autoinc
tag_id : int 11
target_id : int 11
target_type : enum( 'user','blog','other' )

If I'm given a list of tags, say 1,3, and 8, I can select all of the users that have a union of ANY of the tags like so:

SELECT *
FROM `users`
WHERE `id` IN( SELECT `target_id`
               FROM `tag_union`
               WHERE `tag_id` IN( '1','3','8')
                 && `target_type`=='user' )

But how would I select only users that have a union for ALL 3 of the tags? I can't seem to think of a way to do this in a single query.

P.S. Sorry for the crappy title, someone can rename it if they can think of a more fitting one.

3
  • How many rows do you have in each table? Is performance an issue here? Commented Jan 13, 2011 at 21:17
  • @Mark: Currently the users table is max 50K rows, and the tag table ~2k, with the union being somewhere in the middle. Most people have only like one or tag. Commented Jan 13, 2011 at 21:42
  • I suspect that with 50K rows in the user tables the IN will take several seconds or more, whereas the JOIN will be almost instant. But if I'm wrong about that, I'd like to know so that I can learn. Commented Jan 13, 2011 at 21:45

3 Answers 3

2

Your derived table should use a GROUP BY / HAVING:

SELECT *
FROM users
WHERE id IN
(
    SELECT target_id
    FROM tag_union
    WHERE tag_id IN ('1', '3', '8') AND target_type = 'user'
    GROUP BY target_id
    HAVING COUNT(*) = 3
)

Note that in MySQL the IN clause can be very slow. It might be better to use a join instead.

SELECT *
FROM users T1
JOIN
(
    SELECT target_id
    FROM tag_union
    WHERE tag_id IN ('1', '3', '8') AND target_type = 'user'
    GROUP BY target_id
    HAVING COUNT(*) = 3
) T2
ON T1.id = T2.target_id

I'm also assuming that (target_type, tag_id) is unique in the tag_union table.

Sign up to request clarification or add additional context in comments.

4 Comments

It isn't distinct, a tag can be on multiple types, and a type can have multiple tags
@Josh: From your reply, I think you may have misunderstood what it means for a tuple to be distinct. I know the same tag can appear on multiple different types, but can the same tag appear on a single type (e.g. user) multiple times? And if it can, why is that necessary in your design?
No, it can't, sorry I misunderstood.
@Josh: Sorry that I missed the GROUP BY in my answer before. I fixed that now.
0
SELECT *
FROM `users`
WHERE `id` IN( SELECT `target_id`
               FROM `tag_union`
               WHERE `tag_id` IN ('1','3','8')
                 AND `target_type`='user' 
               GROUP BY `target_id`
               HAVING COUNT(DISTINCT `tag_id`) = 3)

Comments

0

I usually do it with the group by/having approach the other suggested. But just for completeness, it can also be done with joins.

select u.id
      ,u.other_columns
  from users u
  join tag_union t1 on(u.id = t1.target_id)
  join tag_union t2 on(u.id = t2.target_id)
  join tag_union t3 on(u.id = t3.target_id)
 where t1.target_type = 'user' and t1.tag_id = 1
   and t2.target_type = 'user' and t2.tag_id = 3
   and t3.target_type = 'user' and t3.tag_id = 8;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.