Understanding Bind Variable Peeking & CURSOR_SHARING Parameter In Oracle Database

Cursor

Bind Variable Peeking

Introduced in 9i,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 plan may not be optimal for all possible bind values.

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

SQL> SET AUTOTRACE ON EXPLAIN

--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 index range scan plan for the first two statements, but a full table scan plan for the statement using literal 165.

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 101;

SUM(SALARY)
-----------
      24000

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 120;

SUM(SALARY)
-----------
     163308

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<120)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 165;

SUM(SALARY)
-----------
     445608

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
| * 2 |   TABLE ACCESS FULL                  | EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("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.   

--This example rewrites the query 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.The DISPLAY_CURSOR output shows that the optimizer chose exactly the same plan for all three statements.

SQL> VAR emp_id NUMBER;

SQL> EXEC :emp_id := 101;

PL/SQL procedure successfully completed.

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < :emp_id;

SUM(SALARY)
-----------
      24000

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<TO_NUMBER(:EMP_ID))

SQL> EXEC :emp_id := 120;

PL/SQL procedure successfully completed.

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < :emp_id;

SUM(SALARY)
-----------
     163308

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<TO_NUMBER(:EMP_ID))

SQL> EXEC :emp_id := 165;

PL/SQL procedure successfully completed.

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < :emp_id;

SUM(SALARY)
-----------
     445608

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<TO_NUMBER(: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.

CURSOR_SHARING Parameter

In Oracle, the CURSOR_SHARING initialization parameter determines how the database handles SQL statements that are similar but not identical (e.g., they differ only in literal values). Its primary purpose is to improve performance by allowing multiple sessions to share the same execution plan, thereby reducing “hard parses” that can cause high CPU usage and latch contention.

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.

SIMILAR :-
Causes statements that may differ in some literals, but are otherwise identical, to share a cursor, unless the literals affect either the meaning of the statement or the degree to which the plan is optimized.

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.

Parameter type           String 
Syntax                   CURSOR_SHARING = { SIMILAR | EXACT | FORCE } 
Default value            EXACT 
Modifiable               ALTER SESSION/ALTER SYSTEM 
Basic                    No 

You can set CURSOR_SHARING at the system or session level, or use the CURSOR_SHARING_EXACT hint at the statement level.

SQL> SHOW parameter CURSOR_SHARING

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cursor_sharing                       string      EXACT

SQL>  ALTER SYSTEM FLUSH SHARED_POOL;

System altered.

SQL> SET AUTOTRACE ON EXPLAIN

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 101;

SUM(SALARY)
-----------
      24000

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593
------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 120;

SUM(SALARY)
-----------
     163308

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<120)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 165;

SUM(SALARY)
-----------
     445608

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<165)
   
SQL> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,HASH_VALUE
  2  FROM   V$SQLAREA
  3  WHERE  SQL_TEXT LIKE '%employees%'
  4  AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                                                   SQL_ID        VERSION_COUNT HASH_VALUE
---------------------------------------------------------- -------------- ------------ -----------
SELECT SUM(salary) FROM employees WHERE employee_id < 165  12rgh61pdj8hp       1       1792582165
SELECT SUM(salary) FROM employees WHERE employee_id < 120  7xstcck4uaspb       1       2309317291
SELECT SUM(salary) FROM employees WHERE employee_id < 101  490j6jm5x334q       1       3419507862

SQL> ALTER SESSION SET CURSOR_SHARING=FORCE;

Session altered.

SQL> SHOW parameter CURSOR_SHARING

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
cursor_sharing                       string      FORCE

SQL>  ALTER SYSTEM FLUSH SHARED_POOL;

System altered.

SQL> SET AUTOTRACE ON EXPLAIN

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 101;

SUM(SALARY)
-----------
      24000

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<101)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 120;

SUM(SALARY)
-----------
     163308

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<120)

SQL> SELECT SUM(salary) FROM employees WHERE employee_id < 165;

SUM(SALARY)
-----------
     445608

Execution Plan
----------------------------------------------------------
Plan hash value: 2410354593

------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |     1 |    26 |     0   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |               |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES     |     1 |    26 |     0   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | EMP_EMP_ID_PK |     1 |       |     0   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPLOYEE_ID"<165)
   
--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.

--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.
   
SQL> SELECT SQL_TEXT,SQL_ID,VERSION_COUNT,HASH_VALUE
  2  FROM   V$SQLAREA
  3  WHERE  SQL_TEXT LIKE '%employees%'
  4  AND    SQL_TEXT NOT LIKE '%SQL_TEXT%';

SQL_TEXT                                                            SQL_ID       VERSION_COUNT  HASH_VALUE
----------------------------------------------------------------- ----------------------------- ----
SELECT SUM(salary) FROM employees WHERE employee_id < :"SYS_B_0"  cbh4a95wf6r79       1        2028166377

Shoumadip Das

Hi Folks, I am Shoumadip Das hailing from Kolkata, India welcoming you to explore my blog www.oraclemasterpiece.com. I am a self motivated and successful professional working in a leading IT Giant for more than 10 years.My area of expertise includes –

  • Oracle Database Programming (SQL & PL/SQL)
  • Perl Programming
  • Unix Programming

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *