MySQL LIMIT and OFFSET Pagination
Also Known As
MySQL pagination
LIMIT OFFSET
cursor pagination
keyset pagination
TL;DR
LIMIT restricts result row count; OFFSET skips rows for pagination — but OFFSET-based pagination degrades on large tables.
Explanation
LIMIT n returns the first n rows. LIMIT n OFFSET m skips m rows first. The problem: OFFSET requires MySQL to scan and discard m rows before returning results — OFFSET 10000 scans 10,000 rows. Keyset pagination (cursor-based) avoids this: WHERE id > last_seen_id ORDER BY id LIMIT n. Keyset pagination is O(log n) via the index rather than O(n) for OFFSET.
Common Misconception
✗ OFFSET pagination is fine for large datasets. It gets linearly slower as the offset increases — page 1 and page 10,000 have vastly different response times.
Why It Matters
Page 500 of an OFFSET-paginated 1M-row table scans 5,000 rows to return 10. Keyset pagination returns the same page in a few milliseconds via an index lookup.
Common Mistakes
- Using OFFSET-based pagination on user-facing APIs without a page count limit.
- Not using ORDER BY with LIMIT — results are non-deterministic without explicit ordering.
- Calculating total pages with SELECT COUNT(*) on large tables — can be slow; use approximate counts.
Avoid When
- Do not use large OFFSET values on production tables — OFFSET 10000 scans 10,000 rows before returning results.
- Never use LIMIT without ORDER BY — results are non-deterministic.
When To Use
- Use LIMIT/OFFSET for shallow pagination (first few pages) where performance is acceptable.
- Use keyset/cursor pagination (WHERE id > :last_id) for deep pagination on large tables.
Code Examples
✗ Vulnerable
// OFFSET pagination — slow on large tables
$offset = ($page - 1) * $perPage; // page 1000 scans 10,000 rows
$stmt = $pdo->prepare('SELECT * FROM users ORDER BY id LIMIT ? OFFSET ?');
$stmt->execute([$perPage, $offset]);
✓ Fixed
// Keyset pagination — consistent performance at any page
$stmt = $pdo->prepare(
'SELECT id, email FROM users WHERE id > ? ORDER BY id LIMIT ?'
);
$stmt->execute([$lastSeenId, $pageSize]);
$rows = $stmt->fetchAll();
$nextCursor = end($rows)['id'] ?? null;
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
31 Mar 2026
Views
45
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 1
ChatGPT 1
ChatGPT 4
Google 1
ChatGPT 30
Google 5
Perplexity 3
Unknown AI 2
Ahrefs 1
Also referenced
How they use it
crawler 38
crawler_json 3
Related categories
⚡
DEV INTEL
Tools & Severity
⚙ Fix effort: Medium
⚡ Quick Fix
For deep pagination, switch from LIMIT/OFFSET to keyset pagination using WHERE id > :last_id ORDER BY id LIMIT :page_size
📦 Applies To
web
cli
🔗 Prerequisites
🔍 Detection Hints
LIMIT ? OFFSET ? with large offset values
Auto-detectable:
✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Medium
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Line
Tests: Update