在Oracle中索引部分值

时间:2018-01-10 17:29:55

标签: oracle query-tuning

我正在创建一个复合索引。

create index I on TEST (A,B);

我的查询就像

select * from TEST where A=:1 and B IS NOT NULL

上面的查询只会返回几行(< 10),但是我的主要颜色" A"并不是很独特,可以为一个值返回50万条记录。

现在,如果我运行上面的查询,它会执行太多逻辑读取,因为这将扫描所有具有值A =:1的块。

是否有任何技巧/解决方法来索引部分数据。例如,只有数据将进入B IS NOT NULL的索引。这将使我的索引非常紧凑和quiker。

2 个答案:

答案 0 :(得分:1)

  

是否有任何技巧/解决方法来索引部分数据。例如,只有数据将进入B IS NOT NULL的索引。

你可以这样做with a function-based index。在他们的条件唯一性示例中,它说:

  

Oracle数据库不会在索引中存储所有键都为NULL的行。

你不是做同样的事情,但可以使用相同的机制:

create index J on TEST (case when B is not null then A end, B);

假设您的表格与您显示的一样简单,并且对于相同的A值有100000行,其中99998个B设置为null,另外两个具有非空值。

create table test (a number not null, b number);
insert into test values (1, 1);
insert into test values (1, 2);
insert into test select 1, null from dual connect by level < 99999;

并创建原始索引和基于函数的索引:

create index I on TEST (A,B);
create index J on TEST (case when B is not null then A end, B);

然后,如果你收集统计数据,你可以看到每个索引中的行数:

select index_name, num_rows from user_indexes where index_name in ('I','J');

INDEX_NAME                       NUM_ROWS
------------------------------ ----------
I                                 1000000
J                                       2

根据执行计划,您的查询使用较小的索引:

var v1 number;
exec :v1 := 1;

set autotrace on
select * from test where a = :v1 and b is not null;

         A          B
---------- ----------
         1          1
         1          2

Explain Plan
-----------------------------------------------------------

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------
Plan hash value: 3591688522

------------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     2 |    10 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS BY INDEX ROWID| TEST |     2 |    10 |     2   (0)| 00:00:01 |
|*  2 |   INDEX FULL SCAN           | J    |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------

   1 - filter("A"=:V1)
   2 - filter("B" IS NOT NULL)

Statistics
-----------------------------------------------------------
               4  Requests to/from client
               4  consistent gets
...

如果我删除J索引并重复查询,则执行全表扫描并获得1610一致性;您可能会看到I索引范围扫描或快速全扫描,具体取决于选择性 - 因为我只有一个A值,这与您的方案完全匹配。

答案 1 :(得分:0)

如果您可以使用它,位图索引将对您有所帮助,因为位图索引也会存储 NULL 值。

但请注意,如果表上存在事务负载,您不想使用位图索引,尤其是并发事务。

我的测试用例

create table tt as
select 1 a, 1 b from dual union all
select 1 a, null b from dual connect by level <= 1000000;

create index tt_idx on tt(a,b);

select /*+ INDEX(tt) */ * from tt where a = 1 and b is not NULL;

执行

   1105  consistent gets

create bitmap index tt_idx on tt(a,b);

执行

     83  consistent gets

或者您可以使用基于函数的索引,但您必须重写查询。

create   index tt_idx on tt(a,case when b is not null then 1 end);

必须重新制定查询,以便可以使用索引

select /*+ INDEX(tt) */ * from tt where a = 1 and case when b is not null then 1 end = 1;

执行

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

   2 - access("A"=1 AND CASE  WHEN "B" IS NOT NULL THEN 1 END =1)

     5  consistent gets