SQL子查询完全指南:从零掌握嵌套查询的三种用法与最佳实践 - 详解

news/2025/12/1 15:32:42/文章来源:https://www.cnblogs.com/ljbguanli/p/19292988

SQL子查询完全指南:从零掌握嵌套查询的三种用法与最佳实践 - 详解

SQL子查询完全指南:从零掌握嵌套查询的三种用法与最佳实践

在实际的数据库开发中,我们经常遇到这样的需求:需要将每个产品的价格与平均价格进行比较,或者查找从未下过订单的客户。这些看似简单的业务需求,仅用一条 SELECT 语句往往难以实现。

这时候就需要用到 SQL 子查询(Subquery)。子查询是嵌套在其他查询中的 SELECT 语句,它能帮助我们将复杂的查询逻辑分解为多个步骤,使代码更加清晰易懂。

本文将系统地介绍 SQL 子查询的概念、三种主要使用位置、常见陷阱以及与 JOIN 的对比。学完本文,你将能够:

  • 理解子查询的工作原理

  • 掌握在 WHERE、SELECT、FROM 子句中使用子查询

  • 了解 EXISTS 和 IN 的区别

  • 知道何时使用子查询,何时使用 JOIN


一、子查询基础概念

1.1 什么是子查询

子查询(Subquery),也称为内部查询嵌套查询,是指包含在另一个 SQL 查询中的 SELECT 语句。

基本语法示例:

SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
);

执行流程:

  1. 内部查询(子查询)先执行:计算 employees 表的平均工资 → 假设结果为 60000

  2. 外部查询使用该结果:筛选出工资高于 60000 的员工记录

1.2 子查询的分类

根据返回结果的不同,子查询可分为三种类型:

类型返回结果示例
标量子查询单个值(1行1列)(SELECT AVG(price) FROM products)
列子查询一列多行(SELECT customer_id FROM orders)
表子查询多行多列(SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id)

关键原则: 子查询返回的数据类型必须与其使用位置相匹配。


二、WHERE 子句中的子查询

2.1 与标量值比较

WHERE 子句中的子查询最常用于与聚合函数结果进行比较。

查询高于平均价格的产品:

SELECT product_name, price
FROM products
WHERE price > (
    SELECT AVG(price) FROM products
);

支持的比较运算符:><=>=<=<>

查询工资高于特定员工的所有员工:

SELECT name, salary
FROM employees
WHERE salary > (
    SELECT salary
    FROM employees
    WHERE name = 'John Doe'
);

2.2 使用 IN 操作符

IN 操作符用于检查某个值是否存在于子查询返回的结果集中。

查询已下过订单的客户:

SELECT customer_id, name
FROM customers
WHERE customer_id IN (
    SELECT customer_id FROM orders
);

语义解释: 返回 customer_id 出现在 orders 表中的所有客户记录。

2.3 NOT IN 的 NULL 陷阱

错误示例:

-- ⚠️ 如果 orders 表中存在 NULL 值,此查询可能返回 0 行
SELECT customer_id, name
FROM customers
WHERE customer_id NOT IN (
    SELECT customer_id FROM orders
);

问题分析:

当子查询结果中包含 NULL 时,NOT IN 的逻辑会变成:

WHERE customer_id <> value1 AND customer_id <> value2 AND customer_id <> NULL

由于任何值与 NULL 的比较都返回 NULL(未知),整个 WHERE 条件变为 NULL,导致该行被过滤。

解决方案:

  1. 在子查询中过滤 NULL:

WHERE customer_id NOT IN (
    SELECT customer_id FROM orders WHERE customer_id IS NOT NULL
)
  1. 使用 NOT EXISTS(推荐):

WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
)

三、SELECT 子句中的子查询

3.1 添加计算列

SELECT 子句中的子查询用于为每行数据计算额外的列。

为每个客户添加订单数量:

SELECT
    customer_id,
    name,
    (
        SELECT COUNT(*)
        FROM orders o
        WHERE o.customer_id = c.customer_id
    ) AS order_count
FROM customers c;

执行机制: 这是一个相关子查询(Correlated Subquery),对外部查询的每一行都会执行一次子查询。

3.2 性能考虑

性能问题:

如果外部查询返回 10,000 行,子查询就会执行 10,000 次,在大数据集上性能较差。

优化方案:使用 JOIN

SELECT c.customer_id, c.name, COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;

3.3 使用场景

适用场景:

  • ✅ 小数据集或临时分析查询

  • ✅ 代码可读性优先于性能

  • ✅ 计算逻辑用 JOIN 实现较复杂

