RANK vs DENSE_RANK vs ROW_NUMBER Practice
3 SQL ranking functions look interchangeable until 2 rows tie. ROW_NUMBER picks a winner. RANK ties them and leaves a gap. DENSE_RANK ties them with no gap. Picking the wrong one is a silent bug: the query runs, returns rows, and the interviewer waits to see if you notice the difference. 8 graded problems below, organized around the 3-cell decision tree.
3 SQL ranking functions look interchangeable until 2 rows tie. ROW_NUMBER picks a winner. RANK ties them and leaves a gap. DENSE_RANK ties them with no gap. Picking the wrong one is a silent bug: the query runs, returns rows, and the interviewer waits to see if you notice the difference. 8 graded problems below, organized around the 3-cell decision tree.
Know ranking the way the interviewer who asks it knows it.
Decision tree: 3 cells
Memorize this and you'll pick the right function on instinct, every time.
Use when the question is "exactly 1 row per partition": most recent order per customer, deduplication, pagination, single-row sample. ROW_NUMBER never ties.
- "Latest per X"
- "Most recent per X"
- "Pick one per X"
- "Deduplicate by X"
Use when ties are real and a gap after them is the right semantics: leaderboards, competition rankings, "top N including ties". WHERE rnk <= 3 may return 4 rows if 2 tied at 2.
- "Top N including ties"
- "Leaderboard"
- "Competition ranking"
- "Standings"
Use when the question is about the Nth distinct value, not the Nth row: 2nd highest distinct salary, 3rd distinct price tier. DENSE_RANK doesn't skip after ties.
- "Nth highest distinct"
- "Nth unique X"
- "Nth tier"
- "Top N distinct values"
How the 3 functions diverge under ties
Same input, same ORDER BY. The divergence on tied rows is the entire interview interest.
'2nd highest distinct salary' has 4 plausible answers, 2 right ones
-- Prompt: "Find the 2nd highest distinct salary."
-- 3 plausible interpretations; the grader rejects the wrong one explicitly.
-- WRONG. Returns the 2nd row by salary descending, which equals the
-- highest salary when 2+ employees share it.
SELECT salary FROM employees
ORDER BY salary DESC OFFSET 1 LIMIT 1;
-- WRONG. RANK = 2 returns nothing if the top is tied (RANK skips to 3).
SELECT DISTINCT salary FROM (
SELECT salary, RANK() OVER (ORDER BY salary DESC) AS r FROM employees
) t WHERE r = 2;
-- RIGHT. DENSE_RANK = 2 returns the 2nd distinct salary regardless of
-- ties at the top.
SELECT salary FROM (
SELECT DISTINCT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
FROM employees
) t WHERE dr = 2;
-- ALSO RIGHT, terser. SELECT DISTINCT inside the subquery isn't needed
-- because DENSE_RANK already collapses ties.
WITH ranked AS (
SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
FROM employees
)
SELECT DISTINCT salary FROM ranked WHERE dr = 2;2 wrong, 2 right. OFFSET 1 and RANK both fail under ties at the top. DENSE_RANK doesn't.
8 ranking practice problems
Each problem maps to a cell of the decision tree. The grader catches wrong-function selection by engineering ties into the seed data.
Return 1 row per customer: the most recent order.
Seed 4 has 2 orders per customer with identical ordered_at. Without an order_id tiebreaker, the returned row is nondeterministic.
Return the 2nd highest distinct salary value.
OFFSET 1 LIMIT 1 returns the top salary when it's tied. RANK skips past the tie. DENSE_RANK is the only correct choice.
For each class, return all students whose score is in the top 3 including anyone tied at 3rd.
Seed 2 has 4 students tied at 3rd place in 1 class. ROW_NUMBER drops 3 of them; RANK with WHERE rnk <= 3 returns all 4 correctly.
For each class, return exactly 3 students even when scores tie. Break ties on student_id ASC.
RANK or DENSE_RANK can return more than 3 rows under ties. Explicit tiebreaker policy is required, and the grader checks the alphabetical fallback.
Find the department with the 3rd highest average salary.
2 departments may tie at the top. RANK would skip to 3 and miss the actual 3rd distinct value; DENSE_RANK lands on it. Aggregate first, rank second.
For each rep, return their percent rank across all reps (0 to 1, where 1 is top performer).
PERCENT_RANK returns (rank - 1) / (n - 1). With 1 row, this is 0/0 = NULL; the grader's last seed is a 1-row partition that exposes this.
Rank candidates by technical DESC, then behavioral DESC, then years_exp DESC. One row per rank.
Seed 7 has 2 candidates identical on all 3 criteria. The grader requires a 4th-level tiebreaker on candidate_id; without it, the ranking is unstable.
Return the latest event per user. 'Latest' = event_time DESC, then ingested_at DESC (in case of duplicate emissions).
Seed 1 has events that arrived in the wrong order: ingested_at is after event_time by hours. Using ingested_at as the primary ORDER BY ranks them incorrectly.
How often ranking functions appear in interviews
Ranking function FAQ
When does it actually matter which ranking function I pick?+
Why does ROW_NUMBER return different rows on different runs?+
What's the right function for 'find the Nth highest distinct salary'?+
Can I rank without PARTITION BY?+
What's PERCENT_RANK used for?+
What's the difference between RANK and DENSE_RANK with ties?+
Open problem 1 (dedup pattern)
- 01
Active recall beats re-reading by 50%
Cognitive-science meta-reviews (Dunlosky et al., 2013) rank practice testing as a top-tier study technique, while re-reading and highlighting rank near the bottom
- 02
76% of hiring managers reject on the coding task, not the resume
From HackerRank's 2024 Developer Skills Report. Candidates who look strong on paper still fail the live screen if they haven't done timed, executable practice
- 03
Five problem shapes cover 80% of data engineer loops
Dedup, sessionization, top-N-per-group, slowly-changing dimensions, partition tricks. Writing the shapes by hand turns the unfamiliar into pattern recognition