Oracle - Index use with optional parameters

情到浓时终转凉″ 提交于 2021-02-08 04:03:43

问题


I use the following trick to be able to index a column that has some nulls:

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

This works great. Sadly this does not help for when you use optional parameters though:

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

This results in a full table scan, even when you provide a value for p_id_number. Is there a trick to use the index in this situation?

Since searching by id number and name are my only 2 searches this is very desirable.


回答1:


The NVL trick ought to work and allow index access. In fact, NVL is generally the best way to do this, and usually works better than other conditions involving CASE or OR. I've used the NVL trick many times and the simple test case below shows that it can use an index.

Schema

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);

Generate Execution Plan

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

select * from table(dbms_xplan.display);

Execution Plan

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)

That plan is a bit confusing at first. But it's got the best of both worlds; the filter operation allows Oracle to decide at run time to use a full table scan when the bind variable is null (and all rows are returned), and an index when the bind variable is not null (and only a few rows are returned).

This all means that there is probably something weird happening in your specific case. You may need to post a fully reproducible test case for us to figure out why an index isn't used.



来源:https://stackoverflow.com/questions/49351568/oracle-index-use-with-optional-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!