2017-03-14 4 views
-1

次のPL/SQLコードを使用して、約2億行の表の各行の1列を更新します。 BULK COLLECTを使用して、テーブルから150,000行を繰り返し取得し、行を更新します。私は50,000回の更新後にコミットします。ORA-01555 BULK COLLECTを使用して2億の行を更新するとエラーが発生する

DECLARE 
    CURSOR jobs_cursor IS 
     SELECT e.ID, e.PTI, e.CAT, e.JOBNAME, e.JOBDATE, e.WORK_DESCRIPTION 
       FROM JOB e 
       WHERE length(e.WORK_DESCRIPTION) > 1000; 

    TYPE JOBS_TYPE IS TABLE OF jobs_cursor%ROWTYPE; 
    v_jobs JOBS_TYPE; 
    fetch_jobs_limit PLS_INTEGER := 150000; 

    trimmed_work_description VARCHAR2(2000 CHAR); 
    sub_string_work_description_left VARCHAR2(1000 CHAR); 
    sub_string_work_description_right VARCHAR2(1000 CHAR); 
    update_counter NUMBER := 0; 
    commit_counter NUMBER := 50000; 

BEGIN 
    OPEN jobs_cursor; 
    LOOP 
     FETCH jobs_cursor BULK COLLECT INTO v_jobs LIMIT fetch_jobs_limit; 
     EXIT WHEN v_jobs.COUNT = 0; 

     FOR idx IN 1..v_jobs.COUNT 
     LOOP 
      trimmed_work_description := ' '; 

      IF v_jobs(idx).WORK_DESCRIPTION IS NOT NULL THEN 
       trimmed_work_description := TRIM(TRAILING ' ' FROM v_jobs(idx).WORK_DESCRIPTION); 
      END IF; 

      IF length(trimmed_work_description) <= 1000 THEN 
       UPDATE JOBS j SET j.WORK_DESCRIPTION = trimmed_work_description WHERE j.ID = v_jobs(idx).ID; 

       update_counter := update_counter + 1; 
       IF mod(update_counter, commit_counter) = 0 THEN 
        COMMIT; 
        update_counter := 0; 
       END IF; 
       CONTINUE; 

      ELSIF length(trimmed_work_description) > 1000 THEN 
       sub_string_work_description_left := SUBSTR(trimmed_work_description, 1, 1000); 
       sub_string_work_description_right := SUBSTR(trimmed_work_description, 1001, 2000); 
      END IF; 

      UPDATE JOBS j SET j.WORK_DESCRIPTION = sub_string_work_description_left WHERE j.ID = v_jobs(idx).ID; 
      INSERT INTO JOBS j VALUES ("SEQUENCE_JOBS".NEXTVAL, j.PTI, j.CAT, j.JOBNAME, j.JOBDATE, sub_string_work_description_right); 

      update_counter := update_counter + 1; 
      IF mod(update_counter, commit_counter) = 0 THEN 
       COMMIT; 
       update_counter := 0; 
      END IF; 

     END LOOP; 
    END LOOP; 
    COMMIT; 
    CLOSE jobs_cursor; 
END; 

コードは数時間実行されますが、その後、OracleではORA-01555 - Snapshot too old - Rollback segment number 14 with name xxxx too smallが発生します。

PL/SQLの問題点を教えてください。私はすでにGoogleの調査をして、このエラーはUNDOテーブルスペースを拡張することで回避できると言っているスレッドを見つけましたが、これは私の場合は選択肢ではありません。したがって、PL/SQLコードを変更する必要があります。

+0

(未検証/テスト)これと同様です。 'MESSAGE'はレコードに存在しません。 –

+0

別のスレッドで再定義と変換を使用してこれを行う方法を教えてもらえませんでしたか? – BobC

+0

@WernfriedDomscheitそれはタイプミスでした、ごめんなさい。メッセージが正しくありません。私はコードを修正しました。 – Javiator

答えて

2

最初のビューでは、ループ内で更新を行う理由は何も表示されません。単一のステートメントで可能である必要があります。 `v_jobs(IDX).MESSAGEがNULL THEN`されていない場合:あなたは、この行でエラーを取得する必要があります

update JOBS j SET 
WORK_DESCRIPTION = SUBSTR(TRIM(TRAILING ' ' FROM WORK_DESCRIPTION), 1, 1000) 
WHERE length(WORK_DESCRIPTION) > 1000; 

INSERT INTO JOBS 
SELECT SEQUENCE_JOBS.NEXTVAL, j.PTI, j.CAT, j.JOBNAME, j.JOBDATE, 
    SUBSTR(WORK_DESCRIPTION, 1001, 2000) 
FROM JOBS j 
WHERE length(TRIM(TRAILING ' ' FROM WORK_DESCRIPTION)) > 1000; 
+0

1つのUPDATE文で更新を行うと、UNDO表スペースに問題はありませんか? – Javiator

+0

UNDO表領域が十分な大きさではない場合は、ORA-01555エラーの原因となる中間コミットであるため、すでに問題があります。だから、あなたが今実際に持っている問題を避ける別のアプローチを試してみませんか? – APC

+0

2億本の行のそれぞれについて更新が必要です。私はすでに選択した行にCOUNT(*)を実行しました。 – Javiator

関連する問題