ORDER BY sorts the rows a query returns by one or more columns or expressions, ascending or descending. Without it, the database returns rows in whatever order is quickest. The examples work in PostgreSQL, MySQL, SQLite and SQL Server.
How to use ORDER BY
Put ORDER BY at the end of the query, after WHERE, GROUP BY and HAVING; only LIMIT comes after it. List the columns to sort by. Each one sorts in ascending order unless you add DESC:
SELECT columns
FROM table_name
ORDER BY column1 [ASC | DESC], column2 [ASC | DESC];
-- Input: customers
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 1 | Olivia Hart | 2024-03-12
-- 2 | Marcus Lee | 2023-11-05
-- 3 | Zara Ahmed | 2024-01-27
-- 4 | Ben Carter | 2023-08-19
SELECT customer_id, name, signup_date
FROM customers
ORDER BY signup_date;
-- Output:
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 4 | Ben Carter | 2023-08-19
-- 2 | Marcus Lee | 2023-11-05
-- 3 | Zara Ahmed | 2024-01-27
-- 1 | Olivia Hart | 2024-03-12
SELECT customer_id, name, signup_date
FROM customers
ORDER BY signup_date DESC;
-- Output:
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 1 | Olivia Hart | 2024-03-12
-- 3 | Zara Ahmed | 2024-01-27
-- 2 | Marcus Lee | 2023-11-05
-- 4 | Ben Carter | 2023-08-19
Rules to know
-
DESC only applies to the column right before it.
ORDER BY author, published DESCsorts authors A to Z, then each author’s books newest first:
-- Input: books
-- title | author | published
-- --------------------+-----------------+----------
-- Emma | Jane Austen | 1815
-- Bleak House | Charles Dickens | 1853
-- Pride and Prejudice | Jane Austen | 1813
-- Hard Times | Charles Dickens | 1854
SELECT title, author, published
FROM books
ORDER BY author, published DESC;
-- Output:
-- title | author | published
-- --------------------+-----------------+----------
-- Hard Times | Charles Dickens | 1854
-- Bleak House | Charles Dickens | 1853
-- Emma | Jane Austen | 1815
-- Pride and Prejudice | Jane Austen | 1813
-
Without ORDER BY, the order isn’t guaranteed. Rows often come back in the order they were inserted, until the table grows, an index changes or the query runs in parallel. That goes for
LIMITtoo: without ORDER BY it returns whichever rows the database reads first. - Tied rows come back in any order. Add a unique column, such as the ID, as the last sort column so the order is the same on every run:
-- Input: customers
-- customer_id | name | signup_date
-- ------------+------------+------------
-- 3 | Mara Lind | 2024-01-15
-- 1 | Owen Price | 2024-01-10
-- 4 | Tariq Aziz | 2024-01-15
-- 2 | Lily Shore | 2024-01-15
-- The three 2024-01-15 rows can come back in any order
SELECT * FROM customers ORDER BY signup_date;
-- Output:
-- customer_id | name | signup_date
-- ------------+------------+------------
-- 1 | Owen Price | 2024-01-10
-- 3 | Mara Lind | 2024-01-15
-- 4 | Tariq Aziz | 2024-01-15
-- 2 | Lily Shore | 2024-01-15
-- customer_id breaks the tie
SELECT * FROM customers ORDER BY signup_date, customer_id;
-- Output:
-- customer_id | name | signup_date
-- ------------+------------+------------
-- 1 | Owen Price | 2024-01-10
-- 2 | Lily Shore | 2024-01-15
-- 3 | Mara Lind | 2024-01-15
-- 4 | Tariq Aziz | 2024-01-15
- NULLs sort first, except in PostgreSQL, which puts them last. That’s for ascending order; DESC flips it:
-- Input: users
-- name | middle_name
-- -----------+------------
-- John Smith | Robert
-- Ana Lopez | NULL
-- Kim Young | Elise
SELECT name, middle_name
FROM users
ORDER BY middle_name;
-- Output:
-- name | middle_name
-- -----------+------------
-- Ana Lopez | NULL
-- Kim Young | Elise
-- John Smith | Robert
-- (PostgreSQL puts Ana Lopez last instead)
To choose where NULLs go, see NULLS FIRST and NULLS LAST below.
Alternative syntax
You can sort by a column alias or by the column’s position in the SELECT list. An alias saves repeating an expression. A position is shorter but breaks silently when someone edits the SELECT list:
-- Input: employees
-- name | monthly_salary
-- ---------+---------------
-- Ivy Chen | 5200
-- Max Dunn | 7600
-- Pia Roth | 4500
SELECT name, monthly_salary * 12 AS annual_salary
FROM employees
ORDER BY annual_salary DESC; -- or ORDER BY 2 DESC
-- Output:
-- name | annual_salary
-- ---------+--------------
-- Max Dunn | 91200
-- Ivy Chen | 62400
-- Pia Roth | 54000
NULLS FIRST and NULLS LAST after a sort column say where its NULLs go:
-- Input: the users table above
SELECT name, middle_name
FROM users
ORDER BY middle_name NULLS LAST;
-- MySQL and SQL Server don't support NULLS LAST. Sort on a CASE first instead:
-- ORDER BY CASE WHEN middle_name IS NULL THEN 1 ELSE 0 END, middle_name
-- Output:
-- name | middle_name
-- -----------+------------
-- Kim Young | Elise
-- John Smith | Robert
-- Ana Lopez | NULL
MySQL has FIELD(), which sorts values in the order you list them. Elsewhere, use a CASE expression like the one in Sort tasks by priority below:
-- Input: products
-- product_name | category
-- -------------+------------
-- Cookbook | Books
-- Raincoat | Clothing
-- Tablet | Electronics
-- Scarf | Clothing
-- MySQL
SELECT * FROM products
ORDER BY FIELD(category, 'Electronics', 'Clothing', 'Books');
-- Output:
-- product_name | category
-- -------------+------------
-- Tablet | Electronics
-- Raincoat | Clothing
-- Scarf | Clothing
-- Cookbook | Books
When should you use ORDER BY?
Use ORDER BY whenever the order of the rows means something to whoever reads them. Most uses fall into one of these four.
Show a list in a predictable order
Names from A to Z, dates oldest first, IDs in sequence. Without ORDER BY the same query can return its rows in a different order from one run to the next, and pages of results can overlap or skip rows.
Find the highest or most recent values
Sort descending to put the biggest, newest or most expensive rows first, then add LIMIT to keep only the top few: best sellers, the latest orders, the highest scores.
Sort by more than one column
When the first sort column has ties, the next one decides the order within them: department then salary, grade then score, status then date. Each column can have its own direction.
Sort by a calculated value or a custom order
ORDER BY accepts any expression, not only columns: a total worked out from two columns, the length of a name, or a CASE expression that puts values like High, Medium and Low in an order the alphabet won’t give you.
Real examples of ORDER BY
Page through a customer list
ORDER BY with LIMIT and OFFSET returns one page at a time. Three customers signed up on the same day, so customer_id breaks the tie. Without it, the database could put the same customer on both pages and skip another:
-- Input: customers
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 1 | Nina Brandt | 2024-02-14
-- 2 | Theo Walsh | 2023-09-30
-- 3 | Iris Novak | 2024-02-14
-- 4 | Felix Moore | 2024-02-14
-- 5 | June Park | 2024-03-22
-- Page 1
SELECT customer_id, name, signup_date
FROM customers
ORDER BY signup_date, customer_id
LIMIT 2 OFFSET 0;
-- Output:
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 2 | Theo Walsh | 2023-09-30
-- 1 | Nina Brandt | 2024-02-14
-- Page 2
SELECT customer_id, name, signup_date
FROM customers
ORDER BY signup_date, customer_id
LIMIT 2 OFFSET 2;
-- SQL Server has no LIMIT: use OFFSET 2 ROWS FETCH NEXT 2 ROWS ONLY
-- Output:
-- customer_id | name | signup_date
-- ------------+-------------+------------
-- 3 | Iris Novak | 2024-02-14
-- 4 | Felix Moore | 2024-02-14
Show a leaderboard’s top three
Sort the scores from highest to lowest and keep the first three:
-- Input: players
-- username | score
-- ----------+------
-- quokka42 | 1870
-- nightowl | 2310
-- pixelpete | 1525
-- zara_k | 2045
-- bytebard | 1990
SELECT username, score
FROM players
ORDER BY score DESC
LIMIT 3;
-- SQL Server has no LIMIT: write SELECT TOP 3 username, score instead
-- Output:
-- username | score
-- ---------+------
-- nightowl | 2310
-- zara_k | 2045
-- bytebard | 1990
Build an order report by status, value and date
Statuses sort alphabetically. Within each status the biggest orders come first, and orders of the same value show the newest first:
-- Input: orders
-- order_id | status | order_value | order_date
-- ---------+------------+-------------+-----------
-- 1001 | Shipped | 120.00 | 2024-03-02
-- 1002 | Delivered | 75.50 | 2024-02-20
-- 1003 | Shipped | 120.00 | 2024-03-05
-- 1004 | Processing | 42.00 | 2024-03-06
-- 1005 | Delivered | 210.00 | 2024-02-28
SELECT order_id, status, order_value, order_date
FROM orders
ORDER BY status ASC, order_value DESC, order_date DESC;
-- Output:
-- order_id | status | order_value | order_date
-- ---------+------------+-------------+-----------
-- 1005 | Delivered | 210.00 | 2024-02-28
-- 1002 | Delivered | 75.50 | 2024-02-20
-- 1004 | Processing | 42.00 | 2024-03-06
-- 1003 | Shipped | 120.00 | 2024-03-05
-- 1001 | Shipped | 120.00 | 2024-03-02
Sort tasks by priority
Alphabetical order would give High, Low, Medium. A CASE expression turns each priority into a number to sort by. Anything else, here a NULL, gets 4 and goes last, and the due date orders tasks within each priority:
-- Input: tasks
-- task_name | priority | due_date
-- ---------------+----------+-----------
-- Update docs | Low | 2024-06-03
-- Fix login bug | High | 2024-06-10
-- Plan offsite | NULL | 2024-05-20
-- Renew SSL cert | High | 2024-06-01
-- Review PR | Medium | 2024-06-04
SELECT task_name, priority, due_date
FROM tasks
ORDER BY
CASE priority
WHEN 'High' THEN 1
WHEN 'Medium' THEN 2
WHEN 'Low' THEN 3
ELSE 4
END,
due_date;
-- Output:
-- task_name | priority | due_date
-- ---------------+----------+-----------
-- Renew SSL cert | High | 2024-06-01
-- Fix login bug | High | 2024-06-10
-- Review PR | Medium | 2024-06-04
-- Update docs | Low | 2024-06-03
-- Plan offsite | NULL | 2024-05-20
Further reading about ORDER BY
On this blog:
- SQL GROUP BY guide: sorting grouped results, such as the top categories by revenue
- SQL CASE statement guide: CASE expressions for custom sort orders
- Limiting rows in PostgreSQL, MySQL, SQLite and SQL Server: LIMIT, OFFSET and TOP
- MySQL select top N rows: ORDER BY with LIMIT in MySQL
-
SQL find duplicates guide: ORDER BY inside
ROW_NUMBER()decides which copy counts as the original
Official documentation:
- PostgreSQL: Sorting rows (ORDER BY)
- MySQL: Sorting rows
- SQLite: SELECT, the ORDER BY clause
- SQL Server: ORDER BY clause
Beekeeper Studio Is A Free & Open Source Database GUI
Best SQL query & editor tool I have ever used. It provides everything I need to manage my database. - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studio is fast, intuitive, and easy to use. Beekeeper supports loads of databases, and works great on Windows, Mac and Linux.
What Users Say About Beekeeper Studio
"Beekeeper Studio completely replaced my old SQL workflow. It's fast, intuitive, and makes database work enjoyable again."
"I've tried many database GUIs, but Beekeeper strikes the perfect balance between features and simplicity. It just works."