我第一次使用基于函数的索引和用户定义的函数,并且在索引无法使用时遇到了性能问题。
在内部,基于函数的索引似乎生成一个隐藏的表列(类型为varchar2(4000),因为我的函数返回一个varchar2),并对其进行索引。使用索引时工作正常,但有时我们必须使用函数作为过滤器进行全表扫描,在这种情况下,我看到性能下降了6倍。在这种情况下,Oracle不使用隐藏列,但重新计算每一行的函数,使查询受CPU限制而不是IO绑定。
有没有办法让Oracle使用该隐藏列进行过滤?我想知道我是否错过了一些重写选项或类似的东西。
如果没有,我必须自己定义列并使用触发器使其保持最新。我更倾向于使用基于函数的索引来实现透明度和更简单的维护。
答案 0 :(得分: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)
/
不要忘记删除基于函数的索引!
答案 1 :(得分:0)
没有通用的方法可以在表扫描中使用基于函数的索引。
我在我的问题中做出的假设,即“在内部,基于函数的索引似乎生成一个隐藏的表列......”,是完全错误的:函数的结果是< em> not 存储在表列中,但仅存储在索引中。
因此,除非在执行扫描时有一种方法可以访问索引(我能想到的唯一方法是它是一个以键列开头的组合索引),预计算的函数结果不能是使用
11g“虚拟列”功能也没有帮助,因为列没有存储在表中,而是在运行中计算,类似于在视图中使用该功能。
总结:如果您不能排除表扫描,并且您的函数调用很昂贵(慢),请使用实列与“插入或更新前”触发器结合使用。基于函数的索引不会这样做。
(注意:添加了这个答案,因为我不想让这个问题没有答案。答案归功于thilo,他指出该列永远不会实现。)