SQL is powerful. But sometimes, just filtering and grouping data isn’t enough. There’s a secret weapon in SQL’s toolbox — Window Functions. And they get super useful when you mix them with PARTITION BY.
TLDR:
Window Functions let you do calculations across rows related to the current row. Adding PARTITION BY breaks the data into smaller chunks — like grouping, but still keeping all rows. It’s amazing for ranking, running totals, and comparing rows. Once you get the hang of it, your SQL powers will level up fast!
What’s a Window Function?
A Window Function does some math on a bunch of rows — but without collapsing them into one result like GROUP BY does. You still get all the rows, but now each row can “see” other rows around it. That’s pretty wild.
Okay, But What About PARTITION BY?
PARTITION BY is like a cousin of GROUP BY. It splits your data into little sections, or partitions. Then the window function runs inside each of those.
Think of it like this: You’re running a raffle in a school, and each classroom gets its own prize winner. That’s what partitioning does — it keeps things fair by grouping the right rows together before doing the math.
Without PARTITION BY, the window function treats all rows the same. Adding it adds structure and meaning.
Time for an Example
Here’s a basic SQL table that we will use. It’s called sales:
id | salesperson | region | amount ----+--------------+---------+-------- 1 | Alice | West | 500 2 | Bob | West | 700 3 | Carl | East | 300 4 | Dana | East | 400 5 | Erin | West | 200
Goal:
Let’s calculate the total sales per region but still keep all rows. GROUP BY can’t do this — it would smush rows together.
With a Window Function:
SELECT salesperson, region, amount, SUM(amount) OVER(PARTITION BY region) AS region_total FROM sales;
Now we get:
salesperson | region | amount | region_total ------------+--------+--------+-------------- Alice | West | 500 | 1400 Bob | West | 700 | 1400 Erin | West | 200 | 1400 Carl | East | 300 | 700 Dana | East | 400 | 700
Pretty, right? Each person sees their region’s total — thanks to PARTITION BY. 💪
Other Handy Window Functions
Let’s take a look at some common ones:
ROW_NUMBER()— Gives each row a unique number per partitionRANK()— Similar toROW_NUMBER, but tied rows get the same rankDENSE_RANK()— LikeRANK(), but no rank gapsSUM(),AVG(),MAX(),MIN()— All work with partitionsLAG(),LEAD()— Let you peek at earlier or later rows
Rank Employees by Sales — Per Region
SELECT salesperson, region, amount, RANK() OVER(PARTITION BY region ORDER BY amount DESC) AS region_rank FROM sales;
salesperson | region | amount | region_rank ------------+--------+--------+------------- Bob | West | 700 | 1 Alice | West | 500 | 2 Erin | West | 200 | 3 Dana | East | 400 | 1 Carl | East | 300 | 2
Each region now has its own ranking chart!
Why Not Just Use GROUP BY?
Great question. The key difference is this:
GROUP BYmerges rows into one summaryPARTITION BYkeeps all rows, but adds context
So use GROUP BY when you want to shrink your table. Use PARTITION BY when you want to enrich your rows with info about their “data neighborhood”.
Peeking with LAG and LEAD
Want to compare someone’s sales to the person before or after them? Easy:
SELECT salesperson, region, amount, LAG(amount, 1) OVER(PARTITION BY region ORDER BY amount DESC) AS prev_sales FROM sales;
The LAG() function brings in the amount from the previous row (within the same region). Great for finding improvements or spotting drops.
And You Can Chain Them!
SELECT salesperson, region, amount, SUM(amount) OVER(PARTITION BY region) AS region_total, RANK() OVER(PARTITION BY region ORDER BY amount DESC) AS region_rank, LAG(amount, 1) OVER(PARTITION BY region ORDER BY amount DESC) AS prev_sales FROM sales;
Now you’ve got advanced analytics going on — all in one query. 🚀
Use Cases That’ll Blow Your Mind
- Find the top salesperson in each region? Easy.
RANK(). - Calculate running totals, resets per month? Yup.
SUM()+PARTITION BY month. - Compare performance this week vs last? Use
LAG(). - Want to know who improved the most? Compare amounts with
LAG()orLEAD().
One More Bonus Trick
Want to calculate someone’s percent of total sales in their region?
SELECT salesperson, region, amount, amount * 100.0 / SUM(amount) OVER(PARTITION BY region) AS percent_of_region FROM sales;
salesperson | region | amount | percent_of_region ------------+--------+--------+------------------- Alice | West | 500 | 35.71 Bob | West | 700 | 50.00 Erin | West | 200 | 14.29 Carl | East | 300 | 42.86 Dana | East | 400 | 57.14
Percentages per region — done in one line! Magic. ✨
In Summary
Window Functions with PARTITION BY are amazing. They let you:
- Keep all your data rows
- But add smart, calculated columns
- Give each row context from its “peers”
- Impress your coworkers 💼
Once you get it, it’s hard to imagine life before it. And trust us — your reports will never be the same.
Quick Reminders Before You Go:
GROUP BY= compress rowsPARTITION BY= organize data, not remove it- Always pair with
OVER()when using a window function - Ordering within each partition helps a lot
Now get out
