oracle之index

   索引与表一样,也属于段(segment)的一种。里面存放了用户的数据,跟表一样需要占用磁盘空间。索引是一种允许直接访问数据表中某一数据行的树型结构,为了提高查询效率而引入,是一个独立于表的对象,可以存放在与表不同的表空间中。索引记录中存有索引关键字和指向表中数据的指针(地址)。对索引进行的I/O操作比对表进行操作要少很多。索引一旦被建立就将被Oracle系统自动维护,查询语句中不用指定使用哪个索引.

   从物理上说,索引通常可以分为:分区和非分区索引、常规B树索引、位图(bitmap)索引、翻转(reverse)索引等。其中,B树索引属于最常见的索引。

一、 ROWID的概念

存储了row在数据文件中的具体位置:64位 编码的数据,A-Z, a-z, 0-9, +, 和 /,

row在数据块中的存储方式

SELECT ROWID, last_name FROM hr.employees WHERE department_id = 20;

比 如:OOOOOOFFFBBBBBBRRR

OOOOOO:data object number, 对应dba_objects.data_object_id

FFF:file#, 对应v$datafile.file#

BBBBBB:block#

RRR:row#

Dbms_rowid包

SELECT dbms_rowid.rowid_block_number('AAAGFqAABAAAIWEAAA') from dual;

具 体到特定的物理文件

二、 索引的概念

1、 类似书的目录结构

2、 Oracle 的“索引”对象,与表关联的可选对象,提高SQL查询语句的速度

3、 索引直接指向包含所查询值的行的位置,减少磁盘I/O

4、 与所索引的表是相互独立的物理结构

5、 Oracle 自动使用并维护索引,插入、删除、更新表后,自动更新索引

6、 语法:CREATE INDEX index ON table (column[, column]...);

7、 B-tree结构(非bitmap):

[一]了解索引的工作原理:

表:emp

目标:查询Frank的工资salary

建立索 引:create index emp_name_idx on emp(name);

 索引的三大特性

1、索引的高度较低

sqlplus ljb/ljb
drop table t1 purge;
drop table t2 purge;
drop table t3 purge;
drop table t4 purge;
drop table t5 purge;
drop table t6 purge;
drop table t7 purge;


create table t1 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1;
create table t2 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=10;
create table t3 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=100;
create table t4 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1000;
create table t5 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=10000;
create table t6 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=100000;
create table t7 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1000000;


create index idx_id_t1 on t1(id);
create index idx_id_t2 on t2(id);
create index idx_id_t3 on t3(id);
create index idx_id_t4 on t4(id);
create index idx_id_t5 on t5(id);
create index idx_id_t6 on t6(id);
create index idx_id_t7 on t7(id);

set linesize 1000
set autotrace off
select index_name,
          blevel,
          leaf_blocks,
          num_rows,
          distinct_keys,
          clustering_factor
     from user_ind_statistics
    where table_name in( 'T1','T2','T3','T4','T5','T6','T7');


INDEX_NAME  BLEVEL LEAF_BLOCKS   NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------ ----------- ---------- ------------- -----------------
IDX_ID_T1        0           1          1             1                 1
IDX_ID_T2        0           1         10            10                 2
IDX_ID_T3        0           1        100           100                15
IDX_ID_T4        1           3       1000          1000               143
IDX_ID_T5        1          21      10000         10000              1429
IDX_ID_T6        1         222     100000        100000             14286
IDX_ID_T7        2        2226    1000000       1000000            142858

sqlplus ljb/ljb
drop table t1 purge;
drop table t2 purge;
drop table t3 purge;
drop table t4 purge;
drop table t5 purge;
drop table t6 purge;
drop table t7 purge;


create table t1 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1;
create table t2 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=10;
create table t3 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=100;
create table t4 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1000;
create table t5 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=10000;
create table t6 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=100000;
create table t7 as select rownum as id ,rownum+1 as id2,rpad('*',1000,'*') as contents from dual connect by level<=1000000;

create index idx_id_t1 on t1(id);
create index idx_id_t2 on t2(id);
create index idx_id_t3 on t3(id);
create index idx_id_t4 on t4(id);
create index idx_id_t5 on t5(id);
create index idx_id_t6 on t6(id);
create index idx_id_t7 on t7(id);

