oracle数据库索引相关知识

1、 索引:

按脚本新建两张表T1,T2表中数据存储以下: java

wps754.tmp.jpeg

当对两张表进行相同的查询操做时,T1表会使用索引,但T2表不会它将进行全表扫描。sql

select * from t1 where id=10;
select * from t2 where id=10;
复制代码

这主要是由于T1表的聚簇因子的值接近表的表块数量,而 T2表的聚簇因子的值接近表中数据数量。 若是聚簇因子的值接近表的表块数量,则说明目标索引索引行和存储于对应表中数据行的存储顺序类似程度很是高。这就意味着Oracle走索引范围扫描后取得目标rowid再回表访问对应表块中的数据时,相邻的索引行所对应的rowid找到的表数据很可能处于同一表块中。即Oracle在经过索引行记录的rowid回表第一次读取对应的表块并将该表块缓存在buffer cache中后,当再经过相邻索引行记录的rowid回表第二次去读取对应的表块时就不须要再产生物理I/O了,由于这两次访问的是同一个表块,而这个表块已经缓存在buffer cache中了。 若是聚簇因子的值接近表的记录数,则说明目标索引索引行和存储于对应表中数据行的存储顺序类似程度很是低,这就意味着Oracle走索引范围扫描取得rowid再回表访问对应表块的数据时,相邻的索引行所对应的rowid很可能不处于同一表块中,说明Oracle每次都要读对应的表块,每次都产生物理I/O。数据库

B-TREE索引结构

wps764.tmp.jpeg

经过B树索引访问数据的过程:先访问相关的B树索引,而后根据访问该索引后获得的ROWID再回表访问相应的数据行记录。 这里访问相关的B树索引和回表都须要消耗I/O,这意味着访问索引的成本由两部分组成:一部分是访问相关的B树索引的成本(从根节点定位到相关的分支块,再定位到相关的叶子块,最后对这些叶子块进行扫描);另外一部分是回表的成本(根据ROWID再回表扫面对应的数据行所在数据块)缓存

wps765.tmp.jpeg

B树索引的优点:

  • 全部的索引叶子块都在同一层,即它们距离索引根节点的深度是相同的。这也意味着访问索引叶子块的任何一个索引键值所花费的时间几乎相同;
  • 经过B树索引访问表里行记录的效率并不会随着表的数据量的递增而显著下降,即经过走索引访问数据的时间是可控的、基本稳定的。

2、 访问索引的方法:

1.索引惟一性扫描,即准对惟一索引的扫面。

wps766.tmp.jpeg

2.索引范围扫描,当扫描的对象时惟一性索引时,where条件必定是范围查询(即detween、<、>等);非惟一索引时没有限制。

wps777.tmp.jpeg

3.索引全扫描:

适用于全部类型的B树索引。所谓的索引全扫描是指扫描目标索引的全部叶子块的全部索引列。这里须要注意索引全扫描须要扫描全部的叶子块但不意味着须要扫描全部的分支块。Oracle在作索引全扫描时只须要经过访问必要的分支块定位到位于该索引最左边的叶子块的第一行索引行就能够利用该索引叶子块之间的双向指针链表,从左到右依次顺序扫描该索引全部叶子块的全部索引行了。 由于索引是有序的因此经过索引全扫描获得的结果也是有序的便可以免排序操做。 函数

wps778.tmp.jpeg

由于索引全扫描获得的结果是有序因此不能并行执行,并且只能单块读。oop

注意:索引列不能容许null,不然将不会走索引全扫描。优化

4.索引快速全扫描:和索引全扫描相似但它可使用多块读,也能够并行执行,但它的结果是无序的。

5.全表扫描可使用多块读,会一直扫描到高水位。

wps788.tmp.jpeg

须要注意当使用delete删除数据时,高水位会保持不变就是说全表扫描所需的时间会差很少。 ui

wps78A.tmp.jpeg

3、 索引的实际开发中的使用

  1. 查询列的值为null的状况下如何走索引,能够参考建立以下的索引:
