Pregunta

Así que tengo un ciclo for que procesa una lista de ID y tiene algunas cosas bastante complejas que hacer. Sin entrar en todos los detalles feos, básicamente esto:

    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;

La forma en que normalmente manejo esto es seleccionando un valor y luego envolviendo el siguiente código en una comprobación de instrucción IF para asegurarme de que la variable de verificación duplicada sea NULL. Pero es molesto. Solo quiero poder decir SIGUIENTE; o NOOP; o algo. Especialmente porque ya tengo que atrapar la excepción NO_DATA_FOUND. Supongo que podría escribir una carta a Oracle, pero tengo curiosidad de cómo otros manejan esto.

También podría incluir esto en una función, pero estaba buscando algo un poco más limpio / simple.

¿Fue útil?

Solución

También es posible contar el número de filas (ver Pourquoi Litytestdata) pero también puede hacer lo que quiera hacer en el bloque when_no_data_found excepcion .

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;

Otros consejos

Oracle 11g agrega un estilo C " continuar " construcción de bucle a PL / SQL, que suena sintácticamente como lo que estás buscando.

Para sus propósitos, ¿por qué no simplemente eliminar los duplicados antes de ingresar al ciclo? Esto se puede hacer consultando l_selected usando una función de tabla y luego filtrando los registros que no desea en lugar de iterar sobre cada valor. Algo así como ...

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;

Simplemente sustituya la lógica para determinar un "duplicado" con la suya (no tenía suficiente información de su ejemplo para realmente responder esa parte)

En lugar de atrapar NO_DATA_FOUND , ¿qué tal si SELECCIONA el número de entradas coincidentes en una variable, diga l_count y continúe si este recuento resulta ser cero? Algo así como lo siguiente:

    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;
<xmp>
<<next_loop>>
loop
...
...
if ....
then
   goto next_loop;

</xmp>

Este es un caso en el que una declaración GOTO podría ser útil. Consulte Documentación de Oracle en el estructuras de control para ver cómo hacer esto. Además, es posible que desee buscar por aquí para averiguar cómo consultar la existencia de un registro. Ejecutar una consulta y esperar una excepción no es óptimo.

Otra forma: convertir el cheque en una función local:

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;

Por supuesto, la función local podría reescribirse para usar el método de conteo si lo prefiere:

  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;

Otro método es generar y manejar una excepción definida por el usuario:

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;

Normalmente no haría esto, pero si hubiera media docena de razones para saltar al siguiente registro, esto podría ser preferible a varios IF anidados.

Sé que esto es antiguo, pero no pude evitar notar que ninguna de las respuestas anteriores tiene en cuenta el cursor atributos :

Hay cuatro atributos asociados con los cursores: ISOPEN, FOUND, NOTFOUND y ROWCOUNT. Se puede acceder a estos atributos con el delimitador% para obtener información sobre el estado del cursor.

La sintaxis para un atributo de cursor es:

cursor_name%attribute

donde cursor_name es el nombre del cursor explícito.

Entonces, en este caso, podría usar ROWCOUNT (que indica el número de filas recuperadas hasta el momento) para sus propósitos, así:

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top