Reputation: 12566
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.
Upvotes: 0
Views: 63
Reputation: 12843
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;
Upvotes: 0
Reputation: 839124
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.
Upvotes: 2
Reputation: 135928
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)
Upvotes: 0