Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. SQL Dictionary
  3. ROW_NUMBER / RANK / DENSE_RANK

ROW_NUMBER / RANK / DENSE_RANK

Since: SQL:2003(2003)

Window functions that assign sequential numbers or rankings to each row. The three functions differ in how they handle ties.

Syntax

ROW_NUMBER: Assigns a unique sequential number with no duplicates.

ROW_NUMBER() OVER ([PARTITION BY partition_column] ORDER BY sort_column)

RANK: Skips the next number after a tie.

RANK() OVER ([PARTITION BY partition_column] ORDER BY sort_column)

DENSE_RANK: Does not skip numbers after a tie.

DENSE_RANK() OVER ([PARTITION BY partition_column] ORDER BY sort_column)

Function Reference

FunctionDescription
ROW_NUMBER()Assigns a unique sequential number to every row, even when rows have the same value.
RANK()Assigns the same rank to tied rows, then skips the next number (e.g., 1, 2, 2, 4).
DENSE_RANK()Assigns the same rank to tied rows without skipping the next number (e.g., 1, 2, 2, 3).

Sample Code

The following examples use the members table.

members name role score user_a Sales 620 user_c Sales 530 user_e Engineering 530 user_b Engineering 420 user_d Sales 380 5 rows in set

Display all three ranking types ordered by score descending.

sample_row_number_rank.sql
SELECT
    name,
    role,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK()       OVER (ORDER BY score DESC) AS rank_val,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_val
FROM members
ORDER BY score DESC;
+--------+-----------+-------+---------+----------+----------------+
| name   | role      | score | row_num | rank_val | dense_rank_val |
+--------+-----------+-------+---------+----------+----------------+
| user_a | Sales  |   620 |       1 |        1 |              1 |
| user_c | Sales  |   530 |       2 |        2 |              2 |
| user_e | Engineering |   530 |       3 |        2 |              2 |
| user_b | Engineering |   420 |       4 |        4 |              3 |
| user_d | Sales  |   380 |       5 |        5 |              4 |
+--------+-----------+-------+---------+----------+----------------+
5 rows in set

Rank members by score within each role, then retrieve the top 2 per role.

sample_row_number_rank.sql
SELECT name, role, score, role_rank
FROM (
    SELECT
        name,
        role,
        score,
        RANK() OVER (PARTITION BY role ORDER BY score DESC) AS role_rank
    FROM members
) AS m
WHERE role_rank <= 2;
+--------+-----------+-------+-----------+
| name   | role      | score | role_rank |
+--------+-----------+-------+-----------+
| user_a | Sales  |   620 |         1 |
| user_c | Sales  |   530 |         2 |
| user_e | Engineering |   530 |         1 |
| user_b | Engineering |   420 |         2 |
+--------+-----------+-------+-----------+
4 rows in set

Database Compatibility

ROW_NUMBER(), RANK(), and DENSE_RANK() are supported in MySQL (8.0+), PostgreSQL, Oracle, SQL Server, and SQLite (3.25+).

SELECT
    name,
    role,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK()       OVER (ORDER BY score DESC) AS rank_val,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_val
FROM members
ORDER BY score DESC;

Notes

The key difference between the three functions is how they handle ties. ROW_NUMBER() always assigns a unique number to every row, so tied values still receive different numbers. The order among tied rows is determined by the database engine and is not guaranteed.

RANK() and DENSE_RANK() both assign the same rank to tied rows, but differ in what comes next. Use RANK() when gaps matter (e.g., sports standings where two 2nd-place finishers mean the next rank is 4th), and DENSE_RANK() when you want consecutive ranks without gaps (e.g., two 2nd-place finishers are followed by 3rd).

To retrieve the top N rows, calculate the rank first in a derived table (or CTE), then filter with a WHERE clause in the outer query. This is necessary because window function results cannot be filtered directly in a WHERE clause. For the basic window function syntax, see OVER / PARTITION BY.

If you find any errors or copyright issues, please .