Modules / Database / Pagination Lab

Limit and Offset in SQL

Return a slice of a result set instead of all of it. Page through real data, then discover the two traps: pagination without ORDER BY, and why large OFFSETs get slower and slower.

LIMIT and OFFSET: Serving Data One Page at a Time

Two small keywords behind every "Next page" button — and two traps that bite at scale.

Quick Context

LIMIT n returns at most n rows. OFFSET m throws away the first m rows before it starts counting. Together they cut a window out of a result set, which is how page 3 of a product listing gets built.

Syntax varies: PostgreSQL, MySQL and SQLite use LIMIT ... OFFSET ...; SQL Server uses OFFSET ... FETCH NEXT ... ROWS ONLY; older Oracle used ROWNUM. The idea is identical.

Trap 1: LIMIT Without ORDER BY Is Meaningless

SQL tables are unordered sets. Without an explicit ORDER BY, the database may return rows in any order it finds convenient — and that order can change between runs, after an update, or when the query plan changes.

So "the first 10 rows" is not a defined concept without ordering. Select "no ORDER BY" in this lab and press "Re-run the same query" a few times: the same query returns different rows. In production this shows up as items mysteriously appearing on two pages, or never appearing at all.

Always pair pagination with a deterministic ORDER BY — and if the sort column has ties, add a unique tiebreaker such as the primary key.

Trap 2: Deep OFFSET Gets Slower and Slower

The database cannot jump straight to row 100,000. It must generate and discard every row before the offset. Watch the "rows scanned" figure in this lab climb while "rows returned" stays fixed — the red portion of the bar is pure wasted work.

At page 5 nobody notices. At OFFSET 500000 the query is reading half a million rows to hand back ten. This is why deep pagination is a classic source of slow queries.

The fix is keyset pagination (also called seek pagination): instead of counting rows to skip, remember the last value you saw and filter on it.

-- offset pagination: scans 100,010 rows SELECT * FROM employees ORDER BY emp_id LIMIT 10 OFFSET 100000; -- keyset pagination: jumps straight there using the index SELECT * FROM employees WHERE emp_id > 100000 ORDER BY emp_id LIMIT 10;

Keyset stays fast at any depth, and it does not skip or duplicate rows when data changes underneath you. The trade-off is that you can only move forward and backward, not jump to an arbitrary page number.

Interactive Exploration Guide

  1. Set LIMIT to 4 and step the page buttons. The highlighted window slides down the full result set — OFFSET is just where the window starts.
  2. Push OFFSET high and watch the red "skipped" portion of the cost bar grow while the green returned portion stays the same size.
  3. Switch to "no ORDER BY" and re-run repeatedly. The returned rows change even though the query text does not.
  4. Set OFFSET beyond the table size. You get zero rows — not an error. A common cause of mysteriously blank final pages.

Key Takeaway

LIMIT and OFFSET slice a result set, but only after ORDER BY has made that set deterministic. Offset pagination is fine for the first few pages and quietly quadratic beyond them — reach for keyset pagination when the data gets big.

Cheat sheet

Limit and Offset in SQL

Return a slice of a result set instead of all of it. Page through real data, then discover the two traps: pagination without ORDER BY, and why large OFFSETs get slower and slower.

DATABASE · vizlearn.in/database/limit_and_offset_in_sql.html