tags:

views:

45

answers:

3

Hello all,

Can anyone explain why this query returns an empty result.

SELECT *
FROM (`bookmarks`)
JOIN `tags` ON `tags`.`bookmark_id` = `bookmarks`.`id`
WHERE `tag` = 'clean'
AND `tag` = 'simple'

In my bookmarks table, I have a bookmark with an id of 70 and in my tags table i have two tags 'clean' and 'simple' both that have the column bookmark_id as 70. I would of thought a result would have been returned?

How can I remedy this so that I have the bookmark returned when it has a tag of 'clean' and 'simple'?

Thanks all for any explanation and solution to this.

Update

My tag table holds many tags. A bookmark can have many tags. id in the bookmarks table and the bookmark_id in the tags table are linked.

+5  A: 

It's unlikely that there's a row whose tag equals both 'clean' and 'simple' :)

So try replacing AND with OR:

WHERE `tag` = 'clean'
OR `tag` = 'simple'

If you intend to retrieve only bookmarks with both tags, consider a double exists clause:

SELECT *
FROM bookmarks b
WHERE EXISTS (
    SELECT *
    FROM tags t
    WHERE t.tag = 'simple'
    AND t.bookmark_id = b.id
) AND EXISTS (
    SELECT *
    FROM tags t
    WHERE t.tag = 'clean'
    AND t.bookmark_id = b.id
)

It's also possible to check using a having statement:

SELECT    b.id
FROM      bookmarks b
JOIN      tags t
ON        t.bookmark_id = b.id
          AND t.tag in ('clean','simple')
GROUP BY  b.id
HAVING    COUNT(distinct t.tag) = 2

The count will ensure both tags are found.

Andomar
That would return any bookmarks that have a tag clean or simple. I want bookmarks that have the tags clean AND simple. So an OR is not going to work, right?
Abs
@Abs: Added an option for that to the answer. Mark Byers' double join will work as well.
Andomar
@Andomar I tried the last query first as it looked efficient - and it worked. I thought this was going to be straight forward but obviously not! Thanks Andomar. :)
Abs
+2  A: 

You get no rows because your where clause is impossible to satisfy.

To get bookmarks that have both a tag 'clean' and a tag 'simple' you need to join with the tags table twice. Try doing it like this:

SELECT bookmarks.*
FROM bookmarks
JOIN tags AS T1 ON T1.bookmark_id = bookmarks.id AND T1.tag = 'clean'
JOIN tags AS T2 ON T2.bookmark_id = bookmarks.id AND T2.tag = 'simple'
Mark Byers
That's a bit wasteful (maybe)? I have updated my question - is a right join possible here??
Abs
@Abs - A right (or left) join will not help. Using Mark's technique would require a join to the Tags table for each tag on which you wanted to search. The optimizer should be able to do this efficiently. It will just be cumbersome to write if you want to find something that has many tags.
Thomas
@Abs: I don't think my query is wasteful (apart from the use of SELECT *, but that's only because your question doesn't specify the columns you want). But rather that taking my word for it, I suggest that you run some benchmark tests to see which answer performs best. Also, you should add a unique index on (bookmark_id, tag) if you have not already done so. This will improve performance.
Mark Byers
+1  A: 
Select ...
From `bookmarks`
Where Exists(
            Select 1
            From `tags`
            Where `tags`.`bookmark_id` = `bookmarks`.`id`
                And 'tag' In('clean','simple')
            Having Count(*) = 2
            )

(BTW, I'm assuming that a bookmark cannot have a given tag twice).

Thomas
Isn't a `group by` required? Could use `count(distinct tag)` to cope with duplicate tags for a bookmark
Andomar
A single bookmark will always have unique tags - i avoid users adding tags that are the same in my app logic.
Abs
A GROUP BY is not required since I'm not returning anything in the SELECT clause nor using anything after the From and Where clause.
Thomas
@Abs: How about adding a unique index on (bookmark_id, tag) in the database?
Mark Byers
@Thomas: +1 for not using any groups with `select 1`... didn't know you could do that
Andomar
@Andomar- When I first posted my comment I thought you might need a GROUP BY if you use Count(Distinct Tag) because it appears in the HAVING clause but apparently you do not (which is a bit surprising).
Thomas