2012-07-24 92 views
3

我第一次使用基于函数的索引和用户定义的函数,并且在无法使用索引时偶然发现了性能问题。未使用索引访问时基于函数的索引

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

有没有办法让Oracle使用该隐藏列也进行过滤?我想知道我是否错过了一些重写选项或者这些内容。

如果不是,我必须自己定义列并使用触发器来保持它最新。我更喜欢使用基于函数的索引进行透明度和更容易维护。

+1

似乎很棘手,因为“隐藏列”不存储在表本身,它是什么组成索引。所以它在表扫描过程中不可用。您必须让Oracle通过索引(或者至少以某种方式加入)来获取预先计算的数据。 – Thilo 2012-07-24 09:39:59

+0

如果这个专栏实际上并不存在,很明显我无法获得两全其美的效果。我只是看着dba_tab_cols,并认为有一个_real_,只是隐藏的列。如果它只是构建索引的虚拟构造,那就回答我的问题 - 需要一个真正的扫描列。 – Chris 2012-07-24 09:54:48

+0

嗯,它是真实的,它存在于索引*内的物化形式*中。与虚拟列相比,虚拟列只是一个定义,其数据即时计算,与视图中的派生列相同。 – Thilo 2012-07-24 09:57:50

回答

0

在表扫描中,没有一种通用的方法可以使用基于函数的索引。

我在我的问题的假设,即“在内部,基于函数的索引似乎生成一个隐藏的表列...”,是完全错误的:该函数的结果是未存储在一个表列,但只在索引中。

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

11g“虚拟列”功能也没有帮助,因为该列未存储在表中,但是可以即时计算,类似于在视图中使用该函数。总之:如果你不能排除表扫描,并且你的函数调用是很昂贵的(慢),那么使用一个真正的列与“插入或更新之前”触发器的组合。基于函数的索引不会。

(注意:添加了这个答案,因为我不想让这个问题没有答案。答案的积分属于thilo,他指出该列从未实现)。

3

您正在使用哪个版本的Oracle?如果它是11克,你应该尝试using a virtual column。这是一个其值从表达式或文字派生的列。它们被定义为表格的一部分,因此它们在表格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) 
/

不要忘了删除您基于功能的索引!

+0

不幸的是,我们的大部分客户仍然使用10g。但是还是要感谢这个功能引起我的注意。我喜欢我可以为此列指定一个类型,因为我发现基于函数的索引隐式使用的varchar2(4000)有点问题(我认为substr-workaround太片面)。 – Chris 2012-07-24 11:51:58

+0

但是,虚拟列未实现。所以它也是即时计算的,并且CPU瓶颈不会被减轻。在其上建立索引等同于现在已经存在的函数索引。 – Thilo 2012-07-25 00:56:27