PLSQL 循环需要大量时间来执行

Posted

技术标签:

【中文标题】PLSQL 循环需要大量时间来执行【英文标题】:PLSQL Loop taking lot of time to execute 【发布时间】:2017-01-04 02:13:55 【问题描述】:

我有一个 Oracle 程序正在更新低于 10,000 条记录。如果我运行正常的 SQL 语句,它会在几秒(30)内立即返回结果。 程序循环中的相同语句将无限循环。

我的循环语句如下。 注意:data FIELD 数据类型是 clob 而不是 varchar2。

声明:

select
  'LB_COPY_CHANGE-'||8  LAST_MODIFIED_BY, 
  rec.COR_ID_old,
  rec.COR_ID_NEW,
  replace(replace(replace(a.data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',') as data 
from KPI_MET_FIELD_DATA a, CUSTOM_TEMP_TABLE_SESSION_1 rec 
where A.cmf_fk_id in (145,146,147) 
    and TYPE_LB in (14,15,16) 
    and  a.KDB_FK_ID in (
       select distinct km.KDB_FK_ID 
       from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc 
       where ksc.SDE_FK_ID=sc.SDE_PK_ID 
           and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID 
           and km.is_deleted=0 
           and kp.kdb_pk_id = km.KDB_FK_ID 
           and kp.is_deleted=0 
           and km.cmf_fk_id in (145,146,147) 
           and sc.sdp_fk_id = 8) 
     and a.is_deleted=0 
     and (a.data like '%'||rec.COR_ID_OLD||'%');


FOR rec in (SELECT * FROM CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16)) LOOP 
   update KPI_MET_FIELD_DATA 
      set LAST_MODIFIED_BY='LB_COPY_CHANGE-'||p2 , 
      data = replace(replace(replace(data,'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||','),''id':'||rec.COR_ID_OLD||',',''id':'||rec.COR_ID_NEW||','),'''id'':'||rec.COR_ID_OLD||',','''id'':'||rec.COR_ID_NEW||',') 
   where cmf_fk_id in (145,146,147) 
       and KDB_FK_ID in (
          select distinct km.KDB_FK_ID 
          from KPI_MET_FIELD_DATA km , KPI_DET_BASE kp, KPI_REL_KPI_SCORECARD ksc, STR_DET_EMP_SCORECARD sc 
          where ksc.SDE_FK_ID=sc.SDE_PK_ID 
              and km.KDB_FK_ID = ksc.KDB_KPI_FK_ID 
              and km.is_deleted=0 
              and kp.kdb_pk_id = km.KDB_FK_ID 
              and kp.is_deleted=0 
              and km.cmf_fk_id in (145,146,147) 
              and sc.sdp_fk_id = p2) 
       and is_deleted=0 ;

【问题讨论】:

你是什么意思,“正常的 SQL 语句?”如果不提供更多细节,比如解释计划,可能有很多可能的原因。需要更多细节。为什么你甚至需要一个循环? 相同结果集的选择语句: 是否有任何理由在循环中多次更新相同的记录?在 WHERE 语句中,您拥有在 LOOP 语句期间未更改的过滤器。这意味着您一次又一次地更新同一条记录 选择不同于更新,它做了两件非常不同的事情。对更新运行解释计划。 在 FOR 循环的更新中,您缺少一个 where 过滤器行“和 (a.data like '%'||rec.COR_ID_OLD||'%')”,这会导致更新运行FOR LOOP 游标返回的每条记录。因此 UPDATE 使用 FOR LOOP 光标进行笛卡尔积,而在 select 中两者之间有一个 JOIN。 【参考方案1】:

您的代码有几个弱点。

WHERE KDB_FK_ID in (select distinct ... 没有任何意义。无需为 IN () 子句创建 DISTINCT

使用 ANSI 连接语法代替旧的 Oracle 连接语法,这样更不容易出错

但主要区别在于,您的循环不包含连接条件(a.data like '%'||rec.COR_ID_OLD||'%'),即您为CUSTOM_TEMP_TABLE_SESSION where TYPE_LB in (14,15,16) 中的每一行一次又一次地更新整个KPI_MET_FIELD_DATA

【讨论】:

是的,先生,同意您的观点,但是否有任何替代解决方案,因为已经相应地改变了您在以前的 cmets 中发现的内容。但仍然是相同的结果。谢谢 再一次,这些语句在做不同的事情 - 您如何期望它们在同一时间执行?

以上是关于PLSQL 循环需要大量时间来执行的主要内容,如果未能解决你的问题,请参考以下文章

PLSQL 过程需要大量时间来执行

执行 CURSOR 时的 PLSQL FOR 循环

我应该清除每个要在 forall 中使用的 fetch 循环的集合吗? PLSQL 甲骨文

求 php 循环执行大量数据 解决办法。

在循环输出 Oracle PLSQL 函数时需要帮助

PLSQL 中的 WHILE 循环有啥问题?