Oracle Database 11g: SQL Query Result Set Caching

Oracle Database 11gR1 offers several new performance enhancements that limit
“round trips” between database server, application servers, and applications
when the identical data is required to answer queries, provide lookup data, or
return deterministic values from a function. This article – the first in this
series – delves into how the result set from a SQL query result set can be
retained in the database’s Shared Pool for later access by a similar or
identical query in the immediate future.

the Oracle RDBMS evolved over the last three decades, it’s provided database
administrators with several options to retain often-queried data in memory to
limit or eliminate unnecessary data reads from a database’s I/O subsystem.
Here’s a quick review of some of the features that a DBA has on her tool belt
to maintain fine-grained control of query-related data in database memory.

Caching Database Blocks Permanently. The CACHE
storage option (also provided as the +CACHE optimizer hint) provides a
method to retain all database blocks
for a table, once that table has been table-scanned into the database buffer
cache. Once cached via this method, the corresponding buffers never age out of
the buffer cache until the database instance is terminated. This strategy has
some drawbacks because it relies upon the DBA’s intimate knowledge of which
tables can most benefit from caching; it also has the tendency to be overused
as a panacea for code tables and reference data.

Influencing Storage Retention of Database Blocks.
Oracle also offers the ability to influence a buffer’s retention in the KEEP
buffer pool of the database buffer cache by either creating or altering a table
or index so that it’s kept within that pool. Oracle treats this as a simple
promise to hold onto the buffers in the KEEP pool for as long as
possible; essentially, they’re placed closer to the most recently used (MRU)
end of the buffer cache. However, nothing is forever, and it’s not impossible
that a buffer in the KEEP buffer pool may eventually be aged out when
large queries need enormous amounts of buffer cache to complete.

Retaining Result Sets Permanently. Materialized views (MVs) offer a way to
hold onto often-queried data: the capability to retain a specific set of rows
for an extended period of time by creating a view with an underlying table. If
properly configured, Oracle will “rewrite” an existing query so that it queries
the MV instead of the underlying database tables directly. Moreover, an MV can
be set up so that a change to its underlying base data automatically forces a
refresh of the dependent MVs. However, this most obvious advantage of an MV is
also its potential bane: Since it needs to be refreshed periodically to insure
that its data is kept current, the actual refresh of the data may take
excessive time, and may even occur at a most inopportune time.

I really need is something less permanent than any of these (still excellent!)
features: a section of memory that stores just the results of a query, and that
could be shared with any other query that needs similar data. For example, a
“code table” that captures the list of U.S. states and
possessions may only span a handful of rows and columns and hardly ever change,
so it would need to be refreshed at extremely infrequent intervals.
Nevertheless, when that set of results is no longer valid, I’d like it to
refresh itself without any intervention on my part. And what about a query that
regularly gets executed by several users, but not frequently enough to take
advantage of a materialized view’s query rewrite capabilities? MVs are not necessarily
trivial to create, configure and refresh, so this feature needs to be simpler to
set up than an MV, and must be able to refresh itself with only minimum performance

SQL Query Result Set Caches

Database 11g offers result set caching
to provide this functionality. A SQL Query Result
will be cached within a subsection of a database instance’s shared
pool in the Shared Global Area (SGA) depending on the setting for several new
initialization parameters.

RESULT_CACHE_MODE. This new parameter accepts
one of three values, and it can be set at either the database (ALTER SYSTEM)
or individual session (ALTER SESSION) level:

  • When set to MANUAL (the default), a SQL query
    result set will only be considered for potential caching if the query itself
    specifies the +RESULT_CACHE
    optimizer hint.
  • If this parameter is set to FORCE,
    however, a query’s result set will always
    be cached unless the query specifies
    optimizer hint.
  • Finally, if this parameter is set to AUTO,
    Oracle 11g uses an unpublished internal algorithm to automatically determine
    whether a query’s result set should be cached based on how often the result set
    could benefit future statement execution. Only if the query specifies the +NO_RESULT_CACHE
    optimizer hint will it be ignored.

