子查询入门|标量 / 行 / 列子查询,简化复杂查询
前言
我们已经学会用多表 JOIN 完成复杂业务查询,但在很多场景下,先查一个值 / 一列 / 一行,再用它做条件会更简单直观,这就是子查询。子查询是 SQL 从 “会用” 到 “熟练” 的关键台阶,也是面试与日常开发高频用法。
本篇从定义、分类、语法、场景、案例、避坑完整精讲,带你一次性掌握标量子查询、列子查询、行子查询三大基础类型,代码可直接复制运行
一、本章知识点汇总
- 子查询定义与执行逻辑
- 子查询三大基础类型:标量、列、行
- 标量子查询:返回单行单列,用
= > < >= <= - 列子查询:返回单列多行,用
IN / ANY / ALL - 行子查询:返回单行多列,用复合条件匹配
- 子查询书写位置:SELECT / WHERE / FROM
- 三大子查询实战案例(用户 - 订单 - 商品场景)
- 子查询与 JOIN 简单对比
- 高频注意事项与职场规范
- 课后练习题 + 思路
二、各知识点详解
1. 什么是子查询?
子查询(嵌套查询):在一个 SQL 内部嵌套另一个 SELECT 查询,先执行内层查询,把结果交给外层查询使用。
- 外层查询:主查询
- 内层查询:子查询
- 子查询必须用( )包裹
- 执行顺序:先子查询,后主查询
2. 三大基础子查询(按返回结果分类)
表格
| 类型 | 返回结果 | 可用操作符 | 常用位置 | 难度 |
|---|---|---|---|---|
| 标量子查询 | 单行单列(一个值) | = > < >= <= <> | WHERE / SELECT | ★☆☆ |
| 列子查询 | 单列多行(一列值) | IN / ANY / ALL | WHERE | ★★☆ |
| 行子查询 | 单行多列(一组值) | 多字段组合匹配 | WHERE | ★★★ |
3. 标量子查询(最常用、最简单)
核心定义
返回一行一列(一个值:数字、字符串、日期)。
适用场景
- 查单个值做条件:高于平均分、最新时间、最大 ID
- SELECT 里动态加计算列
语法
sql
SELECT 字段 FROM 表 WHERE 字段 = (标量子查询);4. 列子查询(条件范围查询)
核心定义
返回一列多行(一列表数据),相当于一个值列表。
适用场景
- 查询在某个集合内的数据:所有有订单的用户、买过某类商品的人
- 替代多表连接简化逻辑
常用关键字
- IN:在列表中任意一个
- ANY:满足任意一个
- ALL:满足所有
5. 行子查询(多字段组合匹配)
核心定义
返回一行多列,用于同时匹配多个字段的场景。
适用场景
- 同时匹配:部门 + 职位、分类 + 价格、用户 + 状态
- 简化多条件并列写法
语法
sql
WHERE (字段1,字段2) = (行子查询)三、实战环境准备(直接复制运行)
沿用前面电商表结构,确保连贯学习:
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 ); -- 商品表 CREATE TABLE goods ( goods_id INT PRIMARY KEY, goods_name VARCHAR(30), price DECIMAL(10,2) ); 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'); INSERT INTO goods VALUES (1,'笔记本',299),(2,'键盘',159),(3,'T恤',99);四、应用案例及结果分析
案例 1:标量子查询实战(高频)
需求 1:查询订单金额高于平均金额的订单
sql
SELECT * FROM orders WHERE amount > (SELECT AVG(amount) FROM orders);- 子查询先算:平均订单金额
- 外层筛选:大于平均值的订单
- 典型报表筛选用法
需求 2:SELECT 中使用标量子查询(动态列)
查询所有订单,并显示订单总金额(全局总计):
sql
SELECT order_id, user_id, amount, (SELECT SUM(amount) FROM orders) AS total_amount FROM orders;案例 2:列子查询实战(范围匹配)
需求:查询下过订单的所有用户信息
sql
SELECT * FROM users WHERE user_id IN (SELECT DISTINCT user_id FROM orders);- 子查询返回:所有有订单的 user_id 列表
- 外层 IN 匹配:只保留有订单的用户
- 等价于内连接,但更简洁
案例 3:行子查询实战(组合匹配)
需求:查询与订单 1001同用户、同金额的订单
sql
SELECT * FROM orders WHERE (user_id, amount) = ( SELECT user_id, amount FROM orders WHERE order_id=1001 );- 一次性匹配两个字段
- 比写
user_id=xx AND amount=xx更简洁
五、注意事项
- 标量子查询必须只返回 1 个值返回多行 → 报错:子查询返回多于 1 行
- 列子查询只能返回 1 列多列会直接语法错误
- 行子查询字段数量、类型必须一一对应
- 子查询尽量简单,不要嵌套过深(可读性差)
- 能用子查询简化就不用复杂 JOIN,但超大表优先 JOIN
- 不要在子查询里用 ORDER BY(无意义、影响性能)
- 条件优先放WHERE,少用 HAVING + 子查询
六、核心总结
- 子查询 = 查询嵌套查询,先内后外执行
- 三大基础类型:
- 标量:单行单列 → 用
= > < - 列:单列多行 → 用
IN / ANY / ALL - 行:单行多列 → 组合匹配
- 标量:单行单列 → 用
- 标量子查询最常用:平均值、最大值、动态列
- 列子查询适合:范围筛选、存在性判断
- 行子查询适合:多字段同时匹配
- 子查询让复杂逻辑分步拆解,更易读、易维护
一句话记忆:标量返回一个值,列是列表用 IN;行是组合多字段,子查询先内后外真省心
七、课后实战练习题(附思路)
题目 1
用标量子查询:查询价格高于所有商品平均价的商品名称、价格。
题目 2
用列子查询:查询没有下过订单的用户(NOT IN)。
题目 3
用行子查询:查询和 “张三” 同一个用户、且金额相同的所有订单。
参考答案思路
题目 1:
sql
SELECT goods_name, price FROM goods WHERE price > (SELECT AVG(price) FROM goods);题目 2:
sql
SELECT * FROM users WHERE user_id NOT IN (SELECT DISTINCT user_id FROM orders);题目 3:
sql
SELECT * FROM orders WHERE (user_id, amount) = ( SELECT user_id, amount FROM orders WHERE user_id = (SELECT user_id FROM users WHERE user_name='张三') );