set linesize 1000
select index_name,
          blevel,
          leaf_blocks,
          num_rows,
          distinct_keys,
          clustering_factor
     from user_ind_statistics
    where table_name in( 'T1','T2','T3','T4','T5','T6','T7');
INDEX_NAME  BLEVEL LEAF_BLOCKS   NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
------------------ ----------- ---------- ------------- -----------------
IDX_ID_T1        0           1          1             1                 1
IDX_ID_T2        0           1         10            10                 2
IDX_ID_T3        0           1        100           100                15
IDX_ID_T4        1           3       1000          1000               143
IDX_ID_T5        1          21      10000         10000              1429
IDX_ID_T6        1         222     100000        100000             14286
IDX_ID_T7        2        2226    1000000       1000000            142858

    

set autotrace traceonly statistics
set linesize 1000
--以下注意观察逻辑读的次数,另外注意尽量每条语句执行2遍以上,观察第2遍的结果。

select * from t1 where id=1;  
统计信息
-----------------------------------------
          0  recursive calls
          0  db block gets
          2  consistent gets          
select /*+full(t1)*/ * from t1 where id=1; 
统计信息
-------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets 
          
          
select * from t2 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets           
select /*+full(t2)*/ * from t2 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          5  consistent gets
        
            
select * from t3 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
select /*+full(t3)*/ * from t3 where id=1; 
统计信息
----------------------------
          0  recursive calls
          0  db block gets
         19  consistent gets
            
select * from t4 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
select /*+full(t4)*/ * from t4 where id=1; 
统计信息
----------------------------
          0  recursive calls
          0  db block gets
        148  consistent gets
     
select * from t5 where id=1;
统计信息
------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
select /*+full(t5)*/ * from t5 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
       1435  consistent gets
       
                 
select * from t6 where id=1;  
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets  
select /*+full(t6)*/ * from t6 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
      14298  consistent gets
      
            
select * from t7 where id=1;  
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
          5  consistent gets  
select /*+full(t7)*/ * from t7 where id=1; 
统计信息
-----------------------------
          0  recursive calls
          0  db block gets
     142866  consistent gets


/*
规律:
  从t1到t7(表记录依次增大10倍,从1到1000000),索引读的逻辑读是     2,3,3,4,4,4,5
  从t1到t7(表记录依次增大10倍,从1到1000000)全表扫描的逻辑读是    3,5,19,148,1435,14298,142866  
*/ 

2、索引存储列值

--要领:只要索引能回答问题,索引就可以当成一个"瘦表",访问路径就会减少。另外切记不存储空值
drop table t purge;
create table t as select * from dba_objects;
update t set object_id=rownum;
commit;
create index idx1_object_id on t(object_id);
set autotrace on
select count(*) from t;
执行计划
-------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |   292   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |     1 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 69485 |   292   (1)| 00:00:04 |
-------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1048  consistent gets

--为啥用不到索引,因为索引不能存储空值,所以加上一个is not null,再试验看看            
select count(*) from t where object_id is not null;
执行计划
----------------------------------------------------------------------------------------
| Id  | Operation             | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                |     1 |    13 |    50   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |                |     1 |    13 |            |          |
|*  2 |   INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 69485 |   882K|    50   (2)| 00:00:01 |
----------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        170  consistent gets

--也可以不加is not null,直接把列的属性设置为not null,也成,继续试验如下:
alter table t modify OBJECT_ID not null;
select count(*) from t;
执行计划
--------------------------------------------------------------------------------
| Id  | Operation             | Name           | Rows  | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                |     1 |    49   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |                |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 69485 |    49   (0)| 00:00:01 |
--------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        170  consistent gets
          0  physical reads
          0  redo size
        425  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


--如果是主键就无需定义列是否允许为空了。
drop table t purge;
create table t as select * from dba_objects;
update t set object_id=rownum;
alter table t add constraint pk1_object_id primary key (OBJECT_ID);
set autotrace on
select count(*) from t;

执行计划
-------------------------------------------------------------------------------
| Id  | Operation             | Name          | Rows  | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |               |     1 |    46   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |               |     1 |            |          |
|   2 |   INDEX FAST FULL SCAN| PK1_OBJECT_ID | 69485 |    46   (0)| 00:00:01 |
-------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        160  consistent gets

SUM/AVG的优化
drop table t purge;
create table t as select * from dba_objects;
create index idx1_object_id on t(object_id);
set autotrace on
set linesize 1000
set timing on 