Controlling Result Set Cache Memory Utilization.
Oracle 11g also provides several methods to limit precisely the amount of memory
that may be allocated for SQL query result set caching:

appropriate amount of SGA memory for all local result caches, the DBA can
specify a value for the RESULT_CACHE_MAX_SIZE initialization parameter. Oracle
11g automatically rounds the supplied value to the nearest 32K boundary.

If no
value is supplied, then Oracle 11g uses the following algorithm to allocate
memory for Result Caches:

  • If a value has been specified for the new Oracle
    parameter (i.e. the total memory allocated to both SGA and PGA for the database
    instance), then Oracle sets RESULT_CACHE_MAX_SIZE to 0.25% of MEMORY_TARGET.
  • If no value for MEMORY_TARGET has been set, but a
    value for SGA_TARGET
    has been set, then Oracle 11g sets RESULT_CACHE_MAX_SIZE to 0.5% of SGA_TARGET.
  • Finally, if neither a value for MEMORY_TARGET
    has been set, then Oracle sets RESULT_CACHE_MAX_SIZE to 1.0% of the memory allocated to the Shared Pool
    based on the setting for SHARED_POOL_SIZE.

of which calculation method is used, note that Oracle 11g will never set RESULT_CACHE_MAX_SIZE to
more than
Moreover, note that if the DBA wants to deactivate SQL Result Caching features completely,
she merely needs to set the size of this memory allocation area to zero (0) to
tell Oracle 11g to reserve absolutely no memory for results caching.

tells Oracle 11g how much of the result cache should be allowed for any individual
query. Its default value of 5% of the entire result cache should usually be
sufficient, but it can also be set between 0% and 100%.

depends on a remote database, then this parameter determines the number of
minutes for which a result set should be retained. The default value of zero
(0) minutes serves as a reminder that any changes to a remote database table
can’t be detected at the local database, and therefore stale result sets might
remain for an unduly long period of time. This parameter can be set globally (ALTER SYSTEM)
or on a per-session basis (ALTER SESSION).

Creating SQL Query Result Caches: A Brief Demonstration

For a
practical demonstration of how to use SQL Query Results Caching features in MANUAL
mode, I’ve provided the code shown in Listing1.1:

  • I first purged the results cache using DBMS_RESULT_CACHE.PURGE
    (see next section for more details), activated MANUAL results caching, and then
    sized the results cache relatively small at only 1MB.
  • I then issued a SQL query to capture a
    summary-level presentation of total and average promotion costs from the
    contents of the Sales History (SH) schema’s PROMOTIONS table. The resulting
    row set that’s captured contains less than 10 rows captured from over 500 rows
    in that source table, so it’s a relatively good candidate for SQL query results
  • I then issued an EXPLAIN PLAN against the original
    query, including the +RESULT_CACHE hint so that I could determine if
    the result cache just created would be utilized by future queries. I also created
    a report that shows in detail how the result cache’s memory has been utilized.
    Here’s a sample of this output:

R e s u l t C a c h e M e m o r y R e p o r t
Block Size = 1K bytes
Maximum Cache Size = 1M bytes (1K blocks)
Maximum Result Size = 10K bytes (10 blocks)
Total Memory = 103528 bytes [0.073% of the Shared Pool]
… Fixed Memory = 5132 bytes [0.004% of the Shared Pool]
……. Cache Mgr = 108 bytes
……. Memory Mgr = 124 bytes
……. Bloom Fltr = 2K bytes
……. State Objs = 2852 bytes
… Dynamic Memory = 98396 bytes [0.069% of the Shared Pool]
……. Overhead = 65628 bytes
……….. Hash Table = 32K bytes (4K buckets)
……….. Chunk Ptrs = 12K bytes (3K slots)
……….. Chunk Maps = 12K bytes
……….. Miscellaneous = 8284 bytes
……. Cache Memory = 32K bytes (32 blocks)
……….. Unused Memory = 30 blocks
……….. Used Memory = 2 blocks
…………… Dependencies = 1 blocks (1 count)
…………… Results = 1 blocks

