LEFT JOIN右表必须建立与ON条件字段顺序、类型严格匹配的联合索引,否则触发全表扫描;右表过滤条件须写在ON而非WHERE中,否则索引失效且语义退化为INNER JOIN。

LEFT JOIN右表不建索引会触发全表扫描
MySQL 和 PostgreSQL 在执行 LEFT JOIN 时,对右表的访问方式高度依赖索引。如果没有索引,优化器只能对右表做全表扫描(type = ALL),每匹配左表一行,就扫一遍右表——左表有 10 万行,右表就要被扫 10 万次,I/O 和 CPU 开销爆炸式增长。
常见错误现象:EXPLAIN 显示右表的 key 为 NULL、rows 值极大、Extra 出现 Using join buffer (Block Nested Loop) 或 Using where; Using join buffer。
- 即使右表只有几万行,没索引也会让查询从毫秒级拖到秒级
- 复合条件如
ON b.user_id = a.id AND b.status = 'active',只建status索引没用,必须建(user_id, status)联合索引 - 字段类型不一致(比如
a.id是BIGINT,b.user_id是VARCHAR)会导致隐式转换,索引直接失效
ON子句字段类型和索引顺序必须严格匹配
右表索引不是“有就行”,而是要和 ON 中的字段顺序、类型、表达式完全对齐。优化器不会智能推导函数等价性,也不会自动拆解复合条件。
例如 ON b.ref_id = a.id AND b.deleted = 0:
- ✅ 正确索引:
INDEX(ref_id, deleted)(最左前缀匹配ref_id) - ❌ 无效索引:
INDEX(deleted, ref_id)(ref_id不在最左,无法用于等值查找) - ❌ 无效写法:
ON CAST(b.ref_id AS SIGNED) = a.id(函数包裹导致索引失效) - ❌ 隐式转换:
a.id是INT,b.ref_id是CHAR,哪怕值全是数字,也会放弃索引
WHERE里过滤右表字段会让索引“白建”
很多人建了索引却没效果,是因为把右表过滤条件写在了 WHERE 而非 ON。一旦这么写:LEFT JOIN b ON a.id = b.a_id WHERE b.status = 'done',语义上已等价于 INNER JOIN,但执行计划仍按 LEFT JOIN 路径走——右表先被全量扫描,再被 WHERE 过滤掉大量 NULL 行,索引根本没参与连接过程。
- ✅ 正确做法:把右表过滤条件移到
ON子句:LEFT JOIN b ON a.id = b.a_id AND b.status = 'done' - ✅ 若必须用
WHERE(比如还要结合左表字段做联合判断),至少确保该条件不单独出现在右表字段上,否则优化器大概率忽略索引 - ⚠️ 特别注意:
WHERE b.id IS NOT NULL这种写法,表面是判空,实际彻底退化为INNER JOIN语义,且无法利用索引加速连接
视图中LEFT JOIN会让右表索引更难生效
在视图定义里写 LEFT JOIN,等于把右表“固化”进执行逻辑。主查询哪怕只选左表三列,优化器也必须完整执行视图里的 LEFT JOIN,无法反向裁剪右表数据集。此时右表索引是否启用,极度依赖统计信息准确度和条件位置。
- 视图外的
WHERE对右表字段过滤,发生在LEFT JOIN完成之后,索引全程无参与机会 - 视图定义中
ON条件若含函数(如ON u.id = CAST(b.user_id AS UNSIGNED)),索引 100% 失效 - 右表数据批量导入后未运行
ANALYZE TABLE,优化器可能误判基数,宁可走全表扫描也不走索引
ON 和 WHERE 的语义边界,再好的索引也形同虚设。

















