← Back to Glossary

Window frame

A window frame is the subset of rows within a window function's partition that a calculation is applied to, defined with ROWS, RANGE, or GROUPS relative to the current row.

Overview

A window frame narrows the rows that a window function sees for each output row. Instead of aggregating an entire partition, a frame lets you compute things like a trailing 7-day sum, a running total, or a centered moving average by sliding a window of rows relative to the "current row."

A frame is declared inside an OVER clause after PARTITION BY and ORDER BY:

Copy code

SELECT order_date, revenue, SUM(revenue) OVER ( ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS trailing_7day_sum FROM orders;

Frame units: ROWS, RANGE, GROUPS

  • ROWS counts physical rows before/after the current row — the most common and predictable choice for running totals and moving averages.
  • RANGE groups by value proximity in the ORDER BY expression (e.g., all rows within a numeric or time range of the current row's value), treating peer rows (equal order-by values) as one unit.
  • GROUPS counts peer groups (sets of tied rows) rather than individual rows.

Frame bounds are UNBOUNDED PRECEDING, N PRECEDING, CURRENT ROW, N FOLLOWING, or UNBOUNDED FOLLOWING.

Default frame

If a query has ORDER BY but no explicit frame, the default is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW — which is why plain running-total queries with ORDER BY alone still work, but can behave surprisingly with tied order-by values. If there is no ORDER BY at all, the frame defaults to the entire partition.

DuckDB specifics

DuckDB implements the full ANSI frame syntax (ROWS/RANGE/GROUPS, all bound types) and also supports the EXCLUDE clause (EXCLUDE CURRENT ROW, EXCLUDE GROUP, EXCLUDE TIES, EXCLUDE NO OTHERS) to fine-tune which rows near the current row are removed from the frame. DuckDB also supports QUALIFY, which lets you filter on a window function's result without wrapping the query in a subquery:

Copy code

SELECT order_date, revenue, AVG(revenue) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS centered_avg FROM orders QUALIFY centered_avg > 1000;

Related terms

FAQS

ROWS counts a fixed number of physical rows before or after the current row, which is predictable even with duplicate ORDER BY values. RANGE instead groups rows by proximity in the ORDER BY value, treating rows with the same order-by value as a single peer group.

With an ORDER BY clause and no explicit frame, the default is RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Without an ORDER BY clause, the frame defaults to the full partition.