This chapter contains the following topics:
Oracle Database can share cursors, which are pointers to private SQL areas in the shared pool. Cursor sharing can improve database application performance by orders of magnitude.
This section contains the following topics:
A private SQL area holds information about a parsed SQL statement and other session-specific information for processing. When a server process executes SQL or PL/SQL code, the process uses the private SQL area to store bind variable values, query execution state information, and query execution work areas. The private SQL areas for each execution of a statement are not shared and may contain different values and data.
A cursor is a name or handle to a specific private SQL area. The cursor contains session-specific state information such as bind variable values and result sets.
As shown in the following graphic, you can think of a cursor as a pointer on the client side and as a state on the server side. Because cursors are closely associated with private SQL areas, the terms are sometimes used interchangeably.
A cursor in the private SQL area points to a shared SQL area in the library cache. Unlike the private SQL area, which contains session state information, the shared SQL area contains the parse tree and execution plan for the statement.
For example, an execution of SELECT * FROM employees
has a plan and parse tree stored in one shared SQL area. An execution of SELECT * FROM departments
, which differs both syntactically and semantically, has a plan and parse tree stored in a separate shared SQL area.
Multiple private SQL areas in the same or different sessions can reference a single shared SQL area, a phenomenon known as cursor sharing. For example, an execution of SELECT * FROM employees
in one session and an execution of the SELECT * FROM employees
(accessing the same table) in a different session can use the same parse tree and plan. A shared SQL area that is accessed by multiple statements is known as a shared cursor.
Oracle Database automatically determines whether the SQL statement or PL/SQL block being issued is identical to another statement currently in the library cache, using the following steps:
The text of the statement is hashed.
If no matching hash value exists, then the SQL statement does not currently exist in the shared pool, so the database performs a hard parse.
The database looks for a matching hash value for an existing SQL statement in the shared pool. The following options are possible:
No matching hash value exists.
In this case, the SQL statement does not currently exist in the shared pool, so the database performs a hard parse. This ends the shared pool check.
A matching hash value exists.
In this case, the database compares the text of the matched statement to the text of the hashed statement to see if they are identical. The text of the SQL statements or PL/SQL blocks must be identical, character for character, including spaces, case, and comments. For example, the following statements cannot use the same shared SQL area:
SELECT * FROM employees; SELECT * FROM Employees; SELECT * FROM employees;
Usually, SQL statements that differ only in literals cannot use the same shared SQL area. For example, the following statements do not resolve to the same SQL area:
SELECT count(1) FROM employees WHERE manager_id = 121; SELECT count(1) FROM employees WHERE manager_id = 247;
The only exception to this rule is when the parameter CURSOR_SHARING
has been set to FORCE
, in which case similar statements can share SQL areas. The costs involved in using CURSOR_SHARING
are explained in "Do Not Use CURSOR_SHARING = FORCE as a Permanent Fix".
The database compares objects referenced in the issued statement to the referenced objects of all existing statements in the pool to ensure that they are identical.
References to schema objects in the SQL statements or PL/SQL blocks must resolve to the same object in the same schema. For example, if two users issue the following SQL statement, and if each user has its own employees
table, then the following statement is not identical because the statement references different employees
tables for each user:
SELECT * FROM employees;
The database determines whether bind variables in the SQL statements match in name, data type, and length.
For example, the following statements cannot use the same shared SQL area because the bind variable names differ:
SELECT * FROM employees WHERE department_id = :department_id; SELECT * FROM employees WHERE department_id = :dept_id;
The database determines whether the session environment is identical.
For example, SQL statements must be optimized using the same optimizer goal (see "Choosing an Optimizer Goal").
See Also:
Oracle Database Reference to learn more about the CURSOR_SHARING
initialization parameter
Every parsed SQL statement has a parent cursor and one or more child cursors. The parent cursor stores the text of the SQL statement. If the text of two statements is identical, then the statements share the same parent cursor. If the text is different, however, then the database creates a separate parent cursor.
Example 15-1 Parent Cursors
In this example, the first two statements are syntactically different (the letter “c” is lowercase in the first statement and uppercase in the second statement), but semantically identical. Because of the syntactic difference, these statements have different parent cursors. The third statement is syntactically identical to the first statement (lowercase “c”), but semantically different because it refers to a customers
table in a different schema. Because of the syntactic identity, the third statement can share a parent cursor with the first statement.
SQL> CONNECT oe@inst1 Enter password: ******* Connected. SQL> SELECT COUNT(*) FROM customers; COUNT(*) ---------- 319 SQL> SELECT COUNT(*) FROM Customers; COUNT(*) ---------- 319 SQL> CONNECT sh@inst1 Enter password: ******* Connected. SQL> SELECT COUNT(*) FROM customers; COUNT(*) ---------- 155500
The following query of V$SQL
indicates the two parents. The statement with the SQL ID of 8h916vv2yw400
, which is the lowercase “c” version of the statement, has one parent cursor and two child cursors: child 0 and child 1. The statement with the SQL ID of 5rn2uxjtpz0wd
, which is the uppercase “c” version of the statement, has a different parent cursor and only one child cursor: child 0.
SQL> CONNECT SYSTEM@inst1 Enter password: ******* Connected. SQL> COL SQL_TEXT FORMAT a30 SQL> COL CHILD# FORMAT 99999 SQL> COL EXEC FORMAT 9999 SQL> COL SCHEMA FORMAT a6 SQL> SELECT SQL_ID, PARSING_SCHEMA_NAME AS SCHEMA, SQL_TEXT, 2 CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC FROM V$SQL 3 WHERE SQL_TEXT LIKE '%ustom%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' ORDER BY SQL_ID; SQL_ID SCHEMA SQL_TEXT CHILD# EXEC ------------- ------ ------------------------------ ------ ----- 5rn2uxjtpz0wd OE SELECT COUNT(*) FROM Customers 0 1 8h916vv2yw400 OE SELECT COUNT(*) FROM customers 0 1 8h916vv2yw400 SH SELECT COUNT(*) FROM customers 1 1
The V$SQLAREA
view contains one row for every parent cursor.
In the following example, a query of V$SQLAREA
shows two parent cursors, each identified with a different SQL_ID
. The VERSION_COUNT
indicates the number of child cursors.
COL SQL_TEXT FORMAT a30 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ------------------------------ ------------- ------------- ---------- SELECT * FROM Employees 5bzhzpaa0wy9m 1 2483976499 SELECT * FROM employees 4959aapufrm1k 2 1961610290
In the preceding output, the VERSION_COUNT
of 2
for SELECT * FROM employees
indicates multiple child cursors, which were necessary because the statement was executed against two different objects. In contrast, the statement SELECT * FROM Employees
(note the capital "E") was executed once, and so has one parent cursor, and one child cursor (VERSION_COUNT
of 1
).
Every parent cursor has one or more child cursors. A child cursor contains the execution plan, bind variables, metadata about objects referenced in the query, optimizer environment, and other information. In contrast to the parent cursor, the child cursor does not store the text of the SQL statement.
V$SQL
describes the statements that currently reside in the library cache. It contains one row for every child cursor, as shown in the following example:
SELECT SQL_TEXT, SQL_ID, USERNAME AS USR, CHILD_NUMBER AS CHILD#, HASH_VALUE, PLAN_HASH_VALUE AS PLAN_HASHV FROM V$SQL s, DBA_USERS d WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' AND d.USER_ID = s.PARSING_USER_ID; SQL_TEXT SQL_ID USR CHILD# HASH_VALUE PLAN_HASHV ----------------------- ------------- --- ------ ---------- ---------- SELECT * FROM Employees 5bzhzpaa0wy9m HR 0 2483976499 1445457117 SELECT * FROM employees 4959aapufrm1k HR 0 1961610290 1445457117 SELECT * FROM employees 4959aapufrm1k SH 1 1961610290 1445457117
In the preceding results, the CHILD#
of the bottom two statements is different (0
and 1
), even though the SQL_ID
is the same. This means that the statements have the same parent cursor, but different child cursors. In contrast, the statement with the SQL_ID
of 5bzhzpaa0wy9m
has one parent and one child (CHILD#
of 0
). All three SQL statements use the same execution plan, as indicated by identical values in the PLAN_HASH_VALUE
column.
If a parent cursor has multiple children, then the V$SQL_SHARED_CURSOR
view provides information about why the cursor was not shared. For several types of incompatibility, the a column indicates a mismatch with the value Y
or N
.
Example 15-2 Translation Mismatch
In this example, the TRANSLATION_MISMATCH
column shows that the two statements (SELECT * FROM employees
) referenced different objects, resulting in a TRANSLATION_MISMATCH
value of Y
for the last statement. Because sharing was not possible, each statement had a separate child cursor, as indicated by CHILD_NUMBER
of 0
and 1
.
SELECT S.SQL_TEXT, S.CHILD_NUMBER, s.CHILD_ADDRESS, C.TRANSLATION_MISMATCH FROM V$SQL S, V$SQL_SHARED_CURSOR C WHERE SQL_TEXT LIKE '%employee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' AND S.CHILD_ADDRESS = C.CHILD_ADDRESS; SQL_TEXT CHILD_NUMBER CHILD_ADDRESS T ------------------------------ ------------ ---------------- - SELECT * FROM employees 0 0000000081EE8690 N SELECT * FROM employees 1 0000000081F22508 Y
If an application issues a statement, and if Oracle Database cannot reuse a cursor, then it must build a new executable version of the application code. This operation is known as a hard parse.
A soft parse is any parse that is not a hard parse, and occurs when the database can reuse existing code (see "Shared Pool Check"). Some soft parses are less resource-intensive than others. For example, if a parent cursor for the statement already exists, then Oracle Database can perform various optimizations, and then store the child cursor in the shared SQL area. If a parent cursor does not exist, however, then Oracle Database must also store the parent cursor in the shared SQL area, which creates additional memory overhead.
Effectively, a hard parse recompiles a statement before running it. Hard parsing a SQL statement before every execution is analogous to recompiling a C program before every execution. A hard parse performs operations such as the following:
Checking the syntax of the SQL statement
Checking the semantics of the SQL statement
Checking the access rights of the user issuing the statement
Creating an execution plan
Accessing the library cache and data dictionary cache numerous times to check the data dictionary
An especially resource-intensive aspect of hard parsing is accessing the library cache and data dictionary cache numerous times to check the data dictionary. When the database accesses these areas, it uses a serialization device called a latch on required objects so that their definition does not change during the check. Latch contention increases statement execution time and decreases concurrency.
For all of the preceding reasons, the CPU and memory overhead of hard parses can create serious performance problems. The problems are especially evident in web applications that accept user input from a form, and then generate SQL statements dynamically. The Real-World Performance group strongly recommends reducing hard parsing as much as possible.
Example 15-3 Finding Parse Information Using V$SQL
You can use various techniques to monitor hard and soft parsing. This example queries the session statistics to determine whether repeated executions of a DBA_JOBS
query increase tehu hard parse count. The first execution of the statement increases the hard parse count to 49
, but the second execution does not change the hard parse count, which means that Oracle Database reused application code.
SQL> ALTER SYSTEM FLUSH SHARED_POOL;
System altered.
SQL> COL NAME FORMAT a18
SQL> SELECT s.NAME, m.VALUE
2 FROM V$STATNAME s, V$MYSTAT m
3 WHERE s.STATISTIC# = m.STATISTIC#
4 AND s.NAME LIKE '%(hard%';
NAME VALUE
------------------ ----------
parse count (hard) 48
SQL> SELECT COUNT(*) FROM DBA_JOBS;
COUNT(*)
----------
0
SQL> SELECT s.NAME, m.VALUE
2 FROM V$STATNAME s, V$MYSTAT m
3 WHERE s.STATISTIC# = m.STATISTIC#
4 AND s.NAME LIKE '%(hard%';
NAME VALUE
------------------ ----------
parse count (hard) 49
SQL> SELECT COUNT(*) FROM DBA_JOBS;
COUNT(*)
----------
0
SQL> SELECT s.NAME, m.VALUE
2 FROM V$STATNAME s, V$MYSTAT m
3 WHERE s.STATISTIC# = m.STATISTIC#
4 AND s.NAME LIKE '%(hard%';
NAME VALUE
------------------ ----------
parse count (hard) 49
Example 15-4 Finding Parse Information Using Trace Files
This example uses SQL Trace and the TKPROF utility to find parse information. Assume that you log in to the database with administrator privileges, and run the following statements, using TRACEFILE_IDENTIFIER
to give the trace file a meaningful name:
ALTER SESSION SET sql_trace=TRUE; ALTER SESSION SET TRACEFILE_IDENTIFIER = "emp_stmt"; ALTER SESSION SET EVENTS '10053 trace name context forever, level 12'; SELECT * FROM hr.employees; EXIT;
The following query shows the directory location of the trace files
SQL> SET LINESIZE 120 COLUMN value FORMAT A80 SELECT value FROM v$diag_info WHERE name = 'Default Trace File'; VALUE -------------------------------------------------------------------------------- /disk1/oracle/log/diag/rdbms/orcl/orcl/trace/orcl_ora_23054.trc
You can search this directory for the trace file that you generated:
% ls *emp_stmt.trc orcl_ora_17950_emp_stmt.trc
Use TKPROF to format the trace file, and then open the formatted file:
% tkprof orcl_ora_17950_emp_stmt.trc emp.out; vi emp.out
The formatted trace file contains the parse information for the query of hr.employees
.
SQL ID: brmjpfs7dcnub Plan Hash: 1445457117
SELECT *
FROM
hr.employees
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.07 0.08 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 9 0.00 0.00 3 12 0 107
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 11 0.07 0.08 3 12 0 107
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYSTEM
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
107 107 107 TABLE ACCESS FULL EMPLOYEES (cr=12 pr=3 pw=0 time=497
us starts=1 cost=2 size=7383 card=107)
A library cache miss indicates a hard parse. Performing the same steps for a second execution of the same statement produces the following trace output, which shows no library cache misses:
SQL ID: brmjpfs7dcnub Plan Hash: 1445457117
SELECT *
FROM
hr.employees
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 9 0.00 0.00 3 12 0 107
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 11 0.00 0.00 3 12 0 107
Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: SYSTEM
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
107 107 107 TABLE ACCESS FULL EMPLOYEES (cr=12 pr=3 pw=0 time=961
us starts=1 cost=2 size=7383 card=107)
This section contains the following topics:
When constructing SQL statements, some Oracle applications use literals instead of bind variables. For example, the statement SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101
uses the literal value 101
for the employee ID. By default, when similar statements do not use bind variables, Oracle Database cannot take advantage of cursor sharing. Thus, Oracle Database sees a statement that is identical except for the value 102
, or any other random value, as a completely new statement, requiring a hard parse.
Through decades of experience, the Real-World Performance group has found that applications that use literals are a frequent cause of performance, scalability, and security problems. In the real world, it is not uncommon for applications to be written quickly, without considering cursor sharing. A classic example is a “screen scraping” application that copies the contents out of a web form, and then concatenates strings to construct the SQL statement dynamically.
Major problems that result from using literal values include the following:
Applications that concatenate literals input by an end user are prone to SQL injection attacks. Only rewriting the applications to use bind variables eliminates this threat (see "Do Not Use CURSOR_SHARING = FORCE as a Permanent Fix").
If every statement is hard parsed, then cursors are not shared, and so the database must consume more memory to create the cursors.
Oracle Database must latch the shared pool and library cache when hard parsing. As the number of hard parses increases, so does the number of processes waiting to latch the shared pool. This situation decreases concurrency and increases contention.
Example 15-5 Literals and Cursor Sharing
Consider an application that executes the following statements, which differ only in literals:
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120; SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
The following query of V$SQLAREA
shows that the three statements require three different parent cursors. As shown by VERSION_COUNT
, each parent cursor requires its own child cursor.
COL SQL_TEXT FORMAT a30 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ------------------------------ ------------- ------------- ---------- SELECT SUM(salary) FROM hr.emp b1tvfcc5qnczb 1 191509483 loyees WHERE employee_id < 165 SELECT SUM(salary) FROM hr.emp cn5250y0nqpym 1 2169198547 loyees WHERE employee_id < 101 SELECT SUM(salary) FROM hr.emp au8nag2vnfw67 1 3074912455 loyees WHERE employee_id < 120
You can develop Oracle applications to use bind variables instead of literals. A bind variable is a placeholder in a query. For example, the statement SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id
uses the bind variable:emp_id
for the employee ID.
The Real-World Performance group has found that applications that use bind variables perform better, are more scalable, and are more secure. Major benefits that result from using bind variables include the following:
Applications that use bind variables are not vulnerable to the same SQL injection attacks as applications that use literals.
When identical statements use bind variables, Oracle Database can take advantage of cursor sharing, and share the plan and other information when different values are bound to the same statement.
Oracle Database avoids the overhead of latching the shared pool and library cache required for hard parsing.
Example 15-6 Bind Variables and Shared Cursors
The following example uses the VARIABLE
command in SQL*Plus to create the emp_id
bind variable, and then executes a query using three different bind values (101
, 120
, and 165
):
VARIABLE emp_id NUMBER EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; EXEC :emp_id := 120; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; EXEC :emp_id := 165; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
The following query of V$SQLAREA
shows one unique SQL statement:
COL SQL_TEXT FORMAT a34 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ---------------------------------- ------------- ------------- ---------- SELECT SUM(salary) FROM hr.employe 4318cbskba8yh 1 615850960 es WHERE employee_id < :emp_id
The VERSION_COUNT
value of 1
indicates that the database reused the same child cursor rather than creating three separate child cursors. Using a bind variable made this reuse possible.
In bind variable peeking (also known as bind peeking), the optimizer looks at the value in a bind variable when the database performs a hard parse of a statement. The optimizer does not look at the bind variable values before every parse. Rather, the optimizer peeks only when the optimizer is first invoked, which is during the hard parse.
When a query uses literals, the optimizer can use the literal values to find the best plan. However, when a query uses bind variables, the optimizer must select the best plan without the presence of literals in the SQL text. This task can be extremely difficult. By peeking at bind values during the initial hard parse, the optimizer can determine the cardinality of a WHERE
clause condition as if literals had been used, thereby improving the plan.
Because the optimizer only peeks at the bind value during the hard parse, the plan may not be optimal for all possible bind values. The following examples illustrate this principle.
Example 15-7 Literals Result in Different Execution Plans
Assume that you execute the following statements, which execute three different statements using different literals (101
, 120
, and 165
), and then display the execution plans for each:
SET LINESIZE 167 SET PAGESIZE 0 SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
The database hard parsed all three statements, which were not identical. The DISPLAY_CURSOR
output, which has been edited for clarity, shows that the optimizer chose the same nested loops plan for the first two statements, but a full table scan plan for the statement using literal 165
:
SQL_ID cn5250y0nqpym, child number 0 ------------------------------------- SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101 Plan hash value: 2410354593 ------------------------------------------------------------------------------------- |Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time| -------------------------------------------------------------------------------------- | 0| SELECT STATEMENT | | | |2 (100)| | | 1| SORT AGGREGATE | |1 | 8 | | | | 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 | 8 |2 (0) | 00:00:01 | |*3| INDEX RANGE SCAN | EMP_EMP_ID_PK |1 | |1 (0) | 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"<101) SQL_ID au8nag2vnfw67, child number 0 ------------------------------------- SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120 Plan hash value: 2410354593 ------------------------------------------------------------------------------------- |Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time| ------------------------------------------------------------------------------------- | 0| SELECT STATEMENT | | | |2 (100)| | | 1| SORT AGGREGATE | |1 | 8 | | | | 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |20|160|2 (0) | 00:00:01 | |*3| INDEX RANGE SCAN | EMP_EMP_ID_PK |20| |1 (0) | 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"<120) SQL_ID b1tvfcc5qnczb, child number 0 ------------------------------------- SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165 Plan hash value: 1756381138 ------------------------------------------------------------------------- | Id | Operation | Name |Rows| Bytes |Cost(%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| EMPLOYEES | 66 | 528 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("EMPLOYEE_ID"<165)
The preceding output shows that the optimizer considers a full table scan more efficient than an index scan for the query that returns more rows.
Example 15-8 Bind Variables Result in Cursor Reuse
This example rewrites the queries executed in Example 15-7 to use bind variables instead of literals. You bind the same values (101
, 120
, and 165
) to the bind variable :emp_id
, and then display the execution plans for each:
VAR emp_id NUMBER EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); EXEC :emp_id := 120; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); EXEC :emp_id := 165; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
The DISPLAY_CURSOR
output shows that the optimizer chose exactly the same plan for all three statements:
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id Plan hash value: 2410354593 ------------------------------------------------------------------------------------- | Id | Operation | Name |Rows|Bytes|Cost (%CPU)|Time| ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | |2 (100)| | | 1 | SORT AGGREGATE | |1|8 | | | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1|8 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | EMP_EMP_ID_PK |1| | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"<:EMP_ID)
In contrast, when the preceding statements were executed with literals, the optimizer chose a lower-cost full table scan when the employee ID value was 165
. This is the problem solved by adaptive cursor sharing (see "Adaptive Cursor Sharing").
This topic explains what the CURSOR_SHARING
initialization parameter is, and how setting it to different values affects how Oracle Database uses bind variables.
This section contains the following topics:
The CURSOR_SHARING
initialization parameter controls how the database processes statements with bind variables.
In Oracle Database 12c, the parameter supports the following values:
EXACT
This is the default value. The database enables only textually identical statements to share a cursor. The database does not attempt to replace literal values with system-generated bind variables. In this case, the optimizer generates a plan for each statement based on the literal value.
FORCE
The database replaces all literals with system-generated bind variables. For statements that are identical after the bind variables replace the literals, the optimizer uses the same plan.
Note:
The SIMILAR
value for CURSOR_SHARING
is deprecated.
You can set CURSOR_SHARING
at the system or session level, or use the CURSOR_SHARING_EXACT
hint at the statement level.
When SQL statements use literals rather than bind variables, setting the CURSOR_SHARING
initialization parameter to FORCE
enables the database to replace literals with system-generated bind variables. Using this technique, the database can sometimes reduce the number of parent cursors in the shared SQL area.
Note:
If a statement uses an ORDER BY
clause, then the database does not perform literal replacement in the clause because it is not semantically correct to consider the constant column number as a literal. The column number in the ORDER BY
clause affects the query plan and execution, so the database cannot share two cursors having different column numbers.
When CURSOR_SHARING
is set to FORCE
, the database performs the following steps during the parse:
Copies all literals in the statement to the PGA, and replaces them with system-generated bind variables
For example, an application could process the following statement:
SELECT SUBSTR(last_name, 1, 4), SUM(salary) FROM hr.employees WHERE employee_id < 101 GROUP BY last_name
The optimizer replaces literals, including the literals in the SUBSTR
function, as follows:
SELECT SUBSTR(last_name, :"SYS_B_0", :"SYS_B_1"), SUM(salary) FROM hr.employees WHERE employee_id < :"SYS_B_2" GROUP BY last_name
Searches for an identical statement (same SQL hash value) in the shared pool
If an identical statement is not found, then the database performs a hard parse. Otherwise, the database proceeds to the next step.
Performs a soft parse of the statement
As the preceding steps indicate, setting the CURSOR_SHARING
initialization parameter to FORCE
does not reduce the parse count. Rather, in some cases, FORCE
enables the database to perform a soft parse instead of a hard parse. Also, FORCE
does not the prevent against SQL injection attacks because Oracle Database binds the values after any injection has already occurred.
Example 15-9 Replacement of Literals with System Bind Variables
This example sets CURSOR_SHARING
to FORCE
at the session level, executes three statements containing literals, and displays the plan for each statement:
ALTER SESSION SET CURSOR_SHARING=FORCE; SET LINESIZE 170 SET PAGESIZE 0 SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR()); SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
The following DISPLAY_CURSOR
output, edited for readability, shows that all three statements used the same plan. The optimizer chose the plan, an index range scan, because it peeked at the first value (101
) bound to the system bind variable, and picked this plan as the best for all values. In fact, this plan is not the best plan for all values. When the value is 165
, a full table scan is more efficient.
SQL_ID cxx8n1cxr9khn, child number 0 ------------------------------------- SELECT SUM(salary) FROM hr.employees WHERE employee_id < :"SYS_B_0" Plan hash value: 2410354593 ------------------------------------------------------------------------------------- | Id | Operation | Name |Rows|Bytes|Cost(%CPU)|Time| ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | |2 (100)| | | 1 | SORT AGGREGATE | |1 | 8 | | | | 2 | TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 | 8 |2 (0) |00:00:01| |* 3 | INDEX RANGE SCAN | EMP_EMP_ID_PK |1 | |1 (0) |00:00:01| ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPLOYEE_ID"<101)
A query of V$SQLAREA
confirms that Oracle Database replaced with the literal with system bind variable :”SYS_B_0”
, and created one parent and one child cursor (VERSION_COUNT=1
) for all three statements, which means that all executions shared the same plan.
COL SQL_TEXT FORMAT a36 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE eee_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ------------------------------------ ------------- ------------- ---------- SELECT SUM(salary) FROM hr.employees cxx8n1cxr9khn 1 997509652 WHERE employee_id < :"SYS_B_0"
See Also:
"Private and Shared SQL Areas" for more details on the various checks performed
Oracle Database Reference to learn about the CURSOR_SHARING
initialization parameter
The adaptive cursor sharing feature enables a single statement that contains bind variables to use multiple execution plans. Cursor sharing is "adaptive" because the cursor adapts its behavior so that the database does not always use the same plan for each execution or bind variable value.
This section contains the following topics:
With bind peeking, the optimizer peeks at the values of user-defined bind variables on the first invocation of a cursor. The optimizer determines the cardinality of any WHERE
clause condition as if literals had been used instead of bind variables. If a column in a WHERE
clause has skewed data, however, then a histogram may exist on this column. When the optimizer peeks at the value of the user-defined bind variable and chooses a plan, this plan may not be good for all values.
In adaptive cursor sharing, the database monitors data accessed over time for different bind values, ensuring the optimal choice of cursor for a specific bind value. For example, the optimizer might choose one plan for bind value 10
and a different plan for bind value 50
. Cursor sharing is "adaptive" because the cursor adapts its behavior so that the optimizer does not always choose the same plan for each execution or bind variable value. Thus, the optimizer automatically detects when different execution of a statement would benefit from different execution plans.
Note:
Adaptive cursor sharing is independent of the CURSOR_SHARING
initialization parameter. Adaptive cursor sharing is equally applicable to statements that contain user-defined and system-generated bind variables. Adaptive cursor sharing does not apply to statements that contain only literals.
Adaptive cursor sharing monitors statements that use bind variables to determine whether a new plan is more efficient.
Assume that an application executes the following statement five times, binding different values every time:
SELECT * FROM employees WHERE salary = :sal AND department_id = :dept
Also assume in this example that a histogram exists on at least one of the columns in the predicate. The database processes this statement as follows:
The application issues the statement for the first time, which causes a hard parse. During the parse, the database performs the following tasks:
Peeks at the bind variables to generate the initial plan.
Marks the cursor as bind-sensitive. A bind-sensitive cursor is a cursor whose optimal plan may depend on the value of a bind variable. To determine whether a different plan is beneficial, the database monitors the behavior of a bind-sensitive cursor that uses different bind values.
Stores metadata about the predicate, including the cardinality of the bound values (in this example, assume that only 5 rows were returned).
Creates an execution plan (in this example, a nested loops join) based on the peeked values.
The database executes the cursor, storing the bind values and execution statistics in the cursor.
The application issues the statement a second time, using different bind variables, causing the database to perform a soft parse, and find the matching cursor in the library cache.
The database executes the cursor.
The database performs the following post-execution tasks:
The database compares the execution statistics for the second execution with the first-execution statistics.
The database observes the pattern of statistics over all previous executions, and then decides whether to mark the cursor as a bind-aware cursor. In this example, assume that the database decides the cursor is bind-aware.
The application issues the statement a third time, using different bind variables, which causes a soft parse. Because the cursor is bind-aware, the database does the following:
Determines whether the cardinality of the new values falls within the same range as the stored cardinality. In this example, the cardinality is similar: 8 rows instead of 5 rows.
Reuses the execution plan in the existing child cursor.
The database executes the cursor.
The application issues the statement a fourth time, using different bind variables, causing a soft parse. Because the cursor is bind-aware, the database does the following:
Determines whether the cardinality of the new values falls within the same range as the stored cardinality. In this example, the cardinality is vastly different: 102 rows (in a table with 107 rows) instead of 5 rows.
Does not find a matching child cursor.
The database performs a hard parse. As a result, the database does the following:
Creates a new child cursor with a second execution plan (in this example, a hash join)
Stores metadata about the predicate, including the cardinality of the bound values, in the cursor
The database executes the new cursor.
The database stores the new bind values and execution statistics in the new child cursor.
The application issues the statement a fifth time, using different bind variables, which causes a soft parse. Because the cursor is bind-aware, the database does the following:
Determines whether the cardinality of the new values falls within the same range as the stored cardinality. In this example, the cardinality is 20.
Does not find a matching child cursor.
The database performs a hard parse. As a result, the database does the following:
Creates a new child cursor with a third execution plan (in this example, a nested loops join)
Determines that this nested loops join execution plan is the same as the nested loops execution plan used for the first execution of the statement
Merges the two child cursors containing nested loop plans, which involves storing the combined cardinality statistics into one child cursor, and deleting the other one
The database executes the cursor using the nested loops execution plan.
A bind-sensitive cursor is a cursor whose optimal plan may depend on the value of a bind variable. The database has examined the bind value when computing cardinality, and considers the query “sensitive” to plan changes based on different bind values. The database monitors the behavior of a bind-sensitive cursor that uses different bind values to determine whether a different plan is beneficial.
The optimizer uses the following criteria to decide whether a cursor is bind-sensitive:
The optimizer has peeked at the bind values to generate cardinality estimates.
The bind is used in an equality or a range predicate.
For each execution of the query with a new bind value, the database records the execution statistics for the new value and compares them to the execution statistics for the previous value. If execution statistics vary greatly, then the database marks the cursor bind-aware.
Example 15-10 Column with Significant Data Skew
This example assumes that the hr.employees.department_id
column has significant data skew. SYSTEM
executes the following setup code, which adds 100,000 employees in department 50 to the employees
table in the sample schema, for a total of 100,107 rows, and then gathers table statistics:
DELETE FROM hr.employees WHERE employee_id > 999; ALTER TABLE hr.employees DISABLE NOVALIDATE CONSTRAINT emp_email_uk; DECLARE v_counter NUMBER(7) := 1000; BEGIN FOR i IN 1..100000 LOOP INSERT INTO hr.employees VALUES (v_counter,null,'Doe','Doe@example.com',null,'07-JUN-02','AC_ACCOUNT',null,null,null,50); v_counter := v_counter + 1; END LOOP; END; / COMMIT; EXEC DBMS_STATS.GATHER_TABLE_STATS ( ownname => 'hr', tabname => 'employees'); ALTER SYSTEM FLUSH SHARED_POOL;
The following query shows a histogram on the employees.department_id
column:
COL TABLE_NAME FORMAT a15 COL COLUMN_NAME FORMAT a20 COL HISTOGRAM FORMAT a9 SELECT TABLE_NAME, COLUMN_NAME, HISTOGRAM FROM DBA_TAB_COLS WHERE OWNER = 'HR' AND TABLE_NAME = 'EMPLOYEES' AND COLUMN_NAME = 'DEPARTMENT_ID'; TABLE_NAME COLUMN_NAME HISTOGRAM --------------- -------------------- --------- EMPLOYEES DEPARTMENT_ID FREQUENCY
Example 15-11 Low-Cardinality Query
This example continues the example in Example 15-10. The following query shows that the value 10
has extremely low cardinality for the column department_id
, occupying .00099% of the rows:
VARIABLE dept_id NUMBER EXEC :dept_id := 10; SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id; COUNT(*) MAX(EMPLOYEE_ID) ---------- ---------------- 1 200
The optimizer chooses an index range scan, as expected for such a low-cardinality query:
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR); PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID a9upgaqqj7bn5, child number 0 ------------------------------------- select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id Plan hash value: 1642965905 ------------------------------------------------------------------------------------- | Id| Operation | Name |Rows|Bytes|Cost (%CPU)|Time | ------------------------------------------------------------------------------------- | 0| SELECT STATEMENT | | | |2(100)| | | 1| SORT AGGREGATE | |1 |8 | | | | 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 |8 |2 (0)|00:00:01| |*3| INDEX RANGE SCAN | EMP_DEPARTMENT_IX |1 | |1 (0)|00:00:01| ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPARTMENT_ID"=:DEPT_ID)
The following query of V$SQL
obtains information about the cursor:
COL BIND_AWARE FORMAT a10 COL SQL_TEXT FORMAT a22 COL CHILD# FORMAT 99999 COL EXEC FORMAT 9999 COL BUFF_GETS FORMAT 999999999 COL BIND_SENS FORMAT a9 COL SHAREABLE FORMAT a9 SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE FROM V$SQL WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT CHILD# EXEC BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE ---------------------- ------ ----- ---------- --------- ---------- --------- SELECT COUNT(*), MAX(e 0 1 196 Y N Y mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id
The preceding output shows one child cursor that has been executed once for the low-cardinality query. The cursor has been marked bind-sensitive because the optimizer believes the optimal plan may depend on the value of the bind variable.
When a cursor is marked bind-sensitive, Oracle Database monitors the behavior of the cursor using different bind values, to determine whether a different plan for different bind values is more efficient. The database marked this cursor bind-sensitive because the optimizer used the histogram on the department_id
column to compute the selectivity of the predicate WHERE department_id = :dept_id
. Because the presence of the histogram indicates that the column is skewed, different values of the bind variable may require different plans.
Example 15-12 High-Cardinality Query
This example continues the example in Example 15-11. The following code re-executes the same query using the value 50
, which occupies 99.9% of the rows:
EXEC :dept_id := 50; SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id; COUNT(*) MAX(EMPLOYEE_ID) ---------- ---------------- 100045 100999
Even though such an unselective query would be more efficient with a full table scan, the optimizer chooses the same index range scan used for department_id=10
. This reason is that the database assumes that the existing plan in the cursor can be shared:
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR); PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID a9upgaqqj7bn5, child number 0 ------------------------------------- SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id Plan hash value: 1642965905 ------------------------------------------------------------------------------------- | Id| Operation | Name |Rows|Bytes|Cost (%CPU)|Time | ------------------------------------------------------------------------------------- | 0| SELECT STATEMENT | | | |2(100)| | | 1| SORT AGGREGATE | |1 |8 | | | | 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 |8 |2 (0)|00:00:01| |*3| INDEX RANGE SCAN | EMP_DEPARTMENT_IX |1 | |1 (0)|00:00:01| ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPARTMENT_ID"=:DEPT_ID)
A query of V$SQL
shows that the child cursor has now been executed twice:
SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE FROM V$SQL WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT CHILD# EXEC BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE ---------------------- ------ ----- ---------- --------- ---------- --------- SELECT COUNT(*), MAX(e 0 2 1329 Y N Y mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id
At this stage, the optimizer has not yet marked the cursor as bind-aware.
See Also:
Oracle Database Reference to learn about V$SQL
A bind-aware cursor is a bind-sensitive cursor that is eligible to use different plans for different bind values. After a cursor has been made bind-aware, the optimizer chooses plans for future executions based on the bind value and its cardinality estimate. Thus, “bind-aware” means essentially “best plan for the current bind value.”
When a statement with a bind-sensitive cursor executes, the optimizer uses an internal algorithm to determine whether to mark the cursor bind-aware. The decision depends on whether the cursor produces significantly different data access patterns for different bind values, resulting in a performance cost that differs from expectations.
If the database marks the cursor bind-aware, then the next time that the cursor executes the database does the following:
Generates a new plan based on the bind value
Marks the original cursor generated for the statement as not sharable (V$SQL.IS_SHAREABLE
is N
). The original cursor is no longer usable and is eligible to age out of the library cache
When the same query repeatedly executes with different bind values, the database adds new bind values to the “signature” of the SQL statement (which includes the optimizer environment, NLS settings, and so on), and categorizes the values. The database examines the bind values, and considers whether the current bind value results in a significantly different data volume, or whether an existing plan is sufficient. The database does not need to create a new plan for each new value.
Consider a scenario in which you execute a statement with 12 distinct bind values (executing each distinct value twice), which causes the database to trigger 5 hard parses, and create 2 additional plans. Because the database performs 5 hard parses, it creates 5 new child cursors, even though some cursors have the same execution plan as existing cursors. The database marks the superfluous cursors as not usable, which means these cursors eventually age out of the library cache.
During the initial hard parses, the optimizer is essentially mapping out the relationship between bind values and the appropriate execution plan. After this initial period, the database eventually reaches a steady state. Executing with a new bind value results in picking the best child cursor in the cache, without requiring a hard parse. Thus, the number of parses does not scale with the number of different bind values.
Example 15-13 Bind-Aware Cursors
This example continues the example in "Bind-Sensitive Cursors". The following code issues a second query employees
with the bind variable set to 50
:
EXEC :dept_id := 50; SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id; COUNT(*) MAX(EMPLOYEE_ID) ---------- ---------------- 100045 100999
During the first two executions, the database was monitoring the behavior of the queries, and determined that the different bind values caused the queries to differ significantly in cardinality. Based on this difference, the database adapts its behavior so that the same plan is not always shared for this query. Thus, the optimizer generates a new plan based on the current bind value, which is 50
:
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR); PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID a9upgaqqj7bn5, child number 1 ------------------------------------- SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id Plan hash value: 1756381138 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost(%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | |254 (100)| | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| EMPLOYEES | 100K | 781K |254 (15)| 00:00:01 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("DEPARTMENT_ID"=:DEPT_ID)
The following query of V$SQL
obtains information about the cursor:
SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE FROM V$SQL WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT CHILD# EXEC BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE ---------------------- ------ ----- ---------- --------- ---------- --------- SELECT COUNT(*), MAX(e 0 2 1329 Y N N mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id SELECT COUNT(*), MAX(e 1 1 800 Y Y Y mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id
The preceding output shows that the database created an additional child cursor (CHILD#
of 1
). Cursor 0
is now marked as not shareable. Cursor 1
shows a number of buffers gets lower than cursor 0
, and is marked both bind-sensitive and bind-aware. A bind-aware cursor may use different plans for different bind values, depending on the selectivity of the predicates containing the bind variable.
Example 15-14 Bind-Aware Cursors: Choosing the Optimal Plan
This example continues the example in "Example 15-13". The following code executes the same employees
query with the value of 10
, which has extremely low cardinality (only one row):
EXEC :dept_id := 10; SELECT COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id; COUNT(*) MAX(EMPLOYEE_ID) ---------- ---------------- 1 200
The following output shows that the optimizer picked the best plan, which is an index scan, based on the low cardinality estimate for the current bind value of 10
:
SQL> SELECT * from TABLE(DBMS_XPLAN.DISPLAY_CURSOR); PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID a9upgaqqj7bn5, child number 2 ------------------------------------- select COUNT(*), MAX(employee_id) FROM hr.employees WHERE department_id = :dept_id Plan hash value: 1642965905 ------------------------------------------------------------------------------------- | Id| Operation | Name |Rows|Bytes|Cost (%CPU)|Time | ------------------------------------------------------------------------------------- | 0| SELECT STATEMENT | | | |2(100)| | | 1| SORT AGGREGATE | |1 |8 | | | | 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 |8 |2 (0)|00:00:01| |*3| INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 1| |1 (0)|00:00:01| ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DEPARTMENT_ID"=:DEPT_ID)
The V$SQL
output now shows that three child cursors exist:
SELECT SQL_TEXT, CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC, BUFFER_GETS AS BUFF_GETS, IS_BIND_SENSITIVE AS BIND_SENS, IS_BIND_AWARE AS BIND_AWARE, IS_SHAREABLE AS SHAREABLE FROM V$SQL WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT CHILD# EXEC BUFF_GETS BIND_SENS BIND_AWARE SHAREABLE ---------------------- ------ ----- ---------- --------- ---------- --------- SELECT COUNT(*), MAX(e 0 2 1329 Y N N mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id SELECT COUNT(*), MAX(e 1 1 800 Y Y Y mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id SELECT COUNT(*), MAX(e 2 1 3 Y Y Y mployee_id) FROM hr.em ployees WHERE departme nt_id = :dept_id
The database discarded the original cursor (CHILD#
of 0
) when the cursor switched to bind-aware mode. This is a one-time overhead. The database marked cursor 0
as not shareable (SHAREABLE
is N
), which means that this cursor is unusable and will be among the first to age out of the cursor cache.
See Also:
Oracle Database Reference to learn about V$SQL
If the optimizer creates a plan for a bind-aware cursor, and if this plan is the same as an existing cursor, then the optimizer can perform cursor merging. In this case, the database merges cursors to save space in the library cache. The database increases the selectivity range for the cursor to include the selectivity of the new bind value.
When a query uses a new bind variable, the optimizer tries to find a cursor that it thinks is a good fit based on similarity in the selectivity of the bind value. If the database cannot find such a cursor, then it creates a new one (see Example 15-10). If the plan for the new cursor is the same as one of the existing cursors, then the database merges the two cursors to save space in the library cache. The merge results in the database marking one cursor as not shareable. If the library cache is under space pressure, then the database ages out the non-shareable cursor first.
You can use the V$
views for adaptive cursor sharing to see selectivity ranges, cursor information (such as whether a cursor is bind-aware or bind-sensitive), and execution statistics:
V$SQL
shows whether a cursor is bind-sensitive or bind-aware.
V$SQL_CS_HISTOGRAM
shows the distribution of the execution count across a three-bucket execution history histogram.
V$SQL_CS_SELECTIVITY
shows the selectivity ranges stored for every predicate containing a bind variable if the selectivity was used to check cursor sharing. It contains the text of the predicates, and the low and high values for the selectivity ranges.
V$SQL_CS_STATISTICS
summarizes the information that the optimizer uses to determine whether to mark a cursor bind-aware. For a sample of executions, the database tracks the rows processed, buffer gets, and CPU time. The PEEKED
column shows YES
when the bind set was used to build the cursor; otherwise, the value is NO
.
See Also:
Oracle Database Reference to learn about V$SQL
and its related views
This section contains the following topics:
The Real-World Performance group strongly suggests that all enterprise applications use bind variables. Oracle Database applications were intended to be written with bind variables. Avoid application designs that result in large numbers of users issuing dynamic, unshared SQL statements.
Whenever Oracle Database fails to find a match for a statement in the library cache, it must perform a hard parse. Despite the dangers of developing applications with literals, not all real-world applications use bind variables. Developers sometimes find that it is faster and easier to write programs that use literals. However, decreased development time does not lead to better performance and security after deployment.
Video:
See RWP #3: Connection Pools and Hard Parsing for a demo illustrating the performance problems caused by hard parsing
The primary benefits of using bind variables are as follows:
Resource efficiency
Compiling a program before every execution does not use resources efficiently, but this is essentially what Oracle Database does when it performs a hard parse. The database server must expend significant CPU and memory to create cursors, generate and evaluate execution plans, and so on. By enabling the database to share cursors, soft parsing consumes far fewer resources. If an application uses literals instead of bind variables, but executes only a few queries each day, then DBAs may not perceive the extra overhead as a performance problem. However, if an application executes hundreds or thousands of queries per second, then the extra resource overhead can easily degrade performance to unacceptable levels. Using bind variables enables the database to perform a hard parse only once, no matter how many times the statement executes.
Scalability
When the database performs a hard parse, the database spends more time acquiring and holding latches in the shared pool and library cache. Latches are low-level serialization devices. The longer and more frequently the database latches structures in shared memory, the longer the queue for these latches becomes. When multiple statements share the same execution plan, the requests for and durations of latches goes down, which increases scalability.
Throughput and response time
When the database avoids constantly reparsing and creating cursors, more of its time is spent in user space. The Real-World Performance group has found that changing literals to use binds often leads to orders of magnitude improvements in throughput and user response time.
Video:
See RWP #4: Bind Variables and Soft Parsing for a demo showing the performance benefits of bind variables
Security
The only way to prevent SQL injection attacks is to use bind variables. Malicious users can exploit application that concatenate strings by “injecting” code into the application.
See Also:
Oracle Database PL/SQL Language Reference for an example of an application that fixes a security vulnerability created by literals
The best practice is to write sharable SQL and use the default of EXACT
for CURSOR_SHARING
. However, for applications with many similar statements, setting CURSOR_SHARING
to FORCE
can sometimes significantly improve cursor sharing. The replacement of literals with system-generated bind values can lead to reduced memory usage, faster parses, and reduced latch contention. However, FORCE
is not meant to be a permanent development solution.
As a general guideline, the Real-World Performance group recommends against setting CURSOR_SHARING
to FORCE
exception in rare situations, and then only when all of the following conditions are met:
Statements in the shared pool differ only in the values of literals.
Response time is suboptimal because of a very high number of library cache misses.
Your existing code has a serious security and scalability bug—the absence of bind variables—and you need a temporary band-aid until the source code can be fixed.
You set this initialization parameter at the session level and not at the instance level.
Setting CURSOR_SHARING
to FORCE
has the following drawbacks:
It indicates that the application does not use user-defined bind variables, which means that it is open to SQL injection. Setting CURSOR_SHARING
to FORCE
does not fix SQL injection bugs or render the code any more secure. The database binds values only after any malicious SQL text has already been injected.
Video:
See RWP #4: Bind Variables and Soft Parsing for an AWR analysis by the Real-World Performance group of an application that uses FORCE
.
The database must perform extra work during the soft parse to find a similar statement in the shared pool.
The database removes every literal, which means that it can remove useful information. For example, the database strips out literal values in SUBSTR
and TO_DATE
functions. The use of system-generated bind variables where literals are more optimal can have a negative impact on execution plans.
There is an increase in the maximum lengths (as returned by DESCRIBE
) of any selected expressions that contain literals in a SELECT
statement. However, the actual length of the data returned does not change.
Star transformation is not supported.
See Also:
Oracle Database Reference to learn about the CURSOR_SHARING
initialization parameter
By default, any variation in the text of two SQL statements prevents the database from sharing a cursor, including the names of bind variables. Also, changes in the size of bind variables can cause cursor mismatches. For this reason, using bind variables in application code is not enough to guarantee cursor sharing.
The Real-World Performance group recommends that you standardize spacing and capitalization conventions for SQL statements and PL/SQL blocks. Also establish conventions for the naming and definitions of bind variables. If the database does not share cursors as expected, begin your diagnosis by querying V$SQL_SHARED_CURSOR
.
Example 15-15 Variations in SQL Text
In this example, an application that uses bind variables executes 7 statements using the same bind variable value, but the statements are not textually identical:
VARIABLE emp_id NUMBER EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :EMP_ID; EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :Emp_Id; EXEC :emp_id := 101; SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id; EXEC :emp_id := 101; select sum(salary) from hr.employees where employee_id < :emp_id; EXEC :emp_id := 101; Select sum(salary) From hr.employees Where employee_id < :emp_id; EXEC :emp_id := 101; Select sum(salary) From hr.employees Where employee_id< :emp_id;
A query of V$SQLAREA
shows that no cursor sharing occurred:
COL SQL_TEXT FORMAT a35 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ----------------------------------- ------------- ------------- ---------- SELECT SUM(salary) FROM hr.employee bkrfu3ggu5315 1 3751971877 s WHERE employee_id < :EMP_ID SELECT SUM(salary) FROM hr.employee 70mdtwh7xj9gv 1 265856507 s WHERE employee_id < :Emp_Id Select sum(salary) From hr.employee 18tt4ny9u5wkt 1 2476929625 s Where employee_id< :emp_id SELECT SUM(salary) FROM hr.employe b6b21tbyaf8aq 1 4238811478 es WHERE employee_id < :emp_id SELECT SUM(salary) FROM hr.employee 4318cbskba8yh 1 615850960 s WHERE employee_id < :emp_id select sum(salary) from hr.employee 633zpx3xm71kj 1 4214457937 s where employee_id < :emp_id Select sum(salary) From hr.employee 1mqbbbnsrrw08 1 830205960 s Where employee_id < :emp_id 7 rows selected.
Example 15-16 Bind Length Mismatch
The following code defines a bind variable with different lengths, and then executes textually identical statements with the same bind values:
VARIABLE lname VARCHAR2(20) EXEC :lname := 'Taylor'; SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname; VARIABLE lname VARCHAR2(100) EXEC :lname := 'Taylor'; SELECT SUM(salary) FROM hr.employees WHERE last_name = :lname;
The following query shows that the database did not share the cursor:
COL SQL_TEXT FORMAT a35 SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE FROM V$SQLAREA WHERE SQL_TEXT LIKE '%mployee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%'; SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE ----------------------------------- ------------- ------------- ---------- SELECT SUM(salary) FROM hr.employee buh8j4557r0h1 2 1249608193 s WHERE last_name = :lname
The reason is because of the bind lengths:
COL BIND_LENGTH_UPGRADEABLE FORMAT a15 SELECT s.SQL_TEXT, s.CHILD_NUMBER, c.BIND_LENGTH_UPGRADEABLE FROM V$SQL s, V$SQL_SHARED_CURSOR c WHERE SQL_TEXT LIKE '%employee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' AND s.CHILD_ADDRESS = c.CHILD_ADDRESS; SQL_TEXT CHILD_NUMBER BIND_LENGTH_UPG ----------------------------------- ------------ --------------- SELECT SUM(salary) FROM hr.employee 0 N s WHERE last_name = :lname SELECT SUM(salary) FROM hr.employee 1 Y s WHERE last_name = :lname
A best practice is to prevent users of the application from changing the optimization approach and goal for their individual sessions. Any changes to the optimizer environment can prevent otherwise identical statements from sharing cursors.
Example 15-17 Environment Mismatches
This example shows two textually identical statements that nevertheless do not share a cursor:
VARIABLE emp_id NUMBER EXEC :emp_id := 110; ALTER SESSION SET OPTIMIZER_MODE = FIRST_ROWS; SELECT salary FROM hr.employees WHERE employee_id < :emp_id; ALTER SESSION SET OPTIMIZER_MODE = ALL_ROWS; SELECT salary FROM hr.employees WHERE employee_id < :emp_id;
A query of V$SQL_SHARED_CURSOR
shows a mismatch in the optimizer modes:
SELECT S.SQL_TEXT, S.CHILD_NUMBER, s.CHILD_ADDRESS, C.OPTIMIZER_MODE_MISMATCH FROM V$SQL S, V$SQL_SHARED_CURSOR C WHERE SQL_TEXT LIKE '%employee%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' AND S.CHILD_ADDRESS = C.CHILD_ADDRESS; SQL_TEXT CHILD_NUMBER CHILD_ADDRESS O ----------------------------------- ------------ ---------------- - SELECT salary FROM hr.employees WHE 0 0000000080293040 N RE employee_id < :emp_id SELECT salary FROM hr.employees WHE 1 000000008644E888 Y RE employee_id < :emp_id