Skip to Main Content
  • Questions
  • Tkprof and Autotrace showing diff. plan

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Jagjeet.

Asked: January 21, 2006 - 11:33 am UTC

Last updated: January 23, 2006 - 12:55 pm UTC

Version: 9.2.0

Viewed 1000+ times

You Asked

Hi,

In this case "set autot on" is telling diff. plan than tkprof
if using bind variables.

SQL> desc big_table

Name Null Type
-------------------------- -------- ----------
R NUMBER
DATA VARCHAR2(300)

SQL> select count(*),r from big_Table group by r;

COUNT(*) R
---------- ----------
49999 1
1 2

I have created index on Big_table ( R )

SQL> exec dbms_stats.gather_Table_Stats(user,'BIG_TABLE',method_opt=>'for all indexed columns',cascade=>true)

PL/SQL procedure successfully completed.

SQL> set autot on exp

Here its showing the correct paln.
===================================

SQL> select count(data) from big_table where r = 1 ;


COUNT(DATA)
-----------
49999


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1556 Card=1 Bytes=100)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1556 Card=49999 Bytes=4999900)


SQL> select count(data) from big_table where r = 2 ;

COUNT(DATA)
-----------
1

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=100)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=4 Card=1 Bytes= 100)
3 2 INDEX (RANGE SCAN) OF 'IND_BIG_TABLE' (NON-UNIQUE) (Cost=3 Card= 1)


SQL> var n number
SQL>
SQL> exec :n := 1

PL/SQL procedure successfully completed.

SQL> set autot on
SQL>
SQL> select count(data) from big_Table where r = :n ;

COUNT(DATA)
-----------
49999

Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1556 Card=1 Bytes=100)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1556 Card=25000 Bytes=2500000)

Statistics
----------------------------------------------------------
0 db block gets
10024 consistent gets
0 physical read


SQL> exec :n := 2

PL/SQL procedure successfully completed.

SQL> select count(data) from big_Table where r = :n ;

COUNT(DATA)
-----------
1


Execution Plan
---------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1556 Card=1 Bytes=100)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1556 Card=25000 Bytes=2500000)

Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
10024 consistent gets

-- still it is using the same plan with 10024 block gets
-- I have put one space extra before where clause to make
-- it a new statement to hard parse


SQL> select count(data) from big_Table where r = :n ;

COUNT(DATA)
-----------
1


Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1556 Card=1 Bytes=100)
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1556 Card=25000 Bytes=2500000)


Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets

Still showing "Full table Scan" but with 4 block gets.

--- From tkprof ---

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

select count(data)
from
big_Table where r = :n


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 0 4 0 1

------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 4 0 1



Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 23


Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE (cr=4 r=0 w=0 time=202 us)
1 TABLE ACCESS BY INDEX ROWID BIG_TABLE (cr=4 r=0 w=0 time=170 us)
1 INDEX RANGE SCAN IND_BIG_TABLE (cr=3 r=0 w=0 time=118 us)(object id 24087)

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






and Tom said...

tkprof, with the row source operation, shows what actually happens.

explain plan, which cannot:

o bind peek
o know the datatypes of the binds
o reuse a 'soft parse' (eg: it always hard parses)
o understand the optimizer environment in use by the 'real plan' always

can mislead.

for example:

---------------------------------------------------
set echo on
drop table t;

create table t
as
select '99' id, all_objects.*
from all_objects;
update t set id = '1' where rownum = 1;
create index t_idx on t(id);
exec dbms_stats.gather_table_stats( user, 'T', method_opt => 'for all indexed columns' );


variable n varchar2(5)
alter session set sql_trace=true;
set autotrace traceonly statistics
exec :n := '1 '
select * from t t_1 where id = :n;
exec :n := '99'
select * from t t_99 where id = :n;
set autotrace off
-----------------------------------------------

a tkprof with explain=u/p will show:

select * from t t_1 where id = :n

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 0 4 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 4 0 1

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID T (cr=4 pr=0 pw=0 time=99 us)
1 INDEX RANGE SCAN T_IDX (cr=3 pr=0 pw=0 time=115 us)(object id 57199)

Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
1 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)

eh, explain plan <> row source operation...

********************************************************************************
select * from t t_99 where id = :n

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 3311 0.16 0.16 0 3988 0 49648
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3313 0.16 0.16 0 3988 0 49648

