2

I have three tables: comics, tags and comicTags.
The comics table ID is connected through a foreign key with the comicTags table while the tags table ID is also connected to the comicTags table through the tagID.

comics table
+----+
| ID |
+----+
|  1 |
|  2 |
+----+

comicTags table
+---------+-------+
| comicID | tagID |
+---------+-------+
|       1 |     1 |
|       2 |     1 |
|       2 |     2 |
+---------+-------+

tags table
+----+-------+
| ID | tag   |
+----+-------+
|  1 | tag1  |
|  2 | tag2  |
+----+-------+

What I'd like to achieve is, if I'm searching for tag1 AND tag2 I'd only like to get comic ID 2 as a result. Given is a string with the two tag names.

SELECT c.ID FROM `tags` `t`
    LEFT JOIN comicTags AS ct 
      ON ct.tagID=t.ID
    LEFT JOIN comics AS c
      ON c.ID=ct.comicID
    WHERE ((t.tag LIKE 'tag1')
      OR (t.tag LIKE 'tag2'))
    GROUP BY c.ID

With this statement I'm obviously getting comic ID 1 as well which I do not want. Changing from OR to AND doesn't work in the statement I've created.

Could you point me in the right direction on how to get only those comic IDs that match all tag IDs?

jrn
  • 2,640
  • 4
  • 29
  • 51

4 Answers4

2

You need to search for rows where the tag_id is one of the tags you are looking for, and then ensure that the number of rows returned is equal to the number of tags, like this:

SELECT c.id
FROM comics c
JOIN comicTags ct ON ct.comicID = c.id
JOIN tags t ON t.id = ct.tagID AND t.tag IN ('tag1', 'tag2')
GROUP BY c.id
HAVING COUNT(*) = 2;

I have changed the condition to use the IN operator. Your current query does not have any wildcards, so I assume you are looking for tags that are an exact match, in which case this query is a little nicer.

Here is an SQL Fiddle example.

Note that if it's possible for a comic to be aligned with a tag more than once, you may want to change your query to COUNT(DISTINCT t.id) to ensure that the number of unique tags matches. Here is an SQL Fiddle example that shows repeated tags not being returned with the current query, but will be with the distinct added.

AdamMc331
  • 16,492
  • 10
  • 71
  • 133
0

Maybe a subquery with ALL is what you need:

select c.id
from tags as t
     left join comicTags as ct on t.id = ct.tagId
     left join comics as c on c.id = ct.comicId
where t.id = ALL (select id from tags where t.tag in ('tag1','tag2'))
Barranka
  • 20,547
  • 13
  • 65
  • 83
0

you can run one query per "tag" you are looking for and then join the result:

select c.*
from
(
    SELECT comicID 
    FROM tags t JOIN comicTags AS ct ON ct.tagID=t.ID
    WHERE (t.tag LIKE 'tag1')
) a join
(
    SELECT comicID 
    FROM tags t 
        JOIN comicTags AS ct ON ct.tagID=t.ID
    WHERE (t.tag LIKE 'tag2')
)b on a.comicID = b.comicID
join comics c on c.ID = a.comicID
Diego
  • 34,802
  • 21
  • 91
  • 134
0

Check sqlFiddle

SELECT *
FROM comics c
WHERE 
    EXISTS  (SELECT comicID 
             FROM comicTags ct INNER JOIN tags t
                ON ct.tagID = t.ID
             WHERE 
                ct.comicID = c.ID
             AND t.tag = 'tag1')
AND EXISTS  (SELECT comicID 
             FROM comicTags ct INNER JOIN tags t
                ON ct.tagID = t.ID
             WHERE 
                ct.comicID = c.ID
             AND t.tag = 'tag2');
Juan Carlos Oropeza
  • 47,252
  • 12
  • 78
  • 118