Oracle Magazine - September/October 2007 - Marcelo Machado
Oracle Magazine - September/October 2007 - Marcelo Machado
Oracle Magazine - September/October 2007 - Marcelo Machado
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
ASK TOM<br />
4 begin<br />
5 for x in<br />
6 (select /*+ result_cache */<br />
7 owner,<br />
8 object_type,<br />
9 count(*) cnt<br />
10 from t<br />
11 group by owner, object_type<br />
12 order by owner, object_type )<br />
13 loop<br />
14 -- do_something<br />
15 null;<br />
16 end loop;<br />
17 end;<br />
18 /<br />
Procedure created.<br />
SQL> set timing on<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.10<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.00<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.01<br />
SQL> set timing off<br />
Note how the first execution took<br />
about 0.1 second, because the answer<br />
was assembled for the first execution,<br />
but that the subsequent executions were<br />
blindingly fast—sometimes so fast that<br />
they appear instantaneous.<br />
The nice thing about this is that the<br />
cache is invalidated and refreshed by<br />
the database—and the process is completely<br />
transparent to the application.<br />
The application need not worry about<br />
“stale” or invalid results. For example,<br />
if I update a single row, thus changing<br />
the results—<br />
SQL> update t<br />
2 set owner = lower(owner)<br />
3 where rownum = 1;<br />
1 row updated.<br />
SQL> commit;<br />
Commit complete.<br />
—I observe the following behavior:<br />
SQL> set timing on<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.10<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.00<br />
SQL> exec my_function<br />
PL/SQL procedure successfully completed.<br />
Elapsed: 00:00:00.01<br />
SQL> set timing off<br />
Note that the first execution after<br />
the UPDATE went back up to about 0.1<br />
second of execution time because it had<br />
to build the new answer. The subsequent<br />
executions benefit from this work<br />
and appear instantaneous.<br />
I’m sure that if you sit back and<br />
ponder your own applications, you<br />
will be able to think of more than one<br />
place where the server results cache<br />
feature will come in handy. It provides<br />
many of the benefits of some materialized<br />
views, but without the setup and<br />
administrative overhead associated<br />
with them.<br />
BUT WAIT—THERE’S MORE. . . .<br />
As they say on late night television,<br />
“If you thought that was really good,<br />
wait till you see this!” <strong>Oracle</strong> Database<br />
11g has a new PL/SQL function results<br />
cache as well. Whereas the aforementioned<br />
server results cache is about<br />
caching SQL result sets, this extension<br />
of the server results cache feature caches<br />
the results of PL/SQL function and procedure<br />
calls.<br />
In the past, if you called a PL/SQL<br />
function 1,000 times and each function<br />
call consumed 1 second, the 1,000<br />
calls would take 1,000 seconds. With<br />
this new function results cache feature,<br />
depending on the inputs to the function<br />
and whether the data underlying<br />
the function changes, 1,000 function<br />
calls could take about 1 second, total. A<br />
small example will be useful: I’ll create<br />
86 SEPTEMBER/OCTOBER <strong>2007</strong> ORACLE.COM/ORACLEMAGAZINE<br />
two functions, identical except in name<br />
and compiler parameter options. They<br />
will both access the previously created<br />
table T:<br />
SQL> create or replace<br />
2 function not_cached<br />
3 ( p_owner in varchar2 )<br />
4 return number<br />
5 as<br />
6 l_cnt number;<br />
7 begin<br />
8 select count(*)<br />
9 into l_cnt<br />
10 from t<br />
11 where owner = p_owner;<br />
12 dbms_lock.sleep(1);<br />
13 return l_cnt;<br />
14 end;<br />
15 /<br />
Function created.<br />
SQL> create or replace<br />
2 function cached<br />
3 ( p_owner in varchar2 )<br />
4 return number<br />
5 result_cache<br />
6 relies_on(T)<br />
7 as<br />
8 l_cnt number;<br />
9 begin<br />
10 select count(*)<br />
11 into l_cnt<br />
12 from t<br />
13 where owner = p_owner;<br />
14 dbms_lock.sleep(1);<br />
15 return l_cnt;<br />
16 end;<br />
17 /<br />
Function created.<br />
The only difference in the functions<br />
besides the names are the compiler<br />
parameters RESULT_CACHE and<br />
RELIES_ON. The RESULT_CACHE<br />
directive tells <strong>Oracle</strong> Database that you<br />
would like the answers from this function<br />
to be saved, so that if someone<br />
invokes this function again with the<br />
same inputs, the code should not<br />
actually be executed but, rather, that<br />
person should just receive the already<br />
known answer. The RELIES_ON clause<br />
tells the database when to invalidate<br />
this function result cache value—in