oracle绑定变量窥视(zt)

使用绑定变量一定是正确的吗?
先来看一个实验
SQL> drop table w_1;
Table dropped
SQL> create table w_1
2 as
3 select decode(mod(rownum,100),0,'N','Y') processed ,a.*
4 from all_objects a ;

Table created
SQL> create index idx_w_1 on w_1 (processed);
Index created
SQL> analyze table w_1 compute statistics
2 for table
3 for all indexes
4 for all indexed columns;
Table analyzed
SQL> set autotrace traceonly
Cannot SET AUTOTRACE
SQL> explain plan for select count(*) from w_1 tt where tt.processed='N' ;
Explained
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 | 1 |
| 1 | SORT AGGREGATE | | 1 | 1 | |
|* 2 | INDEX RANGE SCAN | IDX_W_1 | 310 | 310 | 1 |
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("TT"."PROCESSED"='N')
Note: cpu costing is off
15 rows selected
SQL> explain plan for select count(*) from w_1 tt where tt.processed='Y' ;
Explained
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 | 7 |
| 1 | SORT AGGREGATE | | 1 | 1 | |
|* 2 | INDEX FAST FULL SCAN| IDX_W_1 | 30748 | 30748 | 7 |
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("TT"."PROCESSED"='Y')
Note: cpu costing is off
15 rows selected
从上面的执行计划我们可以看出当processed='Y' ,走的是INDEX FAST FULL SCAN扫描,当processed='N' , 走的是INDEX RANGE SCAN 索引。这是正确的,因为等于‘N’的值很少,走索引显然很划算,‘Y’的值占大部分,走INDEX FAST FULL SCAN或是全表扫描代价会更小一点。
好,我们来验证一下“使用绑定变量”是否会选择正确的执行计划?
先执行processed :='Y',再执行processed :='N'
SQL> alter session set events '10046 trace name context forever,level 12';
Session altered.
SQL> /
variable processed varchar2(1);
exec :processed :='Y';
select count(*) from w_1 t1 where t1.processed=:processed ;
exec :processed :='N';
Session altered.
SQL> SQL>
PL/SQL procedure successfully completed.
SQL> select count(*) from w_1 t1 where t1.processed=:processed ;
alter session set events '10046 trace name context off';
/
COUNT(*)
----------
30748
SQL> SQL>
PL/SQL procedure successfully completed.
SQL>
COUNT(*)
----------
310
SQL> SQL>
Session altered.
执行后,我们来看看这两条语句的执行计划
BEGIN :processed :='Y'; END;

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 1
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.00 0.00 0 0 0 1
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 63
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************
select count(*)
from
w_1 t1 where t1.processed=:processed

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.01 0.03 59 62 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.04 59 62 0 1
Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 63
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE
30748 INDEX FAST FULL SCAN IDX_W_1 (object id 34959)

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 2 0.00 0.00
db file sequential read 1 0.00 0.00
db file scattered read 8 0.00 0.00
SQL*Net message from client 2 0.00 0.00
********************************************************************************
BEGIN :processed :='N'; END;

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 1
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 0.00 0.00 0 0 0 1
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 63
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 1 0.00 0.00
SQL*Net message from client 1 0.00 0.00
********************************************************************************
select count(*)
from
w_1 t1 where t1.processed=:processed

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 62 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 62 0 1
Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 63
Rows Row Source Operation
------- ---------------------------------------------------
1 SORT AGGREGATE
310 INDEX FAST FULL SCAN IDX_W_1 (object id 34959)

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 2 0.00 0.00
SQL*Net message from client 2 0.00 0.00
********************************************************************************
不论processed :='Y'还是='N',走的都是INDEX FAST FULL SCAN IDX_W_1, 显然='N'走错了执行计划。当processed='N' , 应该使用INDEX RANGE SCAN 索引。
为什么会选择错误的执行计划呢?
先从sql语句内部是怎么运行说起,oracle收到一条sql语句后,开始进行一系列的语法和语义检查,然后去pga或是shared_pool里面检查这条语句是否被解析过,如果已经被解析过(称为软分析),那么会跳过去选择更优化的执行计划的机会,直接选择已有的执行计划运行。 如果在共享池没有找到这条sql解析的信息(硬分析),oracle先去优化,选择正确的执行计划,然后才是执行。
显然,使用绑定运行sql语句只执行一次硬解析,而后都是软解析,这就导致了所有sql都在共用一个执行计划。这就有可能选择错误执行计划的情况。
如果执行计划有多个(processed :='Y' 'N'各有自己的执行计划),绑定变量是怎么选择执行计划呢?
优化程序在只有在硬解析的时候会进行窥视,软解析的时候不会。也就是说优化程序不会每回都根据你传递的值去选择正确的执行计划 执行,只在做硬解析的时候,也就是第一次执行语句才会。
如:
exec :processed :='Y';
select count(*) from w_1 t1 where t1.processed=:processed ;(第一次执行)
因为是硬解析,那么oracle在选择执行计划的时候会把=:processed 替换为='Y',然后选择正确的执行计划。。。
exec :processed :='N';
select count(*) from w_1 t1 where t1.processed=:processed ;
第二...次执行,因为pga或是shared_pool里已经有这条语句的执行计划,那么oracle就不会再去窥视了,而是选择已有的执行计划,也就是='Y'的执行计划。
结论:
绑定变量窥视只发生在硬解析,执行计划是根据第一次传递进来的实际参数来确定的,这样会存在绑定变量选择错误执行计划的可能。
引申一下:
如果我们先执行processed :='N'再执行processed :='Y',会是什么样的情况呢?
variable processed varchar2(1);
exec :processed :='N';
select count(*) from w_1 t1 where t1.processed=:processed ;
exec :processed :='Y';
select count(*) from w_1 t1 where t1.processed=:processed ;

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/35489/viewspace-85133/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/35489/viewspace-85133/

你可能感兴趣的:(数据库)