先通过一个实验探讨一下索引的问题

创建测试环境

create table test01(c1 number,c2 number);

declare i number:=1;
begin
  while i<=100000 loop
    insert into test01(c1,c2) select i,dbms_random.value(1,100000) from dual;
    if mod(i,1000)=0 then 
      commit;
    end if;
    i:=i+1;
  end loop;
end;

create index idx_test01_c1 on test01(c1);

 

select * from test01 where c1 between 10000 and 20000;

执行计划:

sqlserver建立联合唯一索引 sql语句建立唯一索引_SQL

可以看出此时走的是索引(INDEX RANGE SCAN),增加数据量再查:

select * from test01 where c1 between 10000 and 30000;

执行计划:

sqlserver建立联合唯一索引 sql语句建立唯一索引_执行计划_02

还是走的索引,继续增加:

select * from test01 where c1 between 10000 and 40000;

执行计划:

sqlserver建立联合唯一索引 sql语句建立唯一索引_sed_03

此时可以看出,虽然查询条件字段上有索引,但查询并没有走索引,这是为什么?

  先来学习一下基础知识。因为oracle内部优化器在解析sql语句执行时,有基于规则(RBO:Rule Based Optimization)和基于成本(CBO: Cost Based Optimization)两种方式来选择sql语句的执行路径。

  基于规则:优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则,对数据是不敏感的。它只借助少量的信息来决定一个sql语句的执行计划。(10G之后不再支持)

  基于成本:通过代价引擎来估计每个执行计划所需的代价,该代价将每个执行计划所耗费的资源进行量化,CBO根据这个代价选择出最优的执行计划。包括:I/O代价、CPU代价、NETWORK代价。

  上面的实验就是一个很好的例子,test01.c1列上存在索引,但返回结果集数据量发生变化时,执行路径并不相同。在第三次,数据量变成3W条时,执行计划选择了全表扫描(TABLE ACCESS FULL),因为oracle认为取3W条数据时走全表扫描比走索引代价更低,所以当条件不同、数据量不同时sql语句的执行计划也不尽相同。

      另外,查询走索引也不一定能加快查询速度。

  强制索引扫描:

SQL> set autotrace traceonly;
SQL> alter system flush buffer_cache;
SQL> select /*+INDEX(TEST01 IDX_TEST01_C1) */ * from test01 where c1 between 10000 and 100000;

跟踪结果:
Statistics
----------------------------------------------------------
      1  recursive calls
      0  db block gets
      12561  consistent gets
    601  physical reads
      0  redo size
    3994849  bytes sent via SQL*Net to client
      66523  bytes received via SQL*Net from client
       6002  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      90001  rows processed

强制全表扫描:

SQL> alter system flush buffer_cache;
SQL> select /*+FULL(TEST01) */ * from test01 where c1 between 10000 and 100000;

跟踪结果:
Statistics
----------------------------------------------------------
      0  recursive calls
      1  db block gets
       6453  consistent gets
    475  physical reads
      0  redo size
    3634901  bytes sent via SQL*Net to client
      66523  bytes received via SQL*Net from client
       6002  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
      90001  rows processed

  根据两个语句的执行计划来看,后者的物理读和一致性读的次数较少,表示更快的sql执行速度,这又是为何?索引范围扫描是串行单块读,全表扫描则是多块读(一个cache buffer chains可加载多个数据块),所以后者的减少了I/O次数,提升了查询效率。