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。
