What is the difference between RANK(), DENSE_RANK(), and ROW_NUMBER()?

Answer

These are three window functions that assign sequential numbers to rows, but they handle ties (duplicate values) differently.

Key Differences

FunctionHandles TiesGaps After TiesUse Case
ROW_NUMBER()Assigns unique numbersN/AUnique sequential numbering
RANK()Same rank for tiesCreates gapsTraditional ranking with gaps
DENSE_RANK()Same rank for tiesNo gapsContinuous ranking without gaps

Sample Data

CREATE TABLE students (
    student_id INT,
    name VARCHAR(100),
    score INT
);

INSERT INTO students VALUES 
(1, 'Alice', 95),
(2, 'Bob', 87),
(3, 'Charlie', 92),
(4, 'Diana', 87),    -- Tie with Bob
(5, 'Eve', 87),      -- Tie with Bob and Diana
(6, 'Frank', 78),
(7, 'Grace', 92);    -- Tie with Charlie

Function Comparisons

All Three Functions Together

SELECT 
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) as row_num,
    RANK() OVER (ORDER BY score DESC) as rank_func,
    DENSE_RANK() OVER (ORDER BY score DESC) as dense_rank_func
FROM students
ORDER BY score DESC, name;

-- Result:
-- name    | score | row_num | rank_func | dense_rank_func
-- Alice   | 95    | 1       | 1         | 1
-- Charlie | 92    | 2       | 2         | 2
-- Grace   | 92    | 3       | 2         | 2  (tie)
-- Bob     | 87    | 4       | 4         | 3  (RANK skips 3)
-- Diana   | 87    | 5       | 4         | 3  (tie)
-- Eve     | 87    | 6       | 4         | 3  (tie)
-- Frank   | 78    | 7       | 7         | 4  (RANK skips 5,6)

ROW_NUMBER()

Purpose: Assigns a unique sequential integer to each row.

Characteristics:

  • Always unique - No two rows get the same number
  • Deterministic with ORDER BY - Same order produces same numbers
  • Arbitrary for ties - Ties get different numbers (order depends on implementation)
-- Basic ROW_NUMBER usage
SELECT 
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) as position
FROM students;

-- ROW_NUMBER with PARTITION BY
SELECT 
    name,
    score,
    grade_level,
    ROW_NUMBER() OVER (PARTITION BY grade_level ORDER BY score DESC) as rank_in_grade
FROM students;

-- Pagination using ROW_NUMBER
SELECT name, score
FROM (
    SELECT 
        name, 
        score,
        ROW_NUMBER() OVER (ORDER BY score DESC) as rn
    FROM students
) ranked
WHERE rn BETWEEN 3 AND 5;  -- Get rows 3-5

RANK()

Purpose: Assigns the same rank to tied values, with gaps in subsequent rankings.

Characteristics:

  • Ties get same rank - Equal values receive identical ranks
  • Creates gaps - Next rank skips numbers equal to tie count
  • Traditional ranking - Like Olympic medal rankings
-- Basic RANK usage
SELECT 
    name,
    score,
    RANK() OVER (ORDER BY score DESC) as rank_position
FROM students;

-- Find top 3 unique scores (handles ties)
SELECT DISTINCT
    score,
    RANK() OVER (ORDER BY score DESC) as rank_position
FROM students
WHERE RANK() OVER (ORDER BY score DESC) <= 3;

-- Rank within departments
SELECT 
    employee_name,
    department,
    salary,
    RANK() OVER (PARTITION BY department ORDER BY salary DESC) as dept_rank
FROM employees;

DENSE_RANK()

Purpose: Assigns the same rank to tied values, without gaps in subsequent rankings.

Characteristics:

  • Ties get same rank - Equal values receive identical ranks
  • No gaps - Next rank is always consecutive
  • Continuous sequence - Like academic grading systems
-- Basic DENSE_RANK usage
SELECT 
    name,
    score,
    DENSE_RANK() OVER (ORDER BY score DESC) as dense_rank_position
FROM students;

-- Find students in top 3 score categories
SELECT name, score
FROM (
    SELECT 
        name, 
        score,
        DENSE_RANK() OVER (ORDER BY score DESC) as dr
    FROM students
) ranked
WHERE dr <= 3;  -- Gets all students with top 3 distinct scores

-- Quartile ranking
SELECT 
    name,
    score,
    DENSE_RANK() OVER (ORDER BY score DESC) as rank_pos,
    CASE 
        WHEN DENSE_RANK() OVER (ORDER BY score DESC) <= 2 THEN 'Top Tier'
        WHEN DENSE_RANK() OVER (ORDER BY score DESC) <= 4 THEN 'Middle Tier'
        ELSE 'Lower Tier'
    END as tier
FROM students;

Practical Use Cases

1. E-commerce Product Ranking

