下面的脚本显示了我用来对 ADDRESSES 索引进行索引范围扫描的步骤。在查看细节之前,您可能只想运行整个程序。如果您没有对最后两个查询进行两次索引范围扫描,那么这可能是我们的版本、设置等方面的差异。我使用的是 10.2.0.1.0。
如果您确实看到了想要的计划,那么您可能希望逐步修改我的脚本以使其更准确地反映真实数据,并尝试找到使其崩溃的确切更改。希望我的设置至少接近真实情况,并且不会遗漏任何与您的确切问题无关的细节。
这是一个奇怪的问题,我不明白这里发生的一切。例如,我不知道为什么 use_nl 有效,但索引提示无效。
(请注意,我的执行时间是基于重复执行的。第一次运行时,一些查询可能会更慢,因为数据没有被缓存。)
--create tables
create table customers (id number, surname varchar2(100), other varchar2(100));
create table addresses (cust_id number, other varchar2(100));
--create data and indexes
insert into customers select level, 'ASDF'||level, level from dual connect by level <= 1000000;
insert into addresses select level, level from dual connect by level <= 1000000;
create index customers_id on customers(id);
create index addresses_cust_id on addresses(cust_id);
create index customers_special_char_filter on customers(special_char_filter(surname));
--create function
create or replace function special_char_filter(surname in varchar) return varchar2 deterministic is
begin
return replace(surname, 'bad value!', null);
end;
/
--gather stats
begin
dbms_stats.gather_table_stats(ownname => user, tabname => 'CUSTOMERS', cascade => true);
dbms_stats.gather_table_stats(ownname => user, tabname => 'ADDRESSES', cascade => true);
end;
/
set autotrace on;
--Index range scan on CUSTOMERS_SPECIAL_CHAR_FILTER, but full table scan on ADDRESSES
--(0.2 seconds)
SELECT *
FROM customers cust
JOIN addresses addr ON addr.cust_id = cust.id
WHERE special_char_filter(cust.surname) like special_char_filter('ASDF100000bad value!%');
--This uses the addresses index but it does an index full scan. Not really what we want.
--I'm not sure why I can't get an index range scan here.
--Various other index hints also failed here. For example, no_index_ffs won't stop an index full scan.
--(1 second)
SELECT /*+ index(addr addresses_cust_id) */ *
FROM customers cust
JOIN addresses addr ON addr.cust_id = cust.id
WHERE special_char_filter(cust.surname) like special_char_filter('ASDF100000bad value!%');
--Success! With this hint both indexes are used and it's super-fast.
--(0.02 seconds)
SELECT /*+ use_nl(cust addr) */ *
FROM customers cust
JOIN addresses addr ON addr.cust_id = cust.id
WHERE special_char_filter(cust.surname) like special_char_filter('ASDF100000bad value!%');
--But forcing the index won't always be a good idea, for example when the value starts with '%'.
--(1.2 seconds)
SELECT /*+ use_nl(cust addr) */ *
FROM customers cust
JOIN addresses addr ON addr.cust_id = cust.id
WHERE special_char_filter(cust.surname) like special_char_filter('%ASDF100000bad value!%');