1

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

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 的索引。这将使我的索引非常紧凑和快速。

4

2 回答 2

1

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

您可以使用基于函数的 index来做到这一点。在他们的条件唯一性示例中,它说:

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

您没有做完全相同的事情,但可以使用相同的机制:

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

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

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 值,这与您的场景不太匹配。

于 2018-01-10T18:12:40.413 回答
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
于 2018-01-10T18:07:41.170 回答