Home>Question Details



Kamal -- Thanks for the question regarding "Bind Variable values available to Optimizer in Oracle9i", version 9.2.0.3

Submitted on 7-Jun-2003 19:09 Central time zone
Last updated 14-Sep-2006 11:17

You Asked

Hi Tom,
I read in the book "Oracle SQL Tuning - Pocket Reference" by Mark Gurry (O'Reilly) on 
page 4, that:

<quote>
"Oracle 9i determines the value of the bind variable prior to deciding on the execution 
plan."
</quote>

In this paragraph, he is talking about the "skewness problem" and ways to overcome that. 
At that point he mentions that Oracle9i will get rid of the "skewess" without using 
constant literals (you can safely use bind variables).

Again, On page 29, he mentions that:

<quote>
"Oracle9i will evaluate the bind variable value prior to deciding the execution plan, 
obviating the need to hard-code literal values."
</quote>

My problem in understanding this is that if Oracle9i uses the value of bind variable 
prior to deciding the execution plan, than for each new value of the bind varaible, it 
will have to redo the execution plan, thus using bind variable will be of no use, since 
the database will have to redo the execution plan for each new value anyway.

Is this not the same thing as NOT using bind variables at all and always passing in the 
unique queries to the database?

Maybe, I'm missing something that is not explained properly in his book? Maybe this is 
not the whole story and there is more to it than that?


But If Oracle9i does that, Is this available via the DBMS_SQL package?
My understanding is that PARSE (and execution plan generation) happens much before the 
binding. At PARSE time how will the optimizer get the actual values of the bind variables 
that the program will use later.

As I understand this, we do a DBMS_SQL.PARSE call where the query plan is decided and 
then we do a DBMS_SQL.BIND_VARIABLE to bind the values of the bind variables used in our 
Query. In this case, it will already have the plan decided, even before the values of the 
bind variables have been supplied. Does this mean, than, that this new feature of 
Oracle9i is NOT accessible yet via the DBMS_SQL package?

How would you code your application in order to make use of this new feature?
1). When using DBMS_SQL package?
2). When not using this package, in static and native dynamic SQL in PL/SQL?

Let me know if you need further information.
Thanks,
 

and we said...

He is discussing bind variable peeking, but if that is all he says, he has missed 90% of 
the discussion.


see
http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:7832114438832#991539182
6680

Oracle will peek at the bind variable value *before the first hard parse* and use that 
value in order to optimize the query plan.

Oracle will not peek at the bind variables during soft parses.  As demonstrated in the 
examples in that link
 

Reviews    
5 stars Excellent...   June 9, 2003 - 9am Central time zone
Reviewer: Kamal Kishore from New Jersey, USA
Hi Tom,
That discussion in the link was excellent. Explained the concept in a concise and clear manner.
The question that now pops up in my mind is:
Will Oracle9i do this "bind variable peeking" ALWAYS first time for a hard parse or will it do it 
only when it sees that the data might be skewed?

If it does it always, will it pose a problem for our queries when we migrate to Oracle9i (soon)?
The concern that I have is if the first hard parse decides the execution plan and it is cached, the 
subsequent executions for other data values may be terrible?

Is this a problem that we need to address?
If yes, what we could (or rather should) do to get around this issue?
Thanks,
 


Followup   June 9, 2003 - 10am Central time zone:

it happens with the CBO during a hard parse.

As for the 9i migration -- you have that right now, without bind variable peeking.  Consider the 
one example proposed in the link with "like".  without bind variable peeking, it would never 
consider the index see, with it, it will.  It goes both ways.  

But -- test test test -- that is something you need to do upon an upgrade....


 

5 stars What if compatible = 8.1.7 is set for the Oracle9i development server...   June 9, 2003 - 10am Central time zone
Reviewer: Kamal Kishore from New Jersey, USA
Hi Tom,
I just checked and found out that the development server that was migrated to Oracle9i some time 
ago (in the process to migrate the production, some day), has the compatible parameter set to 
8.1.7.
I'm assuming that this bind variable peeking will not be available when this parameter is set to 
this value?
Is this correct?

