Dumitru Gutu
Dumitru Gutu

Reputation: 579

Oracle Last Records from Cursor

I have the following piece of code. For the last line of the cursor I don't need to print the '->' symbol. If you run the query you can see that the fourth record is displayed twice

declare
cursor ch is
  select 1 as n from dual union
  select 2 from dual union 
  select 3 from dual union
  select 4 from dual;
v_ch ch%rowtype;
begin

  open ch;
  loop
    fetch ch into v_ch;
    exit when ch%notfound;
    dbms_output.put_line(LPAD(' ',5)||v_ch.n || '->');

  end loop;
  dbms_output.put_line(LPAD(' ',5)||v_ch.n);
  close ch;
end;

result

 1->
 2->
 3->
 4->
 4

Upvotes: 3

Views: 9309

Answers (2)

Lukas Eder
Lukas Eder

Reputation: 220762

A PL/SQL solution

This will work, just a bit of shifting when to print arrows / newlines:

set serveroutput on
declare
cursor ch is
  select 1 as n from dual union
  select 2 from dual union 
  select 3 from dual union
  select 4 from dual;
v_ch ch%rowtype;
first boolean := true;
begin

  open ch;
  loop
    fetch ch into v_ch;
    exit when ch%notfound;

    -- Append the arrow after all rows except the first and implicitly (because of the
    -- above exit) except the last, too
    if not first then
      dbms_output.put_line('->');
    end if;
    first := false;

    -- Use put here, instead of put_line, such that the arrow will be appended on the
    -- same line as the value on the next loop iteration
    dbms_output.put(LPAD(' ',5)||v_ch.n);
  end loop;

  -- Finally, print a newline character
  dbms_output.put_line('');
  close ch;
end;
/

A SQL solution

Of course, you can generate the arrow also in SQL:

set serveroutput on
declare
cursor ch is
  select n, case 
    when row_number() over (order by n) = 
         count(*) over () then '' else '->' end arrow
  from (
      select 1 as n from dual union
      select 2 from dual union 
      select 3 from dual union
      select 4 from dual
  ) t;
v_ch ch%rowtype;
begin
  open ch;
  loop
    fetch ch into v_ch;
    exit when ch%notfound;
    dbms_output.put_line(LPAD(' ',5)||v_ch.n||v_ch.arrow);
  end loop;
  dbms_output.put_line('');
  close ch;
end;
/

Or even:

select listagg(n, '->' || chr(10)) within group (order by n)
from (
  select 1 as n from dual union
  select 2 from dual union 
  select 3 from dual union
  select 4 from dual
);

This only works if your string won't reach the VARCHAR2 length limit

Upvotes: 4

Alex Poole
Alex Poole

Reputation: 191245

You could modify the cursor to identify the position of each row; without any ordering information that's a bit clunky:

declare
cursor ch is
  select n, count(*) over () - rownum as c from (
    select 1 as n from dual union
    select 2 from dual union 
    select 3 from dual union
    select 4 from dual
  );
v_ch ch%rowtype;
begin

  open ch;
  loop
    fetch ch into v_ch;
    exit when ch%notfound;
    dbms_output.put_line(LPAD(' ',5)||v_ch.n || case when v_ch.c > 0 then '->' end);
  end loop;
  close ch;
end;
/

     1->
     2->
     3->
     4


PL/SQL procedure successfully completed.

In this example the c column is zero for the final row returned; so the output uses a case expression to only show the arrow when that is greater than zero - i.e. all except that last row.

With a real query you might be able to just add a column to the current result set using row_number() over (order by <something> desc, which would make the last row #1, and you could base the display logic on that instead. You probably wouldn't need a subquery then either. We can't see your real query though so can only guess how it can best be applied.

Upvotes: 3

Related Questions