Oracle - 使用可选参数的索引

时间:2018-03-18 18:41:10

标签: oracle database-indexes

我使用以下技巧来索引具有一些空值的列:

create index xx_people_idx1 on xx_people(id_number, -1);

这很有效。遗憾的是,当您使用可选参数时,这无济于事:

select *
from xx_people
where id_number = nvl(:p_id_number, id_number); 

即使您为p_id_number提供值,也会导致全表扫描。在这种情况下使用索引有诀窍吗?

由于通过身份证号码和姓名搜索是我唯一的2次搜索,因此这是非常可取的。

1 个答案:

答案 0 :(得分:1)

NVL技巧应该起作用并允许索引访问。事实上,NVL通常是执行此操作的最佳方式,通常比涉及CASEOR的其他条件更有效。我多次使用NVL技巧,下面的简单测试用例表明它可以使用索引。

模式

create table xx_people(id_number number, a number, b number);

insert into xx_people
select level, level, level from dual connect by level <= 100000;

commit;

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

create index xx_people_idx1 on xx_people(id_number, -1);

生成执行计划

explain plan for
select *
from xx_people
where id_number = nvl(:p_id_number, id_number);

select * from table(dbms_xplan.display);

执行计划

Plan hash value: 3301250992

----------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                 |   100K|  3808K|   106   (1)| 00:00:01 |
|   1 |  VIEW                                  | VW_ORE_67373E14 |   100K|  3808K|   106   (1)| 00:00:01 |
|   2 |   UNION-ALL                            |                 |       |       |            |          |
|*  3 |    FILTER                              |                 |       |       |            |          |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| XX_PEOPLE       |     1 |    15 |     3   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN                  | XX_PEOPLE_IDX1  |     1 |       |     2   (0)| 00:00:01 |
|*  6 |    FILTER                              |                 |       |       |            |          |
|*  7 |     TABLE ACCESS FULL                  | XX_PEOPLE       |   100K|  1464K|   103   (1)| 00:00:01 |
----------------------------------------------------------------------------------------------------------

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

   3 - filter(:P_ID_NUMBER IS NOT NULL)
   5 - access("ID_NUMBER"=:P_ID_NUMBER)
   6 - filter(:P_ID_NUMBER IS NULL)
   7 - filter("ID_NUMBER" IS NOT NULL)

这个计划起初有点令人困惑。但它是两全其美的;过滤器操作允许Oracle在运行时决定在绑定变量为null(并返回所有行)时使用全表扫描,并在绑定变量不为null时返回索引(并且只返回几行)。

这一切都意味着在你的具体情况下可能会发生一些奇怪的事情。您可能需要发布一个完全可重现的测试用例,以便找出未使用索引的原因。