使用游标写入CSV并将其插入表中

问题描述

我需要获取既将csv数据写入文件,又用同一记录更新表的数据。该光标可能会找到10万至30万条记录。

有没有比循环更有效的方法呢?我当时想在循环外使用Select进行插入,但是无法知道每条记录是否已成功写入文件

DECLARE
...

   file_output VARCHAR2 (3000);
   col2 column2%type;
   col3 column3%type;

 CURSOR my_cursor
   IS
    select
      csv_val1 || ',' || csv_val2|| ',' || csv_val3,column2,column3
    from my_cursor_table

...

BEGIN

...
   FETCH my_cursor INTO file_output,col2,col3;

   WHILE my_cursor%FOUND
   LOOP
      BEGIN
         UTL_FILE.PUT_LINE (ID,file_output);
         insert into my_insert_table values(col2,col3);
..

END;

解决方法

您可以:

  • bulk collect将查询放入具有限制的数组中
  • 遍历数组,将每个元素写入文件
  • utl_file调用包装在记录错误并从数组中删除该元素的异常处理程序中。
  • 使用forall i in indices of仅插入成功的值

这应该比单行处理更快。在这个简单的示例中,处理了10万行:

  • 逐行处理约为15秒
  • 批量收集/永久保存约1.5秒

所以快10倍左右:

create table t (
  c1 int,c2 int
);

set timing on
declare
  cursor cur is
    select level c1,mod ( level,10 ) c2
    from   dual
    connect by level <= 100000;
  rec cur%rowtype;
  f utl_file.file_type;
begin
  f := utl_file.fopen ('TMP','test.csv','w');
  open cur;
  loop
    fetch cur into rec;
    exit when cur%notfound;
    
    begin 
      utl_file.put_line ( f,rec.c1 || ',' || rec.c2 );
      insert into t values ( rec.c1,rec.c2 );
    exception
      when others then
        dbms_output.put_line( 'Error logging' );
    end;
  end loop;
  close cur;
  utl_file.fclose(f);
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:15.192

declare
  cursor cur is
    select level c1,10 ) c2
    from   dual
    connect by level <= 100000;
  type rec_arr is table of cur%rowtype
    index by pls_integer;
  rec rec_arr;
  f utl_file.file_type;
begin
  f := utl_file.fopen ('TMP','w');
  open cur;
  loop
    fetch cur bulk collect into rec
    limit 100;
    exit when rec.count = 0;
    
    for i in 1 .. rec.count loop
      begin
        utl_file.put_line ( f,rec(i).c1 || ',' || rec(i).c2 );
      exception
        when others then
          dbms_output.put_line( 'Error logging' );
          rec.delete(i);
      end;
    end loop;
    forall i in indices of rec
      insert into t values ( rec(i).c1,rec(i).c2 );
  end loop;
  close cur;
  utl_file.fclose(f);
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.578

请注意,这无法处理写入成功但插入失败的情况...

要解决此问题,您可以执行以下操作:

  • 插入行(使用forall ... save exceptionsinsert ... log errors
  • 写成功的行
  • 删除未能写入文件的行