Oracle谓词越界引发的案例

什么是谓词越界?

目标列指定的where 查询条件的值 不在统计信息收集的最大值与最小值之前。这就是谓词越界

如果出现了这种现状,CBO就无法判断出针对该列的查询条件的选择率, 只能用一个估算的值 ,作为查询条件的可选择率,如果这个估算的值与实际情况严重不符的话,就可能是CBO选错执行计划。


具体案例如下:

开发人员发过一个sql,让给优化下;

SELECT o.refxxxx, count(o.refxxxx)

FROM t_xxxxxxx_open_SSSS o

join (SELECT i.xxxxxxx_account, sum(i.intxxxxx) ljsy1

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date <= '2015-11-23' --1

and i.inxxxxxx_date >= '2015-07-01'

group by i.xxxxxxx_account) tt

on o.xxxxxxx_account = tt.xxxxxxx_account

join (SELECT i.xxxxxxx_account, sum(i.intxxxxx) ljsy2

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date <= '2015-11-24' --2

and i.inxxxxxx_date >= '2015-07-01'

group by i.xxxxxxx_account) ttt

on o.xxxxxxx_account = ttt.xxxxxxx_account

where to_char(o.create_date, 'yyyy-MM-dd') >= '2015-09-01'

and tt.ljsy1 < 1

and ttt.ljsy2 >= 1

and o.areacode = '350000'

/*and o.cxxxcode !='510100' */

and o.from_xxxxx != '07'

group by o.refxxxx

union all

SELECT o.refxxxx, count(o.refxxxx)

FROM t_xxxxxxx_open_SSSS o

join (SELECT i.xxxxxxx_account, sum(i.intxxxxx) ljsy1

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date <= '2015-11-23' --1

and i.inxxxxxx_date >= '2015-07-01'

group by i.xxxxxxx_account) tt

on o.xxxxxxx_account = tt.xxxxxxx_account

join (SELECT i.xxxxxxx_account, sum(i.intxxxxx) ljsy2

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date <= '2015-11-24' --2

and i.inxxxxxx_date >= '2015-07-01'

group by i.xxxxxxx_account) ttt

on o.xxxxxxx_account = ttt.xxxxxxx_account

where to_char(o.create_date, 'yyyy-MM-dd') >= '2015-09-01'

and tt.ljsy1 < 1

and ttt.ljsy2 >= 1

and o.areacode != '350000'

/*and o.cxxxcode !='510100' */

group by o.refxxxx;

经过与其沟通 sql最终更改如下:

SELECT o.refxxxx, count(o.refxxxx)

FROM t_xxxxxxx_open_SSSS o

where o.create_date >= to_date('2015-10-01', 'yyyy-mm-dd')

and ((o.areacode = '350000' and o.from_xxxxx != '07') or

o.areacode != '350000')

and exists (SELECT 1

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date = '2015-12-07' --2

and i.intxxxxx > 0

and i.xxxxxxx_account = o.xxxxxxx_account)

and not exists (SELECT i.xxxxxxx_account

FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date <= '2015-12-06' --1

and i.inxxxxxx_date >= '2015-11-01'

and i.intxxxxx >=1

and i.xxxxxxx_account = o.xxxxxxx_account)

group by o.refxxxx;

更改后的sql 基本由原来的2-3个小时 可以缩短到150s左右 可以出结果.

没过几天 开发又反应 这个优化后的sql 又跑不出来了,

现在看到的执行计划如下:

Oracle谓词越界引发的案例_第1张图片

通过上面我们可以看到 问题出在第7步; 基数才67

实际呢?:

SELECT count(*) FROM t_XXXXXXX_inXXXXX_inXXXXXt i

where i.inxxxxxx_date = '2015-12-07' ; ----5155165

5155165与67 相差的可不是一点点。。

数据量那么大走 NESTED LOOPS 明显不合适了,于是想到了不走索引的情况:

SELECT /*+ no_index(o)*/o.refxxxx, count(o.refxxxx)

2 FROM t_xxxxxxx_open_SSSS o

3 where o.create_date >= to_date('2015-10-01', 'yyyy-mm-dd')

4 and ((o.areacode = '350000' and o.from_xxxxx != '07') or

5 o.areacode != '350000')

6 and exists (SELECT /*+ no_index(i)*/1

7 FROM t_XXXXXXX_inXXXXX_inXXXXXt i

8 where i.inxxxxxx_date = '2015-12-07' --2

9 and i.intxxxxx > 0

10 and i.xxxxxxx_account = o.xxxxxxx_account)

11 and not exists (SELECT /*+ no_index(i)*/i.xxxxxxx_account

12 FROM t_XXXXXXX_inXXXXX_inXXXXXt i

13 where i.inxxxxxx_date <= '2015-12-06' --1

14 and i.inxxxxxx_date >= '2015-11-01'

15 and i.intxxxxx >=1

16 and i.xxxxxxx_account = o.xxxxxxx_account)

17 group by o.refxxxx;

执行计划:

Oracle谓词越界引发的案例_第2张图片

结果确实很快,150s左右就OK了。


然后我们分析产生这个问题的原因:

于是我们看统计信息:(今天20151208)

Oracle谓词越界引发的案例_第3张图片

我们把统计信息里面的LOW_VALUE , HIGH_VALUE 转换为我们可以看懂的字符:

select display_raw('323031332D31322D3133','VARCHAR2') low_val,display_raw('323031352D31312D3234','VARCHAR2') HIGHVALUE FROM DUAL;

2013-12-13 2015-11-24

而我们的查询条件为:i.inxxxxxx_date = '2015-12-07' 很明显 我们要查的条件不在 上面那个范围内.这就是谓词越界的现象. 如果出现了这种现状,CBO就无法判断出针对该列的查询条件的选择率, 只能用一个估算的值 ,作为查询条件的可选择率,如果这个估算的值与实际情况严重不符的话,就可能是CBO选错执行计划,就出现了,我们这个情况。


最后我们收集统计信息,

object_name SEGMENT_TYPE TABLESPACE_NAME SEGMENT_SIZE_G

---------------------------------------- -------------------- -------------------- --------------

BTUPAYPROD.T_XXXXXXING_INxxxxxx_intxxxxx TABLE PARTITION TBS_XXXXXXE_DATA 180.564453

该表非常大,对相关的业务了解之后,决定没有必要对全表进行收集, 只收集最后几个分区 就可以了

begin

DBMS_STATS.GATHER_TABLE_STATS ('BTUPAYPROD','T_XXXXXXING_INxxxxxx_intxxxxx',partname=>'P30', degree=>8);

end;

最后 不加hints 我们看到的执行计划和 加hints一样的效果:这就达到了我们的目的了。

Oracle谓词越界引发的案例_第4张图片

--gt

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30109892/viewspace-1873687/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/30109892/viewspace-1873687/

你可能感兴趣的:(数据库)