11 Indexes

本章提要
--------------------------------------
索引会影响 DML 与 select 操作, 要找到平衡点
最好从一开始就创建好索引
索引概述
B*索引
其他一些索引
索引使用中的一些基本问题
--------------------------------------
索引概述
    oracle提供的索引种类:
        B*树索引, 我们所说的"传统"索引, 并不是二叉树, 这里的"B" 代表平衡, B*树索引的子类型:
            索引组织表
            B*树聚簇索引
            降序索引, 允许索引结构中按"从大到小"顺序排序
            反向键索引, 键中的字节会"反转"
    位图索引:
        在B*树中, 通常索引条目和行之间存在一种一对一的关系, 一个索引条目就指向一行, 而对于位图索引, 一个索引条目则使用一个位图
        同时指向多行, 位图索引适用于高度重复而且通常只读的数据, 在一个OLTP数据库中, 由于存在并发性相关的问题, 所以不能考虑使用
    位图索引:
        位图联结索引, 在多表联结时, 可能被使用, 但是, 同样, OLTP中不能使用.
    基于函数的索引:
        这些就是 B* 树索引或位图索引, 它将一个函数计算得到的结果存储在行的列中, 而不是存储列数据本身, 可以把基于函数的索引看做
        一个虚拟列(或派生列)上的索引, 换句话说, 这个列并不物理地存储在表中, 基于函数的索引可以用于加快形如 select * from t
        where function(database_column) = some_value这样的查询, 因为值 function(database_column)已经提前计算并存储在索引中.
    应用域索引:
B* 树索引
    我们所说的"传统"索引, 数据库最常用的一类索引结构, 其实现与二叉查找树很相似,    如图:
       
        B*树索引不存在非唯一条目, (索引也是一个存储结构, 在这个存储结构中不存在两行完全相同的数据), 在一个非唯一的索引中, oracle会
        把rowid作为一个额外的列追加到键上, 使得键唯一, 例如 create index i on t(x,y)索引, 从概念上讲, 它就是create unique index i
        on t(x, y, rowid). 在一个唯一索引中, 根据你定义的唯一性, oracle 不会再向索引键增加rowid.
        B*树特点之一是, 所有叶子块都应该在树的同一个层上, 这一层也称为索引的高度, 这说明所有索引的根块到叶子块的遍历都会访问同样
        数目的块, 大多数B*树索引的高度都是2或3, 即使索引中有数百万行记录也是如此, 这说明, 一般来讲, 在索引中找到一个键值只需要执行
        2或3次I/O. (blevel统计的是分支数, 即比高度少1)
        select index_name, blevel, num_rows from user_indexes where table_name = 'BIG_TABLE';
        B*树是一个绝佳的通用索引机制, 无论是大表还是小表都适用, 随着底层表大小的增长, 获取数据的性能只稍有恶化(或根本不恶化)
    索引键压缩(个人感觉用处不大)
        索引键条目分解为两部分, 前缀和后缀, 前缀有重复, 后缀没有重复(唯一区域)
        实验,
       

create table t
 as
 select * from all_objects
 where rownum <= 50000;

create index t_idx on
 t(owner,object_type,object_name);

analyze index t_idx validate structure;

create table idx_stats
 as
 select 'noncompressed' what, a.*
 from index_stats a;

drop index t_idx;
create index t_idx on
t(owner,object_type,object_name)
compress &1;    -- 分别用1,2,3来代替
analyze index t_idx validate structure;
insert into idx_stats
select 'compress &1', a.*    -- 分别用1,2,3来代替
from index_stats a;

select what, height, lf_blks, br_blks,
 btree_space, opt_cmpr_count, opt_cmpr_pctsave
 from idx_stats
 /