Do you see any problems with this setup? Do you suggest that the compatible parameter should 
ideally be set to something like 9.0 or 9.2?
What would be your choice in this case?

The argument is that since production is still 8.1.7, we should set the development server to 
compatible = 8.1.7 so as to avoid any problems during migration.

Is this the correct way to approach a migration from Oracle8i to Oracle9i?
Any suggestions? Are we going in the right direction?
Thanks,
 


Followup   June 9, 2003 - 12pm Central time zone:

compatible only affects "disk based structures" really -- it makes it so that if we come up with 
some new format for persisting data -- we won't use it (cause you cannot downgrade if we did).

features like bind variable peeking will work.

you should have the same setting on dev/test/prod.  Next upgrade cycle -- bump it up. 

4 stars Does bv peeking affect Card estimates?   July 28, 2003 - 3pm Central time zone
Reviewer: Dr. Donald K. Demetrious from New York, NY USA
Shouldn't bind variable peeking affect the Card estimates on the execution plan? In the following 
example, why is Card=1526 instead of Card=5?
From Oracle 9.2, with optimizer_features_enable set to 9.2.0:

SQL> create table test_cols as select owner, table_name, column_name from dba_tab_columns;
Table created.
SQL> create index test_cols_n01 on test_cols (column_name);
Index created.
SQL> analyze table test_cols compute statistics;
Table analyzed.
SQL> set autotrace traceonly explain
SQL> select count(*) from test_cols where column_name like 'CHA%';

Execution Plan
----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=10)
  SORT (AGGREGATE)
    INDEX (RANGE SCAN) OF 'TEST_COLS_N01' (NON-UNIQUE) (Cost=2 Card=5 Bytes=50)

SQL> variable p_test varchar2(30);
SQL> begin :p_test := 'CHA%'; end;
  2  /

PL/SQL procedure successfully completed.

SQL> select count(*) from test_cols where column_name like :p_test;

Execution Plan
----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=10)
  SORT (AGGREGATE)
    INDEX (RANGE SCAN) OF 'TEST_COLS_N01' (NON-UNIQUE) (Cost=2 Card=1526 Bytes=15260)
 


Followup   July 28, 2003 - 7pm Central time zone:

explain plan can lie, especially with bind variable peeking.

so, this is a little view I explain in my next book due out next month -- it is useful in 9i 
especially 9ir2 with dbms_xplan:

ops$tkyte@ORA920> create or replace view dynamic_plan_table
  2  as
  3  select
  4   rawtohex(address) || '_' || child_number statement_id,
  5   sysdate timestamp, operation, options, object_node,
  6   object_owner, object_name, 0 object_instance,
  7   optimizer,  search_columns, id, parent_id, position,
  8   cost, cardinality, bytes, other_tag, partition_start,
  9   partition_stop, partition_id, other, distribution,
 10   cpu_cost, io_cost, temp_space, access_predicates,
 11   filter_predicates
 12   from v$sql_plan;

View created.

now, we'll run your simulation:

ops$tkyte@ORA920> drop table t;
Table dropped.

ops$tkyte@ORA920> create table t as select owner, table_name, column_name from dba_tab_columns;
Table created.

ops$tkyte@ORA920> create index t_idx on t(column_name);
Index created.

ops$tkyte@ORA920> analyze table t compute statistics;
Table analyzed.

ops$tkyte@ORA920> set autotrace on explain
ops$tkyte@ORA920> select count(*) from t where column_name like 'CHA%';

  COUNT(*)
----------
       215


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=1 Bytes=10)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=10 Card=2624 Bytes=26240)


so the estimated cardinality in the plan was 2,624 -- but using a bind:

ops$tkyte@ORA920> variable p_test varchar2(30)
ops$tkyte@ORA920> exec :p_test := 'CHA%'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> select /* LOOK FOR ME!!! */ count(*) from t where column_name like :p_test;

  COUNT(*)
----------
       215


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=10)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=3 Card=1742 Bytes=17420)

it appears to be 1,742, but looking at the REAL plan used:


