为什么Oracle在使用索引时会使用全表扫描?

时间:2015-07-27 13:07:28

标签: oracle oracle11g

我正在对Oracle中的查询计划进行一些实验,我有下表:

--create a table to use
create table SKEWED_DATA(
  EMP_ID int,
  DEPT int,
  COL2 int,
  CONSTRAINT SKEWED_DATA_PK PRIMARY KEY (EMP_ID)
);
--add an index on dept
create index SKEWED_DATA_INDEX1 on SKEWED_DATA(DEPT);

然后插入100万行数据,其中999,999行有dept id 1,1行有dept id 99。

在计算表的统计数据之前,Oracle Autotrace显示在运行以下查询时,它正在对两者使用索引扫描:

select AVG(COL2) from SKEWED_DATA D where DEPT = 1;
select AVG(COL2) from SKEWED_DATA D where DEPT = 99;

我的理解是,在这种情况下,对dept id 1使用全表扫描,对dept id 2使用索引扫描会更有效。

然后我运行以下命令来生成表的统计信息:

execute DBMS_STATS.GATHER_TABLE_STATS ('HARRY','SKEWED_DATA'); 

查询dba_tab_statisticsuser_tab_col_statistics确认已收集统计数据和直方图。

现在,对以下查询运行自动跟踪会显示两者的全表扫描!

select AVG(COL2) from SKEWED_DATA D where DEPT = 1;
select AVG(COL2) from SKEWED_DATA D where DEPT = 99;

我的问题是:当只有1行具有此值时,为什么Oracle对dept id 99使用全表扫描?

更新

我尝试使用提示来运行dept 99的查询以强制Oracle使用索引,虽然Autotrace认为效率较低,但是花费的时间是0.001秒,而使用全表扫描的时间是0.03秒,从而证明(我认为?)我的理论,即Oracle应该在这个例子中使用索引。

select /*+ INDEX(D SKEWED_DATA_INDEX1) */ AVG(COL2) from SKEWED_DATA D where DEPT = 99;

1 个答案:

答案 0 :(得分:4)

好的,我想我可能已经解决了。当我有999,999行(dept 1和1行,dept 99)时,我通过运行以下查询来检查直方图桶的数量:

GridSearchCV

这表明有2个不同的值,但只有1个桶。如果我将收集的统计信息更改为:

select COLUMN_NAME, HISTOGRAM, NUM_BUCKETS, NUM_DISTINCT from USER_TAB_COL_STATISTICS where  TABLE_NAME = 'SKEWED_DATA';

然后正确地提出了2个桶,并且autotrace显示了正确的'执行计划。所以,我想这是因为极端的偏斜'我的数据表明,除非estimate_percent很大,否则Oracle无法为其生成正确的统计数据。

有趣的是,如果我的数据偏差稍微少一些(比如所有记录的大约2-3%,而dept id为99),即使我将estimate_percent保留为默认值,Oracle也会正确对待它。

所以,这个故事的寓意似乎是:如果你像这样荒谬地扭曲数据并且Oracle没有使用正确的执行计划,那么试着使用execute DBMS_STATS.GATHER_TABLE_STATS('HARRY','SKEWED_DATA',estimate_percent=>100); 参数。