Oracle轉換user_tab_columns中high_value值為十進位制

shuangoracle發表於2012-07-09
今天本想計算下基於範圍的索引選擇率,但是通過user_tab_columns查詢出來low_value和high_value結果是raw型別的,而且我們知道它是擷取了後32位,
如果我想知道他們代表確切的十進位制數將是什麼呢?折騰了半天從網上查到計算方法(以前用過忘了),記錄下備用:
從user_tab_columns查出的結果:
SQL> select table_name,column_name,low_value,high_value from user_tab_columns where table_name='T1' and column_name='CLUSTERED';
TABLE COLUMN_NAM LOW_VALUE  HIGH_VALUE
----- ---------- ---------- ----------
T1    CLUSTERED  80         C164
實際最大最小值是這樣的:
SQL> select min(CLUSTERED) min_value,max(CLUSTERED) max_value from t1;
 MIN_VALUE  MAX_VALUE
---------- ----------
         0         99
         
用utl_raw包的cast_to_number函式進行型別轉換如下:
SQL> select table_name,
  2         column_name,
  3         utl_raw.cast_to_number(low_value) low,
  4         utl_raw.cast_to_number(high_value) hight
  5    from user_tab_col_statistics
  6   where table_name = 'T1'
  7     and column_name = 'CLUSTERED';
TABLE COLUMN_NAM        LOW      HIGHT
----- ---------- ---------- ----------
T1    CLUSTERED           0         99
ok,和表中時間最小最大值一致。

low_value和high_value對優化器選擇執行計劃有什麼影響?下面這個測試有點白痴,統計資訊沒有變,Oracle很自然地選擇了和之前一樣的執行計劃嘍。
測試資料:
create table t(id,name)
as select rownum rn,'A' from dual connect by level<=10000;
create index idx_t_id on t(ID);
analyze table t compute statistics for table for all indexes for all indexed columns;
SQL> select table_name,column_name,high_value,low_value from user_tab_columns where table_name='T' and column_name='ID';

TABLE COLUMN_NAM HIGH_VALUE LOW_VALUE
----- ---------- ---------- ----------
T     ID         C302       C102
插一句:是不是好奇high_value和low_value怎麼算的?
SQL> select upper(replace(substr(dump(min(id), 16, 0, 32),
  2                instr(dump(min(id), 16, 0, 32), ':', 1) + 1),',','0')) low_value,
  3         upper(replace(substr(dump(max(id), 16, 0, 32),
  4                instr(dump(max(id), 16, 0, 32), ':', 1) + 1),',','0')) high_value
  5   from t;
LOW_VALUE  HIGH_VALUE
---------- ----------
 C102       C302
 
即在原先最小值基礎上取dump,轉換為16進位制,然後取前32位資料。將取得資料逗號替換為0即可。
SQL> select table_name,
  2         column_name,
  3         utl_raw.cast_to_number(low_value) low,
  4         utl_raw.cast_to_number(high_value) hight
  5    from user_tab_col_statistics
  6   where table_name = 'T' and column_name='ID';
TABLE COLUMN_NAM        LOW      HIGHT
----- ---------- ---------- ----------
T     ID                  1      10000

當id<100時走的是索引範圍掃描:
SQL> select  * from t where id<100;
已選擇99行。

執行計劃
----------------------------------------------------------
Plan hash value: 514881935
------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |    99 |   594 |     3 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T        |    99 |   594 |     3 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_ID |    99 |       |     2 |
------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("ID"<100)
   
SQL> delete from t where id>=101;
已刪除9900行。
SQL> commit;
提交完成。
如下查詢說證明明統計資訊沒有變:
SQL> select table_name,
  2         column_name,
  3         utl_raw.cast_to_number(low_value) low,
  4         utl_raw.cast_to_number(high_value) hight
  5    from user_tab_col_statistics
  6   where table_name = 'T' and column_name='ID';
TABLE COLUMN_NAM        LOW      HIGHT
----- ---------- ---------- ----------
T     ID                  1      10000
SQL> select  * from t where id<100;
已選擇99行。

執行計劃
----------------------------------------------------------
Plan hash value: 514881935
------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |    99 |   594 |     3 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T        |    99 |   594 |     3 |
|*  2 |   INDEX RANGE SCAN          | IDX_T_ID |    99 |       |     2 |
------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("ID"<100)
可以看到海是走了索引範圍掃描,而此時表裡只剩下100個資料而已。

重新收集下統計資訊看看:
SQL> analyze table t compute statistics for table for all indexes for all indexed columns;
表已分析。
SQL> select table_name,
  2         column_name,
  3         utl_raw.cast_to_number(low_value) low,
  4         utl_raw.cast_to_number(high_value) hight
  5    from user_tab_col_statistics
  6   where table_name = 'T' and column_name='ID';
TABLE COLUMN_NAM        LOW      HIGHT
----- ---------- ---------- ----------
T     ID                  1        100
ok統計資訊已經變了。
SQL> select  * from t where id<100;
已選擇99行。

執行計劃
----------------------------------------------------------
Plan hash value: 1601196873
----------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost  |
----------------------------------------------------------
|   0 | SELECT STATEMENT  |      |   100 |   500 |     4 |
|*  1 |  TABLE ACCESS FULL| T    |   100 |   500 |     4 |
----------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("ID"<100)
執行計劃變了。

也就是說high_value和low_value反應了該列的最大最小值。
此例說明,如果資料改變了而不及時收集統計資訊,很可能會造成優化器選擇錯誤的執行計劃。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24496749/viewspace-734991/,如需轉載,請註明出處,否則將追究法律責任。

相關文章