select sum(object_id) from t; 
执行计划
----------------------------------------------------------------------------------------
| Id  | Operation             | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                |     1 |    13 |    49   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |                |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 92407 |  1173K|    49   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        170  consistent gets
          0  physical reads
          0  redo size
        432  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
          
--比较一下假如不走索引的代价,体会一下这个索引的重要性
select /*+full(t)*/ sum(object_id) from t;  
SUM(OBJECT_ID)
--------------
  2732093100         
执行计划
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    13 |   292   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 92407 |  1173K|   292   (1)| 00:00:04 |
---------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1047  consistent gets
          0  physical reads
          0  redo size
        432  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed     
          
--起来类似的比如AVG,和SUM是一样的,如下:
select avg(object_id) from t; 
AVG(OBJECT_ID)
--------------
  37365.5338
执行计划
----------------------------------------------------------------------------------------
| Id  | Operation             | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                |     1 |    13 |    49   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |                |     1 |    13 |            |          |
|   2 |   INDEX FAST FULL SCAN| IDX1_OBJECT_ID | 92407 |  1173K|    49   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        170  consistent gets
          0  physical reads
          0  redo size
        448  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--不知大家注意到没,这里的试验已经告诉我们了,OBJECT_ID列是否为空,也不影响SUM/AVG等聚合的结果。

3、索引本身有序

--colocated表根据x列有一定的物理顺序
 
drop table colocated purge;
create table colocated ( x int, y varchar2(80) );
begin
    for i in 1 .. 100000
    loop
        insert into colocated(x,y)
        values (i, rpad(dbms_random.random,75,'*') );
    end loop;
end;
/

alter table colocated
add constraint colocated_pk
primary key(x);
begin
dbms_stats.gather_table_stats( user, 'COLOCATED', cascade=>true );
end;
/

--disorganized 表数据根据x列完全无序
drop table disorganized purge;
create table disorganized
as
select x,y
  from colocated
 order by y;
alter table disorganized
add constraint disorganized_pk
primary key (x);
begin
dbms_stats.gather_table_stats( user, 'DISORGANIZED', cascade=>true );
end;
/

set autotrace off
alter session set statistics_level=all;
set linesize 1000


---两者性能差异显著
select /*+ index( colocated colocated_pk ) */ * from colocated where x between 20000 and 40000;
SELECT * FROM table(dbms_xplan.display_cursor(NULL,NULL,'runstats_last'));

------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |              |      1 |        |  20001 |00:00:00.05 |    2900 |
|   1 |  TABLE ACCESS BY INDEX ROWID| COLOCATED    |      1 |  20002 |  20001 |00:00:00.05 |    2900 |
|*  2 |   INDEX RANGE SCAN          | COLOCATED_PK |      1 |  20002 |  20001 |00:00:00.03 |    1375 |
------------------------------------------------------------------------------------------------------

select /*+ index( disorganized disorganized_pk ) */* from disorganized  where x between 20000 and 40000;
SELECT * FROM table(dbms_xplan.display_cursor(NULL,NULL,'runstats_last'));

---------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name            | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                 |      1 |        |  20001 |00:00:00.09 |   21360 |
|   1 |  TABLE ACCESS BY INDEX ROWID| DISORGANIZED    |      1 |  20002 |  20001 |00:00:00.09 |   21360 |
|*  2 |   INDEX RANGE SCAN          | DISORGANIZED_PK |      1 |  20002 |  20001 |00:00:00.03 |    1375 |
---------------------------------------------------------------------------------------------------------


---看聚合因子,就明白真正的原因了。

select a.index_name,
       b.num_rows,
       b.blocks,
       a.clustering_factor
  from user_indexes a, user_tables b
where index_name in ('COLOCATED_PK', 'DISORGANIZED_PK' )
  and a.table_name = b.table_name;

INDEX_NAME                       NUM_ROWS     BLOCKS CLUSTERING_FACTOR
------------------------------ ---------- ---------- -----------------
COLOCATED_PK                       100000       1252              1190
DISORGANIZED_PK                    100000       1219             99899

--索引回表读(TABLE ACCESS BY INDEX ROWID)的例子
drop table t purge;
create table t as select * from dba_objects;
create index idx1_object_id on t(object_id);

--试验1
set autotrace traceonly
set linesize 1000
set timing on
select * from t where object_id<=5;

