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 观察执行计划,确认数据库是否自动做了优化。
相关推荐
倔强的石头_3 天前
《Kingbase护城河》——数据库存储空间全景探测与精细化瘦身实战
数据库
云技纵横3 天前
唯一索引 INSERT 死锁实战:5 秒复现交叉插入的 S 锁循环等待
sql·mysql
沉默王二3 天前
面试官:RAG 不用向量数据库,用 MySQL 硬扛?我:100 万向量不是很轻松?
mysql·面试·ai编程
冬奇Lab4 天前
每日一个开源项目(第134篇):Zvec - 阿里开源的嵌入式向量数据库,向量搜索界的 SQLite
数据库·人工智能·llm
小猿姐4 天前
MySQL Top 10 热点问题 AI 运维实战:从内核诊断到云原生运维
mysql·云原生·aiops
ClouGence4 天前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
云技纵横4 天前
Gap Lock 死锁实战:5 秒在本地复现 MySQL 间隙锁死锁
后端·mysql
无响应de神4 天前
三、用户与权限管理
数据库·mysql
摇滚侠5 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql