位置:首页 > SQL > SQL LEFT JOIN 与 NOT EXISTS 性能对比及优化建议

SQL LEFT JOIN 与 NOT EXISTS 性能对比及优化建议

时间:2026-08-18  |  作者:骑光打字机  |  阅读:0

NOT EXISTS通常比LEFT JOIN...IS NULL性能更好,因其采用半连接机制,找到首个匹配即停止扫描,内存占用低;而后者需全量连接后再过滤,中间结果集大、易溢出。

SQL LEFT JOIN和NOT EXISTS哪个性能更好

NOT EXISTS 通常比 LEFT JOIN ... IS NULL 性能更好,尤其在子表(右表)数据量大、有索引、或字段允许 NULL 时。但不是绝对——最终得看执行计划,而不是语法本身。

为什么 NOT EXISTS 多数情况下更快?

NOT EXISTS 本质上属于半连接(semi-join / anti-join):一旦优化器找到第一条匹配记录,就可以立刻停止继续扫描;而 LEFT JOIN ... IS NULL 往往得先把整次连接完整跑完,再从结果里筛出 NULL 行,这样一来,中间结果集很可能会膨胀得非常大。

  • 对每个主表行,NOT EXISTS 子查询最多查 1 次索引页(比如 INDEX RANGE SCAN + STOP KEY
  • LEFT JOIN 可能触发 Hash Right JoinMaterialize,内存占用高,甚至溢出到磁盘
  • 当右表字段宽(如含 TEXTJSON)、重复多、或无索引时,LEFT JOIN 的中间数据膨胀更明显
  • PostgreSQL 12+ 和 SQL Server 对 NOT EXISTS 有原生 anti-join 优化;而 LEFT JOIN ... IS NULL 不一定被自动等价重写

LEFT JOIN ... IS NULL 什么时候反而快?

极少数场景下,LEFT JOIN 可能胜出,但需同时满足多个条件:

  • 右表非常小(比如几百行),且已全部缓存在 buffer pool 中
  • 优化器为 LEFT JOIN 选了 Nested Loop + Index Seek,而对 NOT EXISTS 错误预估子查询返回行数,触发了物化(MATERIALIZED)或临时表(MySQL 中预估 >100 行可能退化)
  • 你强制加了 STRAIGHT_JOIN(MySQL)或 OPTION (RECOMPILE)(SQL Server),让驱动顺序可控,且统计信息新鲜
  • 右表连接列无索引,但左表极小 —— 此时 LEFT JOINNested Loop 扫描总代价可能低于 NOT EXISTS 的多次随机 I/O(但这是病态设计,应先建索引)

常见错误:写法不对直接废掉性能

两种写法都容易因细节翻车,不注意就失去所有优势:

  • NOT EXISTS 子查询漏掉关联条件(如写成 WHERE o.status = 'active' 而没写 o.user_id = u.id),变成非相关子查询 → 对主表每行都扫一遍右表全量
  • LEFT JOINON 条件混入过滤逻辑(如 ON o.user_id = u.id AND o.status = 'active'),会导致语义变化:它查的是「没有 active 订单的用户」,而非「没有任何订单的用户」
  • 右表连接字段未建索引,或复合索引顺序错(比如 WHERE o.a = u.a AND o.b = u.b,但索引是 (b, a) 而非 (a, b)
  • LEFT JOIN 中右表字段本身允许 NULL,又用 WHERE o.user_id IS NULL —— 这会把本该匹配的行也判为“不存在”,逻辑错误优先于性能

怎么验证到底谁快?别猜,看执行计划

可以直接运行 EXPLAIN FORMAT=TREE(MySQL 8.0+)、EXPLAIN (ANALYZE, BUFFERS)(PostgreSQL)或 SET STATISTICS XML ON(SQL Server),接下来要盯紧的,主要就是这些关键点:

  • 是否出现 Nested Loop Anti JoinHash Anti JoinMerge Anti Join —— 这是 NOT EXISTS 被正确优化的标志
  • rows_examined_per_scan 值:越接近主表行数 × 1 越好(说明短路生效);若接近主表 × 右表,则 NOT EXISTS 已退化
  • 右表操作符是否有 Using index conditionIndex Only Scan;若显示 Seq ScanHeap Scan,先建索引
  • 是否有 MaterializeSpill to diskTemporary table 等字样 —— 这些是 LEFT JOIN 性能崩坏的明确信号
真正影响性能的从来不是关键字本身,而是执行路径是否短路、中间数据是否可控、索引是否命中。哪怕语法写对了,统计信息过期、参数嗅探失效、或优化器版本老旧,都可能让 NOT EXISTS 走不出 anti-join。动手前,先跑 EXPLAIN

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多