日期:2014-05-16  浏览次数:20395 次

(2)简单B树访问——计算执行计划的成本

通过索引来访问表的成本公式应该包含3个与块相关的组件:按降序遍历的分支层数、遍历 叶块的数目和访问过的表块的数目。

1、入门

SQL> create table t1 
  2  nologging
  3  as
  4  select
  5  trunc(dbms_random.value(0,25))n1,   --n1会产生25个不同的值。
  6  rpad('x',40)ind_pad,                --只有一个值。
  7  trunc(dbms_random.value(0,20))n2,   --n2会产生20个不同的值。
  8  lpad(rownum,10,'0')small_vc,
  9  rpad('x',200)padding
 10  from
 11  all_objects
 12  where
rownum  <= 10000
 14  ;

表已创建。

SQL> create index t1_i1 on t1(n1, ind_pad, n2) 
  2  nologging
  3  pctfree 91
  4  ;

索引已创建。

SQL> begin
  2  dbms_stats.gather_table_stats(
  3  user,
  4  't1',
  5  cascade => true,
  6  estimate_percent => null,
  7  method_opt => 'for all columns size 1'
  8  );
  9  end;
 10  /

PL/SQL 过程已成功完成。
SQL> select t.TABLE_NAME, t.NUM_ROWS, t.BLOCKS
  2    from user_tables t
  3   where table_name = 'T1';

TABLE_NAME                       NUM_ROWS     BLOCKS
------------------------------ ---------- ----------
T1                                  10000        387

SQL> select s.table_name,
  2         s.column_name,
  3         s.num_distinct,
  4         s.density,
  5         s.num_nulls,
  6         s.avg_col_len
  7    from user_tab_col_statistics s
  8   where table_name = 'T1';

TABLE_NAME                     COLUMN_NAME                    NUM_DISTINCT    DENSITY  NUM_NULLS AVG_COL_LEN
------------------------------ ------------------------------ ------------ ---------- ---------- -----------
T1                             N1                                       25        .04          0           3
T1                             IND_PAD                                   1          1          0          41
T1                             N2                                       20        .05          0           3
T1                             SMALL_VC                              10000      .0001          0          11
T1                             PADDING                                   1          1          0         201

SQL> select i.index_name,
  2         i.table_name,
  3         i.blevel,
  4         i.leaf_blocks,
  5         i.distinct_keys,
  6         i.clustering_factor,
  7         i.num_rows
  8    from user_indexes i
  9   where index_name = 'T1_I1';

INDEX_NAME                     TABLE_NAME                         BLEVEL LEAF_BLOCKS DISTINCT_KEYS CLUSTERING_FACTOR   NUM_ROWS
------------------------------ ------------------------------ ---------- ----------- ------------- ----------------- ----------
T1_I1                          T1                                      2        1111           500              9752      10000

可以看见DISTINCT_KEYS等于索引列的NUM_DISTINCT相乘 = 25 * 1 * 20 = 500。一共有500种组合,没种组合对应的行数就是20行(10000 / 500 = 20)。

SQL> select small_vc
  2    from t1
  3   where n1 = 2
  4     and ind_pad = rpad('x', 40)
  5     and n2 = 3;

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

-------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------
|   0 | SELECT STATEMENT            |       |    20 |  1160 |    25   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1    |    20 |  1160 |    25   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T1_I1 |    20 |       |     5   (0)| 00:00:01 |
-------------------------------------------------

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

   2 - access("N1"=2 AND "IND_PAD"='x
              ' AND "N2"=3)

索引选择率:n1选择率*ind_pad选择率*n2选择率=1/25 * 1 * 1/20 = 1/500 = 1 / DISTINCT_KEYS

返回基数:1 / DISTINCT_KEYS * NUM_ROWS = 1/500 * 10000 = 20。

通过索引查询的成本为25,基数为20。基数估算非常准确,但成本又是如何得出的呐?CBO认为一共读取了20行数据,对访问表的成本为20不应该感到惊奇。索引的成本是5,可以