16.12.2012 Views

Oracle Magazine - September/October 2007 - Marcelo Machado

Oracle Magazine - September/October 2007 - Marcelo Machado

Oracle Magazine - September/October 2007 - Marcelo Machado

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!