您的位置:首页 > 数据库 > Oracle

【Oracle 11g】利用基于函数的索引只对部分行建立索引

2016-09-25 18:43 597 查看
基于函数的索引除了对使用内置函数(如UPPER、LOWER等)的查询显然有帮助外、还可以用来有选择的只对表中的某些行建立索引。

前期准备

从这里可以下载big_table.sql文件。

http://pan.baidu.com/s/1nvk3Ged

如不方便下载,可以直接从这里copy.

create table big_table
as
select rownum id, a.*
from all_objects a
where 1=0
/
alter table big_table nologging;

declare
l_cnt number;
l_rows number := &1;
begin
insert /*+ append */
into big_table
select rownum, a.*
from all_objects a
where rownum <= &1;

l_cnt := sql%rowcount;

commit;

while (l_cnt < l_rows)
loop
insert /*+ APPEND */ into big_table
select rownum+l_cnt,
OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID,
OBJECT_TYPE, CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS,
TEMPORARY, GENERATED, SECONDARY, NAMESPACE, EDITION_NAME
from big_table
where rownum <= l_rows-l_cnt;
l_cnt := l_cnt + sql%rowcount;
commit;
end loop;
end;
/
alter table big_table add constraint
big_table_pk primary key(id);

exec dbms_stats.gather_table_stats( user, 'BIG_TABLE', estimate_percent=> 1);


进入文件所在的目录,执行如下命令,创建一个1000 000行的表。

F:\BaiduYunDownload>sqlplus / as sysdba

SQL*Plus: Release 11.2.0.1.0 Production on 星期日 9月 25 17:36:10 2016

Copyright (c) 1982, 2010, Oracle.  All rights reserved.

连接到:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> @big_table.sql 1000000

表已创建。

表已更改。

原值    3:     l_rows number := &1;
新值    3:     l_rows number := 1000000;
原值    9:       where rownum <= &1;
新值    9:       where rownum <= 1000000;

PL/SQL 过程已成功完成。

表已更改。

PL/SQL 过程已成功完成。


实验开始

更新temporary列,在此将Y变成N,以及把N变为Y:

SQL> update big_table set temporary = decode(temporary,'N','Y','N');

已更新1000000行。

SQL> commit;

提交完成。


检查Y与N的比例

SQL> select temporary , cnt,
2  round((ratio_to_report(cnt) over())*100,2) rtr
3  from(
4  select temporary, count(*) cnt
5  from big_table
6  group by temporary
7  )
8  /

T        CNT        RTR
- ---------- ----------
Y     997548      99.75
N       2452        .25


如果对temporary使用传统索引,会发现索引有1 000 000个条目,占用差不多14MB的空间,其高度为3:

SQL> create index processed_flag_idx
2  on big_table(temporary);

索引已创建。

SQL> analyze index processed_flag_idx
2  validate structure;

索引已分析

SQL> select name, btree_space, lf_rows, height
2  from index_stats;

NAME                           BTREE_SPACE    LF_ROWS     HEIGHT
------------------------------ ----------- ---------- ----------
PROCESSED_FLAG_IDX                14536160    1000000          3


通过这个索引获取任何数据都会带来3个I/O才能到达子叶块。这个索引不仅很宽,还很高。要得到第一个未处理的记录,必须至少执行4个I/O(其中3个是对索引的I/O,另外一个是对表的I/O)。

怎么能让索引更小呢?而且更新期间开销更小呢?

采用基于函数的索引,我们可以编写一个函数,如果不想对某个给定行家索引,则这个函数就返回NULL;而对想加索引的行则返回一个非NULL值。(B*树索引对于完全为NULL的键没有对应的条目)

例如,我们只对列值为N的记录感兴趣,所以只对这些记录加索引:

SQL> drop index processed_flag_idx;

索引已删除。

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

索引已创建。

SQL> analyze index processed_flag_idx
2  validate structure;

索引已分析

SQL> select name, btree_space, lf_rows, height
2  from index_stats;

NAME                           BTREE_SPACE    LF_ROWS     HEIGHT
------------------------------ ----------- ---------- ----------
PROCESSED_FLAG_IDX                   48032       2452          2


这就有很大不同了,这个索引只有48K大小,而不是14MB。高度也有所降低,与先前那个更高的索引相比,使用这个索引能少执行一个I/O。

下面进行测试,通过使用函数索引进行查询。

注意:函数索引,必须使用函数表达式,才能通过索引访问。如(case temporary when ‘N’ then ‘N’ end)=’N’.(中间的打印记录省略了)

(关于autotrace,参看官网

SQL> set timing on;
SQL> set autotrace on;
SQL> select * from big_table where (case temporary when  'N' then 'N' end)='N';

已用时间:  00: 00: 19.98

执行计划
----------------------------------------------------------
Plan hash value: 64562289

--------------------------------------------------------------------------------------------------

| Id  | Operation                   | Name               | Rows  | Bytes | Cost(%CPU)| Time     |

--------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT            |                    |  9991 |  1004K|   541   (0)| 00:00:07 |

|   1 |  TABLE ACCESS BY INDEX ROWID| BIG_TABLE          |  9991 |  1004K|   541   (0)| 00:00:07 |

|*  2 |   INDEX RANGE SCAN          | PROCESSED_FLAG_IDX |  2452 |       |     5   (0)| 00:00:01 |

--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

2 - access(CASE "TEMPORARY" WHEN 'N' THEN 'N' END ='N')

统计信息
----------------------------------------------------------
38  recursive calls
0  db block gets
842  consistent gets
0  physical reads
0  redo size
154484  bytes sent via SQL*Net to client
2208  bytes received via SQL*Net from client
165  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
2452  rows processed


可以看到,I/O总共842次。

如果没有使用函数索引访问呢?

可以看到总I/O增加,共计15091 次。

SQL> select * from big_table where temporary='N';

已用时间:  00: 00: 17.64

执行计划
----------------------------------------------------------
Plan hash value: 3993303771

-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |   499K|    49M|  4070   (1)| 00:00:49 |
|*  1 |  TABLE ACCESS FULL| BIG_TABLE |   499K|    49M|  4070   (1)| 00:00:49 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("TEMPORARY"='N')

统计信息
----------------------------------------------------------
0  recursive calls
0  db block gets
15091  consistent gets
0  physical reads
0  redo size
154484  bytes sent via SQL*Net to client
2208  bytes received via SQL*Net from client
165  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
2452  rows processed


关于cost和执行时间,asktom

本文参考《Oracle_Database_9i10g11g编程艺术深入数据库体系结构》第2版,Thomas Kyte著,苏金国 王小振等译,422~424页
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: