我有一个存储过程,我认为它是在一个无限循环中捕获的,但是在将它拆开并单独运行各种循环之后,事实证明我的代码导致了
ORA-01000:超出最大打开游标数
问题似乎与我填充前两个游标的方式有关。这可能看起来像一个愚蠢的问题,但我希望有人能就如何更好地处理这些游标的数量给我一些建议?
下面你将完整地找到我的程序,然后我将分解似乎导致问题的区域。
完整程序:
procedure SplitIntrvlsAtShiftBoundaries is
vRecCount number;
RC number;
vShiftIDAtIntervalStart number;
vShiftIDAtIntervalEnd number;
vDummyRecSecs number(6,4) := 0.0;
vStartTimestamp timestamp with time zone;
vEndTimestamp timestamp with time zone;
cCurs Sys_refcursor;
vCurrentRec TMP_SHIFT_LIST_TBL%rowtype;
vCurrentInterval MACHINE_INTERVAL%rowtype;
vNewInterval MACHINE_INTERVAL%rowtype;
lProcName CONST.PBString;
vIntervalDOW smallint; -- intervals never span a midnite boundary
-- are split over midnite in prior steps.
CURSOR C1 IS SELECT * FROM TMP_SHIFT_MI_TBL ORDER BY START_DATE_TIME ASC, MACHINE_ID;
CURSOR C2 IS SELECT * FROM TMP_SHIFT_LIST_TBL ORDER BY SHIFT_START_DAY ASC, SHIFT_START_TIME;
BEGIN
lProcName := 'TRANSFORM_PROCESS_2.SplitIntrvlsAtShiftBoundaries';
pb_util.logdata(3, lProcName, 'Process Started ' );
DELETE FROM TMP_SHIFT_MI_TBL;
DELETE FROM TMP_SHIFT_LIST_TBL;
INSERT INTO TMP_SHIFT_MI_TBL
SELECT *
FROM MACHINE_INTERVAL
WHERE interval_state = 0
AND start_date_time <> CONST.STARTDATE
AND trunc(calc_end_time) < trunc( CONST.ENDDATE) - 144;
BEGIN
FOR R1 IN C1
LOOP
-- gets the current interval from the machine interval table by using the interval id found from the tmp_table
SELECT * INTO vCurrentInterval
FROM Machine_Interval MI
WHERE R1.Machine_Interval_ID = MI.Machine_Interval_Id;
-- gets the shifts containning the start and end dates obtained from the machine interval
Shift_pkg.getShiftsContaining(R1.start_date_time, R1.calc_end_time, cCurs, vRecCount);
FETCH cCurs INTO vCurrentRec;
LOOP
-- populates the tmp_shift_list_tbl dynamically
BEGIN
EXIT WHEN cCurs%notfound;
EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
(shift_id_pk, shift_name, shift_start_day, shift_start_time,
shift_end_day, shift_end_time, site_id_fk, shift_day_id,
startoffset, endoffset)
VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)'
USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID,
vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;
END;
END LOOP;
pb_util.logdata(3, lProcName, 'FOUND: ', 'found ' || vRecCount ||
' shifts in machine interval: ' || R1.MACHINE_INTERVAL_ID);
-- depending on the # of shifts found in vRecCount different cases are applied.
CASE
WHEN vRecCount = 1 -- apply shift id to interval
THEN
BEGIN
FOR R2 IN C2
LOOP
UPDATE MACHINE_INTERVAL MI
SET MI.SHIFT_ID = R2.SHIFT_ID_PK
WHERE MI.MACHINE_INTERVAL_ID = R1.MACHINE_INTERVAL_ID;
END LOOP;
END;
WHEN vRecCount > 1 -- split up the interval between the shifts i.e. create intervals
THEN
BEGIN
FOR R2 IN C2
LOOP
-- make copy of the current interval.
vNewInterval := vCurrentInterval;
-- set the new interval duration
vCurrentInterval.Interval_Duration := pb_util.intervaltosecond(R2.shift_Start_Time -
vCurrentInterval.Start_Date_Time);
-- set the new shift id for the machine interval table
vCurrentInterval.Shift_ID := R2.Shift_ID_PK;
-- update the record.
DM.MachineIntervalRecordUpdate(MachineIntervalID => vCurrentInterval.Machine_Interval_ID,
StartDateTime => vCurrentInterval.Start_Date_Time,
IntervalDuration => vCurrentInterval.Interval_Duration,
IntervalCategory => vCurrentInterval.INTERVAL_CATEGORY,
NPTCategoryID => vCurrentInterval.NPT_CATEGORY_ID,
NPTControlledID => vCurrentInterval.CATEGORYTYPENUMERIC,
NPTOPStateID => vCurrentInterval.OPERATIONALSTATENUMERIC,
pExecutionSecs => vDummyRecsecs);
UPDATE MACHINE_INTERVAL MI
SET MI.Shift_ID = vCurrentInterval.Shift_ID
WHERE MI.Machine_Interval_ID = vCurrentInterval.Machine_Interval_ID;
-- set new start date time & interval duration
vNewInterval.Start_Date_Time := R2.Shift_End_Time;
vNewInterval.Interval_Duration := pb_util.intervaltosecond(vNewInterval.Calc_End_Time -
vNewInterval.Start_Date_Time);
-- create new record in interval table.
RC := DM.MachineIntervalRecordInsert(MachineIntervalRecord_IN => vNewInterval,
pExecutionSecs => vDummyRecsecs);
-- set current interval to the newly created interval and loop.
vCurrentInterval := vNewInterval;
END LOOP;
END;
ELSE -- if no shifts are found then set id to null
UPDATE MACHINE_INTERVAL MI
SET SHIFT_ID = 0
WHERE MI.MACHINE_INTERVAL_ID = R1.MACHINE_INTERVAL_ID;
END CASE;
END LOOP;
pb_util.logdata(3, lProcName, 'Process Completed ' );
COMMIT;
END;
exception when others then
pb_util.logdata(1, 'TRANSFORM_PROCESS_2.SplitIntrvlsAtShiftBoundaries', 'EXCEPTION THROWN (880B)', SQLERRM || ' STACK: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END SplitIntrvlsAtShiftBoundaries;
我非常肯定导致问题的游标是这两个:
CURSOR C1 IS SELECT * FROM TMP_SHIFT_MI_TBL ORDER BY START_DATE_TIME ASC, MACHINE_ID;
CURSOR C2 IS SELECT * FROM TMP_SHIFT_LIST_TBL ORDER BY SHIFT_START_DAY ASC, SHIFT_START_TIME;
程序似乎打开最大光标的区域超出:
FOR R1 IN C1
LOOP
-- gets the current interval from the machine interval table by using the interval id found from the tmp_table
SELECT * INTO vCurrentInterval
FROM Machine_Interval MI
WHERE R1.Machine_Interval_ID = MI.Machine_Interval_Id;
-- gets the shifts containning the start and end dates obtained from the machine interval
Shift_pkg.getShiftsContaining(R1.start_date_time, R1.calc_end_time, cCurs, vRecCount);
FETCH cCurs INTO vCurrentRec;
LOOP
-- populates the tmp_shift_list_tbl dynamically
BEGIN
EXIT WHEN cCurs%notfound;
EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
(shift_id_pk, shift_name, shift_start_day, shift_start_time,
shift_end_day, shift_end_time, site_id_fk, shift_day_id,
startoffset, endoffset)
VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)'
USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID,
vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;
END;
END LOOP;
pb_util.logdata(3, lProcName, 'FOUND: ', 'found ' || vRecCount ||
' shifts in machine interval: ' || R1.MACHINE_INTERVAL_ID);
答案 0 :(得分:5)
隐式游标永远不会生成ORA-01000错误,因为Oracle会在超出范围时负责关闭它们。
代码中的问题是cCurs
。 Shift_pkg.getShiftsContaining
打开游标,代码从游标中获取。但是,当您从中获取光标时,没有代码可以关闭光标。完成提取后,您的代码需要关闭光标。
代码本身有点令人困惑。通常,您将从循环内的游标中获取。但是,您发布的代码在循环外部进行了提取,这意味着如果cCurs
返回一行,您将拥有无限循环。因此,当cCurs
从FETCH
发送数据时,LOOP
FETCH cCurs INTO vCurrentRec;
EXIT WHEN cCurs%notfound;
EXECUTE IMMEDIATE 'insert into tmp_shift_list_tbl
(shift_id_pk, shift_name, shift_start_day, shift_start_time,
shift_end_day, shift_end_time, site_id_fk, shift_day_id,
startoffset, endoffset)
VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10)'
USING vCurrentRec.SHIFT_ID_PK, vCurrentRec.SHIFT_NAME,
vCurrentRec.SHIFT_START_DAY, vCurrentRec.SHIFT_START_TIME,
vCurrentRec.SHIFT_END_DAY, vCurrentRec.SHIFT_END_TIME,
vCurrentRec.SITE_ID_FK, vCurrentRec.SHIFT_DAY_ID,
vCurrentRec.STARTOFFSET, vCurrentRec.ENDOFFSET;
END LOOP;
CLOSE cCurs;
永远不会返回任何数据,或者您有一个无限循环,或者您发布的代码与您实际运行的代码不完全相同。
通常,你会有这样的东西,它会从循环中的光标中获取,当没有更多行可用时退出循环,然后关闭光标。
C1
话虽如此,似乎没有任何理由在这里使用动态SQL。为变量选择更有意义的名称也会有所帮助:R1
,C2
,R2
,cCurs
和{{1}}并非完全有意义的名称。而且您通常希望将代码分解为更易于阅读和消化的较小块。