如何在不使用Cursor的情况下编写以下pl/sql块?

Nav*_*iam 0 oracle plsql oracle10g

我在pl/sql块中写了一个光标.如果它有更多记录,这个块需要花费很多时间.如何在没有光标的情况下写这个或者是否有其他可以减少时间的替代方法?是否有任何替代查询来执行插入到一个表并使用单个查询从另一个表中删除?

DECLARE
      MDLCursor SYS_REFCURSOR;
    BEGIN
      open MDLCursor for
        select dc.dest_id, dc.digits, dc.Effectivedate, dc.expirydate
          from DialCodes dc
         INNER JOIN MDL d
            ON dc.Dest_ID = d.Dest_ID
           AND d.PriceEntity = 1
          join sysmdl_calltypes s
            on s.call_type_id = v_CallType_ID
           and s.dest_id = dc.Dest_ID
           and s.call_type_id not in
               (select calltype_id from ignore_calltype_for_routing)
         order by length(dc.digits) desc, dc.digits desc;
      loop
        fetch MDLCursor
          into v_mdldest_id, v_mdldigits, v_mdlEffectiveDate, v_mdlExpDate;
        insert into tt_pendingcost_temp
          (Dest_ID,
           Digits,
           CCASDigits,
           Destination,
           tariff_id,
           NewCost,
           Effectivedate,
           ExpiryDate,
           previous,
           Currency)
          select v_mdldest_id,
                 Digits,
                 v_mdldigits,
                 Destination,
                 tariff_id,
                 NewCost,
                 Effectivedate,
                 ExpiryDate,
                 previous,
                 Currency
            FROM tt_PendingCost
           where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
             and instr(Digits, v_MDLDigits) = 1
             and v_mdlEffectiveDate <= effectivedate
             and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
        if SQL%ROWCOUNT > 0 then
          delete FROM tt_PendingCost
           where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
             and instr(Digits, v_MDLDigits) = 1
             and v_mdlEffectiveDate <= effectivedate
             and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
        end if;
        exit when MDLCursor%NOTFOUND;
      end loop;
      close MDLCursor;
    END;
Run Code Online (Sandbox Code Playgroud)

Luk*_*ard 5

我没有你的表格和你的数据所以我只能猜测一些会减慢你速度的事情.

首先,游标中使用的查询中包含一个ORDER BY子句.如果此查询返回大量行,则Oracle必须全部获取它们并在它们返回第一行之前对它们进行排序.如果此查询通常返回大量结果,并且您并不特别需要它返回排序结果,那么您可能会发现如果放弃,PL/SQL块会加速一点ORDER BY.这样,您可以开始从游标中获取结果,而无需获取所有结果,将它们存储在某处并首先对它们进行排序.

其次,以下是WHEREINSERT INTO ... SELECT ...DELETE FROM ...语句中使用的子句:

    where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
      and instr(Digits, v_MDLDigits) = 1
      and v_mdlEffectiveDate <= effectivedate
      and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
Run Code Online (Sandbox Code Playgroud)

我不知道Oracle如何在任何这些条件下有效地使用索引.因此,每次都必须进行全表扫描.

最后两个条件似乎是合理的,似乎没有很多可以用它们完成.我想关注前两个条件,因为我认为它们有更多的改进空间.

四个条件中的第二个是

instr(Digits, v_MDLDigits) = 1
Run Code Online (Sandbox Code Playgroud)

当且仅当Digits以内容开头时,此条件成立v_MDLDigits.写这个的更好方法是

Digits LIKE v_MDLDigits || '%'
Run Code Online (Sandbox Code Playgroud)

LIKE在这种情况下使用的优点INSTR是Oracle可以在使用时使用索引LIKE.如果Digits列上有索引,Oracle将能够将其与此查询一起使用.然后,Oracle可以专注于那些以数字开头v_MDLDigits而不是进行全表扫描的行.

四个条件中的第一个是:

substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
Run Code Online (Sandbox Code Playgroud)

如果v_MDLDigits长度至少为2,并且Digits列中的所有条目的长度也至少为2,那么这个条件是多余的,因为它是我们查看的前一个隐含的.

我不确定你为什么会有这样的情况.我可以想到你可能有这种情况的唯一原因是你有一个功能索引substr(Digits, 1, 2).如果没有,我会试图substr完全消除这种情况.

我不认为光标是使这个过程运行缓慢的原因,并且没有一个我知道的语句可以插入到一个表中并从另一个表中删除.为了加快这个过程,我认为你只需要稍微调整一下这些查询.