SQL example

How to limit rows and paginate in SQL

7 min read Updated Oct 2026 Runs in an isolated runtime
Quick answer

Sort, then cut: ORDER BY score DESC LIMIT 10 returns the top 10 rows. For page k of size rows, add an offset: LIMIT size OFFSET (k - 1) * size. SQL Server spells it SELECT TOP 10 … or OFFSET m ROWS FETCH NEXT n ROWS ONLY.

LIMIT caps how many rows a query returns and OFFSET skips rows before it starts counting. Both act on the result after sorting, so they only mean "the top 10" or "page 2" when the query has an ORDER BY: without one, the database may hand back any rows in any order. The sections below cover the top N rows, numbered pages, the nth row, a sort order that stays stable across pages, keyset pagination for large tables, and a top N that keeps ties. The examples run on SQLite; the table in section 7 gives the PostgreSQL, MySQL and SQL Server forms. Hit Run on any of them, then change the numbers and run it again.

1ORDER BY … LIMIT n: the top N rowsRecommended

Put LIMIT n at the very end of the query, after ORDER BY. The database sorts the whole result first and then keeps the first n rows, so ORDER BY score DESC gives the highest scores and plain ORDER BY score the lowest. The same clause works in SQLite, PostgreSQL and MySQL.

top-n.sql

Output

The first query returns eli (97), ana (92) and ben (85); the second returns cara (64) and fay (70). Leave out the ORDER BY and LIMIT 3 still returns three rows, but which three is up to the database: SQL tables have no built-in order, and the rows you get can change after an update, a new index or a different query plan. If you need the result in a set order, say so with ORDER BY.

2LIMIT n OFFSET m: page k of the results

OFFSET m skips the first m rows of the sorted result, and LIMIT then takes the next n. For page k with size rows per page, the offset is (k - 1) * size: page 1 skips nothing, page 2 skips one page, and so on. The last page holds whatever is left. To show a page count, divide the row count by the page size, rounding up.

pages.sql

Output

Seven players at three per page make pages of eli, ana, ben, gus, dev, fay and just cara, and the count query prints 7 rows and 3 pages. (count + size - 1) / size rounds up because both sides are integers, so the division drops the remainder. In application code, pass the limit and offset as query parameters (LIMIT ? OFFSET ?) rather than pasting numbers into the SQL string. SQLite and MySQL also accept the short form LIMIT 3, 3, with the offset first; the table in section 7 has the details.

3Select the nth row: LIMIT 1 OFFSET n - 1

The nth row of a sorted result is a page of one row: skip n - 1 rows and keep 1. This is also the usual answer to "find the second-highest salary". Wrapped in a scalar subquery, the query returns the value itself, and NULL when there is no such row; the bare query returns no row at all.

nth-row.sql

Output

The third-highest score belongs to ben (85) and the second-highest value is 92. The query with OFFSET 10 prints nothing, because six rows run out before the offset does, while the subquery version prints NULL. Watch out for ties: if two players shared the top score, OFFSET 1 would return that same score again. For the second-highest distinct value, sort SELECT DISTINCT score instead, or rank with DENSE_RANK() as in section 6.

4Keep pages stable with a tie-breaker column

When several rows share the sort value, ORDER BY score doesn't say which of them comes first, and the database is free to pick a different order each time. With paging that is a real bug: page 1 and page 2 are separate queries, so a tied row can appear on both pages while another never appears. Add a unique column, usually the primary key, as the last sort key so that every row has exactly one position.

tie-breaker.sql

Output

The three players on 85 straddle the page break: ben (id 2) ends page 1, and cara (3) and gus (7) start page 2. Because id settles every tie, rerunning either query always gives the same split. Without it, a database could return gus on page 1 one time and ben the next. The tie-breaker can be any column, or set of columns, that is unique; it only decides the order inside a group of equal scores.

5Keyset pagination: seek past the last row instead of OFFSET

OFFSET gets slower the deeper you page: for OFFSET 100000 the database still finds, sorts and throws away 100,000 rows before it returns the page. Keyset pagination (also called the seek method) remembers the sort key of the last row the user saw and asks for the rows after it with a WHERE clause. With an index on the sort columns, the database jumps straight to that point, so page 5,000 costs about the same as page 1. A row value comparison, (score, id) < (85, 7), says "after that row" in one line; SQLite has supported row values since 3.15.

keyset.sql

Output

Page 1 ends with gus (score 85, id 7), so page 2 asks for rows before (85, 7) in that order and gets cara, ben and dev; the spelled-out OR form returns the same three rows. Both sort keys run in the same direction here (score DESC, id DESC) because a row value compares its columns left to right in one direction. The comparison must match the sort: < for descending, > for ascending. Keyset paging also copes better with new rows: an insert above the current page shifts every later OFFSET page by one row, but not a page that starts from a key. The cost is that you can only step to the next or previous page, not jump to page 37, so many apps use it for infinite scroll and "load more" buttons.

6Top N with ties: RANK() <= n

LIMIT 3 always stops at three rows, even when the fourth row has the same score as the third. To keep everyone who ties, number the rows with a window function and filter on the number. RANK() gives tied rows the same rank and leaves gaps after them; DENSE_RANK() leaves no gaps, so <= 3 means "the three highest distinct scores". To do this for each group, such as the top 3 per team, add PARTITION BY; see select the first row in each group.

