πŸ”’ Ranks in Order: Understanding ROW_NUMBER(), RANK(), and DENSE_RANK()


πŸš€ Ranking Like a Pro: Why These Functions Matter

"Xorthax wants to reward the top traders in each star clusterβ€”but what if multiple customers have the same sales? Should they tie? Skip numbers? SQL ranking functions let us handle it with precision."


πŸ€” What Are ROW_NUMBER(), RANK(), and DENSE_RANK()?

These are ranking window functions that assign an ordered position to each row within a partition. They differ in how they handle ties (rows with equal values).

Function Handles Ties? Behavior
ROW_NUMBER() ❌ Always assigns unique rank
RANK() βœ… Tied rows get same rank, skip gaps
DENSE_RANK() βœ… Tied rows get same rank, no gaps

πŸ”§ Syntax Snapshot

FUNCTION() OVER (
  PARTITION BY some_column
  ORDER BY sort_column [ASC|DESC]
)

Use the appropriate function depending on your business rule for ranking.


πŸ“‹ See It in Action

Let's use the same dataset and apply all three ranking functions in a single query. This makes their differences crystal clear:

🧾 Sample Data

CustomerID Region OrderAmount
C1 East 500
C2 East 700
C3 East 700
C4 East 600

πŸ”Ž Query with All Three Functions

SELECT CustomerID, Region, OrderAmount,
       ROW_NUMBER() OVER (PARTITION BY Region ORDER BY OrderAmount DESC) AS RowNum,
       RANK()       OVER (PARTITION BY Region ORDER BY OrderAmount DESC) AS Rank,
       DENSE_RANK() OVER (PARTITION BY Region ORDER BY OrderAmount DESC) AS DenseRank
FROM Orders;

πŸ“Š Result

CustomerID OrderAmount RowNum Rank DenseRank
C2 700 1 1 1
C3 700 2 1 1
C4 600 3 3 2
C1 500 4 4 3

Notice how each function handles ties differently:


🧭 When Should You Use Each?


🧠 Keywords That Hint You're Ranking


⚠️ Common Pitfalls


πŸ”„ CTEs and Filtering for Top Results

Ranking functions become especially powerful when paired with CTEs (Common Table Expressions). CTEs allow you to assign ranks and then filter the results in a clean, readable way.

This is useful when you're asked to:

Use a structure like this to select only the top-ranked rows per partition:

WITH RankedOrders AS (
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY Region ORDER BY OrderAmount DESC) AS RankNum
  FROM Orders
)
SELECT * FROM RankedOrders WHERE RankNum = 1;

βž• Combine with Aggregates or Analytics

Ranking functions can be used as a first step to filter data before applying other analytics. This is especially helpful in "Top N" style analyses, where we want to consider only the best performers.

In the example below, we first rank all customers by total spending within each region. Then we filter to keep only the top 5 spenders per region, and finally use an aggregate function to summarize their behavior:

WITH TopSpenders AS (
  SELECT CustomerID, Region, TotalAmount,
         RANK() OVER (PARTITION BY Region ORDER BY TotalAmount DESC) AS SpendRank
  FROM CustomerTotals
)
SELECT Region, CustomerID, TotalAmount
FROM TopSpenders
WHERE SpendRank <= 5
ORDER BY Region, SpendRank;

🧰 Debugging Tips

Debugging ranking queries often means checking how ordering and ties behave under the hood.


πŸ”— Related Topics

(See also: NTILE(), Aggregate Window Functions, Filtering with CTEs)