Skip to content
In this guide

Guide · The plan, node by node

WindowAgg

A WindowAgg computes window functions over its input without collapsing rows. One row in, one row out, with the window's value attached. It needs the rows in the order the window asks for, so a Sort usually sits under it, on the PARTITION BY columns and then the ORDER BY columns. The sample's rank() OVER (ORDER BY sum(oi.line_total) DESC) runs above the aggregate: 24 country rows in, sorted by revenue, 24 out with a rank.

Why it matters

A WindowAgg over 24 rows costs nothing. The expense is the sort it needs, and the number of WindowAggs in the query. Every window with a different PARTITION BY or ORDER BY is its own node with its own sort of the whole input, so three windows on three orderings of 12,000,000 rows is three full sorts. Look at the rows going into the WindowAgg and at whether a WHERE in an outer query discards most of what it computed. The WindowAgg computes every row before an outer filter sees one.

What to do

  1. Let windows share a sort. Windows with the same PARTITION BY and ORDER BY share one WindowAgg and one Sort. Matching the query's final ORDER BY to the window's lets the planner reuse that sort for the output, as the sample's ORDER BY revenue DESC does.
  2. Cut the input before the window. A WHERE placed_at >= ... in the subquery under the window runs before the sort.
  3. If the sort under the window spills, treat it as a Sort problem: work_mem for the session, or an index in the window's order.

In the Plan tab

The node is labelled WindowAgg and keeps the band width of its input. The Sort under it is where to look, and if that sort went to disk it carries the spill label and the Sort spilled to disk finding.

Last checked against Seqora 0.3.2, PostgreSQL 17 on .

Found a bug, missing a feature, or just curious? Write to me. Every email lands in my own inbox, not a ticket queue.