Database Journal
MS SQL Oracle DB2 Access MySQL PostgreSQL Sybase PHP SQL Etc SQL Scripts & Samples Tips Database Forum Rss Feed

» Database Journal Home
» Database Articles
» Database Tutorials
MS Access
SQL Scripts & Samples
» Database Forum
» Slideshows
Free Newsletters:

News Via RSS Feed

Rss Feed

Database Journal |DBA Support |SQLCourse |SQLCourse2

Featured Database Articles


Posted Jul 30, 2008

Oracle Database 11g: SQL Query Result Set Caching

By Jim Czuprynski

Synopsis. 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.

As 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.

What 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 overhead.

SQL Query Result Set Caches

Oracle Database 11g offers result set caching to provide this functionality. A SQL Query Result Set 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 the +NO_RESULT_CACHE 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:

RESULT_CACHE_MAX_SIZE. To reserve an 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 11g MEMORY_TARGET 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 or SGA_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.

Regardless of which calculation method is used, note that Oracle 11g will never set RESULT_CACHE_MAX_SIZE to more than 75% of SHARED_POOL_SIZE. 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.

RESULT_CACHE_MAX_RESULT. This parameter 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%.

RESULT_CACHE_REMOTE_EXPIRATION. If a query 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 caching.
  • 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

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

  • 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)
SELECT /*SQRC_1.2*/ 
  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

Oracle 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 / Procedure



Returns 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.


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


Flushes the entire contents of the Result Cache


Invalidates a cached result for a specific object in the Result Cache


Invalidates a specific Result Cache based on its Cache ID

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

Results Cache Metadata

Four 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




Lists the various cache settings and memory usage statistics


Lists all memory blocks and corresponding statistics


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


Lists 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

Oracle 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 level for faster client application access to frequently-accessed reference data.

References and Additional Reading

While 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 Reference

Also, 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

Oracle Archives

Latest Forum Threads
Oracle Forum
Topic By Replies Updated
Oracle Data Mining: Classification jan.hasller 0 July 5th, 07:19 AM
Find duplicates - Unique IDs Lava 5 July 2nd, 08:30 AM
no matching unique or primary key rcanter 1 April 25th, 12:32 PM
Update values of one table based on condition of values in other table using Trigger Gladiator 3 February 29th, 06:01 PM