直方图有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 过程已成功完成。