CodeGym /课程 /SQL SELF /用 EXISTS 和 NOT EXISTS 检查数据是否存...

用 EXISTS 和 NOT EXISTS 检查数据是否存在

SQL SELF
第 13 级, 课程 3
可用

欢迎来到新的 SQL 讲座!今天我们要认识一下最不起眼但超强大的操作符——EXISTS 和 NOT EXISTS。想象一下有个侦查员,啥痕迹都不留,但能立刻告诉你:“有,这个对象存在”或者“没有,这里啥都没有”。这俩操作符不会直接返回数据,但能让你在查询里做逻辑超准的判断。

先来点基础。EXISTS 就是用来检查子查询结果里有没有记录。如果子查询能查到至少一条记录,EXISTS 条件就会返回 TRUE,否则就是 FALSE。

SELECT 1
WHERE EXISTS (
    SELECT * 
    FROM students 
    WHERE grade > 3.5
);

你看,其实我们根本不关心子查询查出来的数据,只在乎有没有这样的行。如果有一条符合条件,查询就会返回 1。

EXISTS 的语法

EXISTS 的语法很简单:

SELECT 列名
FROM 表名
WHERE EXISTS (
    SELECT 1
    FROM 另一个表
    WHERE 条件
);

解释:

  • EXISTS 里的子查询可以是任何查询。
  • 子查询的结果决定了是 TRUE 还是 FALSE。

例子:有没有分数大于 4 的学生?

假设有个 students 表:

id name grade
1 Otto 3.2
2 Anna 4.7
3 Dan 5.0
4 Lina 2.9

比如我们想查一下有没有分数大于 4 的学生。用下面这个查询:

SELECT '有分数很高的学生!'
WHERE EXISTS (
    SELECT 1
    FROM students
    WHERE grade > 4
);

结果:

有分数很高的学生!

为啥 EXISTS 比 IN 快?

EXISTS 最大的优点就是只要找到第一个符合条件的就立刻停。也就是说,如果你只是想判断有没有数据,EXISTS 超高效。

比如,假如 students 表有几百万条记录,但你只想知道有没有一条分数大于 4(grade > 4)。SQL 一旦找到第一条符合的,查询就结束了。

用 NOT EXISTS

现在说说 NOT EXISTS。它和 EXISTS 正好相反。只有当子查询一条记录都查不到时才返回 TRUE。

例子:找没有分数(NULL)的学生

假设表里有些学生还没分数:

id name grade
1 Otto NULL
2 Anna 4.7
3 Dan 5.0
4 Lina NULL

我们想选出所有没有分数的学生。用 NOT EXISTS:

SELECT *
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM students 
    WHERE grade IS NOT NULL
    AND id = s.id
);

结果:

id name grade
1 Otto NULL
4 Lina NULL

EXISTS 和 IN 的对比

有时候你会觉得 EXISTS 和 IN 好像差不多。表面上是,但其实有细节。特别是遇到 NULL 的时候,IN 的表现可能很迷,而 EXISTS 就很靠谱。

来看个例子。

courses 表(可以选的课程):

course_id name
1 数学
2 历史

还有学生:

student_id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

enrollments 表(谁选了哪个课):

student_id course_id
1 1
2 2
3 NULL

我们想查一下有哪些课程被人选了。看起来很简单。

用 IN:

SELECT name
FROM courses
WHERE course_id IN (
    SELECT course_id 
    FROM enrollments
);

表面上应该没问题。但如果 enrollments 里有 NULL 的 courseid,比如 Maria Chi,IN 可能啥都查不出来!因为 NULL 让子查询“变得不确定”,SQL 就懵了:万一 NULL 就是我们要找的 courseid 呢?

用 EXISTS:

SELECT name
FROM courses c
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE c.course_id = e.course_id
);

而 EXISTS 就是直接问:“有没有一行 course_id 一样的?”——就这么简单。它完全不怕有 NULL,因为它只找具体的匹配,不管值列表。

结论:如果子查询里可能有 NULL,最好用 EXISTS,不然容易踩坑。

实际问题举例

students 表:

id name
1 Alex Lin
2 Anna Song
3 Maria Chi
4 Dan Seth
5 Shadow Moon

enrollments 表:

student_id course_id
1 1
2 2
3 NULL

例子 1. 已经选了课的学生

找找那些已经“露脸”——选了课的(哪怕像 Maria Chi 这种奇怪的):

SELECT name
FROM students s
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

结果:

Alex Lin
Anna Song
Maria Chi

只要学生在 enrollments 里出现过——不管 course_id 是不是 NULL——都会查出来。

例子 2. 没选课的学生

现在找找那些还只是“存在”但没选课的:

SELECT name
FROM students s
WHERE NOT EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE s.id = e.student_id
);

结果:

Dan Seth
Shadow Moon

看来这俩还没找到喜欢的课。或者就是忘了报名 :)

例子 3. 查找报名人数超过 5 的课程

courses 表:

course_id name
1 数学
2 历史
3 生物
4 哲学

enrollments 表:

student_id course_id
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 NULL

我们想找那些报名人数超过五个的课程。这里 EXISTS 就像在问:“这个课程有没有一组报名记录,学生数大于五?”

SELECT name
FROM courses c
WHERE EXISTS (
    SELECT 1
    FROM enrollments e
    WHERE c.course_id = e.course_id
    GROUP BY e.course_id
    HAVING COUNT(*) > 5
);

结果:

数学

只有“数学”(course_id = 1)有六个学生报名。其他课程还没这么火。

用 EXISTS 和 NOT EXISTS 时常见的坑

  1. 对子查询语法理解错了。一定要检查子查询有没有正确引用外部表。
  2. 忘了处理 NULL。即使用 EXISTS,有时候也得明确写出 NULL 的处理。
  3. 子查询字段没加索引。这样会让查询慢得飞起。

今天就到这!现在你已经知道怎么用 EXISTS 和 NOT EXISTS 检查数据是否存在,也明白了它们和 IN 的区别。下节课我们继续深入子查询,看看怎么在 SELECT 里用它们处理聚合数据。

2
任务
SQL SELF, 第 13 级, 课程 3
已锁定
查找没有成绩的学生
查找没有成绩的学生
2
任务
SQL SELF, 第 13 级, 课程 3
已锁定
检查有学生注册的课程
检查有学生注册的课程
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION