直方图有2中类型,等频与等高。等频就是在收集统计信息的时候,指定的桶数大于唯一值的数量,但是不能超过254个。如果在收集统计信息的时候指定的桶的数量少于唯一值的数量,或唯一值的数量大于254那么就成了等高直方图。下面实验
SQL> select id,count(*) from test group by id;
ID COUNT(*)
---------- ----------
1 10
5 365
99 10000
10 100
使用5个桶收集下信息
SQL> exec dbms_stats.gather_table_stats(ownname=>'baixyu',tabname=>'test',method_opt=>'for columns id size 5');
SQL> select table_name,column_name,num_distinct,num_buckets,histogram from user_tab_col_statistics where table_name='TEST';
TABLE_NAME COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
-------------------- -------------------- ------------ ----------- --------------------
TEST ID 4 4 FREQUENCY
我们看到收集的统计信息是等频的直方图,但是实际上用的桶数是4,与列的唯一值的数量是一样的。
oracle在自动收集统计信息的时候,要看该列是否被使用过,没有使用过的列是不收集统计信息的,查看列是否被使用过可以看sys.col_usage$视图,查看直方图的endpoint信息
SQL> select table_name,column_name,endpoint_number,endpoint_value from dba_tab_histograms where table_name='TEST' and column_name='ID';
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
-------------------- -------------------- --------------- --------------
TEST ID 10 1
TEST ID 375 5
TEST ID 475 10
TEST ID 10475 99
看到有4条记录,每个endpoint_value就是列的唯一值,endpoint_number就是唯一值的记录数,是个累加值。1的数量就是10,5的数量就是375-10=365
下面看下等高直方图,先删除之前的等频直方图的信息
SQL> exec dbms_stats.gather_table_stats(ownname=>'baixyu',tabname=>'test',method_opt=>'for columns id size 1');
然后使用3个桶来收集直方图的信息
SQL> exec dbms_stats.gather_table_stats(ownname=>'baixyu',tabname=>'test',method_opt=>'for columns id size 3');
SQL> select table_name,column_name,num_distinct,num_buckets,histogram from user_tab_col_statistics where table_name='TEST';
TABLE_NAME COLUMN_NAME NUM_DISTINCT NUM_BUCKETS HISTOGRAM
-------------------- -------------------- ------------ ----------- --------------------
TEST ID 4 3 HEIGHT BALANCED
看到现在是等高的直方图信息了,在看下endpoint的信息
SQL> select table_name,column_name,endpoint_number,endpoint_value from dba_tab_histograms where table_name='TEST' and column_name='ID';
TABLE_NAME COLUMN_NAME ENDPOINT_NUMBER ENDPOINT_VALUE
-------------------- -------------------- --------------- --------------
TEST ID 3 99
TEST ID 0 1
TEST ID 0 1
TEST ID 1 1
下面是《基于oracle的sql优化》书中提到的计算value的方法,
对于等高直方图,先对列的值排序,然后每个桶中存放的数量就是行总数/buckets,然后bucket在0开始,一个到bucket_num来存放数据,0号桶存放的是列的最小值,别的桶号就是endpoint_number,别的桶存放的endpoint_value值为前n个桶中的最大值。比如第一个桶就是第一个桶中的最大值,第二个桶就是前2个桶中的最大值,第三个桶就是前三个桶数据中的最大值。
但是我再11g中的测试,在我们的例子中第1个桶中的数据量是10475/3=3581,那么第一个桶中的最大值应该是99,不是1,跟上面的计算方法算出来的不太一样。不知道10g的是不是上面的计算方法,找个时间测试下。
查看直方图中的high_value和low_value的方法
SQL> var temp number;
SQL> exec dbms_stats.convert_raw_value('C102',:temp);
SQL> exec dbms_output.put_line(:temp);
1
PL/SQL 过程已成功完成。
SQL> exec dbms_stats.convert_raw_value('C164',:temp);
PL/SQL 过程已成功完成。
SQL> exec dbms_output.put_line(:temp);
99
PL/SQL 过程已成功完成。