Imagine you're working as a waiter (or a barista, if you're into coffee) in a big restaurant. Every day, you sum up your tips. But here's the thing: the restaurant is split into zones, and you're curious how much tips you made in each zone separately. PARTITION BY is what SQL uses to "split the restaurant into zones."
More formally, PARTITION BY is used in window functions to split all the rows in a table into separate groups (or "partitions"). Inside each group, the window function runs again. It's like you're applying the function separately in each "partition."
Example: how it works
Let's say we have a sales table with sales data:
| region | salesperson | amount |
|---|---|---|
| North | Alice | 100 |
| North | Bob | 200 |
| South | Alice | 150 |
| South | Charlie | 250 |
If we want to calculate how much money each salesperson made, but separately for each region, PARTITION BY is exactly what we need.
Syntax of PARTITION BY
The syntax is pretty simple:
window_function() OVER (PARTITION BY column_or_columns)
window_function()— for example,SUM(),AVG(),ROW_NUMBER(), and so on.PARTITION BY column— tells SQL which column to use to split the rows.OVER()— this operator tells SQL: "Do something within the specified window."
Example: sum by groups
Let's calculate the total sales for each region:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales;
The result will look like this:
| region | salesperson | amount | total_sales_by_region |
|---|---|---|---|
| North | Alice | 100 | 300 |
| North | Bob | 200 | 300 |
| South | Alice | 150 | 400 |
| South | Charlie | 250 | 400 |
What's happening here? SQL splits the rows into groups by the region column (North and South), then applies the SUM() function separately for each group. As a result, the rows inside the "North" group all get the same sum value, and the rows inside the "South" group get another.
Examples of using PARTITION BY
Let's check out how PARTITION BY can be useful in real-world tasks.
Example 1: Ranking inside a group
Suppose we want to rank salespeople within each region by their sales amount. For this, we can use a combo of PARTITION BY and the RANK() function:
SELECT
region,
salesperson,
amount,
RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_in_region
FROM sales;
Result:
| region | salesperson | amount | rank_in_region |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
The RANK() function assigns a rank within each region group, starting from 1. Notice that for each group, the ranks start from one.
Example 2: Comparing each value to the group average
Let's say we want to see how much each salesperson made compared to the average in their region. We'll use AVG():
SELECT
region,
salesperson,
amount,
AVG(amount) OVER (PARTITION BY region) AS avg_sales_by_region,
amount - AVG(amount) OVER (PARTITION BY region) AS diff_from_avg
FROM sales;
Result:
| region | salesperson | amount | avg_sales_by_region | diff_from_avg |
|---|---|---|---|---|
| North | Alice | 100 | 150 | -50 |
| North | Bob | 200 | 150 | 50 |
| South | Alice | 150 | 200 | -50 |
| South | Charlie | 250 | 200 | 50 |
First, SQL splits the rows into groups by region. Then it calculates the average AVG(amount) for each group. Finally, for each row, it calculates the difference between its value and the average.
Example 3: Row numbering inside a group
Say you want to number all transactions inside each region group. Let's use ROW_NUMBER():
SELECT
region,
salesperson,
amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_number
FROM sales;
Result:
| region | salesperson | amount | row_number |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
Comparing with GROUP BY
People often get confused between PARTITION BY and GROUP BY. Let's compare them:
GROUP BY
GROUP BY changes the structure of the output — it turns table rows into aggregates. For example:
SELECT
region,
SUM(amount) AS total_sales
FROM sales
GROUP BY region;
Result:
| region | total_sales |
|---|---|
| North | 300 |
| South | 400 |
Here we lose info about the salespeople, since the data is aggregated.
PARTITION BY
PARTITION BY, on the other hand, doesn't change the structure. We still see every row, but now we have extra values calculated by group. So, PARTITION BY lets you aggregate without losing details.
Common mistakes when using PARTITION BY
Mistake 1: Forgot PARTITION BY
Sometimes you want to group data, but forget to use PARTITION BY. For example:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER () AS total_sales
FROM sales;
Result:
| region | salesperson | amount | total_sales |
|---|---|---|---|
| North | Alice | 100 | 700 |
| North | Bob | 200 | 700 |
| South | Alice | 150 | 700 |
| South | Charlie | 250 | 700 |
Here, SUM(amount) is calculated for the whole table, not separately for each region. If you want to take regions into account, don't forget to specify PARTITION BY region.
Mistake 2: Wrong order in ORDER BY
The order of rows inside the window matters for functions like RANK() or ROW_NUMBER(). Be careful when you use ORDER BY inside OVER().
GO TO FULL VERSION