我有以下PL / SQL代码,它会更新表中每行约1亿行的一列。我使用BULK COLLECT重复从表中获取150,000行并更新行。我在50,000次更新后做了提交。
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有什么问题吗?我已经完成了谷歌的研究并发现一些线程说可以通过扩展UNDO表空间来避免这个错误,但是在我的情况下这不是一个选项。因此,我需要修改PL / SQL代码。
答案 0 :(得分: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;