ops$tkyte@ORA920> set autotrace off
ops$tkyte@ORA920>
ops$tkyte@ORA920> select plan_table_output
  2    from TABLE( dbms_xplan.display
  3                ( 'dynamic_plan_table',
  4                  (select rawtohex(address)||'_'||child_number x
  5                     from v$sql
  6  where sql_text like 'select /* LOOK FOR ME!!! */ count(*) from t where column_name like 
:p_test%' ),
  7                  'serial' ) )
  8  /

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
-------------------------------

-------------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |             |       |       |            |
|   1 |  SORT AGGREGATE      |             |     1 |    10 |            |
|*  2 |   INDEX RANGE SCAN   | T_IDX       |  2624 | 26240 |    10  (10)|
-------------------------------------------------------------------------

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

   2 - access("COLUMN_NAME" LIKE :P_TEST)
       filter("COLUMN_NAME" LIKE :P_TEST)

14 rows selected.

we see the 2,624 again.  explain plan doesn't "see" the peeked binds

 

5 stars bind variable   October 16, 2003 - 10am Central time zone
Reviewer: Pushparaj Arulappan from NJ, USA
Tom,

I have a SQL which is running very fast from the SQL PROMPT
but if I put the same SQL in a PL/SQL block, it is running
very very slow. 

SQL> var var1 char(10);
SQL> var var2 char(7);
SQL> exec :var1 := '1234567890';
SQL> exec :var2 := '1234567';

SQL> SELECT ....
     ....
     FROM tab1 a, 
          tab2 b
          ( SELECT ... 
            FROM tab1 x,
                 tab3 y
            WHERE x.col1 = :var1
            AND   x.col2 = :var2
            AND   x.col1 = y.col1
            AND   x.col2 = y.col2
            ....
          ) zz
     WHERE a.col1 = :var1
     AND   a.col2 = :var2
     AND   a.col1 = b.col1
     AND   a.col2 = b.col2
     AND   zz.col1 = a.col1
     AND   zz.col2 = a.col2
     .....
     
This executes very fast...

But If I convert this into a PL/SQL program..

SQL> 
DECLARE
   var1 CHAR(10);
   var2 CHAR(7);
     
   CURSOR C1 IS
      SELECT ....
             ....
      FROM tab1 a, 
           tab2 b
           (SELECT ... 
            FROM tab1 x,
                 tab3 y
            WHERE x.col1 = var1
            AND   x.col2 = var2
            AND   x.col1 = y.col1
            AND   x.col2 = y.col2
            ....
            ) zz
      WHERE a.col1 = var1
      AND   a.col2 = var2
      AND   a.col1 = b.col1
      AND   a.col2 = b.col2
      AND   zz.col1 = a.col1
      AND   zz.col2 = a.col2
      .....

BEGIN
   var1 := '1234567890';
   var2 := '12345678';

   FOR rec IN c1 LOOP
     ....
   END LOOP;
END;
/

This is running for ever and ever and I have to kill it..


I also want to tell you that the same works fine in SQL prompt and also in PL/SQL if the WHERE 
clause value is different which will match less rows and retrieve very less rows.

This is only a concern for the WHERE class values
which will join many rows and retrieve many rows.

What I should do to this SQL to perform the same way
in a PL/SQL block as it does in the SQL prompt for the WHERE
clause values that will match and retrieve many rows.

Thanks
Pushparaj 


Followup   October 16, 2003 - 12pm Central time zone:

where be the tkprof reports that would show us the different in execution performance and query 
plans used? 

5 stars trace out   October 16, 2003 - 2pm Central time zone
Reviewer: Pushparaj Arulappan from NJ, USA
Tom,

The following is the trace output when I run it from the
SQL prompt. The SELECT columns are more, hence I only
put the FROM and WHERE clause of the original query here.

    FROM    TTSTCPK n,TTKLMTA x
    WHERE   x.nbn_num= :pNbnNum
    and     x.sgp_num= :pSgpNum
    and     n.nbn_num=x.nbn_num
    and     n.sgp_num=x.sgp_num
    and     n.pspl_num=x.pspl_num
    and     n.psps_num=x.psps_num
    GROUP BY x.nbn_num,
             x.sgp_num,
             x.pspl_num,
             x.psps_num) t
    WHERE   a.nbn_num        =   :pNbnNum
    and     a.sgp_num        =   :pSgpNum
    and     a.nbn_num        =   b.nbn_num
    and     a.sgp_num        =   b.sgp_num
    and     a.pspl_num =   b.pspl_num
    and     b.nbn_num        =   t.nbn_num
    and     b.sgp_num        =   t.sgp_num
    and     b.pspl_num =   t.pspl_num
    and     b.psps_num    =   t.psps_num

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      111     18.68      18.27          0     227237          0        1643
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      113     18.68      18.27          0     227237          0        1643
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 28  (SALES1)

