PARTITION BY & ORDER BY in OVER
Split the window into one partition per player with PARTITION BY, and make calculations run in order with ORDER BY.
OVER() treats the whole table as one window. Most real questions are "per something": the total per player, the rank within each team, the running total per player by date. Two clauses inside OVER give you that control.
Definition
PARTITION BY divides the rows into independent groups called partitions. The window function is calculated separately inside each partition and restarts for the next one. Unlike GROUP BY, the rows are not collapsed.
ORDER BY (inside OVER) sorts the rows within each partition and tells the window function the order in which to process them. For aggregate functions it also changes the default frame, so the result becomes cumulative (a running total).
- Partition — like a GROUP BY group, but every row stays visible. Think of colour bands: all Kohli rows in one band, all Rohit rows in another.
- Restarts — Rohit's total does not include Kohli's runs. Each partition has its own calculation.
- ORDER BY inside OVER is different from the query's final ORDER BY: it only controls the calculation, not the display order of the result.
Syntax
sql
window_function(expression) OVER (
PARTITION BY column1 [, column2 ...]
ORDER BY column3 [ASC | DESC] [, ...]
)| Option | Effect |
|---|---|
| no PARTITION BY | one partition = the whole result set |
PARTITION BY player | one partition per player |
PARTITION BY team, MONTH(match_date) | one partition per team and month (combination) |
| no ORDER BY (aggregates) | every row sees the whole partition → same value repeated |
ORDER BY match_date | rows processed by date → value grows row by row |
Example — PARTITION BY: a total per player
sql
SELECT id, player, runs,
SUM(runs) OVER (PARTITION BY player) AS player_total
FROM innings;| id | player | runs | player_total |
|---|---|---|---|
| 1 | Virat Kohli | 82 | 214 |
| 2 | Virat Kohli | 59 | 214 |
| 3 | Virat Kohli | 73 | 214 |
| 4 | Rohit Sharma | 45 | 121 |
| 5 | Rohit Sharma | 76 | 121 |
| 6 | Rohit Sharma | 0 | 121 |
| 7 | MS Dhoni | 26 | 71 |
| 8 | MS Dhoni | NULL | 71 |
| 9 | MS Dhoni | 45 | 71 |
| 10 | Jos Buttler | 73 | 239 |
| 11 | Jos Buttler | 59 | 239 |
| 12 | Jos Buttler | 107 | 239 |
Step by step: (1) split the rows by player into 4 partitions; (2) SUM inside each partition; (3) write the partition's total on each of its rows. Each player gets their own total, and all 12 rows are kept.
Example — PARTITION BY + ORDER BY: running total per player
sql
SELECT id, player, match_date, runs,
SUM(runs) OVER (PARTITION BY player ORDER BY match_date) AS running
FROM innings;| id | player | match_date | runs | running |
|---|---|---|---|---|
| 1 | Virat Kohli | 2025-03-22 | 82 | 82 |
| 2 | Virat Kohli | 2025-03-29 | 59 | 141 |
| 3 | Virat Kohli | 2025-04-05 | 73 | 214 |
| 4 | Rohit Sharma | 2025-03-23 | 45 | 45 |
| 5 | Rohit Sharma | 2025-03-30 | 76 | 121 |
| 6 | Rohit Sharma | 2025-04-06 | 0 | 121 |
| 7 | MS Dhoni | 2025-03-24 | 26 | 26 |
| 8 | MS Dhoni | 2025-03-31 | NULL | 26 |
| 9 | MS Dhoni | 2025-04-07 | 45 | 71 |
| 10 | Jos Buttler | 2025-03-25 | 73 | 73 |
| 11 | Jos Buttler | 2025-04-01 | 59 | 132 |
| 12 | Jos Buttler | 2025-04-08 | 107 | 239 |
Step by step: (1) partition by player; (2) sort each partition by date; (3) add up row by row — each row's value is "all runs so far". Dhoni's NULL innings adds nothing, so his running total stays 26. The last row of each partition equals the player's full total.
Why does ORDER BY make the total grow? When an aggregate window has ORDER BY but no frame, SQL uses the default frame from the first row of the partition up to the current row. Without ORDER BY the frame is the entire partition. Frames are explained in the next topic.
Common mistakes
- ❌ Thinking
PARTITION BYreduces rows like GROUP BY → ✅ it only defines groups for the calculation; every row stays. - ❌ Adding
ORDER BYto OVER "just to make it neat" → ✅ for SUM/AVG/COUNT it changes the result from a total to a running total. - ❌ Relying on OVER's ORDER BY to sort the output → ✅ add a final
ORDER BYto the query if the display order matters. - ❌ Running total ordered by a column with ties (two rows on the same date) → ✅ add a tie-breaker (
ORDER BY match_date, id) and use a ROWS frame (next topic).
Interview tip
Q: What is the difference between PARTITION BY and GROUP BY? A: Both form groups, but GROUP BY returns one row per group, while PARTITION BY (inside OVER) keeps every row and only restarts the window calculation for each group. You can show each innings and its player's total side by side only with PARTITION BY.
Key points to remember
- PARTITION BY splits rows into partitions; the window function restarts in each one, and no rows are removed.
- No PARTITION BY = the whole result set is a single partition.
- You can partition by several columns, e.g. PARTITION BY team, MONTH(match_date).
- ORDER BY inside OVER sets the order of calculation — not the display order of the result.
- An aggregate with ORDER BY (and no frame) becomes cumulative: a running total.
- Without ORDER BY an aggregate window sees the entire partition, so the same value repeats on each row.
Try it yourself
Total per player with PARTITION BY
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Running total per player (PARTITION BY + ORDER BY)
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Practice: running total per team
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Revision Flashcards
Tap a card to flip it and see the answer.
