SQL • QUERYING DATA

LIMIT/TOP — Use LIMIT/TOP to restrict result size (dialect-dependent) (conceptual)

Control how many rows your queries return across every major SQL dialect.

Historical Context & Motivation

When E. F. Codd published his foundational paper on the relational model in 1970, the theoretical framework made no provision for limiting result set cardinality—every query was expected to return all qualifying rows. Early SQL implementations faithfully followed this principle, and for the modest data volumes of the 1970s and early 1980s, returning complete result sets was rarely a performance concern. As databases grew from thousands to millions—and eventually billions—of rows, however, the absence of a built-in row-restriction mechanism became acutely painful. Developers found themselves scanning enormous tables merely to preview a few records, and application layers had to buffer gigantic result sets only to discard most of them. The need for a concise, declarative way to say "give me only the first n rows" drove every major database vendor to invent its own proprietary clause—long before the SQL standard caught up.

1986
SQL-86 Standard
The first ANSI SQL standard is ratified. It defines SELECT, WHERE, ORDER BY, and GROUP BY but includes no mechanism to restrict the number of returned rows.
1995
Microsoft SQL Server introduces TOP
SQL Server 6.0 adds the proprietary TOP keyword, placed immediately after SELECT, to cap the number of rows returned. This becomes the Transact-SQL idiom for decades.
1995–2003
MySQL and PostgreSQL adopt LIMIT
MySQL (from version 3.x) and PostgreSQL independently adopt the LIMIT clause appended at the end of a query. Although non-standard, it becomes the most widely recognized syntax.
2003
Oracle's ROWNUM pseudo-column era
Before Oracle 12c, users rely on the ROWNUM pseudo-column in a WHERE clause to restrict rows—an approach that is functional but semantically awkward and error-prone when combined with ORDER BY.
2008–2012
SQL:2008 standardizes FETCH FIRST
The ISO/IEC 9075:2008 revision introduces the OFFSET ... FETCH FIRST ... ROWS ONLY syntax, finally giving the relational standard a portable row-limiting clause. Oracle 12c (2013) and DB2 adopt it natively.

The central question this lesson addresses is deceptively simple: how do you tell a relational database to return only a subset of qualifying rows, and why does the answer depend on which database engine you are using? Understanding this dialect fragmentation is essential for any computer science student who will write SQL against more than one RDBMS in their career.

Core Principles & Definitions

Before diving into syntax, it is worth establishing the foundational ideas that underpin row-limiting clauses across all SQL dialects. These principles clarify not just how to use LIMIT and TOP, but why the feature exists and what guarantees it does—and does not—provide.

1

Result Set Cardinality Control

LIMIT/TOP constrains the maximum number of rows in the final result set. The database engine may still scan many more rows internally, but the client receives at most n rows.
2

Determinism Requires ORDER BY

Without an explicit ORDER BY, the set of rows returned by LIMIT/TOP is non-deterministic. The engine returns whichever rows it encounters first in its execution plan—results may change between runs.
3

Logical Processing Order

In the SQL logical processing pipeline (FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY), LIMIT/TOP is applied last, after sorting. This guarantees it caps the already-ordered result.
4

Dialect Fragmentation

Different RDBMS platforms use different keywords—LIMIT (MySQL, PostgreSQL, SQLite), TOP (SQL Server, MS Access), FETCH FIRST (Oracle 12c+, DB2, standard SQL)—but the semantic intent is identical.
5

Pagination via OFFSET

LIMIT/TOP is often combined with an OFFSET clause to skip a given number of rows before returning results, enabling page-by-page navigation through large data sets.
KEY TAKEAWAY
Think of LIMIT/TOP as a bouncer at the exit door of a concert venue. Everyone inside (the full result set) has already been filtered (WHERE) and arranged in line (ORDER BY). The bouncer simply counts heads and lets only the first n people through. Without the ORDER BY, the crowd is a mob with no defined line—who gets out first is unpredictable.

Visual Explanation — SQL Logical Processing Pipeline

The LIMIT / TOP / FETCH FIRST clause is logically the final step in SQL's processing pipeline. After rows are filtered (WHERE), grouped (GROUP BY), projected (SELECT), and sorted (ORDER BY), the row-limit clause caps the output.

The diagram above makes a critical point: because LIMIT/TOP is evaluated after ORDER BY in the logical processing order, the rows that survive the limit are determined by the sort. If you omit ORDER BY, the database engine returns an arbitrary subset—potentially different each time the query executes, depending on internal storage layout, parallelism, and caching. For reproducible results, always pair your row-limiting clause with an explicit ORDER BY on a column (or set of columns) that produces a total ordering of the result set.

Dialect-Specific Syntax Deep Dive

Although every major RDBMS supports the concept of restricting result set size, the syntactic position and keywords differ significantly. Understanding these differences is essential when porting queries or working in polyglot database environments.