Rows     Row Source Operation
-------  ---------------------------------------------------
   1643  SORT GROUP BY (cr=227237 r=0 w=0 time=18259319 us)
   4929   NESTED LOOPS  (cr=227237 r=0 w=0 time=18152455 us)
   4929    NESTED LOOPS  (cr=123726 r=0 w=0 time=11201943 us)
   4929     NESTED LOOPS  (cr=108937 r=0 w=0 time=11096958 us)
   4929      TABLE ACCESS BY INDEX ROWID TTKLMTA (cr=497 r=0 w=0 time=22826 us)
   4929       INDEX RANGE SCAN IND_TTKLMTA (cr=60 r=0 w=0 time=5600 us)(object id 391566)
   4929      TABLE ACCESS BY INDEX ROWID TTSTCPK (cr=108440 r=0 w=0 time=11051602 us)
   4929       INDEX RANGE SCAN IND_TTSTCPK (cr=103511 r=0 w=0 time=11016616 us)(object id 391563)
   4929     TABLE ACCESS BY INDEX ROWID TTCOLFP (cr=14789 r=0 w=0 time=84542 us)
   4929      INDEX RANGE SCAN IND_TTCOLFP (cr=9860 r=0 w=0 time=53351 us)(object id 195300)
   4929    INDEX RANGE SCAN IND_TTSTCPK (cr=103511 r=0 w=0 time=6933006 us)(object id 391563)


Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
   1643   SORT (GROUP BY)
   4929    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF 'TTSTCPK'
   4929     NESTED LOOPS
   4929      MERGE JOIN (CARTESIAN)
   4929       NESTED LOOPS
   4929        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTKLMTA'
   4929         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTKLMTA'
                    (UNIQUE)
   4929        INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK'
                   (UNIQUE)
   4929       BUFFER (SORT)
   4929        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTCOLFP'
   4929         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTCOLFP'
                    (UNIQUE)
      0      INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK' (UNIQUE)


********************************************************************************


But Tom, the PL/SQL program is still running since morning and it is not complete yet and I will 
provide whatever so far in the trace file and you can see the change in the execution plan already.

FROM    TTSTCPK n,TTKLMTA x
    WHERE   x.nbn_num= :b2
    and     x.sgp_num= :b1
    and     n.nbn_num=x.nbn_num
    and     n.sgp_num=x.sgp_num
    and     n.pspl_num=x.pspl_num
    and     n.psps_num=x.psps_num
    GROUP BY x.nbn_num,
             x.sgp_num,
             x.pspl_num,
             x.psps_num) t
    WHERE   a.nbn_num        =   :b2
    and     a.sgp_num        =   :b1
    and     a.nbn_num        =   b.nbn_num
    and     a.sgp_num        =   b.sgp_num
    and     a.pspl_num =   b.pspl_num
    and     b.nbn_num        =   t.nbn_num
    and     b.sgp_num        =   t.sgp_num
    and     b.pspl_num =   t.pspl_num
    and     b.psps_num    =   t.psps_num

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.01          0          0          0           0
Execute      1      0.02       0.01          0          0          0           0
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.02       0.02          0          0          0           0

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 28  (SALES1)   (recursive depth: 1)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      0   SORT (GROUP BY)
      0    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF 'TTKLMTA'
      0     NESTED LOOPS
      0      NESTED LOOPS
      0       NESTED LOOPS
      0        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTCOLFP'
      0         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTCOLFP'
                    (UNIQUE)
      0        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTSTCPK'
      0         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK'
                    (UNIQUE)
      0       INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK'
                  (UNIQUE)
      0      INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTKLMTA' (UNIQUE)


