Oracle 驱动表
Oracle驱动表(driving table/outer table)也叫做外部表,也叫外层表,是在多表关联查询中首先遍历的表,驱动表的每一行都要到另一个表中寻找相应的记录,然后计算返回最终数据。
驱动表的概念只在nested loops和hash join时存在。
原则:
1. 2. 3. 4.
驱动表一般是小表,但不绝对,看下边
驱动表一般是通过where条件筛选后剩余行数较少的表。 如果表的一条记录很长,占用几个数据块也适合做驱动表
CBO和RBO中,对于驱动表的选择是不同的,CBO中通过对统计信息的参考进行计算来选择驱动表,而RBO中按照既定原则选择驱动表。
5. RBO中,from后边最右边的表为驱动表(from后边表从右向左遍历,where条件从下
向上遍历)
6. 涉及驱动表的查询,连接条件的索引很重要,驱动表连接字段可以没有索引,但是被驱
动表需要被扫描驱动表经过筛选后剩余条数的遍数,所以被驱动表的连接字段上有一条索引是非常重要的。
分析:
假设a表10行记录,b表1000行记录,两个表都有id列,查询时使用id列进行关联 Select * from a,b where a.id=b.id and a.id=100;
A表作为驱动表比较合适,假设a.id=100只有1行,即使全表扫描a表也就几个块,假设a表占用10个块。
B表的id假如非唯一,如果b表的id列有索引,b表占用100个块,每个块10行记录,id列索引占用10个块,并且id为100有2条记录,在两个块中 那么这条语句的成本(以块计算,下同):
A表(10个块)*b表索引(10个块)+b表id为100的2个块=102个块 如果b表没有索引,成本为:
A表(10个块)*b表(100个块)=1000个块
如果a,b表都没有索引,可以看出不管哪个表作为驱动表,语句的执行成本都是一样的。 如果a,b表id列都有索引,a表id列索引占2个块,成本为:
A表id列索引(2个块)*b表id列索引(10个块)+b表id为100的2个块=22个块
如果B表的记录很长,可以作为驱动表的情况比较复杂,大家可以自己想象适合的场景。
可以看出,在连接中,如果连接列有索引是多么的重要。
实验支撑
SQL> create table a(id,name) as select object_id,object_name from all_objects where rownum< 200;
Table created.
SQL>
SQL> create table b as select * from all_objects ;
Table created.
SQL> select count(*) from a;
COUNT(*) ---------- 199
SQL> select count(*) from b SQL>
COUNT(*) ---------- 89083
SQL>
SQL> exec dbms_stats.gather_table_stats('TEST','A');
PL/SQL procedure successfully completed.
SQL>
SQL> exec dbms_stats.gather_table_stats('TEST','B');
PL/SQL procedure successfully completed.
两个表都没有索引
Select count(*) from a,b where a.id=b.object_id And a.id=53
执行计划:(B表驱动)
SQL> Select count(*) from a,b where a.id=b.object_id 2 And a.id=53 3 /
COUNT(*) ---------- 1
Execution Plan
---------------------------------------------------------- Plan hash value: 319234518
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 420 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |* 2 | HASH JOIN | | 1 | 9 | 420 (1)| 00:00:01 | |* 3 | TABLE ACCESS FULL| B | 1 | 5 | 417 (1)| 00:00:01 | |* 4 | TABLE ACCESS FULL| A | 1 | 4 | 3 (0)| 00:00:01 | ----------------------------------------------------------------------------
Predicate Information (identified by operation id): ---------------------------------------------------
2 - access(\ 3 - filter(\ 4 - filter(\
Statistics
---------------------------------------------------------- 1 recursive calls 0 db block gets
1506 consistent gets 0 physical reads 0 redo size
542 bytes sent via SQL*Net to client
543 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client
| 0 sorts (memory) 0 sorts (disk) 1 rows processed
SQL>
A表作为驱动表
SQL> Select /*+ ordered use_nl(a) */count(*) from a,b where a.id=b.object_id 2 And a.id=53;
COUNT(*) ---------- 1
1 row selected.
Execution Plan
---------------------------------------------------------- Plan hash value: 1397777030
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 9 | 420 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 9 | | |* 2 | HASH JOIN | | 1 | 9 | 420 (1)| 00:00:01 | |* 3 | TABLE ACCESS FULL| A | 1 | 4 | 3 (0)| 00:00:01 | |* 4 | TABLE ACCESS FULL| B | 1 | 5 | 417 (1)| 00:00:01 | ----------------------------------------------------------------------------
Predicate Information (identified by operation id): ---------------------------------------------------
2 - access(\ 3 - filter(\
4 - filter(\
Statistics
---------------------------------------------------------- 1 recursive calls 0 db block gets
| 1506 consistent gets 0 physical reads 0 redo size
542 bytes sent via SQL*Net to client
543 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
SQL>
发现上面两个语句的代价是一样的
/*+ Ordered use_nl(table_name) */ --使用hint强制表作为驱动表,只使用/*+ use_nl(table1,table2)是无法强制驱动表顺序的,另外,这里使用的use_nl,但是走的是hash join,说明在没有索引的情况下,oracle优化器更倾向hash join,因为nested loop并不一定会提前返回数据。
执行计划下,hash loop下第一个表为驱动表。
表B object_id列有索引的情况
SQL> create index id_b_object_id on b(object_id);
Index created.
SQL> exec dbms_stats.gather_table_stats(ownname => 'TEST',TABNAME => 'B',CASCADE => TRUE);
PL/SQL procedure successfully completed.
SQL>
执行计划:
SQL> Select count(*) from a,b where a.id=b.object_id 2 And a.id=53;
COUNT(*) ---------- 1