mysql LEFT JOIN像不像集合并集_mysql连接原理理解

2次阅读

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

mysql LEFT JOIN像不像集合并集_mysql连接原理理解

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:因为 WHEREo.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 是否为 refrangeExtra 是否含 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

text=ZqhQzanResources