我有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);
每个输出行调用大约100次过程cell_write,大约有30,000个输出行。因此输出线通常为8000到10000字节长。
我们预计运行时间会略有增加,但不会出现大幅度的跳跃。我忽略了什么?
我的猜测是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;
我已经使用了很长一段时间的一种方法,就是在执行CLOBs
附加之前使用varchar2
有状态PL/SQL
变量作为缓冲区使用临时CLOB
。我在核心DBMS_LOB
周围创建了一个简单的API,允许我随时写入多个CLOB
,必要时在它们之间交替。您可以看看它,也许您可以获得更好的性能:
https://github.com/GeraldoViana/nksg/blob/master/src/nksg_tempclob.pks https://github.com/GeraldoViana/nksg/blob/master/src/nksg_tempclob.pkb