简体   繁体   中英

Mysql: find 1 row in table1 that joins multiple rows in table2 with given values

Sorry for question title.. I have no idea how to write it down in more clear way.

So, I have theree tables:

  1. tracks , contains id (track id) & name (track name)
  2. tags , contains id (tag id) & name (tag name)
  3. track_tags , contains track_id & tag_id

I have to find all tracks, that have all tags, specified in list (for example i need to find all tracks, that have 'rock', 'pop' and 'dance' tags at one time).

Previously, i has solved similar issue, but finding all tracks with at least 1 tag match. It was quite easy:

SELECT tracks.* 
FROM tags 
INNER JOIN track_tags ON tags.id = track_tags.tag_id 
INNER JOIN tracks ON track_tags.track_id = tracks.id 
WHERE tags.name IN('pop', 'rock', 'dance')
GROUP BY tracks.id

But i have quite no idea how to find all tracks, that match exactly all tags from the list.

I thought about retrieving all tracks and all their tags to my app and filter tracks there - but there are quite lot of them (hundreds of thousands), app is also using pagination, that can be implemented easily using LIMIT in the upper query.

Perhaps it can be done another way, using mysql features? I am really frigtened that selecting all tracks with all tags and filtering them on the app side will kill web server.

The most general way to solve this is using aggregation with a having clause:

SELECT tracks.* 
FROM tags INNER JOIN
     track_tags
     ON tags.id = track_tags.tag_id INNER JOIN
     tracks
     ON track_tags.track_id = tracks.id 
GROUP BY tracks.id
HAVING sum(tags.name = 'pop') > 0 and
       sum(tags.name = 'rock') > 0 and
       sum(tags.name = 'dance') > 0;

A slight variation on this is to do the filtering in the where and then look for three distinct tags:

SELECT tracks.* 
FROM tags INNER JOIN
     track_tags
     ON tags.id = track_tags.tag_id INNER JOIN
     tracks
     ON track_tags.track_id = tracks.id 
WHERE tags.name IN('pop', 'rock', 'dance')
GROUP BY tracks.id
HAVING count(distinct tags.name) = 3;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM