修改统计信息值恢复Oracle执行计划
经常会遇到由于日期字段统计信息的边界值不准确,导致Oracle SQL执行计划发生改变,通常是最大值没有及时更新或者最大值过大。
下面是遇到的一个案例,SQL的执行计划突然改变,原先使用的是index range scan,突然变成了index full scan。 导致SQL语句非常缓慢。
点击(此处)折叠或打开
- select count(*) from prd_t1 where input_date>trunc(sysdate-1)
- and input_date
原来的执行计划:
点击(此处)折叠或打开
- SQL> select count(*) from prd_t1 where input_date > trunc(sysdate);
- COUNT(*)
- ----------
- 193311
- Execution Plan
- ----------------------------------------------------------
- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=8)
- 1 0 SORT (AGGREGATE)
- 2 1 INDEX (RANGE SCAN) OF 'IDX_PRD_T1' (NON-UNIQU
- E) (Cost=4 Card=2 Bytes=16)
变差的执行计划:
点击(此处)折叠或打开
- SQL> select count(*) from prd_t1 where input_date > trunc(sysdate);
- COUNT(*)
- ----------
- 193311
- Execution Plan
- ----------------------------------------------------------
- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=8)
- 1 0 SORT (AGGREGATE)
- 2 1 INDEX (FAST FULL SCAN) OF 'IDX_PRD_T1' (NON-UNIQU
- E) (Cost=6458 Card=6130163 Bytes=49041304)
检查和字段的统计信息有关系,字段input_date的最小值和最大值分别是18-JAN-2016和15-MAY-2026。所以选择了fast full scan。
修改完最大值和最小值,执行计划恢复成使用rang scan
点击(此处)折叠或打开
- SQL> select table_name,TO_CHAR(TO_DATE(TRUNC(ENDPOINT_VALUE),'J'),'DD-MON-YYYY')
- 2 from dba_tab_histograms where table_name='PRD_T1' and column_name='INPUT_DATE';
- TABLE_NAME TO_CHAR(TO_DATE(TRUNC(ENDPOINT
- ------------------------------ ------------------------------
- PRD_T1 19-APR-2016
- PRD_T1 18-APR-2017
具体方法如下:
点击(此处)折叠或打开
- DECLARE
- srec DBMS_STATS.STATREC;
- v_distcnt NUMBER;
- v_density NUMBER;
- v_nullcnt NUMBER;
- v_avgclen NUMBER;
- numvals DBMS_STATS.NUMARRAY;
- charvals DBMS_STATS.CHARARRAY;
- datevals DBMS_STATS.DATEARRAY;
- BEGIN
- -- Date Type
- DBMS_STATS.get_column_stats (ownname => 'DBO',
- tabname => 'PRD_T1',
- colname => 'INPUT_DATE',
- distcnt => v_distcnt,
- density => v_density,
- nullcnt => v_nullcnt,
- srec => srec,
- avgclen => v_avgclen
- );
- datevals := DBMS_STATS.datearray (sysdate-365, sysdate);
- DBMS_STATS.prepare_column_values (srec, datevals);
- DBMS_STATS.set_column_stats (ownname => 'DBO',
- tabname => 'PRD_T1',
- colname => 'INPUT_DATE',
- distcnt => v_distcnt,
- density => v_density,
- nullcnt => v_nullcnt,
- srec => srec,
- avgclen => v_avgclen
- );
- COMMIT;
- END;
- /