子查询及其分类
目录
- 1.概念
- 2.例子
- 一、例子:
- 1. 标量子查询(返回 1 行 1 列,常作 “单一条件值”)
- 2. 列子查询(返回 1 列多行,常配`IN`/`ANY`/`ALL`)
- 3. 行子查询(返回 1 行多列,常作 “多字段匹配条件”)
- 4. 表子查询(返回多行多列,常作 “临时表” 供主查询调用)
- 二、按在主查询中的位置分类(补充场景)
- 1. WHERE 子句中的子查询(前面已覆盖,再补 1 个业务例)
- 2. FROM 子句中的子查询(即表子查询,再补 1 个复杂例)
- 3. SELECT 子句中的子查询(每行数据对应 1 个计算值)
- 关键总结
- sql语句的执行顺序:
1.概念
子查询是嵌套在其他 SQL 查询(如 SELECT
、INSERT
、UPDATE
、DELETE
语句)中的查询。它可以将一个查询的结果作为另一个查询的条件或数据来源,能让复杂的查询需求通过分步、嵌套的方式实现。
子查询主要有以下几类:
- 按返回结果行数分类:
- 标量子查询:返回单一值(一行一列),可用于
WHERE
子句中与某个值进行比较,比如SELECT * FROM student WHERE age > (SELECT AVG(age) FROM student);
,这里子查询返回学生年龄的平均值,主查询筛选出年龄大于该平均值的学生。 - 列子查询:返回单列多行的数据,常与
IN
、ANY
、ALL
等操作符一起使用,例如SELECT * FROM student WHERE class_id IN (SELECT id FROM class WHERE grade = '高三');
,子查询返回高三班级的id
列(多行),主查询筛选出班级id
在这些id
中的学生。 - 行子查询:返回单行多列的数据,可用于与一行数据进行比较,比如
SELECT * FROM student WHERE (age, score) = (SELECT age, score FROM student WHERE name = '张三');
,子查询返回张三的年龄和分数(一行两列),主查询筛选出年龄和分数与之相同的学生。 - 表子查询:返回多行多列的数据,可作为
FROM
子句中的数据源(通常需要给子查询取别名),例如SELECT t.name, t.avg_score FROM (SELECT name, AVG(score) AS avg_score FROM student GROUP BY name) AS t WHERE t.avg_score > 80;
,子查询返回每个学生的姓名和平均分数(多行多列),主查询再筛选出平均分数大于 80 的学生姓名和平均分数。
- 标量子查询:返回单一值(一行一列),可用于
- 按在主查询中的位置分类:
WHERE
子句中的子查询:如前面提到的标量子查询、列子查询等,用子查询结果作为WHERE
子句的条件。FROM
子句中的子查询:即表子查询,将子查询结果当作一张临时表供主查询FROM
子句使用。SELECT
子句中的子查询:子查询位于SELECT
子句中,用于为每行数据生成一个计算值,例如SELECT name, (SELECT AVG(score) FROM student) AS all_avg_score FROM student;
,子查询返回所有学生的平均分数,主查询为每个学生显示姓名和这个总平均分数。
2.例子
一、例子:
先明确基础表结构(方便理解例子):
表名 | 关键字段 |
---|---|
students | 学生 ID (sid )、姓名 (sname )、年龄 (age )、班级 ID (cid ) |
scores | 成绩 ID (scid )、学生 ID (sid )、科目 (subject )、分数 (score ) |
classes | 班级 ID (cid )、班级名称 (cname )(如 “高三 1 班”) |
1. 标量子查询(返回 1 行 1 列,常作 “单一条件值”)
业务场景:查询 “年龄大于全校平均年龄” 的学生姓名和年龄
(子查询先算 “全校平均年龄”,主查询用这个值当筛选条件)
SELECT sname, age
FROM students
-- 子查询返回“单一数值”(如平均年龄17.5),用>、<等符号比较
WHERE age > (SELECT AVG(age) -- 子查询:计算所有学生的平均年龄(1行1列)FROM students
);
结果示例:只显示年龄超过 17.5 的学生,比如(李四,18)、(王五,19)。
2. 列子查询(返回 1 列多行,常配IN
/ANY
/ALL
)
业务场景:查询 “在‘高三班级’中的所有学生姓名和班级 ID”
(子查询先找 “所有高三班级的 ID”,主查询筛选学生的班级 ID 在这个范围内)
SELECT sname, cid
FROM students
-- 子查询返回“多个班级ID”(如1、2),用IN判断“是否在其中”
WHERE cid IN (SELECT cid -- 子查询:找出所有班级名称含“高三”的班级ID(1列多行)FROM classesWHERE cname LIKE '高三%' -- 匹配“高三1班”“高三2班”等
);
结果示例:只显示属于高三班级的学生,比如(张三,1)、(李四,2)。
3. 行子查询(返回 1 行多列,常作 “多字段匹配条件”)
业务场景:查询 “与‘张三’年龄和班级都相同” 的学生(含张三本人)
(子查询先找 “张三的年龄和班级 ID”,主查询用这两个值同时匹配)
SELECT sname, age, cid
FROM students
-- 子查询返回“1行2列”(如张三的年龄18、班级ID1),用=匹配多字段
WHERE (age, cid) = (SELECT age, cid -- 子查询:获取“张三”的年龄和班级ID(1行2列)FROM studentsWHERE sname = '张三' -- 确保子查询只返回1行(张三只有1个)
);
结果示例:显示所有 “年龄 18 且班级 ID1” 的学生,比如(张三,18,1)、(赵六,18,1)。
4. 表子查询(返回多行多列,常作 “临时表” 供主查询调用)
业务场景:查询 “平均分数大于 80 分的学生姓名和其平均分数”
(子查询先算 “每个学生的平均分数”,主查询再筛选平均分数 > 80 的记录)
-- 子查询作为“临时表t”,必须给临时表取别名(这里叫t)
SELECT t.sname, t.avg_sc
FROM (-- 子查询:计算每个学生的平均分数(多行多列:学生姓名、平均分数)SELECT s.sname, AVG(sc.score) AS avg_scFROM students sJOIN scores sc ON s.sid = sc.sid -- 关联学生表和成绩表GROUP BY s.sid, s.sname -- 按学生分组,算每个学生的平均分
) AS t -- 给子查询的临时表取名t
WHERE t.avg_sc > 80; -- 主查询:筛选临时表中平均分数>80的记录select s.name ,avg(score ) from sinner join (select sid, avg(score ) from sc group by sid having avg(score)>80 ) t on t.sid =s.id-- 这个一次出结果
select s.sid, s.sname,avg(score) from s inner join sc on s.id=sc.sid group by s.sid,s.sname having avg(score) >8 0;
结果示例:只显示平均分超 80 的学生,比如(张三,85)、(王五,82)。
二、按在主查询中的位置分类(补充场景)
1. WHERE 子句中的子查询(前面已覆盖,再补 1 个业务例)
业务场景:查询 “数学成绩大于 90 分的学生姓名”
(子查询先找 “数学> 90 的学生 ID”,主查询用 ID 匹配姓名)
SELECT sname
FROM students
WHERE sid IN (SELECT sid -- 子查询:找出数学分数>90的学生IDFROM scoresWHERE subject = '数学' AND score > 90
);
2. FROM 子句中的子查询(即表子查询,再补 1 个复杂例)
业务场景:查询 “每个班级中,语文成绩最高的学生姓名、班级名称和语文分数”
(子查询先算 “每个班级的语文最高分”,再关联表查具体学生)
SELECT s.sname, c.cname, sc.score
FROM students s
JOIN classes c ON s.cid = c.cid
JOIN scores sc ON s.sid = sc.sid
-- 关联“每个班级的语文最高分”临时表
JOIN (-- 子查询:按班级分组,算每个班级的语文最高分(班级ID、最高分)SELECT s.cid, MAX(sc.score) AS max_chineseFROM students sJOIN scores sc ON s.sid = sc.sidWHERE sc.subject = '语文'GROUP BY s.cid
) AS t ON s.cid = t.cid AND sc.score = t.max_chinese -- 匹配班级+分数
WHERE sc.subject = '语文'; -- 确保只查语文成绩
结果示例:每个班级显示 1 名语文最高分学生,比如(张三,高三 1 班,98)、(李四,高三 2 班,95)。
3. SELECT 子句中的子查询(每行数据对应 1 个计算值)
业务场景:查询 “所有学生的姓名、数学分数,以及全校数学的平均分数”
(子查询算 “全校数学平均分”,主查询为每个学生行都显示这个平均分)
SELECT s.sname,sc.score AS math_score,-- 子查询在SELECT中,每行都显示同一个“全校数学平均分”(如82)(SELECT AVG(score) FROM scores WHERE subject = '数学') AS all_math_avg
FROM students s
JOIN scores sc ON s.sid = sc.sid
WHERE sc.subject = '数学'; -- 只查数学成绩
结果示例:
sname | math_score | all_math_avg |
---|---|---|
张三 | 95 | 82 |
李四 | 78 | 82 |
王五 | 85 | 82 |
关键总结
子查询的核心价值是 “拆分复杂逻辑”—— 把 “需要先算的条件 / 数据” 放在子查询中,主查询专注于 “最终筛选 / 展示”,所有例子的本质都是 “先算子查询,再用子查询结果帮主查询干活”,理解业务场景后就能灵活套用。
sql语句的执行顺序:
FROM 和 JOIN - 确定数据源
WHERE - 行级过滤
GROUP BY - 分组
HAVING - 组级过滤 ← 在这里!
SELECT - 选择列并可以定义别名
ORDER BY - 排序
LIMIT - 限制结果