If you’re a data engineer, analyst, or backend developer, you’ve likely encountered scenarios where aggregate functions like SUM(), AVG(), or COUNT() just aren’t enough. Enter Window Functions – arguably one of SQL’s most powerful and elegant features.
Despite being a game-changer, window functions are often underused and misunderstood. In this blog, we’ll break down what makes them so powerful, walk through practical use cases, and prepare you for common interview questions that often catch candidates off guard.
What Are Window Functions?
Window functions perform calculations across a set of table rows that are somehow related to the current row. Unlike aggregate functions, they don’t collapse rows – instead, they augment each row with aggregated or ranked data.
Basic Syntax:
sql
<window_function>() OVER (
[PARTITION BY column]
[ORDER BY column]
[ROWS BETWEEN ...]
)
Why Are Window Functions Beautiful?
Here’s what makes them shine:
1. Row-Level Aggregation Without Losing Granularity
Unlike GROUP BY, which aggregates and reduces rows, window functions allow you to keep each row intact while adding a calculated field.
2. Flexible Partitioning and Ordering
With PARTITION BY, you can segment your dataset for localized calculations. ORDER BY allows you to control ranking, running totals and more.
3. Versatility Across Use Cases
You can rank rows, compute rolling averages, find first/last values, calculate time differences, and more – all without subqueries or joins.
Common Window Functions
1. ROW_NUMBER()
Assigns a unique sequential number to rows within a partition. e.g.
sql
SELECT
user_id,
ROW_NUMBER() OVER (PARTITION BY country ORDER BY signup_date) AS rank
FROM users;
2. RANK() / DENSE_RANK()
Ranks rows but handles ties differently.
RANK()skips numbers on ties.DENSE_RANK()does not.
sql
SELECT
department,
name,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_salary,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank_salary
FROM employees
ORDER BY department, rank_salary;
3. SUM( ) / AVG( ) / COUNT( )
Calculate rolling or partitioned aggregates.
sql
SELECT
user_id,
country,
SUM(purchase_amount) OVER (PARTITION BY country) AS total_by_country
FROM purchases;
4. LAG() / LEAD()
Access data from previous or following rows.
sql
SELECT
user_id,
purchase_date,
LAG(purchase_date) OVER (PARTITION BY user_id ORDER BY purchase_date) AS previous_purchase
FROM purchases;
5. FIRST_VALUE() / LAST_VALUE()
Get the first/last value in an ordered partition.
sql
SELECT
product_id,
FIRST_VALUE(price) OVER (PARTITION BY category ORDER BY created_at) AS initial_price
FROM products;
Real-Life Use Cases
1. Running Totals and Moving Averages
sql
SUM(sales) OVER (PARTITION BY region ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
2. Time to Next Event
sql
LEAD(event_timestamp) OVER (PARTITION BY user_id ORDER BY event_timestamp)
3. Customer Behavior: First/Last Purchase
sql
FIRST_VALUE(purchase_date) OVER (PARTITION BY user_id ORDER BY purchase_date)
4. Deduplication
Use ROW_NUMBER() to retain only the first instance of duplicates.
Interview Questions on Window Functions
These often appear in interviews for data engineers, analysts, and even backend devs. Here are some frequently asked questions, ranging from beginner to advanced:
Beginner Level
Q1: What is the difference between ROW_NUMBER(), RANK(), and DENSE_RANK()?
A: All three assign a ranking based on order, but handle duplicates differently. ROW_NUMBER() gives unique ranks; RANK() skips ranks on duplicates; DENSE_RANK() doesn’t.
Q2: When would you use PARTITION BY in a window function?
A: When you want to compute the window function within groups, like computing the rank of products by category or computing total spend by customer.
Intermediate Level
Q3: Write a query to find the second highest salary for each department.
A:
sql
SELECT *
FROM (
SELECT *,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rnk
FROM employees
) sub
WHERE rnk = 2;
Q4: What is the difference between GROUP BY and PARTITION BY?
A: GROUP BY collapses rows into groups; PARTITION BY keeps all rows but computes aggregates within partitions.
Advanced Level
Q5: Write a query to calculate a 7-day moving average of sales.
A:
sql
SELECT
date,
product_id,
AVG(sales) OVER (
PARTITION BY product_id
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS moving_avg_7d
FROM sales_data;
Q6: How would you get the first value after a condition is met?
A: This usually requires combining FILTER, CASE, or a LAG()/LEAD() with window functions.
Tips to Master Window Functions
- Visualize the data – It helps to draw or print the table and understand the movement across rows.
- Start with
ROW_NUMBER()andSUM()– They’re the easiest to test and understand. - Use
PARTITION BY+ORDER BYconsciously – They dramatically change the output. - Practice with real datasets – Kaggle datasets or your company’s analytics tables are great playgrounds.
Conclusion
Window functions aren’t just syntactic sugar — they’re power tools that allow you to express complex logic clearly and efficiently. Whether you’re calculating rolling averages, ranking rows, or analyzing user behavior over time, window functions provide you with an unmatched level of control.
So next time you’re faced with a tough SQL problem, don’t jump straight into subqueries or joins. Consider the beauty – and power – of window functions.
Want to Go Deeper?
I’ll be publishing a follow-up post soon with window function challenges and solutions – subscribe or follow me on LinkedIn to stay updated.
Have a favorite use case for window functions? Drop it in the comments!
