Pro*C/C++ Precompiler Programmer's Guide
Release 8.0






Prev Next

Performance Tuning


Topics are:

What Causes Poor Performance?


Another cause of poor performance is inefficient SQL statements. Because SQL is so flexible, you can get the same result with two different statements, but one statement might be less efficient. For example, the following two SELECT statements return the same rows (the name and number of every department having at least one employee):

EXEC SQL SELECT dname, deptno 
    FROM dept 
    WHERE deptno IN (SELECT deptno FROM emp); 
EXEC SQL SELECT dname, deptno 
    FROM dept 
    (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno); 




How Can Performance Be Improved?



You can reduce processing overhead-sometimes dramatically-by


Using Host Arrays

Host arrays can increase performance because they let you manipulate an entire collection of data with a single SQL statement. For example, suppose you want to INSERT salaries for 300 employees into the EMP table. Without arrays your program must do 300 individual INSERTs-one for each employee. With arrays, only one INSERT is necessary. Consider the following statement:

EXEC SQL INSERT INTO emp (sal) VALUES (:salary); 

If salary is a simple host variable, Oracle executes the INSERT statement once, inserting a single row into the EMP table. In that row, the SAL column has the value of salary. To insert 300 rows this way, you must execute the INSERT statement 300 times.

However, if salary is a host array of size 300, Oracle inserts all 300 rows into the EMP table at once. In each row, the SAL column has the value of an element in the salary array.

For more information, see Chapter 12, "Using Host Arrays".

Using Embedded PL/SQL


Also, you can use PL/SQL subprograms to reduce calls from your application to Oracle. For example, to execute ten individual SQL statements, ten calls are required, but to execute a subprogram containing ten SQL statements, only one call is required.

Figure 0-1 PL/SQL Boosts Performance

PL/SQL can also cooperate with Oracle application development tools such as SQL*Forms, SQL*Menu, and SQL*ReportWriter. By adding procedural processing power to these tools, PL/SQL boosts performance. Using PL/SQL, a tool can do any computation quickly and efficiently without calling on Oracle. This saves time and reduces network traffic.

For more information, see Chapter 6, "Using Embedded PL/SQL", and the PL/SQL User's Guide and Reference.

Optimizing SQL Statements

For every SQL statement, the Oracle optimizer generates an execution plan, which is a series of steps that Oracle takes to execute the statement. These steps are determined by rules given in Oracle8 Application Developer's Guide. Following these rules will help you write optimal SQL statements.

Optimizer Hints

In some cases, you can suggest to Oracle the way to optimize a SQL statement. These suggestions, called hints, let you influence decisions made by the optimizer.


You can use hints to specify the

Hence, hints fall into the following four categories:

For example, the two optimization approach hints, COST and NOCOST, invoke the cost-based optimizer and the rule-based optimizer, respectively.

You give hints to the optimizer by placing them in a C-style comment immediately after the verb in a SELECT, UPDATE, INSERT, or DELETE statement. For instance, the optimizer uses the cost-based approach for the following statement:

SELECT /*+ COST */ ename, sal INTO ... 

For C++ code, optimizer hints in the form //+ are also recognized.

For more information about optimizer hints, see Oracle8 Application Developer's Guide.

Trace Facility


The SQL trace facility generates statistics for every SQL statement executed by Oracle. From these statistics, you can determine which statements take the most time to process. Then, you can concentrate your tuning efforts on those statements.

The EXPLAIN PLAN statement shows the execution plan for each SQL statement in your application. An execution plan describes the database operations that Oracle must carry out to execute a SQL statement. You can use the execution plan to identify inefficient SQL statements.

For instructions on using these tools and analyzing their output, see Oracle8 Application Developer's Guide.

Using Indexes

Using ROWIDs, an index associates each distinct value in a table column with the rows containing that value. An index is created with the CREATE INDEX statement. For details, see Oracle8 SQL Reference.


Any query that names an indexed column in its WHERE clause can use the index. For guidelines that help you choose which columns to index, see Oracle8 Application Developer's Guide.

Taking Advantage of Row-Level Locking


You can specify table-level locking, but it lessens the effectiveness of the transaction processing option. For more information about table locking, see the section "Using LOCK TABLE" on page 10-11.


Eliminating Unnecessary Parsing



Handling Explicit Cursors

Recall that there are two types of cursors: implicit and explicit. Oracle implicitly declares a cursor for all data definition and data manipulation statements. However, for queries that return more than one row, you must explicitly declare a cursor (or use host arrays). You use the DECLARE CURSOR statement to declare an explicit cursor. The way you handle the opening and closing of explicit cursors affects performance.

If you need to reevaluate the active set, simply reOPEN the cursor. OPEN will use any new host-variable values. You can save processing time if you do not CLOSE the cursor first.

Note: To make performance tuning easier, Oracle lets you reOPEN an already open cursor. However, this is ANSI extension. So, when MODE=ANSI, you must CLOSE a cursor before reOPENing it.

Only CLOSE a cursor when you want to free the resources (memory and locks) acquired by OPENing the cursor. For example, your program should CLOSE all cursors before exiting.

Cursor Control

In general, there are three ways to control an explicitly declared cursor:

With the first way, beware of unnecessary parsing. OPEN does the parsing, but only if the parsed statement is unavailable because the cursor was CLOSEd or never OPENed. Your program should DECLARE the cursor, reOPEN it every time the value of a host variable changes, and CLOSE it only when the SQL statement is no longer needed.

With the second way (for dynamic SQL Methods 3 and 4), PREPARE does the parsing, and the parsed statement is available until a CLOSE is executed. Your program should PREPARE the SQL statement and DECLARE the cursor, reOPEN the cursor every time the value of a host variable changes, rePREPARE the SQL statement and reOPEN the cursor if the SQL statement changes, and CLOSE the cursor only when the SQL statement is no longer needed.