不适用场景:

  • ❌ 生产环境的大表查询

  • ❌ 需要多个相关子查询的情况

  • ❌ JOIN 实现更清晰的场景


四、EXISTS 与 NOT EXISTS

4.1 EXISTS 的工作原理

EXISTS 用于检查子查询是否返回任何行,返回布尔值(TRUE/FALSE)。

查询已下过订单的客户:

SELECT customer_id, name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id
);

执行特点:

  • 对外部查询的每一行执行子查询

  • 找到第一个匹配行即停止(短路评估)

  • 不关心子查询的列内容,只关心是否有结果

4.2 NOT EXISTS 的应用

查询从未下过订单的客户:

SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id
);

4.3 EXISTS vs IN 对比

特性EXISTSIN
返回类型布尔值值列表匹配
NULL 处理不受 NULL 影响NOT IN 遇 NULL 失败
性能短路评估,找到即停构建完整列表
使用场景存在性检查简单值匹配
子查询类型通常为相关子查询通常为非相关子查询

最佳实践: 对于存在性检查,优先使用 EXISTS/NOT EXISTS,避免 NOT IN 的 NULL 陷阱。

4.4 SELECT 1 的含义

-- 以下写法等价
WHERE EXISTS (SELECT 1 FROM ...)
WHERE EXISTS (SELECT * FROM ...)
WHERE EXISTS (SELECT customer_id FROM ...)

SELECT 1 是约定俗成的写法,明确表示"不关心具体数据,只关心行是否存在"。


五、FROM 子句中的子查询(派生表)

5.1 基本概念

FROM 子句中的子查询称为派生表(Derived Table)或内联视图(Inline View),用于创建临时结果集。

基本语法:

SELECT columns
FROM (
    subquery
) AS alias  -- 别名是必需的
WHERE conditions;

5.2 实际应用

查询订单数超过 5 的客户:

SELECT *
FROM (
    SELECT
        customer_id,
        COUNT(*) AS order_count,
        SUM(amount) AS total_spent
    FROM orders
    GROUP BY customer_id
) AS customer_summary
WHERE order_count > 5;

为什么需要派生表:

不能在 WHERE 子句中直接使用聚合函数结果,需要先在子查询中聚合,再在外层查询中过滤。

5.3 与 HAVING 的对比

使用 HAVING(简单场景):

SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5;

使用派生表(复杂场景):

适用于需要多步骤聚合或复杂过滤逻辑的情况。

5.4 派生表 vs 临时表

特性派生表临时表
作用域单个查询整个会话
创建自动需要显式创建
索引不支持支持
清理自动需要手动删除
重用性不可重用可在会话中重用

六、子查询 vs JOIN:选择策略

6.1 使用子查询的场景

存在性检查

-- 使用 EXISTS 检查客户是否下过订单
WHERE EXISTS (SELECT 1 FROM orders WHERE ...)

与聚合值比较

-- 查找高于平均值的记录
WHERE price > (SELECT AVG(price) FROM products)

构建中间结果集

-- 在 FROM 中使用派生表
FROM (SELECT ... GROUP BY ...) AS summary

6.2 使用 JOIN 的场景

需要多表列

-- 需要客户和订单的详细信息
SELECT c.name, o.order_date, o.amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id

行级别数据合并

-- 将两个表的数据逐行合并
FROM table1 JOIN table2 ON ...

性能关键场景

  • 大数据集上,JOIN 通常比相关子查询快

  • 可以利用索引优化

6.3 实例对比

场景:查询已下过订单的客户

方案 1:子查询(推荐用于存在性检查)

SELECT customer_id, name
FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);

方案 2:JOIN(需要订单详情时使用)

SELECT c.customer_id, c.name, o.order_id, o.amount
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

6.4 决策树

是否需要多表的列?
├─ 是 → 使用 JOIN
└─ 否 → 是否是存在性检查?
    ├─ 是 → 使用 EXISTS
    └─ 否 → 是否需要与集合比较?
        ├─ 是 → 使用 IN(注意NULL)
        └─ 否 → 需要复杂计算?
            ├─ 是 → 使用子查询或CTE
            └─ 否 → 使用 JOIN(通常最快)

七、最佳实践与性能优化

7.1 编码规范

  1. 为派生表添加有意义的别名

FROM (SELECT ...) AS customer_summary  -- Good
FROM (SELECT ...) AS t1                -- Bad
  1. 优先使用 EXISTS 而非 NOT IN

-- 推荐
WHERE NOT EXISTS (SELECT 1 FROM orders WHERE ...)
-- 避免
WHERE customer_id NOT IN (SELECT customer_id FROM orders)
  1. 避免在 SELECT 中使用相关子查询处理大数据