11-1

 


        对现在来说, 这种压缩并不是免费的, 现在压缩索引比原来更复杂了, oracle会花更多时间来处理这个索引结构中的数据, 不光在修改期间
        维护索引更耗时, 查询期间搜索索引也更花时间.    
    反向键索引(还比较重要, 分情况使用)
        反向键索引只是将索引键中各个列的字节反转, 如果考虑 90101, 90102, 和90103, 如果使用 oracle dump函数查看其内部表示, 可以看到
        这几个数表示如下:
        select 90101, dump(90101,16) from dual
         union all
        select 90102, dump(90102,16) from dual
         union all
         select 90103, dump(90103,16) from dual
         /
        结果是:
         90101 DUMP(90101,16)       
        ---------- ---------------------
         90101 Typ=2 Len=4: c3,a,2,2
         90102 Typ=2 Len=4: c3,a,2,3
         90103 Typ=2 Len=4: c3,a,2,4

        3 rows selected.    
        每个数的长度都是4字节, 它们只是最后一个字节有所不同, 这些书最后可能在一个索引结构中向右一次放置(放置的比较近), 不过, 如果
        反转这些数的字节, oracle就会插入以下值:
        90101 reversed = 2,2,a,c3
        90102 reversed = 3,2,a,c3
        90103 reversed = 4,2,a,c3
        这些数之间最后可能相距很远, 这样访问同一个块(最右边的块)的RAC实例个数就能减少, 反向键索引的缺点之一是, 能用常规索引的地方
        不一定能用反向键索引, 例如: 在回答谓词时, x 上的反向键索引就没用: where x > 5
        存储之前, 数据部是按 x 在索引中派讯, 而是按 reverse(x)排序, 因此, 对 x>5的区间扫描不能使用这个索引. 另外有些谓词有可以,比如
        在(x,y)上有一个串联索引, where x = 5 就可以, 这是因为, 首先将x的字节翻转, 然后再将 y 的字节翻转, oracle并不是将(x||y)的字节
        反转, 而是会存储(reverse(x) || reverse(y)), 这说明, x = 5 的所有值会存储在一起, 所以 oracle 可以对这个索引执行区间扫描来
        找到所有这些数据.
        下面假设一个用序列填充的表上有一个代理主键,而且不需要在这个(主键)索引上使用区间扫描, 也就是说, 不需要 max(primary_key),
        where primary_key < 100 等查询(如果有这个查询条件, 那么就不能使用反向索引, 因为反向索引在这种情况下不能被使用), 在有大量插入
        操作的情况下, 即使只有一个实例, 也要使用反向索引. 下面测试:
      

create table t tablespace assm
as
select 0 id, a.*
from all_objects a
where 1=0;

alter table t
add constraint t_pk
primary key (id)
using index (create index t_pk on t(id) &indexType tablespace assm);

create sequence s cache 1000;

-- 如果把 &indexType 替换为reverse, 就会创建一个反向索引, 如果不加&indexType
-- 即替换为"什么也没有", 则表示使用一个"常规"索引

-- 开始测试
create or replace procedure do_sql
as
begin
for x in ( select rownum r, all_objects.* from all_objects )
loop
insert into t
( id, OWNER, OBJECT_NAME, SUBOBJECT_NAME,
OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE, CREATED,
LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY,
GENERATED, SECONDARY )
values
( s.nextval, x.OWNER, x.OBJECT_NAME, x.SUBOBJECT_NAME,
x.OBJECT_ID, x.DATA_OBJECT_ID, x.OBJECT_TYPE, x.CREATED,
x.LAST_DDL_TIME, x.TIMESTAMP, x.STATUS, x.TEMPORARY,
x.GENERATED, x.SECONDARY );
if ( mod(x.r,100) = 0 )
then
commit;
end if;
end loop;
commit;
end;
/

-- c 代码, 目前无法测试
exec sql declare c cursor for select * from all_objects;
exec sql whenever notfound do break;
for(;;)
{
exec sql
fetch c into :owner:owner_i,
:object_name:object_name_i, :subobject_name:subobject_name_i,
:object_id:object_id_i, :data_object_id:data_object_id_i,
:object_type:object_type_i, :created:created_i,
:last_ddl_time:last_ddl_time_i, :timestamp:timestamp_i,
:status:status_i, :temporary:temporary_i,
:generated:generated_i, :secondary:secondary_i;
exec sql
insert into t
( id, OWNER, OBJECT_NAME, SUBOBJECT_NAME,
OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE, CREATED,
LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY,
GENERATED, SECONDARY )
values
( s.nextval, :owner:owner_i, :object_name:object_name_i,
:subobject_name:subobject_name_i, :object_id:object_id_i,
:data_object_id:data_object_id_i, :object_type:object_type_i,
:created:created_i, :last_ddl_time:last_ddl_time_i,
:timestamp:timestamp_i, :status:status_i,
:temporary:temporary_i, :generated:generated_i,
:secondary:secondary_i );
if ( (++cnt%100) == 0 )
{
exec sql commit;
}
}
exec sql whenever notfound continue;
exec sql commit;
exec sql close c;
11-2

 


        测试的结果是(书上看的,目前无法测试)使用反向索引高效的多
    降序索引(个人感觉, 作用一般)
        测试:
       

