当操纵BULK COLLECT数据时,是否在存储过程中发生了上下文切换?

时间:2016-02-14 20:42:39

标签: oracle plsql

我在11g和12c Oracle系统上运行。我有一些关于我即将编写的PL / SQL的问题。我一直在广泛研究,我没有看到答案,并希望从论坛获得一些意见。我的主要目标是保持高水平的表现。我正在尝试在处理此数据时避免上下文切换。

SP的目的是批量连接来自2个不同表的一组数据,操作集合中的一组字段并将其写入另一个实例中的另一个表。我显然希望尽可能多地在BULK中做,但我不确定,当我完成它时,由于复杂的场操作,我不会最终得到某种单行上下文切换。

详情如下:

声明一个将进行内部连接的游标。在SELECT中我想做一些数据操作。我想调用一个Function来执行此操作,因为尝试在select语句中执行它会非常麻烦:

 CURSOR c1 is 
      SELECT DISTINCT A.ID,
        A.PT_NBR,
        A.PT_DT,
        A.PT_QTY,
      (COMPLX_CALC_FUNCTION(B.TR_TM,B.TR_CD ) nRESULT
      FROM PT_TABLE A
      INNER JOIN TRAN_TABLE B
        ON (A.PT_NBR           =  B.TRAN_NBR
        AND A.PT_DT         =  B.TRAN_DT
        AND A.PT_DT BETWEEN B.START_DT AND B.END_DT)
      WHERE A.ID = vID;

结果将是数十万或数百万条记录,因此我想批量进行

BEGIN
  OPEN c1;
  LOOP 
    FETCH c1 BULK COLLECT INTO vTable LIMIT 20000;

  ---body code

  -- trying to avoid additional manipulation of the data here but there may still be some

 FORALL indx IN vTable.FIRST .. vTable.LAST

...

 INSERT INTO Table2 VALUES vTable(indx); 

--ending stuff

;

COMPLX_CALC_FUNCTION将接受输入并执行数学运算并调用其他函数,如NVL,SUM和CEIL。

所以,问题是......由于光标选择中的函数调用或者我是否需要操作SP体中的数据,我是否会受上下文切换的影响?另外,在这种情况下我还需要考虑其他任何性能问题吗?我试图避免将数据转储到临时物理表并操纵它,因为这似乎比在内存中执行它要慢得多。您的专家建议表示赞赏。

3 个答案:

答案 0 :(得分:1)

在内存循环中调用表中的函数?这将避免SQL-PLSQL上下文切换,这种切换成本更高,特别是因为每行将切换回来。 FORALL循环可以用内存中的表替换内存中的常规for循环。这应该仍然非常快。如果你想使用FORALL进行批量插入,你可以从“for循环”中取出插件,但是为了额外的开销我不认为它会产生很大的不同。

至于批量做所有事情 - 我的建议是,对于数百万行 - 取决于表的“厚度/薄度” - 你肯定会在数据库中吹掉你的PGA和排序区域大小,除非你有一个非常友好的DBA,为您提供所需的所有记忆(可疑)。作为一般规则,我尽量不在内存中放置超过2-5百万行。

declare
  l_complex number; -- dont know the real datatype here??
BEGIN
  OPEN c1;
  LOOP 
    FETCH c1 BULK COLLECT INTO vTable LIMIT 20000;

  ---body code
  -- trying to avoid additional manipulation of the data here but there may     still be some

 FOR indx IN vTable.FIRST .. vTable.LAST loop
     l_complex : = COMPLX_CALC_FUNCTION(vtable(indx).TR_TM,vtable(indx).TR_CD );


    INSERT INTO Table2 VALUES vTable(indx); 
 end loop;

...



--ending stuff

; 

答案 1 :(得分:1)

  嘿你好。只是有机会查看您的查询。我会怎么做   建议做一个简单的测试。在我看来,这是可能的   如果可以避免任何上下文切换,总是使用SQL。

     

以下片段肯定会帮助您理解这一点。希望它有所帮助

set serveroutput on;
DECLARE
  lv_num DBMS_SQL.NUMBER_TABLE;
  lv_t1 PLS_INTEGER;
  lv_t2 PLS_INTEGER;
BEGIN
  lv_t1:=dbms_utility.get_time();
  SELECT LEVEL bulk collect INTO lv_num FROM DUAL CONNECT BY LEVEL < 1000000;
  FORALL I IN lv_num.FIRST..lv_num.LAST
  INSERT INTO NUM_TAB VALUES
    (lv_num(I)
    );
  lv_t2:=dbms_utility.get_time();
  dbms_output.put_line(lv_t2-lv_t1||' '||' Bulk collect');
  lv_t1:=dbms_utility.get_time();
  INSERT INTO NUM_TAB
  SELECT LEVEL FROM DUAL CONNECT BY LEVEL < 1000000;
  lv_t2:=dbms_utility.get_time();
  dbms_output.put_line(lv_t2-lv_t1||' '||' SQL Statement');
END;

-------------------------------OUTPUT--------------------------------------

PL/SQL procedure successfully completed.
16493  Bulk collect
1475  SQL Statement

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

答案 2 :(得分:0)

对于表演,你有两件事

  
      
  1. select语句:为此,您可以检查select语句的执行计划并调整select语句。
  2.         

    2.您的insert语句看起来非常精细,因为您在单击时选择20000行(LIMIT 20000)并将其插入另一行   带有批量插入的桌子。通过这种方式,您将有一个上下文切换   对于20000行,也没有内存不足的问题。

         

    在这种情况下你只需要调整你的select语句。