ROW_NUMBER()
Assign unique sequential numbers — and solve top-N-per-group
Unique Row Numbers
ROW_NUMBER() assigns a unique sequential integer to each row in its partition, starting at 1. It never produces ties — even rows with identical values get different numbers. Here's the key pattern:
WHERE rn = 1 keeps only the top item per group. WHERE rn <= 3 keeps the top three items per group for ranked displays.rn = 1. This is a clean and efficient way to deduplicate data without using DELETE statements or creating temporary staging tables.How ROW_NUMBER Works
Assign unique sequential numbers
- Assigns 1, 2, 3... to rows within each partition
- No ties — every row gets a unique number
- Tie-breaking is non-deterministic without sufficient ORDER BY
- Most common use: top-N-per-group pattern
Top-N Per Group
The killer pattern for ROW_NUMBER
- Assign ROW_NUMBER within each group (PARTITION BY)
- Filter to rn = 1 for "latest" or "best" per group
- Wrap in CTE or subquery for the WHERE filter
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY price DESC
) AS rn
FROM products
)
SELECT * FROM ranked WHERE rn = 1;ROW_NUMBER Pitfalls
Where it goes wrong
- Tied rows get arbitrary numbering without tiebreaker
- Add a unique column to ORDER BY for deterministic results
- Can't filter ROW_NUMBER in same query — use CTE
- PARTITION BY is optional — omit for global numbering
ROW_NUMBER()Always unique — 1, 2, 3... (no ties)PARTITION BYRestart numbering for each groupORDER BYDetermines which row gets number 1| name | category | price |
|---|---|---|
| Wireless Mouse | Electronics | 24.99 |
| Office Desk | Furniture | 189 |
| LED Desk Lamp | Furniture | 45.5 |
| USB-C Cable | Electronics | 12.99 |
| Bluetooth Speaker | Electronics | 79 |
| Office Chair | Furniture | 95 |
| Coffee Maker | Appliances | 89.99 |
| Notebook Set | Stationery | 8.5 |
| Wireless Headphones | Electronics | 159 |
| Standing Desk | Furniture | 425 |
| id | customer_id | order_date | total |
|---|---|---|---|
| 5001 | C1001 | 2024-01-15 | 59.98 |
| 5002 | C1002 | 2024-02-08 | 449 |
| 5003 | C1001 | 2024-02-20 | 12.5 |
| 5004 | C1003 | 2024-03-05 | 89.99 |
| 5005 | C1006 | 2024-03-15 | 245 |
| 5006 | C1005 | 2024-03-22 | 159 |
| 5007 | C1004 | 2024-04-02 | 45.5 |
| 5008 | C1009 | 2024-04-12 | 79 |
| 5009 | C1012 | 2024-04-22 | 425 |
| 5010 | C1007 | 2024-05-05 | 18.99 |
| 5011 | C1008 | 2024-05-18 | 134 |
| 5012 | C1010 | 2024-06-08 | 65.5 |
Worked Example
Find the most expensive product in each category.
| category | name | price |
|---|---|---|
| Furniture | Standing Desk | 425 |
| Electronics | Wireless Headphones | 159 |
| Appliances | Coffee Maker | 89.99 |
Key Concepts
Pro Tip
ROW_NUMBER enables top-N-per-group queries — one of the most asked SQL interview questions and one of the most useful analytical patterns.
When to Use
Most expensive product per category, latest order per customer, deduplication, pagination, sampling N rows per group.
For each customer, find their 2 most recent orders. Show customer_id, order_date, total, and the row number.