Oracle

时间:2016-07-30 19:28:54

标签: sql oracle performance plsql query-optimization

假设我们有两个名为Tb1和Tb2的表,我们将把数据从一个替换为另一个。 Tb1是主要的数据来源,Tb2是目的地。这种更换操作有3个部分。

在第一部分中,我们将验证Tb1中的所有行并检查它们是否正确。例如,国家安全代码必须具有10位数字,或者真正的客户必须具有有效的出生日期,因此根据这些验证规则,已经考虑了28种不同的验证方法和错误代码。 在验证过程中,每个被破坏的行的描述和状态都将更新为新状态。

第2部分修复了行的问题,第3部分将它们替换为Tb2。

例如,这一行表示它有4个不同的错误。

  

- Tb1.desc = 6,8,14,16
   - Tb1.sts = 0

正确的数据行

  

- Tb1.desc = Null i    - Tb1.sts = 1

我最近一直在研究第一部分,并提出了一个解决方案,它工作正常,但速度太慢。不幸的是,验证100,000行需要31分钟。在实际情况下,我们将验证超过200万条记录,因此尽管它具有所有功能,但它完全没用。

让我们来看看我的包裹:

procedure Val_primary IS
      begin    
        Open X_CUSTOMER;
              Loop            
                fetch X_CUSTOMER bulk collect into CUSTOMER_RECORD;
                EXIT WHEN X_CUSTOMER%notfound;                  
                For i in CUSTOMER_RECORD.first..CUSTOMER_RECORD.last loop       
                Val_CTYP(CUSTOMER_RECORD(i).XCUSTYP);
                Val_BRNCH(CUSTOMER_RECORD(i).XBRNCH);

                --Rest of the validations ...

                UptDate_Val(CUSTOMER_RECORD(i).Xrownum);
                end loop;
                CUSTOMER_RECORD.delete;                              
              End loop;
         Close X_CUSTOMER;         
      end Val_primary;

验证程序内:

procedure Val_CTYP(customer_type IN number)IS
  Begin
    IF(customer_type<1 or customer_type>3)then
      RW_FINAL_STATUS:=0;
      FINAL_ERR_DSC:=Concat(FINAL_ERR_DSC,ERR_INVALID_CTYP);
    End If;
  End Val_CTYP;

更新程序内:

procedure UptDate_Val(rownumb IN number) IS 
  begin 
    update tb1 set tb1.xstst=RW_FINAL_STATUS,tb1.xdesc=FINAL_ERR_DSC where xc1customer.xrownum=rownumb; 
    RW_FINAL_STATUS:=1;
    FINAL_ERR_DSC:=null;
  end UptDate_Val;

有没有办法减少执行时间? 超过200万条记录必须在20分钟内完成。

2 个答案:

答案 0 :(得分:0)

也许每个验证检查可以是内联视图中的case表达式,您可以在封闭查询中连接它们等,为您提供一个可以驱动update的SQL语句。有点像:

select xxx, yyy, zzz  -- whatever columns you need from xc1customer
     , errors  -- concatenation of all error codes that apply
     , case when errors is not null then 0 else 1 end as status
from   ( select xxx, yyy, zzz
              , trim(ltrim(val_ctyp||' ') || ltrim(val_abc||' ') || ltrim(val_xyz||' ') || etc...) as errors
         from   ( select c.xxx, c.yyy, c.zzz
                       , case when customer_type < 1 or customer_type > 3 then err_invalid_ctyp end as val_ctyp
                       , case ... end as val_abc
                       , case ... end as val_xyz
                  from   xc1customer c
                )
       );

坚持程序方法,缓慢的部分似乎是单行更新。将所有2000万行批量收集到会话内存中仅用于应用2000万个单独更新没有任何优势。快速解决方法是向limit添加bulk collect子句(并将exit移动到循环的底部),让您的验证过程设置一个值数组而不是更新表,并将更新批处理为每循环迭代一次forall

通过将记录和数组传入和传出过程而不是将所有内容都设置为全局变量,您可以更自由一点,因为通过引用传递意味着没有性能开销。

答案 1 :(得分:0)

有两种潜在的攻击线。

  1. 具体实施。将集合读入会话内存。与全局内存分配相比,这通常非常小。将100000个长行读入会话内存是一个坏主意,可能会导致性能问题。因此,将流程分解为更小的块(比如1000行)很可能会提高吞吐量。
  2. 一般实施。三方进程有什么意义?使用一些错误标志更新Table1是一项昂贵的活动。更有效的方法是将修复程序应用于集合中的数据并将其应用于Table2。如果需要跟踪所做的更改,可以编写日志记录。
  3. 应用这些建议,您最终会得到一个看起来有点像这样的程序:

    procedure one_and_only is
    begin    
        open x_customer;
        << tab_loop >>
        loop            
            fetch x_customer bulk collect into customer_record
                limit 1000;
            exit when customer_record.count() = 0;  
            << rec_loop >>
            for i in customer_record.first..customer_record.last loop       
                val_and_fix_ctyp(customer_record(i).xcustyp);
                val_and_fix_brnch(customer_record(i).xbrnch);
    
                --rest of the validations ...
    
            end loop rec_loop;
    
            -- apply the cleaned data to target table
            forall j in 1..customer_record.count()
                insert into table_2
                values customer_record(j);                              
         end loop tab_loop;
        close x_customer;         
    end one_and_only;
    

    请注意,此方法要求customer_record集合与目标表的投影相匹配。此外,请勿使用%notfound来测试光标的结尾,除非您可以保证读取记录的总数是LIMIT编号的精确倍数