ROW_NUMBER, RANK, DENSE_RANK in PostgreSQL - Time & Space Complexity
When using ROW_NUMBER, RANK, or DENSE_RANK in SQL, it's important to understand how the time to get results grows as the data grows.
We want to know how the database handles sorting and numbering rows as the table gets bigger.
Analyze the time complexity of the following code snippet.
SELECT employee_id, department_id, salary,
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank
FROM employees;
This query assigns row numbers and ranks to employees within each department based on salary order.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Sorting rows within each department by salary.
- How many times: Once per department group, but overall it processes all rows.
As the number of employees (rows) grows, the database must sort more data within each department.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 log 10 (sorting 10 rows) |
| 100 | About 100 log 100 (sorting 100 rows) |
| 1000 | About 1000 log 1000 (sorting 1000 rows) |
Pattern observation: The work grows a bit faster than the number of rows because sorting takes more time as data grows.
Time Complexity: O(n log n)
This means the time to assign row numbers and ranks grows a little faster than the number of rows because sorting is involved.
[X] Wrong: "ROW_NUMBER and RANK just scan rows once, so time grows linearly with data size."
[OK] Correct: These functions require sorting rows within groups, which takes more time than a simple scan, so the time grows faster than just the number of rows.
Understanding how window functions like ROW_NUMBER and RANK scale helps you explain query performance clearly and shows you know how databases handle sorting and grouping.
"What if we removed the PARTITION BY clause? How would the time complexity change?"
Practice
ROW_NUMBER() function do in PostgreSQL?Solution
Step 1: Understand ROW_NUMBER() purpose
ROW_NUMBER() gives each row a unique sequential number, starting at 1.Step 2: Compare with RANK() and DENSE_RANK()
Unlike RANK() or DENSE_RANK(), ROW_NUMBER() never assigns the same number to multiple rows.Final Answer:
Assigns a unique sequential number to each row in the result set. -> Option DQuick Check:
ROW_NUMBER() = unique row numbers [OK]
- Confusing ROW_NUMBER with RANK or DENSE_RANK
- Thinking ROW_NUMBER skips numbers after ties
- Assuming ROW_NUMBER counts total rows
RANK() over a column named score ordered descending?Solution
Step 1: Recall correct RANK() syntax
RANK() is used with OVER() clause specifying ORDER BY column and direction.Step 2: Check each option
SELECT RANK() OVER (ORDER BY score DESC) FROM table_name; correctly uses RANK() OVER (ORDER BY score DESC). Others misuse syntax or clauses.Final Answer:
SELECT RANK() OVER (ORDER BY score DESC) FROM table_name; -> Option CQuick Check:
RANK() OVER (ORDER BY column DESC) syntax [OK]
- Putting column inside RANK() function
- Using PARTITION BY incorrectly
- Omitting OVER() clause
players with columns name and score, what will be the output of this query?SELECT name, score, RANK() OVER (ORDER BY score DESC) AS rank FROM players;
Data:
name | score
-----|------
Alice | 100
Bob | 90
Carol | 90
Dave | 80Solution
Step 1: Understand RANK() behavior with ties
RANK() assigns the same rank to tied scores and skips ranks after ties.Step 2: Apply ranks to scores
Alice has highest score 100 -> rank 1. Bob and Carol tie at 90 -> both rank 2. Next rank after tie skips to 4 for Dave.Final Answer:
[{"name": "Alice", "score": 100, "rank": 1}, {"name": "Bob", "score": 90, "rank": 2}, {"name": "Carol", "score": 90, "rank": 2}, {"name": "Dave", "score": 80, "rank": 4}] -> Option BQuick Check:
RANK() skips ranks after ties [OK]
- Assuming ranks after ties are consecutive
- Confusing RANK() with DENSE_RANK()
- Misordering rows by score
SELECT name, DENSE_RANK(score) OVER (ORDER BY score DESC) FROM players;
Solution
Step 1: Check DENSE_RANK() syntax
DENSE_RANK() does not take any arguments inside parentheses; it is called as DENSE_RANK() OVER (...).Step 2: Identify the error in the query
The query incorrectly uses DENSE_RANK(score), which causes a syntax error.Final Answer:
DENSE_RANK() should not have a column inside the parentheses. -> Option AQuick Check:
DENSE_RANK() has empty parentheses [OK]
- Putting column inside DENSE_RANK()
- Thinking PARTITION BY is mandatory
- Omitting OVER() clause
Options:
A)
ROW_NUMBER() because it gives unique numbers.B)
RANK() because it skips ranks after ties.C)
DENSE_RANK() because it assigns ranks without gaps after ties.D)
COUNT() because it counts rows.Solution
Step 1: Understand ranking requirements
The requirement is to assign ranks with no gaps even if employees tie in sales.Step 2: Match functions to behavior
ROW_NUMBER() gives unique numbers, ignoring ties. RANK() skips ranks after ties causing gaps. DENSE_RANK() assigns ranks without gaps after ties.Step 3: Choose correct function
DENSE_RANK() fits the requirement perfectly.Final Answer:
DENSE_RANK() because it assigns ranks without gaps after ties. -> Option AQuick Check:
DENSE_RANK() = no gaps in ranks [OK]
- Choosing RANK() and expecting no gaps
- Using ROW_NUMBER() which ignores ties
- Confusing COUNT() with ranking functions
Ready to prove it?
Pass the test and this pattern moves unknown