create table t
 as
 select *
 from all_objects
 /

create index t_idx
 on t(owner,object_type,object_name);

begin
 dbms_stats.gather_table_stats
 ( user, 'T', method_opt=>'for all indexed columns' );
 end;
 /

set autotrace traceonly explain
select owner, object_type
 from t
 where owner between 'T' and 'Z'
 and object_type is not null
 order by owner DESC, object_type DESC;

-- oracle 会往前读索引
-- 这个explain 计划中最后没有排序步骤, 数据已经是有序的, 不过, 如果你有一组列,
-- 其中一些列按升序排序(ASC), 另外一些列按降序排列(DESC), 此时降序索引就能用.例如:

select owner, object_type
 from t
 where owner between 'T' and 'Z'
 and object_type is not null
 order by owner DESC, object_type ASC;

-- oracle 不能再使用(owner, object_type, object_name)上索引对数据排序, 它可以往前
-- 读得到按 owner desc 排序的数据, 但是现在还需要"向后读"来得到按object_type升序
-- ASC的数据, 此时oracle的实际做法是, 它会把所有行收集起来,然后排序, 但是如果有
-- DESC索引, 则有:
create index desc_t_idx on t(owner desc,object_type asc);

select owner, object_type
 from t
 where owner between 'T' and 'Z'
 and object_type is not null
 order by owner DESC, object_type ASC;
11-3

 


        查询中最好别少了 orader by, 即使你的查询计划中包含一个索引, 但这并不表示数据会以"某种顺序"返回, 要想从数据库以某种有序的顺序
        获取数据, 唯一的办法就是在查询中包括一个 order by 子句, order by 是无可替代的.
    什么情况下应该使用 B* 树索引
        仅当要通过索引访问表中很少的一部分(只占很小百分比, 2%), 才能使用 B* 树在裂伤建立索引
        如果要处理表中的多行, 而且可以使用索引而不用表, 就可以使用一个B*树索引.(一个查询, 索引包含了足够的信息来回答查询, 我们根本
            不用去访问表) select count(*) from t 就是只读索引就可以了, 不需要访问表了.
        重要的是, 要了解这两个概念间的区别, 如果必须完成 table access by index rowid, 就必须确保只访问表中很少的一部分块(很小百分比),
        如果我们访问的行太多(所占百分比过大, 20%以上), 那么与全表扫描相比, 通过B*树索引来访问这些数据通常要花更长的时间.
        一般来讲, B*树索引会放在频繁使用查询谓词的裂伤, 而且我们希望从表中只返回少量的数据, 在一个thin表(也就是说很少的列, 或列很小),
        这个百分比相当小(2%~3%), 如果在一个fat表(也就是说, 有很多列, 或列很宽)百分比可能会升到表的20%~25%, 索引按索引建的顺序存储,
        索引会按键的有序顺序访问, 索引指向的块则随机的存储在堆中, 因此,我们通过索引访问表时, 会执行大量分散, 分散是指: 索引会告诉我们
        读取块1, 然后是块1000, 块205, 块321, 等等, 它不会要求我们按一种连续的方式读取块1, 块2, 块3 等等.
        下面来看一个例子:
        假设我们索引读取一个thin表, 而且要读取表中20%的行, 若这个表中有100 000行, 其中20%就是20000行, 如果行大小为80字节,
        在一个块大小为8kb的数据库中, 每个块上大约100行, 这说明, 大约是20000个 table access by rowid 操作, 为此要处理20000个表快来执行
        查询, 不过, 整个表才只有1000个块, 在这种情况下 全表扫描就比索引高效的多.
        1) 物理组织
            数据在磁盘上如何物理地组织, 对上述计算会有显著影响, 因为这会大大影响索引访问的开销, 假设一个表, 其中的行主键由一个序列来
            填充, 向这个表增加数据时, 序列号相邻的行一般存储位置也会彼此相邻.表会很自然的按主键顺序聚簇(因为数据或多或少就是以这种
            顺序增加的), 当然, 它不一定严格按照聚簇(要想做到这一点, 必须使用一个IOT), 一般来讲, 主键值彼此接近的行的物理位置也会靠在
            一起, 如果发生下面查询: select * from t where primary_key between :x and :y;
            你想要的行通常就位于同样的块上, 在这种情况下, 即使要访问大量的行(占很大的百分比), 索引区间扫描可能也很有用, 原因在于:
            我们需要读取和重新读取的数据库块可能会被缓存, 因为数据共同放置在同一个位置, 另一方面, 如果行并非共同存储在一个位置上, 使用
            这个索引对性能来讲可能就是灾难性的. 下面演示:
          

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');
 end;
 /

