Most DB2 queries can qualify millions of rows. Online screens need a page. This page covers how Db2 for z/OS limits a result (FETCH FIRST, OFFSET, FETCH NEXT) and how it hints the optimizer (OPTIMIZE FOR). They look similar in English and do different jobs—mixing them up is a classic access-path incident.
| Clause | Effect |
|---|---|
| FETCH FIRST n ROWS ONLY | Hard cap of n rows in the result |
| FETCH FIRST n ROWS WITH TIES | Cap plus extra rows tied on the ORDER BY key |
| OFFSET k ROWS | Skip k rows, then return (Db2 12+) |
| FETCH NEXT n ROWS ONLY | After OFFSET, return n rows (pagination spelling) |
| OPTIMIZE FOR n ROWS | Access-path hint only; no row cap |
IBM’s programming guide: when you need only a few of the thousands of rows that satisfy a query, use the optimize clause or the fetch clause of SELECT. FETCH FIRST changes the result. OPTIMIZE FOR changes (or tries to change) the access path.
FETCH FIRST n ROWS ONLY limits the result table to n rows. Benefits IBM documents:
12345SELECT EMPNO, LASTNAME, SALARY FROM DSN8C10.EMP WHERE WORKDEPT = 'A00' ORDER BY SALARY DESC FETCH FIRST 10 ROWS ONLY;
Always pair FETCH FIRST with ORDER BY when “top 10” has a meaning. Without ORDER BY, you get ten rows the access path happened to produce first—not the ten highest salaries.
FETCH FIRST also appears on SELECT INTO to prevent -811 when more than one row qualifies. That is a correctness use, not a paging use: add ORDER BY if you care which single row you keep.
FETCH FIRST n ROWS WITH TIES (Db2 12 pagination) keeps extra rows that match the ORDER BY key of the last included row. Fifth place in a salary contest should not drop the other employee who has the same salary. ONLY never returns more than n rows; WITH TIES might.
Db2 12 added SQL pagination. OFFSET k ROWS skips k rows of the result table before returning rows. Combined with a fetch clause you get page 2, page 3, and so on:
1234567-- Page 2 of 10 (skip 10, return 10) SELECT EMPNO, LASTNAME, SALARY FROM DSN8C10.EMP WHERE SALARY > 50000 ORDER BY EMPNO OFFSET 10 ROWS FETCH FIRST 10 ROWS ONLY;
FETCH NEXT n ROWS ONLY is the standard spelling of “return n rows” after an OFFSET. On z/OS you will also see FETCH FIRST used for the same cap. Read your SQL Reference for the exact diagram on your function level; the idea is skip-then-take.
OFFSET may be a literal or a variable castable to BIGINT, which makes a host variable page number easy. Restrictions IBM and community write-ups emphasize: OFFSET belongs on the outermost fullselect of a prepared statement or DECLARE CURSOR (and SELECT INTO), not in a view definition, MQT, SQL table function RETURN, row permission, or column mask, and not with a sensitive dynamic cursor. Statements with OFFSET must not use non-deterministic or external-action expressions in ways the manual forbids.
OFFSET 10000 still makes Db2 find and throw away those 10000 rows. Page 500 of a busy index is much slower than page 1. Prefer keyset (data-dependent) pagination:
1234567SELECT EMPNO, LASTNAME, SALARY FROM DSN8C10.EMP WHERE SALARY > 50000 AND (EMPNO) > :LAST-EMPNO ORDER BY EMPNO FETCH FIRST 10 ROWS ONLY OPTIMIZE FOR 10 ROWS;
The WHERE starts at the next key after the last row the user saw. An index on EMPNO can position directly. Composite keys use row comparison: (LASTNAME, EMPNO) > (:LN, :EN). This is the pattern IDUG and IBM paging talks recommend for high page numbers.
OPTIMIZE FOR n ROWS does not limit the result. It tells Db2 you probably will not fetch more than n rows, so the optimizer may pick a path that returns the first row quickly (often avoiding a sort, using a matching index).
12345SELECT EMPNO, LASTNAME FROM DSN8C10.EMP WHERE LASTNAME >= 'JONES' ORDER BY LASTNAME, EMPNO OPTIMIZE FOR 1 ROW;
JDBC/SQLJ setMaxRows discards extra rows in the driver; it does not replace FETCH FIRST for server-side limiting. IBM notes that server optimization to limit rows happens when FETCH FIRST is in the SQL, not merely because the client set max rows.
For a local COBOL cursor, FETCH FIRST means the result table really has n rows; further FETCH gets +100. For SELECT INTO, FETCH FIRST 1 ROW ONLY is how you force a single row. For DDF, FETCH FIRST plus implicit close reduces CHATTY chat. Do not use OPTIMIZE FOR 1 ROW on a batch job that will drain the whole cursor—that job wants the all-rows plan.
ORDER BY plus FETCH FIRST can use an in-memory “keep the best n” technique instead of sorting the entire answer set (Db2 has done this for years for small n). That is another reason FETCH FIRST is more than a client-side loop that discards rows.
FETCH FIRST 10 is the teacher saying “only ten kids get to come in.” OPTIMIZE FOR 10 is the teacher whispering to the bus driver “we think only ten kids are riding, so a small bus is fine”—if 200 kids show up, the small bus was a bad idea. OFFSET 20 is “skip the first twenty kids in line,” which still means counting twenty kids. A smarter line is “start after kid number 000340” (keyset), so you do not recount everyone from the door.
Db2 for z/OS uses FETCH FIRST, not MySQL-style LIMIT. Some other Db2 families accept LIMIT as an alias. Write FETCH FIRST on z/OS.
Typically no—OFFSET is for the outermost fullselect of a statement, not a view body. Put paging in the application query that reads the view.
The result is ordered, then limited (conceptually). That is why ORDER BY matters: you limit the ordered result, not a random subset that is sorted afterward in the client.
1. What does FETCH FIRST 10 ROWS ONLY do?
2. What does OPTIMIZE FOR 1 ROW do?
3. What does OFFSET 20 ROWS do?
4. If you specify FETCH FIRST 10 and OPTIMIZE FOR 1, what happens?
5. Why is keyset pagination often better than OFFSET 10000?