批量收集问题
Posted
技术标签:
【中文标题】批量收集问题【英文标题】:Issue with Bulk Collect 【发布时间】:2015-07-22 16:36:09 【问题描述】:我有一个过程,我需要使用批量收集从游标中获取数据。这种方法的问题是 - 有时记录正在被处理,有时它们不是。我无法确定根本原因。当我尝试调试问题时,toad 没有响应并超时。这是代码。请帮忙!
PROCEDURE GetPendingItems(pprogramidlst IN VARCHAR2, EventCur OUT cur)
AS
vSessionId NUMBER;
vDefaultValue svoltp.param.DefaultValue%TYPE;
TYPE EventRec IS TABLE OF TrigEventQueue.TrigEventQueueId%TYPE;
TYPE EventPartitionDate IS TABLE OF TrigEventQueue.PartitionDate%TYPE;
vEventRec EventRec;
vEventPartitionDate EventPartitionDate;
vOldestPossibleDate DATE;
vtrigeventqueueid VARCHAR2 (250);
vprogramidlst VARCHAR2 (250);
vETInterval number;
VCOUNT NUMBER;
VCOUNT1 NUMBER;
CURSOR PRCURSOR(vCount1 NUMBER) IS
SELECT TrigEventQueueId, PartitionDate FROM TRIGEVENTQUEUE A
WHERE TrigEventQueueId IN (SELECT TrigEventQueueId
FROM ( SELECT teq.TrigEventQueueId, teq.PartitionDate, teq.EventProcessingSessionId,teq.TrigEventStateId
FROM svoltp.TrigEventQueue teq,
programtrigevent pte,
trigevent te
WHERE teq.TrigEventStateId = gcEventStatePending
AND teq.EventProcessingSessionId IS NULL
AND teq.ProgramTrigEventId = pte.ProgramTrigEventId
AND pte.TrigEventId = te.TrigEventId
AND teq.PartitionDate >
(SYSDATE - (te.NumHoursUntilEventExpired / 24))
AND teq.PartitionDate > vOldestPossibleDate
ORDER BY teq.TrigEventCreatedTS) a
WHERE ROWNUM <= vCount1)
FOR UPDATE OF A.TrigEventQueueId, A.PARTITIONDATE SKIP LOCKED;
BEGIN
vSessionId := TrigEventSessionIdSeq.NEXTVAL;
vprogramidlst := pprogramidlst;
SELECT DefaultValue
INTO vDefaultValue
FROM svoltp.Param
WHERE ParamId = gcMaxPenEventsParam;
SELECT DefaultValue
INTO vETInterval
FROM svoltp.Param
WHERE ParamId = 2755;
-- Use MAX number of expiry hours to identify an oldest possible date/time that any event could be picked up for.
SELECT SYSDATE - (MAX (NumHoursUntilEventExpired) / 24)
INTO vOldestPossibleDate
FROM trigevent;
SELECT COUNT(1) INTO VCOUNT1
FROM ( SELECT teq.TrigEventQueueId, teq.PartitionDate
FROM svoltp.TrigEventQueue teq,
programtrigevent pte,
trigevent te
WHERE teq.TrigEventStateId = gcEventStatePending
AND teq.EventProcessingSessionId IS NULL
AND teq.ProgramTrigEventId = pte.ProgramTrigEventId
AND pte.TrigEventId = te.TrigEventId
AND teq.PartitionDate >
(SYSDATE - (te.NumHoursUntilEventExpired / 24))
AND teq.PartitionDate > vOldestPossibleDate
ORDER BY teq.TrigEventCreatedTS) a
WHERE ROWNUM <= vDefaultValue;
IF VCOUNT1 > 0 THEN
SELECT count(1) into vcount FROM ETINSTANCESTATUS
WHERE datediff ('SS', INSTANCEUPDATETIME, SYSDATE) < vETInterval;
if vcount > 0 then
vcount1 := round(vcount1/vcount);
else
vcount1 := vcount1;
end if;
END IF;
OPEN PRCURSOR(vcount1);
LOOP
FETCH PRCURSOR BULK COLLECT INTO vEventRec, vEventPartitionDate LIMIT 100;
-- EXIT WHEN PRCURSOR%NOTFOUND;
--SVOLTP.PKGSVOLTPLOCK.SLEEP(1);
FORALL i IN vEventRec.FIRST .. vEventRec.LAST
UPDATE svoltp.TrigEventQueue teq
SET teq.EventProcessingSessionId = vSessionId,
teq.TrigEventStateId = gcEventStateLocked, --6 : Locked State
teq.LastUser = 1003,
teq.LastUpdate = SYSDATE
WHERE teq.TrigEventQueueId = vEventRec (i)
AND teq.PartitionDate = vEventPartitionDate (i);
END LOOP;
COMMIT;
CLOSE PRCURSOR;
OPEN EventCur FOR
SELECT TrigEventQueueId, ProgramTrigEventId, PartitionDate
FROM svoltp.TrigEventQueue teq
WHERE teq.EventProcessingSessionId = vSessionId
AND teq.TrigEventStateId = gcEventStateLocked
AND teq.PartitionDate > vOldestPossibleDate;
EXCEPTION
WHEN OTHERS
THEN
OPEN EventCur FOR
SELECT 1
FROM DUAL
WHERE 1 = 2;
END GetPendingItems;
【问题讨论】:
随着循环 EXIT 被注释掉,它将永远运行...... ... 但是在使用批量收集时,出口需要在循环的末尾,而不是直接在获取之后;也许那是您的问题,有些工作没有完成,而您尝试调试它却变得更糟? (不过,您的when others
异常处理程序不会帮助您找到任何真正的问题,如果这不是问题的话......)
【参考方案1】:
看起来您已注释掉 exit
作为您尝试调试的一部分 - 这就是导致 Toad 无响应的原因,因为这意味着循环永远不会退出(正如 Tony Andrews 指出的那样)。
有了exit
,如果游标查询发现的行数少于100 行,您将不会处理任何内容,即使实际检索到了一些数据,您也会看到PRCURSOR%NOTFOUND
。如果它发现超过 100 行,您将处理第一批,但仍然会丢失最后一批少于 100 的记录。
因此您需要将exit
移动到循环的末尾并更改条件:
LOOP
FETCH PRCURSOR BULK COLLECT INTO vEventRec, vEventPartitionDate LIMIT 100;
--SVOLTP.PKGSVOLTPLOCK.SLEEP(1);
FORALL i IN 1 .. vEventRec.COUNT
UPDATE svoltp.TrigEventQueue teq
SET teq.EventProcessingSessionId = vSessionId,
teq.TrigEventStateId = gcEventStateLocked, --6 : Locked State
teq.LastUser = 1003,
teq.LastUpdate = SYSDATE
WHERE teq.TrigEventQueueId = vEventRec (i)
AND teq.PartitionDate = vEventPartitionDate (i);
EXIT WHEN vEventRec.COUNT < 100;
END LOOP;
我还将FORALL
循环更改为使用1..count
而不是first..last
,因此如果最后一个实际批次正好是100 行,而下一个批次的行数为零,则不会有问题 - 它'将在FORALL
中什么都不做,然后退出。
this Oracle Magazine article 的后半部分介绍了这种情况(“改掉 %NOTFOUND 习惯”)。
【讨论】:
以上是关于批量收集问题的主要内容,如果未能解决你的问题,请参考以下文章