pbPassingBI
/
Window functions intermediate 6 min

ROW_NUMBER, RANK and DENSE_RANK

The three ranking functions, how they treat ties, and top-N per group.

What you'll be able to do
  • Distinguish the three ranking functions
  • Write a top-N-per-group query
  • Choose the right one for deduplication

The three

ROW_NUMBER() OVER (PARTITION BY region ORDER BY total DESC)
RANK()       OVER (PARTITION BY region ORDER BY total DESC)
DENSE_RANK() OVER (PARTITION BY region ORDER BY total DESC)

On values 100, 90, 90, 80:

FunctionResult
ROW_NUMBER1, 2, 3, 4
RANK1, 2, 2, 4
DENSE_RANK1, 2, 2, 3

ROW_NUMBER never ties and breaks them arbitrarily. RANK ties then skips. DENSE_RANK ties without skipping.

This comes up in most SQL interviews at analyst level.

Top N per group

SELECT * FROM (
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY region ORDER BY total DESC) AS rn
  FROM orders
) t
WHERE rn <= 3;

ROW_NUMBER is usually right here because it guarantees exactly three rows. RANK would return four if two orders tied for third — which is sometimes what you want, so state your assumption.

Deduplication

The standard way to keep one row per key:

SELECT * FROM (
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY email ORDER BY updated_at DESC) AS rn
  FROM customers
) t WHERE rn = 1;

Keeps the most recently updated record per email and drops the rest. Add a tiebreaker to the ORDER BY if updated_at can be equal, otherwise which row survives is arbitrary.

NTILE

NTILE(4) OVER (ORDER BY total)   -- quartiles

Distributes rows into n buckets as evenly as it can.

Key points
  • RANK skips after ties, DENSE_RANK does not, ROW_NUMBER never ties
  • ROW_NUMBER guarantees exactly N rows for a top-N query
  • ROW_NUMBER with PARTITION BY is the standard deduplication pattern
Check yourself