Skip to main content
Window functions let you perform calculations across a set of rows that are related to the current row. They can be used to perform calculations similar to those done with aggregate functions, but differ in that a window function doesn’t cause rows to be grouped into a single output - rather, individual rows are still returned.

Standard window functions

ClickHouse supports the standard SQL grammar for windows and window functions. The table below shows which features are currently supported:

Syntax

  • PARTITION BY - defines how to break a resultset into groups.
  • ORDER BY - defines how to order rows inside the group during calculation aggregate_function.
  • ROWS, RANGE, or GROUPS - defines bounds of a frame, aggregate_function is calculated within a frame. ROWS counts physical rows, RANGE counts ORDER BY values, and GROUPS counts peer groups (rows equal on the ORDER BY key).
  • WINDOW - allows multiple expressions to use the same window definition.

Functions usable only as window functions

The following functions can only be used as window functions. Most are standard SQL functions; lagInFrame, leadInFrame, and nonNegativeDerivative are ClickHouse extensions.

Examples

Let’s have a look at some examples of how window functions can be used.

Numbering rows

Aggregation functions

Compare each player’s salary to the average for their team.
Compare each player’s salary to the maximum for their team.

Partitioning by column

Frame bounding

GROUPS frame

A GROUPS frame counts whole peer groups — sets of rows that are equal on the ORDER BY key — rather than physical rows (ROWS) or ORDER BY values (RANGE). N PRECEDING and N FOLLOWING count N peer groups before and after the current row’s peer group, and an included peer group always contributes all of its rows. The query below applies the same 1 PRECEDING AND 1 FOLLOWING bounds as a ROWS, a RANGE, and a GROUPS frame. The order column contains duplicate and non-consecutive values, so the three modes cover different rows:
Each mode interprets the bounds differently:
  • ROWS counts physical rows, so the frame is at most three adjacent rows: the current row plus one on each side.
  • RANGE counts order values, so 1 PRECEDING and 1 FOLLOWING cover rows whose order is within 1 of the current row’s. With gaps of 10, no neighbouring row qualifies, so the frame holds only the rows that share the current order.
  • GROUPS counts peer groups, so 1 PRECEDING and 1 FOLLOWING always include the adjacent groups in full, whatever the gaps between order values.

Real world examples

The following examples solve common real-world problems.

Maximum/total salary per department

Cumulative sum

Moving / sliding average (per 3 rows)

Moving / sliding average (per 10 seconds)

Moving / sliding average (per 10 days)

Temperature is stored with second precision, but using Range and ORDER BY toDate(ts) we form a frame with the size of 10 units, and because of toDate(ts) the unit is a day.

References

GitHub Issues

The roadmap for the initial support of window functions is in this issue. All GitHub issues related to window functions have the comp-window-functions tag.

Tests

These tests contain the examples of the currently supported grammar: https://github.com/ClickHouse/ClickHouse/blob/master/tests/performance/window_functions.xml https://github.com/ClickHouse/ClickHouse/blob/master/tests/queries/0_stateless/01591_window_functions.sql

Postgres Docs

https://www.postgresql.org/docs/current/sql-select.html#SQL-WINDOW https://www.postgresql.org/docs/devel/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS https://www.postgresql.org/docs/devel/functions-window.html https://www.postgresql.org/docs/devel/tutorial-window.html

MySQL Docs

https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
Last modified on August 12, 2026