什么时候CBO会选择全表扫描

本文通过创建两个具有不同ID分布的表t1和t2,对比分析了Oracle Cost-Based Optimizer (CBO)在不同情况下选择索引扫描或全表扫描的策略。当ID值连续集中时,CBO倾向于使用索引;而当ID值分布均匀时,则更可能选择全表扫描。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么时候CBO会选择全表扫描
1.创建两个表t1,t2,并分别在id列创建索引,注意id列取值的不同方式。插入数据后对表做表分析
create table t1 as 
select trunc((rownum-1)/100) id,
       rpad(rownum,100) tpad 
  from dba_source 
 where rownum<=10000;


create index t1_index1 on t1(id);


zo_leave@orcl> exec dbms_stats.gather_table_stats('SYS','t1',method_opt=>'FOR AL
L COLUMNS SIZE 1', CASCADE=>TRUE);


create table t2 as
select mod(rownum,100) id,
       rpad(rownum,100) tpad
  from dba_source
 where rownum<=10000;     


create index t2_index1 on t2(id); 


zo_leave@orcl> EXEC DBMS_STATS.GATHER_TABLE_STATS('SYS','T2',METHOD_OPT=>'FOR AL
L COLUMNS SIZE 1',CASCADE=>TRUE);


PL/SQL 过程已成功完成。  


zo_leave@orcl> SELECT COUNT(*) FROM T1 WHERE ID = '1';


  COUNT(*)
----------
       100


已用时间:  00: 00: 00.00
zo_leave@orcl> SELECT COUNT(*) FROM T2 WHERE ID = '1';


  COUNT(*)
----------
       100


已用时间:  00: 00: 00.01


可以看到在t1,t2表中ID为1的值都是100条,那么执行计划是否应该一致,而且返回值为1%,这个查询是否一定会走索引呢
look
t1的执行计划
zo_leave@orcl> select * from t1 where id = '1';


已选择100行。


已用时间:  00: 00: 00.00


执行计划
----------------------------------------------------------
Plan hash value: 3353991022


--------------------------------------------------------------------------------
---------
| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| T
ime     |
--------------------------------------------------------------------------------
---------
|   0 | SELECT STATEMENT            |           |   100 | 10300 |     3   (0)| 0
0:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1        |   100 | 10300 |     3   (0)| 0
0:00:01 |
|*  2 |   INDEX RANGE SCAN          | T1_INDEX1 |   100 |       |     1   (0)| 0
0:00:01 |
--------------------------------------------------------------------------------
---------


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


   2 - access("ID"=1)




统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         19  consistent gets
          0  physical reads
          0  redo size
      12177  bytes sent via SQL*Net to client
        482  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed
很明显走了索引,执行计划也给出了准确的返回结果行
再看t2


zo_leave@orcl> select * from t2 where id = '1';


已选择100行。


已用时间:  00: 00: 00.00


执行计划
----------------------------------------------------------
Plan hash value: 1513984157


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   100 | 10300 |    43   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   |   100 | 10300 |    43   (0)| 00:00:01 |
--------------------------------------------------------------------------


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


   1 - filter("ID"=1)




统计信息
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        163  consistent gets
          0  physical reads
          0  redo size
      11855  bytes sent via SQL*Net to client
        482  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed
恩?t2居然不走索引,他选择了全表扫描,而且预计返回值也是正确的,和t1相比
这是为什么呢?


我们来查询下表t1,t2的数据行和数据块统计信息
select table_name,num_rows,blocks from user_tables where table_name = 'T1';     
zo_leave@orcl> select table_name,num_rows,blocks from user_tables where table_na
me = 'T1';


TABLE_NAME                       NUM_ROWS     BLOCKS
------------------------------ ---------- ----------
T1                                  10000        152    


zo_leave@orcl> select table_name,num_rows,blocks from user_tables where table_na
me = 'T2';


TABLE_NAME                       NUM_ROWS     BLOCKS
------------------------------ ---------- ----------
T2                                  10000        152


已用时间:  00: 00: 00.03
块数和数据行也一致,到底是为什么呢?


让我们回到对t1,t2表的插入id数据的这个过程      
t1表:trunc((rownum-1)/100) id
zo_leave@orcl> declare
  2  a number;
  3  begin
  4    for i in 1..10 loop
  5      select trunc((i-1)/100)  into a from dual;
  6      dbms_output.put_line(a);
  7      end loop;
  8      end ;
  9  /
0
0
0
0
0
0
0
0
0
0


PL/SQL 过程已成功完成。


已用时间:  00: 00: 00.00


t2表:mod(rownum,100) id
zo_leave@orcl> declare
  2  a number;
  3  begin
  4    for i in 1..10 loop
  5      select mod(i,100)  into a from dual;
  6      dbms_output.put_line(a);
  7      end loop;
  8      end ;
  9  /
1
2
3
4
5
6
7
8
9
10


PL/SQL 过程已成功完成。


已用时间:  00: 00: 00.01
可以很明显的看到,t1表示100个0,100个1,100个2。。。存放数据,一个是0-99顺序存放一次,再从头开始,0-99顺序存放一次
我们在查询select * from tablename where id = '1'的时候
t1表只需要访问几个数据块就能取得查询所需的100行数据,而t2的数据行在物理上是零散存储的,对它的查询几乎需要读取大部
分的数据块来获取同样的100行数据。优化器计算出使用索引来读取每一个数据块的时间可能比直接用全表扫描,然后直接舍弃不
需要的数据花掉的时间还长

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

转载于:http://blog.itpub.net/26477854/viewspace-1072535/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值