-- Product popularity ranking
SELECT 
    product_name,
    total_sales,
    ROW_NUMBER() OVER (ORDER BY total_sales DESC) as sales_position,
    RANK() OVER (ORDER BY total_sales DESC) as sales_rank,
    DENSE_RANK() OVER (ORDER BY total_sales DESC) as sales_category
FROM (
    SELECT 
        product_name,
        SUM(quantity * price) as total_sales
    FROM order_items oi
    JOIN products p ON oi.product_id = p.product_id
    GROUP BY product_name
) product_sales;

2. Employee Performance Evaluation

-- Performance ranking by department
SELECT 
    employee_name,
    department,
    performance_score,
    RANK() OVER (
        PARTITION BY department 
        ORDER BY performance_score DESC
    ) as dept_rank,
    DENSE_RANK() OVER (
        ORDER BY performance_score DESC
    ) as company_rank
FROM employee_performance
WHERE evaluation_year = 2024;

3. Sales Leaderboard

-- Monthly sales ranking
WITH monthly_sales AS (
    SELECT 
        salesperson_id,
        salesperson_name,
        SUM(sale_amount) as monthly_total
    FROM sales
    WHERE MONTH(sale_date) = MONTH(CURRENT_DATE)
    GROUP BY salesperson_id, salesperson_name
)
SELECT 
    salesperson_name,
    monthly_total,
    ROW_NUMBER() OVER (ORDER BY monthly_total DESC) as position,
    RANK() OVER (ORDER BY monthly_total DESC) as rank_with_ties,
    CASE 
        WHEN DENSE_RANK() OVER (ORDER BY monthly_total DESC) = 1 THEN 'Gold'
        WHEN DENSE_RANK() OVER (ORDER BY monthly_total DESC) = 2 THEN 'Silver'
        WHEN DENSE_RANK() OVER (ORDER BY monthly_total DESC) = 3 THEN 'Bronze'
        ELSE 'Participant'
    END as medal_category
FROM monthly_sales;

When to Use Each Function

Use ROW_NUMBER() when:

  • Need unique sequential numbers for each row
  • Implementing pagination
  • Breaking ties arbitrarily is acceptable
  • Creating unique identifiers for duplicate data
-- Pagination example
SELECT * FROM (
    SELECT 
        product_name,
        price,
        ROW_NUMBER() OVER (ORDER BY price DESC) as rn
    FROM products
) ranked
WHERE rn BETWEEN 21 AND 30;  -- Page 3 (10 items per page)

Use RANK() when:

  • Traditional ranking system is needed
  • Gaps after ties are acceptable/desired
  • Following sports/competition ranking conventions
  • Need to know how many items are better than current item
-- Olympic-style ranking
SELECT 
    athlete_name,
    score,
    RANK() OVER (ORDER BY score DESC) as olympic_rank,
    CASE 
        WHEN RANK() OVER (ORDER BY score DESC) = 1 THEN 'Gold Medal'
        WHEN RANK() OVER (ORDER BY score DESC) = 2 THEN 'Silver Medal'
        WHEN RANK() OVER (ORDER BY score DESC) = 3 THEN 'Bronze Medal'
        ELSE 'No Medal'
    END as medal
FROM competition_results;

Use DENSE_RANK() when:

  • Need continuous ranking without gaps
  • Categorizing into performance tiers
  • Academic grading systems
  • Want to know distinct performance levels
-- Academic grade categories
SELECT 
    student_name,
    final_score,
    DENSE_RANK() OVER (ORDER BY final_score DESC) as performance_level,
    CASE 
        WHEN DENSE_RANK() OVER (ORDER BY final_score DESC) <= 2 THEN 'A'
        WHEN DENSE_RANK() OVER (ORDER BY final_score DESC) <= 4 THEN 'B'
        WHEN DENSE_RANK() OVER (ORDER BY final_score DESC) <= 6 THEN 'C'
        ELSE 'D'
    END as letter_grade
FROM student_scores;

Performance Considerations

-- Ensure proper indexing for ranking functions
CREATE INDEX idx_students_score ON students(score DESC);

-- Efficient ranking query
SELECT 
    name,
    score,
    RANK() OVER (ORDER BY score DESC) as rank_pos
FROM students
WHERE score >= 80;  -- Filter before ranking when possible

Interview Tips

  • Remember: ROW_NUMBER() is always unique, RANK() has gaps, DENSE_RANK() has no gaps
  • Understand how each handles ties differently
  • Know when to use each function based on business requirements
  • Practice with examples that have multiple ties
  • Be able to explain the gap behavior in RANK() vs DENSE_RANK()

Test Your Knowledge

Take a quick quiz to test your understanding of this topic.

Test Your SQL Knowledge

Ready to put your skills to the test? Take our interactive SQL quiz and get instant feedback on your answers.