---
title: "Window frame"
description: "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."
canonical: "https://motherduck.com/glossary/window-frame/"
related:
  - title: "Window functions | MotherDuck Docs"
    url: "https://motherduck.com/docs/sql-reference/duckdb-sql-reference/window-functions/"
  - title: "DuckLake Architecture Deep Dive"
    url: "https://motherduck.com/blog/ducklake-architecture-deep-dive/"
  - title: "MotherDuck Window Functions"
    url: "https://motherduck.com/blog/motherduck-window-functions-in-sql/"
---

# 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`:

```sql
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:

```sql
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;
```