mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-18 04:12:19 +00:00
a1cd07b9a0
Co-authored-by: olgarev <56617294+olgarev@users.noreply.github.com>
485 lines
25 KiB
Markdown
485 lines
25 KiB
Markdown
---
|
||
toc_priority: 38
|
||
toc_title: "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0435\u0020\u0430\u0433\u0440\u0435\u0433\u0430\u0442\u043d\u044b\u0435\u0020\u0444\u0443\u043d\u043a\u0446\u0438\u0438"
|
||
---
|
||
|
||
# Параметрические агрегатные функции {#aggregate_functions_parametric}
|
||
|
||
Некоторые агрегатные функции могут принимать не только столбцы-аргументы (по которым производится свёртка), но и набор параметров - констант для инициализации. Синтаксис - две пары круглых скобок вместо одной. Первая - для параметров, вторая - для аргументов.
|
||
|
||
## histogram {#histogram}
|
||
|
||
Рассчитывает адаптивную гистограмму. Не гарантирует точного результата.
|
||
|
||
histogram(number_of_bins)(values)
|
||
|
||
Функция использует [A Streaming Parallel Decision Tree Algorithm](http://jmlr.org/papers/volume11/ben-haim10a/ben-haim10a.pdf). Границы столбцов устанавливаются по мере поступления новых данных в функцию. В общем случае столбцы имею разную ширину.
|
||
|
||
**Параметры**
|
||
|
||
`number_of_bins` — максимальное количество корзин в гистограмме. Функция автоматически вычисляет количество корзин. Она пытается получить указанное количество корзин, но если не получилось, то в результате корзин будет меньше.
|
||
`values` — [выражение](../syntax.md#syntax-expressions), предоставляющее входные значения.
|
||
|
||
**Возвращаемые значения**
|
||
|
||
- [Массив](../../sql-reference/data-types/array.md) [кортежей](../../sql-reference/data-types/tuple.md) следующего вида:
|
||
|
||
```
|
||
[(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)]
|
||
```
|
||
|
||
- `lower` — нижняя граница корзины.
|
||
- `upper` — верхняя граница корзины.
|
||
- `height` — количество значений в корзине.
|
||
|
||
**Пример**
|
||
|
||
``` sql
|
||
SELECT histogram(5)(number + 1)
|
||
FROM (
|
||
SELECT *
|
||
FROM system.numbers
|
||
LIMIT 20
|
||
)
|
||
```
|
||
|
||
``` text
|
||
┌─histogram(5)(plus(number, 1))───────────────────────────────────────────┐
|
||
│ [(1,4.5,4),(4.5,8.5,4),(8.5,12.75,4.125),(12.75,17,4.625),(17,20,3.25)] │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
С помощью функции [bar](../../sql-reference/aggregate-functions/parametric-functions.md#function-bar) можно визуализировать гистограмму, например:
|
||
|
||
``` sql
|
||
WITH histogram(5)(rand() % 100) AS hist
|
||
SELECT
|
||
arrayJoin(hist).3 AS height,
|
||
bar(height, 0, 6, 5) AS bar
|
||
FROM
|
||
(
|
||
SELECT *
|
||
FROM system.numbers
|
||
LIMIT 20
|
||
)
|
||
```
|
||
|
||
``` text
|
||
┌─height─┬─bar───┐
|
||
│ 2.125 │ █▋ │
|
||
│ 3.25 │ ██▌ │
|
||
│ 5.625 │ ████▏ │
|
||
│ 5.625 │ ████▏ │
|
||
│ 3.375 │ ██▌ │
|
||
└────────┴───────┘
|
||
```
|
||
|
||
В этом случае необходимо помнить, что границы корзин гистограммы не известны.
|
||
|
||
## sequenceMatch(pattern)(timestamp, cond1, cond2, …) {#function-sequencematch}
|
||
|
||
Проверяет, содержит ли последовательность событий цепочку, которая соответствует указанному шаблону.
|
||
|
||
``` sql
|
||
sequenceMatch(pattern)(timestamp, cond1, cond2, ...)
|
||
```
|
||
|
||
!!! warning "Предупреждение"
|
||
События, произошедшие в одну и ту же секунду, располагаются в последовательности в неопределенном порядке, что может повлиять на результат работы функции.
|
||
|
||
**Параметры**
|
||
|
||
- `pattern` — строка с шаблоном. Смотрите [Синтаксис шаблонов](#sequence-function-pattern-syntax).
|
||
|
||
- `timestamp` — столбец, содержащий метки времени. Типичный тип данных столбца — `Date` или `DateTime`. Также можно использовать любой из поддержанных типов данных [UInt](../../sql-reference/aggregate-functions/parametric-functions.md).
|
||
|
||
- `cond1`, `cond2` — условия, описывающие цепочку событий. Тип данных — `UInt8`. Можно использовать до 32 условий. Функция учитывает только те события, которые указаны в условиях. Функция пропускает данные из последовательности, если они не описаны ни в одном из условий.
|
||
|
||
**Возвращаемые значения**
|
||
|
||
- 1, если цепочка событий, соответствующая шаблону найдена.
|
||
- 0, если цепочка событий, соответствующая шаблону не найдена.
|
||
|
||
Тип: `UInt8`.
|
||
|
||
<a name="sequence-function-pattern-syntax"></a>
|
||
**Синтаксис шаблонов**
|
||
|
||
- `(?N)` — соответствует условию на позиции `N`. Условия пронумерованы по порядку в диапазоне `[1, 32]`. Например, `(?1)` соответствует условию, заданному параметром `cond1`.
|
||
|
||
- `.*` — соответствует любому количеству событий. Для этого элемента шаблона не надо задавать условия.
|
||
|
||
- `(?t operator value)` — устанавливает время в секундах, которое должно разделять два события. Например, шаблон `(?1)(?t>1800)(?2)` соответствует событиям, которые произошли более чем через 1800 секунд друг от друга. Между этими событиями может находиться произвольное количество любых событий. Операторы могут быть `>=`, `>`, `<`, `<=`.
|
||
|
||
**Примеры**
|
||
|
||
Пусть таблица `t` содержит следующие данные:
|
||
|
||
``` text
|
||
┌─time─┬─number─┐
|
||
│ 1 │ 1 │
|
||
│ 2 │ 3 │
|
||
│ 3 │ 2 │
|
||
└──────┴────────┘
|
||
```
|
||
|
||
Выполним запрос:
|
||
|
||
``` sql
|
||
SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2) FROM t
|
||
```
|
||
|
||
``` text
|
||
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2))─┐
|
||
│ 1 │
|
||
└───────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
Функция нашла цепочку событий, в которой число 2 следует за числом 1. Число 3 между ними было пропущено, поскольку оно не было использовано ни в одном из условий.
|
||
|
||
``` sql
|
||
SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 3) FROM t
|
||
```
|
||
|
||
``` text
|
||
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 3))─┐
|
||
│ 0 │
|
||
└──────────────────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
В этом случае функция не может найти цепочку событий, соответствующую шаблону, поскольку событие для числа 3 произошло между 1 и 2. Если бы в этом же случае мы бы проверяли условие на событие для числа 4, то цепочка бы соответствовала шаблону.
|
||
|
||
``` sql
|
||
SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 4) FROM t
|
||
```
|
||
|
||
``` text
|
||
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 4))─┐
|
||
│ 1 │
|
||
└──────────────────────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Смотрите также**
|
||
|
||
- [sequenceCount](#function-sequencecount)
|
||
|
||
## sequenceCount(pattern)(time, cond1, cond2, …) {#function-sequencecount}
|
||
|
||
Вычисляет количество цепочек событий, соответствующих шаблону. Функция обнаруживает только непересекающиеся цепочки событий. Она начитает искать следующую цепочку только после того, как полностью совпала текущая цепочка событий.
|
||
|
||
!!! warning "Предупреждение"
|
||
События, произошедшие в одну и ту же секунду, располагаются в последовательности в неопределенном порядке, что может повлиять на результат работы функции.
|
||
|
||
``` sql
|
||
sequenceCount(pattern)(timestamp, cond1, cond2, ...)
|
||
```
|
||
|
||
**Параметры**
|
||
|
||
- `pattern` — строка с шаблоном. Смотрите [Синтаксис шаблонов](#sequence-function-pattern-syntax).
|
||
|
||
- `timestamp` — столбец, содержащий метки времени. Типичный тип данных столбца — `Date` или `DateTime`. Также можно использовать любой из поддержанных типов данных [UInt](../../sql-reference/aggregate-functions/parametric-functions.md).
|
||
|
||
- `cond1`, `cond2` — условия, описывающие цепочку событий. Тип данных — `UInt8`. Можно использовать до 32 условий. Функция учитывает только те события, которые указаны в условиях. Функция пропускает данные из последовательности, если они не описаны ни в одном из условий.
|
||
|
||
**Возвращаемое значение**
|
||
|
||
- Число непересекающихся цепочек событий, соответствущих шаблону.
|
||
|
||
Тип: `UInt64`.
|
||
|
||
**Пример**
|
||
|
||
Пусть таблица `t` содержит следующие данные:
|
||
|
||
``` text
|
||
┌─time─┬─number─┐
|
||
│ 1 │ 1 │
|
||
│ 2 │ 3 │
|
||
│ 3 │ 2 │
|
||
│ 4 │ 1 │
|
||
│ 5 │ 3 │
|
||
│ 6 │ 2 │
|
||
└──────┴────────┘
|
||
```
|
||
|
||
Вычислим сколько раз число 2 стоит после числа 1, причем между 1 и 2 могут быть любые числа:
|
||
|
||
``` sql
|
||
SELECT sequenceCount('(?1).*(?2)')(time, number = 1, number = 2) FROM t
|
||
```
|
||
|
||
``` text
|
||
┌─sequenceCount('(?1).*(?2)')(time, equals(number, 1), equals(number, 2))─┐
|
||
│ 2 │
|
||
└─────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Смотрите также**
|
||
|
||
- [sequenceMatch](#function-sequencematch)
|
||
|
||
## windowFunnel {#windowfunnel}
|
||
|
||
Отыскивает цепочки событий в скользящем окне по времени и вычисляет максимальное количество произошедших событий из цепочки.
|
||
|
||
Функция работает по алгоритму:
|
||
|
||
- Функция отыскивает данные, на которых срабатывает первое условие из цепочки, и присваивает счетчику событий значение 1. С этого же момента начинается отсчет времени скользящего окна.
|
||
|
||
- Если в пределах окна последовательно попадаются события из цепочки, то счетчик увеличивается. Если последовательность событий нарушается, то счетчик не растет.
|
||
|
||
- Если в данных оказалось несколько цепочек разной степени завершенности, то функция выдаст только размер самой длинной цепочки.
|
||
|
||
**Синтаксис**
|
||
|
||
``` sql
|
||
windowFunnel(window, [mode])(timestamp, cond1, cond2, ..., condN)
|
||
```
|
||
|
||
**Параметры**
|
||
|
||
- `window` — ширина скользящего окна по времени. Единица измерения зависит от `timestamp` и может варьироваться. Должно соблюдаться условие `timestamp события cond2 <= timestamp события cond1 + window`.
|
||
- `mode` - необязательный параметр. Если установлено значение `'strict'`, то функция `windowFunnel()` применяет условия только для уникальных значений.
|
||
- `timestamp` — имя столбца, содержащего временные отметки. [Date](../../sql-reference/aggregate-functions/parametric-functions.md), [DateTime](../../sql-reference/aggregate-functions/parametric-functions.md#data_type-datetime) и другие параметры с типом `Integer`. В случае хранения меток времени в столбцах с типом `UInt64`, максимально допустимое значение соответствует ограничению для типа `Int64`, т.е. равно `2^63-1`.
|
||
- `cond` — условия или данные, описывающие цепочку событий. [UInt8](../../sql-reference/aggregate-functions/parametric-functions.md).
|
||
|
||
**Возвращаемое значение**
|
||
|
||
Максимальное количество последовательно сработавших условий из цепочки в пределах скользящего окна по времени. Исследуются все цепочки в выборке.
|
||
|
||
Тип: `Integer`.
|
||
|
||
**Пример**
|
||
|
||
Определим, успевает ли пользователь за установленный период выбрать телефон в интернет-магазине, купить его и сделать повторный заказ.
|
||
|
||
Зададим следующую цепочку событий:
|
||
|
||
1. Пользователь вошел в личный кабинет (`eventID = 1001`).
|
||
2. Пользователь ищет телефон (`eventID = 1003, product = 'phone'`).
|
||
3. Пользователь сделал заказ (`eventID = 1009`)
|
||
4. Пользователь сделал повторный заказ (`eventID = 1010`).
|
||
|
||
Входная таблица:
|
||
|
||
``` text
|
||
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
|
||
│ 2019-01-28 │ 1 │ 2019-01-29 10:00:00 │ 1003 │ phone │
|
||
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
|
||
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
|
||
│ 2019-01-31 │ 1 │ 2019-01-31 09:00:00 │ 1007 │ phone │
|
||
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
|
||
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
|
||
│ 2019-01-30 │ 1 │ 2019-01-30 08:00:00 │ 1009 │ phone │
|
||
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
|
||
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
|
||
│ 2019-02-01 │ 1 │ 2019-02-01 08:00:00 │ 1010 │ phone │
|
||
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
|
||
```
|
||
|
||
Сделаем запрос и узнаем, как далеко пользователь `user_id` смог пройти по цепочке за период в январе-феврале 2019-го года.
|
||
|
||
Запрос:
|
||
|
||
``` sql
|
||
SELECT
|
||
level,
|
||
count() AS c
|
||
FROM
|
||
(
|
||
SELECT
|
||
user_id,
|
||
windowFunnel(6048000000000000)(timestamp, eventID = 1003, eventID = 1009, eventID = 1007, eventID = 1010) AS level
|
||
FROM trend
|
||
WHERE (event_date >= '2019-01-01') AND (event_date <= '2019-02-02')
|
||
GROUP BY user_id
|
||
)
|
||
GROUP BY level
|
||
ORDER BY level ASC
|
||
```
|
||
|
||
## retention {#retention}
|
||
|
||
Аналитическая функция, которая показывает, насколько
|
||
выдерживаются те или иные условия, например, удержание динамики/уровня [посещаемости сайта](https://yandex.ru/support/partner2/statistics/metrika-visitors-statistics.html?lang=ru).
|
||
|
||
Функция принимает набор (от 1 до 32) логических условий, как в [WHERE](../../sql-reference/statements/select/where.md#select-where), и применяет их к заданному набору данных.
|
||
|
||
Условия, кроме первого, применяются попарно: результат второго будет истинным, если истинно первое и второе, третьего - если истинно первое и третье и т. д.
|
||
|
||
**Синтаксис**
|
||
|
||
``` sql
|
||
retention(cond1, cond2, ..., cond32)
|
||
```
|
||
|
||
**Параметры**
|
||
|
||
- `cond` — вычисляемое условие или выражение, которое возвращает `UInt8` результат (1/0).
|
||
|
||
**Возвращаемое значение**
|
||
|
||
Массив из 1 или 0.
|
||
|
||
- 1 — условие выполнено.
|
||
- 0 — условие не выполнено.
|
||
|
||
Тип: `UInt8`.
|
||
|
||
**Пример**
|
||
|
||
Рассмотрим пример расчета функции `retention` для определения посещаемости сайта.
|
||
|
||
**1.** Создадим таблицу для илюстрации примера.
|
||
|
||
``` sql
|
||
CREATE TABLE retention_test(date Date, uid Int32)ENGINE = Memory;
|
||
|
||
INSERT INTO retention_test SELECT '2020-01-01', number FROM numbers(5);
|
||
INSERT INTO retention_test SELECT '2020-01-02', number FROM numbers(10);
|
||
INSERT INTO retention_test SELECT '2020-01-03', number FROM numbers(15);
|
||
```
|
||
|
||
Входная таблица:
|
||
|
||
Запрос:
|
||
|
||
``` sql
|
||
SELECT * FROM retention_test
|
||
```
|
||
|
||
Ответ:
|
||
|
||
``` text
|
||
┌───────date─┬─uid─┐
|
||
│ 2020-01-01 │ 0 │
|
||
│ 2020-01-01 │ 1 │
|
||
│ 2020-01-01 │ 2 │
|
||
│ 2020-01-01 │ 3 │
|
||
│ 2020-01-01 │ 4 │
|
||
└────────────┴─────┘
|
||
┌───────date─┬─uid─┐
|
||
│ 2020-01-02 │ 0 │
|
||
│ 2020-01-02 │ 1 │
|
||
│ 2020-01-02 │ 2 │
|
||
│ 2020-01-02 │ 3 │
|
||
│ 2020-01-02 │ 4 │
|
||
│ 2020-01-02 │ 5 │
|
||
│ 2020-01-02 │ 6 │
|
||
│ 2020-01-02 │ 7 │
|
||
│ 2020-01-02 │ 8 │
|
||
│ 2020-01-02 │ 9 │
|
||
└────────────┴─────┘
|
||
┌───────date─┬─uid─┐
|
||
│ 2020-01-03 │ 0 │
|
||
│ 2020-01-03 │ 1 │
|
||
│ 2020-01-03 │ 2 │
|
||
│ 2020-01-03 │ 3 │
|
||
│ 2020-01-03 │ 4 │
|
||
│ 2020-01-03 │ 5 │
|
||
│ 2020-01-03 │ 6 │
|
||
│ 2020-01-03 │ 7 │
|
||
│ 2020-01-03 │ 8 │
|
||
│ 2020-01-03 │ 9 │
|
||
│ 2020-01-03 │ 10 │
|
||
│ 2020-01-03 │ 11 │
|
||
│ 2020-01-03 │ 12 │
|
||
│ 2020-01-03 │ 13 │
|
||
│ 2020-01-03 │ 14 │
|
||
└────────────┴─────┘
|
||
```
|
||
|
||
**2.** Сгруппируем пользователей по уникальному идентификатору `uid` с помощью функции `retention`.
|
||
|
||
Запрос:
|
||
|
||
``` sql
|
||
SELECT
|
||
uid,
|
||
retention(date = '2020-01-01', date = '2020-01-02', date = '2020-01-03') AS r
|
||
FROM retention_test
|
||
WHERE date IN ('2020-01-01', '2020-01-02', '2020-01-03')
|
||
GROUP BY uid
|
||
ORDER BY uid ASC
|
||
```
|
||
|
||
Результат:
|
||
|
||
``` text
|
||
┌─uid─┬─r───────┐
|
||
│ 0 │ [1,1,1] │
|
||
│ 1 │ [1,1,1] │
|
||
│ 2 │ [1,1,1] │
|
||
│ 3 │ [1,1,1] │
|
||
│ 4 │ [1,1,1] │
|
||
│ 5 │ [0,0,0] │
|
||
│ 6 │ [0,0,0] │
|
||
│ 7 │ [0,0,0] │
|
||
│ 8 │ [0,0,0] │
|
||
│ 9 │ [0,0,0] │
|
||
│ 10 │ [0,0,0] │
|
||
│ 11 │ [0,0,0] │
|
||
│ 12 │ [0,0,0] │
|
||
│ 13 │ [0,0,0] │
|
||
│ 14 │ [0,0,0] │
|
||
└─────┴─────────┘
|
||
```
|
||
|
||
**3.** Рассчитаем количество посещений сайта за день.
|
||
|
||
Запрос:
|
||
|
||
``` sql
|
||
SELECT
|
||
sum(r[1]) AS r1,
|
||
sum(r[2]) AS r2,
|
||
sum(r[3]) AS r3
|
||
FROM
|
||
(
|
||
SELECT
|
||
uid,
|
||
retention(date = '2020-01-01', date = '2020-01-02', date = '2020-01-03') AS r
|
||
FROM retention_test
|
||
WHERE date IN ('2020-01-01', '2020-01-02', '2020-01-03')
|
||
GROUP BY uid
|
||
)
|
||
```
|
||
|
||
Результат:
|
||
|
||
``` text
|
||
┌─r1─┬─r2─┬─r3─┐
|
||
│ 5 │ 5 │ 5 │
|
||
└────┴────┴────┘
|
||
```
|
||
|
||
Где:
|
||
|
||
- `r1` - количество уникальных посетителей за 2020-01-01 (`cond1`).
|
||
- `r2` - количество уникальных посетителей в период между 2020-01-01 и 2020-01-02 (`cond1` и `cond2`).
|
||
- `r3` - количество уникальных посетителей в период между 2020-01-01 и 2020-01-03 (`cond1` и `cond3`).
|
||
|
||
## uniqUpTo(N)(x) {#uniquptonx}
|
||
|
||
Вычисляет количество различных значений аргумента, если оно меньше или равно N.
|
||
В случае, если количество различных значений аргумента больше N, возвращает N + 1.
|
||
|
||
Рекомендуется использовать для маленьких N - до 10. Максимальное значение N - 100.
|
||
|
||
Для состояния агрегатной функции используется количество оперативки равное 1 + N \* размер одного значения байт.
|
||
Для строк запоминается не криптографический хэш, имеющий размер 8 байт. То есть, для строк вычисление приближённое.
|
||
|
||
Функция также работает для нескольких аргументов.
|
||
|
||
Работает максимально быстро за исключением патологических случаев, когда используется большое значение N и количество уникальных значений чуть меньше N.
|
||
|
||
Пример применения:
|
||
|
||
``` text
|
||
Задача: показывать в отчёте только поисковые фразы, по которым было хотя бы 5 уникальных посетителей.
|
||
Решение: пишем в запросе GROUP BY SearchPhrase HAVING uniqUpTo(4)(UserID) >= 5
|
||
```
|
||
|
||
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/agg_functions/parametric_functions/) <!--hide-->
|