Calculate across related rows
Window functions add a ranking or running calculation while keeping each original row. A partition is the group used for that calculation.
Before you run it: Supported frames and combinations are listed below. These examples use window functions in the SELECT list.
First load the four-order numeric sample. PARTITION BY customer_id restarts the calculation for each customer. ORDER BY inside OVER chooses the order of that calculation; it does not by itself promise a sorted final response.
- Group rows into windowsUse the customer as the partition key.
- Order inside each windowDefine the order needed for ranking or running totals.
- Calculate alongside each rowReturn the original fields with the new window value.
Follow the example
- Load the sample orders.
- Run the ranking example within each customer.
- Run the running-total example.
- Compare each result with the partition and order in OVER.
Supported operations
ROW_NUMBER(), RANK(), DENSE_RANK(), LAG() and LEAD() with OVER (PARTITION BY ... ORDER BY ...) all execute server-side through the SQL translator, alongside SUM / AVG / COUNT / MIN / MAX over a window and the value and distribution functions (FIRST_VALUE, LAST_VALUE, NTH_VALUE, NTILE, PERCENT_RANK, CUME_DIST). Explicit ROWS BETWEEN / RANGE frame clauses (running totals, moving windows) execute too — on the frame-respecting functions: the aggregates plus FIRST_VALUE / LAST_VALUE / NTH_VALUE. Ranking and offset functions ignore a frame by definition, so writing one on ROW_NUMBER, RANK, LAG, LEAD or NTILE is refused with a hint rather than silently dropped.
Rank orders for each customer
Rank each customer's orders by amount - the canonical window use - runs server-side:
SELECT id, customer_id, amount_cents,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY amount_cents DESC
) AS rn
FROM shop.ordersEach row comes back with its rn computed in one pass - no client-side sorting needed.
Calculate a running total
Add a ROWS BETWEEN frame to accumulate a running total per partition — the classic dashboard shape — in one server-side pass:
SELECT id, customer_id, amount_cents,
SUM(amount_cents) OVER (
PARTITION BY customer_id
ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running
FROM shop.ordersWith the four-row sample, running totals amounts in ID order within each customer: customer 1 → 14900, 18800; customer 2 → 7200, 29200. Each original order remains in the result beside its running value.
The two frame units differ in what they accept. ROWS takes every bound — UNBOUNDED PRECEDING, N PRECEDING, CURRENT ROW, N FOLLOWING, UNBOUNDED FOLLOWING — so a moving window such as ROWS BETWEEN 1 PRECEDING AND CURRENT ROW works. RANGE takes peer bounds only (UNBOUNDED PRECEDING, CURRENT ROW, UNBOUNDED FOLLOWING), where CURRENT ROW spans the whole tie group; a value offset such as RANGE BETWEEN 5 PRECEDING is refused, as are the GROUPS frame unit and EXCLUDE.
Check query and frame limits
A window function runs over a single-table SELECT. It cannot yet appear in the same SELECT as a JOIN, a GROUP BY or DISTINCT, and the OVER call has to sit in the select list - you can ORDER BY the alias it was given, but not write the call itself into a WHERE or ORDER BY expression. Do the join or the aggregate in a subquery first, then window over its output. Named windows (WINDOW w AS (...) with OVER w) are supported; chaining one named window off another is not.
Expected result
Rankings and totals appear beside each order instead of collapsing orders into a summary.