8.0
深览指数
科技Bestblogs·一只牛博··AI 生成

LEFT JOIN 到底什么时候会被优化器偷偷改写成 INNER JOIN

LEFT JOIN 在 WHERE 条件过滤右表字段时,会被优化器悄无声息地改写为 INNER JOIN,导致保留左表所有行的语义丢失。文章通过实验验证了三种边界条件:WHERE 过滤右表字段(非 IS NULL)会消除外连接;IS NULL 条件不消除;左表条件放入 ON 则不受影响。给出了五条判断规则,帮助 SQL 开发者避免生产环境的语义陷阱。适合有 2 年以上 SQL 开发经验、遇到过 JOIN 行数对不上的读者。原文 ↗

核心观点
  • WHERE 子句过滤右表可空字段(且不使用 IS NULL)会导致 LEFT JOIN 被优化器安全地消除,改写为 INNER JOIN,丢失外连接保底语义。
  • IS NULL 条件检测右表 NULL 行时,优化器不会消除外连接,因为改写会改变结果集。
  1. 01实验使用 MySQL 5.7 + 8.0 验证,执行计划中 `Hash Join`(不带 `Left`)是外连接被消除的直接证据。
  2. 02利⼦ db2 WHERE `a.name1 = 'b'` 过滤右表字段,若不使用 IS NULL,行结果集从 8 行变为仅 4 行(相当于 INNER JOIN 的结果)。
  3. 03同样语法在 Oracle 的 (+) 写法上验证,结果一致:WHERE 非 IS NULL 过滤右表列 → 外连接消除。
  4. 04如何保留外连接:把右表过滤条件从 WHERE 下推到 ON 子句,此时执行计划会恢复为 `Hash Left Join`,左表行完整保留。
反方 / 局限
  • IS NULL 过滤右表不会消除外连接,但会导致查询结果完全不同(只保留左右表均匹配的行 + 右表为 NULL 的行),这可能不是开发者预期的行为。
  • 文章只测试了单表 LEFT JOIN,多表链式外连接的场景下,优化器的消除规则可能更复杂,但作者未涉及。
3 分钟 · 5 卡片 · 13 资料
读原文 →

概念锚点

前置背景

平行视角

未来推演

延伸追问