Mysql 04: 子查询——5 大核心用法

子查询(Subquery),也叫嵌套查询,是 MySQL 中在一个查询语句内部嵌套另一个查询的高级语法,能实现复杂的多表关联、条件过滤、数据筛选,是 SQL 进阶的核心知识点。

本文围绕图片中的 5 大类子查询,从语法、逻辑、代码示例三个维度,带你彻底掌握 MySQL 子查询。


一、子查询基础概念

1. 核心定义

  • 子查询 :嵌套在 SELECT/INSERT/UPDATE/DELETE 语句中的 SELECT 语句,也叫「内查询」
  • 外层查询:包含子查询的主语句,也叫「外查询」
  • 执行顺序先执行子查询,再执行外层查询,子查询的结果作为外层查询的条件 / 数据源

2. 准备测试数据

创建两张关联表,用于所有示例:

学生表 student
sql 复制代码
CREATE TABLE student (
    stu_id INT PRIMARY KEY AUTO_INCREMENT,
    stu_name VARCHAR(20) NOT NULL,
    class VARCHAR(10) NOT NULL,
    age INT NOT NULL
);
INSERT INTO student VALUES
(1, '张三', '一班', 18),
(2, '李四', '二班', 19),
(3, '王五', '一班', 18),
(4, '赵六', '三班', 20),
(5, '孙七', '二班', 19);
成绩表 score
sql 复制代码
CREATE TABLE score (
    score_id INT PRIMARY KEY AUTO_INCREMENT,
    stu_id INT NOT NULL,
    subject VARCHAR(20) NOT NULL,
    score INT NOT NULL,
    FOREIGN KEY (stu_id) REFERENCES student(stu_id)
);
INSERT INTO score VALUES
(1, 1, '语文', 85),
(2, 1, '数学', 92),
(3, 2, '语文', 78),
(4, 2, '数学', 88),
(5, 3, '数学', 88),
(6, 4, '英语', 60),
(7, 5, '语文', 95);

二、5 大类子查询详解


1. 带比较运算符的子查询(最基础)

核心逻辑

= > < >= <= != 等比较运算符,将外层查询的字段与子查询返回的单行单列结果进行比较。

要求:子查询必须返回1 行 1 列的标量值,否则报错。

代码示例
sql 复制代码
-- 需求:查询成绩 > 班级平均分的学生信息
-- 步骤1:先查二班的平均分(子查询,返回标量)
-- 步骤2:外层查询筛选成绩 > 平均分的学生
SELECT s.stu_name, sc.subject, sc.score
FROM student s
JOIN score sc ON s.stu_id = sc.stu_id
WHERE s.class = '二班' 
  AND sc.score > (
      -- 子查询:计算二班的平均分
      SELECT AVG(score) FROM score 
      WHERE stu_id IN (SELECT stu_id FROM student WHERE class = '二班')
  );
运行结果
stu_name subject score
李四 数学 88
孙七 语文 95
说明

子查询先算出二班平均分 (78+88+95)/3 ≈ 87,外层查询筛选出二班中成绩 > 87 的学生。


2. 带 IN 关键字的子查询(最常用)

核心逻辑

IN 用于判断外层字段是否在子查询返回的多行单列结果集中 ,等价于「多个 OR 条件」。

要求:子查询返回多行 1 列的结果集。

代码示例
sql 复制代码
-- 需求:查询有成绩的学生信息(子查询返回所有有成绩的stu_id)
SELECT * FROM student
WHERE stu_id IN (
    -- 子查询:返回所有有成绩的stu_id(多行单列)
    SELECT DISTINCT stu_id FROM score
);

-- 反向:查询没有成绩的学生(NOT IN)
SELECT * FROM student
WHERE stu_id NOT IN (SELECT DISTINCT stu_id FROM score);
运行结果(IN 示例)
stu_id stu_name class age
1 张三 一班 18
2 李四 二班 19
3 王五 一班 18
4 赵六 三班 20
5 孙七 二班 19
说明

子查询先查出所有有成绩的 stu_id,外层查询用 IN 匹配学生表,筛选出对应学生。


3. 带 ANY/SOME 关键字的子查询

核心逻辑

ANYSOME 完全等价,表示 **「满足子查询结果中的任意一个」**,即「只要有一个满足条件,就返回 true」。

语法:字段 > ANY(子查询) → 字段 > 子查询结果中的任意一个值 (等价于 > 最小值)语法:字段 < ANY(子查询) → 字段 < 子查询结果中的任意一个值(等价于 < 最大值)

代码示例
sql 复制代码
-- 需求:查询成绩 > 一班任意学生成绩的学生(即 > 一班最低分)
SELECT s.stu_name, sc.subject, sc.score
FROM student s
JOIN score sc ON s.stu_id = sc.stu_id
WHERE sc.score > ANY(
    -- 子查询:一班所有学生的成绩(85,92,88)
    SELECT sc2.score FROM score sc2
    JOIN student s2 ON sc2.stu_id = s2.stu_id
    WHERE s2.class = '一班'
);
运行结果
stu_name subject score
张三 数学 92
李四 数学 88
王五 数学 88
孙七 语文 95
说明

一班成绩为 [85,92,88]> ANY 等价于 > 85(最小值),因此所有 >85 的成绩都会被筛选出来。


4. 带 ALL 关键字的子查询

核心逻辑

ALL 表示 **「满足子查询结果中的所有值」**,即「必须全部满足条件,才返回 true」。

语法:字段 > ALL(子查询) → 字段 > 子查询结果中的所有值 (等价于 > 最大值)语法:字段 < ALL(子查询) → 字段 < 子查询结果中的所有值(等价于 < 最小值)

