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:
FeatureSupported?Comment
Ad hoc window specification (count(*) OVER (PARTITION BY id ORDER BY time DESC))
Expressions involving window functions, e.g. (count(*) OVER ()) / 2
WINDOW clause (SELECT ... FROM table WINDOW w AS (PARTITION BY id))
ROWS frame
RANGE frameUsed by default when a frame is not specified explicitly (RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW).
INTERVAL syntax for DateTime RANGE OFFSET frameSpecify the number of seconds instead (RANGE works with any numeric type).
GROUPS frame
Calculating aggregate functions over a frame (sum(value) OVER (ORDER BY time))All aggregate functions are supported.
rank(), dense_rank()/denseRank(), row_number()
percent_rank()/percentRank()Efficiently computes the relative standing of a value within a partition. It replaces the more verbose and computationally intensive manual SQL calculation expressed as ifNull((rank() OVER (PARTITION BY x ORDER BY y) - 1) / nullif(count(1) OVER (PARTITION BY x) - 1, 0), 0).
cume_dist()Computes the cumulative distribution of a value within a group of values. Returns the percentage of rows with values less than or equal to the current row’s value.
lag/lead(value, offset)You can also use one of the following workarounds:
1) any(value) OVER (... ROWS BETWEEN <offset> PRECEDING AND <offset> PRECEDING), or FOLLOWING instead of PRECEDING for lead
2) lagInFrame/leadInFrame, which are analogous but respect the window frame. To get behavior identical to lag/lead, use ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
ntile(buckets)Specify the window as, for example, (PARTITION BY x ORDER BY y ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING).

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 or RANGE - defines bounds of a frame, aggregate_function is calculated within a frame.
  • 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.
FunctionDescription
row_number()Number the current row within its partition starting from 1.
first_value(x)Return the first value evaluated within its ordered frame.
last_value(x)Return the last value evaluated within its ordered frame.
nth_value(x, offset)Return the first non-NULL value evaluated against the nth row (offset) in its ordered frame.
rank()Rank the current row within its partition with gaps.
dense_rank()Rank the current row within its partition without gaps.
lagInFrame(x)Return a value evaluated at the row that is at a specified physical offset row before the current row within the ordered frame.
leadInFrame(x)Return a value evaluated at the row that is offset rows after the current row within the ordered frame.
ntile(buckets)Divide the ordered rows within a partition into the given number of buckets and return the bucket number of the current row.
nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS])Compute the non-negative derivative of metric_column with respect to timestamp_column. Specific to ClickHouse.

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

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 July 1, 2026