ヒストグラムの初歩的な理解

10105 ワード

参照
システム内のいくつかのテーブルに高度に不均一なデータ分布がある場合、ヒストグラムを使用すると、より良い選択的評価が生成され、より最適化された実行計画が生成されます.
次の例でヒストグラムの役割を感じます
ベースデータ
drop user sure cascade;
create user sure identified by oracle;
grant resource to sure;
create table sure.tab (a number, b number);

1万件のデータを挿入
begin
for i in 1..10000 loop
insert into sure.tab values (i, i);
end loop;
commit;
end;
/

せいぞう
不均一な場合
update sure.tab set b=5 where b between 6 and 9995;
commit;

このときb列のデータは
select b, count(*) from sure.tab group by b order by b;
         B   COUNT(*)
---------- ----------
         1          1
         2          1
         3          1
         4          1
         5       9991
      9996          1
      9997          1
      9998          1
      9999          1
     10000          1
    
【1】インデックスを作成する前に、クエリb=1またはb=5にかかわらず、移動するしかない
全表スキャン.
explain plan for select * from sure.tab where b=1;
select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    26 |     6   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB  |     1 |    26 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------
explain plan for select * from sure.tab where b=5;
select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  9991 |   253K|     6   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB  |  9991 |   253K|     6   (0)| 00:00:01 |
--------------------------------------------------------------------------

【2】b列にインデックスを作成する
create index sure.ix_tab_b on sure.tab(b);
explain plan for select * from sure.tab where b=1;
select * from table(dbms_xplan.display);
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     1 |    26 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TAB      |     1 |    26 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TAB_B |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
   - dynamic sampling used for this statement
18 rows selected.
explain plan for select * from sure.tab where b=5;
select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  9991 |   253K|     6   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB  |  9991 |   253K|     6   (0)| 00:00:01 |
--------------------------------------------------------------------------
   - dynamic sampling used for this statement
17 rows selected

ネット上の例では、インデックスがあればINDEX RANGE SCANを歩くべきだが、実際の状況(10201)では、b=5の場合、正しいパスが選択されている--
全表スキャン.統計がないため、Oracleは
ダイナミックサンプリングは、一時的に統計を収集することに相当するので、この場合は逆に正確です.
【3】統計は収集するがヒストグラムは収集しない
analyze table sure.tab compute statistics;
統計に関連するビュー
select num_rows, blocks, empty_blocks, avg_space, chain_cnt, avg_row_len
from dba_tables where table_name = 'TAB';
  NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE  CHAIN_CNT AVG_ROW_LEN
---------- ---------- ------------ ---------- ---------- -----------
     10000         20            4       2080          0          10
   
col low_value format a16  
col high_value format a16
col column_name format a16
select column_name, num_distinct, low_value, high_value, density, num_buckets
from dba_tab_columns where table_name = 'TAB';
COLUMN_NAME      NUM_DISTINCT LOW_VALUE        HIGH_VALUE          DENSITY NUM_BUCKETS
---------------- ------------ ---------------- ---------------- ---------- -----------
A                       10000 C102             C302                  .0001           1
B                          10 C102             C302                     .1           1

select table_name, column_name, endpoint_number, endpoint_value
from dba_tab_histograms where table_name = 'TAB';
TABLE_NA COLUMN_NAME      ENDPOINT_NUMBER ENDPOINT_VALUE
-------- ---------------- --------------- --------------
TAB      B                              0              1
TAB      A                              0              1
TAB      B                              1          10000
TAB      A                              1          10000

実行計画の観察
explain plan for select * from sure.tab where b=1;
select * from table(dbms_xplan.display);
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |  1000 |  6000 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TAB      |  1000 |  6000 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TAB_B |  1000 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
explain plan for select * from sure.tab where b=5;
select * from table(dbms_xplan.display);
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |  1000 |  6000 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TAB      |  1000 |  6000 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TAB_B |  1000 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

統計情報があり、
b=5の照会はかえって偏った.
最適化器は、tabテーブルb列の柱状図統計を作成し、その列の各値の具体的な分布を知ることができる.
analyze table sure.tab compute statistics for columns b size 10;
ヒストグラムのENDPOINT_VALUEはカラム値を表し、ENDPOINT_NUMBERは累積した行数を表す.
select table_name, column_name, endpoint_number, endpoint_value
from dba_histograms where table_name = 'TAB';
TABLE_NA COLUMN_NAME      ENDPOINT_NUMBER ENDPOINT_VALUE
-------- ---------------- --------------- --------------
TAB      B                              1              1
TAB      B                              2              2
TAB      B                              3              3
TAB      B                              4              4
TAB      B                           9995              5
TAB      B                           9996           9996
TAB      B                           9997           9997
TAB      B                           9998           9998
TAB      B                           9999           9999
TAB      B                          10000          10000
TAB      A                              0              1
TAB      A                              1          10000

実行計画の観察
explain plan for select * from sure.tab where b=1;
select * from table(dbms_xplan.display);
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     1 |     6 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TAB      |     1 |     6 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IX_TAB_B |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
explain plan for select * from sure.tab where b=5;
select * from table(dbms_xplan.display);
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  9991 | 59946 |     6   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB  |  9991 | 59946 |     6   (0)| 00:00:01 |
--------------------------------------------------------------------------

【まとめ】b=5のクエリにとって、全テーブルスキャンはインデックス範囲スキャンよりも合理的であり、ヒストグラムがあれば、オプティマイザは正確な判断を下すことができる.(統計はありませんが、ダイナミックサンプリングでパスを選択したため、歪んでいます)