MySQL / PostgreSQL / SQLite — LIMIT ... OFFSET

LIMIT SYNTAX
SELECT columns FROM table [WHERE ...] [ORDER BY ...] LIMIT n [OFFSET skip];
n = maximum rows returned. skip = number of rows to bypass before returning results. OFFSET defaults to 0 if omitted.

SQL Server / MS Access — TOP

TOP SYNTAX
SELECT TOP n [PERCENT] columns FROM table [WHERE ...] [ORDER BY ...];
n = maximum rows (or percentage if PERCENT is specified). TOP appears immediately after SELECT, before the column list. SQL Server 2012+ also supports OFFSET … FETCH NEXT for pagination.

Oracle 12c+ / DB2 / SQL:2008 Standard — FETCH FIRST

FETCH FIRST SYNTAX
SELECT columns FROM table [WHERE ...] [ORDER BY ...] OFFSET skip ROWS FETCH FIRST n ROWS ONLY;
This is the ISO SQL:2008 standard syntax. 'ROWS ONLY' may be replaced with 'ROWS WITH TIES' to include tied rows beyond the limit. OFFSET clause is optional.

Legacy Oracle (pre-12c) — ROWNUM

ROWNUM SYNTAX
SELECT * FROM (SELECT columns FROM table ORDER BY col) WHERE ROWNUM <= n;
ROWNUM is assigned before ORDER BY in a single-level query, so a subquery is required to sort first, then limit. This is a common pitfall in legacy Oracle code.
⚠️ PITFALL: ROWNUM + ORDER BY
Writing SELECT * FROM employees WHERE ROWNUM <= 5 ORDER BY salary DESC in legacy Oracle does not return the five highest-paid employees. ROWNUM is assigned before ORDER BY, so you get five arbitrary rows, then those five are sorted. Wrap the ordered query in a subquery and apply ROWNUM to the outer query.

Cross-Dialect Comparison & Pagination Patterns

Side-by-side comparison of row-limiting and pagination syntax across MySQL/PostgreSQL/SQLite, SQL Server, and Oracle/SQL:2008. Note that TOP appears after SELECT, while LIMIT and FETCH FIRST appear at the end of the query.
Dialect feature comparison for row-limiting clauses
FeatureMySQL / PostgreSQLSQL ServerOracle 12c+ / SQL:2008
KeywordLIMITTOPFETCH FIRST
Position in queryEnd (after ORDER BY)After SELECTEnd (after ORDER BY)
Percentage modeNot supportedTOP n PERCENTFETCH FIRST n PERCENT ROWS ONLY
WITH TIES supportNot natively (use window functions)TOP n WITH TIESFETCH FIRST n ROWS WITH TIES
PaginationLIMIT n OFFSET skipOFFSET skip ROWS FETCH NEXT n ROWS ONLYOFFSET skip ROWS FETCH FIRST n ROWS ONLY
ISO SQL Standard?No (de facto standard)No (Transact-SQL)Yes (SQL:2008)

Worked Example — Building a Paginated Leaderboard

Suppose you are building a web application that displays a game leaderboard. The players table has columns player_id, username, and score. You want page 3 of the leaderboard, where each page displays 10 players ranked by descending score. The backend uses PostgreSQL.

Paginated Leaderboard Query (PostgreSQL)
1
Step 1 — Determine the OFFSETPage 3 means we skip the first 2 pages. With 10 rows per page, OFFSET = (page_number − 1) × page_size = (3 − 1) × 10 = 20.
OFFSET = 20
2
Step 2 — Write the base query with ORDER BYWe need a deterministic sort. Since multiple players can share the same score, we add a tiebreaker on player_id to guarantee a total ordering:
SELECT player_id, username, score FROM players ORDER BY score DESC, player_id ASC
3
Step 3 — Append LIMIT and OFFSETWe append the row-limiting clause at the end of the query. LIMIT restricts the page to 10 rows; OFFSET skips the first 20.
SELECT player_id, username, score FROM players ORDER BY score DESC, player_id ASC LIMIT 10 OFFSET 20;
4
Step 4 — Translate to SQL Server (Transact-SQL)If the backend were SQL Server 2012+, the equivalent query uses the OFFSET … FETCH NEXT pattern. Note that SQL Server requires ORDER BY when OFFSET is used—this is enforced at parse time.
SELECT player_id, username, score FROM players ORDER BY score DESC, player_id ASC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
5
Step 5 — Verify performance considerationsFor large tables, OFFSET-based pagination can be costly because the engine must sort and skip through all preceding rows. With OFFSET = 20 this is negligible, but for deep pages (e.g., OFFSET = 1,000,000) a keyset pagination approach (using WHERE score < last_seen_score) is preferable.
Tip: For deep pagination, prefer keyset/cursor-based pagination over OFFSET.

Strengths, Limitations & Common Pitfalls