代码示例
sql 复制代码
-- 需求:查询成绩 > 一班所有学生成绩的学生(即 > 一班最高分)
SELECT s.stu_name, sc.subject, sc.score
FROM student s
JOIN score sc ON s.stu_id = sc.stu_id
WHERE sc.score > ALL(
    -- 子查询:一班所有学生的成绩(85,92,88)
    SELECT sc2.score FROM score sc2
    JOIN student s2 ON sc2.stu_id = s2.stu_id
    WHERE s2.class = '一班'
);
运行结果
stu_name subject score
孙七 语文 95
说明

一班最高分为 92> ALL 等价于 > 92,因此只有孙七的 95 分满足条件。


5. 带 EXISTS 关键字的子查询(关联子查询)

核心逻辑

EXISTS关联子查询,子查询会引用外层查询的字段,执行逻辑为:

  1. 外层查询逐行遍历表
  2. 子查询用当前行的字段进行查询
  3. 若子查询返回至少 1 行结果 ,则 EXISTStrue,外层行保留;否则丢弃

特点:子查询不返回实际数据,只返回 true/false,性能极高(适合大表)

代码示例
sql 复制代码
-- 需求:查询有成绩的学生信息(EXISTS 版本,等价于 IN 版本)
SELECT * FROM student s
WHERE EXISTS(
    -- 关联子查询:用外层s的stu_id查询成绩表
    SELECT 1 FROM score sc 
    WHERE sc.stu_id = s.stu_id
);

-- 反向:查询没有成绩的学生(NOT EXISTS)
SELECT * FROM student s
WHERE NOT EXISTS(
    SELECT 1 FROM score sc 
    WHERE sc.stu_id = s.stu_id
);
运行结果

IN 示例完全一致,返回所有有成绩的学生。

关键说明
  • EXISTS 子查询中 SELECT 1 是最优写法(无需查询实际字段,只判断是否有行)
  • EXISTS 性能远优于 IN,尤其是大表场景(IN 会全表扫描,EXISTS 逐行匹配)
  • IN 适合子查询结果集小的场景,EXISTS 适合子查询结果集大的场景

三、核心对比与避坑指南

1. 5 大类子查询核心区别

子查询类型 关键字 子查询结果要求 核心逻辑 适用场景
比较运算符 = > < 单行单列 与标量值比较 单值条件过滤
IN IN 多行单列 匹配结果集中任意一个 多值匹配、去重筛选
ANY/SOME ANY/SOME 多行单列 满足任意一个 「比任意一个大 / 小」场景
ALL ALL 多行单列 满足所有 「比所有都大 / 小」场景
EXISTS EXISTS 任意(只看是否有行) 关联匹配,返回 true/false 大表关联、高性能筛选

2. 常见避坑

  1. 子查询结果行数错误

    • 比较运算符要求子查询必须返回1 行 1 列,否则报错
    • IN/ANY/ALL 要求子查询返回多行 1 列,否则逻辑错误
  2. NOT IN 空值陷阱

    • 若子查询结果包含 NULLNOT IN 会返回空结果 (因为 NULL 参与比较结果为 UNKNOWN
    • 解决方案:用 NOT EXISTS 替代 NOT IN,或在子查询中过滤 NULL
  3. 关联子查询性能优化

    • EXISTS 子查询中,关联字段必须加索引,否则性能骤降
    • 避免在子查询中使用 SELECT *,用 SELECT 1 最优
  4. 子查询嵌套层级

    • MySQL 支持多层子查询嵌套,但层级过多会严重影响性能,尽量用连接查询替代

四、综合实战:多子查询组合

sql 复制代码
-- 需求:查询二班中,成绩 > 一班所有学生成绩的学生信息
SELECT s.stu_name, sc.subject, sc.score
FROM student s
JOIN score sc ON s.stu_id = sc.stu_id
WHERE s.class = '二班'
  AND sc.score > ALL(
      SELECT sc2.score FROM score sc2
      JOIN student s2 ON sc2.stu_id = s2.stu_id
      WHERE s2.class = '一班'
  );

运行结果

stu_name subject score
孙七 语文 95

五、核心总结

  1. 子查询本质:嵌套查询,先内后外,子查询结果作为外层条件
  2. 5 大核心用法
    • 比较运算符:单值比较,子查询必须返回标量
    • IN:多值匹配,子查询返回多行单列
    • ANY/SOME:任意满足,等价于「> 最小 / < 最大」
    • ALL:全部满足,等价于「> 最大 / < 最小」
    • EXISTS:关联匹配,高性能,适合大表
  3. 性能优先级EXISTS > IN > 多层子查询
  4. 避坑关键 :注意子查询结果行数、NOT IN 空值陷阱、关联字段加索引
相关推荐
小雨下雨的雨12 小时前
井字棋AI机器人实现详解 - Minimax算法实战-鸿蒙PC Electron框架完成
前端·人工智能·算法·华为·electron·鸿蒙
睡不醒男孩03082313 小时前
第二篇:深入探索开源数据库高可用:构建基于CLup的PostgreSQL生产级高可用与读写分离架构
数据库·postgresql·开源·clup
xieliyu.15 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
Micro麦可乐15 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
一条小锦吕*15 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化
码农阿豪15 小时前
从零到一:Spring Boot快速接入金仓数据库实战
数据库·spring boot·后端
鼎讯信通16 小时前
风电光缆运维提质增效:G-4000A 光缆故障追踪仪破解风场巡检难题
运维·网络·数据库
三十..16 小时前
MySQL 从入门到高可用架构实战精要
运维·数据库·mysql
cfm_291417 小时前
Redis五大基本数据结构底层了解
数据结构·数据库·redis
如竟没有火炬17 小时前
最大矩阵——单调栈
数据结构·python·线性代数·算法·leetcode·矩阵