前言
本篇聚焦EXISTS、IN、ANY、ALL 四大高阶关键字,从原理、用法、性能对比、面试高频、实战优化全方位精讲,帮你从 "会写子查询" 升级到 "写好子查询"。
一、本章知识点汇总
- EXISTS 存在查询核心原理
- IN / ANY / ALL 关键字用法与区别
- EXISTS 与 IN 的性能对比(大表必看)
- NOT EXISTS 与 NOT IN 的陷阱(NULL 问题)
- 四大关键字适用场景与企业规范
- 实战案例:存在性判断、范围匹配、优化改写
- 子查询优化通用原则
- 高频面试题与避坑指南
- 核心总结
- 课后练习题 + 答案思路
二、各知识点详解
1. EXISTS(存在性查询,面试 + 优化必考)
核心原理
EXISTS 只判断 "有没有",不查具体数据。
- 子查询返回至少 1 行 → 结果为 TRUE
- 子查询返回0 行 → 结果为 FALSE
- 语法:
EXISTS (子查询) - 特点:外表驱动内表,遇到匹配立即返回,不继续扫描
语法
sql
SELECT 字段 FROM 外表 A
WHERE EXISTS (
SELECT 1 FROM 内表 B
WHERE A.关联字段 = B.关联字段
);
写法规范:子查询里固定写 SELECT 1,不查真实字段,提升速度。
2. IN(包含匹配,最常用)
核心原理
判断字段是否在指定列表 / 子查询结果中。
- 语法:
字段 IN (值1,值2,值3)或字段 IN (子查询) - 匹配到任意一个 → TRUE
- 注意:遇到 NULL 会直接返回 NULL,导致结果异常
3. ANY(任意一个满足,较少用)
核心原理
和子查询结果中 "任意一个值" 比较成功即可。
- 必须搭配:
> < >= <= = <> 字段 > ANY(子查询)→ 大于子查询中任意一个就行- 等价于:大于最小值
4. ALL(全部满足,极少用)
核心原理
和子查询结果中 "所有值" 都比较成功才行。
- 必须搭配:
> < >= <= = <> 字段 > ALL(子查询)→ 大于所有值- 等价于:大于最大值
5. 四者核心区别(面试必背)
表格
| 关键字 | 作用 | 返回 | 性能 | 适用场景 |
|---|---|---|---|---|
| EXISTS | 是否存在 | TRUE/FALSE | 大表极高 | 关联存在判断 |
| IN | 是否在列表中 | TRUE/FALSE | 小表快、大表慢 | 固定值 / 小结果集 |
| ANY | 任意一个满足 | TRUE/FALSE | 一般 | 范围比较 |
| ALL | 全部满足 | TRUE/FALSE | 一般 | 极值比较 |
6. 超级重点:EXISTS 与 IN 性能(企业必懂)
- 小表查大表:IN 更快
- 大表查大表 :EXISTS 完胜 IN(MySQL 优化器对 EXISTS 极友好)
- 千万级表:必须用 EXISTS,IN 会严重卡顿
职场规范:不确定数据量时,优先写 EXISTS
7. NOT IN 致命陷阱(NULL)
当子查询返回 NULL 时:
NOT IN (NULL)→ 结果永远是空- 企业坑点:用 NOT IN 查询无订单用户,遇到脏数据直接漏数据
解决方案:永远用 NOT EXISTS 替代 NOT IN
三、实战环境准备(直接复制运行)
sql
-- 用户表
CREATE TABLE users (
user_id INT PRIMARY KEY,
user_name VARCHAR(20) NOT NULL
);
-- 订单表
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
order_time DATE
);
INSERT INTO users VALUES (1,'张三'),(2,'李四'),(3,'王五'),(4,'赵六');
INSERT INTO orders VALUES
(1001,1,299,'2024-06-01'),
(1002,2,420,'2024-06-02'),
(1003,3,88,'2024-06-03');
四、应用案例及结果分析
案例 1:EXISTS 查 "有订单的用户"(推荐写法)
sql
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
);
- 性能最优
- 大表不卡顿
- 企业标准写法
案例 2:IN 查 "有订单的用户"(小表可用)
sql
SELECT * FROM users
WHERE user_id IN (SELECT user_id FROM orders);
案例 3:NOT EXISTS 查 "无订单用户"(最安全)
sql
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
);
- 无 NULL 陷阱
- 不会丢数据
- 生产环境唯一推荐写法
案例 4:ANY 查询 "比任意订单金额高的订单"
sql
SELECT * FROM orders
WHERE amount > ANY (SELECT amount FROM orders);
- 大于任意一个 → 大于最小值
案例 5:ALL 查询 "比所有订单金额都高的订单"
sql
SELECT * FROM orders
WHERE amount > ALL (SELECT amount FROM orders WHERE order_id != 1002);
- 大于所有 → 大于最大值
五、注意事项
- 大表关联必用 EXISTS,禁止用 IN
- 禁止用 NOT IN,必须用 NOT EXISTS
- EXISTS 子查询固定写 SELECT 1,不要查字段
- ANY/ALL 尽量少用,可读性差,可改用 MAX/MIN 替代
- 子查询不要嵌套超过 3 层,可读性极差
- 关联字段必须建索引,否则 EXISTS 也会慢
- 不要在子查询中做多余排序、分组,影响性能
六、核心总结
- EXISTS 只判断存在,大表性能最强
- IN 适合小列表,大表慎用
- NOT IN 有 NULL 陷阱,生产禁用
- NOT EXISTS 是无数据查询唯一标准
- ANY/ALL 可读性差,优先用 MAX/MIN 改写
- 优化口诀:小表用 IN,大表用 EXISTS,非存在必用 NOT EXISTS
一句话记忆: 存在判断用 EXISTS,包含匹配小表 IN;非存在别用 NOT IN,NULL 坑人要小心
七、课后实战练习题(附思路)
题目 1
用 EXISTS 查询:所有有订单且订单金额 > 200 的用户。
题目 2
用 NOT EXISTS 查询:从来没有下过单的用户。
题目 3
把下面语句改写成性能更高的 EXISTS 写法:
sql
SELECT * FROM goods WHERE goods_id IN (SELECT goods_id FROM order_item);
参考答案思路
题目 1:
sql
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
AND o.amount > 200
);
题目 2:
sql
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.user_id
);
题目 3:
sql
SELECT * FROM goods g
WHERE EXISTS (
SELECT 1 FROM order_item oi
WHERE oi.goods_id = g.goods_id
);