Fundamentals

SQL ORDER BY and LIMIT: Sorting, Top-N and Pagination

7 min readUpdated September 10, 2026Every example verified
On this page 10 sections ▾
  1. Sorting one column
  2. Sorting by several columns
  3. Sorting by an expression or an alias
  4. Where NULLs end up
  5. LIMIT: the top few rows
  6. OFFSET and pagination
  7. The syntax is different on every database
  8. Practice this topic
  9. Frequently asked questions
  10. Related reading

Rows in a table have no order. That is not a detail — it is the whole reason ORDER BY exists. A query without it returns rows in whatever sequence was cheapest for the database to produce, which may look sorted today and come back shuffled tomorrow after an index changes.

This page covers how sorting actually works, then pairs it with LIMIT to answer the two questions that follow: give me the top few, and give me page four. Everything runs against the sample database.

Sorting one column

Ascending is the default, so these two are the same query:

SELECT first_name, salary
FROM Employees
ORDER BY salary;
first_namesalary
Omar61000
Daniel68000
Hiroshi74500
Sofia79000
Grace89500
Isabela92000
Leila96000
Priya105000
Marcus118000
Tomas131000
Ananya142000
Chen156000

12 rows · produced by running this query on the sample database

Add DESC for highest first, which is what you want far more often in reports:

SELECT first_name, salary
FROM Employees
ORDER BY salary DESC;
first_namesalary
Chen156000
Ananya142000
Tomas131000
Marcus118000
Priya105000
Leila96000
Isabela92000
Grace89500
Sofia79000
Hiroshi74500
Daniel68000
Omar61000

12 rows · produced by running this query on the sample database

ASC and DESC attach to each column separately, not to the whole clause — a point that matters as soon as you sort by more than one thing.

Sorting by several columns

The second column breaks ties in the first, the third breaks ties in the second, and so on:

SELECT first_name, dept_id, salary
FROM Employees
ORDER BY dept_id ASC, salary DESC;
first_namedept_idsalary
Ananya10142000
Marcus10118000
Leila1096000
Sofia1079000
Omar1061000
Tomas20131000
Priya20105000
Grace2089500
Hiroshi2074500
Daniel3068000
Chen50156000
Isabela5092000

12 rows · produced by running this query on the sample database

Departments in ascending order; within each department, the best paid first. Swap the two and you get a completely different report from the same data — the order of the columns in ORDER BY is the order of priority.

A tie with no tie-breaker is genuinely unpredictable

If you sort only by department and two people share one, their relative order is not defined and can change between runs. Whenever the sort must be stable — and it must be for pagination — end the ORDER BY with something unique, such as a primary key.

Sorting by an expression or an alias

You can sort by anything you can compute, whether or not it is in the SELECT list:

SELECT first_name, salary
FROM Employees
ORDER BY salary * 12 DESC;
first_namesalary
Chen156000
Ananya142000
Tomas131000
Marcus118000
Priya105000
Leila96000
Isabela92000
Grace89500
Sofia79000
Hiroshi74500
Daniel68000
Omar61000

12 rows · produced by running this query on the sample database

And unlike WHERE, ORDER BY can use a column alias, because it runs after the SELECT list has been evaluated:

SELECT first_name, salary * 12 AS annual_pay
FROM Employees
ORDER BY annual_pay DESC;
first_nameannual_pay
Chen1872000
Ananya1704000
Tomas1572000
Marcus1416000
Priya1260000
Leila1152000
Isabela1104000
Grace1074000
Sofia948000
Hiroshi894000
Daniel816000
Omar732000

12 rows · produced by running this query on the sample database

That asymmetry surprises people: WHERE annual_pay > 1000000 fails, while ORDER BY annual_pay works. Both follow from the clause order — WHERE is evaluated before SELECT, ORDER BY after it.

Sorting by column position also works, and is best avoided:

SELECT first_name, dept_id
FROM Employees
ORDER BY 2, 1;
first_namedept_id
Ananya10
Leila10
Marcus10
Omar10
Sofia10
Grace20
Hiroshi20
Priya20
Tomas20
Daniel30
Chen50
Isabela50

12 rows · produced by running this query on the sample database

ORDER BY 2, 1 means “by the second column, then the first”. It saves typing and breaks silently the moment somebody reorders the SELECT list, which is why most style guides ban it.

Where NULLs end up

NULL is not a value, so it has no natural place in a sort order — and the standard leaves the choice to the database:

SELECT first_name, manager_id
FROM Employees
ORDER BY manager_id;
first_namemanager_id
AnanyaNULL
TomasNULL
PriyaNULL
SofiaNULL
ChenNULL
Marcus1
Leila1
Grace4
Hiroshi4
Daniel7
Omar9
Isabela11

12 rows · produced by running this query on the sample database

Default NULL placement when sorting ascending
DatabaseNULLs appearOverride
PostgreSQLlastNULLS FIRST / NULLS LAST
OraclelastNULLS FIRST / NULLS LAST
MySQLfirstsort by an expression, or use IS NULL
SQL Serverfirstsort by an expression, or use IS NULL
SQLitefirstNULLS FIRST / NULLS LAST (3.30+)

