EXISTS 与 NOT EXISTS,一篇彻底搞懂存在性查询
大家好,欢迎来到《LeetCode SQL 从入门到进阶(MySQL)》专栏。
上一篇我们学习了子查询(Subquery)的基本使用方法,本篇继续学习另一类在 LeetCode 和实际开发中都非常重要的知识------EXISTS 与 NOT EXISTS。
很多初学者知道 IN,却不知道什么时候应该使用 EXISTS。事实上,它们虽然都可以完成类似的查询,但执行思路完全不同。
一、什么是 EXISTS?
EXISTS 的作用非常简单:
判断子查询是否能够返回至少一条记录。
语法:
SELECT ...
FROM 表A
WHERE EXISTS (
SELECT 1
FROM 表B
WHERE ...
);
如果子查询能够返回数据,则 EXISTS 返回 TRUE;否则返回 FALSE。
二、一个简单例子
员工表:
| id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Tom |
订单表:
| id | employeeId |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
现在要求:
查询至少拥有一个订单的员工。
可以写:
SELECT *
FROM Employee e
WHERE EXISTS (
SELECT 1
FROM Orders o
WHERE o.employeeId = e.id
);
执行过程:
对于 Alice:
Orders 中是否存在 employeeId=1?
有。
因此保留。
对于 Bob:
Orders 中是否存在 employeeId=2?
没有。
因此过滤。
对于 Tom:
Orders 中是否存在 employeeId=3?
有。
因此保留。
最终得到:
| name |
|---|
| Alice |
| Tom |
三、为什么写 SELECT 1?
很多人第一次都会疑惑:
SELECT 1
为什么不是:
SELECT *
实际上:
对于 EXISTS 来说,
数据库并不关心返回什么内容。
它只关心:
有没有记录。
因此:
SELECT 1
SELECT *
SELECT id
效果完全一样。
一般推荐:
SELECT 1
表达:
我只是判断是否存在。
四、NOT EXISTS
NOT EXISTS 就是:
不存在。
例如:
查询没有任何订单的员工。
SELECT *
FROM Employee e
WHERE NOT EXISTS (
SELECT 1
FROM Orders o
WHERE o.employeeId = e.id
);
最终得到:
| name |
|---|
| Bob |
五、EXISTS 与 IN 的区别
例如:
SELECT *
FROM Employee
WHERE id IN (
SELECT employeeId
FROM Orders
);
和:
SELECT *
FROM Employee e
WHERE EXISTS (
SELECT 1
FROM Orders o
WHERE o.employeeId=e.id
);
都能得到相同结果。
区别在于:
IN
更像:
先把子查询所有结果取出来,再判断是否属于这个集合。
EXISTS
更像:
找到第一条符合条件的数据后立即停止。
因此:
当子查询数据很多时,
EXISTS 往往更高效。
六、相关子查询
注意:
WHERE o.employeeId=e.id
这里:
子查询引用了外层查询的:
e.id
这种写法叫:
相关子查询(Correlated Subquery)。
它是 EXISTS 最常见的使用方式。
七、LeetCode 常见应用
常见题型:
-
查询存在订单的用户
-
查询没有登录过的用户
-
查询购买过商品的顾客
-
查询未参加考试的学生
基本都可以想到:
EXISTS
或者:
NOT EXISTS
八、常见错误
错误一
忘记关联条件。
错误:
WHERE EXISTS (
SELECT 1
FROM Orders
)
因为:
只要 Orders 非空,
所有员工都会返回。
正确:
WHERE EXISTS (
SELECT 1
FROM Orders o
WHERE o.employeeId=e.id
)
错误二
把 EXISTS 和 IN 混用。
例如:
WHERE EXISTS (
SELECT employeeId
)
EXISTS 不关心返回哪一列。
真正重要的是:
子查询能否返回记录。
九、本章总结
今天学习了:
-
EXISTS:存在至少一条记录。
-
NOT EXISTS:不存在任何记录。
-
EXISTS 常与相关子查询一起使用。
-
EXISTS 判断存在性,IN 判断是否属于集合。
掌握 EXISTS 后,你已经具备解决绝大多数存在性查询问题的能力。
下一篇,我们将进入字符串处理部分,学习 MySQL 中最常见的字符串函数。