PL / SQL CPU性能调优

时间:2016-08-19 13:44:45

标签: performance plsql

我有PL / SQL程序,经过一些更改已经从30分钟的运行时间变为3小时,我们无法确定如何提高性能。 该程序最初输出一个csv文件,以便在excel中使用。该程序现已更改为输出XML以在excel中使用。 因此,SQL或数据库读取方面没有代码更改。 相关代码添加是从相关包中获取的过程,该包构建clobs,然后定期输出到文件,以避免过多的内存使用。在两个版本的程序中,utl_file.put_line用法的数量相似。

用于构建CLOB的代码示例是

PROCEDURE cell_write(p_xml_body  IN OUT NOCOPY CLOB,
                     p_data_type IN            VARCHAR2 := 'String',
                     p_style_id  IN            VARCHAR2 := NULL,
                     p_merge     IN            VARCHAR2 := NULL, 
                     p_formula   IN            VARCHAR2 := NULL,
                     p_line_feed IN            BOOLEAN  := TRUE,
                     p_content   IN            VARCHAR2) IS     
   v_line_feed   VARCHAR2(01) ;                    
BEGIN
  IF p_line_feed 
  THEN 
      v_line_feed := chr(10) ;
  ELSE 
      v_line_feed := ' ' ;
  END IF ;  
  p_xml_body := p_xml_body || '    <Cell';
  IF p_merge IS NULL 
  THEN 
      p_xml_body := p_xml_body ;
  ELSE 
      p_xml_body := p_xml_body || ' ss:MergeAcross="' || p_merge || '"' ;
  END IF ; 
  IF p_style_id IS NULL 
  THEN 
      p_xml_body := p_xml_body ;
  ELSE 
      p_xml_body := p_xml_body || ' ss:StyleID="'||p_style_id||'"' ;
  END IF;
  IF p_formula IS NULL
  THEN 
      p_xml_body := p_xml_body ;
  ELSE 
      p_xml_body := p_xml_body || ' ss:Formula="'||p_formula||'"' ;
  END IF;
  p_xml_body := p_xml_body || '><Data ss:Type="'||p_data_type||'">' ||                   P_content || '</Data></Cell>' || v_line_feed;
END cell_write;

写输出的代码是

PROCEDURE write_file(p_filename    IN VARCHAR2,
                     p_dir         IN VARCHAR2,
                     p_file_handle IN utl_file.file_type,
                     p_clob        IN CLOB)
  IS

c_amount CONSTANT BINARY_INTEGER := 32767;
l_buffer   VARCHAR2(32767);
l_chr10    PLS_INTEGER;
l_cloblen  PLS_INTEGER;
l_fhandler utl_file.file_type;
l_pos      PLS_INTEGER := 1;

BEGIN

l_cloblen  := dbms_lob.getlength(p_clob);

WHILE l_pos < l_cloblen
LOOP
  l_buffer := dbms_lob.substr(p_clob, c_amount, l_pos);
  EXIT WHEN l_buffer IS NULL;
  l_chr10 := instr(l_buffer, chr(10), -1);

  IF l_chr10 != 0 THEN
    l_buffer := substr(l_buffer, 1, l_chr10 - 1);
  END IF;
  DBMS_OUTPUT.PUT_LINE('Buffer Length ' || LENGTH(l_buffer)) ;
  DBMS_OUTPUT.PUT_LINE(l_buffer) ;
  utl_file.put_line(p_file_handle, l_buffer, TRUE);
  l_pos := l_pos + least(length(l_buffer) + 1, c_amount);
END LOOP;

EXCEPTION
WHEN OTHERS THEN
  DBMS_OUTPUT.PUT_LINE('Buffer Length ' || LENGTH(l_buffer)) ;
  DBMS_OUTPUT.PUT_LINE(l_buffer) ; 
  IF utl_file.is_open(l_fhandler) THEN
    utl_file.fclose(l_fhandler);
  END IF;
  RAISE;
 END;

和示例调用是

pk_create_excel_workbook.cell_write(p_xml_body    =>v_clob_term,
                                    p_line_feed   => FALSE,
                                    p_data_type   =>'Number',
                                    p_style_id    =>'s94',
                                    p_content     =>w_totemployeecontribT);

每个输出行调用过程cell_write大约100次,并且大约有30,000个输出行。因此输出行通常为8000到10000字节长。

我们预计运行时间会略有增加,但不会发生大幅度的跳跃。我忽略了什么?

2 个答案:

答案 0 :(得分:0)

我的猜测是cell_write中的多个连接。 (分析将确认。)

理想情况下,你会使用提供的XML接口工具,而不是像这样手动连接标签,但无论如何作为快速修复,我会尝试这样的事情(未经测试):

procedure cell_write
    ( p_xml_body  in out nocopy clob
    , p_data_type in varchar2 := 'String'
    , p_style_id  in varchar2 := null
    , p_merge     in varchar2 := null
    , p_formula   in varchar2 := null
    , p_line_feed in boolean := true
    , p_content   in varchar2 )
is
    k_line_feed constant varchar2(1) := case when p_line_feed then chr(10) else ' ' end;
begin
    p_xml_body := p_xml_body ||
        '    <Cell' ||
        case when p_merge is not null then ' ss:MergeAcross="' || p_merge || '"' end ||
        case when p_style_id is not null then ' ss:StyleID="' || p_style_id || '"' end ||
        case when p_formula is not null then ' ss:Formula="' || p_formula || '"' end ||
        '><Data ss:Type="' || p_data_type || '">' || p_content || '</Data></Cell>' ||
        k_line_feed;
end cell_write;

答案 1 :(得分:0)

我现在使用的一种方法是在执行{{1}之前使用CLOBs有状态varchar2变量作为缓冲区使用临时PL/SQL附加。我在核心CLOB周围创建了一个简单的API,允许我随时写入多个DBMS_LOB,必要时在它们之间交替。你可以看看它,也许你可以获得更好的表现:

https://github.com/GeraldoViana/nksg/blob/master/src/nksg_tempclob.pks https://github.com/GeraldoViana/nksg/blob/master/src/nksg_tempclob.pkb