Oracle 不使用索引访问时基于函数的索引

Oracle 不使用索引访问时基于函数的索引,oracle,indexing,user-defined-functions,Oracle,Indexing,User Defined Functions,我第一次将基于函数的索引与用户定义的函数一起使用,当索引无法使用时,我偶然发现了一个性能问题 在内部,基于函数的索引似乎会生成一个隐藏的表列(类型为varchar2(4000),因为我的函数返回varchar2),并对其进行索引。当使用索引时,这很好,但有时我们必须使用函数作为过滤器进行完整的表扫描,在这种情况下,我看到性能下降了6倍。在这种情况下,Oracle似乎不使用隐藏列,而是为每一行重新计算函数,使查询绑定CPU而不是IO 有没有办法让Oracle也将该隐藏列用于筛选?我想知道我是否错过

我第一次将基于函数的索引与用户定义的函数一起使用,当索引无法使用时,我偶然发现了一个性能问题

在内部,基于函数的索引似乎会生成一个隐藏的表列(类型为varchar2(4000),因为我的函数返回varchar2),并对其进行索引。当使用索引时,这很好,但有时我们必须使用函数作为过滤器进行完整的表扫描,在这种情况下,我看到性能下降了6倍。在这种情况下,Oracle似乎不使用隐藏列,而是为每一行重新计算函数,使查询绑定CPU而不是IO

有没有办法让Oracle也将该隐藏列用于筛选?我想知道我是否错过了一些重写选项或类似的东西


如果没有,我必须自己定义列,并使用触发器使其保持最新。我更喜欢使用基于函数的索引,以提高透明度和便于维护。

您使用的是哪一版本的Oracle?如果是11g,你应该试试。这是一个列,其值派生自表达式或文字。它们被定义为表的一部分,因此它们在表DESC中具有可见性(不同于基于函数的索引)。我们可以在虚拟列上建立索引。它们是自动维护的,不需要触发器

因此,可以使用与基于函数的索引相同的表达式向表中添加虚拟列。也许是这样的:

create table t23
   (id number
    , col_a varchar2(10)
    , vcol_a as (upper(substr(col_a, 1, 1)))
  )
/
请注意,我们无法插入或更新虚拟列。因此,您需要指定insert语句的投影:

insert into t23 (id, col_a) values (1, 'this is a test');
然后可以在虚拟列上构建常规索引:

create index t23_vc_i on t23(vcol_a)
/

不要忘记删除基于函数的索引

在表扫描中没有使用基于函数的索引的通用方法

我在问题中所做的假设,即“在内部,基于函数的索引似乎会生成一个隐藏的表列…”是完全错误的:函数的结果不存储在表列中,而只存储在索引中

因此,除非在执行扫描时有方法访问索引(我能想到的唯一方法是如果它是以键列开始的组合索引),否则无法使用预计算的函数结果

11g的“虚拟列”功能也没有帮助,因为该列不存储在表中,而是动态计算的,类似于在视图中使用该函数

总之:如果您不能排除表扫描,并且您的函数调用很昂贵(很慢),请将一个实列与“插入或更新前”触发器结合使用。基于函数的索引不行


(注意:添加此答案是因为我不想让这个问题成为未回答的问题。答案的功劳属于,他指出该列从未具体化)。

看起来很棘手,因为“隐藏列”不存储在表本身中,它是索引的组成部分。因此,在表扫描期间,它不可用。你必须让Oracle通过索引(或者至少以某种方式加入索引)来获取预先计算的数据。如果列实际上不在那里,很明显,我不能两全其美。我只是看着dba_tab_cols,认为有一个真实的、隐藏的列。如果它只是一个用于构建索引的虚拟构造,这就回答了我的问题-需要一个用于扫描的真实列。好吧,它是真实的,因为它以物化形式存在于索引中。与虚拟列相反,虚拟列只是一个动态计算数据的定义,与视图中的派生列相同。是的,我意识到了这一点,这就是为什么我在那里看到SYS_NC….$列时非常惊讶-我错误地认为它在表中。那我就按列+触发器的方式。谢谢不幸的是,我们的大多数客户仍然使用10g。但还是要感谢你让我注意到这个功能。我喜欢为这个列指定一个类型,因为我发现基于函数的索引隐式使用的varchar2(4000)有点问题(而且我认为substr的解决方法太脆弱了)。然而,虚拟列并没有具体化。因此,它也是动态计算的,CPU瓶颈不会得到缓解。在其上构建索引相当于现在使用的函数索引。