• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Paging Cursors And Position To

    June 2, 2015 Paul Tuohy

    Note: The code accompanying this article is available for download here.

    I had a reader email in relation to my article Paging Cursors. The article described a method for paging large lists using embedded SQL in RPG, and the reader was wondering if there was a way to position the list at a certain value. Of course there is!

    Please refer to the original article for all of the gruesome details but the basic concept is that you have a subprocedure that returns a “page” of rows from a result set. A page can be any number of rows up to a maximum size. On each call, the caller can specify which page to retrieve by a page number or next/previous page. As well as returning the page of rows, the subprocedure also returns the page number returned, the total number of page, the total number of rows in the result set and the number of rows in the returned page.

    Changing the subprocedure to cater for Position To requires:

    • New parameters
    • A means of determining the page to retrieve based on the requested Position To value

    The good news is that all we are going to do is add some code to the original subprocedure that still works as before: declare a cursor, open the cursor, determine how many rows are in the result set, calculate which page to retrieve, and retrieve the page.

    To help demonstrate how Position To will work, I will use the result set shown below in the code in Figure 1. This is from the Employee table in the DB2 sample database that, as in the original article, lists all employees in a requested department (in this case, department D11). In the examples that follow, we will be requesting a page size of five rows, which means that three possible pages can be returned by the subprocedure (rows 1 to 5, 6 to 10, and row 11 on the last page).

    000060	IRVING STERN        32250.00
    000150	BRUCE ADAMSON       25280.00
    000160	ELIZABETH PIANKA    22250.00
    000170	MASATOSHI YOSHIMURA	24680.00
    000180	MARILYN SCOUTTEN	21340.00
    000190	JAMES WALKER        20450.00
    000200	DAVID BROWN         27740.00
    000210	WILLIAM JONES       18270.00
    000220	JENNIFER LUTZ       29840.00
    200170	KIYOSHI YAMAMOTO    24680.00
    200220	REBA JOHN           29840.00 
    

    Figure 1: A test result set.

    You can create the DB2 sample database on your system by calling the SQL stored procedure CREATE_SQL_SAMPLE and providing the name of the schema you want to create. For example:

    CALL QSYS/CREATE_SQL_SAMPLE('MYSAMPLEDB').
    

    Expected Results

    Assuming we call the subprocedure for department D11 with a current page of one and a page size of five, these are the results of requesting different Position To values. For each call, returned values for total pages will be three and total rows will be 11.

    No position to:

    DSPLY  Current page is 1
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 0
    DSPLY  First Employee on Page 000060
    

    Position to 000160:

    DSPLY  Current page is 1
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 3
    DSPLY  First Employee on Page 000060
    

    Position to 000180:

    DSPLY  Current page is 1
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 5
    DSPLY  First Employee on Page 000060
    

    Position to 000190:

    DSPLY  Current page is 2
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 1
    DSPLY  First Employee on Page 000190
    

    Position to 000210:

    DSPLY  Current page is 2
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 3
    DSPLY  First Employee on Page 000190
    

    Position to 200170:

    DSPLY  Current page is 2
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 5
    DSPLY  First Employee on Page 000190
    

    Position to 000001 (Does not exist. Before first row in result set):

    DSPLY  Current page is 1
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 1
    DSPLY  First Employee on Page 000060
    

    Position to 000205 (Does not exist but within result set):

    DSPLY  Current page is 2
    DSPLY  Rows this page 5
    DSPLY  Position To Rows at 3
    DSPLY  First Employee on Page 000190
    

    Position to 200200 (Does not exist but within result set):

    DSPLY  Current page is 3
    DSPLY  Rows this page 1
    DSPLY  Position To Rows at 1
    DSPLY  First Employee on Page 200220
    

    Position to 300000 (Does not exist. After last row in result set):

    DSPLY  Current page is 3
    DSPLY  Rows this page 1
    DSPLY  Position To Rows at 1
    DSPLY  First Employee on Page 200220
    

    New Parameters

    Two new parameters need to be added:

    1. A “Position To” parameter that will contain the required value to position the result set. In this example, Position To will be for an Employee Number.
    2. Which row in the returned page is the “Positioned At” row. Remember, this subprocedure returns a page of rows (rows 1 to 5, 6 to 10, and row 11 in the last page), so the Positioned At row will not necessarily be the first row in the returned page.

    Determining The Position At Row

    The introduction of Online Analytical Processing (OLAP) tools in V5R4 make it fairly straight forward to retrieve a row number in a result set. Full details of OLAP functions can be found in New in V5R4: OLAP Ranking Specifications.

    The code in Figure 2 below shows the select statement used in generating the result set shown in Figure 1:

           exec SQL
             declare C001 insensitive scroll cursor for
              select empno, firstNme || ' ' || lastname, salary
                 from employee
                 where workdept = :department
                 order by empno
                 for read only;
    

    Figure 2: The SQL Select statement used in the subprocedure.

    If we had a means of numbering each of the rows in the result set, we would have a row number that could be used to determine which page to retrieve. The row_Number() over() OLAP functions will give us what we need.

    The statement shown below in Figure 3 would return the result set shown in Figure 4. The keys (which we will be using for Position To) and corresponding row numbers for the original result set in Figure 1.

             select empno, 
                row_Number() over(order by empno) as rowIs
             from employee
             where workdept = :workdept;
    

    Figure 3: Using the row_Number() over() OLAP functions.

    000060   1	
    000150   2	
    000160   3	
    000170   4	
    000180   5	
    000190   6	
    000200   7	
    000210   8	
    000220   9	
    200170  10	
    200220  11
    

    Figure 4: Results from using the row_Number() over() OLAP functions.

    For this to work properly, we need to ensure that order by argument in the over() function and the where clause of the statement are exactly the same as the where and order by clauses in the original select statement in Figure 2.

    But how do we use this for Position To? Figure 5 shows the code placed into the original subprocedure just before it performs the Fetch of the page of rows. This code is only executed if a Position To has been requested. The row number of the Position To row is determined by placing the OLAP select statement (Figure 3) in a Common Table Expression (CTE) and, from the generated result set, selecting the lowest row number where the value of the key (empno) is greater than or equal to the requested Position To value. If the requested Position To value is greater than any of the keys in the result set, the coalesce(min(rowIs), 0) ensures that a value of zero is returned.

           if (positionTo <> *blanks);
              exec SQL
                 with t1 as
                    (select empno, 
                            row_Number() 
                            over(order by empno) as rowIs
                     from employee
                     where workdept = :department)
                 select coalesce(min(rowIs), 0)
                    into :positionRow
                    from t1
                    where empno >= :positionTo;
    
              re_calculate_Page_Data( positionRow
                                    : totalRows
                                    : pageSize
                                    : currentPageIs
                                    : rowInPage
                                    : rowOffset );
           endIf;
    

    Figure 5: Position To logic added to subprocedure.

    Once the row number of the requested Position To has been determined, we need to re-calculate the Page data.

    Re-Calculate Page Data

    Figure 6 shows the re_calculate_Page_Data() subprocedure that recalculates necessary fields: currentPage, the row no of the Position To row in the page (as opposed to the result set) and the relative row number to use on the Fetch.

         p re_calculate_Page_Data...
         p                 b                   export
         d                 pi
         d  positionRowIn                10i 0 const
         d  totalRows                    10i 0 const
         d  pageSize                     10i 0 const
         d  currentPageIs                10i 0
         d  rowInPage                    10i 0
         d  rowOffset                    10i 0
    
         d positionRow     s             10i 0
    
          /free
           positionRow = positionRowIn;
    
           // If positionRow is 0, then requested position is beyond last row
           if (positionRow = 0);
              positionRow = totalRows;
           endIf;
    
           // Calculate page positioned row is in and position of row in page
           currentPageIs = (positionRow / pageSize);
           rowInPage = %rem(positionRow: pageSize);
    
           if (rowInPage > 0);
              currentPageIs += 1;
           else;
              rowInPage = pageSize;
           endIf;
    
           // Re-Calculate offset to 1st row to retrieve
           rowOffset = ((currentPageIs - 1) * pageSize) + 1;
    
          /end-Free
         p                 e
    

    Figure 6: The re_calculate_Page_Data() subprocedure.

    Since this recalculation logic is the same in all instances of Position To logic, best to have it in its own subprocedure!

    Paging Logic

    In any environment, paging logic can be cumbersome. With this added method for Position To, using the code provided, the only real concern is with the OLAP functions. Just remember to ensure that order by argument in the over() function is the same as the order by clause in the original select statement and the where clause of the statement is the same as the where clause in the original select statement.

    Paul Tuohy is CEO of ComCon, an iSeries consulting company, and is one of the co-founders of System i Developer, which hosts the RPG & DB2 Summit conferences. He is an award-winning speaker who also speaks regularly at COMMON conferences, and is the author of “Re-engineering RPG Legacy Applications,” “The Programmers Guide to iSeries Navigator,” and the self-study course called “iSeries Navigator for Programmers.” Send your questions or comments for Paul to Ted Holt via the IT Jungle Contact page.

    RELATED STORIES

    Paging Cursors

    New in V5R4: OLAP Ranking Specifications

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    WorksRight Software

    Do you need area code information?
    Do you need ZIP Code information?
    Do you need ZIP+4 information?
    Do you need city name information?
    Do you need county information?
    Do you need a nearest dealer locator system?

    We can HELP! We have affordable AS/400 software and data to do all of the above. Whether you need a simple city name retrieval system or a sophisticated CASS postal coding system, we have it for you!

    The ZIP/CITY system is based on 5-digit ZIP Codes. You can retrieve city names, state names, county names, area codes, time zones, latitude, longitude, and more just by knowing the ZIP Code. We supply information on all the latest area code changes. A nearest dealer locator function is also included. ZIP/CITY includes software, data, monthly updates, and unlimited support. The cost is $495 per year.

    PER/ZIP4 is a sophisticated CASS certified postal coding system for assigning ZIP Codes, ZIP+4, carrier route, and delivery point codes. PER/ZIP4 also provides county names and FIPS codes. PER/ZIP4 can be used interactively, in batch, and with callable programs. PER/ZIP4 includes software, data, monthly updates, and unlimited support. The cost is $3,900 for the first year, and $1,950 for renewal.

    Just call us and we’ll arrange for 30 days FREE use of either ZIP/CITY or PER/ZIP4.

    WorksRight Software, Inc.
    Phone: 601-856-8337
    Fax: 601-856-9432
    Email: software@worksright.com
    Website: www.worksright.com

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    The Omni User:  Chicago's OMNI Technical Conference, June 4-5, Palos Hills, Illinois
    ASNA:  Create great IBM i-driven smartphone and tablet mobile apps with nothing but plain ol' RPG.
    LaserVault:  FREE ON-DEMAND WEBINAR: Understanding Tapeless Backups. Watch it now >

    SQL Query And Report Tool Gets The ProData Treatment HelpSystems Adds SkyView Partners To Its Security Assets

    Leave a Reply Cancel reply

Volume 15, Number 11 -- June 2, 2015
THIS ISSUE SPONSORED BY:

WorksRight Software
SEQUEL Software
United Computer Group, Inc.

Table of Contents

  • Paging Cursors And Position To
  • Beware The Temporary Table
  • EIM Identifier Naming

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • Meet The Next Gen Of IBMers Helping To Build IBM i
  • Looks Like IBM Is Building A Linux-Like PASE For IBM i After All
  • Will Independent IBM i Clouds Survive PowerVS?
  • Now, IBM Is Jacking Up Hardware Maintenance Prices
  • IBM i PTF Guide, Volume 27, Number 24
  • Big Blue Raises IBM i License Transfer Fees, Other Prices
  • Keep The IBM i Youth Movement Going With More Training, Better Tools
  • Remain Begins Migrating DevOps Tools To VS Code
  • IBM Readies LTO-10 Tape Drives And Libraries
  • IBM i PTF Guide, Volume 27, Number 23

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2025 IT Jungle