驱动表由CBO根据统计信息、索引和WHERE条件过滤后的实际行数自动推导,真正起作用的是三要素:过滤后行数、驱动表是否走索引扫描、被驱动表连接字段是否有高效索引;LEADING比ORDERED更灵活,但必须配合有效过滤和准确统计,否则无效。

Oracle 19c里驱动表不是“选出来”的,而是由CBO根据统计信息、索引和WHERE条件自动推导出来的——你真正能干预的,是给它足够可靠的输入,再用hint引导它做出你要的决策。
驱动表实际由哪些因素决定
优化器不会看你的表书写顺序,它只算成本:outer_table_scan_cost + (outer_table_rows × inner_table_access_cost)。真正起作用的是三样东西:
- WHERE条件过滤后的行数(
Actual Rows比Rows更可信) - 驱动表能否走索引扫描(全表扫描
TABLE ACCESS FULL基本等于放弃) - 被驱动表连接字段是否有高效索引(
ref或eq_ref才够快)
比如写SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.status = 'vip',只要customers.status有索引且统计准确,CBO大概率选customers当驱动表——哪怕它在FROM里排第二。
/*+ LEADING() */比/*+ ORDERED */更实用
/*+ ORDERED */强制按FROM顺序左深嵌套,但只对NESTED LOOPS生效,且要求左侧表必须能被WHERE大幅裁剪;而/*+ LEADING(customers) */更灵活:它指定连接树根节点,后续JOIN仍可由优化器选HASH JOIN或重排顺序。
- 必须搭配有效过滤:比如
WHERE customers.status = 'vip',否则LEADING只是摆设 - 不能跨schema乱指:
/*+ LEADING(hr.employees) */在非hr用户下会失效 - 多表时写全:
/*+ LEADING(a b c) */表示a→b→c的连接起点,不是只定a
执行计划里看到LEADING没生效?先查DBMS_STATS.GATHER_TABLE_STATS是否跑过,再确认customers.status字段有没有索引——hint不会创造索引,也不会修复过期统计。
LEFT JOIN里驱动表根本没法换
语法上LEFT JOIN的左表就是驱动表,优化器不能调换顺序。写big_table LEFT JOIN small_table ON ...,哪怕small_table只有10行,Oracle也得全扫big_table。
- 现象:
EXPLAIN里左表type=ALL,右表type=ref但查询依然慢 - 解法一:业务允许就改
INNER JOIN,让优化器自由选驱动表 - 解法二:提前用子查询过滤左表,比如
FROM (SELECT * FROM big_table WHERE flag = 1) b LEFT JOIN small_table s ON ... - 注意:
ON里的条件不减少驱动表扫描量,只有WHERE或子查询才能真正缩小驱动集
执行计划里怎么验证驱动表选对了
别只看PLAN_TABLE的缩进,重点盯三列:
-
OPERATION列最顶层(缩进最少)的TABLE ACCESS操作,通常是驱动表入口 -
ROWS和Actual Rows是否接近——如果Actual Rows比ROWS大10倍,说明统计严重不准 -
ACCESS PREDICATES里有没有customer_id = :1这类等值索引访问,而不是FILTER PREDICATES里做全表筛
最常被忽略的是:驱动表本身没走索引,却指望被驱动表靠索引救场——这种情况下加再多/*+ LEADING() */也没用,得先建索引或更新统计。



















