我正在对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_statistics
和user_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;
答案 0 :(得分:4)
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);
参数。