********************************************************************************
 


Now, I ran the same PL/SQL program from another session
but with the different WHERE clause value which returns
joins very less rows and returns very less rows and the following is the trace output for that.


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        6      0.00       0.00          0        109          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        8      0.00       0.00          0        109          0           5

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 28  (SALES1)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      5  SORT GROUP BY (cr=109 r=0 w=0 time=1606 us)
     15   FILTER  (cr=109 r=0 w=0 time=1205 us)
     15    TABLE ACCESS BY INDEX ROWID TTKLMTA (cr=109 r=0 w=0 time=1174 us)
     41     NESTED LOOPS  (cr=107 r=0 w=0 time=1090 us)
     25      NESTED LOOPS  (cr=30 r=0 w=0 time=430 us)
      5       MERGE JOIN CARTESIAN (cr=8 r=0 w=0 time=219 us)
      1        TABLE ACCESS BY INDEX ROWID TTCOLFP (cr=4 r=0 w=0 time=73 us)
      1         INDEX RANGE SCAN IND_TTCOLFP (cr=3 r=0 w=0 time=47 us)(object id 195300)
      5        BUFFER SORT (cr=4 r=0 w=0 time=106 us)
      5         INDEX RANGE SCAN IND_TTSTCPK (cr=4 r=0 w=0 time=47 us)(object id 391563)
     25       TABLE ACCESS BY INDEX ROWID TTSTCPK (cr=22 r=0 w=0 time=176 us)
     25        INDEX RANGE SCAN IND_TTSTCPK (cr=17 r=0 w=0 time=96 us)(object id 391563)
     15      INDEX RANGE SCAN IND_TTKLMTA (cr=77 r=0 w=0 time=546 us)(object id 391566)


Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      5   SORT (GROUP BY)
     15    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF 'TTKLMTA'
     15     NESTED LOOPS
     41      NESTED LOOPS
     25       NESTED LOOPS
      5        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTCOLFP'
      1         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTCOLFP'
                    (UNIQUE)
      1        TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
                   'TTSTCPK'
      5         INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK'
                    (UNIQUE)
      5       INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTSTCPK'
                  (UNIQUE)
     25      INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'IND_TTKLMTA' (UNIQUE)



 


Followup   October 16, 2003 - 5pm Central time zone:

what do you mean "the select columns are more" 

5 stars more   October 16, 2003 - 8pm Central time zone
Reviewer: A reader 
Tom,

The list of columns in the SELECT is more and hence
instead of pasting the entire query as it will look very large query, I just avoided that portion 
of the query here.

Thanks
Pushparaj  


Followup   October 16, 2003 - 9pm Central time zone:

so, the select lists were IDENTICAL right? 

4 stars SELECT LIST   October 17, 2003 - 8am Central time zone
Reviewer: Pushparaj Arulappan from NJ, USA
Tom,
The SELECT list is exactly the same and
absoulyely no difference.

Thanks
Pushparaj 


Followup   October 17, 2003 - 11am Central time zone:

what command exactly do you use to analyze all of the tables and are the statistics all up to date? 

4 stars   October 17, 2003 - 4pm Central time zone
Reviewer: Pushparaj Arulappan from NJ, USA
Tom,
We use COMPUTE STATISTICS to collect the staticts
for all our tables and indexes.

SQL> select table_name,num_rows,last_analyzed from dba_tables where table_name in 
('TTSTCPK','TTKLMTA','TTCOLFP');

TABLE_NAME   NUM_ROWS  LAST_ANALYZED
------------ --------  -----------
TTCOLFP      4250390   2003-10-11.08.00.36
TTSTCPK      3807971   2003-10-12.16.13.05
TTKLMTA      1423913   2003-10-12.15.46.45

