Sei sulla pagina 1di 6

5/4/2014

Ask Tom: On Oracle Database 11g

Sign In/Register Help Country

Products
Oracle Technology Network
Oracle Magazine Online
2014
2013
2012
2011
2010
2009
2008
2007

Solutions

Oracle Magazine Issue Archive

Communities

Downloads
2007

I am a...

Store

I want to...

Support

Search

Training

Partners

About

OTN

September 2007

January 2007
March 2007
May 2007
July 2007
September 2007
November 2007
TECHNOLOGY: Ask Tom

As Published In

On Oracle Database 11g


By Tom Kyte

Our technologist takes a look at the server results and PL/SQL function caches.

September/October 2007

Over the next few months, I'll be looking at new features coming out with the latest release of Oracle Database, Oracle Database 11g. There
are so many to choose from that it was hard to pick a few to start with. So I've been talking with Bryn Llewellyn, the PL/SQL product manager,
and he filled me in on a couple of great new SQL and PL/SQL features.
Cache It
Oracle Database 11g raises the bar on data caching. In the past, Oracle Database cached database blocks. It could cache these blocks in
various places, such as the default pool, a keep buffer pool, or a recycle buffer pool. But it always cached blocks of datathe building blocks
used to build result sets.
Starting with Oracle Database 11g, the database can also now cache result sets! If you have a query that is executed over and over again
against slowly or never-changing data, you will find the new server results cache to be of great interest. This is a feature from which virtually
every application can and will benefit.
In a nutshell, the concept is this: when you run a query, Oracle Database 11g will first see if the results of that query have already been
computed and cached by some session or user, and if so, it will retrieve the answer from the server result cache instead of gathering all of the
database blocks and computing the answer all over again from scratch. The first execution of the query will take as long as usual (because it is
computing the result), but subsequent executions will appear to be instantaneous, because the answer is being returned immediately
instead of being computed and then returned.
I view this feature as being analogous to a just-in-time materialized view. In Oracle8i Database Release 1, Oracle introduced the concept of
materialized views, whereby DBAs could create a summary table, much as they might create an index, and the database optimizer would
recognize that this summary table existed and, when possible, use it to respond to queries rather than query and process the detailed
information stored in the base tables. This method of operation works well but is rather static, much like an indexing scheme. The Oracle
Database 11g server results cache, however, is in effect creating and maintaining materialized views on the fly, using a cache. The server
result cache (a just-in-time materialized view) is populated as needed without DBA intervention.
By way of example, I'll copy the ALL_OBJECTS table into a table:
SQL> create table t
2 as
3 select *
4
from all_objects;
Table created.
Now I'll create a small function that executes a reporta query against this table. To clearly demonstrate the difference in performance of this
query, I'll do nothing inside the loop (where I would normally be printing the rows and performing some computation):
SQL> create or replace procedure
2 my_function
3 as
4 begin
5
for x in
6
(select owner,
7
object_type,
8
count(*) cnt
9
from t
10
group by owner, object_type
11
order by owner, object_type )
12
loop
13
-- do_something
14
null;
15
end loop;
16 end;
17 /
Procedure created.
Now I execute this process three times, noting the time it takes to execute each invocation:
SQL> set timing on
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.54

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

1/6

5/4/2014

Ask Tom: On Oracle Database 11g


SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.10
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.11
SQL> set timing off
Note that the first execution took a significant amount of time, about 1.5 seconds, due to the physical I/O required to read the data from disk into
the conventional buffer cache. The second and third executions took much less time, about 0.1 second, because the blocks needed to answer
the query were found in the cache, not on disk. Compare what happens if I add a "cache the results" hint to the query:
SQL> create or replace procedure
2 my_function
3 as
4 begin
5
for x in
6
(select /*+ result_cache */
7
owner,
8
object_type,
9
count(*) cnt
10
from t
11
group by owner, object_type
12
order by owner, object_type )
13
loop
14
-- do_something
15
null;
16
end loop;
17 end;
18 /
Procedure created.
SQL> set timing on
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.10
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> set timing off
Note how the first execution took about 0.1 second, because the answer was assembled for the first execution, but that the subsequent
executions were blindingly fastsometimes so fast that they appear instantaneous.
The nice thing about this is that the cache is invalidated and refreshed by the databaseand the process is completely transparent to the
application. The application need not worry about "stale" or invalid results. For example, if I update a single row, thus changing the results-
SQL> update t
2 set owner = lower(owner)
3 where rownum = 1;
1 row updated.
SQL> commit;
Commit complete.
I observe the following behavior:
SQL> set timing on
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.10
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
SQL> exec my_function
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> set timing off
Note that the first execution after the UPDATE went back up to about 0.1 second of execution time because it had to build the new answer. The
subsequent executions benefit from this work and appear instantaneous.
I'm sure that if you sit back and ponder your own applications, you will be able to think of more than one place where the server results cache
feature will come in handy. It provides many of the benefits of some materialized views, but without the setup and administrative overhead
associated with them.

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