create index idx_t3 on test3(object_id,0);
复制代码
  1. 函数索引:
create index idx_t3_tm on test3(trunc(created));
复制代码
  1. 左%使用索引:
create index idx_t3_nm on test3(reverse(object_name));
复制代码
  1. 两列拼后接走索引:
create index idx_t3_un on test3(owner||'_'||object_type);
复制代码
  1. 组合索引:选择性高的字段放前面
create index idx_t3 on test3(object_name,object_id);
select t.* from edcs.test3 t where t.object_id > 1 and t.object_name='I_IND1';
复制代码

4、 表链接方法

1. 排序合并连接(Sort Merge Join)

若是两张表(T1和T2表)在作表连接时使用的是排序合并连接,则Oarcle会按下面的步骤执行:spa

  1. 首先以目标表SQL中指定的谓语条件(若是有的话)去访问表T1,而后对访问结果按照表T1中的连接列作排序,排序后获得的结果集记为1;
  2. 接着以目标表SQL中指定的谓语条件(若是有的话)去访问表T2,而后对访问结果按照表T2中的连接列作排序,排序后获得的结果集记为2;
  3. 最后对结果集1和结果集1作合并操做,从中取出匹配的记录返回。

一般状况下,排序合并连接的效率不如哈希连接,但哈希连接只能使用等值连接条件,而排序合并连接能够用于非等值连接。排序合并连接由于须要对数据进行排序当数据量很大时排序是很耗I/O的。3d

可使用use_merge()提示指定走排序合并连接。

wps79B.tmp.jpeg

2. 嵌套循环连接(Nested Loops Join)

若是两张表(T1和T2表)在作表连接时使用的是嵌套循环连接,则Oarcle会按下面的步骤执行:

  1. 首先优化器会按必定的规则来决定表T1 、T2中谁是驱动表、谁是被驱动表。驱动表用于外层循环,被驱动表用于内层循环。这里假设T1是驱动表,T2是被驱动表;
  2. 接着以目标SQL中指定的谓语条件(若是有的话)去访问驱动表T1获得的结果集记为结果集1;
  3. 而后遍历结果集1逐条取出,在遍历被驱动表T2找到匹配的记录。至关于java中的两层for循环;

经过上面的描述咱们能够看出,驱动表的结果集每每比较小,若是在被驱动表的连接上又存在选择性较好的索引,那么嵌套循环执行效率就会很是高。 嵌套循环还有一个优点就是它能够实现快速响应,即它能够第一时间将已连接过且符合连接条件的数据先返回,而不用等全部的连接操做都完成在返回。 可使用use_nl()提示指定走嵌套循环连接。

wps79C.tmp.jpeg

三、 哈希连接(Hash Join)

哈希连接是一种两个表在作表连接时主要依靠哈希运算来获得连接结果集的表连接方法。

在Oracle7.3以前数据库经常使用的表链接方法主要是排序合并连接和嵌套循环连接两种,但这两种方法都有各自明显的缺陷。对于排序合并连接,若是两个表在施加了目标SQL中指定的谓语条件后获得的结果集很大且须要排序则排序合并连接的执行效率必定不高;对于嵌套循环连接若是驱动表对应的驱动结果集很大,即便在被驱动表的连接列上存在索引效率也是很低。为解决上面的问题在Oracle7.3之后引入了哈希连接。但它只适用于等值的连接条件。

可使用use_hash()提示指定走哈希连接。

wps79D.tmp.jpeg

4. 笛卡尔连接(Cross Join)

若是两张表(T1和T2表)在作表连接时使用的是笛卡尔连接,则Oarcle会按下面的步骤执行:

  1. 首先以目标表SQL中指定的谓语条件(若是有的话)去访问表T1,此时获得的结果集记为结果集1记录数为m;
  2. 接着以目标表SQL中指定的谓语条件(若是有的话)去访问表T2,此时获得的结果集记为结果集2记录数为n;
  3. 最终对结果集1和结果集2执行合并操做,最终的记录数为m*n。

sql优化脚本

