3

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

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

有没有办法让 Oracle 也使用该隐藏列进行过滤?我想知道我是否遗漏了一些重写选项或类似的东西。

如果没有,我将不得不自己定义该列并使用触发器使其保持最新。我更喜欢使用基于函数的索引来提高透明度和更容易维护。

4

2 回答 2

3

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

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

create table t23
   (id number
    , col_a varchar2(10)
    , vcol_a as (upper(substr(col_a, 1, 1)))
  )
/

请注意,我们不能插入或更新虚拟列。所以需要指定插入语句的投影:

insert into t23 (id, col_a) values (1, 'this is a test');

然后你可以在虚拟列上建立一个常规索引:

create index t23_vc_i on t23(vcol_a)
/

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

于 2012-07-24T10:55:36.703 回答
0

在表扫描中无法使用基于函数的索引。

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

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

11g 的“虚拟列”特性也无济于事,因为列不存储在表中,而是动态计算,类似于在视图中使用函数。

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

(注意:添加此答案是因为我不想让这个问题悬而未决。答案的功劳属于thilo,他指出该列从未实现)。

于 2012-07-25T13:53:01.113 回答