I have checked the indexes also and they all have statistics.

The above three are the tables joined in this query...

The following are the row counts for the bind variable
values used in the WHERE cluase and which is taking very long time.

TTCOLFP = 1
TTSTCPK = 1643
TTKLMTA = 9381

The following are the row counts for the bind variable
values used in the WHERE cluase and which executes in no
time.

TTCOLFP = 1
TTSTCPK = 5
TTKLMTA = 15

The query goes faster for fewer counts but for the large counts it takes hours.

Thanks
Pushparaj 


Followup   October 19, 2003 - 5pm Central time zone:

*exact* command please.  there are hundreds of possible ways to compute statistics. 

5 stars analyze   October 20, 2003 - 10am Central time zone
Reviewer: Pushparaj Arulappan from NJ, USA
Tom,

We use the following analyze command for collecting
statistics for tables and the indexes. We run this every
week during the weekend.

ANALYZE TABLE ttcolfp COMPUTE STATISTICS;
ANALYZE TABLE ttstcpk COMPUTE STATISTICS;
ANALYZE TABLE ttklmta COMPUTE STATISTICS;


Thanks for your help.
Pushparaj
 


Followup   October 20, 2003 - 10am Central time zone:

I've a feeling it is a bind variable "peeking" issue.

Can you try this

a) flush your shared pool
b) run the plsql with the BAD inputs first (with the values that make it run slow)

what I think might be happening is -- you run it with inputs that should use a merge join cartesian 
(you have histograms on those tables) and that plan "sticks".  You try in sqlplus with "bad" values 
and get a different plan.

It could be that you don't want histograms in this particular case (compute statistics takes the 
MOST resources -- you might consider using just for table for all indexes -- and maybe for "some 
columns"

Here is a for example of bind variable peeking -- SAME query more or less (differs only in the 
correlation name in the query) but run with different starting inputs:

ops$tkyte@ORA920> create table t
  2  as
  3  select 1000 id, all_objects.* from all_objects;
 
Table created.
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> insert into t
  2  select 1 id, all_objects.* from all_objects where rownum = 1;
 
1 row created.
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> create index t_idx on t(id);
 
Index created.
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> analyze table t compute statistics
  2  for table
  3  for all indexes
  4  for all indexed columns;
 
Table analyzed.
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> alter system flush shared_pool;
 
System altered.
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> variable x number
ops$tkyte@ORA920> alter session set sql_trace=true;
 
Session altered.
 
ops$tkyte@ORA920> set autotrace traceonly statistics
ops$tkyte@ORA920> exec :x := 1000
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA920> select * from t x_1000 where id = :x;
 
30189 rows selected.
 
 
Statistics
----------------------------------------------------------
        593  recursive calls
          0  db block gets
       2530  consistent gets
        223  physical reads
          0  redo size
    2155534  bytes sent via SQL*Net to client
      22631  bytes received via SQL*Net from client
       2014  SQL*Net roundtrips to/from client
          7  sorts (memory)
          0  sorts (disk)
      30189  rows processed
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> exec :x := 1
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA920> select * from t x_1 where id = :x;
 
 
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          1  physical reads
          0  redo size
       1216  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
ops$tkyte@ORA920>
ops$tkyte@ORA920> set autotrace off


tkprof shows us:

select * from t x_1000 where id = :x
                                                                                                    
                                    
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     2014      0.34       0.36        223       2440          0       30189
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     2016      0.35       0.37        223       2440          0       30189
                                                                                                    
                                    
Rows     Row Source Operation
-------  ---------------------------------------------------
  30189  TABLE ACCESS FULL T (cr=2440 r=223 w=0 time=193232 us)
********************************************************************************
select * from t x_1 where id = :x
                                                                                                    
                                    
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        2      0.00       0.00          1          4          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          1          4          0           1
                                                                                                    
                                    
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS BY INDEX ROWID T (cr=4 r=1 w=0 time=254 us)
      1   INDEX RANGE SCAN T_IDX (cr=3 r=1 w=0 time=214 us)(object id 41314)


different plans -- that will stay with that query until the shared pool is flushed/it ages out
 

