概念
| 语法 | 用途 | 常用场景 |
|---|---|---|
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 |