执行计划
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     4 |   828 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     4 |   828 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX1_OBJECT_ID |     4 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          5  consistent gets
          0  physical reads
          0  redo size
       1666  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed

--比较消除TABLE ACCESS BY INDEX ROWID回表后的性能,将select * from改为select object_id from 
set autotrace traceonly
set linesize 1000
set timing on

select object_id from t where object_id<=5;
执行计划
-----------------------------------------------------------------------------------
| Id  | Operation        | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                |     4 |    52 |     2   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| IDX1_OBJECT_ID |     4 |    52 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        478  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed

--试验2:通过构造联合索引,再观察一个消除TABLE ACCESS BY INDEX ROWID的例子
set autotrace traceonly
set linesize 1000
select object_id,object_name from t where object_id<=5;
执行计划
----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     4 |   316 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T              |     4 |   316 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX1_OBJECT_ID |     4 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          5  consistent gets
          0  physical reads
          0  redo size
        567  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed
          
--准备工作,对t表建联合索引
create index idx_un_objid_objname on t(object_id,object_name);
--该联合索引建完后,产生功效了!消除了TABLE ACCESS BY INDEX ROWID

select object_id,object_name from t where object_id<=5;
执行计划
-----------------------------------------------------------------------------------------
| Id  | Operation        | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                      |    12 |   948 |     2   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| IDX_UN_OBJID_OBJNAME |    12 |   948 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        567  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          4  rows processed

--:索引无法消除UNION 排序,一般来说在使用UNION时要确定必要性,在数据不会重复时只需UNION ALL即可。 

--DISTINCT测试前的准备
drop table t purge;
create table t as select * from dba_objects;
update t set object_id=rownum;
alter table T modify OBJECT_ID not null;
update t set object_id=2;
update t set object_id=3 where rownum<=25000;
commit;
/*
在oracle10g的R2环境之后,DISTINCT由于其 HASH UNIQUE的算法导致其不会产生排序,其调整的
ALTER SESSION SET "_gby_hash_aggregation_enabled" = FALSE
*/
set linesize 1000
set autotrace traceonly

select  distinct object_id from t ;
执行计划
-----------------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 88780 |  1127K|       |   717   (1)| 00:00:09 |
|   1 |  HASH UNIQUE       |      | 88780 |  1127K|  1752K|   717   (1)| 00:00:09 |
|   2 |   TABLE ACCESS FULL| T    | 88780 |  1127K|       |   292   (1)| 00:00:04 |
-----------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1047  consistent gets
          0  physical reads
          0  redo size
        462  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed


/*不过虽然没有排序,通过观察TempSpc可知distinct消耗PGA内存进行HASH UNIQUE运算,
接下来看看建了索引后的情况,TempSpc关键字立即消失,COST也立即下降许多,具体如下*/

--为T表的object_id列建索引
create index idx_t_object_id on t(object_id);
set linesize 1000
set autotrace traceonly

select  /*+index(t)*/ distinct object_id from t ;
执行计划
--------------------------------------------------------------------------------------
| Id  | Operation          | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |                 | 88780 |  1127K|   582   (1)| 00:00:07 |
|   1 |  SORT UNIQUE NOSORT|                 | 88780 |  1127K|   582   (1)| 00:00:07 |
|   2 |   INDEX FULL SCAN  | IDX_T_OBJECT_ID | 88780 |  1127K|   158   (1)| 00:00:02 |
--------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        145  consistent gets
          0  physical reads
          0  redo size
        462  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

--索引与排序 
  
drop table t purge;
create table t as select * from dba_objects ;
set autotrace traceonly
--oracle还算智能,不会傻到这里都去排序,做了查询转换,忽略了这个排序
select count(*) from t order by object_id;

 ---以下语句说明排序
set autotrace traceonly
set linesize 1000
drop table t purge;
create table t as select * from dba_objects;

--以下语句没有索引又有order by ,必然产生排序
select * from t where object_id>2 order by object_id;
执行计划
-----------------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 92407 |    18M|       |  4454   (1)| 00:00:54 |
|   1 |  SORT ORDER BY     |      | 92407 |    18M|    21M|  4454   (1)| 00:00:54 |
|*  2 |   TABLE ACCESS FULL| T    | 92407 |    18M|       |   294   (2)| 00:00:04 |
-----------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1047  consistent gets
          0  physical reads
          0  redo size
    3513923  bytes sent via SQL*Net to client
      54029  bytes received via SQL*Net from client
       4876  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      73117  rows processed