top-with-ties.sql

Output

RANK() numbers the two 92s as 1 and the three 85s as 3 (rank 2 is skipped), so "top 3 with ties" returns five players. DENSE_RANK() numbers the 85s as 2, which lets dev (78) in at 3: six players across three distinct scores. The filter has to sit in an outer query because a WHERE clause can't refer to a window function in the same SELECT. SQLite has had window functions since 3.25. PostgreSQL and SQL Server also have a shortcut for the RANK() case, shown in the next section.

7Other databases: TOP, OFFSET … FETCH and LIMIT m, n

LIMIT is not in the SQL standard, although SQLite, PostgreSQL and MySQL all accept it. The standard spelling is OFFSET m ROWS FETCH NEXT n ROWS ONLY, which PostgreSQL and SQL Server (2012 and later) support; older SQL Server code uses SELECT TOP n. Every form below was run on PostgreSQL 18, MySQL 8.4 and SQL Server's T-SQL (through Babelfish) except where the note says otherwise. Oracle has no LIMIT: Oracle 12c and later accept FETCH FIRST n ROWS ONLY and OFFSET m ROWS FETCH NEXT n ROWS ONLY, while older versions sort in a subquery and filter on ROWNUM in the outer query.

SyntaxDatabaseNotes
ORDER BY … LIMIT n OFFSET mSQLite, PostgreSQL, MySQLThe form on this page. SQLite and MySQL need a LIMIT before OFFSET; PostgreSQL also accepts OFFSET alone.
LIMIT m, nMySQL, SQLiteOffset first, then count: LIMIT 3, 3 skips 3 rows and returns 3. Easy to misread; prefer LIMIT n OFFSET m.
OFFSET m ROWS FETCH NEXT n ROWS ONLYSQL Server 2012+, PostgreSQLStandard SQL paging. SQL Server requires an ORDER BY; use OFFSET 0 ROWS for the first page.
FETCH FIRST n ROWS ONLYPostgreSQLStandard spelling of LIMIT n. SQLite and MySQL reject it; SQL Server needs OFFSET 0 ROWS in front.
SELECT TOP n …SQL ServerTop N with no offset. Add ORDER BY to say which N; TOP (@n) takes a variable.
FETCH FIRST n ROWS WITH TIESPostgreSQL 13+Top N plus every row tied with the last one, like RANK() <= n. Needs ORDER BY.
SELECT TOP n WITH TIES …SQL ServerThe SQL Server form of the same thing. Not run here: Babelfish doesn't support WITH TIES.
WHERE (a, b) < (x, y)PostgreSQL, MySQL, SQLite 3.15+Row values for keyset paging. SQL Server has none: write a < x OR (a = x AND b < y).

Frequently asked questions

How do I select the top 10 rows in SQL?

Sort and limit: SELECT * FROM t ORDER BY score DESC LIMIT 10 in SQLite, PostgreSQL and MySQL. In SQL Server, write SELECT TOP 10 * FROM t ORDER BY score DESC. The standard FETCH FIRST 10 ROWS ONLY works in PostgreSQL, but SQLite rejects it with near "FETCH": syntax error.

How do I calculate the OFFSET for a page number?

Use OFFSET (page - 1) * page_size: with 3 rows per page, page 1 is LIMIT 3 OFFSET 0, page 2 is LIMIT 3 OFFSET 3 and page 3 is LIMIT 3 OFFSET 6. The number of pages is (count(*) + page_size - 1) / page_size in integer arithmetic, which gives 3 pages for 7 rows.

Can I use OFFSET without LIMIT?

Not in SQLite or MySQL: ORDER BY v OFFSET 3 fails in SQLite with near "OFFSET": syntax error, and MySQL rejects it with a syntax error too. In SQLite, write LIMIT -1 OFFSET 3, where a negative limit means no limit. PostgreSQL accepts OFFSET 3 on its own.

Why does LIMIT without ORDER BY return different rows?

A table has no guaranteed order, so without ORDER BY the database returns whichever rows it reaches first, and that can change after inserts, deletes, a new index or a different plan. Always pair LIMIT with ORDER BY, and end the sort with a unique column such as id so tied rows keep a fixed position across pages.

Why is a large OFFSET slow, and what is the alternative?

The database has to produce and discard every skipped row, so OFFSET 100000 reads 100,000 rows to return a handful. Keyset pagination remembers the last row of the current page and filters past it, for example WHERE (score, id) < (85, 7) ORDER BY score DESC, id DESC LIMIT 3; with an index on (score, id) it starts right at that row. On SQL Server, which has no row values, write score < 85 OR (score = 85 AND id < 7).

How do I limit the number of rows in Oracle?

Oracle 12c and later support ORDER BY score DESC FETCH FIRST 10 ROWS ONLY and OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY. On older versions, sort inside a subquery and filter in the outer query: SELECT * FROM (SELECT * FROM t ORDER BY score DESC) WHERE ROWNUM <= 10. Filtering on ROWNUM in the same query as the ORDER BY picks the rows before sorting them.

Run it yourself

Open any of these in the SQLite playground to change the page size, add rows and run them again.

SQLite playground