An expandable subfile buffers more rows than it displays, but far fewer than the whole result set. SFLSIZ is set moderately larger than SFLPAG — a few hundred rows loaded, maybe 20 shown at a time — and when the user scrolls to the end of what has been loaded, the program loads and appends the next batch starting at the next relative record number (RRN). No single page waits on a fresh database read for most page turns, and the subfile never has to hold the entire result set — but it is not unbounded, either in the application’s own batch-size sense or in IBM i’s enforced sense: no subfile, expandable or otherwise, can exceed 9,999 records.
This page covers how to pick an initial SFLSIZ/batch size against that hard ceiling, how the program gets control back when the user pages past the last loaded row, how to read the subfile cursor’s position with the correct keyword, how to extend the subfile by writing the next batch at the correct RRN through the actual SFILE-declared field, and when to stop extending for either of two independent reasons. Every claim about keyword behavior below is tied to IBM’s IBM i 7.4/7.6 documentation (see Sources Consulted); where the documentation doesn’t explicitly confirm a detail, the article says so.
This is a how-to page for RPG developers who have a result set too large to load-all safely, but who find strict page-at-a-time paging too chatty against the database for a smooth “keep scrolling” experience. It assumes you know what a load-all subfile is and what strict page-at-a-time paging is, because expandable subfiles sit between them. If either is new to you, read the RPGLE Load-All Subfile Programming article and the IBM i RPG Subfile Programming: SFLCTL, SFLPAG, SFLDSP Paging article first, then come back here for the hybrid.
Prerequisites
- Basic DDS subfile structure: a named subfile record format with
SFL, a named subfile control format withSFLCTL,SFLPAG,SFLSIZ, andSFLDSP/SFLDSPCTL. Every example on this page keeps DDS names and RPG references identical and verifiable by inspection. - The concept of a relative record number (RRN), and the fact that the RPG
SFILEkeyword on the display-file declaration names the single RPG variable used for that subfile’s RRN on bothWRITEandREADC— there is no second, informal counter that substitutes for it. - RPGLE free-format basics:
dcl-f,dcl-s,dow/for, and the file opcodesREAD/SETLL/WRITE. If any of these opcodes are unfamiliar, see the RPGLE CHAIN Opcode Reference and the file I/O opcode reference it links to.
The problem: two patterns that each cost something
A load-all subfile reads the entire result set into the subfile once, up front, then lets the display file handle scrolling locally with no further database activity. It works well when the result set is a few hundred rows or fewer. Past that, the initial load gets slow, the subfile consumes a correspondingly large chunk of the job’s memory, and most of what was loaded may never be viewed if the user only looks at the first screen or two.
Strict page-at-a-time paging sets SFLSIZ equal to SFLPAG and reads one page’s worth of records from the database for every page the user requests, whether moving forward or backward. This keeps memory flat and handles result sets of any size, but it means a database round trip on every single PAGEDOWN, which can be noticeably slower for a user who scrolls quickly through a lot of records, and it adds load on the database for every page turn.
The expandable subfile pattern trades a bounded amount of extra memory and one slightly more complex load routine for far fewer database round trips than strict paging, while staying far short of the memory and startup cost of load-all. The subfile holds a batch — enough rows to cover several screens of browsing without going back to the file — and only reads again when the user has actually scrolled to the end of that batch. But “bounded” here needs a precise meaning, covered next.
Choosing an initial SFLSIZ and batch size — against a hard ceiling
IBM’s DDS reference for the SFLSIZ keyword states plainly: “The maximum number of records allowed is 9999.” The same page explains that when SFLSIZ differs from SFLPAG and the program writes a record beyond the current size, “the subfile is automatically extended to contain it (up to a maximum of 9999 records).” This is an IBM i platform limit enforced by the display file support — it has nothing to do with how large a batch your application chooses to read at once. Your application’s batch size (say, 100 or 300 rows) is a design choice you tune; 9,999 is not. Set your batch-append loop’s stopping condition so that the running total of appended rows can never exceed that ceiling — attempting to write past it is a hard failure condition the application must prevent by checking before each append, not by catching an error after the fact.
There’s no single correct ratio of SFLSIZ to SFLPAG below that ceiling; it’s a judgment call based on how your users actually browse, and you should treat your first choice as a starting point to tune, not a fixed answer.
Some practical guidance:
- If the batch is only a little larger than
SFLPAG— saySFLPAG20 and a batch of 40 — you’ve effectively rebuilt page-at-a-time paging with extra steps, because the user hits the end of the batch after two page-downs. - If the batch is very large relative to typical usage — say several thousand rows when most sessions only ever view the first 100 — you’ve rebuilt load-all with extra code and you’re also creeping toward the 9,999 ceiling for no payoff.
- A reasonable middle starting point for many browse screens is a batch on the order of 5-20x
SFLPAG— for exampleSFLPAG20 and batches of 100-400 rows, with a clear, explicit stop once total loaded rows would pass a ceiling comfortably under 9,999 (not right up against it). - Weigh the row size too. A batch of 300 rows of a narrow transaction-history layout is cheap; the same count with a wide, multi-field record format is a larger memory commitment.
Be accurate about what this pattern actually bounds: it bounds initial load time and keeps most page turns off the database. If your batch-append logic keeps appending every time the user scrolls further, the subfile’s memory footprint is capped only by your application’s ceiling — which itself cannot exceed IBM’s 9,999-record limit — and for a wide record format that can be a non-trivial amount of job memory. Don’t describe this pattern as memory-bounded without saying what the bound actually is.
Getting control back at the end of the loaded rows: PAGEDOWN
When SFLSIZ differs from SFLPAG, paging through rows that are already in the subfile is handled by the system, not by your program — the EXFMT does not return while the user pages up and down inside the loaded rows. That is the payoff of the pattern, but it also means a “near the end” check that runs only after EXFMT returns will not fire just because the user scrolled.
IBM’s SFLEND documentation describes what happens at the last loaded page: if the SFLEND indicator is off, the system cannot page further on its own, so the program has to supply more records — and you specify PAGEDOWN on the subfile control record so that “control is passed to your program when the Page Down key is pressed again.” The program can then add records to the subfile and use SFLRCDNBR to choose which page is displayed next. That is exactly the expandable pattern: the DDS below specifies PAGEDOWN(25), and the RPG treats response indicator 25 as “the user paged past the last loaded row — load the next batch, then show the page holding the first new row.”
SFLRCDNBR matters here because, per IBM’s reference, if it is not specified the system displays the first page of the subfile by default — so without it, the user would be bounced back to the top after every batch load. IBM also documents that if the SFLRCDNBR field holds “a value less than 1 or a value greater than the number of records in the subfile” on an output operation, “an error is returned to your program,” so the program must keep that field within 1..rows loaded.
Detecting “near end of loaded rows” — with the correct keyword
As a supplement to PAGEDOWN, some screens also prefetch the next batch when the user presses Enter or a function key while the cursor sits near the bottom of the loaded rows. Reading the cursor’s subfile position for that check uses SFLCSRRRN, not SFLRCDNBR(CURSOR) (which positions the cursor on output; it does not report where the cursor is). IBM’s DDS reference describes SFLCSRRRN as returning “the relative record number of the record on which the cursor is located within a subfile,” and states that the field will “contain the value 0 if the cursor is not located in the subfile,” with the same applying when the cursor is within the subfile boundary but “is not in an active record within the subfile.”
Practically: declare a hidden numeric field on the control record format, and reference it from the SFLCSRRRN keyword with a literal & before the field name (SFLCSRRRN(&CSRRRN)). IBM’s own worked example on the SFLCSRRRN/SFLMODE reference page is explicit about this syntax:
A SFLCSRRRN(&RELRCD)
A SFLMODE(&MODE)
A RELRCD 5S 0H
A MODE 1A H
The & belongs on the keyword’s parameter reference only; the field’s own declaration line (RELRCD 5S 0H) carries no &. Note also that SFLCSRRRN is a record-level keyword, so — as in IBM’s example — it is coded before the control record’s field definitions, not after the field it references (a keyword line placed directly after a field line attaches to that field instead).
After every EXFMT of the control record, that field holds either the RRN the cursor sits on, or 0 if the cursor isn’t on an active row. Any near-end check must treat 0 as “unknown position,” not as “row zero” or “top of file.” Check for 0 explicitly first and skip the near-end comparison that cycle if it comes back 0. Confirm in your own testing that your display file reports a current, correct value at the point you check it — IBM’s documentation confirms the keyword’s defined behavior, but exact timing relative to your own keyword combination is worth confirming against your own DDS.
Extending the subfile: appending the next batch through the SFILE RRN field
The RPG SFILE keyword on the display-file declaration names exactly one RRN variable per subfile — in the form SFILE(record-format-name : RRN-field-name) — and IBM’s documentation on using subfiles states that this same field “is also used to specify the record number that the RPG program uses for WRITE operation to the subfile,” and that the WRITE operation “require[s] that a relative-record-number field be specified in the second position of the SFILE keyword.” In plain terms: there is exactly one RRN field that RPG and the subfile support share, named once in SFILE. A program that tracks a separate nextRrn counter, increments it, and issues WRITE without copying nextRrn into the SFILE-declared field writes at whatever value that field last held — not the row the program’s logic believes it is writing.
The fix is one line, placed immediately before every WRITE: assign the SFILE RRN variable from your batch counter, then write.
**free
// IBM DOC CHECKED — SFILE/RRN assignment pattern checked against
// IBM i 7.4 "Using subfiles" documentation (see Sources Consulted).
// Record-format names (SFL1 / SFLCTL1) match the DDS example below exactly.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-f custDspf workstn sfile(SFL1 : rrn1);
dcl-f custMstr keyed usage(*input);
dcl-s rrn1 packed(4 : 0); // the ONE field named in SFILE(SFL1:rrn1)
dcl-s rowsLoaded packed(4 : 0) inz(0);
dcl-s nextRrn packed(4 : 0) inz(0);
dcl-s firstNew packed(4 : 0) inz(0);
dcl-s batchSize packed(4 : 0) inz(100);
dcl-s sflMax packed(4 : 0) inz(9900); // margin below IBM's 9999 ceiling
dcl-s moreRows ind inz(*on);
dcl-s loadedThisBatch packed(4 : 0) inz(0); // initialized before first use
// Indicators (no INDDS in this file, so plain *INnn indicators are used):
// 31 = SFLCLR, 32 = SFLEND(*MORE), 33 = SFLDSP, 34 = SFLDSPCTL,
// 25 = PAGEDOWN response (user paged past the last loaded row).
// SFLEND polarity per IBM: indicator ON suppresses +More / shows Bottom;
// OFF shows +More. moreRows tracks "more rows genuinely remain," so
// *in32 must be the logical NEGATION of moreRows.
// --- Initialize the subfile once before writing any rows ---
// 31 selects SFLCLR ONLY on this clear write. 33 keeps SFLDSP off
// until records exist; 34 keeps control display off during the clear.
*in31 = *on;
*in33 = *off;
*in34 = *off;
write SFLCTL1;
*in31 = *off;
// --- Load the first batch ---
setll *loval custMstr;
exsr loadNextBatch;
rcdnbr = 1; // SFLRCDNBR field: show page 1 first
// --- Main subfile display loop ---
dow *on;
*in33 = (rowsLoaded > 0); // SFLDSP: never display an unactivated subfile
*in34 = *on; // SFLDSPCTL: control remains available if empty
*in31 = *off; // no SFLCLR during an EXFMT
*in32 = not moreRows; // SFLEND: ON = bottom reached, OFF = more remain
exfmt SFLCTL1;
if *in25;
// PAGEDOWN at the last loaded page: the system has handed control
// back because it cannot page further on its own.
if moreRows;
firstNew = rowsLoaded + 1;
exsr loadNextBatch;
if rowsLoaded >= firstNew;
rcdnbr = firstNew; // show the page holding the first new row
endif;
endif;
elseif csrRrn > 0 and moreRows and ((rowsLoaded - csrRrn) < (2 * 20));
// Optional prefetch on Enter/function key when the cursor is near the
// bottom. 0 from SFLCSRRRN means "unknown position" and is skipped.
exsr loadNextBatch;
rcdnbr = csrRrn; // stay on the page holding the cursor's row
endif;
// ... handle function keys, exit conditions, etc.
enddo;
begsr loadNextBatch;
loadedThisBatch = 0;
// Stop before the IBM-enforced 9999 ceiling, independent of EOF.
dow (loadedThisBatch < batchSize) and moreRows and (nextRrn < sflMax);
read custMstr; // file cursor continues after the last loaded key
if %eof(custMstr);
// True end-of-file on the result set — distinct from hitting the
// sflMax ceiling, handled separately below.
moreRows = *off;
leave;
endif;
nextRrn += 1;
rrn1 = nextRrn; // assign SFILE's RRN field before EVERY WRITE
write SFL1; // writes at rrn1 = nextRrn
loadedThisBatch += 1;
enddo;
rowsLoaded = nextRrn;
if nextRrn >= sflMax;
// Reached the capacity ceiling with rows still possibly remaining in
// custMstr. This is NOT the same condition as end-of-file.
moreRows = *off;
endif;
endsr;
Status: NOT TESTED. This is a conceptual illustration, not a compiled-and-run program. The RRN-assignment pattern, the SFLCSRRRN zero-case, the PAGEDOWN/SFLRCDNBR hand-off at the last page, the SFLSIZ 9,999 ceiling, and the SFLEND indicator polarity are each tied to the IBM documentation cited in Sources Consulted; the surrounding loop structure, variable names, and file names are illustrative and have not been compiled against a live IBM i system.
A few notes on this sketch: the read custMstr inside loadNextBatch relies on the file cursor still being positioned where the previous batch’s reads left it — no setll is reissued inside the batch routine, so the next batch picks up immediately after the last row already loaded, which avoids duplicate rows. The near-end threshold of 2 * 20 (two pages’ worth) in the optional prefetch is a starting value to tune, not a rule. loadedThisBatch is explicitly initialized at declaration and again at the top of loadNextBatch, so it is never read in a loop condition before it has a value. The SFLRCDNBR field (rcdnbr) is always set to a value between 1 and rowsLoaded before it is displayed; the zero-row case (where rcdnbr is 1 but no records exist and SFLDSP is off) is one of the paths you should exercise in runtime testing.
When to stop extending — two independent reasons
Stop issuing further batch loads for either of two separate reasons, and track them separately rather than collapsing them into one flag:
- True end-of-file on the underlying result set. A read of the next batch comes back short —
%eoffires beforebatchSizerows were read — meaning the database cursor has nothing left. SetmoreRows = *off. This is a property of the data, independent of how large the subfile has grown. - The application ceiling.
nextRrnreaches the capacity you’ve decided the subfile may grow to (sflMaxabove, kept with margin below IBM’s absolute 9,999), regardless of whether more rows remain in the result set. This is a property of the subfile’s capacity, independent of whether the data is exhausted.
Confusing these two is itself a mistake: a program that only checks for EOF and never checks the ceiling will attempt a WRITE past 9,999 records once a very large result set is browsed far enough. A program that only checks the ceiling and never checks EOF will keep trying to read a file that’s already exhausted. Once either condition is true, the display file’s own end-of-subfile behavior (governed by SFLEND) should reflect that no further batches are coming — but the program, not the display file, has to know which condition fired, because reaching the ceiling with data still remaining is a different situation (and arguably a sizing problem to revisit) from genuinely running out of rows. The sketch above folds both into moreRows for brevity; a production program should keep a separate flag if it needs to tell the user which one occurred.
DDS example
A DSPSIZ(24 80 *DS3)
A R SFL1 SFL
A CUSTNO 7A O 3 4
A CUSTNAME 30A O 3 12
A BALANCE 11S 2O 3 45
A R SFLCTL1 SFLCTL(SFL1)
A SFLSIZ(0100)
A SFLPAG(0020)
A 33 SFLDSP
A 34 SFLDSPCTL
A 32 SFLEND(*MORE)
A OVERLAY
A 31 SFLCLR
A PAGEDOWN(25)
A SFLCSRRRN(&CSRRRN)
A CSRRRN 5S 0H
A RCDNBR 4S 0H SFLRCDNBR
Status: IBM DOC CHECKED for the keywords themselves (SFLCTL, SFLSIZ, SFLPAG, SFLDSP, SFLDSPCTL, SFLEND, PAGEDOWN, SFLCSRRRN, SFLRCDNBR, and the 9,999-record ceiling are all checked against the IBM i DDS reference pages cited below). NOT COMPILED ON IBM i: the DDS uses documented keywords, but it and the dependent RPGLE program still require real compilation and interactive verification. The RPGLE excerpt deliberately omits application-specific field population, function-key exit handling, and full paging-backward behavior.
Notes on this fragment:
- The subfile record format is named explicitly (
A R SFL1 SFL), the control formatSFLCTL1references it viaSFLCTL(SFL1), and every RPG reference —sfile(SFL1 : rrn1),write SFL1,exfmt SFLCTL1— uses those exact names. SFLSIZ(0100)matches the batch size, so the subfile’s initial allocation covers one batch; becauseSFLSIZdiffers fromSFLPAG, later batches extend it automatically, up to IBM’s 9,999 maximum. The program’s ownsflMax(9,900) is the ceiling it enforces before that limit is reached.SFLEND(*MORE)is conditioned on indicator 32, because IBM’sSFLENDdocumentation is explicit: “An option indicator must be specified for this keyword.” IBM documents the polarity as: “Set the indicators off to display the plus sign or the More text” and “Set the indicators on to remove the plus sign … or to display the Bottom text.” So the RPG sets*in32 = not moreRows.SFLCLR(31),SFLDSP(33), andSFLDSPCTL(34) each have their own indicator. The initialWRITE SFLCTL1runs with 31 on and 33/34 off, clearing records without trying to display an inactive subfile; 31 then stays off, so later batches append rather than clear. 33 enablesSFLDSPonly whenrowsLoaded > 0, while 34 enablesSFLDSPCTLeven for the empty case. This example does not useSFLDLT; deleting the subfile is unnecessary for incremental loading.PAGEDOWN(25)returns control to the program when the user pages past the last loaded row;RCDNBRwithSFLRCDNBRthen selects the page to display after a batch is appended.SFLCSRRRN(&CSRRRN)is coded with the record-level keywords, before the field definitions, matching IBM’s worked example.
Expected output/behavior
The user sees the first page of results appear quickly, since only the initial batch (not the whole result set) was loaded. Scrolling through the first several pages feels instant, because those pages are served by the system from rows already in the subfile, with no database activity and no return to the program. When the user pages down past the last loaded row, the PAGEDOWN key returns control to the program, which reads and appends the next batch — a brief pause, ideally imperceptible if the batch size is tuned well — and redisplays at the page holding the first new row. This repeats until one of two things happens: the result set is genuinely exhausted (EOF), or the subfile reaches its chosen capacity ceiling short of IBM’s 9,999-record maximum — and the program must be able to tell the user, and itself, which of those occurred. Throughout, +More/Bottom on screen is driven by indicator 32, which is OFF while moreRows is true and ON only once it goes false.
Common mistakes and troubleshooting
- Assigning a separate “next RRN” counter but never copying it into the field named in the RPG
SFILEkeyword beforeWRITE. The subfile support positions the write using the SFILE-declared field, not whatever variable your application logic happens to be incrementing — if they aren’t the same field, or the counter isn’t copied into the real field before eachWRITE, the write lands at a stale, previously-set RRN rather than where your logic intended. - Relying on a post-
EXFMTcursor check alone to trigger loading. While the user pages within already-loaded rows, the system handles paging andEXFMTdoes not return. WithoutPAGEDOWNon the control record, the user simply hits the end of the loaded rows; with it, the program gets control at exactly the point it needs to append more. - Forgetting
SFLRCDNBRafter appending a batch. Without it, the system displays the first page by default, bouncing the user back to the top after every load. And its field must hold a value from 1 to the number of loaded records on output, or IBM documents that an error is returned. - Reading
SFLRCDNBRwhereSFLCSRRRNis actually needed. These are different keywords:SFLRCDNBRchooses which page to display (and optionally places the cursor);SFLCSRRRNreports where the cursor is. - Treating a 0 returned by
SFLCSRRRNas “row zero” instead of “cursor not on an active subfile row.” IBM’s documentation states the field is explicitly set to 0 in that case; a near-end check that doesn’t special-case 0 will misbehave at exactly the moments the cursor leaves the subfile area. - Omitting the leading
&inSFLCSRRRN‘s keyword parameter, or coding the keyword after the field. IBM’s worked example writes the parameter as&fieldname, with the keyword placed among the record-level keywords; the field’s own declaration line never carries the&. - Letting the batch-append loop run with no explicit check against the 9,999 ceiling. This is an IBM-enforced hard limit, not a tunable. Checking only for end-of-file on the underlying result set is not sufficient for a result set that can plausibly exceed your chosen ceiling.
- Off-by-one RRN errors when appending. Writing the next batch starting at
rowsLoadedinstead ofrowsLoaded + 1overwrites or duplicates the last row of the previous batch. Reissuingsetllbefore the next batch’s reads, instead of letting the file cursor continue from where it left off, can either skip or duplicate rows depending on the positioning used. - Reading a counter variable in a loop condition before it has ever been assigned. Declare batch and progress counters with an explicit
inz(0), and re-initialize per-batch counters at the top of the subroutine that uses them, not just at the top of the program. - Record-format name mismatches between DDS and RPG. If the RPG program’s
WRITE/EXFMTtargets don’t literally match a record format actually named in the DDS source (viaA R <name>), the program will not compile — verify every name by searching for it in both files. - Setting a “more rows remain” flag’s indicator to the same polarity as the flag itself.
SFLEND‘s indicator means the opposite of “more rows remain” — ON means bottom reached, OFF means more remain. A flag namedmoreRowsshould be assigned to the indicator’s negation (*in32 = not moreRows), not copied straight across. - Describing this pattern as flatly “memory-bounded.” It is bounded by whatever ceiling your application sets, which itself cannot exceed IBM’s 9,999-record limit — but if your application’s own ceiling is set close to 9,999 “just in case,” the actual memory commitment for a wide record format at that size is substantial.
- Not clearing/resetting the subfile and counters when the user changes the filter or sort on the browse screen. An expandable subfile that’s been partially loaded under one filter has to be fully reset — the SFILE RRN field,
rowsLoaded,nextRrn,moreRows, theSFLRCDNBRfield, and the subfile contents itself — before loading under a new filter, or stale rows from the old filter persist alongside new ones.
Performance and security considerations
The core trade-off this pattern makes is memory versus database round trips: a larger batch size means fewer reads from the database but more memory held by the job for the subfile, for longer, whether or not the user ever scrolls that far — and that memory commitment is capped by your chosen ceiling, which is itself capped by IBM’s 9,999-record limit, not by anything that shrinks automatically. Tune batch size against your actual access patterns — instrumenting how far users typically scroll on a given browse screen is more useful than guessing.
On security, nothing about the expandable pattern changes basic defensive practice: the underlying read should still only ever return rows the signed-on user is authorized to see, enforced the same way it would be for load-all or page-at-a-time (through object-level or row-level authority on the database file, or application-level filtering in the SELECT/read logic). Because batches are read in one direction and appended, make sure a browse screen that lets the user change filters mid-session fully discards the previous batch’s rows rather than silently mixing authorized and since-revoked data if authority changes mid-session — an edge case worth considering for long-running interactive jobs.
Practical use case
A transaction-history browse screen is a natural fit: a customer or account might have many thousands of historical transactions, far too many to load-all safely, but the overwhelming majority of user sessions only look at the most recent few hundred before giving up or switching to a filter/search instead of scrolling further. An expandable subfile with a batch size in the low hundreds, ordered most-recent-first, and an explicit stopping check well clear of the 9,999-record ceiling, serves that common case with no per-page database wait for most page turns, while being honest with the user (via SFLEND and the application’s own messaging) on the rare session that scrolls deep enough to hit either true end-of-file or the capacity ceiling.
FAQ
Is there a “right” SFLSIZ-to-SFLPAG ratio? No fixed ratio is correct for every screen. It depends on how far your users typically scroll and how wide each row is. Start with batches of roughly 5-20x SFLPAG, well clear of the 9,999-record absolute ceiling, and adjust based on observed usage.
Does SFLSIZ have to equal the total number of rows I’ll ever load? No. When SFLSIZ differs from SFLPAG, IBM documents that writing a record beyond the current size automatically extends the subfile, up to 9,999 records. SFLSIZ sets the initial allocation; your program tracks how many rows are actually loaded, and enforces its own ceiling below 9,999.
How does my program know the user has scrolled to the end of the loaded rows? Specify PAGEDOWN on the subfile control record. Paging inside loaded rows is handled by the system; when the user pages past the last loaded row (with the SFLEND indicator off), control returns to the program with the PAGEDOWN response indicator on, and the program can append the next batch.
What keyword tells me where the cursor is positioned in the subfile? SFLCSRRRN, not SFLRCDNBR. It returns the RRN of the active subfile row under the cursor, or 0 if the cursor isn’t positioned on an active row. Its keyword parameter must be written as &fieldname (e.g. SFLCSRRRN(&CSRRRN)), per IBM’s own worked example — the field’s declaration line itself carries no &.
What happens if the program forgets to assign the batch counter to the SFILE RRN field before WRITE? The write uses whatever value is currently in the field named in the SFILE keyword, which may be stale from a previous operation — not the value your batch-tracking variable holds unless you explicitly copy it over first.
What happens if the user scrolls backward into rows from an earlier batch? Rows already loaded stay in the subfile until the program clears it, so scrolling backward within already-loaded rows is free — no new database read, same as load-all for that portion.
Can this pattern work with SFLEND? Yes; SFLEND governs how the display file signals “more records” to the user, and the program is responsible for telling it accurately once the true end of the result set (not just the end of the current batch) or the capacity ceiling is reached. Remember the indicator’s polarity is inverted relative to “more rows remain”: ON means bottom/no more, OFF means more remain.
How is this different from the load-all subfile pattern? Load-all reads the entire result set once, up front, and never reads again. The expandable pattern reads a bounded batch at a time, appending more as needed, up to a capacity ceiling that is itself capped by IBM’s 9,999-record limit.
How is this different from strict page-at-a-time paging? Page-at-a-time sets SFLSIZ equal to SFLPAG and reads from the database on every single page turn. The expandable pattern reads a batch covering many pages at once, so most page turns are served by the system with no database activity, within the same 9,999-record ceiling that applies to every subfile.
Related reading
- RPGLE Load-All Subfile Programming — the simpler alternative when the whole result set is small enough to hold in memory at once.
- IBM i RPG Subfile Programming: SFLCTL, SFLPAG, SFLDSP Paging — the strict page-at-a-time alternative this page builds on and contrasts with.
- RPGLE CHAIN Opcode Reference — the keyed-read opcode underlying positioned reads used when resuming or re-positioning a batch load.
Sources Consulted
- IBM Documentation, SFLCSRRRN (Subfile Cursor RRN) keyword for display files, IBM i 7.6: https://www.ibm.com/docs/en/i/7.6.0?topic=s-sflcsrrrn —
SFLCSRRRN(notSFLRCDNBR) returns the RRN of the record the cursor is positioned on within a subfile, and the field is set to 0 when the cursor is not on an active subfile record. The page’s worked DDS example (SFLCSRRRN(&RELRCD)/SFLMODE(&MODE), with the referenced fields declaredRELRCD 5S 0H/MODE 1A H, neither carrying a&) shows the literal&on the keyword parameter and the record-level keyword placement before the field definitions. - IBM Documentation, Using subfiles (ILE RPG, “Using Externally Described WORKSTN Files”), IBM i 7.4: https://www.ibm.com/docs/en/i/7.4.0?topic=files-using-subfiles — the
SFILEkeyword names a single RRN field in the formSFILE(record-format-name : RRN-field-name); “This field is also used to specify the record number that the RPG program uses for WRITE operation to the subfile”; and “The WRITE operation code … require[s] that a relative-record-number field be specified in the second position of the SFILE keyword.” - IBM Documentation, Rules for the LIKEFILE keyword (ILE RPG Reference), IBM i 7.4: https://www.ibm.com/docs/en/i/7.4.0?topic=filename-rules-likefile-keyword — states that SFILE “must be specified for the new file to indicate which variable is to be used” for the subfile’s relative record number, and refers to it as “the variable used to specify the RRN,” corroborating that the program sets this variable to choose the record written.
- IBM Documentation, SFLSIZ (Subfile Size) keyword for display files, IBM i 7.4: https://www.ibm.com/docs/en/i/7.4.0?topic=80-sflsiz-subfile-size-keyword-display-files — “The maximum number of records allowed is 9999,” and when
SFLSIZdiffers fromSFLPAGa record written beyond the current size causes “the subfile is automatically extended to contain it (up to a maximum of 9999 records).” - IBM Documentation, SFLEND (Subfile End) keyword for display files, IBM i 7.4: https://www.ibm.com/docs/en/i/7.4.0?topic=80-sflend-subfile-end-keyword-display-files — “An option indicator must be specified for this keyword”; “Set the indicators off to display the plus sign or the More text” / “Set the indicators on to remove the plus sign … or to display the Bottom text”; and, for system paging, specifying
PAGEDOWNso that “control is passed to your program when the Page Down key is pressed again,” after which the program can add records and useSFLRCDNBRto display a new page. - IBM Documentation, SFLRCDNBR (Subfile Record Number) keyword for display files, IBM i 7.4: https://www.ibm.com/docs/en/i/7.4.0?topic=80-sflrcdnbr-subfile-record-number-keyword-display-files — the field must be zoned decimal with zero decimal positions; the displayed page is the one containing the record whose RRN is in the field; without the keyword the first page is displayed; a value less than 1 or greater than the number of records on output returns an error.
Verification Status
- DDS example: IBM DOC CHECKED for keyword names and placement, the 9,999 ceiling and automatic extension, the
SFLENDoption-indicator requirement,PAGEDOWN/SFLRCDNBRbehavior at the last page, and the required&onSFLCSRRRN‘s parameter; NOT TESTED as a compilable member. - RPGLE example: IBM DOC CHECKED for the SFLCSRRRN zero-case behavior, the SFILE/RRN-field relationship, the
PAGEDOWNhand-off, and theSFLENDindicator’s ON/OFF polarity; NOT TESTED — not compiled or run on a live IBM i system or PUB400. Treat both as conceptual illustrations to compile and test yourself, not as verified, production-ready code. - The precise rule “the program must assign the SFILE RRN field before every WRITE” is this article’s own inference from IBM’s documented description of the field (it “is also used to specify the record number that the RPG program uses for WRITE operation to the subfile”) and is consistent with IBM’s own sample subfile programs, which increment the RRN immediately before each subfile
WRITE— but it is UNVERIFIED_SOURCE as a single standalone normative IBM sentence. The underlying mechanism (one shared field, used for WRITE positioning) is directly documented. - Offline validation only: every check above is offline text analysis against IBM documentation. None of the DDS or RPGLE in this article has been compiled on PUB400 or any live IBM i system.