SQL优化必懂概念(三):直方图

来源:这里教程网 时间:2026-03-03 19:40:04 作者:

    数据分布不均衡会导致在查询该列的时候, 要么走全表扫描, 要么走索引扫描, 这个时候很容易 走错执行计划。说白了就是判断列适不适合走索引 ( 根据 where 条件后跟的值不同,返回结果也不同,那么该列走不走索引的结果也不同 )

    基数高的列,一般数据分布一定均衡,比如主键列

    1 a

    2 b

    3 c

。。。。。。因为他没有重复的,优化器认为他每一个条件过滤出来的都只占一行,所以在执行计划里就会预估过滤或者返回出一行,但是如果分布不均匀的数据,我某个条件过滤出占总行数 50% 以上的数据,由于没收集直方图,导致执行计划预估为 1 行,那么执行计划就肯定走错了。!

 

    如果 没有对基数低的列收集直方图统计信息 , 基于成本的优化器( CBO )会认为该列数据分布是均衡的 ( 就会默认去走索引,这时候如果返回的数据很多,那么明显就走了错误的执行计划 )

    以测试表 test 为例, 用实验讲解直方图。

 

   首先我们对测试表 test 收集统计信息,在收集统计信息的时候, 不收集列的直方图, 语句 for all columns size 1 表示对所有列都不收集直方图。

 

BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST',
  tabname => 'TEST',
                              estimate_percent => 100,
                              method_opt => 'for all columns size 1',
                              no_invalidate => FALSE,
                              degree => 1,
                              cascade => TRUE);
END;
/

 

Histogram none 表示没有收集直方图。

select a.column_name,
b.num_rows,
a.num_distinct Cardinality,
round(a.num_distinct / b.num_rows * 100, 2) selectivity,
a. histogram,
a.num_buckets
from dba_tab_col_statistics a, dba_tables b
where a.owner = b.owner
and a.table_name = b.table_name
and a.owner = 'TEST'
and a.table_name = 'TEST'
;

 

 

owner 列基数很低, 现在我们对 owner 列进行查询。

set autot trace
set autotrace off(关闭用这个)
select * from test where owner= 'ORACLE_OCM';

 

 

 

查询 owner='ORACLE_OCM' 返回了 6 条数据 , 但是 CBO 在计算 Rows 的时候认为 owner='TEST' 返回 1766 条数据, Rows 估算得不是特别准确。从 22955 条数据里面查询出 6 条数据 , 应该走索引 , 所以现在我们对 owner 列创建索引。

create index idx_owner on test(owner);

 

 

然后查询owner='SYS'用户

select * from test where owner= 'SYS';

发现还是走的索引,从 2W 多行数据中返回 14567 条数据,明显是不应该走索引的,因此 走错了执行计划

因为 owner 的基数太低了,只有 13 ,而返回的总行数有 22955 ,很巧妙的发现 22955/13=1766 ,即 CBO 估算的 ROWS ,这是因为我们在收集统计信息的时候

没有(或者说没对 owner 这列)进行直方图收集。下面收集一下。

 

BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'TEST',
  tabname => 'TEST',
                              estimate_percent => 100,
                              method_opt => 'for columns owner size skewonly',
                              no_invalidate => FALSE,
                              degree => 1,
                              cascade => TRUE);
END;
/

 

查看 owner 列直方图收集情况

 

然后在查询一下 SQL ,看看是否会走错执行计划

 

最后发现终于走对了, ROWS 也变得相应正确了。

 

收集直方图究竟完成了什么操作呢?对 owner 列收集直方图其实就相当于运行了以下 SQL.

select owner,count(*) from test group by owner;

 

其中 NUM_BUCKETS,也就是 直方图桶的作用就是记录收集 count(*),也就是提前收集该列的数据分布情况,让CBO优化提前知道有几行,也就是为什么条件后跟的值不同,返回的rows不同的原因了,这样 就可以选择正确的执行计划了。

根据基数分为两种直方图:

1 num_buckets 超过 254 的为等高直方图,即—— HEIGHT BALANCED

2 num_buckets 小于 254 的为频率直方图,即—— FREQUENCY

 

11g

基数 超过 254 的,直方图桶数最高也就是 254 ,一个桶会放多个基数。—— HEIGHT BALANCED 等高直方图

基数 低于 254 的,直方图桶数为基数值。 —— FREQUENCY 频率直方图

 

12c :可以大于 254

相关推荐