1

我需要在大约 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

4 回答 4

9

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

中间提交只会实现

  • 额外的开销,因为每次提交都会产生额外的工作,
  • 在错误的情况下失去可重新启动性(以及事务完整性的损失),
  • 遇到 ORA-1555 的机会更大

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

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

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

到目前为止,您可以使用的最大优化是从集合的角度来考虑,而不是传统的由成批的单行语句组成的过程方法。

于 2012-06-01T08:45:57.840 回答
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; 
于 2012-06-01T08:21:55.767 回答
0

我不会为此推荐游标方法。对于这种情况,我使用附加并行提示。大多数情况下,您的查询实际上以 N 倍的速度运行,其中 N 是并行度。偶尔使用 nologging / noarchivelog 绕过灾难恢复是个好主意。

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

于 2012-06-01T06:04:06.377 回答
-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; 
于 2012-06-01T06:06:51.847 回答