为什么不走索引 小实验

http://zotao.iteye.com/blog/810157


两种连接方式,有驱动表的概念(其实应该说是驱动行源更为准确)

-- hash join

-- nested-loop join

 

对于Nested-loop join

适合于小表(几千条,几万条记录)与大表做联接在联接列上有索引。分内表和外表(驱动表),靠近from子句的是内表。

从效率上讲,小表应该作外表,大表应该作内表,即大表查询时走索引。

 

COST= Access cost of A(驱动表) + (access cost of B * number of rows from A)

 

成本计算方法:

设小表100行,大表100000行。

 

两表均有索引:

如果小表在内,大表在外(驱动表)的话,则扫描次数为:

  100000+100000*2 (其中2表示IO次数,一次索引,一次数据)

如果大表在内,小表在外(驱动表)的话,则扫描次数为:

  100+100*2.

 

两表均无索引:

如果小表在内,大表在外的话,则扫描次数为:

  100000+100*100000

如果大表在内,小表在外的话,则扫描次数为:

  100+100000*100

 

注意:如果一个表有索引,一个表没有索引,ORACLE会将没有索引的表作驱动表。如果两个表都有索引

,则外表作驱动表。如果两个都没索引的话,则也是外表作驱动表。

 

基本的执行计划如下所示:

  NESTED LOOPS

           TABLE ACCESS (BY ROWID)  OF  our_outer_table

                   INDEX (..SCAN) OF outer_table_index(….)

           TABLE ACCESS (BY ROWID)  OF  our_inner_table

             INDEX (..SCAN) OF inner_table_index(….)

 

从效率上讲

from B,        --内表,大表

     A                --外表,小表,驱动表

COST = Access cost of A + (access cost of B * number of rows from A)     

 

---------------------------实验(条件要求,小表很小,大表很大,效果更具有可见性)、结论(仅代表个人意见,有不同意见的可以和我讨论)

大表tt1:

CREATE table tt1

as select * from dba_objects;

小表tt2:

CREATE table tt2

as select * from user_objects;

分析表:

select num_rows,blocks,table_name

from user_tables

where table_name in('TT1','TT2');

 

 

1、两表都无索引,大表在内,小表在外

select tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id;  

 

2、两表都无索引,小表在内,大表在外

select tt1.object_name,tt2.object_name

from tt2,

     tt1

where tt1.object_id = tt2.object_id;  

 

3、两表无索引,使用hint /*+use_nl(tt2)*/

select /*+use_nl(tt2)*/tt1.object_name,tt2.object_name

from tt2,

     tt1

where tt1.object_id = tt2.object_id; 

 

执行计划:

---------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      |    84 |  4032 |   162   (2)| 00:00:02 |

|*  1 |  HASH JOIN         |      |    84 |  4032 |   162   (2)| 00:00:02 |

|   2 |   TABLE ACCESS FULL| TT2  |    86 |  1548 |     3   (0)| 00:00:01 |

|   3 |   TABLE ACCESS FULL| TT1  | 49863 |  1460K|   158   (2)| 00:00:02 |

---------------------------------------------------------------------------

 

结论:

        在没有索引的情况下:

        -- hash join的cost更小,优化器自动选择hash join

        -- 优化器自动选择小表做驱动表

        -- hints也不能改变连接方式

 

 

create index tt1_ind on tt1(object_id);

4、大表有索引,小表无索引,大表在内,小表在外

select tt1.object_name,tt2.object_name

from tt2,

     tt1

where tt1.object_id = tt2.object_id; 

5、大表有索引,小表无索引,小表在内,大表在外

select tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id; 

 

---------------------------------------------------------------------------------------

| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |         |   673 | 32304 |   950   (1)| 00:00:12 |

|   1 |  TABLE ACCESS BY INDEX ROWID| TT1     |     8 |   240 |    11   (0)| 00:00:01 |

|   2 |   NESTED LOOPS              |         |   673 | 32304 |   950   (1)| 00:00:12 |

|   3 |    TABLE ACCESS FULL        | TT2     |    86 |  1548 |     3   (0)| 00:00:01 |