--建表
create table test1 as (select trunc((rownum-1)/100) id,rpad(rownum,100) t_pad from dba_source where rownum<=10000);
create table test2 as (select mod(rownum-1,100) id,rpad(rownum,100) t_pad from dba_source where rownum<=10000);
--建索引
create index idx_t1 on test1(id);
create index idx_t2 on test2(id);
--表分析的方法
analyze table test1 compute statistics for table ;
analyze table test2 compute statistics for table ;
--索引分析的方法
analyze index idx_t1 compute statistics ;
analyze index idx_t2 compute statistics ;

--查看统计信息
SELECT A.INDEX_NAME, B.NUM_ROWS, B.BLOCKS, A.CLUSTERING_FACTOR
  FROM USER_INDEXES A, USER_TABLES B
 WHERE A.INDEX_NAME IN ('IDX_T1','IDX_T2')
   AND A.TABLE_NAME = B.TABLE_NAME;
   
select count(1) from test1 where id=10;
select count(1) from test2 where id=10; 

select count(1) from test1 ;
select count(1) from test2 ;
  
select * from edcs.test1 where id=10;
select * from edcs.test2 t where id=10;  


create table test3 as select * from dba_objects;
analyze table edcs.test3 compute statistics for table ;
create index idx_t3 on test3(object_id);
analyze index idx_t3 compute statistics;
create index idx_t3_tm on test3(created);
analyze index idx_t3_tm compute statistics ;
select count(1) from test3 t;  --77230

--查看执行计划
F5
set autot trace;
select * from table(dbms_xplan.display_cursor(null,null,'advanced'));

--惟一索引
select * from tm_dcs_process_detail_config t where t.process_detail_id=2585378;
--范围索引
select * from edcs.test3 t where t.object_id=15;
--索引全扫描
select t.process_detail_id from tm_dcs_process_detail_config t ;
--索引快速全扫描
select T.OBJECT_ID from TEST3 T;
select /*+ index_ffs(T IDX_T3) */ T.OBJECT_ID from TEST3 T;


--索引开发举例
--存在null值走索引的方法
select rowid ,t.* from test3 t where t.object_id is null;
drop index idx_t3;
create index idx_t3 on test3(object_id,0);
analyze index idx_t3 compute statistics ;

--函数索引
select * from test3 t where trunc(created)>=date'2011-09-16';
drop index idx_t3_tm;
create index idx_t3_tm on test3(trunc(created));
analyze index idx_t3_tm compute statistics ;
--%使用索引
create index idx_t3_nm on test3(object_name);
select * from test3 t where t.object_name like '%CON1';
drop index idx_t3_nm;
create index idx_t3_nm on test3(reverse(object_name));
analyze index idx_t3_nm compute statistics ;
select t.* from test3 t where reverse(t.object_name) LIKE reverse('%CON1'); 
--两列拼接走索引
select * from test3 t where owner||'_'||object_type ='SYS_CLUSTER';
create index idx_t3_un on test3(owner||'_'||object_type);
analyze index idx_t3_un compute statistics ;
--组合索引
create index idx_t3 on test3(object_id,object_name);
create index idx_t3 on test3(object_name,object_id);
select t.* from edcs.test3 t where t.object_id > 1 and t.object_name='I_IND1';

--表链接方式
select * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;

select /*+ use_merge(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;
select /*+ use_nl(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;
select /*+ use_hash(t1 t2) */ * from tt_waybill_bak t1,tt_waybill_fee_bak t2 where t1.waybill_no=t2.waybill_no;


select t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
       
select t1.*       
  from tt_waybill_fee_bak t1,temp3 t2
 where t1.waybill_no = t2.waybill_no;      
       
select t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select /*+ no_unnest */ t2.waybill_no from temp3 t2);  

--并行 
select /*+ PARALLEL(T1 4)*/ t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
       
select  t1.*
  from tt_waybill_fee_bak t1
 where t1.waybill_no in
       (select t2.waybill_no from temp3 t2);
复制代码

转载自: 简书 - 低至一折起

文章:www.jianshu.com/p/45b1b8838…

相关文章
相关标签/搜索