๐ข ROW_NUMBER() is a SQL window function that assigns a unique sequential number to each row in a result set. Numbering starts at 1 and increases by one for every row within the specified window.
๐ What is ROW_NUMBER()?
ROW_NUMBER() generates a unique number for each row based on the order defined in the ORDER BY clause of the OVER() window. Even if multiple rows contain identical values, each row receives a different row number.
Information
๐ฏ Why Use ROW_NUMBER()?
ROW_NUMBER() is useful whenever every row needs a unique position within a result set or partition.
- ๐ Generate sequential row numbers.
- ๐ Rank records uniquely.
- ๐ Implement pagination.
- ๐ Remove duplicate rows.
- ๐ Select top records from each group.
๐ Sample Table
| EmployeeID | EmployeeName | Department | Salary |
|---|---|---|---|
| 101 | Alice | Sales | 60000 |
| 102 | Bob | Sales | 55000 |
| 103 | Charlie | IT | 75000 |
| 104 | David | IT | 70000 |
| 105 | Emma | HR | 50000 |
๐ Basic Syntax
ROW_NUMBER() Syntax
SELECT
column1,
column2,
ROW_NUMBER() OVER (
ORDER BY column_name
) AS RowNum
FROM table_name;The ORDER BY clause inside OVER() determines the sequence in which row numbers are assigned.
๐ก Example: Number All Employees
Assign Sequential Row Numbers
SELECT
EmployeeName,
Salary,
ROW_NUMBER() OVER (
ORDER BY Salary DESC
) AS RowNum
FROM Employees;Employees are numbered from the highest salary to the lowest salary.
๐ Example Output
| RowNum | EmployeeName | Salary |
|---|---|---|
| 1 | Charlie | 75000 |
| 2 | David | 70000 |
| 3 | Alice | 60000 |
| 4 | Bob | 55000 |
| 5 | Emma | 50000 |
๐ก Example: Restart Numbering by Department
Use PARTITION BY to restart numbering for each group.
ROW_NUMBER() with PARTITION BY
SELECT
EmployeeName,
Department,
Salary,
ROW_NUMBER() OVER (
PARTITION BY Department
ORDER BY Salary DESC
) AS DepartmentRank
FROM Employees;๐ Example Output
| Employee | Department | Salary | DepartmentRank |
|---|---|---|---|
| Alice | Sales | 60000 | 1 |
| Bob | Sales | 55000 | 2 |
| Charlie | IT | 75000 | 1 |
| David | IT | 70000 | 2 |
| Emma | HR | 50000 | 1 |
๐ก Example: Return the Highest-Paid Employee in Each Department
Top Employee per Department
SELECT *
FROM (
SELECT
EmployeeName,
Department,
Salary,
ROW_NUMBER() OVER (
PARTITION BY Department
ORDER BY Salary DESC
) AS RowNum
FROM Employees
) RankedEmployees
WHERE RowNum = 1;This query returns only the highest-paid employee from each department.
๐ก Example: Pagination
ROW_NUMBER() is commonly used to retrieve a specific range of rows for paginated results.
Pagination Example
SELECT *
FROM (
SELECT
EmployeeID,
EmployeeName,
ROW_NUMBER() OVER (
ORDER BY EmployeeID
) AS RowNum
FROM Employees
) EmployeeList
WHERE RowNum BETWEEN 11 AND 20;โ๏ธ ROW_NUMBER() vs RANK() vs DENSE_RANK()
| Function | Duplicate Values | Gaps in Ranking |
|---|---|---|
| ROW_NUMBER() | Always assigns unique numbers. | Not applicable. |
| RANK() | Same rank for ties. | โ Yes |
| DENSE_RANK() | Same rank for ties. | โ No |
Important
๐ผ Real-World Applications
- ๐ Rank employees within each department.
- ๐ Implement paginated search results.
- ๐ Select the latest record for each customer.
- ๐งน Remove duplicate records while keeping one row.
- ๐ Retrieve the highest-selling product in each category.
๐๏ธ Database Compatibility
| Database System | ROW_NUMBER() Support |
|---|---|
| MySQL | โ Supported in MySQL 8.0 and later. |
| PostgreSQL | โ Fully supported. |
| SQL Server | โ Fully supported. |
| Oracle | โ Fully supported. |
| SQLite | โ Supported in SQLite 3.25.0 and later. |
โ ๏ธ Common Mistakes
- โ Omitting the ORDER BY clause inside OVER().
- โ Assuming row numbers remain the same if the ordering changes.
- โ Using ROW_NUMBER() when tied rows should share the same rank.
- โ Forgetting to use PARTITION BY when numbering should restart for each group.
Warning
โ ๏ธ Best Practices
Best Practice
๐ Key Points to Remember
- ๐ ROW_NUMBER() assigns a unique sequential number to each row.
- ๐ It is a window function and requires the OVER() clause.
- ๐ ORDER BY determines the numbering sequence.
- ๐ PARTITION BY restarts numbering for each group.
- ๐ It is ideal for pagination, deduplication, and top-N queries.
- ๐ Unlike RANK(), duplicate values never share the same row number.