在 SQL 中使用
LEFT JOIN
时,左表(左侧的表)的选择不仅影响结果集的完整性,还直接决定了查询性能。核心原则是:在满足业务逻辑的前提下,优先让小表作为左表,大表作为右表。但需注意,这一结论的前提是 “业务逻辑允许”—— 因为
LEFT JOIN
的核心是 “保留左表的所有记录,右表匹配不到则补 NULL”,左表的选择首先必须符合业务对结果集的要求(即需要完整保留哪些数据)。
LEFT JOIN
的执行效率与左表(驱动表)的大小、右表(被驱动表)的索引 / 大小密切相关,具体原因如下:
LEFT JOIN
的执行逻辑是:先遍历左表的所有行,再逐行到右表中匹配符合条件的记录。因此,左表的行数直接决定了 “需要执行多少次匹配操作”。
- 若左表是小表(如 10 万行),则只需执行 10 万次匹配;
- 若左表是大表(如 1 亿行),则需执行 1 亿次匹配,即使每次匹配效率很高,总耗时也会显著增加。
示例:假设左表 A
有 100 万行,右表 B
有 1 亿行,LEFT JOIN A ON B
需执行 100 万次匹配;若反过来 LEFT JOIN B ON A
,则需执行 1 亿次匹配,后者的基础匹配次数是前者的 100 倍,性能差距悬殊。
右表的匹配效率取决于是否有合适的索引。大表通常会为连接字段(ON
后的条件字段)建立索引(否则全表扫描成本极高),而小表即使没有索引,全表扫描的成本也很低(因为数据量小)。
- 若右表是大表且有索引:每次匹配可通过索引快速定位(如 B+ 树索引,单次查询时间复杂度 O (logN)),即使左表是小表,整体效率也很高;
- 若右表是小表:即使没有索引,全表扫描(O (N))的成本也可接受(比如小表只有 1 万行,全表扫描 1 万次对数据库压力很小)。
反例:若左表是大表,右表是小表且无索引,此时左表的每一行都需要对右表做全表扫描,总耗时 = 大表行数 × 小表行数,性能会急剧下降(例如 1 亿 × 1 万 = 10 万亿次操作,完全不可接受)。
LEFT JOIN
的结果集行数 至少等于左表行数(右表匹配不到时补 NULL)。
- 若左表是小表:结果集行数较少(如 10 万行),内存 / 磁盘存储压力小,后续处理(如排序、聚合)效率高;
- 若左表是大表:结果集行数至少是大表行数(如 1 亿行),可能导致内存溢出(OOM)或磁盘临时文件暴涨,拖慢整个查询。
LEFT JOIN
的左表选择不能仅看性能,必须优先满足业务需求。例如:
- 需查询 “所有用户的订单记录(包括没有订单的用户)”,此时
用户表
必须作为左表(即使 用户表
是大表,订单表
是小表);
- 需查询 “所有商品的销售数据(包括未销售的商品)”,
商品表
必须作为左表(无论大小)。
这种情况下,只能接受大表作为左表,但可通过以下方式优化性能:
- 给右表的连接字段建立索引(如
订单表.user_id
),减少匹配耗时;
- 对左表进行过滤(如
WHERE 左表.日期 > '2023-01-01'
),减少左表的实际行数;
- 避免在
LEFT JOIN
后使用 SELECT *
,只查询必要字段,减少数据传输和存储成本。
部分数据库(如 PostgreSQL、Oracle)的优化器会对 JOIN
进行 “重排序”,但 LEFT JOIN
因语义限制(必须保留左表所有行),优化器无法交换左右表顺序。例如:
A LEFT JOIN B
中,优化器必须以 A
为驱动表,无法自动将 B
作为驱动表(即使 B
更小)。
而 MySQL 的优化器对 LEFT JOIN
的重排序限制更严格,几乎完全遵循用户指定的顺序。因此,用户必须主动选择更优的左表,不能依赖优化器自动调整。
- 业务优先:左表必须是 “需要完整保留所有记录” 的表(如 “用户表”“商品表”),这是
LEFT JOIN
的语义决定的;
- 性能优化:在满足业务的前提下,优先选择小表作为左表,大表作为右表,理由是:
- 减少驱动表的行数,降低匹配次数;
- 大表作为右表时,更易通过索引优化匹配效率;
- 控制结果集大小,减少资源消耗。
一句话结论:先看业务需要保留哪个表的全部数据,再在这个前提下,选小的那个表做左表。