Generates a sequential number for each row, ordered and optionally partitioned, enabling easy ranking, pagination, and de-duplication.
ROW_NUMBER() returns a unique, consecutive integer for every row in the result set. The counter restarts whenever the optional PARTITION BY clause changes, and the sequence follows the ORDER BY expression inside the window.
Use it for pagination, selecting the first product per category, removing duplicates while keeping the newest record, or assigning a stable rank without session variables.
Place ROW_NUMBER() in the SELECT list, add an OVER() clause, include an ORDER BY for deterministic results, and optionally a PARTITION BY to restart counting within logical groups.
Partition when you want numbering to restart per customer, per product, or any other business dimension—e.g., order sequencing per customer.
Wrap the query in a CTE or derived table, then add a WHERE clause on the generated column (e.g., row_num = 1) to keep only the desired rows.
Always specify ORDER BY. Index the ordering columns for performance. Use meaningful aliases like row_num. Keep window functions in a subquery when further filtering.
WITH ordered_orders AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) AS row_num,
id, customer_id, order_date, total_amount
FROM Orders)
SELECT *
FROM ordered_orders
WHERE row_num = 1;
This returns each customer's earliest order.
Skipping ORDER BY makes row numbers nondeterministic—always provide it. Forgetting PARTITION BY when needed causes numbering to run globally; declare it to restart counts logically.
Yes. MySQL will number the entire result set once. Add PARTITION BY only when you need separate sequences per group.
The function is supported from MySQL 8.0 onward. Earlier versions require user-defined variables to emulate the behavior.
Wrap the query in a CTE or subquery, then filter by row_num BETWEEN (page-1)*page_size+1 AND page*page_size.