does setting the result cache mode to FORCE affect the current contents
of the SQL Query Results Cache? As the code shown in Listing

  • I first activated FORCE mode for the results cache,
    and I then sized the results cache relatively large at 20MB and allowed the
    maximum size for any individual result cache to one-half of that value (10MB).
  • Next, I issued a simple SQL query to capture the
    names of all Vendors from table AP.VENDORS in the Accounts
    Payable (AP) test data I originally generated in my previous
    article series
    on Database Capture and Replay. Since this query doesn’t
    include the +NO_RESULT_CACHE
    optimizer directive, the result set will be cached immediately.
  • I then issued a SQL query to capture a more
    complex, summary-level presentation of Accounts Payable (AP) test data. Since the
    resulting row set incorporates the +NO_RESULT_CACHE optimizer
    directive, the result set will not
    be cached at all.
  • My final step is to issue an EXPLAIN PLAN
    against these two queries to see the impact on any future result set that might
    be likewise generated. I also recreated the detailed report on the result
    cache’s memory to see if anything has changed there:

R e s u l t C a c h e M e m o r y R e p o r t
Block Size = 1K bytes
Maximum Cache Size = 20M bytes (20K blocks)
Maximum Result Size = 10M bytes (10K blocks)
Total Memory = 103528 bytes [0.073% of the Shared Pool]
… Fixed Memory = 5132 bytes [0.004% of the Shared Pool]
……. Cache Mgr = 108 bytes
……. Memory Mgr = 124 bytes
……. Bloom Fltr = 2K bytes
……. State Objs = 2852 bytes
… Dynamic Memory = 98396 bytes [0.069% of the Shared Pool]
……. Overhead = 65628 bytes
……….. Hash Table = 32K bytes (4K buckets)
……….. Chunk Ptrs = 12K bytes (3K slots)
……….. Chunk Maps = 12K bytes
……….. Miscellaneous = 8284 bytes
……. Cache Memory = 32K bytes (32 blocks)
……….. Unused Memory = 24 blocks
……….. Used Memory = 8 blocks
…………… Dependencies = 2 blocks (2 count)
…………… Results = 6 blocks
………………. SQL = 6 blocks (2 count)

FROM ap.vendors

Plan hash value: 2620802014
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 164 | 3772 | 3 (0)| 00:00:01 |
| 1 | RESULT CACHE | 89gqh0j9248q8d0w79w0fcwhw2 | | | | |
| 2 | TABLE ACCESS FULL| VENDORS | 164 | 3772 | 3 (0)| 00:00:01 |
Result Cache Information (identified by operation id):
1 – column-count=2; dependencies=(AP.VENDORS); name=”SELECT /*SQRC_1.2*/
FROM ap.vendors
,C.cust_last_name || ‘, ‘ || C.cust_first_name AS customer_fullname
,SUM(ID.extended_amt) total_detail
ap.vendors V
,ap.invoices I
,ap.invoice_items ID
,oe.customers C
,oe.product_information P
WHERE ID.invoice_id = I.invoice_id
AND I.vendor_id = V.vendor_id
AND I.customer_id = C.customer_id
AND ID.product_id = P.product_id
AND I.active_ind = ‘Y’
,C.cust_last_name || ‘, ‘ || C.cust_first_name
,C.cust_last_name || ‘, ‘ || C.cust_first_name
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
SQL> 2
Plan hash value: 500053926
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 956 | 34416 | 11 (19)| 00:00:01 |
| 1 | SORT GROUP BY | | 956 | 34416 | 11 (19)| 00:00:01 |
|* 2 | HASH JOIN | | 956 | 34416 | 10 (10)| 00:00:01 |
| 3 | NESTED LOOPS | | | | | |
| 4 | NESTED LOOPS | | 25 | 700 | 6 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | CUSTOMERS | 319 | 6061 | 5 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | INVOICES_CUST_IDX | 25 | | 0 (0)| 00:00:01 |
|* 7 | TABLE ACCESS BY INDEX ROWID| INVOICES | 1 | 9 | 1 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | INVOICE_ITEMS | 975 | 7800 | 3 (0)| 00:00:01 |
Predicate Information (identified by operation id):
2 – access(“ID”.”INVOICE_ID”=”I”.”INVOICE_ID”)
6 – access(“I”.”CUSTOMER_ID”=”C”.”CUSTOMER_ID”)
7 – filter(“I”.”ACTIVE_IND”=’Y’)

