Reprinted with Permission by Quest Software Sept.  2001

The Power of Cursor Sharing in 9i
By: Szabo Imre

Sql statements differ only in literals can be forced to share a cursor. This is a new feature in 8i and can be switched on with the parameter setting cursor_sharing=force. This is accomplished by transforming the statement into a new one by replacing the literals with system generated bind variables.  The problem with bind variables is that the optimizer cannot predict precise selectivity.

Using cursor_sharing = force will parse the FIRST sql statement loaded into memory and will use this execution plan for the subsequent similar sql statements. This might be a problem if the FIRST statement's literals have good selectivity but those of the subsequent statements don't. In this case the statements with poor selectivity will also use the index whereas a full table scan would perform better. However, if the firstly run statement doesn't have good selectivity and a full table scan is used, the subequent  similar statements will also use FTS.

To overcome this problem in 9i we can use cursor_sharing=similar setting which makes the optimizer examine the histograms associated with the columns.

We can examine the execution plans of parsed sqls with the use of v$sql_plan view. I used this view to check the cursor sharing behavior.

Short example:

SQL> desc bigemp

 Name                                      Null?    Type
 ----------------------------------------- -------- -------------------
 EMPNO                                              NUMBER(4)
 ENAME                                              VARCHAR2(10)
 JOB                                                VARCHAR2(9)
 MGR                                                NUMBER(4)
 HIREDATE                                           DATE
 SAL                                                NUMBER(7,2)
 COMM                                               NUMBER(7,2)
 DEPTNO                                             NUMBER(2)
 PKCOL                                              NUMBER


 220000 rows are in the table.
 110000 rows have unique values for PKCOL and 110000 rows have the value of 100 (half
of the table).

 PKCOL column has a non-unique index with histogram. 

The statements to be run are:

select * from bigemp where pkcol = 100;
select * from bigemp where pkcol = 1000;

Autotrace shows:

SQL> set autotrace traceonly explain
SQL> select * from bigemp where pkcol = 100;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=814 Card=119276 Bytes=4890316)
   1    0   TABLE ACCESS (FULL) OF 'BIGEMP' (Cost=814 Card=119276 Bytes=4890316)

Full table scan is advantageous because half of the table data will be returned.

SQL> select * from bigemp where pkcol = 1000;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=41)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'BIGEMP' (Cost=6 Card=1 Bytes=41)
   2    1     INDEX (RANGE SCAN) OF 'PK_NU' (NON-UNIQUE) (Cost=4 Card=1)

   Small amount of data (1 row), index access is beneficial.
Select statement to examine parsed sqls' execution plan:
  select s.sql_text, p.operation, p.options, p.object_name, p.optimizer
  from v$sql s, v$sql_plan p
  where s.address = p.address
  and s.hash_value = p.hash_value
  and s.child_number = p.child_number
  and sql_text like 'select * from bigemp%';
cursor_sharing = exact
SQL_TEXT
--------------------------------------------------------------------------
 OPERATION                      OPTIONS              OBJECT_NAM OPTIMIZER
 ------------------------------ -------------------- ---------- ----------
select * from bigemp where pkcol = 1000
 SELECT STATEMENT                                               CHOOSE

select * from bigemp where pkcol = 1000
  TABLE ACCESS                  BY INDEX ROWID       BIGEMP     ANALYZED

select * from bigemp where pkcol = 1000
   INDEX                        RANGE SCAN           PK_NU      ANALYZED


SQL_TEXT
--------------------------------------------------------------------------
 OPERATION                      OPTIONS              OBJECT_NAM OPTIMIZER
 ------------------------------ -------------------- ---------- ----------
select * from bigemp where pkcol = 100
 SELECT STATEMENT                                               CHOOSE

select * from bigemp where pkcol = 100
  TABLE ACCESS                  FULL                 BIGEMP     ANALYZED

As it can be seen above using cursor_sharing = exact leaves cursors intact.

cursor_sharing = force
SQL_TEXT
--------------------------------------------------------------------------
 OPERATION                      OPTIONS              OBJECT_NAM OPTIMIZER
 ------------------------------ -------------------- ---------- ----------
select * from bigemp where pkcol = :"SYS_B_0"
 SELECT STATEMENT                                               CHOOSE

select * from bigemp where pkcol = :"SYS_B_0"
   INDEX                        RANGE SCAN           PK_NU      ANALYZED

select * from bigemp where pkcol = :"SYS_B_0"
  TABLE ACCESS                  BY INDEX ROWID       BIGEMP     ANALYZED

Cursor_sharing=force transforms literals into binds and all similar statments (differ only in literals) use the same execution plans. In this case 'select * from bigemp where pkcol = 1000;' was run first, this is why index access is used for all 'select * from bigemp where pkcol = :"SYS_B_0"' queries.

cursor_sharing = similar
SQL_TEXT
--------------------------------------------------------------------------
 OPERATION                      OPTIONS              OBJECT_NAM OPTIMIZER
 ------------------------------ -------------------- ---------- ----------
select * from bigemp where pkcol = :"SYS_B_0"
 SELECT STATEMENT                                               CHOOSE

select * from bigemp where pkcol = :"SYS_B_0"
  TABLE ACCESS                  BY INDEX ROWID       BIGEMP     ANALYZED

select * from bigemp where pkcol = :"SYS_B_0"
   INDEX                        RANGE SCAN           PK_NU      ANALYZED


SQL_TEXT
--------------------------------------------------------------------------
 OPERATION                      OPTIONS              OBJECT_NAM OPTIMIZER
 ------------------------------ -------------------- ---------- ----------
select * from bigemp where pkcol = :"SYS_B_0"
 SELECT STATEMENT                                               CHOOSE

select * from bigemp where pkcol = :"SYS_B_0"
  TABLE ACCESS                  FULL                 BIGEMP     ANALYZED

Cursor_sharing = similar also transforms the literals into binds, but the optimizer examines the statistics and may use different execution plans.

Make sure that statistics are available on the colulmns (PKCOL) to let optimizer make good decisions.

Be careful when setting this parameter it affects other optimization steps.

RevealNet Pipeline SYSOP Remarks:

Andrew Simkovsky

This looks pretty good. I did want to throw in one thing, but I don't know how relevant it is, since the article talks mainly to 9i. There is some bugs with CURSOR_SHARING that I know of up to at least 8.1.6.3 which causes ORA-0600 errors. Here's one bug for 8.1.6: [BUG:1365873] Here's another for 8.1.7:

fact: Oracle Server - Enterprise Edition 8.1.7
fact: INIT.ORA parameter CURSOR_SHARING=FORCE
symptom: Error performing a SELECT statement
symptom: ORA-12704 character set mismatch
change: Upgraded from Oracle Server 8.1.6 to Oracle Server 8.1.7
cause: Bug:1561329

fix:

Workaround: Change the INIT.ORA parameter CURSOR_SHARING to EXACT.

This maintains pre 8.1.6 behaviour for literal SQL statements in that literals are not replaced with bind variables and the literal values can be used by the CBO.

To make the change, edit the INIT.ORA file to include the line:

CURSOR_SHARING = EXACT

Note : This change can have an impact on performance. See Oracle8i Reference Guide, Page 1-23 CURSOR_SHARING Parameter, for more information.