SQL RANK() Function: Ties and PARTITION BY
You're going to learn RANK once and then spend your whole career choosing between it and ROW_NUMBER and DENSE_RANK. That's the real skill. The syntax takes ten minutes. Deciding which one to reach for takes a few hundred reps, and nobody walks you through the decision until you've already bombed one interview on it.
RANK() Syntax
RANK is a window function. It requires an OVER clause with ORDER BY. PARTITION BY is optional. Without PARTITION BY, the entire result set is treated as one group.
RANK() Syntax
RANK() OVER (
[PARTITION BY partition_column]
ORDER BY sort_column [ASC | DESC]
)
-- Basic RANK: global ranking by score
SELECT
student_name,
exam_score,
RANK() OVER (ORDER BY exam_score DESC) AS score_rank
FROM exam_results;
-- RANK with PARTITION BY: ranking per department
SELECT
department,
employee_name,
salary,
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_salary_rank
FROM employees;RANK() Syntax Parts
PARTITION BY (optional)
Divides the result set into groups. RANK restarts at 1 for each group. Without PARTITION BY, all rows belong to a single group. You can partition by one or more columns. PARTITION BY department creates one ranking per department. PARTITION BY department, fiscal_year creates one ranking per department-year combination.
ORDER BY (required)
Defines the ranking criteria. ORDER BY salary DESC ranks the highest salary as 1. ORDER BY created_at ASC ranks the oldest record as 1. You must include ORDER BY; without it, most engines throw an error. SQL Server allows omitting it but returns nondeterministic results, which is never what you want.
How RANK Handles Ties
If you remember nothing else from this page, remember that ties are the entire point of RANK. The formula is simple: next rank equals current rank plus number of tied rows.
| Name | Score | RANK() |
|---|---|---|
| Alice | 95 | 1 |
| Bob | 90 | 2 |
| Carol | 90 | 2 |
| Dave | 85 | 4 |
| Eve | 80 | 5 |
Tie Behavior with RANK()
SELECT
student_name,
score,
RANK() OVER (ORDER BY score DESC) AS rnk
FROM exam_results;
-- Alice 95 1
-- Bob 90 2
-- Carol 90 2
-- Dave 85 4 <-- rank 3 is skipped
-- Eve 80 5Key insight: Bob and Carol both score 90 and both get rank 2. Dave gets rank 4, not 3. Rank 3 does not exist because positions 2 and 3 were both consumed by the tie. This is the gap that interviewers ask about.
RANK with PARTITION BY
PARTITION BY creates independent ranking windows. Each partition starts its ranking at 1. Without PARTITION BY, rows only compete against rows globally.
| Department | Name | Salary | RANK() |
|---|---|---|---|
| Engineering | Alice | $150,000 | 1 |
| Engineering | Bob | $140,000 | 2 |
| Engineering | Carol | $140,000 | 2 |
| Engineering | Dave | $130,000 | 4 |
| Marketing | Eve | $120,000 | 1 |
| Marketing | Frank | $115,000 | 2 |
| Marketing | Grace | $110,000 | 3 |
RANK with PARTITION BY
SELECT
department,
employee_name,
salary,
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_rank
FROM employees;
-- Multiple PARTITION BY columns
SELECT
department,
fiscal_year,
employee_name,
sales_total,
RANK() OVER (
PARTITION BY department, fiscal_year
ORDER BY sales_total DESC
) AS yearly_sales_rank
FROM annual_sales;When to Use RANK()
RANK is the right choice when ties should share a position and the gap after ties communicates meaningful information.
Competition-Style Leaderboards
When two contestants tie for 2nd place, there should be no 3rd place. The next contestant gets 4th. This is exactly what RANK does. The gap communicates that positions were consumed by the tie. Sports leaderboards, sales contests, and any ranking where ties share a position use RANK.
Percentile and Threshold Analysis
RANK is useful when you want to know the exact position of a row relative to all others, including the effect of ties. If 10 people tie for 1st, the 11th person is rank 11, not rank 2. Use this when you need to answer questions like 'how many people scored higher than this row?'
Filtering with Awareness of Tie Counts
When you write WHERE rank <= 3, RANK might return more than 3 rows if ties exist at the boundary. This is a feature, not a bug. If two people tie for 3rd, both are included. This behavior is what interviewers test: they want to see that you understand the result set size is not guaranteed when using RANK with a filter.
RANK vs DENSE_RANK vs ROW_NUMBER
All three are ranking window functions. They only differ in how they handle ties. Picking the wrong one is a silent bug: the query runs, returns results, and the interviewer waits to see if you notice.
| Function | Ties? | Gaps? | Best For |
|---|---|---|---|
| RANK() | Yes | Yes | Competition rankings, positional analysis |
| DENSE_RANK() | Yes | No | Nth distinct value, consecutive numbering |
| ROW_NUMBER() | No | No | Deduplication, exactly one row per group |
3 RANK Interview Questions
These questions test whether you understand RANK beyond the textbook definition. Each includes what the interviewer is actually evaluating and how to structure your answer.
Explain the difference between RANK() and DENSE_RANK(). Give an example where they return different results.
Draw a concrete example with tied values. Show both outputs side by side. With scores 95, 90, 90, 85: RANK gives 1, 2, 2, 4. DENSE_RANK gives 1, 2, 2, 3. The gap in RANK means WHERE rnk = 3 returns nothing. WHERE dr = 3 with DENSE_RANK returns 85. Use DENSE_RANK when you need to address specific rank positions by number.
Write a query to find the top 2 highest-paid employees per department, including ties.
Use RANK() OVER (PARTITION BY department ORDER BY salary DESC) in a CTE, then filter WHERE rnk <= 2. Mention that the result may contain more than 2 rows per department if ties exist, and that this is the intended behavior.
A dashboard query uses RANK() but the stakeholder complains about 'missing' rank numbers. What is happening and how do you fix it?
RANK intentionally skips numbers after ties. If the business needs consecutive numbers, use DENSE_RANK. If the business needs exactly one row per rank, use ROW_NUMBER. Clarify the requirement before changing the query.
RANK and NULL Values
-- Control NULL placement explicitly
SELECT
employee_name,
salary,
RANK() OVER (
ORDER BY salary DESC NULLS LAST
) AS salary_rank
FROM employees;
-- NULLs are ranked together at the bottom
-- All NULL salary rows share the same rankBest practice: Always use NULLS LAST or NULLS FIRST explicitly when your ORDER BY column might contain NULLs. In engines that do not support this syntax (like older MySQL versions), use COALESCE to replace NULLs with a sentinel value.
SQL RANK Function FAQ
What does the RANK function do in SQL?+
How does RANK handle ties?+
Can you use RANK without PARTITION BY?+
A dozen reps and you'll stop second-guessing
- 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