【问题标题】:Does Context Switching occur in stored procedure when BULK COLLECT data is manipulated?操作 BULK COLLECT 数据时,存储过程中是否会发生上下文切换?
【发布时间】:2016-02-14 20:42:38
【问题描述】:

我在 11g 和 12c Oracle 系统上运行。我对即将写的一些 PL/SQL 有疑问。我一直在进行广泛的研究,但我并没有完全看到答案,并希望从论坛中获得一些意见。我的主要目标是保持高水平的性能。在处理这些数据时,我试图避免上下文切换。

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

详情如下:

声明一个将执行内部连接的游标。在 SELECT 中我想做一些数据操作。我想调用一个函数来执行此操作,因为尝试在 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 主体中的数据?此外,对于这种情况,我还需要考虑其他性能方面的事情吗?我试图避免将数据转储到临时物理表中并对其进行操作,因为这似乎比在内存中执行要慢得多。感谢您的专家建议。

【问题讨论】:

  • 简而言之,是的。在 SQL 和 PL/SQL 引擎之间会有一个上下文切换,根据 Tom Kyte 的说法,这是一个 nasty 类型的切换。如果你真的想避免它,从循环体内部调用函数,或者尽可能使用 SQL 和原生函数来实现你的逻辑。
  • 你能给出这个“COMPLX”函数的定义吗?这个函数是否只对其参数执行简单的计算?或者该函数是否在其主体中调用了一些 SQL 语句(SELECT、UPDATE 等),这些语句会导致从 PL/SQL 到 SQL 引擎的额外上下文切换?您能否告诉我们表 A 和 B 之间的关系是 1-1 还是 1-n,我的意思是,如果来自 A 和 B 的两条记录的连接总是只产生 1 条记录,(1-1),或者可能产生许多记录 (1-n) ?
  • 是的,函数执行基本计算,例如:BEGIN IF TR_CD = '6' OR TR_CD = '7' THEN real_days := TR_CD; ELSE real_days := '5';万一; tr_pct := MOD(real_weeks := (NVL(TR_TM, 0) / 24) / real_days, 1); real_weeks := real_weeks - tr_pct;返回 real_weeks;

标签: oracle plsql


【解决方案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

; 

【讨论】:

  • 谢谢。所以听起来像从游标选择调用函数会注入上下文切换但在 BULK COLLECT 中将有助于避免它,即使会有单行操作?在正文中,我可能还需要进行一些计算,例如: calc_qty := CEIL(real_weeks*A.PT_QTY);然后根据结果添加一些 IF ELSE。
【解决方案2】:

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

下面的 sn-p 肯定会帮助你理解这一点。希望对你有帮助

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

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

【讨论】:

    【解决方案3】:

    为了性能,这里有两件事

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

    2.您的插入语句看起来非常好,因为您在一次提取时选择 20000 行(LIMIT 20000)并将其插入另一个 带有批量插入的表。这样,您将拥有一个上下文切换 20000行也不会出现内存不足的问题。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-18
      • 2017-04-24
      • 2019-10-02
      • 2015-06-28
      • 2014-09-02
      • 1970-01-01
      • 2013-08-12
      相关资源
      最近更新 更多