sqlpostgresqljoinmany-to-many

Get all related rows when at least one matches


I have a photos table, a tags table, and a bridge table called photos_tags. Imagine there's a photo which has been tagged with dog, cat, and bird. This means that there'll be 1 row in photos, 1 row in tags, and 3 rows in photos_tags.

I'm trying to write an SQL statement to fetch all 3 tags if one of them matches. I've gotten this far:

select distinct p.photo_id, t.tag from photos p
join photos_tags pt using (photo_id)
join tags t on pt.tag_id = t.tag_id and t.tag = 'dog'

But this only returns the rows where tag = 'dog'.

Is there a way to accomplish what I'm trying to do?


Solution

  • First identify all of the photos that have the "dog" tag, which you already did but I'm going to rewrite for the CTE

    SELECT photo_id
    FROM photos_tags
    WHERE tag_id IN (SELECT tag_id FROM tags WHERE tag = 'dog')
    

    Now use this to filter your query to show all tags for any photo_id that was surfaced:

    WITH dog_photos AS 
    (
       SELECT photo_id
       FROM photos_tags
       WHERE tag_id IN (SELECT tag_id FROM tags WHERE tag = 'dog')
    )
    SELECT p.photo_id, t.tag 
    FROM photos p
      INNER JOIN photos_tags pt USING (photo_id)
      INNER JOIN tags t ON pt.tag_id = t.tag_id 
    WHERE p.photo_id IN (SELECT photo_id FROM dog_photos)