查询优化器以“数据是均匀分布的”这一原则为出发点。贯穿前面部分的测试表存储在ID列上的数据正是一个均匀分布的数据集的例子。实际上,它将1~1000之间的每个整数正好存储一次。在这种情形下,要生成根据该列上的谓词条件(例如id BETWEEN 6
AND 19)过滤掉的记录数的合理估算,查询优化器仅需要描述谓词部分的对象统计信息:最小值、最大值以及非重复值的数量。
如果数据并非均匀分布的,那么查询优化器在没有额外信息的情况下就无法计算合理的估算。举例来说,对于存储在val2列上的已知数据集合(见下面查询的输出部分),查询优化器如何对像val2=105这样的谓词做出有意义的估算?答案是不能,因为查询优化器丝毫不知道有大概50%的记录满足这个谓词条件:
select val2,count(*) FROM t GROUP BY val2
ORDER BY val2;
VAL2
COUNT(*)
101
8
102
25
103
68
104
185
105
502
106
212
查询优化器需要的关于非均匀分布数据的额外信息被称作直方图 (histogram)。 在12.1版本之前,有两种类型的直方图可用:频率直方图 (frequency histogram)和高度均衡直方图(height-balanced histogram)。Oracle Database 12.1引入了两种额外的直方图来取代高度均衡直方图:高频率直方图(top frequency histogram)和混合直方图(hybrid histogram)。
警告 只有当收集对象统计信息时使用dbms_stats.auto_sample_size作为采样频率时,dbms_stats 包才会创建高频率直方图和混合直方图。
1.1.1.1 频率直方图
频率直方图就是大多数人对直方图这一概念的理解。图8-2是频率直方图的一个例子,也是对之前查询返回数据的一个直观图示。

存储在数据字典中的频率直方图与这个图示很像。主要的区别是字典中不是使用频率,而是使用累积频率。下面的查询通过计算两个相邻桶的值(注意,endpoint_number即是累计频率)之间的差来将累计频率转化为频率:
COLUMN endpoint_value FORMAT 9999
COLUMN endpoint_number FORMAT 999999
COLUMN frequency FORMAT 999999
SELECT endpoint_value, endpoint_number,
endpoint_number - lag(endpoint_number,1,0)
OVER (ORDER BY
endpoint_number) AS frequency
FROM user_tab_histograms
WHERE table_name = 'T'
AND column_name = 'VAL2'
ORDER BY endpoint_number;
ENDPOINT_VALUE ENDPOINT_NUMBER FREQUENCY
101 8 8
102 33 25
103 101 68
104 286 185
105 788 502
106 1000 212
频率直方图的核心特性如下所述。
Ø 桶的数量(换句话说,类别的数量)与不重复值的数量一致。在像user_tab_histograms这样的视图中每个桶都有一条对应的记录可用。
Ø endpoint_value列提供其自身值的一个数值形式表示。因此,对于非数值形式的数据类型,必须将其实际值编码为一个数字。根据数据、数据类型以及版本的不同,实际值可能在endpoint_actual_value列中可见。要非常清楚地了解存储在直方图中的值,只能根据前面32个字节(在12.1版本中是64个字节)来区分。结果就是拥有较长固定前缀的值可能会危及直方图的有效性。尤其是当使用每个字符可能占用四个字节的多字节字符集时更是如此。
Ø endpoint_number列提供值的累积频率。要获得真正的频率,必须减去前一条记录的endpoint_number列的值。
警告 假如动态采样用于构建直方图,则频率信息应根据采样大小按比例决定。要知道比例因子,请用采样大小(sample_size)除以记录的数量(num_rows)。这两个列都是由类似user_tab_statistics这样的视图提供的。
接下来的例子展示了查询优化器如何利用频率直方图来精确估算一个在val2列上使用了谓词的查询返回的记录数量 (cardinality):
DELETE plan_table;
EXPLAIN PLAN SET STATEMENT_ID '101' FOR
SELECT * FROM t WHERE val2 = 101;
EXPLAIN PLAN SET STATEMENT_ID '102' FOR
SELECT * FROM t WHERE val2 = 102;
EXPLAIN PLAN SET STATEMENT_ID '103' FOR
SELECT * FROM t WHERE val2 = 103;
EXPLAIN PLAN SET STATEMENT_ID '104' FOR
SELECT * FROM t WHERE val2 = 104;
EXPLAIN PLAN SET STATEMENT_ID '105' FOR
SELECT * FROM t WHERE val2 = 105;
EXPLAIN PLAN SET STATEMENT_ID '106' FOR
SELECT * FROM t WHERE val2 = 106;
COLUMN statement_id FORMAT A12
SELECT statement_id, cardinality
FROM plan_table
WHERE id = 0
ORDER BY statement_id;
statement_id cardinality
101 8
102 25
103 68
104 185
105 502
106 212
在上面的例子中,所有的谓词仅引用了直方图中体现的值。当使用其他值时,又会发生什么呢?直到10.2.0.3版本(包括10.2.0.3版本在内)为止,查询优化器都使用1作为频率。从10.2.0.4版本起开始,有两种不同的情况需要考虑。
第一,如果使用的值在最小值和最大值之间,查询优化器取直方图中体现的所有值中最低的频率并将其除以2。
第二,如果使用的值超出了直方图的涵盖范围,则频率依赖于到最小值或最大值的距离。
下面的例子证明了这一点:
DELETE plan_table;
EXPLAIN PLAN SET STATEMENT_ID '096' FOR
SELECT * FROM t WHERE val2 = 96;
EXPLAIN PLAN SET STATEMENT_ID '098' FOR
SELECT * FROM t WHERE val2 = 98;
EXPLAIN PLAN SET STATEMENT_ID '100' FOR
SELECT * FROM t WHERE val2 = 100;
EXPLAIN PLAN SET STATEMENT_ID '103.5' FOR
SELECT * FROM t WHERE val2 = 103.5;
EXPLAIN PLAN SET STATEMENT_ID '107' FOR
SELECT * FROM t WHERE val2 = 107;
EXPLAIN PLAN SET STATEMENT_ID '109' FOR
SELECT * FROM t WHERE val2 = 109;
EXPLAIN PLAN SET STATEMENT_ID '111' FOR
SELECT * FROM t WHERE val2 = 111;
COLUMN statement_id FORMAT A12
SELECT statement_id, cardinality
FROM plan_table
WHERE id = 0
ORDER BY statement_id;
STATEMENT_ID CARDINALITY
096 1
098 2
100 3
103.5 4
107 3
109 2
111
1




