Introduction to Window Functions
Add aggregated or ranked values to every row — without collapsing anything
The OVER() Clause
Window functions compute a value across a set of related rows without collapsing them — unlike GROUP BY which merges rows into groups. Every original row stays in the output and gets an additional computed column. Here's how the OVER() clause works:
SUM(total) OVER () adds a grand total column to every single row, letting you see each row's value alongside the overall total.SUM(total) OVER (PARTITION BY category) computes category totals while keeping every individual row intact. Like GROUP BY but without losing the row-level detail.SUM(total) OVER (ORDER BY date) creates a running total that accumulates row by row. Add PARTITION BY to get running totals that reset per group.How Window Functions Work
Calculate across rows without collapsing them
- Add computed columns WITHOUT GROUP BY collapsing
- OVER() defines the window: PARTITION BY + ORDER BY
- Each row sees a "frame" of related rows
- Runs after WHERE, GROUP BY, HAVING — just before ORDER BY
The Window Concept
Think of a sliding frame over your data
Imagine looking through a window at your sorted data — the function computes over whatever the window reveals.
- PARTITION BY = divide rows into groups (like GROUP BY but no collapse)
- ORDER BY = define sequence within each partition
- Frame = which rows in the partition are visible to the function
- Default frame: UNBOUNDED PRECEDING to CURRENT ROW
Query Execution Order
Where window functions fit
Window vs Aggregate
Key difference
When to Use Windows
Classic scenarios
- Running totals and moving averages
- Rank within groups (top N per category)
- Compare current row to previous/next
- Percent of total calculations
- Deduplication with ROW_NUMBER
OVER ()Window = all rowsPARTITION BYSplits into groups without collapsingORDER BY in OVERRow ordering for running calculations| customer_id | order_date | total |
|---|---|---|
| C1001 | 2024-01-15 | 59.98 |
| C1001 | 2024-02-20 | 12.5 |
| C1002 | 2024-02-08 | 449 |
| id | name | category | price | stock |
|---|---|---|---|---|
| P101 | Wireless Mouse | Electronics | 24.99 | 150 |
| P102 | Office Desk | Furniture | 189 | 25 |
| P103 | LED Desk Lamp | Furniture | 45.5 | 80 |
| P104 | USB-C Cable | Electronics | 12.99 | 500 |
| P105 | Bluetooth Speaker | Electronics | 79 | 60 |
| P106 | Office Chair | Furniture | 95 | 15 |
| P107 | Coffee Maker | Appliances | 89.99 | 40 |
| P108 | Notebook Set | Stationery | 8.5 | 200 |
| P109 | Wireless Headphones | Electronics | 159 | 30 |
| P110 | Standing Desk | Furniture | 425 | 8 |
Worked Example
Show each order with a running total per customer.
| customer_id | order_date | total | running_total |
|---|---|---|---|
| C1001 | 2024-01-15 | 59.98 | 59.98 |
| C1001 | 2024-02-20 | 12.5 | 72.48 |
| C1002 | 2024-02-08 | 449 | 449 |
Key Concepts
Pro Tip
GROUP BY forces you to collapse rows — you lose detail. Window functions keep every row while adding aggregated context. This is the most powerful analytical feature in SQL.
When to Use
Running totals, moving averages, ranking within categories, comparing each row to its group average, cumulative distributions.
Show each product with its name, category, price, and the average price within its category. Name the column category_avg_price (rounded to 2 decimals).