---新增索引后,Oracle就有可能利用索引本身就有序的特点,利用索引来避免排序,如下:
create index idx_t_object_id on t(object_id);
set autotrace traceonly

select * from t where object_id>2 order by object_id;
执行计划
-----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                 | 92407 |    18M|  1302   (1)| 00:00:16 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T               | 92407 |    18M|  1302   (1)| 00:00:16 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_OBJECT_ID | 92407 |       |   177   (1)| 00:00:03 |
-----------------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      10952  consistent gets
          0  physical reads
          0  redo size
    8115221  bytes sent via SQL*Net to client
      54029  bytes received via SQL*Net from client
       4876  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      73117  rows processed

--如下情况Oracle肯定毫不犹豫的选择用索引,因为回表取消了 !      
select  object_id from t where object_id>2 order by object_id;
执行计划
------------------------------------------------------------------------------------
| Id  | Operation        | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                 | 92407 |  1173K|   177   (1)| 00:00:03 |
|*  1 |  INDEX RANGE SCAN| IDX_T_OBJECT_ID | 92407 |  1173K|   177   (1)| 00:00:03 |
------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       5027  consistent gets
          0  physical reads
          0  redo size
    1062289  bytes sent via SQL*Net to client
      54029  bytes received via SQL*Net from client
       4876  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      73117  rows processed
      
--另外,如果是如下语句,Oracle打死也不用索引了。  
select  object_id from t where object_id>2;  

--MAX/MIN 的索引优化
drop table t purge;
create table t as select * from dba_objects;
update t set object_id=rownum;
alter table t add constraint pk_object_id primary key (OBJECT_ID);
set autotrace on
set linesize 1000

select max(object_id) from t;
执行计划
-------------------------------------------------------------------------------------------
| Id  | Operation                  | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |              |     1 |    13 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |              |     1 |    13 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| PK_OBJECT_ID |     1 |    13 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        431  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
          
--最小值老师的试验就无需展现执行计划结果了,必然和最大值的执行计划一样!          
select min(object_id) from t;

--如果没用到索引的情况是如下,请看看执行计划有何不同,请看看代价和逻辑读的差异!
select /*+full(t)*/ max(object_id) from t;
执行计划
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    13 |   292   (1)| 00:00:04 |
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |
|   2 |   TABLE ACCESS FULL| T    | 92407 |  1173K|   292   (1)| 00:00:04 |
---------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1047  consistent gets
          0  physical reads
          0  redo size
        431  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


---另外,可以做如下试验观察在有索引的情况下,随这记录数增加,性能差异是否明显?
set autotrace off
drop table t_max purge;
create table t_max as select * from dba_objects;
insert into t_max select * from t_max;
insert into t_max select * from t_max;
insert into t_max select * from t_max;
insert into t_max select * from t_max;
insert into t_max select * from t_max;
select count(*) from t_max;
create index idx_t_max_obj on t_max(object_id);
set autotrace on 
select max(object_id) from t_max;

执行计划
--------------------------------------------------------------------------------------------
| Id  | Operation                  | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |               |     1 |    13 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |               |     1 |    13 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| IDX_T_MAX_OBJ |     1 |    13 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        431  bytes sent via SQL*Net to client
        415  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
          

/*
object_id如果允许为空,加个索引后,会走INDEX FULL SCAN (MIN/MAX)高效算法吗,
当然会了!取最大最小还怕啥空值?
*/ 
drop table t purge;
create table t as select * from dba_objects ;
create index idx_object_id on t(object_id);
set autotrace on
set linesize 1000
select max(object_id) from t;       

组合索引经要素!

/* 1.适用在单独查询返回记录很多,组合查询后忽然返回记录很少的情况:

   比如where 学历=硕士以上 返回不少的记录
   比如where 职业=收银员 同样返回不少的记录
   于是无论哪个条件查询做索引,都不合适。
   可是,如果学历为硕士以上,同时职业又是收银员的,返回的就少之又少了。
   于是联合索引就可以这么开始建了。
*/   
   

