Difference Between Rank And Dense Rank

7 min read

Understanding the difference between rank and dense rank is a fundamental concept for anyone working with ordered data, especially in SQL, business intelligence tools, or data‑analysis programming languages. Both functions assign a sequential number to each row within a partition based on a specified ordering, but they handle ties in distinct ways. Grasping this distinction helps you produce accurate reports, avoid misleading rankings, and choose the right function for your analytical needs.


What Is RANK()?

The RANK() function assigns a rank to each row, but when two or more rows share the same value in the ordering column, they receive the same rank. The next distinct value’s rank is then incremented by the number of rows that tied for the previous rank. Basically, RANK() leaves gaps in the ranking sequence whenever ties occur And it works..

Counterintuitive, but true.

Syntax (SQL‑standard)

RANK() OVER (
    PARTITION BY    -- optional
    ORDER BY  [ASC|DESC]
)

Key characteristics

  • Tie handling: equal values → identical rank.
  • Gap creation: after a tie, the rank jumps by the count of tied rows.
  • Use case: when you want to reflect the “position” in a competition where ties skip places (e.g., Olympic medals).

What Is DENSE_RANK()?

The DENSE_RANK() function also assigns the same rank to tied rows, but it does not create gaps. Now, after a set of ties, the next distinct value receives the immediately following integer rank. This means the ranking sequence is dense—there are no missing numbers That's the part that actually makes a difference..

Not the most exciting part, but easily the most useful.

Syntax (SQL‑standard)

DENSE_RANK() OVER (
    PARTITION BY    -- optional
    ORDER BY  [ASC|DESC]
)

Key characteristics

  • Tie handling: equal values → identical rank (same as RANK()).
  • No gaps: ranking continues consecutively regardless of ties.
  • Use case: when you need a simple, gap‑free ordering (e.g., tiered loyalty levels, product popularity tiers).

Key Differences Between RANK() and DENSE_RANK()

Aspect RANK() DENSE_RANK()
Ranking after ties Skips numbers equal to the count of tied rows Continues with the next integer
Resulting sequence May contain gaps (e.g., 1, 2, 2, 4, 5) Always gap‑free (e.g.

Quick note before moving on.

Understanding these differences ensures you select the function that matches the logical interpretation you need.


Practical Examples

Example Dataset

Consider a table sales with columns salesperson, region, and amount.

salesperson region amount
Alice North 5000
Bob North 5000
Carol North 3000
Dave South 7000
Eve South 7000
Frank South 4000

We want to rank salespeople within each region by amount descending Simple, but easy to overlook. Practical, not theoretical..

Using RANK()

SELECT
    salesperson,
    region,
    amount,
    RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_val
FROM sales;

Result

salesperson region amount rank_val
Alice North 5000 1
Bob North 5000 1
Carol North 3000 3
Dave South 7000 1
Eve South 7000 1
Frank South 4000 3

Honestly, this part trips people up more than it should No workaround needed..

Notice the gap after the tie: rank jumps from 1 to 3.

Using DENSE_RANK()

SELECT
    salesperson,
    region,
    amount,
    DENSE_RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS dense_rank_val
FROM sales;

Result

salesperson region amount dense_rank_val
Alice North 5000 1
Bob North 5000 1
Carol North 3000 2
Dave South 7000 1
Eve South 7000 1
Frank South 4000 2

Here the ranking is dense: after the tie at rank 1, the next distinct value receives rank 2.


When to Use RANK() vs DENSE_RANK()

Situation Prefer RANK() Prefer DENSE_RANK()
Competitive standings where ties should leave vacant places (e.g., “1st, 1st, 3rd”) ✅ ❌
Tiered classification where you want consecutive levels (e.g.

In practice, many analysts start with DENSE_RANK() for simplicity and switch to RANK()

ROW_NUMBER(): The Sequential Identifier

While RANK() and DENSE_RANK() handle ties by assigning the same rank, ROW_NUMBER() assigns a unique, sequential integer to every row within the partition, regardless of ties in the ORDER BY column. This makes it invaluable when you need a distinct row identifier for pagination, deduplication, or when the physical order matters more than the logical ranking.

SELECT
    salesperson,
    region,
    amount,
    ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_num
FROM sales;

Result

salesperson region amount row_num
Alice North 5000 1
Bob North 5000 2
Carol North 3000 3
Dave South 7000 1
Eve South 7000 2
Frank South 4000 3

Even though Alice and Bob have the same amount, they receive different row_num values (1 and 2). The order among tied rows is nondeterministic unless a secondary sort key is specified. To ensure consistent results, you can add a tiebreaker column:

ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC, salesperson ASC)

When to Use ROW_NUMBER()

  • Pagination: Fetching a specific page of results (e.g., rows 11–20) requires a stable, unique sequence.
  • Deduplication: When you need to keep only one record per group (e.g., the latest transaction per customer), you can filter by ROW_NUMBER() = 1.
  • Random Sampling: Assigning a random row number to select a subset of data.
  • When Order Within Ties Matters: Take this: alphabetizing salespeople with the same sales volume.

Choosing the Right Function: A Decision Guide

Use Case Function Why
Olympic medal standings (gold, silver, bronze with ties) RANK() Ties share a rank, and the next rank is skipped (e.And
Calculating percentiles with equal-sized buckets NTILE() Divides rows into a specified number of roughly equal groups. g.Practically speaking,
Fetching the top N records per group ROW_NUMBER() Guarantees exactly N unique rows, even if there are ties. Which means g. Also, , 1, 1, 3).
Product tiers ( Platinum, Gold, Silver) DENSE_RANK() Ties share a rank, and the next rank is consecutive (e., 1, 1, 2).
Generating a sequential ID for export or display ROW_NUMBER() Provides a consistent, gap-free numbering independent of data values.

Advanced Considerations

  • Performance: All three functions have similar overhead. The choice should be driven by logic, not speed.
  • Window Frame: By default, these functions use RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. For cumulative rankings (e.g., running totals), you might adjust the frame, but for standard ranking, the default is sufficient.
  • NULLs: By default, NULL values are sorted last in DESC order and first in ASC order. Use NULLS FIRST or NULLS LAST to control this behavior explicitly.

Conclusion

Mastering RANK(), DENSE_RANK(), and ROW_NUMBER() is a cornerstone of advanced SQL analytics. The key is to align the function’s behavior with the business semantics of your ranking:

  • Use RANK() when ties should create gaps in the sequence (e.g., competitive standings).
  • Use DENSE_RANK() when you want a continuous sequence without gaps (e.g., tiered classifications).
  • Use ROW_NUMBER() when you need a unique, arbitrary order for each row (e.g., pagination or deduplication).

By understanding these distinctions, you can transform raw data into meaningful insights, whether you’re building leaderboards, segmenting customers, or preparing data for downstream processing. The window functions toolkit empowers you to answer complex analytical questions with clarity and precision.

Just Hit the Blog

Freshly Published

Curated Picks

More Good Stuff

Thank you for reading about Difference Between Rank And Dense Rank. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home