2/6

5/4/2014

Ask Tom: On Oracle Database 11g


But Wait, There's More. . . .
As they say on late night television, "If you thought that was really good, wait till you see this!" Oracle Database 11g has a new PL/SQL function
results cache as well. Whereas the aforementioned server results cache is about caching SQL result sets, this extension of the server results
cache feature caches the results of PL/SQL function and procedure calls.
In the past, if you called a PL/SQL function 1,000 times and each function call consumed 1 second, the 1,000 calls would take 1,000 seconds.
With this new function results cache feature, depending on the inputs to the function and whether the data underlying the function changes,
1,000 function calls could take about 1 second, total. A small example will be useful: I'll create two functions, identical except in name and
compiler parameter options. They will both access the previously created table T:
SQL> create or replace
2 function not_cached
3 ( p_owner in varchar2 )
4 return number
5 as
6
l_cnt number;
7 begin
8
select count(*)
9
into l_cnt
10
from t
11
where owner = p_owner;
12
dbms_lock.sleep(1);
13
return l_cnt;
14 end;
15 /
Function created.
SQL> create or replace
2 function cached
3 ( p_owner in varchar2 )
4 return number
5 result_cache
6 relies_on(T)
7 as
8
l_cnt number;
9 begin
10
select count(*)
11
into l_cnt
12
from t
13
where owner = p_owner;
14
dbms_lock.sleep(1);
15
return l_cnt;
16 end;
17 /
Function created.
The only difference in the functions besides the names are the compiler parameters RESULT_CACHE and RELIES_ON. The
RESULT_CACHE directive tells Oracle Database that you would like the answers from this function to be saved, so that if someone invokes
this function again with the same inputs, the code should not actually be executed but, rather, that person should just receive the already
known answer. The RELIES_ON clause tells the database when to invalidate this function result cache valuein this case, if the table T is
modified (which would change the answer from my cached function, hence I need it to be re-executed). Note that for effect, I've put a 1-second
sleep in both functions and that that sleep call will make it painfully obvious when the function is actually invoked and when the results are
reused.
I start by calling the conventional (not_cached) function three times with timing on:
SQL> exec dbms_output.put_line( not_cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.93
SQL> exec dbms_output.put_line( not_cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.29
SQL> exec dbms_output.put_line( not_cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.07
As you can see, each invocation takes at least one secondthe work done by the function plus the SQL it executes take a little more than a
second. Now I try out the cached version of this function:
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.09
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

3/6

5/4/2014

Ask Tom: On Oracle Database 11g

6
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01

As you can see, the first execution takes a little more than a second, just like the noncached versions, but the subsequent ones run at light
speed, simply because the cached function is, in fact, not being executed. If I modify the table this function relies on or change the inputs I use,
I can see that the database does the right thing:
SQL> update t set owner = initcap(owner) where rownum = 1;
1 row updated.
SQL> commit;
Commit complete.
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.25
This first execution of the cached function takes more than a second, because it must refresh the result cache, but subsequent executions
benefit from this cached result:
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
And just to show that the PL/SQL function results cache understands that different inputs result in different outputs, I can call the cached
function with a different username:
SQL> exec dbms_output.put_line( cached( 'SYS' ) );
29339
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.21
SQL> exec dbms_output.put_line( cached( 'SYS' ) );
29339
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
Note that the first execution takes a relatively long time (to build the result cache), a subsequent execution is fast, and calling the function with a
different username does not invalidate or throw away other cached results:
SQL> exec dbms_output.put_line( cached( 'SCOTT' ) );
6
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00
Function calls that use SCOTT as the input will remain fast until the cache must be invalidated or until it runs out of room and must make way
for other elements.
Again, this is a feature that can be implemented without massive rearchitecting of your applicationsin fact, without rearchitecting at all. The
compiler parameter RESULT_CACHE can activate this feature, and any client that invokes the function will benefit. For example, informal tests
with Oracle Application Express show an approximately 15 percent decrease in runtime in a standard benchmark harnessyour mileage may
vary, of course!
WHEN OTHERS Then Null Redux
In the July/August 2007 issue of Oracle Magazine, I wrote about a certain PL/SQL programming construct, the WHEN OTHERS exception
handler. I wrote
There's a programming construct, WHEN OTHERS, in PL/SQL that I wish weren't there.
Well, I knew Oracle could never really remove this language featurebut Oracle certainly has made it safer. Just to reiterate why I wish this
programming construct did not exist, I'll quote myself again:
But the problem I see is that far too many people use the WHEN OTHERS clause without following it by a RAISE or
RAISE_APPLICATION_ERROR call. That effectively hides the error. In reality, the error happened, but it was not handled in any meaningful way;
it was just ignoredsilently. The invoker of your code has no idea that something went horribly wrong and that your code failed, and he or she
typically thinks that everything was, in fact, successful.
Many timesmany more times than I can recall or count "quirky" behavior in a PL/SQL program unit is found to be caused by the improper
use of a WHEN OTHERS clauseone that is not followed by RAISE. The error was hidden, error handling was effectively disabled, and the
outcome is typically logically corrupt data (the application did not do its job) or the wrong answer.
Now, in Oracle Database 11g, I can easily find the offending bits of code. When I suspect that an exception is being raised and hidden in this
fashion, I'll be able to verify my hunch rapidly. Consider this innocuous procedure:
SQL> create table t( x varchar2(4000) );
Table created.
SQL> create or replace
2 procedure maintain_t
3 ( p_str in varchar2 )
4 as

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

4/6

5/4/2014

Ask Tom: On Oracle Database 11g

4 as
5 begin
6
insert into t
7
( x ) values
8
( p_str );
9 exception
10
when others
11
then
12
-- call some log_error() routine
13
null;
14 end;
15 /
Procedure created.

It's as simple as can be. Nothing should go wrong, but in case it does, I'll log the error by using a utility routine I've developed. That'll write a
nice error message somewhere (hopefully!), but whoever invokes this routine will have no idea that an exception that was not expectedand
that could not be fixedhappened. (I see code like this time after time after time.) Now when someone calls this routine,
SQL> exec maintain_t( rpad( 'x', 4001, 'x' ) ); PL/SQL procedure successfully completed.

it sure looks successful, but it wasn't:


SQL> select * from t;
no rows selected
This is where confusion comes in: people get on the phone; they write e-mails; they say "Oracle
Database is broken; the transaction succeeds, but the data is wrong." In fact, the problem is the
hidden error. Now, to find these errors in Oracle Database 11g, I will simply ask people to perform
the following operation on their code first:

ASK Tom
Oracle Vice President Tom Kyte answers
your most difficult technology questions.
Highlights from that forum appear in this
column.
asktom.oracle.com

SQL> alter procedure maintain_t compile


2 PLSQL_Warnings = 'enable:all'
3 reuse settings
4 /
SP2-0805: Procedure altered with
compilation warnings

READ more about Oracle Database 11g

SQL> show errors procedure maintain_t


Errors for PROCEDURE MAINTAIN_T:
LINE/COL
---------9/8

Next Steps

READ more Tom


Expert Oracle Database Architecture: 9i
and 10g Programming Techniques and
Solutions

ERROR
----------------------------PLW-06009: procedure
"MAINTAIN_T" OTHERS handler
does not end in RAISE or
RAISE_APPLICATION_ERROR

DOWNLOAD Oracle Database 11g

And instantly you'll have a list of applicationsand the source code linesto immediately go to
and add a simple RAISE to see where this hidden error is coming from. Job done.
It's the Little Things. . . .
There's a saying that goes, "It's the little things in life that really make a difference." So I'll close with a little thing that will make your coding life a
bit easier and the PL/SQL language a bit more complete:
SQL> create sequence this_is_nice;
Sequence created.
SQL> declare
2
n number;
3 begin
4
n := this_is_nice.nextval;
5
dbms_output.put_line( n );
6 end;
7 /
1
PL/SQL procedure successfully completed.
Compare that to what would have happened in Oracle Database 10g and prior releases. In Oracle Database 11g, there's no more selecting a
sequence FROM DUAL. PL/SQL is that much more complete.

Tom Kyte has worked for Oracle since 1993. He is a vice president in the Oracle Public Sector group and the author of Expert Oracle Datab ase
Architecture: 9i and 10g Programming Techniques and Solutions (Apress, 2005) and Effective Oracle b y Design (Oracle Press, 2003), among
others.
Send us your comments

E-mail this page

ORACLE CLOUD

JAVA

CUSTOMERS AND EVENTS

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

COMMUNITIES

Printer View

SERVICES AND STORE

5/6

5/4/2014

Ask Tom: On Oracle Database 11g

Learn About Oracle Cloud

Learn About Java

Blogs

Log In to My Oracle Support

Dow nload Java for Consumers

Explore and Read Customer


Stories

Get a Free Trial

Discussion Forums

Training and Certification

Learn About PaaS

Dow nload Java for Developers

All Oracle Events

Wikis

Become a Partner

Learn About SaaS

Java Resources for Developers

Oracle OpenWorld

Oracle ACEs

Find a Partner Solution

Learn About IaaS

Java Cloud Service

JavaOne

User Groups

Purchase from the Oracle Store

Java Magazine

Social Media Channels

CONTACT AND CHAT


Phone: +1.800.633.0738
Global Contacts
Oracle Support
Partner Support

Subscribe Careers Contact Us Site Maps Legal Notices Terms of Use Privacy Cookie Preferences Oracle Mobile

http://www.oracle.com/technetwork/issue-archive/2007/07-sep/o57asktom-101814.html

6/6

Potrebbero piacerti anche