当前位置: 代码迷 >> Oracle技术 >> 一个诡谲的存储过程
  详细解决方案

一个诡谲的存储过程

热度:254   发布时间:2016-04-24 08:07:03.0
一个诡异的存储过程
存储过程,在PL/SQL进行test,输入201311,执行,瞬间执行完成,点击游标输出,要2分半钟才有结果
CREATE OR REPLACE PROCEDURE RP3005
(
 V_RPDATE     integer, --月份
 SMENU_CURSOR out query_pkg.Query_cur, --结果集
 SMENU_CURSOR2 out query_pkg.Query_cur --结果集

IS

BEGIN

  OPEN SMENU_CURSOR for
  select S_CH,I_YUCETZ,i_xiaofeil
  ,to_char(round((i_xiaofeil-I_YUCETZ)/I_YUCETZ,2))*100||'%' I_WUCHALV
  from (
   select substr(yc.s_ch,1,2) S_CH
   ,sum(decode(yc.i_yucetz,null,yc.i_ycxyrjyl,yc.I_YUCETZ)*ceil(ly.d_xiacicbrq-ly.d_bencicbrq)) I_YUCETZ
   ,sum(sj.i_xiaofeil) i_xiaofeil 
   from cb_cebenly ly
   inner join GB_YUCEWCFX yc on yc.s_ch=ly.s_ch and yc.i_fenxiny=ly.i_zhangwuny 
   inner join cb_chaobiaosj sj on yc.i_fenxiny=sj.i_zhangwuny and yc.i_gongci=sj.i_gongci and yc.s_cid=sj.s_cid
   where ly.s_st='41' and sj.s_st='41' and ly.i_jlzt=0 and ly.i_lingyongzt in(2,3,4) and sj.i_zhangwuny=V_RPDATE
   and yc.I_FENXINY = V_RPDATE and yc.i_jlzt=0 and yc.i_jlzt=0 group by substr(yc.s_ch,1,2)
   ) tbl;

     OPEN SMENU_CURSOR2 for
     select V_RPDATE from dual;
END;


拷贝存储过程语句出来,把输入参数(V_RPDATE)替换成201311,在PL/SQL执行,秒级耗时查询出结果。

很是费劲!通过sp和直接执行语句,速度相差如此之大?


语句一、语句二,分别F5,执行计划Cost相差不大。
比较诡异的现象二:语句一走索引,语句二均是全部扫描。
为何全表扫描比走索引还快呢?

尝试在存储过程,添加/*all_rows*/,强制全表扫描,执行输出结果的速度依旧2分多钟。⊙﹏⊙b汗


语句一
select S_CH,I_YUCETZ,i_xiaofeil
  ,to_char(round((i_xiaofeil-I_YUCETZ)/I_YUCETZ,2))*100||'%' I_WUCHALV
  from (
   select substr(yc.s_ch,1,2) S_CH
   ,sum(decode(yc.i_yucetz,null,yc.i_ycxyrjyl,yc.I_YUCETZ)*ceil(ly.d_xiacicbrq-ly.d_bencicbrq)) I_YUCETZ
   ,sum(sj.i_xiaofeil) i_xiaofeil 
   from cb_cebenly ly
   inner join GB_YUCEWCFX yc on yc.s_ch=ly.s_ch and yc.i_fenxiny=ly.i_zhangwuny 
   inner join cb_chaobiaosj sj on yc.i_fenxiny=sj.i_zhangwuny and yc.i_gongci=sj.i_gongci and yc.s_cid=sj.s_cid
   where ly.s_st='41' and sj.s_st='41' and ly.i_jlzt=0 and ly.i_lingyongzt in(2,3,4) and sj.i_zhangwuny=V_RPDATE
   and yc.I_FENXINY = V_RPDATE and yc.i_jlzt=0 and yc.i_jlzt=0 group by substr(yc.s_ch,1,2)
   ) tbl;


语句二
select S_CH,I_YUCETZ,i_xiaofeil
  ,to_char(round((i_xiaofeil-I_YUCETZ)/I_YUCETZ,2))*100||'%' I_WUCHALV
  from (
   select substr(yc.s_ch,1,2) S_CH
   ,sum(decode(yc.i_yucetz,null,yc.i_ycxyrjyl,yc.I_YUCETZ)*ceil(ly.d_xiacicbrq-ly.d_bencicbrq)) I_YUCETZ
   ,sum(sj.i_xiaofeil) i_xiaofeil 
   from cb_cebenly ly
   inner join GB_YUCEWCFX yc on yc.s_ch=ly.s_ch and yc.i_fenxiny=ly.i_zhangwuny 
   inner join cb_chaobiaosj sj on yc.i_fenxiny=sj.i_zhangwuny and yc.i_gongci=sj.i_gongci and yc.s_cid=sj.s_cid
   where ly.s_st='41' and sj.s_st='41' and ly.i_jlzt=0 and ly.i_lingyongzt in(2,3,4) and sj.i_zhangwuny=201311
   and yc.I_FENXINY = 201311 and yc.i_jlzt=0 and yc.i_jlzt=0 group by substr(yc.s_ch,1,2)
   ) tbl

------解决思路----------------------
mark 一下,等高手解答
------解决思路----------------------
全表扫描与索引扫描对比,并不是索引扫描一定快,大师有句名言:“避免不必要的全表扫描”,因为全表扫描时多块读,大部分索引扫描是单块读,不同场景运用不同的方式。

不知道这里的Oracle版本是多少?从10g以后只有CBO这种优化器,它会根据统计信息自动计算一条SQL的不同执行路径,然后选择一条成本最低的作为他的执行计划,因此一条SQL的执行计划可能是全表扫描,也可能是索引扫描,例如一张表只有100条数据,全表扫描肯定比索引扫描要快,这要具体问题具体分析,如果想知道为什么上述不同的现象,请用10053做这两条SQL的trace,可以看下Oracle选择的成本值。
------解决思路----------------------
最好贴出你的执行计划,10053的trace日志,这样可以更详细地看下为什么效果不同。
------解决思路----------------------
引用:
刚到6L,结贴有点浪费,\(^o^)/~
又原因分享Oracle知识的,请贴出来,结贴都给分。

我感觉跟你的表连接有关系
------解决思路----------------------
时间是游标消耗掉的吧,遇到过类似问题,语句直接执行很快,放到游标里就慢了
不常见,原因未知
------解决思路----------------------
看样子,是你执行 test 时,并没有真正的去执行这两个 sql,只是确定了执行计划,你点击 cursor 输出时,才真正去跑这些语句。
------解决思路----------------------
如果没猜错,

sj.i_zhangwuny 这个字段是 varchar2 类型,
你试试把 201311 改成 '201311' 再执行sql二。

如果速度也很快,基本就可以解释了。
plsql在test时候,输入的应该是按照字符串传了。
------解决思路----------------------
那就把你的执行计划贴出来。

引用:
Quote: 引用:

如果没猜错,

sj.i_zhangwuny 这个字段是 varchar2 类型,
你试试把 201311 改成 '201311' 再执行sql二。

如果速度也很快,基本就可以解释了。
plsql在test时候,输入的应该是按照字符串传了。

i_zhangwuny是int类型
varchar类型的字段,我们命名是以S_开头的
呵呵

------解决思路----------------------
我擦,才看见13年的帖子。
一般情况你这种情况基本是因为隐士类型转换到值的索引失效。
既然时间已经久远就不提他了。

引用:
Quote: 引用:

那就把你的执行计划贴出来。

Quote: 引用:

Quote: 引用:

如果没猜错,

sj.i_zhangwuny 这个字段是 varchar2 类型,
你试试把 201311 改成 '201311' 再执行sql二。

如果速度也很快,基本就可以解释了。
plsql在test时候,输入的应该是按照字符串传了。

i_zhangwuny是int类型
varchar类型的字段,我们命名是以S_开头的
呵呵


很久之前的事情了,后来通过其他方式把这个存储过程执行效率的问题处理了。现在让我拿执行计划出来,拿不出来的了
  相关解决方案