If the placement matters, do not rely on the default. The portable trick is to sort by a flag first:

SELECT first_name, manager_id
FROM Employees
ORDER BY CASE WHEN manager_id IS NULL THEN 1 ELSE 0 END,
         manager_id;
first_namemanager_id
Marcus1
Leila1
Grace4
Hiroshi4
Daniel7
Omar9
Isabela11
AnanyaNULL
TomasNULL
PriyaNULL
SofiaNULL
ChenNULL

12 rows · produced by running this query on the sample database

That puts the rows with a value first and the NULLs after them on every database, because you have made the rule explicit instead of inheriting one.

LIMIT: the top few rows

Sort, then take the first n. This is the whole top-N pattern:

SELECT first_name, salary
FROM Employees
ORDER BY salary DESC
LIMIT 3;
first_namesalary
Chen156000
Ananya142000
Tomas131000

3 rows · produced by running this query on the sample database

LIMIT without ORDER BY is a coin toss

SELECT * FROM Employees LIMIT 3 returns three rows, but which three is not defined. It is a useful way to peek at a table’s shape and a bug in anything that reports a “top 3”.

OFFSET and pagination

OFFSET skips rows before LIMIT starts counting, which is how a page of results is fetched:

SELECT first_name, salary
FROM Employees
ORDER BY salary DESC, emp_id
LIMIT 4 OFFSET 4;
first_namesalary
Priya105000
Leila96000
Isabela92000
Grace89500

4 rows · produced by running this query on the sample database

Rows five to eight — page two, at four rows per page. The general form is LIMIT page_size OFFSET (page_number - 1) * page_size. Note the emp_id at the end of the ORDER BY: without a unique tie-breaker, a row can appear on two pages or on none, because the database is free to order tied rows differently on each query.

The same mechanism answers “the second highest salary” — skip one, take one — which is covered in full on the second highest salary page.

OFFSET gets slower the deeper you go

To return rows 100,001 to 100,020 the database generally has to produce and discard the first 100,000. For deep pagination, remember the last value you saw and filter on it instead — WHERE salary < :last_salary ORDER BY salary DESC LIMIT 20. This is called keyset pagination, and it stays fast at any depth.

The syntax is different on every database

Row limiting is the least portable part of everyday SQL:

Taking the top 3 rows
DatabaseSyntax
PostgreSQL, MySQL, SQLiteORDER BY salary DESC LIMIT 3
SQL ServerSELECT TOP 3 ... ORDER BY salary DESC
SQL Server / Oracle 12c+ (standard)ORDER BY salary DESC FETCH FIRST 3 ROWS ONLY
Oracle 11g and earlierwrap the query and filter on ROWNUM <= 3

The engine in this playground accepts both LIMIT and TOP:

SELECT TOP 3 first_name, salary
FROM Employees
ORDER BY salary DESC;
first_namesalary
Chen156000
Ananya142000
Tomas131000

3 rows · produced by running this query on the sample database

The dialect differences page lists these side by side along with the date and string functions, which are the other places portable SQL stops being portable.

Practice this topic

Reading explains the idea; producing it yourself is what makes it stick. These exercises use exactly what this page covers, and your answer is checked by running it against the same sample database:

See all 42 exercises →

Frequently asked questions

What does ORDER BY do in SQL?

It sorts the rows a query returns. Without it, rows come back in whatever order the database found cheapest to produce, which is not guaranteed and can change over time. ORDER BY column sorts ascending by default; add DESC for descending.

How do I sort by two columns in SQL?

List them separated by commas: ORDER BY dept_id ASC, salary DESC. The first column is the primary sort and each later column breaks ties in the one before it. ASC and DESC apply to each column individually, not to the whole clause.

Can I use a column alias in ORDER BY?

Yes. ORDER BY runs after the SELECT list is evaluated, so the alias already exists. WHERE runs before it, which is why the same alias fails there and you have to repeat the expression.

How do I get the top 10 rows in SQL?

ORDER BY the column that defines "top" and add LIMIT 10 - or SELECT TOP 10 on SQL Server, or FETCH FIRST 10 ROWS ONLY on Oracle 12c and newer. The ORDER BY is essential: LIMIT without it returns an arbitrary ten rows.

What is the difference between LIMIT and OFFSET?

LIMIT says how many rows to return; OFFSET says how many to skip first. LIMIT 10 OFFSET 20 returns rows 21 to 30, which is the third page at ten rows per page.

Where do NULL values appear when sorting?

It depends on the database: PostgreSQL and Oracle put them last when sorting ascending, while MySQL, SQL Server and SQLite put them first. Use NULLS FIRST or NULLS LAST where supported, or sort by a CASE expression that makes the rule explicit and works everywhere.

About the author

SQL Practice

SQL Practice publishes free SQL tutorials, an in-browser playground and 42 practice exercises, built so you can run what you read. Every runnable example in our tutorials is executed against the site’s sample database before it is published. Read how we keep tutorials accurate, or report a mistake.