SQL Practice Logo

SQLPractice Online

SQL Server: Windowing & Pagination: Mistakes

Module: Database-Specific Features

Using large OFFSET values for deep pagination

Use keyset pagination (WHERE clause) for deep pages

OFFSET pagination scans all skipped rows. For page 10000 with 10 items per page, SQL Server scans 100,000 rows and returns 10. This is O(n) complexity - performance degrades linearly with page depth.

Example - Deep pagination performance:

-- WRONG: OFFSET for page 10000 (scans 100K rows, 10 seconds)

SELECT question_id, title, created_at

FROM questions

ORDER BY created_at DESC

OFFSET 100000 ROWS -- Scans and skips 100,000 rows

FETCH NEXT 10 ROWS ONLY;

-- Execution plan: Index Scan (100,010 rows), 10 seconds

-- CORRECT: Keyset pagination (seeks to position, 10ms)

SELECT TOP 10

question_id,

title,

created_at

FROM questions

WHERE created_at < @LastCreatedAt -- Seek directly to position

OR (created_at = @LastCreatedAt AND question_id < @LastQuestionID)

ORDER BY created_at DESC, question_id DESC;

-- Execution plan: Index Seek (10 rows), 10ms

Keyset pagination has O(1) constant performance. Page 1: 10ms. Page 10000: 10ms. Trade-off: Cannot jump to arbitrary pages, only next/previous. Use OFFSET/FETCH for page numbers (1-100), keyset for infinite scroll (deep pages).

Use OFFSET/FETCH for shallow pagination (pages 1-100). Use keyset pagination for deep pagination or infinite scroll. Keyset provides 100-1000x better performance for deep pages.

High

OFFSET 100000 scans 100,000 rows and skips them, taking 10+ seconds

Missing tie-breaker column in ORDER BY

Always include unique column (id) as tie-breaker in ORDER BY

When ORDER BY column has duplicates, SQL Server can return rows in any order within the duplicate group. This causes inconsistent pagination - same row appears on multiple pages or skips pages entirely.

Example - Inconsistent pagination:

-- WRONG: No tie-breaker (inconsistent ordering)

SELECT question_id, title, created_at

FROM questions

ORDER BY created_at DESC -- Multiple questions have same created_at

OFFSET 10 ROWS

FETCH NEXT 10 ROWS ONLY;

-- Page 1: Questions A, B, C (all created_at = 2024-01-20 14:30:00)

-- Page 2: Questions B, C, D (B and C appear again!)

-- CORRECT: Include unique tie-breaker

SELECT question_id, title, created_at

FROM questions