Domanda

Quindi ho un ciclo for che elabora un elenco di ID e ha alcune cose abbastanza complesse da fare. Senza entrare in tutti i dettagli brutti, sostanzialmente questo:

    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;

Il modo in cui lo gestisco normalmente è selezionando un valore e quindi racchiudendo il seguente codice in un'istruzione IF verificando che la variabile di controllo duplicata sia NULL. Ma è fastidioso. Voglio solo poter dire NEXT; o NOOP; o qualcosa. Soprattutto da quando devo già prendere l'eccezione NO_DATA_FOUND. Suppongo di poter scrivere una lettera a Oracle, ma sono curioso di sapere come gli altri lo gestiranno.

Potrei anche racchiuderlo in una funzione, ma stavo cercando qualcosa di un po 'più pulito / semplice.

È stato utile?

Soluzione

È anche possibile contare il numero di righe (vedi Pourquoi Litytestdata) ma puoi anche fare quello che vuoi fare nel blocco 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;

Altri suggerimenti

Oracle 11g aggiunge uno stile C "continua" costrutto loop su PL / SQL, che suona sintatticamente come quello che stai cercando.

Per i tuoi scopi, perché non eliminare semplicemente i duplicati prima di entrare nel ciclo? Questo potrebbe essere fatto interrogando l_selected usando una funzione table, e quindi filtrando i record che non vuoi invece di iterare su ogni valore. Qualcosa come ...

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;

Basta sostituire la logica per determinare un "duplicato" con il tuo (non hai avuto abbastanza informazioni dal tuo esempio per rispondere davvero a quella parte)

Invece di prendere NO_DATA_FOUND , che ne dici di SELEZIONARE il numero di voci corrispondenti in una variabile, dire l_count , e procedere se questo conteggio risulta essere zero? Qualcosa di simile al seguente:

    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>

Questo è un caso in cui un'istruzione GOTO potrebbe essere utile. Vedi la Documentazione Oracle nella strutture di controllo per vedere come farlo. Inoltre, potresti voler cercare qui intorno per scoprire come interrogare l'esistenza di un record. Eseguire una query e attendere un'eccezione non è ottimale.

Un altro modo: trasforma il segno di spunta in una funzione locale:

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;

Naturalmente, la funzione locale potrebbe essere riscritta per utilizzare il metodo count se si preferisce:

  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;

Un altro metodo è sollevare e gestire un'eccezione definita dall'utente:

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 non lo farei, ma se ci fossero mezza dozzina di motivi per passare al record successivo, questo potrebbe essere preferibile a più IF nidificati.

So che si tratta di un vecchio ma non ho potuto fare a meno di notare che nessuna delle risposte precedenti tiene conto del cursore attributi :

Esistono quattro attributi associati ai cursori: ISOPEN, FOUND, NOTFOUND e ROWCOUNT. È possibile accedere a questi attributi con il delimitatore% per ottenere informazioni sullo stato del cursore.

La sintassi per un attributo cursore è:

cursor_name%attribute

dove cursore_name è il nome del cursore esplicito.

Quindi in questo caso potresti usare ROWCOUNT (che indica il numero di righe recuperate finora) per i tuoi scopi, in questo modo:

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top