Are there alternative methods for saying 'next' in a pl/sql for loop?

11,971

Solution 1

To count the number of rows is also possible (see Pourquoi Litytestdata) but you can also do what you want to do in the when_no_data_found exception block.

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;

Solution 2

Oracle 11g adds a C-style "continue" loop construct to PL/SQL, which syntactically sounds like what you're looking for.

For your purposes, why not just eliminate the duplicates prior to entering the loop? This could be done by querying l_selected using a table function, and then filtering out records you don't want instead of iterating over every value. Something like...

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;

Just substitute the logic for determining a "duplicate" with your own (didn't have enough info from your example to really answer that part)

Solution 3

Instead of catching NO_DATA_FOUND, how about SELECTing the number of matching entries into a variable, say l_count, and proceeding if this count works out to be zero? Something like the following:

    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;

Solution 4

<xmp>
<<next_loop>>
loop
...
...
if ....
then
   goto next_loop;

</xmp>
Share:
11,971
Billy Gray
Author by

Billy Gray

Senior Developer for Zetetic (where I write on the blog), guitar player for Ben Franklin, where I write the music. I like to contribute to other projects on GitHub. I have a few records and songs to my name. I prefer stout to lager and whisky to vodka.

Updated on June 22, 2022

Comments

  • Billy Gray
    Billy Gray almost 2 years

    So I've got a for loop that processes a list of IDs and has some fairly complex things to do. Without going into all the ugly details, basically this:

        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;
    

    How I normally handle this is by selecting into a value, and then wrap the following code in an IF statement checking to make sure that duplicate check variable is NULL. But it's annoying. I just want to be able to say NEXT; or NOOP; or something. Especially since I already have to catch the NO_DATA_FOUND exception. I suppose I could write a letter to Oracle, but I'm curious how others handle this.

    I could also wrap this in a function, too, but I was looking for something a little cleaner/simpler.

  • Dave Costa
    Dave Costa over 15 years
    Was just about to write the same thing
  • Billy Gray
    Billy Gray over 15 years
    That's actually a really tight solution, the GOTO statement, thanks! I don't quite get the rest of your answer. I'm more than happy to query duplicate records, it's necessary. Although, I like the idea of selecting a count better, since there's no exception thrown.