Существуют ли альтернативные способы сказать «следующий» в цикле pl / sql for? - PullRequest
3 голосов
/ 09 января 2009

Итак, у меня есть цикл for, который обрабатывает список идентификаторов и выполняет довольно сложные вещи. Не вдаваясь во все безобразные детали, в основном это:

    DECLARE
      l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

      ...snip...
    BEGIN

      -- get the list ids
      l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
      -- process each in a nice loop
      FOR i IN 1..l_selected.count 
      LOOP
        -- do some data checking stuff...

        -- here we will look for duplicate entries, so we can noop if duplicate is found
        BEGIN
          SELECT county_id INTO v_dup_check FROM org_county_accountable
          WHERE organization_id = :P4_ID AND county_id = v_county_id;
          -- NEXT;! NOOP;! but there is no next!
        EXCEPTION WHEN NO_DATA_FOUND THEN
          dbms_output.put_line('no dups found, proceeding');
        END;
        -- here we have code we only want to execute if there are no dupes already
        IF v_dup_check IS NULL THEN
          -- if not a duplicate record, proceed...

        ELSE
          -- reset duplicate check variable
          v_dup_check := NULL;
        END;
      END LOOP;
    END;

Как я обычно это делаю, выбирая значение, а затем оборачивая следующий код в проверку оператора IF, чтобы убедиться, что дублирующая переменная проверки имеет значение NULL. Но это раздражает. Я просто хочу иметь возможность сказать СЛЕДУЮЩИЙ; или NOOP; или что-то. Тем более, что я уже должен поймать исключение NO_DATA_FOUND. Я полагаю, что мог бы написать письмо в Oracle, но мне любопытно, как другие справляются с этим.

Я тоже мог бы обернуть это в функцию, но я искал что-то немного чище / проще.

Ответы [ 8 ]

4 голосов
/ 09 января 2009

Oracle 11g добавляет в PL / SQL конструкцию цикла продолжения в стиле C, которая синтаксически звучит как то, что вы ищете.

Для ваших целей, почему бы просто не удалить дубликаты до входа в цикл? Это можно сделать, запросив l_selected с помощью табличной функции, а затем отфильтровывая ненужные записи вместо итерации по каждому значению. Что-то вроде ...

declare

l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

cursor no_dups_cur (p_selected APEX_APPLICATION_GLOBAL.VC_ARR2) is 
  select * from (
  select selected.*, 
         count(*) over (partition by county_id) cnt -- analytic to find counts grouped by county_id
    from table(p_selected) selected -- use table function to treat VC_ARR2 like a table 
    ) where cnt = 1 -- remove records that have duplicate county_ids
    ;

begin

l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);

for i in no_dups_cur(l_selected) loop

  null; -- do whatever to non-duplicates 

end loop;

end;

Просто замените логику определения «дубликата» своей собственной (у вас недостаточно информации из вашего примера, чтобы действительно ответить на эту часть)

3 голосов
/ 09 января 2009

Вместо перехвата NO_DATA_FOUND, как насчет ВЫБОРА количества подходящих записей в переменной, скажем, l_count, и продолжения, если это число оказывается равным нулю? Примерно так:

    DECLARE
      l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
      l_count    INTEGER;

      ...snip...
    BEGIN

      -- get the list ids
      l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
      -- process each in a nice loop
      FOR i IN 1..l_selected.count 
      LOOP
        -- do some data checking stuff...

        -- here we will count duplicate entries, so we can noop if duplicate is found
        SELECT COUNT(*) INTO l_count FROM org_county_accountable
         WHERE organization_id = :P4_ID AND county_id = v_county_id;
        IF l_count = 0 THEN
          -- here we have code we only want to execute if there are no dupes already
          -- if not a duplicate record, proceed...

        END IF;
      END LOOP;
    END;
1 голос
/ 20 июля 2011
<xmp>
<<next_loop>>
loop
...
...
if ....
then
   goto next_loop;

</xmp>
1 голос
/ 09 января 2009

Подсчет количества строк также возможен (см. Pourquoi Litytestdata), но вы также можете делать то, что хотите, в блоке when_no_data_found exception.

declare 
  l_selected    apex_application_global.vc_arr2;
  l_county_id   org_county_accountable.count_id%type;
