Mysql--基础知识点--94.1--嵌套子查询转关联查询

什么是嵌套子查询?为什么要改成关联查询?

嵌套子查询(Non-correlated subquery)是指子查询独立于外层查询,不引用外层的列。它先执行子查询得到结果集,然后外层查询利用这个结果集进行过滤或计算。

关联子查询(Correlated subquery)是指子查询引用了外层查询的列,对外层每一行都要执行一次子查询(通常可以利用索引快速判断)。

在某些场景下,将嵌套子查询改写成关联查询 (如 EXISTSJOIN)可以大幅提升性能,避免子查询产生巨大的中间结果集,或者避免 NULL 带来的逻辑陷阱。


典型改写场景:INEXISTS

原始嵌套子查询(使用 IN

sql 复制代码
-- 找出所有下过至少一单的客户
SELECT * FROM customers 
WHERE customer_id IN (
    SELECT customer_id FROM orders   -- 子查询独立,不依赖外层
);

执行逻辑

  1. 先完整执行子查询:从 orders 表中取出所有 customer_id,可能几十万行。
  2. 将结果集去重并构建哈希表。
  3. customers 每一行检查 customer_id 是否在哈希表中。

问题 :如果 orders 表非常大(几百万行),子查询结果集巨大,会消耗大量内存和 I/O。

改写为关联查询(使用 EXISTS

sql 复制代码
SELECT * FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.customer_id = c.customer_id   -- 关联条件
);

执行逻辑(实际优化器会做半连接):

  1. 遍历 customers 表(假设只有几千行)。
  2. 对每个客户,在 orders 表的索引上快速查找是否存在该客户的订单,找到一行就立即返回 TRUE,不再继续扫描。
  3. 整个过程无需物化庞大的订单 ID 集合。

优势

  • 外表小、内表大 时,EXISTS 通常远快于 IN
  • 避免了大结果集的物化,内存压力小。
  • 可以利用 orders.customer_id 上的索引。

另一种改写:嵌套子查询 → JOIN(关联查询)

sql 复制代码
-- 同样查询有订单的客户,使用 JOIN(注意去重)
SELECT DISTINCT c.* 
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id;

注意JOIN 可能导致客户重复(一个客户有多笔订单),所以需要 DISTINCT。如果 customers.customer_id 是主键,DISTINCT 的开销通常可接受。

性能特点

  • 数据库优化器可能将 INEXISTS 转换为类似的 JOIN 执行计划。
  • 但显式 JOIN 有时更灵活,可以同时获取订单表的其他字段。

什么时候不应该改写?

  • 子查询结果集很小 (如几十行)且不经常执行:IN 写法更直观,性能差异可忽略。
  • 需要判断 NOT IN 且子查询无 NULL :但最好还是用 NOT EXISTSLEFT JOIN 避免 NULL 陷阱。
  • 子查询是标量查询 (返回单个值),例如 SELECT ... WHERE salary > (SELECT AVG(salary) FROM employees),这种无法简单改成关联查询,因为子查询只需执行一次。

总结对照表

写法 子查询类型 执行次数 适用场景
IN (SELECT ...) 嵌套(非关联) 子查询执行1次 子查询结果集小,且外表大
EXISTS (SELECT ... WHERE 关联) 关联 外表每行执行1次(但可提前终止) 外表小,内表大,且内表有索引
JOIN ... ON 关联 关联 一次连接操作 需要同时获取两表数据,注意去重

核心建议

  • 默认优先考虑语义清晰 ,但遇到性能问题时,把嵌套子查询(特别是 INNOT IN)改写成关联查询(EXISTSJOIN)是非常有效的优化手段。
  • 使用 EXPLAIN 观察执行计划,确认数据库是否自动做了优化。
相关推荐
Flying pigs~~5 小时前
RAG智慧问答项目
数据库·人工智能·缓存·微调·知识库·rag
misL NITL6 小时前
mysql之如何获知版本
数据库·mysql
许彰午6 小时前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存
2401_832365527 小时前
JavaScript中rest参数(...args)取代arguments的优势
jvm·数据库·python
2301_779622418 小时前
Go语言怎么用信号量控制并发_Go语言semaphore信号量教程【入门】
jvm·数据库·python
2301_766283448 小时前
c++如何将控制台输出保存到文件_cout重定向到txt【详解】
jvm·数据库·python
北极的冰箱8 小时前
MySQL Ver 8.0.41 for macos14.7密码遗忘
数据库·mysql
XDH_CS8 小时前
MySQL 8.0 安装与 MySQL Workbench 使用全流程(超详细教程)
开发语言·数据库·mysql
秋99 小时前
MySQL 8.0.46 全平台安装与配置详解(Windows/Linux/macOS)
linux·windows·mysql
treacle田9 小时前
达梦数据库-统计信息收集-记录
数据库·达梦数据库统计信息收集