Skip to content

Window Anatomy

To use any window function, you must define a Window—the set of rows the function will operate on. This is done using the OVER() clause.

The OVER() clause defines the “window” of rows for a function. It consists of three optional components:

  1. PARTITION BY: Divides the data into buckets (similar to GROUP BY).
  2. ORDER BY: Defines the sequence of rows within each bucket.
  3. FRAME: Defines the boundaries of the window (e.g., “the previous 3 rows”).
Standard Aggregate (GROUP BY)Window Function (OVER)
Collapses many rows into 1 row.Keeps all original rows.
You lose access to non-grouped columns.You retain access to every column.
Result set size = number of groups.Result set size = original table size.

If you want a calculation to “reset” for every department, you partition by department. OVER(PARTITION BY Department)

Within the window, how should the rows be ordered? This is critical for functions like RANK() or for calculating running totals. OVER(PARTITION BY Dept ORDER BY Salary DESC)

If you provide an empty OVER(), the “window” is the entire result set. SELECT Salary, SUM(Salary) OVER() FROM Employees; This would show each employee’s salary next to the total payroll of the whole company.

We have an Employees table:

NameDeptSalary
AliceIT90000
BobSales70000
CharlieIT85000

Task: Show each employee’s salary and their department’s average salary.

Section titled “Task: Show each employee’s salary and their department’s average salary.”
Basic Windowing
SELECT
Name,
Dept,
Salary,
AVG(Salary) OVER(PARTITION BY Dept) AS DeptAvg
FROM Employees;
NameDeptSalaryDeptAvg
AliceIT9000087500
CharlieIT8500087500
BobSales7000070000

Window functions happen very late in the execution of a query:

  1. FROM
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. WINDOW FUNCTIONS
  6. SELECT (Final Projection)
  7. ORDER BY

Because window functions happen after the WHERE clause, you cannot filter the results of a window function using WHERE.

Incorrect: SELECT Name, RANK() OVER(...) AS MyRank FROM Staff WHERE MyRank = 1;

Solution: You must wrap the query in a CTE or a Subquery and then filter the output of that CTE.