-- preparation drop table test1 purge; create table test1(id int,name varchar2(100)) tablespace users; insert into test1 (id, name) select rownum as id, 'name_'||rownum as name from dual connect by rownum <= 100000; update test1 set id = 1 where id <= 10; update test1 set id = 2 where id between 11 and 2000; update test1 set id = 3 where id > 2000; commit; create index idx_test1_n1 on test1 (id); -- calc the histogram begin dbms_stats.gather_table_stats(user, 'TEST1', cascade => true, method_opt => 'FOR All INDEXED COLUMNS'); end; / -- check data count select id,count(1) from test1 group by id order by id; ID COUNT(1) ----- ---------- 1 10 2 1990 3 98000 |
set lines 120; spool e:\bind_peeking_10g.log select * from v$version; alter session set cursor_sharing=similar; -- histogram select column_name, histogram from user_tab_cols where table_name='TEST1'; alter system flush shared_pool; prompt '先大量再少量' var id number; exec :id := 3; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); exec :id := 1; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); alter system flush shared_pool; prompt '先少量再大量' exec :id := 1; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); exec :id := 3; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); prompt '随机执行几次' exec :id := 3; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); exec :id := 2; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); spool off |
BANNER ---------------------------------------------------------------- Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Prod PL/SQL Release 10.2.0.4.0 - Production CORE 10.2.0.4.0 Production TNS for Linux: Version 10.2.0.4.0 - Production NLSRTL Version 10.2.0.4.0 - Production 。。。。。。 '先少量再大量' COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 10 name_9 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 36 | 468 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 36 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 98000 name_99999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 36 | 468 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 36 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 到这里我们就可以看到,因为第一次使用的值选择性高,所以CBO生成了使用索引的计划。后面使用不同的绑定值,计划不会发生改变 '随机执行几次' COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 98000 name_99999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 36 | 468 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 36 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 1990 name_999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 36 | 468 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 36 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 随后的几次执行中,执行计划也不会根据绑定值的变化而变化。 |
。。。。 ---- check adaptive cursor sharing info select child_number, is_bind_sensitive, is_bind_aware, is_shareable from v$sql where sql_id = 'bvqnv1dsynqdj' order by child_number; prompt '随机执行几次' exec :id := 3; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); exec :id := 2; select count(1),max(name) from test1 where id = :id; select * from table(dbms_xplan.display_cursor); ---- check adaptive cursor sharing info select child_number, is_bind_sensitive, is_bind_aware, is_shareable from v$sql where sql_id = 'bvqnv1dsynqdj' order by child_number; |
BANNER -------------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production PL/SQL Release 11.1.0.6.0 - Production CORE 11.1.0.6.0 Production TNS for 32-bit Windows: Version 11.1.0.6.0 - Production NLSRTL Version 11.1.0.6.0 - Production 。。。 '先少量再大量' COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 10 name_9 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 1 | 13 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 1 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 98000 name_99999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 0 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 1 | 13 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 1 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 到这里都和10g中的情况一样,使用第一次的绑定值生成索引扫描的计划 ---- check adaptive cursor sharing info select child_number, is_bind_sensitive, is_bind_aware, is_shareable from v$sql where sql_id = 'bvqnv1dsynqdj' order by child_number; CHILD_NUMBER I I I ------------ - - - 0 Y N Y 到这里还只有一个cursor,11g新增的字段含义如下 is_bind_sensitive indicates not only whether bind variable peeking was used to generate the execution plan but also whether the execution plan depends on the peeked value. If this is the case, the column is set to Y; otherwise, it¡¯s set to N. is_bind_aware indicates whether the cursor is using extended cursor sharing. If yes, the column is set to Y; if not, it¡¯s set to N. If set to N, the cursor is obsolete, and it will no longer be used. is_shareable indicates whether the cursor can be shared. If it can, the column is set to Y; otherwise, it¡¯s set to N. If set to N, the cursor is obsolete, and it will no longer be used. '随机执行几次' COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 98000 name_99999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 1 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 3896847026 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 103 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | |* 2 | TABLE ACCESS FULL| TEST1 | 98167 | 1246K| 103 (1)| 00:00:02 | ---------------------------------------------------------------------------- 注意这里,因为使用了绑定值3,CBO重新生成了更适合的full table scan COUNT(1) MAX(NAME) ---------- ---------------------------------------------------------------------------------------------------- 1990 name_999 PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID bvqnv1dsynqdj, child number 2 ------------------------------------- select count(:"SYS_B_0"),max(name) from test1 where id = :id Plan hash value: 1876089611 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 10 (100)| | | 1 | SORT AGGREGATE | | 1 | 13 | | | | 2 | TABLE ACCESS BY INDEX ROWID| TEST1 | 1824 | 23712 | 10 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | IDX_TEST1_N1 | 1824 | | 4 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 注意这里,因为使用了绑定值2,CBO仍然使用更适合的index range scan CHILD_NUMBER I I I ------------ - - - 0 Y N N 1 Y Y Y 2 Y Y Y 这里可以看到,原始生成的cursor已经被废弃(is_shareable=N),同时又生成了2个不同的cursor(child_number=1,2)。我们可以理解为这是2个不同的执行计划。 |