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 空值陷阱、关联字段加索引
相关推荐
宁小法2 小时前
MySQL- ORDINAL_POSITION 详解
mysql·表结构·字段顺序
码云数智-大飞2 小时前
数据库索引原理:B+树与哈希索引的深度对决
数据库·oracle
深邃-2 小时前
字符函数和字符串函数(2)
c语言·数据结构·c++·后端·算法·restful
bekote2 小时前
PTA基础编程题目集-6-11 求自定类型元素序列的中位数(简单解法)
数据结构·c++·算法
数据库小组11 小时前
2026 年,MySQL 到 SelectDB 同步为何更关注实时、可观测与可校验?
数据库·mysql·数据库管理工具·数据同步·ninedata·selectdb·迁移工具
华科易迅11 小时前
MybatisPlus增删改查操作
android·java·数据库
Kethy__11 小时前
计算机中级-数据库系统工程师-计算机体系结构与存储系统
大数据·数据库·数据库系统工程师·计算机中级
SHoM SSER11 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
米粒112 小时前
力扣算法刷题 Day 27
算法·leetcode·职场和发展