概念
| 语法 | 用途 | 常用场景 | 
|---|---|---|
IN | 
判断是否在某个列表 | 列表是静态值(IN (1,2,3))或子查询 | 
EXISTS | 
判断是否存在匹配 | 子查询里经常配合外层列 | 
JOIN | 
真正把两表关联起来,输出结果 | 需要拿到匹配列时 | 
底层
| IN | EXISTS | JOIN | |
|---|---|---|---|
| 执行原理 | 将子查询结果先计算成一个临时集合,外层值去匹配 | 外层值带进去跑子查询,看是否至少存在一行 | 两张表做笛卡尔积 + 连接条件过滤,或用索引、Hash Join、Nested Loop 加速 | 
| 适用场景 | 小范围列表/短子查询结果 | 子查询通常跟外层值有关(correlated subquery) | 需要输出连接列 | 
| 优化器行为 | 对 IN 和 EXISTS 常可相互转换 | 
对 IN 和 EXISTS 有时会改写成半连接 | 
JOIN 可以是 Nested Loop / Hash Join / Merge Join | 
性能
| 场景 | 谁更快 | 
|---|---|
IN (静态值) vs EXISTS | 
IN 更快(因为直接转 OR) | 
IN (子查询) vs EXISTS (子查询) | 
大多数情况下 EXISTS 更优,尤其是子查询和外层相关时 | 
JOIN vs EXISTS | 
如果只是判断是否匹配,EXISTS 比 JOIN 更省内存,不会产生笛卡尔积;JOIN 在需要取出列时更方便 | 
何时用
| 需求 | 推荐 | 
|---|---|
| 判断某值是否属于固定列表 | IN | 
| 判断是否存在匹配记录 | EXISTS | 
| 需要取匹配行的列 | JOIN | 
| 子查询和外层列有依赖关系 | EXISTS 通常比 IN 更优 | 
| 大集合匹配 | 优先 JOIN(配合索引) | 
示例
| 写法 | 解释 | 
|---|---|
WHERE id IN (1, 2, 3) | 
最快,直接 OR | 
WHERE id IN (SELECT id FROM big_table) | 
如果 big_table 很大,EXISTS 更合适 | 
WHERE EXISTS (SELECT 1 FROM big_table WHERE big_table.id = small_table.id) | 
如果 big_table 有索引,EXISTS 很快 | 
JOIN | 
如果要输出 big_table 的列,必须用 JOIN | 
实战经验
- 
IN的子查询结果集比较大时会变慢(内部先 materialize) - 
EXISTS通常对相关子查询更友好(会 index lookup) - 
JOIN要注意是否会产生重复行(笛卡尔积),如果只需要“是否有匹配”,不要用JOIN 
判断方法
一定要看 EXPLAIN!
因为 MySQL 优化器有时会把:
- 
IN改成EXISTS - 
EXISTS改成 Semi-Join - 
JOIN改成 Hash Join 
| 你只要记住: | 
|---|
只需判断是否存在:用 EXISTS | 
小范围值匹配:用 IN | 
要输出匹配的列:用 JOIN |