-- 对大表性能差
SELECT name, (SELECT COUNT(*) FROM orders WHERE ...) FROM customers;
-- 应改用 JOIN
SELECT c.name, COUNT(o.id) FROM customers c LEFT JOIN orders o ... GROUP BY c.name;

7.2 性能优化技巧

  1. 使用 EXPLAIN 分析执行计划

EXPLAIN
SELECT * FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
  1. 为关联字段建立索引

CREATE INDEX idx_orders_customer_id ON orders(customer_id);
  1. 考虑使用 CTE 替代复杂子查询

WITH customer_summary AS (
    SELECT customer_id, COUNT(*) AS order_count
    FROM orders
    GROUP BY customer_id
)
SELECT * FROM customer_summary WHERE order_count > 5;
  1. 避免重复的子查询

-- 不好:子查询执行两次
WHERE price > (SELECT AVG(price) FROM products)
  AND price < (SELECT MAX(price) FROM products)
-- 好:使用派生表或 CTE
WITH price_stats AS (
    SELECT AVG(price) AS avg_price, MAX(price) AS max_price
    FROM products
)
SELECT * FROM products, price_stats
WHERE price > avg_price AND price < max_price;

7.3 常见错误

标量子查询返回多行

-- 错误:子查询返回多行
WHERE salary > (SELECT salary FROM employees WHERE department = 'Sales')
-- 正确:使用聚合函数
WHERE salary > (SELECT MAX(salary) FROM employees WHERE department = 'Sales')

派生表缺少别名

-- 错误
SELECT * FROM (SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id)
-- 正确
SELECT * FROM (SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id) AS summary

过度使用相关子查询

-- 性能差:每行执行一次
SELECT name, (SELECT COUNT(*) FROM orders WHERE customer_id = c.customer_id)
FROM customers c;
-- 改用 JOIN + GROUP BY
SELECT c.name, COUNT(o.id)
FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name;

八、总结

子查询的三个使用位置:

  1. WHERE 子句 — 用于过滤(最常见)

  2. SELECT 子句 — 用于计算列(注意性能)

  3. FROM 子句 — 用于派生表(复杂逻辑)

关键操作符:

  • IN / NOT IN — 值列表匹配(注意 NULL 陷阱)

  • EXISTS / NOT EXISTS — 存在性检查(推荐)

  • 比较运算符 — 与标量值比较

选择策略:

  • 存在性检查 → EXISTS

  • 与聚合值比较 → 子查询

  • 需要多表列 → JOIN

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/983055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rust自定义迭代器

Rust自定义迭代器 Rust要自定迭代器要实现Iterator这个Trait,就可以使用next方法,也可以实现IntoIterator方法就可以把对象转换为一个迭代器。这与Python中的可迭代对象和迭代器的概念类似。在Python中,实现了__ite…

适合幼儿园开展的STEM课程品牌介绍及分析

