适合刚接触子查询的同学。用同一套「学生 / 成绩」例子,把
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 固定名单
sql
SELECT *
FROM students
WHERE student_id IN (1, 2, 5);
含义:学号是 1 或 2 或 5 的学生。
- 在名单里 → 条件为真,留下
- 不在 → 为假,丢掉
3.2 名单来自子查询
需求:找出有成绩的学生。
sql
SELECT *
FROM students
WHERE 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:存不存在关联行
同样的需求:找出有成绩的学生。
sql
SELECT *
FROM students s
WHERE 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:找出「没有成绩」的学生
sql
SELECT *
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM scores sc
WHERE sc.student_id = s.student_id
);
| 学生 | 有没有成绩 | NOT EXISTS | 留下? |
|---|---|---|---|
| 小明 | 有 | 假 | 否 |
| 小红 | 有 | 假 | 否 |
| 小刚 | 无 | 真 | 是 |
结果:
| student_id | name |
|---|---|
| 3 | 小刚 |
也可以写成:
sql
SELECT *
FROM students
WHERE 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。