I have an m:n relationship of images and tags in my database, which uses a crosstable to model this.
Table imgs
contains much more information than just img_id
, but that is all that's required to uniquely identify an image.
I want to find every img_id
which has both tagA and tagB (and tagC and so on, I'll build this string so it won't really matter whether its two or ten tags).
Now, where I'm stuck is, of course first you'll join imgs
with img_tags
with tags
, add a where clause for the tags;
SELECT *
FROM imgs
INNER JOIN img_tags ON imgs.img_id = img_tags.img_id
INNER JOIN tags ON img_tags.tag_id = tags.tag_id
WHERE tag = 'tagA' OR tag = 'tagB';
and then you'll get rows with identical imgs
information, only differing in tag
and tag_id
. Now I should be able to count those, targeting only those which appear in the same amount as tags were supplied (Count(*) = n
), and then using group by
to aggregate them? But I can't quite figure it out.
In case it might be of relevance, you might assume the fields in img_tags are both foreign keys referencing the other tables, however that is not the case, they are not linked in any way.
You can use aggregation like this:
SELECT i.*
FROM imgs i JOIN
img_tags it
ON i.img_id = it.img_id JOIN
tags t
ON it.tag_id = t.tag_id
WHERE tag IN ('tagA', 'tagB')
GROUP BY i.img_id
HAVING COUNT(*) = 2;
Aggregating by i.img_id
is safe -- and supported by the SQL standard -- assuming that img_id
is the primary key in the table.