๐Ÿ“Š Aggregate Power Unleashed: Using SUM(), AVG(), COUNT(), MIN(), and MAX() as Window Functions


๐Ÿš€ Why Windowed Aggregates Are a Game-Changer

"Xorthax wanted to track each customer's total purchases over timeโ€”without losing the detail of each individual transaction. That's where aggregate window functions shine."


๐Ÿค” What Are Aggregate Window Functions?

These are traditional aggregate functions (like SUM() or COUNT()) used with the OVER() clause so that they return a value for each row, instead of collapsing rows into groups.

You keep all your rows and get the aggregated insight across a window.


๐Ÿ”ง Syntax Snapshot: Building a Windowed Aggregate Step-by-Step

When you use an aggregate function like SUM() or AVG() in a window, you apply it across a moving or defined slice of dataโ€”not the whole table, and not a reduced group.

The full form looks like this:

SUM(column) OVER (
  PARTITION BY grouping_column   -- optional: split data into subgroups
  ORDER BY ordering_column       -- defines the row sequence in the window
  ROWS BETWEEN ...               -- optional: controls which rows are included
)

You can mix and match these pieces depending on what you're calculating:


๐Ÿ“‹ See It in Action

๐Ÿ“ˆ Cumulative Sum

OrderID CustomerID OrderDate Amount
1 A1 2024-01-01 100
2 A1 2024-01-05 150
3 A1 2024-01-10 200
SELECT OrderID, Amount, 
       SUM(Amount) OVER (PARTITION BY CustomerID ORDER BY OrderDate) AS RunningTotal
FROM table
OrderID Amount RunningTotal
1 100 100
2 150 250
3 200 450

๐Ÿ“‰ Moving Average (3-Row Window)

SELECT OrderID, Amount, 
       AVG(Amount) OVER (
           PARTITION BY CustomerID 
           ORDER BY OrderDate 
           ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
       ) AS MovingAvg
FROM table
OrderID Amount MovingAvg
1 100 100.0
2 150 125.0
3 200 150.0

๐Ÿงญ When Should You Use Windowed Aggregates?


๐Ÿง  Key Words That Hint You're in Aggregate Territory

Some phrases in a problem can be strong clues that an aggregate window function is appropriate:


โš ๏ธ Watch Out: Common Pitfalls


๐Ÿ“ Mastering Frame Clauses

A frame clause tells SQL which specific rows to include in the window when applying the aggregate function. It allows you to define a sliding or cumulative window relative to the current row.

By default, if you specify an ORDER BY in a window function, SQL might apply a default frame like RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWโ€”but for full control, you can specify it explicitly using ROWS BETWEEN or RANGE BETWEEN.

Example 1: Rolling 3-Row Average

AVG(Amount) OVER (
  PARTITION BY CustomerID
  ORDER BY OrderDate
  ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
)

๐Ÿ” This defines a 3-row window: it includes the current row and the two preceding rows. That means:

This window "slides" forward row by row, so row 3 forward is always the last 3 rows.

Example 2: Cumulative Sum (From Start to Current Row)

SUM(Amount) OVER (
  PARTITION BY CustomerID
  ORDER BY OrderDate
  ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)

๐Ÿ” This frame includes all rows from the beginning of the partition up to and including the current row. It ensures each row receives a cumulative total that builds as the window slides forward.

This is useful when questions ask things like "total spent so far" or "balance as of this transaction."


๐Ÿ”„ Using CTEs to Filter with Ranked Rows

Use CTEs to:

WITH RollingSums AS (
  SELECT *, SUM(Amount) OVER (...) AS RunningTotal
  FROM Orders
)
SELECT * FROM RollingSums WHERE RunningTotal > 500;

โž• Mix and Match: Combining with Other Functions

Windowed aggregates often work best when used alongside other window functions like ROW_NUMBER(), RANK(), or even another aggregate. Here are a couple of helpful combinations:

Example 1: Rank and Running Total Together

You can add both RANK() and SUM() in the same query to show how a user compares to others and what their running progress looks like:

SELECT 
  CustomerID,
  OrderDate,
  SUM(Amount) OVER (PARTITION BY CustomerID ORDER BY OrderDate) AS RunningTotal,
  RANK() OVER (PARTITION BY CustomerID ORDER BY OrderDate) AS OrderRank
FROM Orders;

Example 2: Difference from the Max

Want to know how far a row is from the highest value in its group? Combine with MAX():

SELECT 
  CustomerID,
  OrderDate,
  Amount,
  MAX(Amount) OVER (PARTITION BY CustomerID) AS MaxAmount,
  Amount - MAX(Amount) OVER (PARTITION BY CustomerID) AS BehindTop
FROM Orders;

These combinations give richer context for trends, outliers, or leaderboard-style logic. For example, the second query above shows how far each row's value is behind the top performer within its partitionโ€”perfect for highlighting gaps or performance deltas.


๐Ÿงฐ Debug Smarter, Not Harder


๐Ÿ” GROUP BY vs. Window Functions

Use Case GROUP BY โŒ Aggregate Window Function โœ…
Total per customer โœ… โœ…
Keep each row โŒ โœ…
Running total โŒ โœ…

๐Ÿ”— Related Topics

(You might also like: RANK, ROW_NUMBER, NTILE, Frame Clauses)