Оконные функции
Оконные функции позволяют выполнять вычисления над набором строк, связанных с текущей строкой. Часть таких вычислений аналогична тем, что можно выполнить с агрегатной функцией, но оконная функция не приводит к объединению строк в единый результирующий набор — отдельные строки по‑прежнему возвращаются.
Стандартные оконные функции
ClickHouse поддерживает стандартную грамматику для определения окон и оконных функций. В таблице ниже указано, поддерживается ли та или иная возможность.
| Feature | Supported? |
|---|---|
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 frame | ✅ (по умолчанию) |
INTERVAL syntax for DateTime RANGE OFFSET frame | ❌ (вместо этого указывайте количество секунд (RANGE работает с любым числовым типом).) |
GROUPS frame | ❌ |
Calculating aggregate functions over a frame (sum(value) over (order by time)) | ✅ (поддерживаются все агрегатные функции) |
rank(), dense_rank(), row_number() | ✅ Псевдоним: denseRank() |
percent_rank() | ✅ Эффективно вычисляет относительное положение значения внутри секции (partition) набора данных. Эта функция фактически заменяет более многословный и вычислительно затратный ручной SQL‑расчёт, выраженный как ifNull((rank() OVER(PARTITION BY x ORDER BY y) - 1) / nullif(count(1) OVER(PARTITION BY x) - 1, 0), 0) Псевдоним: percentRank() |
cume_dist() | ✅ Вычисляет накопленное распределение значения в группе значений. Возвращает процент строк со значениями, меньшими либо равными значению в текущей строке. |
lag/lead(value, offset) | ✅ Вы также можете использовать один из следующих обходных решений: 1) any(value) over (.... rows between <offset> preceding and <offset> preceding), или following для lead 2) lagInFrame/leadInFrame, которые являются аналогами, но учитывают оконный фрейм. Чтобы получить поведение, идентичное lag/lead, используйте rows between unbounded preceding and unbounded following |
| ntile(buckets) | ✅ Задайте окно следующим образом: (partition by x order by y rows between unbounded preceding and unbounded following). |
Оконные функции ClickHouse
Также доступна следующая оконная функция ClickHouse:
nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS])
Вычисляет неотрицательную производную для заданного столбца metric_column по столбцу timestamp_column.
Параметр INTERVAL можно опустить, по умолчанию используется INTERVAL 1 SECOND.
Вычисляемое значение для каждой строки:
0для первой строки,- для -й строки.
Синтаксис
PARTITION BY- задает, как разбить результирующий набор на группы.ORDER BY- задает, как упорядочить строки внутри группы при вычисленииaggregate_function.ROWS or RANGE- задает границы фрейма,aggregate_functionвычисляется внутри этого фрейма.WINDOW- позволяет нескольким выражениям использовать одно и то же определение окна.
Функции
Эти функции можно использовать только как оконные функции.
row_number()- Нумерует текущую строку в её разделе, начиная с 1.first_value(x)- Возвращает первое значение, вычисленное в пределах упорядоченного фрейма.last_value(x)- Возвращает последнее значение, вычисленное в пределах упорядоченного фрейма.nth_value(x, offset)- Возвращает первое значение, не равное NULL, вычисленное для n-й строки (offset) в её упорядоченном фрейме.rank()- Присваивает ранг текущей строке в её разделе с пропусками.dense_rank()- Присваивает ранг текущей строке в её разделе без пропусков.lagInFrame(x)- Возвращает значение, вычисленное для строки, которая находится на заданное количество строк раньше текущей строки в упорядоченном фрейме.leadInFrame(x)- Возвращает значение, вычисленное для строки, которая находится на заданное количество строк позже текущей строки в упорядоченном фрейме.
Примеры
Рассмотрим несколько примеров использования оконных функций.
Нумерация строк
Функции агрегации
Сравните зарплату каждого игрока со средней зарплатой по его команде.
Сравните зарплату каждого игрока с максимальной зарплатой в его команде.
Партиционирование по столбцу
Границы фрейма
┌─part_key─┬─value─┬─order─┬─frame_values─┬─rn_1─┬─rn_2─┬─rn_3─┬─rn_4─┐ │ 1 │ 1 │ 1 │ [5,4,3,2,1] │ 5 │ 5 │ 5 │ 2 │ │ 1 │ 2 │ 2 │ [5,4,3,2] │ 4 │ 4 │ 4 │ 2 │ │ 1 │ 3 │ 3 │ [5,4,3] │ 3 │ 3 │ 3 │ 2 │ │ 1 │ 4 │ 4 │ [5,4] │ 2 │ 2 │ 2 │ 2 │ │ 1 │ 5 │ 5 │ [5] │ 1 │ 1 │ 1 │ 1 │ └──────────┴──────┴──────┴──────────────┴──────┴──────┴──────┴──────┘
Скользящее среднее (за каждые 10 секунд)
Скользящее среднее (за 10 дней)
Температура хранится с точностью до секунды, но, используя Range и ORDER BY toDate(ts), мы формируем окно размером 10 единиц, и благодаря toDate(ts) единицей является день.
insert into sensors values('ambient_temp', '2020-01-01 00:00:00', 16),
('ambient_temp', '2020-01-01 12:00:00', 16),
('ambient_temp', '2020-01-02 11:00:00', 9),
('ambient_temp', '2020-01-02 12:00:00', 9),
('ambient_temp', '2020-02-01 10:00:00', 10),
('ambient_temp', '2020-02-01 12:00:00', 10),
('ambient_temp', '2020-02-10 12:00:00', 12),
('ambient_temp', '2020-02-10 13:00:00', 12),
('ambient_temp', '2020-02-20 12:00:01', 16),
('ambient_temp', '2020-03-01 12:00:00', 16),
('ambient_temp', '2020-03-01 12:00:00', 16),
('ambient_temp', '2020-03-01 12:00:00', 16);
Ссылки
GitHub Issues
Дорожная карта начальной поддержки оконных функций представлена в этом issue.
Все GitHub issues, связанные с оконными функциями, имеют тег comp-window-functions.
Тесты
Эти тесты содержат примеры грамматики, поддерживаемой на данный момент:
https://github.com/ClickHouse/ClickHouse/blob/master/tests/performance/window_functions.xml
Документация Postgres
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
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