使用JOIN而不是INTERSECT进行查询

atp*_*atp 2 mysql sql database

我有这样的架构:

// 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
Run Code Online (Sandbox Code Playgroud)

我想要做的是选择badge_types特定用户尚未获得的所有内容.在上面的示例中,调用查询:

user_id = 5回归badge_type_id 23

user_id = 6返回empty集(用户已经获得所有这些)

user_id = 19回归badge_type_id 13

我可以用一个INTERSECT条款做到这一点.但我想知道它是否可以通过简单的方式完成JOIN?任何帮助,将不胜感激.

OMG*_*ies 13

使用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
Run Code Online (Sandbox Code Playgroud)

使用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 = ?)
Run Code Online (Sandbox Code Playgroud)

使用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 = ?)
Run Code Online (Sandbox Code Playgroud)

MySQL的

如果列不可为空,则LEFT JOIN/IS NULL是最佳选择.如果它们是空的,NOT EXISTS并且NOT IN是最好的选择.

SQL Server

心说SQL Server上,NOT INNOT EXISTS有更好的表现比LEFT JOIN如果有问题的字段不可为空/ IS NULL.

神谕

对于Oracle,所有三个都是等效的,但如果列不可为空,则优选LEFT JOIN/IS NULL和NOT EXISTS.