-- 这个表正好满足前面的描述, 即在块大小为8kb的一个数据库中, 每块大约 100 行,
-- 在这个表中, x = 1,2,3 的行极有可能在同一个块上, 仍取这个表, 但有意的使他
-- "无组织", 在 colocated表中, 我们创建了一个y列, 它带有一个前导随机数, 现在
-- 利用这一点使得数据无组织, 即不再按主键排序:

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');
 end;
 /
-- 比较这两个表,虽然含有相同的结果集, 但是在性能上却天壤之别
11-4

 


        2) 聚簇因子
            我们查看 user_indexes视图中的 clustering_factor 列, 这个列有以下含义:
            根据索引的值指示表中行的有序程度
                如果这个值与块数接近, 则说明表相当有序, 得到了很好的组织, 在这种情况下, 同一个叶子块中的索引条目可能指向同一个数据
                    块上的行.
                如果这个值与行数接近, 表的次序可能就是非常随机的, 在这种情况下, 同一个叶子块上的索引条目不太可能指向同一个数据块上的行.
            可以把聚簇因子看做是通过索引读取整个表时对表执行的逻辑I/O次数. 查看索引时, 会得到以下结果:
            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 99930
            所以, 数据库说, "如果通过索引 COLOCATED_PK从头到尾读取COLOCATED表中的每一行, 就要执行1190次I/O, 不过我们队DISORGANIZED表做
            同样的事情, 则会对这个表执行 99930次 I/O" 差别的原因在于: 当oracle对索引结构执行区间扫描时, 如果它发现索引中的下一行与前
            一行在同一个数据库块上, 就不会再执行另一个 I/O, 不过, 如果下一行不在同一个块上, 就会释放当前的这个块, 而执行令一个I/O从
            缓冲区缓存获取到处理的下一个块, 因此, 在我们对索引执行区间扫描时, COLOCATED_PK索引就会发现下一行几乎总与前一行在同一个块上,
            DISORGANIZED_PK 索引发现的情况则恰好相反.
位图索引
    是为数据仓库, 即查询环境设计的, 位图索引的结构: 其中用一个索引键条目存储指向多行的指针, 这与 B*树结构部同, 在位图索引中, 可能只有
    很少的索引条目, create BITMAP index job_idx on emp(job);
    什么情况下使用位图索引:
    差异数低的数据最为合适, 比如性别, 在一个上亿条记录的表中, 100000也能算差异低的数据.
位图索引联结(个人感觉用处不大)
    通常在都是在一个表上创建索引,而且值使用这个表的列, 位图联结索引则打破了这个规则, 它允许使用另外某个表的列对一个给定表建立索引, 例如:
    create bitmap index emp_bm_idx
    on emp( d.dname )
    from emp e, dept d
    where e.deptno = d.deptno
    /
