left join实际驱动表由explain的table列首行决定,语法左表固定为驱动表,但嵌套子查询可能改变;需检查type、key、rows及字符集/类型一致性。

LEFT JOIN 的连接顺序不能靠 SQL 写法决定,必须看 EXPLAIN 的 table 列从上到下的物理执行顺序——左表固定为驱动表,但“谁是左表”在嵌套或子查询里可能和你直觉相反。
怎么看 LEFT JOIN 实际驱动表?
在 Navicat 中右键 SQL → “解释已选择的”,重点盯 EXPLAIN 输出的 table 列:
- 第一行的表就是驱动表(对 LEFT JOIN 来说,它必须是语法上最左侧的那个表,但若用了子查询或 CTE,实际驱动表可能是子查询结果)
- 如果第一行
type = ALL且rows极大,说明这个表被全表扫描了——哪怕它是小表,也代表 WHERE 过滤没生效或索引失效 - 注意:LEFT JOIN 的“左”只约束语义(左表全保留),不保证优化器不会把它当被驱动表;但若你在 FROM 后直接写
A LEFT JOIN B,A 就是强制驱动表
为什么加了索引,LEFT JOIN 还是 type=ALL?
常见硬性拦截点,不是建错索引,而是环境不匹配:
-
JOIN字段类型不一致:比如A.user_id INT对B.uid VARCHAR,MySQL 隐式转成字符串比较,B.uid索引完全失效 - 字符集或排序规则不同:用
SHOW FULL COLUMNS FROM table_name LIKE 'join_column'检查两边的Collation,哪怕都是utf8mb4,utf8mb4_0900_ai_ci和utf8mb4_general_ci不兼容 - 右表的过滤条件写在
WHERE而非ON:例如LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid',这会让 MySQL 先连再筛,右表无法下推索引,等效于 INNER JOIN
如何让 LEFT JOIN 真正走索引?
核心是“右表先瘦身,再连接”,而非堆索引:
- 右表务必用子查询预聚合或预过滤:
LEFT JOIN (SELECT user_id, MAX(create_time) AS last_time FROM orders WHERE create_time > '2025-01-01' GROUP BY user_id) o ON u.id = o.user_id - 左表能提前 WHERE 就别拖到 JOIN 后:把
WHERE u.dept = 'tech'放在最外层,比塞进子查询更易触发索引下推 - 避免
SELECT *:尤其右表有TEXT/BLOB字段时,IO 成倍增加,rows值会虚高,误导优化器 - 确认右表连接字段有单列索引:如
ON u.id = o.user_id,则o.user_id必须单独建索引(复合索引需是最左前缀)
STRAIGHT_JOIN 能不能救急?
不能。LEFT JOIN 本身已锁定左表为驱动表,STRAIGHT_JOIN 在 LEFT JOIN 场景下无效;它只对 INNER JOIN 或 RIGHT JOIN 有调整作用。强行加反而报错或被忽略。
真正容易被忽略的是:当你用 WITH 或子查询重写 LEFT JOIN 逻辑时,驱动表可能悄悄变成子查询结果——这时 EXPLAIN 的第一行不再是原始左表,而是子查询别名,必须重新评估它的 rows 和索引使用情况。











