在游标中执行插入的最佳实践

时间:2012-06-01 05:51:43

标签: oracle plsql

我需要在大约300000行的游标中进行一些插入,然而这运行得很慢,有关如何让它运行得更快的任何想法?我可以通过批量提交加快速度吗?所以例如我会在第1000行之后执行提交

DECLARE

  CURSOR test_cursor IS 
    SELECT a from database.mytable
BEGIN

  FOR curRow IN test_cursor LOOP

    insert into tableb (testval)
    values ('something');

  commit;

  END LOOP;
END; 

4 个答案:

答案 0 :(得分:8)

300000行不是那么多行。除非每行大,否则不应在批处理中间提交。

中级提交will only achieve

  • 额外开销,因为每次提交都会创建额外的工作,
  • 在出现错误(以及失去事务完整性)的情况下失去可重启性,
  • 更有可能遇到ORA-1555

如果你的进程实际上是一个在循环中有一个插入的游标,你应该运行一个语句:

BEGIN
   INSERT INTO tableb (col1..coln) (SELECT col1..coln FROM database.mytable);
END;

如果您仍然需要额外的性能,可以考虑直接插入和并行操作,但可能是“仅”300k行的过度优化。

到目前为止,您可以使用的最大优化是根据而不是传统的程序方法进行思考,而不是由批量单行语句组成。

答案 1 :(得分:3)

或者你可以试试这个:

DECLARE
  CURSOR test_cursor IS 
    SELECT col1 from table_a;

  TYPE fetch_array IS TABLE OF test_cursor%ROWTYPE;
  test_array fetch_array;

  l_errors                              PLS_INTEGER;
  l_dml_errors                          EXCEPTION;
  PRAGMA EXCEPTION_INIT(l_dml_errors, -24381);    

BEGIN

  open test_cursor;
  loop
    fetch test_cursor bulk collect into test_array limit 10000;
    forall i in 1..test_array.count save exceptions
      insert into table_b(col1)
      values(test_array(i).col1);
      exit when test_cursor%notfound;
  end loop;
  close test_cursor;
  commit;

EXCEPTION 
WHEN l_dml_errors THEN
    l_errors := SQL%BULK_EXCEPTIONS.COUNT;
    dbms_output.put_line('Number of INSERT statements that failed: ' || l_errors);
    FOR i IN 1 .. l_errors
    LOOP
      dbms_output.put_line('Error #' || i || ' at '|| 'iteration #' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);
      dbms_output.put_line('Error message is ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
    END LOOP;
END; 

答案 2 :(得分:0)

我不建议使用游标方法。我使用追加并行提示这样的情况。大多数情况下,您的查询字面上运行的速度是N次,其中N是并行度。使用nologging / noarchivelog绕过灾难恢复有时是一个好主意。

对于真正大型的迁移(几十到几百GB),我发现在表的自然键(通常是日期)上批处理是个好主意。周围的一些少量状态可以让你在必要时随意取消+恢复迁移。

答案 3 :(得分:-1)

可能会这会帮助你试试这个

DECLARE
i number;
  CURSOR test_cursor IS 
    SELECT a from database.mytable
BEGIN

  FOR curRow IN test_cursor LOOP

    insert into tableb (testval)
    values ('something');
i:i+1;
if mod(i,1000)=0 then
  commit;
end if;
  END LOOP;
commit;
END; 
相关问题