Hay dos maneras de hacer esto. Prefiero la primera forma, que es auto-unirse para cada etiqueta:
SELECT l.*
FROM Locations l
JOIN LocationsTagsAssoc a1 ON a1.LocationID = l.ID
JOIN Tags t1 ON a1.TagID = t1.ID AND t1.Name = ?
JOIN LocationsTagsAssoc a2 ON a2.LocationID = l.ID
JOIN Tags t2 ON a2.TagID = t2.ID AND t2.Name = ?
JOIN LocationsTagsAssoc a3 ON a3.LocationID = l.ID
JOIN Tags t3 ON a3.TagID = t3.ID AND t3.Name = ?;
La otra forma también funciona, pero usando GROUP BY
en MySQL tiende a incurrir en una tabla temporal y el rendimiento es lento:
SELECT l.*
FROM Locations l
JOIN LocationsTagsAssoc a ON a.LocationID = l.ID
JOIN Tags t ON a.TagID = t.ID
WHERE t.Name IN (?, ?, ?)
GROUP BY l.ID
HAVING COUNT(*) = 3;
Re comentario de @Erikoenig:
Si desea asegurarse de que no haya etiquetas adicionales, puede hacerlo de esta manera:
SELECT l.*
FROM Locations l
JOIN LocationsTagsAssoc a ON a.LocationID = l.ID
JOIN Tags t ON a.TagID = t.ID
GROUP BY l.ID
HAVING COUNT(*) = 3 AND SUM(t.Name IN (?, ?, ?)) = 3;
Sacar la cláusula WHERE permite contar otras etiquetas, si las hay. Entonces COUNT() puede ser mayor que 3.
O si el conteo es exactamente tres etiquetas, pero algunas de estas tres no son las etiquetas correctas, entonces la condición SUM() en la cláusula HAVING asegura que las tres etiquetas que desea estén presentes en el grupo.