Oracle索引和MySQL索引是一个概念,都是为了提高数据库查询效率,例如字典的目录,就是一种索引。不同的索引有不同的查询效率,比如字典的目录有以拼音首字母的,有偏旁部首的。当我们对所有索引类型有了了解之后,就可以针对性的写出高效的SQL语句、建立最合适的索引。
那Oracle索引都有哪些类型呢?
首先我们来看一下索引的数据结构:B树
在介绍之前,首先建立一张表
create table test(
id number,
a number,
b number,
c number
);
alter table test add constraint pk_test primary key (id) using index;
插入数据
begin
for i in 1 .. 100000 loop
insert into test
values
(i,mod(i,2),mod(i,20000),mod(i,20000));
end loop;
commit;
end;
表建好了,有一个主键id,和三个值a、b、c。
下面开始我们的表演
看下面这个SQL语句
select id from test where id = 5000;
我们知道id是主键,具有唯一性,而上述语句的SQL是一个等值条件(id=400,而<、>、>=等都属于非等值条件),所以该语句执行的索引为INDEX UNIQUE SCAN,这个速度是最快的。
条件:主键等值查询
看下面这个SQL
select id from test where id<5000;
这个属于主键上的非等值查询条件,走的就是索引范围扫描,当然还包括非主键索引情况。
下面我们再针对a、b、c建立两个索引:
create index idx_test_id_ab on test(a,b);
create index idx_test_id_c on test(c);
第一个索引是一个a和b的复合索引,第二个索引建立在c上。
我们看下面这个SQL
select * from test where c=200;
该语句也会走索引范围扫描。
你是否有疑问,我们如何知道语句是走哪种索引的?这可以从Oracle的执行计划看到,查看Oracle的执行计划只需要将你要观察的索引加入到其监控中。
exec dbms.stats.gather_table_stats('table_space','table_name');
exec dbms.stats.gather_index_stats('table_space','index_name');
条件:主键非等值查询、非主键查询。
对于表来说,有全表扫描。对于索引来说,也存在全索引扫描,与全表扫描非常类似。索引扫描只在CBO模式下起作用。
什么是CBO模式?这就不得不说Oracle的两种优化器:
RBO:Rule-Based Optimization,Oracle 10g版本后被弃用,RBO是基于预先设定好的语法优先级对语法进行执行计划的优化,所以开发者必须非常了解RBO的规则,这种方式非常呆板,因为其只认规则。
CBO:Cost-Based Optimization,Oracle 8引入,Oracle 10g取代了RBO,根据SQL执行情况的统计信息来对SQL的执行计划进行优化,这部分是Oracle公司保密的。问过Oracle公司的讲师说也不清楚具体细节。
这种方式有个特点,会自动对数据进行排序。省去了全表扫描后,再进行order by的操作。
因为B树索引本身就是排序好的,默认是ASC升序,可以在创建索引的时候进行指定。但是Oracle的执行计划会自动针对升序的降序查询进行优化,那么为什么要存在降序操作?答案是:在复合索引上,可以对(a desc,b asc),满足一定的业务场景。
我们看下面这个SQL
select * from test order by id;
因为排序的条件只有id,并且id已经建立索引,所以执行计划会被优化成INDEX FULL SCAN。
条件:表和表进行连接查询,查询语句中有order by,group by并且子句所有列都在索引中(联合索引)
小tip:如何看oracle的执行计划?sqlplus也可,但当然还有更好的方式,我用的Navicat,执行完sql后,可以通过它的“解释”功能看执行计划,走的什么索引类型,走的哪个索引。
快速全表扫描是扫描索引中的所有数据块,与全表扫描比,区别就是其不进行排序,即在这种方式下,返回的数据不是以排序的形式。可以多块读、并行读。所以叫FAST。
看下面的语句
select a,b from test where b<1000;
这个语句有两个特点,第一:返回值a和b都在索引上,第二:查询条件也在索引上。这条语句通过B树索引查询到rowid后,不需要额外在去原来的表里查数据了。为什么呢?回忆一下,符合索引包括根、枝、叶,叶子上存储的是索引值,包括:rowid、键值、键值长度、所属标号。看到没,如果所取的值都在索引上,就可以直接返回了,如果是
select a,b,c from test where b<1000;
这样返回值多了一个c,并不在复合索引上,所以还会用查到的rowid,去原表中取c的值,这样就不会走INDEX FAST FULL SCAN了。
这个也很简单,在复合索引中,可能会有如下类型数据:
a | b |
---|---|
1 | 1 |
1 | 2 |
1 | 3 |
0 | 4 |
0 | 5 |
0 | 6 |
… | … |
可以看b的值是不同的,a的取值只有0和1。索引跳跃扫描是扫描意思呢?就是当在这样一种a,b取值的情况下,对a和b建复合索引,oracle的优化器会将其优化成两个索引,分别是当a=0,a=1时的索引。
那么我们在a,b上建联合索引,仿佛有些问题。理论上不会在这样的一个没有多大区分度的a值上建索引的,所以一般看到INDEX SKIP SCAN,其一般开销都很大。
当一个查询语句中,有两个查询,这两个查询列对应两个索引值。这种情况下就会出现索引组合扫描。比如:
select * from test where b<1000 and c>200;
ps,这里假设我们对b和c分别建立的索引。
走INDEX COMBINE会比单独走b或c的索引,开销都要小。
有心人是否看到,这个sql要取的值是所有,并非b或c。那么如果取的b或c呢?看下面的
select b,c from test where b<1000 and c>200;
这种情况就是所查询的值在索引上,可以直接返回,不在用查到的rowid回原表取数据的情况。
严格来说,INDEX COMBINE和INDEX JOIN都不能算是一种独立的索引,只是对开头的5种索引的一种优化或补充。
到这里7种索引类型都介绍完了,留一个疑问
INDEX FAST FULL SCAN一定比INDEX FULL SCAN要快吗?