本文适合刚接触子查询的MySQL初学者。通过同一套「学生/成绩」示例数据,清晰讲解 IN 和 EXISTS 的用法与区别。

一、它们是干什么的
在 MySQL 数据库查询中,IN 和 EXISTS 这两个关键字经常与子查询搭配使用,共同完成数据过滤任务。简单来说,就是先(或同时)参考另一张表的数据,再决定当前表的哪些行应该被保留。
可以先记住一句通俗版的理解:
| 关键字 | 通俗理解 |
|---|---|
| IN | 左边这个值,是否出现在右边的名单里? |
| EXISTS | 对当前这一行,括号里的子查询能否至少返回一行? |
二、准备示例数据
学生表 students
| student_id | name |
|---|---|
| 1 | 小明 |
| 2 | 小红 |
| 3 | 小刚 |
成绩表 scores
| score_id | student_id | subject | score |
|---|---|---|---|
| 101 | 1 | 语文 | 90 |
| 102 | 1 | 数学 | 85 |
| 103 | 2 | 语文 | 92 |
结论:小明、小红有成绩记录;小刚没有成绩记录。
三、IN:在不在名单里
3.1 固定名单
SELECT *FROM studentsWHERE student_id IN (1, 2, 5);
含义:查询学号为 1、2 或 5 的学生。
- 在名单里 → 条件为真,保留该行
- 不在名单里 → 条件为假,丢弃该行
3.2 名单来自子查询
需求:找出有成绩记录的学生。
SELECT *FROM studentsWHERE student_id IN ( SELECT student_id FROM scores);
执行逻辑:
- 先执行括号内的子查询:从
scores表获取student_id列表,例如(1, 1, 2),等同于名单(1, 2) - 再扫描
students表:检查每个学生的学号是否出现在名单中 - 在名单中的保留
逐步分析:
| 学生 | student_id | 在名单里? | 结果 |
|---|---|---|---|
| 小明 | 1 | 是 | 保留 |
| 小红 | 2 | 是 | 保留 |
| 小刚 | 3 | 否 | 丢弃 |
查询结果:
| student_id | name |
|---|---|
| 1 | 小明 |
| 2 | 小红 |
四、EXISTS:存不存在关联行
同样的需求:找出有成绩记录的学生。
SELECT *FROM students sWHERE EXISTS ( SELECT 1 FROM scores sc WHERE sc.student_id = s.student_id);
4.1 怎么理解
对外层查询的每一行学生,执行一次内层查询:
- 内层查询能返回至少一行 →
EXISTS结果为真 → 保留该学生 - 内层查询返回零行 → 结果为假 → 丢弃该学生
SELECT 1 只是占位符:EXISTS 只关心子查询是否有结果行,不关心具体查询内容。
4.2 逐行走一遍
小明(id=1)
- 成绩表里是否存在
student_id = 1的记录?存在 → 保留
小红(id=2)
- 是否存在
student_id = 2的记录?存在 → 保留
小刚(id=3)
- 是否存在
student_id = 3的记录?不存在 → 丢弃
结果与上面 IN 写法一致:小明、小红。
五、NOT EXISTS:找出「没有成绩」的学生
SELECT *FROM students sWHERE NOT EXISTS ( SELECT 1 FROM scores sc WHERE sc.student_id = s.student_id);
| 学生 | 有没有成绩 | NOT EXISTS | 保留? |
|---|---|---|---|
| 小明 | 有 | 假 | 否 |
| 小红 | 有 | 假 | 否 |
| 小刚 | 无 | 真 | 是 |
结果:
| student_id | name |
|---|---|
| 3 | 小刚 |
也可以写成:
SELECT *FROM studentsWHERE student_id NOT IN ( SELECT student_id FROM scores);
在这组干净数据下,结果同样是小刚。
小提示:
NOT IN的子查询结果中如果包含NULL值,会导致预期之外的行为;很多场景下更推荐使用NOT EXISTS。初学者先了解这个差异即可。
六、IN 和 EXISTS 怎么选(入门版)
很多业务场景下,两者能写出等价的结果,只是思考角度不同:
| IN | EXISTS | |
|---|---|---|
| 思考方式 | 先获取一份值列表,判断值是否在其中 | 对当前行提问:关联表中是否有匹配行 |
| 直观写法 | id IN (1,2,3) 或 id IN (子查询) | EXISTS (带关联条件的子查询) |
| 适合场景 | 列表较短、语义就是「属于某个集合」 | 语义就是「是否存在关联数据」 |
关于性能:谁更快取决于数据量、索引和优化器策略,不要死记「永远谁更快」。入门阶段优先:写对、读得懂。
七、和外键的一点联系(可选)
- 外键约束:插入数据时就不允许指向不存在的学生
- IN / EXISTS:查询时筛选「有没有关联成绩」等
一个是写数据时的规则,一个是查数据时的过滤手段,结合起来理解「表与表之间的关联方式」。
八、核心要点回顾
IN:值是否在这个列表里。
EXISTS:对当前行,子查询是否至少存在一行。
NOT EXISTS:对当前行,是否不存在关联行。
同一道题「有成绩的学生」:
- 用
IN:学号是否出现在成绩表的学号列表中 - 用
EXISTS:该学生在成绩表里能否找到记录
结果可以一样,思考问题的角度不同。
九、练习
- 用
IN查出有成绩的学生(默写一遍)。 - 用
EXISTS再写一遍。 - 用
NOT EXISTS查出没有成绩的学生。
能不看笔记写出来,这部分就算过关了。
笔记整理自零基础学习过程,示例表为 students / scores。
