2017-03-14 7 views
-1

다음 PL/SQL 코드를 사용하면 약 2 억 개의 행이있는 테이블에서 각 행의 한 열을 업데이트합니다. 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; 

코드는 몇 시간 동안 실행되지만 오라클은 ORA-01555 - Snapshot too old - Rollback segment number 14 with name xxxx too small을 발생시킵니다.

내 PL/SQL의 문제점을 알려주십시오. 이미 Google 조사를 수행하고 일부 스레드에서 UNDO 테이블 공간을 확장하여이 오류를 피할 수 있다고 말했지만 내 경우에는 옵션이 아닙니다. 따라서 PL/SQL 코드를 수정해야합니다.

+0

이 줄에는 'IF v_jobs (메시지) .MESSAGE IS NOT NULL THEN'이라는 오류가 표시됩니다. 'MESSAGE'는 기록에 존재하지 않습니다. –

+0

다른 스레드에서 재정의 대 변형을 사용하여 이것을 수행하는 방법을 보여주지 않았습니까? – BobC

+0

@WernfriedDomscheit 그건 미안 해요. 메시지가 올바르지 않습니다. 코드를 수정했습니다. – Javiator

답변

2

첫 번째보기에서는 루프를 사용하여 업데이트하는 이유가 표시되지 않습니다. 단일 문으로 가능해야합니다. 이와 유사 할 것입니다. (확인/테스트되지 않았습니다)

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

하나의 UPDATE 문에서 업데이트를 수행하면 실행 취소 테이블 공간에 문제가 발생하지 않습니까? – Javiator

+0

UNDO 테이블 공간이 충분히 큰 경우가 아니라면 ORA-01555 오류의 원인 인 중간 커밋이므로 이미 문제가 있습니다. 그렇다면 문제를 피할 수있는 다른 접근법을 시도해보십시오 * 실제로 당신이 지금 당장 가지고 있습니까? – APC

+0

2 억 줄의 각각에 대해 업데이트해야합니다. 선택한 행에서 이미 COUNT (*)를 수행했습니다. – Javiator