5

我在函数内部有一个带有 RESULT_CACHE 的查询。

因此,当表更改时-我的缓存无效并再次执行函数。

我想要的是实现依赖于输入参数的函数,而不依赖于任何隐式依赖项(如表等)。

有可能(没有动态sql)吗?

4

4 回答 4

2

正确答案是否定的。在结果缓存和物化视图等由于失效或开销过多而无法工作的情况下,Oracle 内存中数据库缓存选项是一种解决方案。查看结果缓存 ..... 大量修改的数据呢?这是一个真正的明智选择,并不便宜。

于 2011-04-29T06:17:25.853 回答
2

仅依赖于其参数的函数可以声明为 DETERMINISTIC。此函数的结果在某些情况下会被缓存。OTN 论坛上的这个主题展示了确定性函数结果如何缓存在 SQL 语句中。

从 10gR2 开始,函数结果不会跨 SQL 语句缓存,也不会缓存在 PL/SQL 中。尽管如此,如果您在 SELECT 中调用一个函数,该函数可能会被调用很多时间,则此缓存功能仍然很有用。

我现在没有可用的 11gR2 实例,所以我无法测试 RESULT_CACHE 功能,但是您是否考虑过依赖固定的虚拟表(例如永远不会更新的表)来取消您的函数?

于 2011-04-29T08:18:50.000 回答
2

如果您使用数据库链接,则可以创建一个函数结果缓存,该缓存将在参数更改时从表中读取,但在表更改时不会失效。

显然,这种方法存在一些问题;性能(即使是自链接)、维护、函数可能返回错误的结果、每个人都讨厌数据库链接等。

请注意,RELIES_ON 在 11gR2 中已弃用。依赖关系是在运行时自动确定的,即使是动态 SQL 在这里也无济于事。但显然这种依赖跟踪不适用于数据库链接。

下面的脚本演示了它是如何工作的。从函数中删除“@myself”以查看其正常工作方式。部分代码基于这篇很棒的文章

--For testing, create a package that will hold a counter.
create or replace package counter is
    procedure reset;
    procedure increment;
    function get_counter return number;
end;
/

create or replace package body counter as
    v_counter number := 0;
    procedure reset is begin v_counter := 0; end;
    procedure increment is begin v_counter := v_counter + 1; end;
    function get_counter return number is begin return v_counter; end;
end;
/

--Create database link
create database link myself connect to <username> identified by "<password>"
using '<connect string>';

drop table test purge;
create table test(a number primary key, b varchar2(100));
insert into test values(1, 'old value1');
insert into test values(2, 'old value2');
commit;

--Cached function that references a table and keeps track of the number of executions.
drop function test_cache;
create or replace function test_cache(p_a number) return varchar2 result_cache is
    v_result varchar2(100);
begin
    counter.increment;
    select b into v_result from test@myself where a = p_a;
    return v_result;
end;
/

--Reset
begin
    counter.reset;
end;
/

--Start with 0 calls
select counter.get_counter from dual;

--First result is "value 1", is only called once no matter how many times it runs.
select test_cache(1) from dual;
select test_cache(1) from dual;
select test_cache(1) from dual;
select counter.get_counter from dual;

--Call for another parameter, counter only increments by 1.
select test_cache(2) from dual;
select test_cache(2) from dual;
select test_cache(2) from dual;
select counter.get_counter from dual;

--Now change the table.  This normally would invalidate the cache.
update test set b = 'new value1' where a = 1;
update test set b = 'new value2' where a = 2;
commit;

--Table was changed, but old values are still used.  Counter was not incremented.
select test_cache(1) from dual;
select test_cache(2) from dual;
select counter.get_counter from dual;

--The function is not dependent on the table.
SELECT ro.id           AS result_cache_id
,      ro.name         AS result_name
,      do.object_name
FROM   v$result_cache_objects    ro
,      v$result_cache_dependency rd
,      dba_objects               do
WHERE  ro.id = rd.result_id
AND    rd.object_no = do.object_id;
于 2011-06-01T06:26:35.073 回答
1

两种选择:

  1. 不要查询任何表。

  2. 实现自己的缓存 - 将函数包装在一个包中,并将查询结果存储在内存中的 PL/SQL 表中。然而,这种方法的缺点是缓存只能在单个会话中工作。每个会话都将维护自己的缓存。

于 2011-04-29T02:54:33.250 回答