Empty RELIES_ON for RESULT_CACHE
I have a query inside the function with RESULT_CACHE.
So when the table is changed - my cache is invalidated and function is executed again.
What I want is to impleme开发者_JAVA百科nt the function that depends only on input parameters, and doesn't depend on any implicit dependencies (like tables, etc).
Is it possible (without dynamic sql)?
a function that depends only on its parameters can be declared DETERMINISTIC. The results of this function will be cached in some cases. This thread on the OTN forums shows how deterministic function results get cached inside SQL statements.
As of 10gR2, the function results don't get cached across SQL statements nor do they get cached in PL/SQL. Still, this cache feature can be useful if you call a function in a SELECT where it might get called lots of time.
I don't have a 11gR2 instance available right now, so I can't test the RESULT_CACHE feature, but have you considered delaring your function relying on a fixed dummy table (a table that never gets updated for instance)?
The correct answer is NO. A solution in cases where things like result caches and materialized views won't work because of invalidations or too much overhead is the Oracle In-Memory Database Cache option. See result caches ..... what about heavily modified data It's a real smart option, not cheap.
If you use a database link it is possible to create a function result cache that will read from a table when a parameter changes but will not be invalidated when the table changes.
Obviously there are some issues with this approach; performance (even for a self-link), maintenance, the function may return the wrong result, everybody hates database links, etc.
Note that RELIES_ON is deprecated in 11gR2. Dependencies are automatically determined at run-time, even dynamic SQL wouldn't help you here. But apparently this dependency tracking doesn't work over database links.
The script below demonstrates how this works. Remove "@myself" from the function to see how it normally works. Some of the code is based on this great article.
--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;
Two options:
Don't query any table.
Implement your own cache - wrap the function in a package, and store the query results in a PL/SQL table in memory. The downside to this approach, however, is that the cache only works within a single session. Each session will maintain its own cache.
精彩评论