rxmnnxfpvg
rxmnnxfpvg

Reputation: 30993

Query using JOIN instead of INTERSECT

I have a schema like this:

// table badge_types
id | name
+++|++++++++++
1  | mentor
2  | proctor
3  | doctor

// table badges
id | badge_type_id | user_id     
+++|+++++++++++++++|++++++++
1  | 1             | 5
2  | 1             | 6
3  | 2             | 6
4  | 3             | 6
5  | 2             | 19
6  | 3             | 20

What I want to do, is select all badge_types that a particular user has not yet gotten. In the above example, calling the query for:

user_id = 5 returns badge_type_id 2 and 3

user_id = 6 returns empty set (user got all of them already)

user_id = 19 returns badge_type_id 1 and 3

I can do this with an INTERSECT clause. But I'm wondering if it can be done with a simple JOIN? Any help would be appreciated.

Upvotes: 2

Views: 2057

Answers (1)

OMG Ponies
OMG Ponies

Reputation: 332581

Using LEFT JOIN/IS NULL:

   SELECT bt.name
     FROM BADGE_TYPES bt
LEFT JOIN BAGDES b ON b.badge_type_id = bt.id
                  AND b.user_id = ?
    WHERE b.id IS NULL

Using NOT EXISTS

   SELECT bt.name
     FROM BADGE_TYPES bt
    WHERE NOT EXISTS(SELECT NULL
                       FROM BADGES b 
                      WHERE b.badge_type_id = bt.id
                        AND b.user_id = ?)

Using NOT IN

   SELECT bt.name
     FROM BADGE_TYPES bt
    WHERE bt.id NOT IN (SELECT b.badge_type_id
                       FROM BADGES b 
                      WHERE b.user_id = ?)

MySQL

If the columns are not nullable, LEFT JOIN/IS NULL is the best choice. If they are nullable, NOT EXISTS and NOT IN are the best choice.

SQL Server

Mind that on SQL Server, NOT IN and NOT EXISTS perform better than LEFT JOIN /IS NULL if the columns in question are not nullable.

Oracle

For Oracle, all three are equivalent but LEFT JOIN/IS NULL and NOT EXISTS are preferable if columns aren't nullable.

Upvotes: 13

Related Questions