我们查看sql语句的执行计划有set autotrace,explain plan for和10046 raw trace三种主要方式,但在有绑定变量存在的情况时,默认情况下10046 raw trace得到的执行计划与另外两种主要方式不同。以下给出了一个演示:
1、初始化环境数据并收集统计信息
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Linux: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
SQL> conn syk/syk
Connected.
SQL> DROP TABLE T_BIND_VAR PURGE;
Table dropped.
SQL> CREATE TABLE T_BIND_VAR ( A INT, B VARCHAR(64));
Table created.
SQL> create index inx_a on T_BIND_VAR(A);
Index created.
SQL> begin
2 for i in 1..10000 loop
3 insert into t_bind_var values (1,DBMS_RANDOM.STRING('B',64));
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> update t_bind_var set a = 2 where rownum <=1;
1 rows updated.
SQL> commit;
Commit complete.
SQL> begin
2 dbms_stats.gather_table_stats(
3 ownname => 'SYK',
4 tabname => 'T_BIND_VAR',
5 estimate_percent => null,
6 method_opt => 'for all indexed columns',
7 cascade => true);
8 end;
9 /
PL/SQL procedure successfully completed.
SQL> select endpoint_value,
2 endpoint_number,
3 endpoint_number - lag(endpoint_number, 1, 0) over(order by endpoint_number) as frequency
4 from user_tab_histOgrams
5 where table_name = 'T_BIND_VAR'
6 AND COLUMN_NAME = 'A'
7 ORDER BY ENDPOINT_NUMBER;
ENDPOINT_VALUE ENDPOINT_NUMBER FREQUENCY
-------------- --------------- ----------
1 9999 9999
2 10000 1
2、对比执行计划
脚本xplan_with_bind.sql中包含了三种获取执行计划的方式,执行脚本可以获得三种方式所产生的结果。
SQL> ! cat xplan_with_bind.sql
set linesize 80 pagesize 200 echo on feedback off
var v_a number
exec :v_a := 2
explain plan for SELECT * FROM T_BIND_VAR WHERE A = :v_a;
select * from table(dbms_xplan.display);
SET AUTOT ON EXPLAIN
SELECT * FROM T_BIND_VAR WHERE A = :v_a;
SET AUTOT OFF
alter session set events='10046 trace name context forever,level 12';
SELECT * FROM T_BIND_VAR WHERE A = :v_a;
alter session set events='10046 trace name context off';
SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Default Trace File';
exit
SQL> conn syk/syk
Connected.
SQL> @xplan_with_bind
SQL> var v_a number
SQL> exec :v_a := 2
SQL> explain plan for SELECT * FROM T_BIND_VAR WHERE A = :v_a;
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3172564556
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5000 | 180K| 30 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T_BIND_VAR | 5000 | 180K| 30 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("A"=TO_NUMBER(:V_A))
SQL> SET AUTOT ON EXPLAIN
SQL> SELECT * FROM T_BIND_VAR WHERE A = :v_a;
A B
---------- ----------------------------------------------------------------
2 TUABBMPSWVDCUUBXGNSVQZEEOPLNDZJFAZIJSROBEUJMIJIVRDSTIGGHJMOFLYYX
Execution Plan
----------------------------------------------------------
Plan hash value: 3172564556
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5000 | 180K| 30 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T_BIND_VAR | 5000 | 180K| 30 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("A"=TO_NUMBER(:V_A))
SQL> SET AUTOT OFF
SQL> alter session set events='10046 trace name context forever,level 12';
SQL> SELECT * FROM T_BIND_VAR WHERE A = :v_a;
A B
---------- ----------------------------------------------------------------
2 TUABBMPSWVDCUUBXGNSVQZEEOPLNDZJFAZIJSROBEUJMIJIVRDSTIGGHJMOFLYYX
SQL> alter session set events='10046 trace name context off';
SQL> SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Default Trace File';
VALUE
--------------------------------------------------------------------------------
/oracle/diag/rdbms/aceace/ACE/trace/ACE_ora_27886.trc
检查上述raw trace文件,可以发现下面的片段,其中包含了执行计划:
=====================
PARSING IN CURSOR #47474678551288 len=39 dep=0 uid=91 oct=3 lid=91 tim=1349864661566215 hv=3864287956 ad='ae6d3cf8' sqlid='91t6tngm58qqn'
SELECT * FROM T_BIND_VAR WHERE A = :v_a
END OF STMT
PARSE #47474678551288:c=1000,e=499,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2359727817,tim=1349864661566208
BINDS #47474678551288:
Bind#0
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
oacflg=03 fl2=1000000 frm=00 csi=00 siz=24 off=0
kxsbbbfp=2b2d8f5cf300 bln=22 avl=02 flg=05
value=2
EXEC #47474678551288:c=1000,e=798,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2359727817,tim=1349864661567125
WAIT #47474678551288: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1349864661567237
FETCH #47474678551288:c=0,e=117,p=0,cr=3,cu=0,mis=0,r=1,dep=0,og=1,plh=2359727817,tim=1349864661567385
WAIT #47474678551288: nam='SQL*Net message from client' ela= 191 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1349864661567621
FETCH #47474678551288:c=0,e=4,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=2359727817,tim=1349864661567650
STAT #47474678551288 id=1 cnt=1 pid=0 pos=1 obj=82005 op='TABLE ACCESS BY INDEX ROWID T_BIND_VAR (cr=3 pr=0 pw=0 time=175 us cost=2 size=37 card=1)'
STAT #47474678551288 id=2 cnt=1 pid=1 pos=1 obj=82006 op='INDEX RANGE SCAN INX_A (cr=2 pr=0 pw=0 time=128 us cost=1 size=0 card=1)'
WAIT #47474678551288: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1349864661568391
WAIT #47474678551288: nam='SQL*Net message from client' ela= 748 driver id=1650815232 #bytes=1 p3=0 obj#=-1 tim=1349864661569212
CLOSE #47474678551288:c=0,e=6,dep=0,type=0,tim=1349864661569246
=====================
使用tkprof工具格式化后的trace文件可以发现下面的片段:
SQL ID: 91t6tngm58qqn Plan Hash: 2359727817
SELECT *
FROM
T_BIND_VAR WHERE A = :v_a
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 3 0 1
------ ------ -------- -------- ------- -------- -------- ----------
total 4 0.00 0.00 0 3 0 1
Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 91
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max) Row Source Operation
---------- ---------- ---------- ---------------------------------------------------
1 1 1 TABLE ACCESS BY INDEX ROWID T_BIND_VAR (cr=3 pr=0 pw=0 time=175 us cost=2 size=37 card=1)
1 1 1 INDEX RANGE SCAN INX_A (cr=2 pr=0 pw=0 time=128 us cost=1 size=0 card=1)(object id 82006)
可以看出三种方式得出的结果并不是完全一样。
3、原因
MOS给的原因解释是
1.Bind Peeking (9.2 and later):
A real execution peeks bind values during the hard parse. “Explain Plan” and SQL*PLUS AUTOTRACE do NOT peek bind values when they generate plans.
2.Data Type Conversion:
For example, the type of a character column is converted when it is compared with a numeric value. In this case, the index on the column cannot be used. But, “Explain Plan” and SQL*PLUS AUTOTRACE do NOT consider data type conversion.
4、解决方案
使用SQL TRACE(10046 trace)方式获取正确的执行计划。另外,如果知晓sql_id,亦可以使用V$SQL_PLAN或DBMS_XPLAN.DISPLAY_CURSOR获得正确的执行计划。
--转自