The execution of the same SQL statement might result in different execution plans due to various reasons. In my last post, I had demonstrated how the value of parameter OPTIMIZER_MODE can cause change in execution plan. In this post, I will demonstrate that in case of skewed data distribution, change in the value of a bind variables can also result in differing execution plans for the same statement. I will also explore how we can find out the values of bind variables used during various executions of the same statement when
- the cursors are still available in shared pool
- the cursors have been flushed to AWR
As part of the setup, I have created a table TEST_BIND with skewed data distribution in column ID which is indexed. There are 1000 records for ID = 1 and only 1 record with ID = 2.
SQL>select id, count(*) from hr.test_bind group by id; ID COUNT(*) ---------- ---------- 1 1000 2 1
Subsequently, I have executed the following statement with values 1 and 2 assigned to bind variable VID multiple no. of times so that Adaptive Cursor Sharing kicks in and the statement executes with Full Table Scan when VID = 1 and with Index Range Scan when VID = 2 .
SQL>select * from hr.test_bind where id = :vid;
– Now I will execute the statement with different values of the bind variable
SQL>variable vid number; exec :vID := 1; select * from hr.test_bind where id = :vid; variable vid number; exec :vID := 2; select * from hr.test_bind where id = :vid;
– Since the statement is still in shared pool, we can find out sql_id for the statement from V$SQL
– It can be seen two child cursors have been created indicating that the two executions used different execution plans
SQL>select sql_id, child_number, sql_text, is_bind_sensitive bind_sensitive, is_bind_aware bind_aware , is_shareable shareable fom v$sql where sql_text like '%select * from hr.test_bind%' and is_shareable = 'Y' and sql_text not like '%sql_text%'; SQL_ID CHILD_NUMBER SQL_TEXT BIND_SENSITIVE BIND_AWARE SHAREABLE ------------- ------------ ----------------------------------- ---------- 7a73kdgy0q1c2 1 select * from hr.test_bind whe Y Y Y re id = :vid 7a73kdgy0q1c2 2 select * from hr.test_bind whe Y Y Y re id = :vid
– It can be verified that different plans are being used by the same statement during two executions due to different values of bind variable VID. The respective values of the bind variable are visible in Peeked Binds section of the execution plan
SQL>select * from table (dbms_xplan.display_cursor('7a73kdgy0q1c2', 1, format => 'TYPICAL +PEEKED_BINDS')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------- SQL_ID 7a73kdgy0q1c2, child number 1 ------------------------------------- select * from hr.test_bind where id = :vid Plan hash value: 3519963602 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | |* 1 | TABLE ACCESS FULL| TEST_BIND | 1000 | 7000 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------- Peeked Binds (identified by position): -------------------------------------- 1 - :VID (NUMBER): 1 SQL>select * from table (dbms_xplan.display_cursor('7a73kdgy0q1c2', 2, format => 'TYPICAL +PEEKED_BINDS')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------- SQL_ID 7a73kdgy0q1c2, child number 2 ------------------------------------- select * from hr.test_bind where id = :vid Plan hash value: 2825156252 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_BIND | 1 | 7 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | TEST_IDX | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------- Peeked Binds (identified by position): -------------------------------------- 1 - :VID (NUMBER): 2
– We can look at v$sql_bind_capture also to find out the values of bind variable across various executions of the SQL
SQL>SELECT NAME, child_number, DATATYPE_STRING,VALUE_STRING FROM v$sql_bind_capture WHERE sql_id='7a73kdgy0q1c2' order by child_number; NAME CHILD_NUMBER DATATYPE_STRING VALUE_STRING --------------- ------------ -------------------- --------------- :VID 0 NUMBER 2 :VID 1 NUMBER 1 :VID 2 NUMBER 2
Now I will demonstrate how we can find out different execution plans and corresponding values of bind variables after the statement has been flushed to AWR.
– To flush the statement to AWR, let’s take a snapshot
SQL>exec dbms_workload_repository.create_snapshot ('ALL');
– We can verify that that the statement is indeed in AWR
SQL> select sql_id, sql_text
from dba_hist_sqltext
where sql_text like '%select * from hr.test_bind%'
and sql_text not like '%sql_text%';
SQL_ID SQL_TEXT
------------- --------------------------------------------------
7a73kdgy0q1c2 select * from hr.test_bind where id = :vid
– Find out various execution plans andcorresponding values of bind variables used by the statement
SQL>select * from table(dbms_xplan.display_awr ('7a73kdgy0q1c2', format => 'TYPICAL +PEEKED_BINDS')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------- SQL_ID 7a73kdgy0q1c2 -------------------- select * from hr.test_bind where id = :vid Plan hash value: 2825156252 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID| TEST_BIND | 1 | 7 | 2 (0)| 00:00:01 | | 2 | INDEX RANGE SCAN | TEST_IDX | 1 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------- Peeked Binds (identified by position): -------------------------------------- 1 - :VID (NUMBER): 2 SQL_ID 7a73kdgy0q1c2 -------------------- select * from hr.test_bind where id = :vid Plan hash value: 3519963602 ------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | | 1 | TABLE ACCESS FULL| TEST_BIND | 1000 | 7000 | 3 (0)| 00:00:01 | ------------------------------------------------------------------------- Peeked Binds (identified by position): -------------------------------------- 1 - :VID (NUMBER): 1
– We can also use dba_hist_sqlbind to check the values of bind variables during 2 executions
SQL>SELECT sql_id, NAME,DATATYPE_STRING,VALUE_STRING FROM DBA_HIST_SQLBIND WHERE SQL_ID='7a73kdgy0q1c2' ; SQL_ID NAME DATATYPE_STRING VALUE_STRING ------------- --------------- -------------------- --------------- 7a73kdgy0q1c2 :VID NUMBER 1 7a73kdgy0q1c2 :VID NUMBER 2
Summary:
In case of skewed data distribution, different values of bind variables can result in different execution plans. Various execution plans and corresponding values of bind variables can be determined both when the statement is still shared pool and when it has been flushed to AWR.
I hope this post was useful. Your comments and suggestions are always welcome.
Keep visiting my blog …
References:
http://shaharear.blogspot.in/
—————————————————————————————————
Related links:
Identify difference in CBO parameters across two executions of a SQL
It is possible for the same CHILD and same PLAN but different peeked binds. Then, you’d only say the latest peeked bind.
For example, run your test with one more row with ID=3
True Hemant. If multiple values of bind variable result in the same plan, only one child cursor gets created for all the values having selectivity in the same range. In that case execution plan, v$sql_bind_capture and DBA_HIST_SQLBIND show the latest value of peeked bind variable.
Thanks and regards
Anju
Hi Anju,
As always, excellent post.
One query- As per the demo the sqls were flushed to AWR when you took a manual snapshot. Does this apply for snapshots taken by oracle at regular intervals.
Regards
Krunal
Hi Krunal,
Thanks for your time and feedback.
Yes, This applies to both manual as well as automatic snapshots.
regards
Anju
Always nice to read your posts Anju,
Foued
Thanks Foued for your time and feedback. Your comments and suggestions are always welcome!
Regards
Anju
Am new for this really easy understanding