LEFT JOIN 是左表全集与右表匹配子集的组合,带 NULL 填充,非集合并集;其本质是左表驱动的嵌套循环关联,ON 控制匹配、WHERE 控制最终过滤,二者语义严格区分。

LEFT JOIN 不是集合并集,它是“左表全集 + 右表匹配子集”的组合,带 NULL 填充;而并集(UNION)是去重后的行合并,语义和结果都完全不同。
LEFT JOIN 的本质:左表驱动的保留式关联
它不是数学集合运算,而是基于驱动表逐行扫描、按条件查找匹配的执行过程。MySQL 内部用 Nested-Loop Join(嵌套循环)实现:
– 左表每行作为基准,去右表找所有满足 ON 条件的行;
– 找到则拼接成结果行;
– 找不到就补 NULL,但左表这行仍保留。
-
LEFT JOIN结果行数 ≥ 左表行数(一对多时会膨胀) - 右表无索引时,可能触发
Block Nested-Loop Join,导致全表扫描右表多次,性能骤降 -
ON条件只控制“如何匹配”,不负责过滤;WHERE才真正筛最终结果——这点极易误用
SELECT u.id, u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.amount > 100;
⚠️ 这条语句实际等效于 INNER JOIN:因为 WHERE 对 o.amount 过滤,会把 o.amount IS NULL 的行全干掉。想保留用户即使没大额订单,得写成:
SELECT u.id, u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.amount > 100;
为什么不能当成 UNION 理解?
UNION 是垂直拼接两组独立查询结果,要求列数、类型兼容,且自动去重;LEFT JOIN 是水平扩展字段,行与行之间存在逻辑绑定关系(靠 ON 维系),且绝不自动去重。
-
UNION:两表各查 10 行 → 最多返回 20 行(去重后可能更少) -
LEFT JOIN:左表 10 行 × 右表平均 2 行匹配 → 返回 20 行(无去重,结构拉宽) - 字段来源不同:
UNION字段必须同名/同序;LEFT JOIN可混用任意字段,甚至加计算列
连接原理落地:驱动表 + 索引决定性能生死线
MySQL 优化器默认选小表作驱动表,但 LEFT JOIN 强制左表为驱动表——所以左表数据量要可控,右表必须在 ON 字段上有索引,否则就是灾难。
- 检查执行计划:
EXPLAIN SELECT ... LEFT JOIN ...,重点看type是否为ref或range,Extra是否含Using index condition - 若
Extra出现Using join buffer (Block Nested Loop),说明右表没走索引,正在暴力扫描 - 右表连接字段缺失索引?立刻加:
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
最容易被忽略的坑:ON 和 WHERE 的语义鸿沟
这是线上慢查和空结果的头号元凶。记住口诀:
– ON 定义“谁跟谁配对”;
– WHERE 定义“最后留哪些行”。
- 把右表过滤条件写在
ON里 → 保留左表全部,未匹配项右字段为NULL - 把右表过滤条件写在
WHERE里 → 实际砍掉所有右表不满足的行,包括本该保留的左表记录 - 多表
LEFT JOIN时,每个ON只作用于紧邻的右表,不跨表生效
复杂关联下,宁可拆成子查询或 CTE 显式控制逻辑,也不要靠直觉堆 LEFT JOIN + WHERE。










