🔥 0
0
Lesson 3 of 6 18 min +200 XP

Aggregate Window Functions

You know SUM, AVG, COUNT with GROUP BY. Now use them with OVER() to create running totals, moving averages, and percentage calculations - all while keeping your rows intact.

Running Total

The most common use case: cumulative sum over time.

SELECT
  date,
  revenue,
  SUM(revenue) OVER (ORDER BY date) as running_total
FROM daily_sales;
date revenue running_total
2024-01-01 1000 1000
2024-01-02 1500 2500
2024-01-03 1200 3700
2024-01-04 1800 5500
2024-01-05 2000 7500

Running Total per Category

Add PARTITION BY to restart the running total for each group:

SELECT
  category,
  date,
  revenue,
  SUM(revenue) OVER (
    PARTITION BY category
    ORDER BY date
  ) as category_running_total
FROM sales;
category date revenue running_total
Electronics Jan 1 500 500
Electronics Jan 2 700 1200
Electronics Jan 3 600 1800
Clothing Jan 1 300 300
Clothing Jan 2 400 700

Notice: Running total resets for each category!

Percentage of Total

Calculate what percentage each row contributes:

SELECT
  department,
  employee,
  salary,
  ROUND(
    100.0 * salary / SUM(salary) OVER (), 2
  ) as pct_of_company,
  ROUND(
    100.0 * salary / SUM(salary) OVER (PARTITION BY department), 2
  ) as pct_of_dept
FROM employees;
department employee salary pct_of_company pct_of_dept
Engineering Alice 120000 24.00% 40.00%
Engineering Bob 100000 20.00% 33.33%
Engineering Carol 80000 16.00% 26.67%
Sales Dave 90000 18.00% 45.00%
Sales Eve 110000 22.00% 55.00%

Running Count

Track how many events have occurred:

SELECT
  date,
  new_customers,
  SUM(new_customers) OVER (ORDER BY date) as total_customers,
  COUNT(*) OVER (ORDER BY date) as day_number
FROM signups;
date new_customers total_customers day_number
2024-01-01 15 15 1
2024-01-02 22 37 2
2024-01-03 18 55 3

Running Average

Calculate the average of all values up to the current row:

SELECT
  date,
  temperature,
  ROUND(AVG(temperature) OVER (ORDER BY date), 1) as avg_temp_so_far
FROM weather_data;
date temperature avg_temp_so_far
Mon 72 72.0
Tue 75 73.5
Wed 68 71.7
Thu 70 71.3

The ORDER BY Effect

SUM(...) OVER ()

Total of ALL rows.
Same value for every row.

→ 1000 (every row)

SUM(...) OVER (ORDER BY date)

Running total up to current row.
Grows as you go down.

→ 100, 350, 600, 1000
Key Insight

Adding ORDER BY to a window function changes its behavior from "entire partition" to "running/cumulative" calculation. This is because ORDER BY implies a default frame of ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.

Combining Multiple Window Functions

SELECT
  date,
  product,
  revenue,
  -- Running total
  SUM(revenue) OVER (ORDER BY date) as running_total,
  -- Total (constant)
  SUM(revenue) OVER () as grand_total,
  -- Percentage of total
  ROUND(100.0 * revenue / SUM(revenue) OVER (), 2) as pct_of_total,
  -- Rank
  RANK() OVER (ORDER BY revenue DESC) as revenue_rank
FROM sales;

Real-World: YTD (Year-to-Date) Calculation

SELECT
  date,
  EXTRACT(YEAR FROM date) as year,
  revenue,
  SUM(revenue) OVER (
    PARTITION BY EXTRACT(YEAR FROM date)
    ORDER BY date
  ) as ytd_revenue
FROM daily_sales;
date year revenue ytd_revenue
2023-12-30 2023 1200 985000
2023-12-31 2023 1500 986500
2024-01-01 2024 2000 2000
2024-01-02 2024 1800 3800

YTD resets at the start of each year!

Key Takeaways

  • OVER() - Calculates across all rows (same result each row)
  • OVER(ORDER BY) - Running/cumulative calculation
  • OVER(PARTITION BY) - Restarts calculation per group
  • Percentage = value / SUM() OVER() - Common analytics pattern

Next up: Value Functions - LAG, LEAD, FIRST_VALUE, and LAST_VALUE for comparing rows.

🧠 Quick Quiz

Test your understanding of this lesson.

1

What does SUM(amount) OVER (ORDER BY date) calculate?

2

To calculate each employee's salary as a percentage of the department total, you would use:

3

AVG(value) OVER () without PARTITION BY or ORDER BY calculates:

Ranking Functions