Why interviewers love RANK(): it checks whether you know the difference between ordering rows and numbering them when ties appear.
Question: What does RANK() do in SQL?
Answer: RANK() is a window function that assigns a position to each row after sorting it. If two rows have the same sort value, they share the same rank. The next row then skips ahead, so ties create gaps.
Interview-Ready Answer: I use RANK() when I need ordered results but want equal values to share the same position. It’s a window function, so it keeps every row and calculates the rank over a sorted set, often within each group using PARTITION BY. The key detail is that ties create gaps: if two rows tie for 1st, the next row is 3rd. If I want no gaps, I’d use DENSE_RANK(); if I want unique row numbers, I’d use ROW_NUMBER().
RANK() isA window function is a function that computes a value across a related set of rows without collapsing them the way GROUP BY does. RANK() belongs to the ranking family: it tells you where a row stands inside an ordered list, and rows with the same ordering value are treated as peers (rows that compare equal for the window ORDER BY).
PARTITION BY.ORDER BY clause. This sort is the expensive part.RANK() vs the other ranking functions| Function | Ties | Gaps | Best use |
|---|---|---|---|
RANK() | Same rank | Yes | Podiums, ties matter |
DENSE_RANK() | Same rank | No | Compact ranking |
ROW_NUMBER() | None | No | Unique sequence |
Ranking usually costs about the same as a sort: roughly O(n log n) for the sort plus O(n) to walk the rows. If you partition first, the cost becomes the sum of each partition’s sort cost. Large partitions can spill to disk if the engine’s memory grant is too small, which is when a query that looked fine in dev starts taking seconds in production.
ORDER BY; that changes the definition of “equal.”WHERE, so you filter ranks in a subquery or CTE.ORDER BY, ranking is either not allowed or not useful, depending on the database.Imagine a sales dashboard for a subscription company. Every month, the business wants the top 10 reps per region, and if two reps tie on revenue, they should share the same place on the leaderboard.
Using RANK() lets the dashboard show fair standings: two reps tied at $50,000 both get rank 1, and the next rep gets rank 3. That is exactly the kind of detail executives notice.
What goes wrong: a developer swaps in ROW_NUMBER() because it “also numbers rows.” Suddenly tied reps get different positions, one rep appears to beat another even though their numbers are equal, and the monthly report no longer matches finance. The symptom is a support ticket like “why did the leaderboard change when sales did not?” and the bug shows up as conflicting totals between the dashboard and the exported report.
-- Example: ranking students within each class by score.
-- Ties share the same rank, and the next rank is skipped.
-- PostgreSQL syntax; also works in many SQL databases that support window functions.
WITH scores(student, class_name, score) AS (
VALUES
('Alice', 'Math', 98),
('Bob', 'Math', 95),
('Cara', 'Math', 95),
('Dan', 'Math', 91),
('Eve', 'Science', 88),
('Finn', 'Science', 88),
('Gina', 'Science', 84),
('Hana', 'Science', 79)
)
SELECT
class_name,
student,
score,
RANK() OVER (
PARTITION BY class_name
ORDER BY score DESC
) AS score_rank
FROM scores
ORDER BY class_name, score_rank, student;
-- Edge case / best practice: if you need "top 2 per class including ties",
-- filter the ranked result in a subquery or CTE.
-- Window functions are not allowed directly in WHERE in most databases.
WITH scores(student, class_name, score) AS (
VALUES
('Alice', 'Math', 98),
('Bob', 'Math', 95),
('Cara', 'Math', 95),
('Dan', 'Math', 91),
('Eve', 'Science', 88),
('Finn', 'Science', 88),
('Gina', 'Science', 84),
('Hana', 'Science', 79)
), ranked AS (
SELECT
class_name,
student,
score,
RANK() OVER (
PARTITION BY class_name
ORDER BY score DESC
) AS score_rank
FROM scores
)
SELECT
class_name,
student,
score,
score_rank
FROM ranked
WHERE score_rank <= 2
ORDER BY class_name, score_rank, student;Follow-up & Tricky Questions:
RANK() different from DENSE_RANK()? Both give the same rank to ties, but RANK() leaves gaps and DENSE_RANK() does not. If the next position after a tie must be consecutive, use DENSE_RANK().RANK() in a CTE or subquery, then filter WHERE rank <= 3. That keeps the window logic separate from the filter step.RANK() in WHERE? Usually no, because WHERE runs before window functions are computed. Use a subquery, CTE, or a database feature like QUALIFY if your engine supports it.PARTITION BY change? It restarts ranking for each group, such as each department or each month. Without it, the ranking covers the whole result set.RANK() guarantee a unique order? No. Ties intentionally share the same rank, so if you need a single winner, use ROW_NUMBER() or add a separate business rule.ORDER BY, do ties still share a rank? Not usually. The extra sort column becomes part of the ranking key, so rows that used to tie may no longer be peers.RANK() zero-based? No, ranking starts at 1. The first row in each partition gets rank 1.RANK().Common Mistakes:
ROW_NUMBER() when ties should share a place. Fix: use RANK() or DENSE_RANK() depending on whether you want gaps.WHERE. Fix: compute the rank in a subquery or CTE, then filter outside it.ORDER BY. Fix: only rank by the business metric if equal values must stay tied.PARTITION BY when you need per-group ranking. Fix: partition by the category, department, or date bucket you want to restart within.Memory Hook: Think of a podium: tied athletes share the same medal, and the next place is skipped. RANK() is the “shared medal, skipped seat” rule.
Cheat Sheet:
RANK() = same value, same rank.PARTITION BY to rank inside groups.WHERE.DENSE_RANK() for no gaps, ROW_NUMBER() for unique numbering.Practice Tasks:
RANK(), DENSE_RANK(), and ROW_NUMBER() to see the difference.