Проблема с массовым сбором

У меня есть процедура, где мне нужно получить данные из курсора с помощью массового сбора. Проблема с этим подходом заключается в том, что иногда записи обрабатываются, а иногда нет. Я не могу определить причину. Когда я пытаюсь отладить проблему, жаба не отвечает и время ожидания истекает. Вот код Пожалуйста помоги!

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;

1 ответ

Решение

Похоже, вы закомментировали exit как часть вашей попытки отладки - и это то, что заставляет Жабу перестать отвечать, поскольку это означает, что цикл никогда не завершается (как указал Тони Эндрюс).

С 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 и затем выход.

Этот сценарий описан во второй половине этой статьи в Oracle Magazine ("Kicking the %NOTFOUND Habit").

Другие вопросы по тегам