是否有任何技巧/解决方法来索引部分数据。例如,只有该数据将进入 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 值,这与您的场景不太匹配。