外连接中驱动表优先匹配,被驱动表需关联字段索引;ON决定匹配逻辑,WHERE过滤最终结果;NULL填充无法索引加速,哈希外连接仅在无索引全扫时可能提速。
外连接执行时,驱动表和被驱动表怎么选
外连接的执行顺序不完全由 SQL 写法决定,而取决于优化器对
顺序的选择。左外连接(
)中,左侧表默认为驱动表,右侧表为被驱动表;右外连接(
)则相反。但若开启
或存在合适索引,优化器可能重排顺序——此时
的
列顺序和
字段(如
vs
)才是真实执行路径的关键依据。
常见错误现象:明明写了
,却在
子句里对右表字段加了非空条件(如
),结果变成隐式内连接——因为过滤发生在连接后,NULL 行被直接剔除。
驱动表应尽量小,或有高选择性索引,减少循环次数
被驱动表必须在关联字段上有索引,否则触发全表扫描(
)
用
可强制按书写顺序执行,但需确认统计信息准确,否则反而更慢
ON 条件和 WHERE 条件在外连接中的作用差异
是连接阶段的“匹配规则”,决定哪些行能组成临时结果;
是连接完成后的“最终过滤”,会筛掉已生成的整行(包括左表数据)。对左外连接来说,
中右表条件不满足时,右表字段补 NULL,左表行仍保留;而
中对右表字段的限制会让这些 NULL 行消失。
示例:
—— 这里
在
中,不影响
行输出;若挪到
,则所有无匹配活跃用户或用户为非活跃的订单都会被过滤掉。
支持对任意表字段做条件,包括被驱动表的非索引字段(但性能差)
中对被驱动表字段的等值条件,可能让优化器放弃外连接语义,转为内连接执行
多表外连接链(如 A LEFT JOIN B LEFT JOIN C)中,
条件只作用于相邻两表,不能跨跳
NULL 值填充发生在哪一步,能否被索引利用
NULL 填充是外连接算法的最后一步:先完成驱动表与被驱动表的匹配(类似内连接逻辑),再将未匹配的驱动表行单独拎出,对其被驱动表字段补 NULL。这个过程发生在存储引擎返回数据之后、Server 层组装结果之前,因此无法被任何索引加速——索引只参与匹配阶段的查找,不参与补 NULL。
php版微信js-sdk支付接口类
php版微信js-sdk支付接口类
下载
这意味着:即使被驱动表完全没数据,只要驱动表有 100 万行,外连接结果也至少有 100 万行,且每行都要构造一次 NULL 值。如果后续还有
或
,代价会进一步放大。
补 NULL 不触发磁盘 I/O,但增加 CPU 和内存开销(尤其大字段如
)
联合索引无法覆盖 “驱动表有值 + 被驱动表无匹配” 这一场景,所以别指望靠索引避免 NULL 行
用
查看
字段,若被驱动表的
接近 0,说明大量行未匹配,补 NULL 开销已成瓶颈
MySQL 8.0+ 的哈希外连接是否真能提速
MySQL 8.0.18 引入了哈希外连接(Hash Outer Join),但仅适用于被驱动表走全表扫描(
或
)且无合适索引的场景。它把驱动表建哈希表,再流式扫描被驱动表做探测。相比嵌套循环,它减少了随机 I/O,但内存占用更高,且不支持被驱动表使用索引查找(即不兼容
/
)。
是否启用由优化器自动判断,可通过
手动打开,但需注意:若驱动表太大导致哈希表溢出磁盘,性能反而暴跌;且哈希连接不支持
语法,只认
显式条件。
查看
输出中
是否含
哈希外连接无法下推
条件到被驱动表扫描前,所以过滤越晚,扫描越多
小数据量(
外连接的真实开销往往不在连接本身,而在补 NULL 后的数据流转——比如后续排序、聚合、网络传输,这些环节对 NULL 的处理成本常被低估。
JOINLEFT JOINRIGHT JOINoptimizer_switch='join_cache_level=2'EXPLAINtabletyperefALLLEFT JOINWHEREWHERE t2.status = 'active'type: ALLSTRAIGHT_JOINONWHEREONWHERESELECT * FROM orders o LEFT JOIN users u ON o.user_id = u.id AND u.status = 'active'u.status = 'active'ONordersWHERE u.status = 'active'ONWHEREONGROUP BYORDER BYTEXTEXPLAIN FORMAT=JSONfilteredfilteredtype: ALLtype: indexrefeq_refSET optimizer_switch='hash_join=on'USINGONEXPLAINExtraUsing where; Using join buffer (hash join)WHERE