基于函数的索引(有点用)
    利用基于函数的索引, 我们能够对计算得出的列建立索引, 并在查询中使用这些索引.
    简单的基于函数索引的例子:
    我们想在EMP表的ENAME列上执行一个大小写无关的搜索, 在基于函数的索引引入之前, 我们可能必须采用另外一种完全不同的方式来做, 可能要为EMP
    表增加一个额外的列, 例如名为 UPPER_ENAME的列, 这个列由 insert 和 update 上的一个触发器维护, 这个触发器只是设置
    NEW.UPPER_NAME := UPPER(:NEW.ENAME). 另外要在这个额外的列上建立索引, 但是, 现在我们可以利用基于函数的索引了.
  

create table emp
 as
 select *
 from scott.emp
 where 1=0;

insert into emp
 (empno,ename,job,mgr,hiredate,sal,comm,deptno)
 select rownum empno,
 initcap(substr(object_name,1,10)) ename,
 substr(object_type,1,9) JOB,
 rownum MGR,
 created hiredate,
 rownum SAL,
 rownum COMM,
 (mod(rownum,4)+1)*10 DEPTNO
 from all_objects
 where rownum < 10000;

create index emp_upper_idx on emp(upper(ename));

-- 接下来分析这个表, 让查询使用函数索引
-- 从 oracle 10g以后, 这步骤不必要, 因为默认就会使用
begin
 dbms_stats.gather_table_stats
 (user,'EMP',cascade=>true);
 end;
 /

select *
 from emp
 where upper(ename) = 'KING';

-- 通过执行计划, 可以看到, access(upper("ENAME")='KING')
11-5

 


    计划函数的索引除了对使用内置函数的查询显然有帮助之外(自定义的函数也可以), 还可以用来有选择地只是对表中的某些行建立索引, 如果在表T上
    有一个索引 I: create index I on t(a, b); -- 这是B*树索引, 而且行中的A和B都为NULL, 索引结构中就没有相应的条目, 如果只对表中的某些行
    建立索引, 这就能用的上, 考虑一个很大的表, 其中有一个 NOT NULL列, 名为 PROCESSED_FLAG, 它有两个可取值: Y或N, 默认为N, 增加新行时,
    这个值都为N, 指示这一行未得到处理, 等到处理了这一行后, 则会将其更新为Y来指示已处理, 我们可能想对这个列建立索引, 从而能很快速的获取
    值为N的记录, 但是这里有数百万行, 而且几乎所有的行的值都为Y, 所得到的B*树索引将会很大, 如果我们把值从N更新为Y, 维护这样一个大索引的
    开销也相当高, 如果我们能只对感兴趣的记录建立索引(即该列值为 N 的记录).我们可以编写一个函数, 如果不想对某个给定的行加索引, 则这个
    函数就返回NULL, 而对想加索引的行则返回一个非NULL值. 例如, 由于我们只对列值为N的记录感兴趣, 所以只对这些记录加索引:
  

create index processed_flag_idx
 on big_table( case temporary when 'N' then 'N' end );

analyze index processed_flag_idx
 validate structure;

select name, btree_space, lf_rows, height
 from index_stats;
11-6

 


    当你在 to_date 函数上创建索引, 有时候并不能成功, 要在基于函数的索引使用to_date, 必须使用一种无歧义的确定性日期格式, 比如你用YYYY,
    to_date就是不确定的.
应用域索引(个人感觉没用)
    oracle 所谓的扩展索引, 利用应用域索引, 你可以创建自己的索引结构, 使之像oracle提供的索引一样工作. 举例, oracle自己的文本索引
索引的常见问题
    以下就是大师回答过最多关于索引的问题的一个小总结.
    1) 视图能使用索引么?
        视图实际上就是一个存储查询, oracle 会把查询中访问视图的有关文本代之以视图定义本身, 视图只是为了方便最终用户, 优化器还是对基表
        使用查询, 使用视图时, 完全可以考虑使用为基表编写的查询中所能用的所有索引, 对视图建立索引实际上就是对基表建立索引.
    2) null 和 索引能协作么?
        B*树索引不会存储完全为null的条目, 而位图和聚簇索引则不同, 另外, 还有人问, 为什么我的查询不使用索引, select * from t where x
        is null; 这个查询无法使用索引, 正是因为 B*树索引不会存储完全为null的条目, 但是如果你的索引键值中包含1列不为null的情况, 那么就
        可以使用索引, 如下例子:
     