begin
  l_selected := apex_util.string_to_table(:p4_select_lst);
  for i in l_selected.first..l_selected.last loop
    begin
      select count_id
      into   l_county_id
      from   org_county_accountable
      where  organization_id = :p4_id
      and    county_id       = v_county_id;
    exception
      when no_data_found then 
        -- here we have code we only want to execute if there are no dupes already
        -- if not a duplicate record, proceed...
    end;
  end loop;
end;
0 голосов
/ 02 февраля 2015

Я знаю, что это старый, но я не мог не заметить, что ни один из ответов выше не учитывает курсор атрибуты :

С курсорами связаны четыре атрибута: ISOPEN, FOUND, NOTFOUND и ROWCOUNT. Эти атрибуты могут быть доступны с помощью разделителя%, чтобы получить информацию о состоянии курсора.

Синтаксис для атрибута курсора:

cursor_name%attribute

где cursor_name - это имя явного курсора.

Так что в этом случае вы можете использовать ROWCOUNT (который указывает количество выбранных строк) для ваших целей, например так:

declare 
   aux number(10) := 0;
   CURSOR cursor_name is select * from table where something;
begin
     select count(*) into aux from table where something;
     FOR row IN cursor_name LOOP
        IF(aux > cursor_name%ROWCOUNT) THEN 'do something is not over';
        ELSE 'do something else';
        END IF;
     END LOOP;
end;
0 голосов
/ 14 января 2009

Другой метод - вызвать и обработать пользовательское исключение:

DECLARE
  l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
  duplicate_org_county EXCEPTION;

  ...snip...
BEGIN

  -- get the list ids
  l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
  -- process each in a nice loop
  FOR i IN 1..l_selected.count 
  LOOP
    BEGIN
      -- do some data checking stuff...

      -- here we will look for duplicate entries, so we can noop if duplicate is found
      BEGIN
        SELECT county_id INTO v_dup_check FROM org_county_accountable
        WHERE organization_id = :P4_ID AND county_id = v_county_id;
        RAISE duplicate_org_county;
      EXCEPTION WHEN NO_DATA_FOUND THEN
        dbms_output.put_line('no dups found, proceeding');
      END;
      -- here we have code we only want to execute if there are no dupes already

    EXCEPTION
      WHEN duplicate_org_county THEN NULL;
    END;
  END LOOP;
END;

Обычно я бы этого не делал, но если бы было полдюжины причин для перехода к следующей записи, это может быть предпочтительнее, чем несколько вложенных IF.

0 голосов
/ 14 января 2009

Другой способ - превратить чек в локальную функцию:

DECLARE
  l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;

  ...snip...
  FUNCTION dup_exists 
     ( p_org_id org_county_accountable.organization_id%TYPE
     , p_county_id org_county_accountable.county_id%TYPE
     ) RETURN BOOLEAN 
  IS
    v_dup_check org_county_accountable.county_id%TYPE;
  BEGIN
    SELECT county_id INTO v_dup_check FROM org_county_accountable
    WHERE organization_id = p_org_id AND county_id = p_county_id;
    RETURN TRUE;
  EXCEPTION WHEN NO_DATA_FOUND THEN
    RETURN FALSE;
  END;
BEGIN

  -- get the list ids
  l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
  -- process each in a nice loop
  FOR i IN 1..l_selected.count
  LOOP
    -- do some data checking stuff...

    -- here we have code we only want to execute if there are no dupes already
    IF NOT dup_exists (:P4_ID, v_county_id) THEN
      -- if not a duplicate record, proceed...

    END;
  END LOOP;
END;

Конечно, локальная функция может быть переписана для использования метода count, если вы предпочитаете:

  FUNCTION dup_exists 
     ( p_org_id org_county_accountable.organization_id%TYPE
     , p_county_id org_county_accountable.county_id%TYPE
     ) RETURN BOOLEAN 
  IS
    l_count INTEGER;
  BEGIN
     SELECT COUNT(*) INTO l_count 
       FROM org_county_accountable
      WHERE organization_id = p_org_id AND county_id = p_county_id;
     RETURN (l_count > 0);
  END;
0 голосов
/ 09 января 2009

Это тот случай, когда оператор GOTO может быть полезным. См. Документация Oracle в управляющих структурах, чтобы узнать, как это сделать. Кроме того, вы можете поискать здесь, чтобы узнать, как запросить наличие записи. Выполнение запроса и ожидание исключения не является оптимальным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...