When possible, avoid placing OPEN and CLOSE statements in a loop; this is a potential cause of unnecessary reparsing of the SQL statement. In the next example, both the OPEN and CLOSE statements are inside the outer while loop. When MODE=ANSI, the CLOSE statement must be positioned as shown, because ANSI requires a cursor to be CLOSEd before being reOPENed.

     SELECT ename, sal from emp where sal >  :salary and  
                                      sal <= :salary + 1000; 
salary = 0; 
while (salary < 5000) 
     EXEC SQL OPEN emp_cursor; 
     while (SQLCODE==0) 
         EXEC SQL FETCH emp_cursor INTO .... 
     salary += 1000; 
     EXEC SQL CLOSE emp_cursor; 

With MODE=ORACLE, however, a CLOSE statement can execute without the cursor being OPENed. By placing the CLOSE statement outside the outer while loop, you can avoid possible reparsing at each iteration of the OPEN statement.

while (salary < 5000) 
     EXEC SQL OPEN emp_cursor; 
     while (sqlca.sqlcode==0) 
         EXEC SQL FETCH emp_cursor INTO .... 
     salary += 1000; 
EXEC SQL CLOSE emp_cursor;

Using the Cursor Management Options

A SQL statement need be parsed only once unless you change its makeup. For example, you change the makeup of a query by adding a column to its select list or WHERE clause. The HOLD_CURSOR, RELEASE_CURSOR, and MAXOPENCURSORS options give you some control over how Oracle manages the parsing and reparsing of SQL statements. Declaring an explicit cursor gives you maximum control over parsing.

SQL Areas and Cursor Cache

When a data manipulation statement is executed, its associated cursor is linked to an entry in the Pro*C/C++ cursor cache. The cursor cache is a continuously updated area of memory used for cursor management. The cursor cache entry is in turn linked to a private SQL area.

The private SQL area, a work area created dynamically at run time by Oracle, contains the addresses of host variables, and other information needed to process the statement. An explicit cursor lets you name a SQL statement, access the information in its private SQL area, and, to some extent, control its processing.

Figure 0-2 Cursors Linked via the Cursor Cache

Figure 0-2 represents the cursor cache after your program has done an INSERT and a DELETE.

Resource Use

The maximum number of open cursors per user session is set by the Oracle initialization parameter OPEN_CURSORS.


If the value of MAXOPENCURSORS is less than the number of cache entries actually needed, Oracle uses the first cache entry marked as reusable. For example, suppose an INSERT statement's cache entry E(1) is marked as reusable, and the number of cache entries already equals MAXOPENCURSORS. If the program executes a new statement, cache entry E(1) and its private SQL area might be reassigned to the new statement. To re-execute the INSERT statement, Oracle would have to reparse it and reassign another cache entry.


Thus, specifying a low value for MAXOPENCURSORS saves memory but causes potentially expensive dynamic allocations and deallocations of new cache entries. Specifying a high value for MAXOPENCURSORS assures speedy execution but uses more memory.

Infrequent Execution



When RELEASE_CURSOR=YES, after Oracle executes the SQL statement and the cursor is closed, the private SQL area is automatically freed and the parsed statement lost. This might be necessary if, for example, MAXOPENCURSORS is set low at your site to conserve memory.

If a data manipulation statement precedes a data definition statement and they reference the same tables, specify RELEASE_CURSOR=YES for the data manipulation statement. This avoids a conflict between the parse lock obtained by the data manipulation statement and the exclusive lock required by the data definition statement.

When RELEASE_CURSOR=YES, the link between the private SQL area and the cache entry is immediately removed and the private SQL area freed. Even if you specify HOLD_CURSOR=YES, Oracle must still reallocate memory for a private SQL area and reparse the SQL statement before executing it because RELEASE_CURSOR=YES overrides HOLD_CURSOR=YES.

However, when RELEASE_CURSOR=YES, the reparse might still require no extra processing because Oracle caches the parsed representations of SQL statements and PL/SQL blocks in its Shared SQL Cache. Even if its cursor is closed, the parsed representation remains available until it is aged out of the cache.

Frequent Execution

The links between a frequently executed SQL statement and its private SQL area should be maintained because the private SQL area contains all the information needed to execute the statement. Maintaining access to this information makes subsequent execution of the statement much faster.


When RELEASE_CURSOR=NO (the default), the link between the cache entry and the private SQL area is maintained after Oracle executes the SQL statement and is not reused unless the number of open cursors exceeds the value of MAXOPENCURSORS. This is useful for SQL statements that are executed often because the parsed statement and allocated memory remain available.

Note: With prior versions of Oracle, when RELEASE_CURSOR=NO and HOLD_CURSOR=YES, after Oracle executes a SQL statement, its parsed representation remains available. But, with Oracle8, when RELEASE_CURSOR=NO and HOLD_CURSOR=YES, the parsed representation remains available only until it is aged out of the Shared SQL Cache. Normally, this is not a problem, but you might get unexpected results if the definition of a referenced object changes before the SQL statement is reparsed.

Parameter Interactions

Table 0-2 shows how HOLD_CURSOR and RELEASE_CURSOR interact. Notice that HOLD_CURSOR=NO overrides RELEASE_CURSOR=NO and that RELEASE_CURSOR=YES overrides HOLD_CURSOR=YES.

Table 0-2 HOLD_CURSOR and RELEASE _CURSOR Interactions





marked as reusable












removed immediately






removed immediately



Copyright © 1997 Oracle Corporation.

All Rights Reserved.