批量收集问题

Issue with Bulk Collect

我有一个程序需要使用批量收集从游标中获取数据。这种方法的问题是——有时记录得到处理,有时却没有。我无法确定根本原因。当我尝试调试问题时,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 - 这就是导致 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 ('Kicking the %NOTFOUND Habit') 的后半部分介绍。