top of page

Welcome
to NumpyNinja Blogs

NumpyNinja: Blogs. Demystifying Tech,

One Blog at a Time.
Millions of views. 

SQL Ranking Functions Demystified: When to Use ROW_NUMBER, RANK, or DENSE_RANK

Jan 13
4 min read

Ranking is everywhere in analytics.

Every time you build a leaderboard, select top-performing customers, remove duplicate records, or prioritize tasks, you are using ranking logic—sometimes without even realizing it.

SQL gives us three powerful window functions for this purpose:

  • ROW_NUMBER()

  • RANK()

  • DENSE_RANK()

They look almost identical in syntax. But their behavior—especially when ties exist—can completely change your results. Choosing the wrong one can quietly introduce errors into dashboards, reports, or business decisions.

This post breaks down these three ranking functions using a simple dataset, real-world examples, and clear explanations—so you always know which one to use and why.


Why Ranking Functions Matter More Than You Think

Ranking functions are not just for “top 10” lists. They show up in many everyday analytics problems, such as:

  • Selecting the top N records per group

  • Identifying duplicate rows

  • Assigning priority or order

  • Categorizing performance levels

  • Building competition-style leaderboards

  • Creating tiers, bands, or segments

  • Resolving tie-breaking logic

In real projects, the difference between ROW_NUMBER and RANK might decide:

  • Who gets a bonus

  • Which customer is contacted first

  • Which record survives deduplication

  • How results appear in executive dashboards

That’s why understanding tie behavior is critical.


The Three Ranking Functions at a Glance

Function

Handles Ties

Behavior

Common Use Case

ROW_NUMBER

 No

Always unique numbers

Deduplication, pagination

RANK

 Yes

Skips numbers after ties

Competitions, awards

DENSE_RANK

 Yes

No gaps after ties

Segmentation, tiering

This table alone is useful—but the real understanding comes from seeing them in action.


Our Sample Dataset

Imagine we’re working with employee performance data:

employee_id

name

score

101

Alice

95

102

Bob

90

103

Carol

95

104

David

88

105

Emma

88

106

Frank

85

Notice two important things:

  • Alice and Carol have the same score

  • David and Emma also share a score

These ties are exactly where ranking functions start to behave differently.



Applying All Three Ranking Functions

Let’s apply all three ranking functions in a single query:

SELECT

    employee_id,

    name,

    score,

    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,

    RANK() OVER (ORDER BY score DESC) AS rank_num,

    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_num

FROM employee_scores;


This query sorts employees by score (highest first) and assigns three different types of ranks.



Output Comparison

name

score

ROW_NUMBER

RANK

DENSE_RANK

Alice

95

1

1

1

Carol

95

2

1

1

Bob

90

3

3

2

David

88

4

4

3

Emma

88

5

4

3

Frank

85

6

6

4

At first glance, the numbers look similar. But the logic behind them is very different.



Understanding Each Function

ROW_NUMBER(): Strict Ordering, No Mercy for Ties

ROW_NUMBER() assigns a unique number to every row, no matter what.

Even if two employees have the same score, SQL still forces an order and assigns different numbers.

Think of it as: “Line everyone up and number them one by one.”

Best use cases:

  • Removing duplicates

  • Pagination (page 1, page 2, etc.)

  • Selecting the most recent or first record

  • Situations where ties should not exist logically

If you need exactly one row per group, ROW_NUMBER() is your best friend.



RANK(): Competition-Style Ranking

RANK() respects ties—but introduces gaps.

When two employees tie for first place, they both get rank 1. The next employee doesn’t get rank 2—they get rank 3.

Think of it as: “How medals are awarded in competitions.”

Best use cases:

  • Sales contests

  • Sports leaderboards

  • Exam or scholarship rankings

  • Award-based systems

If skipping rank numbers feels natural to the problem, RANK() is usually the right choice.



DENSE_RANK(): Clean and Continuous Ranking

DENSE_RANK() also respects ties—but never skips numbers.

If two employees tie for first, the next rank is 2—not 3.

Think of it as: “Grouping people into performance levels.”

Best use cases:

  • Performance bands

  • Risk levels

  • Customer segmentation

  • Reporting categories

If skipped numbers would confuse stakeholders, DENSE_RANK() keeps things clean and readable.


Real-World Scenarios You’ll Actually Face

1️⃣ Deduplication with ROW_NUMBER

You want the latest record per customer:

ROW_NUMBER() OVER (

  PARTITION BY customer_id 

  ORDER BY updated_at DESC

)


Filter where row_number = 1, and duplicates disappear—cleanly and predictably.


2️⃣ Competitions and Awards with RANK

Two sales reps tie for the highest revenue:

  • Both get rank 1

  • The next rep gets rank 3

This mirrors how competitions work in the real world—and avoids unfair rankings.


3️⃣ Performance Bands with DENSE_RANK

You want to group customers into tiers:

  • High value → Tier 1

  • Medium value → Tier 2

  • Low value → Tier 3

This is where DENSE_RANK() shines, especially in dashboards and reports.


Which One Should You Use?

Scenario

Best Function

Remove duplicates

ROW_NUMBER

Pick top N per group

ROW_NUMBER

Leaderboards with ties

RANK

Ranking without gaps

DENSE_RANK

Grouping similar values

DENSE_RANK


Final Thoughts

ROW_NUMBER, RANK, and DENSE_RANK may look similar, but they solve very different problems.

  • ROW_NUMBER → unique ordering, no ties

  • RANK → ties allowed, gaps included

  • DENSE_RANK → ties allowed, no gaps

Once you understand how each function treats ties, ranking problems stop being confusing—and your SQL becomes more intentional and reliable.

If you work in analytics, reporting, or data engineering, mastering these three functions is not optional. It’s essential


 
 

+1 (302) 200-8320

NumPy_Ninja_Logo (1).png

Numpy Ninja Inc. 8 The Grn Ste A Dover, DE 19901

© Copyright 2025 by Numpy Ninja Inc.

  • Twitter
  • LinkedIn
bottom of page