5 stars Can we flush just one query plan from shared pool?   December 29, 2003 - 4pm Central time zone
Reviewer: Arun Gupta from Harrisburg, PA USA
Tom,
If a bad execution plan gets into the shared pool because of bind variable peeking, what are the 
ways to get rid of it? The query is heavily used and chances of it's getting flushed out of shared 
pool are low. Is flushing the entire shared pool our only option here?

Thanks. 


Followup   December 29, 2003 - 5pm Central time zone:

analyze the table, grant/revoke on the table (eg: grant select on it to scott, revoke select on it 
from scott) 

3 stars Not missing the point: Grasping for some explanation of how bind peeking is useful   March 19, 2004 - 5pm Central time zone
Reviewer: Kerry from Austin, TX USA
I don't think Kamal's question can be easily dismissed. The hypothetical, more dynamic peeking he 
is talking about is just an attempt to understand an otherwise perplexing feature...

I think people's difficulty in understanding what bind peeking does is that they are grasping for a 
set of behaviors that actually make it beneficial. Now that I understand that the current 
implementation (in 9i), is simply not useful, it is much easier to comprehend. With the plan cached 
and spanning sessions, it is not a reasonable assumption that any "representative" bind values can 
be used at the hard parse:

  (1) If data isn't skewed, then bind peeking isn't necessary - the overall statistics for the 
tables and indexes, independent of bind values - should suffice

  (2) If data is skewed and peeking makes a significant difference, then it will produce a plan for 
the initial set of values different than what it would choose on subsequent executions: likely a 
poor choice for any other set of bind values.

How could I possibly want this?

I do not want my application to know when the cache has flushed, so I cannot supply 
"representative" values at the correct time. Furthermore, if there is "representative" data, how 
skewed can the data actually be?

We have tables partitioned by clients, and bind peeking is nothing but trouble. Coding all dynamic 
SQL in PL/SQL would be an absolute joke... 


Followup   March 20, 2004 - 9am Central time zone:

1) correct
2) and the initial plan should be the right plan for that query at that place.  The concept is "i 
have an oltp app that picks "where x = :x".  the count(*) for the x's i use is "small" reletive to 
the table.  I SHOULD use an index.  Therefore the first time we run, we see x = :x and :x = 5 and 5 
should use an index -- for that exact query (cause remember the plans are by EXACT queries) the 
optimizer will choose an index.  that is the right plan for that application.  no matter what 
happens -- that query should use and index and every time I run that program that query at that 
place will supply bind inputs that lean the optimizer towards the index.  I need to bind because 
there are potentially hundreds of codes there -- but all would use the index.  The value(s) that 
would not use the index would never be called there (remember, we are "transactional")

sorry you don't see the use, I sure do and I am glad it is there -- else it would lead to lots more 
hinting going on.   

4 stars Data in table may be skewed, but data seen by query must not be?   March 24, 2004 - 12pm Central time zone
Reviewer: Kerry from Austin, TX USA
To summarize: Bind peeking may be useful if the data in the table is skewed, but the data that I 
query is of a consistent proportion relative to the size of the table, for a given query.

Consider the skewness as falling into two equivalence classes: BIG fanout and SMALL fanout.

Here is where I find this problematic: Unless I code my application to recognize the equivalence 
classes and handle the BIG and SMALL cases with two different queries then there will always be a 
chance that a BIG dataset will cause a plan to cache that is not at all appropriate for the SMALL 
case, or vice versa. Since I don't define the equivalence classes or provide the data, I can't 
control it.  (And this is a simple case: assuming just 2 equivalence classes)

It may be worth note that our database is hardly OLTP. It's an ASP-model (website) with some OLTP 
aspects, some data mining, and some data warehousing. I believe this is why bind peeking was 
particularly bad for us. 


Followup   March 24, 2004 - 1pm Central time zone:

the concept is -- in a given point in a program -- they will be "the same".

at point X in the program (say the user interactive part), always index, the binds will drive you 
that way.

at point Y in the program (system), full scan, the binds will drive you that way.


