pgconfigurator
pgconfigurator

WindowAgg

Appears in EXPLAIN asWindowAgg

Computes window functions (OVER ...) across ordered partitions.

What it is

A WindowAgg evaluates window functions such as row_number(), rank(), and running sums. It needs its input ordered by the window's PARTITION BY / ORDER BY, usually via a preceding Sort.

When the planner picks it

Whenever the query uses an OVER () window clause.

Is it good or bad?

Expected for window queries. The cost is almost always the Sort feeding it: a wide ORDER BY over a big input can spill to disk. Index-provided ordering avoids the sort entirely.

In depth

How a window function actually runs

A WindowAgg evaluates window functions (row_number(), rank(), sum(x) OVER (...), …) by streaming through input that is already partitioned and ordered the way the window asks. It accumulates per-partition state, emits one output row per input row, and resets state at each new partition boundary.

The catch: the input must arrive in the window's order. That's almost always why a Sort sits beneath a WindowAgg — and that Sort is usually the dominant cost.

Reading the plan

WindowAgg  (actual rows=580000 loops=1)
  Window: w1 AS (ORDER BY payload ROWS UNBOUNDED PRECEDING)
  Storage: Memory  Maximum Storage: 17kB
  ->  Sort  (actual rows=580000 loops=1)
        Sort Key: payload
        Sort Method: external merge  Disk: 34824kB

The window expression and frame are echoed back. Maximum Storage is the peak per-partition state — usually tiny because the frame summary is small. The expensive part is almost always the Sort that feeds it.

Removing the Sort

If you can give the WindowAgg an index that already returns rows in PARTITION BY … ORDER BY … order, the Sort disappears entirely:

CREATE INDEX events_by_user_ts ON events (user_id, created_at);

SELECT user_id, created_at,
       row_number() OVER (PARTITION BY user_id ORDER BY created_at)
FROM events;

That turns a multi-second sort over millions of rows into a streaming index scan plus a near-free window pass.

Frame choices matter

ROWS UNBOUNDED PRECEDING is the running aggregate over the whole partition; ROWS BETWEEN N PRECEDING AND CURRENT ROW is a moving window. Sliding-window frames keep more state and can change which frame the planner picks — measure when in doubt.

What the analyzer flags here

  • Sort spilled to disk — the ordering for the window exceeded work_mem

Paste a plan into the analyzer →

Settings that influence it

See also