大数跨境
0
0

子查询 vs JOIN 查询性能比较:谁更快?什么时候用?

子查询 vs JOIN 查询性能比较:谁更快?什么时候用? Linux运维技术之路
2025-11-24
8
导读:子查询 vs JOIN 查询性能比较:谁更快?什么时候用?在写 SQL 的时候,经常会纠结:“这里要用子查询好,还是 JOIN 好?

 










 

子查询 vs JOIN 查询性能比较:谁更快?什么时候用?

在写 SQL 的时候,经常会纠结:

“这里要用子查询好,还是 JOIN 好?”
“听说子查询性能差,是真的吗?”
“JOIN 会不会比子查询更重?”

这篇文章一次把两者的原理与性能差异讲清楚。


一、什么是子查询与 JOIN?

✅ 子查询(Subquery)

将一个查询嵌入到另一个查询中:


   
    
   SELECT *
FROM
 user
WHERE
 id IN (SELECT user_id FROM orders);

✅ JOIN 查询(连接查询)


   
    
   SELECT u.*, o.*
FROM
 user u
JOIN
 orders o ON u.id = o.user_id;

两者都能实现“关联多表”的目的,但执行原理不同。


二、性能核心差异总结

JOIN 一般比子查询更快,中大型数据量下几乎总是推荐 JOIN。

原因:JOIN 可以使用优化器对多表进行统一优化,而很多子查询(尤其是相关子查询)不能被完全优化。


三、为什么 JOIN 通常比子查询快?

1)JOIN 可以被 MySQL 优化器重写、索引优化更充分

JOIN 是显式的关联方式:

  • • 可以使用嵌套循环(Nested Loop)
  • • 可以使用驱动表/被驱动表策略
  • • 可以使用索引 join
  • • 可以选择更优的执行顺序
  • • 可以提前过滤数据(WHERE pushdown)

子查询(尤其是相关子查询)无法做到这些优化。


2)JOIN 只扫描必要的表,而子查询容易“重复扫描”

例如相关子查询:


   
    
   SELECT name,
       (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_cnt
FROM
 user u;

如果 users 有 1 万条记录:

  • • 子查询执行 10000 次
  • • 每次扫描 orders(很可能没有被缓存)

成本巨大。

JOIN 查询效果要好得多:


   
    
   SELECT u.name, COUNT(o.id)
FROM
 user u
LEFT
 JOIN orders o ON u.id = o.user_id
GROUP
 BY u.id;

JOIN 只扫描 一次 orders 表。


3)子查询可能导致临时表(temp table),JOIN 不一定

例如:


   
    
   SELECT * FROM orders
WHERE
 user_id IN (SELECT id FROM user WHERE status = 1);

MySQL 会把子查询结果写入临时表,再扫描判断 IN,可能是磁盘临时表:

  • • 额外 I/O
  • • 内存/磁盘开销大

JOIN 则不需要额外的临时表结构,优化器可以直接利用索引进行过滤:


   
    
   SELECT o.*
FROM
 orders o
JOIN
 user u ON o.user_id = u.id
WHERE
 u.status = 1;

四、是否子查询一定性能差?并不是!

MySQL 8.0 之后改进很大,某些子查询已经可以被优化器“合并重写”为 JOIN。

例如:


   
    
   SELECT * 
FROM
 orders
WHERE
 user_id IN (SELECT id FROM user);

MySQL 可能自动重写成 JOIN。

但以下场景仍然不能优化:

❌ 相关子查询

❌ 使用聚合函数的子查询

❌ 子查询依赖外层列

❌ 子查询中包含 LIMIT、ORDER BY

❌ 大子查询 + IN/NOT IN

❌ ANY/ALL/SOME 子查询

这些情况下 JOIN 显著优于子查询。


五、什么时候推荐 JOIN?

✔ 数据量大
✔ 复杂业务逻辑
✔ 需要多个关联字段过滤
✔ 子查询结果非常大
✔ 性能要求高的业务(订单流、推荐流、榜单、日志系统)

大部分实际业务场景都是这一类。


六、什么时候推荐子查询?(少数场景)

✔ 子查询结果非常小(例如 1 条)
✔ 子查询和主查询没有关联(非相关子查询)
✔ 比 JOIN 写法更直观(可读性优先)
✔ 想减少返回列数量(只希望过滤,不希望 JOIN 导致行增多)
✔ 简单 IN 子查询(MySQL 8.0 已可自动优化)

比如统计类 SQL:


   
    
   SELECT *
FROM
 orders
WHERE
 amount > (SELECT AVG(amount) FROM orders);

JOIN 无法替代。


七、总结

1、子查询 vs JOIN 的性能差异主要来自执行方式不同。
2、 JOIN 使用的是嵌套循环,可使用索引、驱动表优化、提前过滤等优势,一般性能更好。
3、而子查询可能导致多次扫描、临时表、大量重复执行,尤其是相关子查询,性能最差。
4、 MySQL 8.0 对部分子查询进行了优化,可以自动转为 JOIN,但不是全部。
实际开发中:
多表关联优先使用 JOIN;相关子查询尽量改写为 JOIN;子查询只在结果小且不适合 JOIN 时使用。

 




 

 


往期回顾


【声明】内容源于网络
0
0
Linux运维技术之路
专注运维架构、高可用、高并发、高性能、大数据、容器化、数据库、python、devops等开源技术和实践的分享。
内容 347
粉丝 0
Linux运维技术之路 专注运维架构、高可用、高并发、高性能、大数据、容器化、数据库、python、devops等开源技术和实践的分享。
总阅读751
粉丝0
内容347