1. 对表进行分析,通常情况下可以对表,索引,列进行单独分析,或者进行组合分析,但这三者哪些是相对重要的,哪些分析显得不那么重要?通过本篇文章的实验相信大家也会对直方图有更一步的了解. 
  2.  
  3. 1.首先创建测试表,并插入100000条数据 
  4. SQL> create table test(id number,nick varchar2(30)); 
  5.  
  6. Table created. 
  7.  
  8. SQL> begin 
  9.   2      for i in 1..100000 loop 
  10.   3            insert into test(id) values(i); 
  11.   4      end loop; 
  12.   5      commit
  13.   6  end
  14.   7  / 
  15.  
  16. PL/SQL procedure successfully completed. 
  17.  
  18. 更新nick字段,使数据发生严重倾斜 
  19. SQL> update test set nick='abc' where rownum<99999;  
  20.  
  21. 99998 rows updated. 
  22.  
  23. SQL> commit
  24.  
  25. Commit complete. 
  26.  
  27. SQL> create index idx_test_nick on test(nick); 
  28.  
  29. Index created. 
  30.  
  31.  
  32. SQL> update test set nick='def' where nick is null
  33.  
  34. rows updated. 
  35.  
  36. SQL> commit
  37.  
  38. Commit complete. 
  39.  
  40. --只对索引进行分析 
  41. SQL> analyze index idx_test_nick compute statistics
  42.  
  43. Index analyzed. 
  44.  
  45. SQL> select index_name,LEAF_BLOCKS,DISTINCT_KEYS,NUM_ROWS from user_indexes where index_name='IDX_TEST_NICK'
  46.  
  47. INDEX_NAME                     LEAF_BLOCKS DISTINCT_KEYS   NUM_ROWS 
  48. ------------------------------ ----------- ------------- ---------- 
  49. IDX_TEST_NICK                          210             2     100000 
  50.  
  51.  
  52. SQL> select COLUMN_NAME,NUM_BUCKETS,num_distinct from USER_tab_columns where table_name='TEST'
  53.  
  54. COLUMN_NAME                    NUM_BUCKETS NUM_DISTINCT 
  55. ------------------------------ ----------- ------------ 
  56. ID 
  57. NICK 
  58.  
  59. 查看只分析索引的情况下的执行计划,从执行计划可以看出,优化器选择RBO,都走索引 
  60. SQL> set autotrace trace exp 
  61. SQL> select * from test where nick ='abc'
  62.  
  63. Execution Plan 
  64. ---------------------------------------------------------- 
  65.    0      SELECT STATEMENT Optimizer=CHOOSE 
  66.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' 
  67.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE
  68.  
  69.   
  70.  
  71. SQL> select * from test where nick ='def'
  72.  
  73. Execution Plan 
  74. ---------------------------------------------------------- 
  75.    0      SELECT STATEMENT Optimizer=CHOOSE 
  76.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' 
  77.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE
  78.  
  79.  
  80. 分析有数据严重倾斜的nick列后,也没有产生正确的执行计划,此时使用的优化器仍然是RBO 
  81. 通过此实验,说明只分析索引和列,ORACLE不会使用CBO的优化器 
  82. SQL> analyze table test compute statistics for columns size 2 nick; 
  83.  
  84. Table analyzed. 
  85.  
  86. SQL> select * from test where nick ='abc'
  87.  
  88. Execution Plan 
  89. ---------------------------------------------------------- 
  90.    0      SELECT STATEMENT Optimizer=CHOOSE 
  91.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' 
  92.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE
  93.  
  94.   
  95.  
  96. SQL> select * from test where nick ='def'
  97.  
  98. Execution Plan 
  99. ---------------------------------------------------------- 
  100.    0      SELECT STATEMENT Optimizer=CHOOSE 
  101.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' 
  102.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE
  103.  
  104.  
  105. 现在分析表后,产生了正确的执行计划 
  106. SQL> analyze table test compute statistics for table
  107.  
  108. Table analyzed. 
  109.  
  110. SQL> select * from test where nick ='abc'
  111.  
  112. Execution Plan 
  113. ---------------------------------------------------------- 
  114.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=50 Card=99998 Bytes= 
  115.           1499970) 
  116.  
  117.    1    0   TABLE ACCESS (FULLOF 'TEST' (Cost=50 Card=99998 Bytes=14 
  118.           99970) 
  119.  
  120.  
  121. SQL> select * from test where nick ='def'
  122.  
  123. Execution Plan 
  124. ---------------------------------------------------------- 
  125.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=2 Bytes=30) 
  126.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=2 Byt 
  127.           es=30) 
  128.  
  129.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost 
  130.           =1 Card=2) 
  131.           
  132. 删除所有的统计数据,并只对表与列进行分析,不分析索引,ORACLE使用CBO的优化器,并产生了正确的执行计划 
  133. SQL> analyze table test delete statistics
  134.  
  135. Table analyzed. 
  136.  
  137. SQL> analyze table test compute statistics for table for columns size 2 nick; 
  138.  
  139. Table analyzed. 
  140.  
  141. SQL> select * from test where nick ='abc'
  142.  
  143. Execution Plan 
  144. ---------------------------------------------------------- 
  145.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=50 Card=99998 Bytes= 
  146.           1499970) 
  147.  
  148.    1    0   TABLE ACCESS (FULLOF 'TEST' (Cost=50 Card=99998 Bytes=14 
  149.           99970) 
  150.  
  151. SQL> select * from test where nick ='def'
  152.  
  153. Execution Plan 
  154. ---------------------------------------------------------- 
  155.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=2 Bytes=30) 
  156.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=2 Byt 
  157.           es=30) 
  158.  
  159.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost 
  160.           =1 Card=2) 
  161.  
  162.  
  163. 创建TEST表ID列上的索引,但不对索引进行分析 
  164. SQL> create index idx_test_id on test(id); 
  165.  
  166. Index created. 
  167.  
  168. SQL> select * from test where id=1; 
  169.  
  170. Execution Plan 
  171. ---------------------------------------------------------- 
  172.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1000 Bytes=15 
  173.           000) 
  174.  
  175.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1000 
  176.           Bytes=15000) 
  177.  
  178.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1 
  179.            Card=400) 
  180.  
  181. 当条件中即有id,又有nick时,因为nick上有直方图,ORACLE知道nick='abc'的值特别的多,所以不走IDX_TEST_NICK索引,走IDX_TEST_ID上的索引 
  182. SQL> select * from test where id=5 and nick='abc'
  183.  
  184. Execution Plan 
  185. ---------------------------------------------------------- 
  186.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1000 Bytes=15 
  187.           000) 
  188.  
  189.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1000 
  190.           Bytes=15000) 
  191.  
  192.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1 
  193.            Card=400) 
  194.  
  195. 当条件中即有id,又有nick时,因为nick上有直方图,ORACLE知道nick='def'的值特别的少,所以走IDX_TEST_NICK上的索引,不走IDX_TEST_ID索引 
  196.  
  197.  
  198. SQL> select * from test where id=5 and nick='def'
  199.  
  200. Execution Plan 
  201. ---------------------------------------------------------- 
  202.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=15) 
  203.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1 Byt 
  204.           es=15) 
  205.  
  206.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_NICK' (NON-UNIQUE) (Cost 
  207.           =1 Card=2) 
  208.  
  209. 在分析ID列后,ORACLE发现ID列的选择度更高,所以不再选择IDX_TEST_NICK索引,而是选择IDX_TEST_ID 
  210. SQL> analyze table test compute statistics for columns size 1 id; 
  211.  
  212. Table analyzed. 
  213.  
  214. SQL> select * from test where id=5 and nick='def'
  215.  
  216. Execution Plan 
  217. ---------------------------------------------------------- 
  218.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=7) 
  219.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1 Byt 
  220.           es=7) 
  221.  
  222.    2    1     INDEX (RANGE SCAN) OF 'IDX_TEST_ID' (NON-UNIQUE) (Cost=1 
  223.            Card=1) 
  224.  
  225. 下面来看另外一种情况,我们删除所有的统计数据,然后在ID列上创建唯一索引,在此条件下,只分析表与分析列nick,我们看到ORACLE走了正确的执行计划,走了UK_TEST_ID,其实从这里也给我们带来很多的启示:在主键与唯一键约束的列上是否需要直方图的问题?如果在这些列上有像这样的查询where id > 100 and id < 1000,我们还是需要有直方图的,但除此之外,好像真的没有直方图的必要了! 
  226. SQL> analyze table test delete statistics;    
  227.  
  228. Table analyzed. 
  229.  
  230. SQL> drop index idx_test_id; 
  231.  
  232. Index dropped. 
  233.  
  234. SQL> create unique index uk_test_id on test(id); 
  235.  
  236. Index created. 
  237.  
  238. SQL> analyze table test compute statistics for table for columns size 2 nick; 
  239.  
  240. Table analyzed. 
  241.  
  242. SQL> select * from test where id=5 and nick='def'
  243.  
  244. Execution Plan 
  245. ---------------------------------------------------------- 
  246.    0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=15) 
  247.    1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TEST' (Cost=1 Card=1 Byt 
  248.           es=15) 
  249.  
  250.    2    1     INDEX (UNIQUE SCAN) OF 'UK_TEST_ID' (UNIQUE) (Cost=1 Car 
  251.           d=1) 
  252.  
  253. 从以上一系列的实验可以看出,对ORACLE的优化器CBO来说,表的分析与列的分析才是最重要的,索引的分析次之。还有我们可以考虑我们的哪些列上需要直方图,对于bucket的个数问题,oracle的默认值是75个,所以根据你的应用规则,选择合适的桶数对性能也是有帮助的。因为不必要的桶的个数的大量增加,必然会带来SQL语句硬解析时产生执行计划的复杂度问题。