如果将UPPER应用于数字列,Oracle是否能够找出索引

use*_*850 0 oracle case-insensitive

如果我们在表"person"中有一个列"dob"(varchar2),
我们可以为它创建一个索引,即"在person(dob)上创建索引"

如果我们执行诸如upper(dob)之类的查询,它将忽略现有索引,以便
我们需要创建一个索引,例如"在person上创建索引(upper(dob))"

但是,如果dob列的类型为数字怎么办?
当查询为upper(dob)或更低(dob)或对数字列没有影响的任何操作时,Oracle能够聪明地使用为person(dob)创建的索引吗?
我知道如果它们没有意义,就不应该将这些函数应用于列.但是我们假设你有一个列作为varchar2但是意识到它可以是一个只有数字的列并且没有改变使用这个列的代码.
或者,如果您有一些通用逻辑来应用不区分大小写而不检查列类型.
或者像Dave在评论中所说的那样,如果你有现有的查询使用无法更改的UPPER(dob).
我知道至少对于11gr2以下的版本目前还没有

Ben*_*Ben 5

TL;博士

答案是不.


这是一个相对奇怪的问题.我不确定你为什么要大写一个数字.我不确定你为什么要在NUMBER专栏中存储出生日期!

如果可以,将出生日期存储为DATE,并且不应用任何字符串操作功能.

但是,让我们试试吧.我把它简单地设置为:

create table person as
 select level as a
   from dual
connect by level <= 50000;

create index i_person on person (a);
Run Code Online (Sandbox Code Playgroud)

然后,在11.2.0.3数据库上你会注意到数字上限会产生很大的差异.一个简单的SELECT 完全符合我们的预期,并执行索引范围扫描(因为索引不是唯一的).

explain plan for
 select * from person where a = 10045;

Explained.

select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 125785894

-----------------------------------------------------------------------------
| Id  | Operation        | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT |          |     1 |    13 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| I_PERSON |     1 |    13 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - access("A"=10045)

Note
-----
   - dynamic sampling used for this statement (level=2)

17 rows selected.
Run Code Online (Sandbox Code Playgroud)

但是,将该UPPER()函数应用于查询时,过滤器会完全更改并执行表的完整扫描.这是因为您通过未编制索引的过滤器访问该表; 您需要索引UPPER(A)(在这种情况下),以便您的查询使用索引.请注意,Oracle隐式将其转换为字符,然后再转换为数字,即使这样,索引也未被使用.

explain plan for
 select * from person where upper(a) = 10045;

Explained.

select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1493655343

----------------------------------------------------------------------------
| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |        |     1 |    13 |    19   (6)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| PERSON |     1 |    13 |    19   (6)| 00:00:01 |
----------------------------------------------------------------------------

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

   1 - filter(TO_NUMBER(UPPER(TO_CHAR("A")))=10045)

Note
-----
   - dynamic sampling used for this statement (level=2)
Run Code Online (Sandbox Code Playgroud)

如果你不能改变查询(你真的应该没有点上层数字列)并且你当前的查询太慢(因为他们没有使用索引)那么你将不得不添加一个函数索引UPPER(DOB).我只会更改查询...


从理论上讲,12c的自适应执行计划可以纠正这个中途飞行,但我还没有设法通过以下代码实现它,并在V$SQL_PLAN执行时查看计划:

declare
  l_a number;
begin
  for xx in 1 .. 1000 loop
    select a into l_a from person where upper(a) = xx;
  end loop;
end;
/
Run Code Online (Sandbox Code Playgroud)

使用更大的表,因此Oracle有更多时间采取行动可能会发生变化.但是,如果可能的话,最好只使用普通索引.