OraQA

Oracle Question and Answer

  • Do you have a solution to a problem? Do you have an unanswered question? Login and share it with the Oracle community. More...

Oracle News


Entries RSS feed

Comments RSS feed

How to paginate through an ordered result set

February 9th, 2006 By Eddie Awad

You want to paginate through an ordered result set, returning a few rows at a time. For example:

HR@XE> select first_name,
  2    hire_date
  3  from employees
  4  order by hire_date
  5  /

FIRST_NAME           HIRE_DATE
-------------------- ---------
Steven               17-JUN-87
Jennifer             17-SEP-87
Neena                21-SEP-89
Alexander            03-JAN-90
Bruce                21-MAY-91
Lex                  13-JAN-93
Susan                07-JUN-94
Hermann              07-JUN-94
Shelley              07-JUN-94
William              07-JUN-94
Daniel               16-AUG-94
Nancy                17-AUG-94
Den                  07-DEC-94
Payam                01-MAY-95

You want to return 3 rows at a time, while at the same time preserving the order of the result set.

One way (using rownum):

HR@XE> var lower number
HR@XE> var higher number
HR@XE> exec :lower := 1

PL/SQL procedure successfully completed.

HR@XE> exec :higher := 3

PL/SQL procedure successfully completed.

HR@XE> select first_name,
  2    hire_date
  3  from
  4    (select a.*,
  5       rownum r
  6     from
  7      (select first_name,
  8         hire_date
  9       from employees
 10       order by hire_date)
 11    a
 12     where rownum <= :higher)
 13  where r >= :lower
 14  /

FIRST_NAME           HIRE_DATE
-------------------- ---------
Steven               17-JUN-87
Jennifer             17-SEP-87
Neena                21-SEP-89

HR@XE> exec :lower := 4

PL/SQL procedure successfully completed.

HR@XE> exec :higher := 6

PL/SQL procedure successfully completed.

HR@XE> /

FIRST_NAME           HIRE_DATE
-------------------- ---------
Alexander            03-JAN-90
Bruce                21-MAY-91
Lex                  13-JAN-93

Another way (using row_number):

HR@XE> exec :lower := 1

PL/SQL procedure successfully completed.

HR@XE> exec :higher := 3

PL/SQL procedure successfully completed.

HR@XE> select first_name,
  2    hire_date
  3  from
  4    (select row_number() over(
  5     order by hire_date) as
  6    rn,
  7       first_name,
  8       hire_date
  9     from employees)
 10  x
 11  where rn between :lower
 12   and :higher
 13  /

FIRST_NAME           HIRE_DATE
-------------------- ---------
Steven               17-JUN-87
Jennifer             17-SEP-87
Neena                21-SEP-89

HR@XE> exec :lower := 4

PL/SQL procedure successfully completed.

HR@XE> exec :higher := 6

PL/SQL procedure successfully completed.

HR@XE> /

FIRST_NAME           HIRE_DATE
-------------------- ---------
Alexander            03-JAN-90
Bruce                21-MAY-91
Lex                  13-JAN-93

3 Responses to “How to paginate through an ordered result set”

  1. Karl Reitschuster Says:

    Hi Eddie,
    how about the execution plans – are they the same?
    Greetings

  2. Eddie Awad Says:

    Using rownum:

    --------------------------------------------------------------------------------------
    | Id  | Operation                | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT         |           |   107 |  3638 |     4  (25)| 00:00:01 |
    |*  1 |  VIEW                    |           |   107 |  3638 |     4  (25)| 00:00:01 |
    |*  2 |   COUNT STOPKEY          |           |       |       |            |          |
    |   3 |    VIEW                  |           |   107 |  2247 |     4  (25)| 00:00:01 |
    |*  4 |     SORT ORDER BY STOPKEY|           |   107 |  1605 |     4  (25)| 00:00:01 |
    |   5 |      TABLE ACCESS FULL   | EMPLOYEES |   107 |  1605 |     3   (0)| 00:00:01 |
    --------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("R">=TO_NUMBER(:LOWER))
       2 - filter(ROWNUM< =TO_NUMBER(:HIGHER))
       4 - filter(ROWNUM<=TO_NUMBER(:HIGHER))
    
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              7  consistent gets
              0  physical reads
              0  redo size
            556  bytes sent via SQL*Net to client
            380  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              1  sorts (memory)
              0  sorts (disk)
              3  rows processed
    

    Using row_number:

    ----------------------------------------------------------------------------------
    | Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
    ----------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT     |           |   107 |  3638 |     4  (25)| 00:00:01 |
    |*  1 |  VIEW                |           |   107 |  3638 |     4  (25)| 00:00:01 |
    |   2 |   WINDOW SORT        |           |   107 |  1605 |     4  (25)| 00:00:01 |
    |*  3 |    FILTER            |           |       |       |            |          |
    |   4 |     TABLE ACCESS FULL| EMPLOYEES |   107 |  1605 |     3   (0)| 00:00:01 |
    ----------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - filter("RN">=TO_NUMBER(:LOWER) AND "RN"< =TO_NUMBER(:HIGHER))
       3 - filter(TO_NUMBER(:LOWER)<=TO_NUMBER(:HIGHER))
    
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
              7  consistent gets
              0  physical reads
              0  redo size
            556  bytes sent via SQL*Net to client
            380  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              1  sorts (memory)
              0  sorts (disk)
              3  rows processed
    

    Looks like the optimizer had to go through less steps using row_number.

  3. Karl Reitschuster Says:

    Thank you!
    is see both variants uses the same resources.
    and strange – on the second plan only 75% of CPU cost is shown;
    where is the rest? But that’s not a ‘winning battle question’
    Regards
    Karl

Leave a Reply

You must be logged in to post a comment.

RSS feed for comments on this question