Oracle - 索引使用可选参数
Oracle - Index use with optional parameters
我使用以下技巧来索引包含一些空值的列:
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
提供值,这也会导致完整的 table 扫描。在这种情况下使用索引有技巧吗?
因为我只有 2 次搜索,所以按身份证号码和姓名搜索,这是非常可取的。
NVL
技巧应该起作用并允许索引访问。事实上,NVL
通常是执行此操作的最佳方法,并且通常比涉及 CASE
或 OR
的其他条件更有效。我已经多次使用 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 在 运行 时间决定在绑定变量为空时使用完整 table 扫描(并返回所有行),并在绑定变量不为空时使用索引(并且仅返回几行)。
这一切都意味着在您的具体情况下可能发生了一些奇怪的事情。您可能需要 post 一个完全可重现的测试用例,以便我们找出未使用索引的原因。
我使用以下技巧来索引包含一些空值的列:
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
提供值,这也会导致完整的 table 扫描。在这种情况下使用索引有技巧吗?
因为我只有 2 次搜索,所以按身份证号码和姓名搜索,这是非常可取的。
NVL
技巧应该起作用并允许索引访问。事实上,NVL
通常是执行此操作的最佳方法,并且通常比涉及 CASE
或 OR
的其他条件更有效。我已经多次使用 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 在 运行 时间决定在绑定变量为空时使用完整 table 扫描(并返回所有行),并在绑定变量不为空时使用索引(并且仅返回几行)。
这一切都意味着在您的具体情况下可能发生了一些奇怪的事情。您可能需要 post 一个完全可重现的测试用例,以便我们找出未使用索引的原因。