create table t ( x int, y int NOT NULL );

create unique index t_idx on t(x,y);

insert into t values ( 1, 1 );

insert into t values ( NULL, 1 );

begin
 dbms_stats.gather_table_stats(user,'T');
 end;
 /

set autotrace on

select * from t where x is null; -- 这时, 显示使用了索引
11-8

 


    3) 外键是否应该加索引?
        前面章节中已经讨论过, 外键必须加索引.前几张也讨论了什么情况可以不对外键加索引(个人建议, 强烈加索引)
    4) 为什么没有使用我的索引?
        情况1, 我们在使用一个B*树索引, 而且谓词中没有使用索引的最前列.
            如果是这种情况, 可以假设一个表T, 在T(X,Y)上有一个索引, 我们要做查询: select * from t where y = 5, 此时, 优化器不打算使用
            T(x,y)上的索引, 因为谓词中不涉及X列, 在这种情况下, 倘若使用索引, 可能就必须查询每一个索引条目(稍后会讨论一种索引跳跃式扫描,
            这是一种例外情况), 而优化器通常更倾向于对T做全表扫描, 但这并不完全排除使用索引, 如果查询是 select x, y from t where y = 5,
            优化器就会注意到, 它不必全面扫描表来得到X 或 y, 对索引本身做一个快速的全面扫描会更合适, 因为这个索引一般比底层表小的多. 另
            一种情况下CBO也会使用T(X,Y)上的索引, 这就是索引跳跃式扫描, 当且仅当索引的最前列(在上一个例子中, 最前列就是x)只有很少的几个
            不同值, 而且优化器了解这一点, 跳跃式扫描就能很好的发挥作用.
        情况2, 我们使用 select count(*) from t 查询(或类似查询), 而且在表t上有一个B*树索引, 不过, 优化器并不是统计索引条目, 而是全表扫描
            在这种情况下, 索引可能建立在一些允许有null值的列上, 由于对于索引键完全为null的行不会建立相应的索引条目, 所以索引中的行数可
            能并不是表中的行数, 这里优化器选择是对的(当然, 如果这种情况使用索引, 那返回的行就不够了)
        情况3, 对于一个有索引的列, 做以下查询: select * from t where f(indexed_column) = value, 发现没有使用索引?
            原因是这个列上使用了函数, 我们是对 index_column 的值建立的索引, 而不是对 f(indexed_column)建立的索引, 因此不能使用这个索引,
            如果愿意, 可以另外对函数建立索引.(这个列上即有普通索引, 又有函数索引)
        情况4, 我们已经对一个字符列建立了索引, 这个列只包含数值数据, 如果使用以下语法来查询:
            select * from t where indexed_column = 5 注意查询中的数字5是常数5(而不是一个字符串), 此时就没有用indexed_column上的索引.
            因为, 我们队这个列隐式的应用了一个函数, select * from t where to_number(indexed_column) = 5, 这样就跟情况3一样了. 如果可能
            的话, 陶若谓词中有函数, 尽量不要对数据库列应用这些函数, 比如:
            where date_col >= trunc(sysdate) and date_col < trunc(sysdate+1), 可见应该尽量将函数应用在值上, 而不是列上.
        情况5, 有时使用了索引, 实际上反而会更慢, 例如:
           

create table t
 ( x, y , primary key (x) )
 as
 select rownum x, object_name
 from all_objects
 /

begin
 dbms_stats.gather_table_stats
 ( user, 'T', cascade=>true );
 end;
 /

set autotrace on explain

select count(y) from t where x < 50;    -- 优化器使用索引

select count(y) from t where x < 15000; -- 全表扫描
11-9

 


            对于查询调优时, 如果发现你认为本该使用的某个索引实际上并没有使用, 就不要冒然强制使用这个索引, 而应该先做个测试, 并证明使用
            这个索引后确实会加快速度, 然后再考虑强制使用索引.
        情况6, 有一段时间没有分析表, 这些表起先很小, 但等到查看时, 它已经增长的非常大, 有时候, 分析这个表, 然后就会使用索引. 分析表的
            作用时, 将这个表的正确的统计信息反馈给 CBO, 这样CBO能作出正确的选择.
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值