/* 2.组合查询的组合顺序,要考虑单独的前缀查询情况(否则单独前缀查询的索引不能生效或者只能用到跳跃索引)
   比如你在建id,object_type的联合索引时,要看考虑是单独where id=xxx查询的多,还是单独where object_type查询的多。
   这里细节就暂时略去了,在案例的部分中还有描述
*/

--3.仅等值无范围查询时,组合索引顺序不影响性能(比如where col1=xxx and col2=xxx,无论COL1+COL2组合还是COL2+COL1组合)

drop table t purge;
create table t as select * from dba_objects;
insert into t select * from t;
insert into t select * from t;
insert into t select * from t;
update t set object_id=rownum ;
commit;
create index idx_id_type on t(object_id,object_type);
create index idx_type_id on t(object_type,object_id);
set autotrace off
alter session set statistics_level=all ;
set linesize 366

select /*+index(t,idx_id_type)*/ * from  t  where object_id=20  and object_type='TABLE';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |      1 |        |      1 |00:00:00.01 |       5 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T           |      1 |     57 |      1 |00:00:00.01 |       5 |
|*  2 |   INDEX RANGE SCAN          | IDX_ID_TYPE |      1 |      9 |      1 |00:00:00.01 |       4 |
-----------------------------------------------------------------------------------------------------

select /*+index(t,idx_type_id)*/ * from  t  where object_id=20  and object_type='TABLE';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
Plan hash value: 3420768628

-----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |      1 |        |      1 |00:00:00.01 |       5 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T           |      1 |     57 |      1 |00:00:00.01 |       5 |
|*  2 |   INDEX RANGE SCAN          | IDX_TYPE_ID |      1 |      9 |      1 |00:00:00.01 |       4 |
-----------------------------------------------------------------------------------------------------


--4.组合索引最佳顺序一般是将列等值查询的列置前。(测试组合索引在条件是不等的情况下的情况,条件经常是不等的,要放在后面,让等值的在前面)

select /*+index(t,idx_id_type)*/ *  from   t where object_id>=20 and object_id<2000 and object_type='TABLE';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
-----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |      1 |        |    469 |00:00:00.01 |      86 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T           |      1 |     14 |    469 |00:00:00.01 |      86 |
|*  2 |   INDEX RANGE SCAN          | IDX_ID_TYPE |      1 |      1 |    469 |00:00:00.01 |      40 |
-----------------------------------------------------------------------------------------------------


select /*+index(t,idx_type_id)*/ *  from  t  where object_id>=20 and object_id<2000   and object_type='TABLE';
-----------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |             |      1 |        |    469 |00:00:00.01 |      81 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T           |      1 |    469 |    469 |00:00:00.01 |      81 |
|*  2 |   INDEX RANGE SCAN          | IDX_TYPE_ID |      1 |    469 |    469 |00:00:00.01 |      35 |
-----------------------------------------------------------------------------------------------------


--5.注意组合索引与组合条件中关于IN 的优化(将会在案例部分描述,展现结果就不在这里贴出了)

--案例1

UPDATE t SET OBJECT_ID=20 WHERE ROWNUM<=26000;
UPDATE t SET OBJECT_ID=21 WHERE OBJECT_ID<>20;
COMMIT;
set linesize 1000
set pagesize 1
alter session set statistics_level=all ;
select  /*+index(t,idx1_object_id)*/ * from t  where object_TYPE='TABLE'  AND OBJECT_ID >= 20 AND OBJECT_ID<= 21;


--6.案例2
--依然是关于IN的优化 (col1,col2,col3的索引情况,如果没有为COL2赋予查询条件时,COL3只能起到检验作用)

drop table t purge;
create table t as select * from dba_objects;
UPDATE t SET OBJECT_ID=20 WHERE ROWNUM<=26000;
UPDATE t SET OBJECT_ID=21 WHERE OBJECT_ID<>20;
Update t set object_id=22 where rownum<=10000;
COMMIT;

create index idx_union on t(object_type,object_id,owner);
set autotrace off
alter session set statistics_level=all ;
set linesize 1000
select * from t where object_type='VIEW' and OWNER='LJB';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
--这里就略去了展现结果,在案例中有描述。

select /*+INDEX(T,idx_union)*/ * from t T where object_type='VIEW' and OBJECT_ID IN (20,21,22) AND OWNER='LJB';
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));


                                                
                                               

猜你喜欢

转载自blog.csdn.net/weixin_41492331/article/details/86649733
今日推荐