Rows Row Source Operation
------- ---------------------------------------------------
49648 TABLE ACCESS FULL T (cr=3988 pr=0 pw=0 time=99473 us)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT MODE: ALL_ROWS
49648 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)


row source operation = reality, what actually happened. execution plan = guess based on "incomplete information". explain plan does not bind peek, doesn't have that ability

How about datatypes? Consider:



ops$tkyte@ORA10GR2> create table t
2 as
3 select to_char(object_id) id, all_objects.*
4 from all_objects
5 /

Table created.

ops$tkyte@ORA10GR2> alter table t add constraint t_pk primary key(id);

Table altered.

ops$tkyte@ORA10GR2>
ops$tkyte@ORA10GR2> variable n number
ops$tkyte@ORA10GR2> exec :n := 42

PL/SQL procedure successfully completed.

ops$tkyte@ORA10GR2> set autotrace traceonly
ops$tkyte@ORA10GR2> select * from t where id = :n;


Execution Plan
----------------------------------------------------------
Plan hash value: 1303508680

------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 150 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 150 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | T_PK | 1 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------

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

2 - access("ID"=:N)

here, explain plan assumes all binds are strings, it doesn't "see" that in sqlplus we told sqlplus "hey, when you bind, please bind a number" - all it sees is an explain plan command with a :n in it - you don't BIND to explain plans, hence explain plan doesn't know what datatype, or the bind value to peek

Statistics
----------------------------------------------------------
536 recursive calls
0 db block gets
888 consistent gets
0 physical reads
0 redo size
1254 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed

Now, I'm pretty sure that isn't the plan used - 888 IO's to get a single row via a primary key index??

ops$tkyte@ORA10GR2> select * from t where id = 42;


Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 150 | 236 (3)| 00:00:02 |
|* 1 | TABLE ACCESS FULL| T | 1 | 150 | 236 (3)| 00:00:02 |
--------------------------------------------------------------------------

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

1 - filter(TO_NUMBER("ID")=42)

see, when we compare a STRING to a NUMBER, we have to conver the string to a number, so there is an implicit function on the database column. That is why we full scan in reality...

Note
-----
- dynamic sampling used for this statement


Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
800 consistent gets
0 physical reads
0 redo size
1254 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed

ops$tkyte@ORA10GR2> set autotrace off


Even if explain plan "peeked" at binds, it would not be good however. Since explain plan hard parsed each time, it would simply show us what would happen if the query was bound with that value and hard parsed - it won't show us what'll really happen.

I suggest you use v$sql_plan or tkprof with row source operation so as to see "reality"


Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Then why did it change plan second time.

Jagjeet Singh, January 22, 2006 - 10:22 am UTC

Thanks for explained answer.

I have one doubt ..

> explain plan does not bind peek, doesn't have that ability

Then why did it change it's plan when I put one space extra.

==================
SQL> exec :n := 2
SQL> select count(data) from  big_Table where r = :n ;

Statistics
----------------------------------------------------------
      0  recursive calls
      0  db block gets
      10024  consistent gets

-- I have put one space extra before where clause to make
-- it a new statement to hard parse
 
SQL> select count(data) from big_Table  where r = :n ;

COUNT(DATA)
-----------
      1

Statistics
---------------------------------
      0  recursive calls
      0  db block gets
      4  consistent gets
==============

Thanks,
Jagjeet Singh 

Tom Kyte
January 22, 2006 - 4:15 pm UTC

that is not explain plan, that is "real life", that is an actual query being executed, which will

a) do bind variable peeking
b) understand the bind types

and by you changing the query, you caused a hard parse - so it "peeked" the number 2 and came up with the plan the query would have come up with for

select count(data) from big_table where r = 2;


Still Confused

Jagjeet Singh, January 23, 2006 - 12:09 pm UTC

I am still confused.

First you said ..

"can not reuse a 'soft parse' (eg: it always hard parses)"

and now

"and by you changing the query, you caused a hard parse - so it "peeked" .."

Regards,
Jagjeet singh


Tom Kyte
January 23, 2006 - 12:55 pm UTC

you were not using explain plan - I said quote one ABOUT EXPLAIN PLAN (which cannot peek).

You changed a query and ran it. (NOTE: forget about explain plan, it was not used, it is not relevant, it does not come into play). A query that is run the first time is hard parsed. Bind peeking is an option. Here, bind peeking was done and the plan changed as a result

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library