without bind peeking, you would always have that 50/50 chance of "not getting it right". 

5 stars BIND PEEKING   July 23, 2004 - 12pm Central time zone
Reviewer: Leonard Williams from Dallas, TX.
This was very good. We were going back and forth in my whether to use CURSOR_SHARING or BIND 
VARIABLES.
I stressed to them the "Proper use of bind variables" and the affects of turning on 
"cursor_sharing" in the DB configuration.

They went with Bind Variables and noticed that EXPLAIN PLAN sort of lied to them. This article or 
discussion help shed some light on a lot things regarding Bind Peeking and it's relevance or affect 
on the optimizer and the explain plan output.

Thanks Much,
 


3 stars Skewed data used once only   February 7, 2005 - 9am Central time zone
Reviewer: Adrian from UK
Tom,

>> the concept is -- in a given point in a program -- they will be "the same".

I understand fully your position on peeking and that different parts of the application might 
require different "pieces" of the skewed data. My concern is how this helps when we have an on-line 
application with only one point of reference to the skewed, "histogrammed" data - where genuinely, 
a lookup with binds + peeking could really screw the application once and for all if we got the 
wrong one on the fabled Monday morning.

I read somewhere on usenet that somebody's mother's uncle's brother thought that 10g might be 
introducing a "re-peek and parse" under certain conditions. Don't suppose you know anything about 
this ?

Regards
Adrian 


Followup   February 7, 2005 - 10am Central time zone:

you just sort of decribed "cursor_sharing=similar"


 

3 stars Only if...   February 7, 2005 - 11am Central time zone
Reviewer: Adrian from UK
If the app was using literals in the SQL, then I'd agree and we'd be on the way to solving this.

However, the app is at a halfway house where it is binding a user-input but has a skewed column 
with a histogram. I'm not convinced peeking will provide anything in this instance over the 8i and 
earlier days which said "binds won't make use of histograms". We'd still see the same proportion of 
people unhappy either way. But then again, SOME people will get the quick response they'd expect 
though ;o)

I don't know what the answer to this is.

Regards
Adrian 


Followup   February 7, 2005 - 11am Central time zone:

Dont bind that query, that is a possibility.  
   Do the math, if there are a handful of inputs to that "bind", use a literal
 
Dont use histograms
      Get the general plan Consistent Plan, but typically 
      not the best plan for all

Use your domain knowledge of the data
     Input dates within the last month  use this query, else use that query
     if ( date > sysdate-30 )
     then
        open this_query using select * from t DT_CLOSE_TO_TODAY where ....
     else
        open this_query using select * from t DT_FAR_IN_THE_PAST where...
     end if

     Codes less than 50  use this query, else use that query

     Status values of A, M and N . Else.

You can disable it (using undocumented, hence not talked about parameter) but that is like dont use 
histograms in a system that uses binds.


 

4 stars And beware of partitioned tables...   February 10, 2005 - 1pm Central time zone
Reviewer: kerry from Austin, TX USA
Simply turning of the gathering of histograms is not enough if you have partitioned tables.  Bind 
peeking allows oracle to use partition level statistics as well.  You can have the same issues if 
the distribution of data differs between partitions: the number of distinct values for some column 
in a partition may be drastically different from other partitions.  But the same cached plan will 
be used regardless. 


5 stars Does Peeking works when cursor_sharing is left at the default value ?   September 14, 2006 - 10am Central time zone
Reviewer: VLS from Bombay, India
Hi Tom,

Does Bind Variable peeking works when the cursor_sharing is at default value ?

Regards
VLS 


Followup   September 14, 2006 - 11am Central time zone:

of exact, yes - absolutely.

It happens with all settings actually. 


Write a Review
 


All information and materials provided here are provided "as-is"; Oracle disclaims all express and implied warranties, including, the implied warranties of merchantability or fitness for a particular use. Oracle shall not be liable for any damages, including, direct, indirect, incidental, special or consequential damages for loss of profits, revenue, data or data use, incurred by you or any third party in connection with the use of this information or these materials.

About Oracle | Legal Notices and Terms of Use | Privacy Statement