ORACLE直方圖(10g)
為什麽需要直方圖 ?當表中一列數據比較的值分布比較均勻時,optimzer可以很好的通過最大值,最小值和NDV(唯一值的個數),就可以判斷出cardinality.對於cardinality越精確,optimzer就可以更加好的選擇執行計劃。
--創建測試表並插入數據
create table t1(a int,b varchar2(100));
begin
for i in 1..100 loop
insert into t1 values (1,‘abcd‘);
end loop;
commit;
end;
/
begin
for i in 1..100 loop
insert into t1 values (2,‘efg‘);
end loop;
commit;
end;
/
---收集統計信息
exec dbms_stats.gather_table_stats(tabname => ‘t1‘,ownname => user,method_opt => ‘for all columns size 1‘); --for all columns size 1 不收集直方圖信息
---執行一個語句來看看optimizer評估的行
explain plan for select * from t1 where a=1;
select * from table(dbms_xplan.display());
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 700 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T2 | 100 | 700 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
返回100行,說明優化器在這種數據平均分布的情況下評估很準確。現在insert into t1 values(3,‘mnb‘); 一行,人為的模擬數據分布不均,再次收集統計信息
explain plan for select * from t1 where a=3;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1513984157
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 67 | 469 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T2 | 67 | 469 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
優化器評估為67行.計算公式為 rows/ndv=(200/3)=66.66666
看看收集了集方圖後的結果
SQL> exec dbms_stats.gather_table_stats(tabname => ‘T1‘,ownname => user,method_opt => ‘FOR ALL COLUMNS SIZE AUTO‘);
SQL> explain plan for select * from t1 where a=3;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1513984157
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T2 | 1 | 7 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
可以看出通過增加了直方圖,oracle比較準確的評估了cardinality。
SQL> select column_name,histogram from user_tab_col_statistics where table_name=‘T2‘;
COLUMN_NAME HISTOGRAM
------------------------------ ---------------
A FREQUENCY --頻率直方圖
B NONE
直方圖分為兩種頻率直方圖和高度平衡直方圖
直方圖的限制:1,收集直方圖有開銷,如cpu和磁盤空間;2,對於每個欄位超過254的distinct value,頻率直方圖的作用開始下降
隨著NDV的增加,精度進一步下降,這時候只能使用高度平衡直方圖.3,對於字符類型,只能收集前32個字節;
4,在非索引的欄位上收集直方圖的效果有限.
高度平衡和頻率直方圖的選擇:對於某個欄位的NDV小於所定義的桶數,使用頻率直方圖,否則使用高度平衡直方圖。兩種方式的最大的桶數為254,
SQL> create table t2(a int);
begin
for i in 1..76 loop
insert into t2 values (i);
end loop;
commit;
end;
/
SQL> select count(distinct a) from t2; --insert 76種不同的值
COUNT(DISTINCTA)
----------------
76
SQL> exec dbms_stats.gather_table_stats(tabname => ‘T2‘,ownname => user,method_opt => ‘FOR COLUMNS A SIZE 75‘);
人為的定義桶數小於NDV,在這種條件,oracle會使用高度平衡直方圖,因為頻率直方圖75個bucket容不下76
SQL> select column_name,histogram from user_tab_col_statistics where table_name=‘T2‘;
COLUMN_NAME HISTOGRAM
------------------------------ ---------------
A HEIGHT BALANCED
對於頻率直方圖,如果NDV小於254的情況,ndv應該是和桶數相等的.有些bug會產生不一致,導致評估不準確,具體可以參考metalink的相關bug。
SQL> select count(b.endpoint_value) from user_histograms b where table_name=‘T1‘ and column_name=‘A‘;
COUNT(B.ENDPOINT_VALUE)
-----------------------
3
SQL> select table_name,column_name,num_distinct from user_tab_col_statistics where table_name=‘T1‘ and column_name=‘A‘;
TABLE_NAME COLUMN_NAME NUM_DISTINCT
------------------------------ ------------------------------ ------------
T2 A 3
一般建議的收集方法為‘FOR ALL COLUMNS SIZE AUTO‘,除非有很好的理由去更改,由oracle自行決定是否需要histogram和桶數
為什麽需要直方圖 ?當表中一列數據比較的值分布比較均勻時,optimzer可以很好的通過最大值,最小值和NDV(唯一值的個數),就可以判斷出cardinality.對於cardinality越精確,optimzer就可以更加好的選擇執行計劃。
--創建測試表並插入數據create table t1(a int,b varchar2(100));beginfor i in 1..100 loopinsert into t1 values (1,‘abcd‘);end loop;commit;end;/beginfor i in 1..100 loopinsert into t1 values (2,‘efg‘);end loop;commit;end;/---收集統計信息exec dbms_stats.gather_table_stats(tabname => ‘t1‘,ownname => user,method_opt => ‘for all columns size 1‘); --for all columns size 1 不收集直方圖信息
---執行一個語句來看看optimizer評估的行explain plan for select * from t1 where a=1;select * from table(dbms_xplan.display());--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 100 | 700 | 3 (0)| 00:00:01 ||* 1 | TABLE ACCESS FULL| T2 | 100 | 700 | 3 (0)| 00:00:01 |--------------------------------------------------------------------------返回100行,說明優化器在這種數據平均分布的情況下評估很準確。現在insert into t1 values(3,‘mnb‘); 一行,人為的模擬數據分布不均,再次收集統計信息explain plan for select * from t1 where a=3;PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------Plan hash value: 1513984157--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 67 | 469 | 3 (0)| 00:00:01 ||* 1 | TABLE ACCESS FULL| T2 | 67 | 469 | 3 (0)| 00:00:01 |--------------------------------------------------------------------------優化器評估為67行.計算公式為 rows/ndv=(200/3)=66.66666看看收集了集方圖後的結果SQL> exec dbms_stats.gather_table_stats(tabname => ‘T1‘,ownname => user,method_opt => ‘FOR ALL COLUMNS SIZE AUTO‘);SQL> explain plan for select * from t1 where a=3;PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------Plan hash value: 1513984157--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 ||* 1 | TABLE ACCESS FULL| T2 | 1 | 7 | 3 (0)| 00:00:01 |--------------------------------------------------------------------------可以看出通過增加了直方圖,oracle比較準確的評估了cardinality。SQL> select column_name,histogram from user_tab_col_statistics where table_name=‘T2‘;COLUMN_NAME HISTOGRAM------------------------------ ---------------A FREQUENCY --頻率直方圖B NONE直方圖分為兩種頻率直方圖和高度平衡直方圖直方圖的限制:1,收集直方圖有開銷,如cpu和磁盤空間;2,對於每個欄位超過254的distinct value,頻率直方圖的作用開始下降隨著NDV的增加,精度進一步下降,這時候只能使用高度平衡直方圖.3,對於字符類型,只能收集前32個字節;4,在非索引的欄位上收集直方圖的效果有限.高度平衡和頻率直方圖的選擇:對於某個欄位的NDV小於所定義的桶數,使用頻率直方圖,否則使用高度平衡直方圖。兩種方式的最大的桶數為254,SQL> create table t2(a int);beginfor i in 1..76 loopinsert into t2 values (i);end loop;commit;end;/SQL> select count(distinct a) from t2; --insert 76種不同的值COUNT(DISTINCTA)---------------- 76SQL> exec dbms_stats.gather_table_stats(tabname => ‘T2‘,ownname => user,method_opt => ‘FOR COLUMNS A SIZE 75‘);人為的定義桶數小於NDV,在這種條件,oracle會使用高度平衡直方圖,因為頻率直方圖75個bucket容不下76SQL> select column_name,histogram from user_tab_col_statistics where table_name=‘T2‘;COLUMN_NAME HISTOGRAM------------------------------ ---------------A HEIGHT BALANCED
對於頻率直方圖,如果NDV小於254的情況,ndv應該是和桶數相等的.有些bug會產生不一致,導致評估不準確,具體可以參考metalink的相關bug。SQL> select count(b.endpoint_value) from user_histograms b where table_name=‘T1‘ and column_name=‘A‘;COUNT(B.ENDPOINT_VALUE)----------------------- 3SQL> select table_name,column_name,num_distinct from user_tab_col_statistics where table_name=‘T1‘ and column_name=‘A‘;TABLE_NAME COLUMN_NAME NUM_DISTINCT------------------------------ ------------------------------ ------------T2 A 3一般建議的收集方法為‘FOR ALL COLUMNS SIZE AUTO‘,除非有很好的理由去更改,由oracle自行決定是否需要histogram和桶數
ORACLE直方圖(10g)