在当前以科技教育为主的大环境,幼儿园如何开展STEM教育,哪个课程品牌更容易落地,风险更小呢? 在教育部等七部门发布《关于加强中小学科技教育的意见》的大背景,很多人在思考:在幼儿园阶段引入STEM教育(科学、技…

分库分表全面总结

目录分库分表全面总结分库分表的本质分库分表方式示例1:垂直分库(按业务拆分到不同数据库)示例2:垂直分表(按列拆分到不同表)示例3:水平分库(数据分布到不同数据库的同名表)示例4:水平分表(数据分布到同库的…

2025年深圳品牌策划公司推荐排行榜:深圳品牌策划公司能整合

在企业品牌竞争白热化的当下,选择一家能整合资源、提供稳定优质服务的深圳品牌策划公司,成为众多企业突破增长瓶颈的关键。为帮助企业精准选型,我们从资源整合能力、服务稳定性、服务质量、客户口碑及案例落地效果五…

2025年中国十大网店代运营公司推荐:代运营哪家强?

本榜单依托产业带深度调研与工厂真实合作口碑,聚焦源头厂商电商痛点,筛选出十家能解决招人难、成本高、效果差的标杆代运营企业,为实体厂商选型提供客观依据,助力精准匹配适配的服务伙伴。 TOP1 推荐:广东飞客网…

2025年户外橡胶地垫制造厂权威推荐榜单:减震橡胶地垫/公园橡胶地垫/复合橡胶地垫源头厂家精选

户外橡胶地垫作为公共空间安全防护与运动场地建设的重要材料,其市场需求持续增长。据行业统计,我国户外橡胶地垫市场规模年均增长率达12%以上,其中厚度20mm以上的高规格产品占比超过40%。本文基于产品质量、市场覆盖…

靠谱的国际短信验证码平台,短信验证码服务商盘点,兼顾速度、安全与成本控制!

在全球化日益加深的今天,国际短信验证码服务已成为企业拓展海外市场、提升用户体验的关键环节。从跨境电商到金融科技,从游戏出海到远程办公,高效、稳定、安全的短信验证码平台成为企业不可或缺的合作伙伴。根据QYR…

【JPCS出版 | EI检索】第七届国际科技创新学术交流大会暨机械工程与自动化国际学术会议(MEA 2025)

第七届机械工程与自动化国际学术会议(MEA 2025)将于2025年12月19日至21日在中国广州举行。【往届见刊后快至1个月检索、连续5年稳定EI检索、易中稿】 第七届国际科技创新学术交流大会暨机械工程与自动化国际学术会议…

这个代码有什么用

$ a.out test init=ok search=34/34 candidate_num=157 dec=34 py2=shijiwentiyaodaoshijicainengjiejue $ a.out read successfully, lemma num: 65101 spelling tree construct successfully. ... $ make g++ -g -…

2025年日字扣生产厂家权威推荐榜单:箱包塑料配件‌/塑料制品‌/塑胶扣具‌源头厂家精选

日字扣,虽只是箱包、户外装备上不起眼的小部件,却是决定产品使用便利性与耐用性的关键。据行业报告,2025年全球滑扣市场(包含日字扣等调节扣具)正稳健发展,其增长得益于箱包、户外及功能服装市场的持续需求。 在…

2025年磷酸铁回转窑生产厂家权威推荐榜单:回转窑干燥设备‌/磷酸铁锂回转窑‌/大豆渣回转窑干燥机‌源头厂家精选

在新能源与绿色制造浪潮推动下,回转窑作为处理磷酸铁、大豆渣等复杂物料的关键装备,其市场需求与技术迭代正同步加速。2025年,中国回转窑市场已达到19.32亿元人民币的规模,全球市场更是高达50.84亿元,且市场正持续…

JavaScript基础笔记碎片-对象、数组、Map与Set

注意:本篇学习笔记基于原网站: JavaScript教程 - 廖雪峰的官方网站 笔记仅作学习留档使用 本篇目录对象 循环 Map和Set iterable对象 let object ={...}表示一个对象,键值对 以xxx: xxx形式申明,,隔开。最后一个键…

java17版本,用IDEA开发,在JavaFX中无法找到com.google.gson。

java17版本,用IDEA开发,在JavaFX中无法找到com.google.gson。 切换版本为<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.10.1…

2025手机配件到迪拜物流专线送仓TOP5权威推荐:甄选企业

中东跨境电商蓬勃发展,手机配件作为热门品类,年出口额超300亿元,但物流环节的痛点成为企业出海的大阻碍。据2024年行业调研,45%的手机配件出口企业因中东空运的头程延误、清关卡顿、末端派送失败,导致订单纠纷率高…

Visual Studio Code 轻量不简陋!VS Code:零基础编程者的第一款「万能编辑器」安装+中文设置步骤

简介 Visual Studio Code(简称 VS Code)是微软 2015 年 4 月在 Build 开发者大会发布的开源免费轻量级代码编辑器,它填补了普通文本编辑器和重型集成开发环境(IDE)之间的空白,如今已是全球多数开发者的首选工具,…

推荐几家海外展会推广公司,五家效果不错的海外展会营销推广平台详细介绍

随着 “一带一路” 倡议深入推进及 RCEP 区域合作深化,中国企业出海需求持续攀升。据中国会展经济研究院数据显示,2024 年中国境外自主办展总数达 216 个,增幅 13.68%,其中 78.24% 的展会布局在 “一带一路” 共建…

2025年12月AI 编程代码生成效率实战推荐:技术性能与应用场景的全面分析

在软件开发进入AI驱动的全新阶段,AI编程工具已成为提升开发者效率的核心要素。据IDC预测,2025年全球AI开发者工具和平台支出将达到224亿美元,年复合增长率高达24.8%;同时,Stack Overflow 2024年度开发者调查显示,…

开源高性能IM+集成AI能力,基于SpringBoot +Tauri+Vue 3+TypeScript支持全平台与丰富会话模式

开源高性能IM+集成AI能力,基于SpringBoot +Tauri+Vue 3+TypeScript支持全平台与丰富会话模式 天若源码教程实战2025年11月27日 07:00 湖南 点击“蓝色字”,关注我们哦!!一、平台介绍在某次兼职项目中,我受其他公司…