JOIN顺序由优化器基于成本模型决定,非SQL书写顺序;真正执行顺序以EXPLAIN输出的table列为据;LEFT JOIN右表过滤条件须写在ON中,否则退化为INNER JOIN;多表JOIN需确保ON条件完整、字段有索引,避免笛卡尔积。

JOIN顺序由优化器决定,不是你写的顺序
数据库不会按你SQL里FROM A JOIN B JOIN C的书写顺序执行。MySQL、PostgreSQL等会基于统计信息(行数、索引区分度)、成本模型选择代价最低的连接路径——可能先连A和C,再用结果去连B。这意味着调换表位置无法“强制先过滤小表”。
验证实际顺序的方法是看EXPLAIN输出:id最小且type为const、ref或range(非ALL)的那行,通常是驱动表;第一行不等于最先写的表。
- 旧版MySQL(5.7之前)优化器较弱,可临时用
STRAIGHT_JOIN强制顺序,但绕过优化器有风险,必须实测 - 不要依赖“把小表写前面”来提速,真正有效的是让小表带高选择性WHERE条件,且索引覆盖JOIN字段
- 三张及以上表JOIN时,漏写任一
ON条件极易触发笛卡尔积——比如A(1000)×B(1000)×C(1000) = 10亿行
LEFT JOIN中ON和WHERE对右表的过滤效果完全不同
ON在关联阶段生效,WHERE在关联完成后才过滤。这个区别在LEFT JOIN里直接决定左表是否“全量保留”。
错误写法:LEFT JOIN users u ON o.user_id = u.id WHERE u.status = 'active'——NULL值不满足等值判断,所有没匹配到active用户的订单行都会被干掉,LEFT JOIN退化成INNER JOIN。
- 要保留左表全部记录,只取右表满足条件的部分:把条件写进
ON,如ON o.user_id = u.id AND u.status = 'active' - 想先完整关联再筛结果:可用
WHERE,但右表字段只能做IS NULL或IS NOT NULL判断,否则仍会丢左表行 - 某些旧版PostgreSQL不支持
ON里对右表做非等值条件(如u.status = 'active'),需查对应版本文档
多次引用同一张表必须用不同别名
同一个表在FROM或JOIN中出现多次,别名必须唯一。否则解析失败,报错ERROR 1066 (42000): Not unique table/alias。
例如订单表要关联创建人、审核人、负责人(都来自users表),不能两次都写JOIN users u。
- 正确写法:
JOIN users creator ON o.creator_id = creator.id,JOIN users auditor ON o.auditor_id = auditor.id - 别名建议带语义(如
creator、auditor),比u1/u2更易维护 -
SELECT中所有同名列必须加前缀,否则报ambiguous column,例如不能写SELECT id,得写SELECT creator.id
驱动表选错是多表JOIN慢的最常见原因
驱动表被当作外层循环,每行都要去探查被驱动表。如果驱动表本身很大(如100万行),又没索引支撑JOIN条件,被驱动表就会被扫描100万次。
典型现象:慢查询日志显示Rows_examined远大于单表行数,比如t1有100行、t2有10万行,但Rows_examined高达上千万。
- 优先选带高选择性WHERE条件的表作驱动表(如
WHERE status = 'paid' AND ctime > '2024-01-01'),让它先被过滤缩小 - JOIN字段类型必须严格一致(
intvsbigint、varchar(50)vsvarchar(100)、是否带COLLATE),否则索引可能失效 - 外键字段不等于自动有索引,务必手动确认并创建;复合索引顺序应为:等值条件字段 → 范围条件字段 → JOIN字段(用于覆盖)
ANALYZE TABLE),优化器照样可能选错路径。

