|*  4 |    INDEX RANGE SCAN         | TT1_IND |     8 |       |     2   (0)| 00:00:01 |

---------------------------------------------------------------------------------------

 

结论:

        大表有索引:

        -- 在大表够大的情况下,优化器采用了NL的连接方式

        -- 驱动表的选择与内外没有关系,自动选择小的行源做为驱动表

 

drop index tt1_ind

create index tt2_ind on tt2(object_id);

6、小表有索引,大表无索引,小表在内,大表在外

select tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id; 

 

7、小表有索引,大表无索引,小表在内,大表在外,加hints

select /*+use_nl(tt1)*/*tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id; 

---------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      |   673 | 32304 |  1230   (2)| 00:00:15 |

|*  1 |  HASH JOIN         |      |   673 | 32304 |  1230   (2)| 00:00:15 |

|   2 |   TABLE ACCESS FULL| TT2  |    86 |  1548 |     3   (0)| 00:00:01 |

|   3 |   TABLE ACCESS FULL| TT1  |   398K|    11M|  1220   (2)| 00:00:15 |

---------------------------------------------------------------------------

结论:

        小表有索引:

        -- 始终不能用大表做驱动表

 

create index tt1_ind on tt1(object_id);

8、大表小表都有索引

select tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id; 

 

---------------------------------------------------------------------------------------

| Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |         |   673 | 32304 |   950   (1)| 00:00:12 |

|   1 |  TABLE ACCESS BY INDEX ROWID| TT1     |     8 |   240 |    11   (0)| 00:00:01 |

|   2 |   NESTED LOOPS              |         |   673 | 32304 |   950   (1)| 00:00:12 |

|   3 |    TABLE ACCESS FULL        | TT2     |    86 |  1548 |     3   (0)| 00:00:01 |

|*  4 |    INDEX RANGE SCAN         | TT1_IND |     8 |       |     2   (0)| 00:00:01 |

---------------------------------------------------------------------------------------        

结论:

        小表、大表都有索引:

        -- 符合原则,小且做驱动,大表走索引

 

9、两个表一样大,且都有索引

select tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id;          

-----------------------------------------------------------------------------------

| Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

-----------------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |      | 47358 |  7307K|       |   816   (1)| 00:00:10 |

|*  1 |  HASH JOIN         |      | 47358 |  7307K|  4216K|   816   (1)| 00:00:10 |

|   2 |   TABLE ACCESS FULL| TT2  | 47358 |  3653K|       |   158   (2)| 00:00:02 |

|   3 |   TABLE ACCESS FULL| TT1  | 52860 |  4078K|       |   158   (2)| 00:00:02 |

-----------------------------------------------------------------------------------

select /*+index(tt2 tt2_ind)*/tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id;           

------------------------------------------------------------------------------------------------

| Id  | Operation                    | Name    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |         | 47358 |  7307K|       |  1898   (1)| 00:00:23 |

|*  1 |  HASH JOIN                   |         | 47358 |  7307K|  4216K|  1898   (1)| 00:00:23 |

|   2 |   TABLE ACCESS BY INDEX ROWID| TT2     | 47358 |  3653K|       |  1240   (1)| 00:00:15 |

|   3 |    INDEX FULL SCAN           | TT2_IND | 47358 |       |       |   124   (2)| 00:00:02 |

|   4 |   TABLE ACCESS FULL          | TT1     | 52860 |  4078K|       |   158   (2)| 00:00:02 |

------------------------------------------------------------------------------------------------

 

结论:

        --两个表一样大,无论用哪个来做驱动表,cost都会很高。

        --优化器自动选择hash join

        --如果要走索引,显然会让cost值增大(因为每一条记录,都要先用索引获得rowid,再回表。)

 

************************************************************************************************************************

 

set linesize 200 pagesize 1000

explain plan for

select /*+index(tt2 tt2_ind)*/tt1.object_name,tt2.object_name

from tt1,

     tt2

where tt1.object_id = tt2.object_id;    

select * from table(dbms_xplan.display());


猜你喜欢

转载自blog.csdn.net/lppl010_/article/details/80261794