Controlling SQL Query Result Set Caching With DBMS_RESULT_CACHE

11g also supplies the DBMS_RESULT_CACHE package to interrogate
the status of and precisely control the contents of the SQL result cache. Here’s
a brief summary of its capabilities:

Table 1-1.
DBMS_RESULT_CACHE Functions and Procedures

Function /



the current status of the
Result Cache. Values include:

ENABLED: The result cache is enabled.

DISABLED: The result cache has been disabled.

BYPASSED: The result cache is temporarily unavailable.

SYNC: The result cache is available, but is currently being resynchronized with other RAC nodes.


either a summary (by default)
or detailed report of Result
Cache memory usage


Flushes the entire contents of the Result Cache


a cached result for a specific object
in the Result Cache


a specific Result Cache based
on its Cache ID

shows some additional examples of how to use these
packaged procedures and functions.

Results Cache

dynamic views provide information about existing Results Cache contents, memory
usage, and the database objects on which Result Caches depend:

Table 1-2. SQL Result
Cache Metadata




the various cache settings and memory usage statistics


all memory blocks and corresponding statistics


all the objects (cached results and dependencies) along with their attributes


the dependency details between the cached results and dependencies

See Listing 1.4
for several queries I’ve created against the single-instance (V$)
views for this article; it’s a relatively simple task to expand these queries
to the global resource view (GV$) for Real Application Clusters databases. In Listing 1.5
I’ve also reproduced the results from the query against the V$RESULT_CACHE_OBJECTS
view to demonstrate what metadata it contains for cached result sets.

Next Steps

11g’s new result caching capabilities offer an Oracle DBA several simple yet
elegant tools to capture, retain, monitor and manage SQL Query Result Caches
that speed application access to relatively persistent data. In the final article
in this series, I’ll illustrate how to take advantage of result set caching within PL/SQL functions, as well as how to
cache result sets at the application server
for faster client application access to frequently-accessed
reference data.

References and Additional Reading

I’m hopeful that I’ve given you a thorough grounding in the technical aspects
of the features I’ve discussed in this article, I’m also sure that there may be
better documentation available since it’s been published. I therefore strongly
suggest that you take a close look at the corresponding Oracle documentation on
these features to obtain crystal-clear understanding before attempting to
implement them in a production environment. Please note that I’ve drawn upon
the following Oracle Database 11gR1 documentation for the deeper technical
details of this article:

B28320-01 Oracle Database 11gR1 Reference Guide

B28274-01 Oracle Database 11gR1 Performance Tuning Guide

B28279-02 Oracle Database 11gR1 New Features Guide

B28419-02 Oracle Database 11gR1 PL/SQL Packages and Types

the following MetaLink documents help clarify this feature set:

453567.1 11g New Feature: SQL Query Result Cache

563828.1 Result Cache Could Not Be Enabled


See All Articles by Columnist
Jim Czuprynski

Jim Czuprynski
Jim Czuprynski
Jim Czuprynski has accumulated over 30 years of experience during his information technology career. He has filled diverse roles at several Fortune 1000 companies in those three decades - mainframe programmer, applications developer, business analyst, and project manager - before becoming an Oracle database administrator in 2001. He currently holds OCP certification for Oracle 9i, 10g and 11g. Jim teaches the core Oracle University database administration courses on behalf of Oracle and its Education Partners throughout the United States and Canada, instructing several hundred Oracle DBAs since 2005. He was selected as Oracle Education Partner Instructor of the Year in 2009. Jim resides in Bartlett, Illinois, USA with his wife Ruth, whose career as a project manager and software quality assurance manager for a multinational insurance company makes for interesting marital discussions. He enjoys cross-country skiing, biking, bird watching, and writing about his life experiences in the field of information technology.

Latest Articles