Strengths and limitations of row-limiting clauses
AspectStrengthsLimitations / Pitfalls
SimplicityA single clause caps output size—easy to write and read.Syntax differs across dialects, requiring translation when migrating.
PerformanceThe optimizer can often short-circuit scans once the limit is reached (e.g., top-N sort optimization).OFFSET-based pagination degrades at large offsets because the engine still processes skipped rows internally.
DeterminismFully deterministic when paired with ORDER BY on a unique key.Without ORDER BY, results are arbitrary and non-reproducible.
Tied valuesSQL Server's TOP … WITH TIES and standard FETCH … WITH TIES include all rows sharing the boundary value.MySQL's LIMIT has no WITH TIES option; achieving the same result requires window functions.
ComposabilityCan be nested inside subqueries or CTEs for complex logic.Some dialects restrict LIMIT/TOP inside certain subquery positions (e.g., views, inline queries in older SQL Server versions).
KEY TAKEAWAY
LIMIT/TOP is like a valve on a firehose: it does not reduce the water pressure in the pipe (the internal workload), it merely controls how much water reaches your bucket (the client). For truly efficient data retrieval at scale, combine LIMIT with appropriate indexes on the ORDER BY columns and consider keyset pagination to eliminate the internal cost of skipping rows.

Connection to Advanced Query Patterns

Row-limiting clauses are the entry point to a family of more sophisticated SQL patterns. As queries grow in complexity, the simple LIMIT/TOP mechanism often needs to be augmented or replaced with more expressive tools.

Progression from basic LIMIT/TOP to advanced patterns
LIMIT/TOP (Basic)Advanced Alternative
LIMIT 10 — returns first 10 rowsROW_NUMBER() OVER(ORDER BY col) — assigns explicit row numbers, enabling complex filtering
TOP 3 WITH TIES — includes all tied rowsRANK() / DENSE_RANK() OVER(...) — window functions offering granular tie-handling semantics
LIMIT n OFFSET skip — offset paginationKeyset (cursor) pagination using WHERE id > last_seen_id LIMIT n — O(log N) via index seek
Subquery with LIMIT per group (workaround)LATERAL JOIN or ROW_NUMBER() OVER(PARTITION BY group_col) — top-N per group elegantly

Mastering LIMIT/TOP establishes the conceptual foundation for window functions and pagination strategies that are central to building performant, production-grade data-access layers. In courses on database internals, you will see how the query optimizer uses the limit count to choose between a full sort and a top-N heap sort, which runs in O(N log k) time instead of O(N log N), where k is the limit value and N is the total number of qualifying rows.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain why a query using LIMIT (or TOP) without an ORDER BY clause can return different rows on consecutive executions, even if the underlying data has not changed.
PROBLEM 2BASIC CALCULATION
You have a table orders with columns order_id, customer_id, total_amount, and order_date. Write a MySQL query to return the 5 most recent orders by date.
PROBLEM 3INTERMEDIATE
Translate the following PostgreSQL query into equivalent SQL Server (Transact-SQL) and standard SQL:2008 syntax: SELECT product_name, price FROM products ORDER BY price ASC LIMIT 20 OFFSET 40;
PROBLEM 4APPLIED
You are building a REST API endpoint GET /api/articles?page=5&size=25 backed by PostgreSQL. The articles table has 2 million rows. Write the query and explain a potential performance problem with this approach at scale. Propose a more efficient alternative.
PROBLEM 5CRITICAL THINKING
Consider the query SELECT TOP 5 WITH TIES employee_name, salary FROM employees ORDER BY salary DESC; in SQL Server. If the top 5 salaries are 120k, 110k, 100k, 100k, 100k, and a sixth employee also earns 100k, how many rows does this query return? How would you achieve the same semantics in PostgreSQL, which does not support WITH TIES natively?

Lesson Summary

Row-limiting clauses allow you to control the cardinality of your query results. LIMIT (MySQL, PostgreSQL, SQLite) is appended at the end of a query, TOP (SQL Server) is placed immediately after SELECT, and FETCH FIRST … ROWS ONLY (Oracle 12c+, DB2, SQL:2008 standard) appears at the query's end. Despite syntactic differences, all three accomplish the same goal: restricting the number of rows delivered to the client. ORDER BY is essential for deterministic results, because LIMIT/TOP is logically the final step in query processing.

For pagination, combine LIMIT with OFFSET to skip rows before the returned page. Be aware that large OFFSET values degrade performance because the engine must still process and discard skipped rows; in such cases, keyset (cursor-based) pagination is the preferred alternative. Advanced use cases—such as top-N per group or handling ties—are addressed by window functions like ROW_NUMBER(), RANK(), and DENSE_RANK(), which generalize the concept of row limiting into a powerful analytical framework.

Varsity Tutors • SQL • LIMIT/TOP — Use LIMIT/TOP to restrict result size (dialect-dependent)