ClickHouse/docs/ru/sql-reference/aggregate-functions/combinators.md
2022-08-26 13:37:11 -04:00

285 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
slug: /ru/sql-reference/aggregate-functions/combinators
sidebar_position: 37
sidebar_label: "Комбинаторы агрегатных функций"
---
# Комбинаторы агрегатных функций {#aggregate_functions_combinators}
К имени агрегатной функции может быть приписан некоторый суффикс. При этом, работа агрегатной функции некоторым образом модифицируется.
## -If {#agg-functions-combinator-if}
К имени любой агрегатной функции может быть приписан суффикс -If. В этом случае, агрегатная функция принимает ещё один дополнительный аргумент - условие (типа UInt8). Агрегатная функция будет обрабатывать только те строки, для которых условие сработало. Если условие ни разу не сработало - возвращается некоторое значение по умолчанию (обычно - нули, пустые строки).
Примеры: `sumIf(column, cond)`, `countIf(cond)`, `avgIf(x, cond)`, `quantilesTimingIf(level1, level2)(x, cond)`, `argMinIf(arg, val, cond)` и т. п.
С помощью условных агрегатных функций, вы можете вычислить агрегаты сразу для нескольких условий, не используя подзапросы и `JOIN`-ы.
Например, в Яндекс.Метрике, условные агрегатные функции используются для реализации функциональности сравнения сегментов.
## -Array {#array}
К имени любой агрегатной функции может быть приписан суффикс -Array. В этом случае, агрегатная функция вместо аргументов типов T принимает аргументы типов Array(T) (массивы). Если агрегатная функция принимает несколько аргументов, то это должны быть массивы одинаковых длин. При обработке массивов, агрегатная функция работает, как исходная агрегатная функция по всем элементам массивов.
Пример 1: `sumArray(arr)` - просуммировать все элементы всех массивов arr. В данном примере можно было бы написать проще: `sum(arraySum(arr))`.
Пример 2: `uniqArray(arr)` - посчитать количество уникальных элементов всех массивов arr. Это можно было бы сделать проще: `uniq(arrayJoin(arr))`, но не всегда есть возможность добавить arrayJoin в запрос.
Комбинаторы -If и -Array можно сочетать. При этом, должен сначала идти Array, а потом If. Примеры: `uniqArrayIf(arr, cond)`, `quantilesTimingArrayIf(level1, level2)(arr, cond)`. Из-за такого порядка получается, что аргумент cond не должен быть массивом.
## -SimpleState {#agg-functions-combinator-simplestate}
При использовании этого комбинатора агрегатная функция возвращает то же значение, но типа [SimpleAggregateFunction(...)](../../sql-reference/data-types/simpleaggregatefunction.md). Текущее значение функции может храниться в таблице для последующей работы с таблицами семейства [AggregatingMergeTree](../../engines/table-engines/mergetree-family/aggregatingmergetree.md).
**Синтаксис**
``` sql
<aggFunction>SimpleState(x)
```
**Аргументы**
- `x` — параметры агрегатной функции.
**Возвращаемое значение**
Значение агрегатной функции типа `SimpleAggregateFunction(...)`.
**Пример**
Запрос:
``` sql
WITH anySimpleState(number) AS c SELECT toTypeName(c), c FROM numbers(1);
```
Результат:
``` text
┌─toTypeName(c)────────────────────────┬─c─┐
│ SimpleAggregateFunction(any, UInt64) │ 0 │
└──────────────────────────────────────┴───┘
```
## -State {#state}
В случае применения этого комбинатора, агрегатная функция возвращает не готовое значение (например, в случае функции [uniq](reference/uniq.md#agg_function-uniq) — количество уникальных значений), а промежуточное состояние агрегации (например, в случае функции `uniq` — хэш-таблицу для расчёта количества уникальных значений), которое имеет тип `AggregateFunction(...)` и может использоваться для дальнейшей обработки или может быть сохранено в таблицу для последующей доагрегации.
Для работы с промежуточными состояниями предназначены:
- Движок таблиц [AggregatingMergeTree](../../engines/table-engines/mergetree-family/aggregatingmergetree.md).
- Функция [finalizeAggregation](../../sql-reference/aggregate-functions/combinators.md#function-finalizeaggregation).
- Функция [runningAccumulate](../../sql-reference/aggregate-functions/combinators.md#runningaccumulate).
- Комбинатор [-Merge](#aggregate_functions_combinators-merge).
- Комбинатор [-MergeState](#aggregate_functions_combinators-mergestate).
## -Merge {#aggregate_functions_combinators-merge}
В случае применения этого комбинатора, агрегатная функция будет принимать в качестве аргумента промежуточное состояние агрегации, доагрегировать (объединять вместе) эти состояния, и возвращать готовое значение.
## -MergeState {#aggregate_functions_combinators-mergestate}
Выполняет слияние промежуточных состояний агрегации, аналогично комбинатору -Merge, но возвращает не готовое значение, а промежуточное состояние агрегации, аналогично комбинатору -State.
## -ForEach {#foreach}
Преобразует агрегатную функцию для таблиц в агрегатную функцию для массивов, которая применяет агрегирование для соответствующих элементов массивов и возвращает массив результатов. Например, `sumForEach` для массивов `[1, 2]`, `[3, 4, 5]` и `[6, 7]` даст результат `[10, 13, 5]`, сложив соответственные элементы массивов.
## -Distinct {#agg-functions-combinator-distinct}
При наличии комбинатора Distinct, каждое уникальное значение аргументов, будет учитано в агрегатной функции только один раз.
Примеры: `sum(DISTINCT x)`, `groupArray(DISTINCT x)`, `corrStableDistinct(DISTINCT x, y)` и т.п.
## -OrDefault {#agg-functions-combinator-ordefault}
Изменяет поведение агрегатной функции.
Если на вход агрегатной функции передан пустой набор данных, то с помощью комбинатора `-OrDefault` функция возвращает значение по умолчанию для соответствующего типа данных. Комбинатор применяется к агрегатным функциям, которые могут принимать пустые входные данные.
`-OrDefault` можно использовать с другими комбинаторами.
**Синтаксис**
``` sql
<aggFunction>OrDefault(x)
```
**Аргументы**
- `x` — аргументы агрегатной функции.
**Возращаемые зачения**
Возвращает значение по умолчанию для соответствующего типа агрегатной функции, если агрегировать нечего.
Тип данных зависит от используемой агрегатной функции.
**Пример**
Запрос:
``` sql
SELECT avg(number), avgOrDefault(number) FROM numbers(0)
```
Результат:
``` text
┌─avg(number)─┬─avgOrDefault(number)─┐
│ nan │ 0 │
└─────────────┴──────────────────────┘
```
Также `-OrDefault` может использоваться с другими комбинаторами. Это полезно, когда агрегатная функция не принимает пустые входные данные.
Запрос:
``` sql
SELECT avgOrDefaultIf(x, x > 10)
FROM
(
SELECT toDecimal32(1.23, 2) AS x
)
```
Результат:
``` text
┌─avgOrDefaultIf(x, greater(x, 10))─┐
│ 0.00 │
└───────────────────────────────────┘
```
## -OrNull {#agg-functions-combinator-ornull}
Изменяет поведение агрегатной функции.
Комбинатор преобразует результат агрегатной функции к типу [Nullable](../data-types/nullable.md). Если агрегатная функция не получает данных на вход, то с комбинатором она возвращает [NULL](../syntax.md#null-literal).
`-OrNull` может использоваться с другими комбинаторами.
**Синтаксис**
``` sql
<aggFunction>OrNull(x)
```
**Аргументы**
- `x` — аргументы агрегатной функции.
**Возвращаемые значения**
- Результат агрегатной функции, преобразованный в тип данных `Nullable`.
- `NULL`, если у агрегатной функции нет входных данных.
Тип: `Nullable(aggregate function return type)`.
**Пример**
Добавьте `-orNull` в конец агрегатной функции.
Запрос:
``` sql
SELECT sumOrNull(number), toTypeName(sumOrNull(number)) FROM numbers(10) WHERE number > 10
```
Результат:
``` text
┌─sumOrNull(number)─┬─toTypeName(sumOrNull(number))─┐
│ ᴺᵁᴸᴸ │ Nullable(UInt64) │
└───────────────────┴───────────────────────────────┘
```
Также `-OrNull` может использоваться с другими комбинаторами. Это полезно, когда агрегатная функция не принимает пустые входные данные.
Запрос:
``` sql
SELECT avgOrNullIf(x, x > 10)
FROM
(
SELECT toDecimal32(1.23, 2) AS x
)
```
Результат:
``` text
┌─avgOrNullIf(x, greater(x, 10))─┐
│ ᴺᵁᴸᴸ │
└────────────────────────────────┘
```
## -Resample {#agg-functions-combinator-resample}
Позволяет поделить данные на группы, а затем по-отдельности агрегирует данные для этих групп. Группы образуются разбиением значений одного из столбцов на интервалы.
``` sql
<aggFunction>Resample(start, end, step)(<aggFunction_params>, resampling_key)
```
**Аргументы**
- `start` — начальное значение для интервала значений `resampling_key`.
- `stop` — конечное значение для интервала значений `resampling_key`. Интервал не включает значение `stop` (`[start, stop)`).
- `step` — шаг деления полного интервала на подинтервалы. Функция `aggFunction` выполняется для каждого из подинтервалов независимо.
- `resampling_key` — столбец, значения которого используются для разделения данных на интервалы.
- `aggFunction_params` — параметры `aggFunction`.
**Возвращаемые значения**
- Массив результатов `aggFunction` для каждого подинтервала.
**Пример**
Рассмотрим таблицу `people` со следующими данными:
``` text
┌─name───┬─age─┬─wage─┐
│ John │ 16 │ 10 │
│ Alice │ 30 │ 15 │
│ Mary │ 35 │ 8 │
│ Evelyn │ 48 │ 11.5 │
│ David │ 62 │ 9.9 │
│ Brian │ 60 │ 16 │
└────────┴─────┴──────┘
```
Получим имена людей, чей возраст находится в интервалах `[30,60)` и `[60,75)`. Поскольку мы используем целочисленное представление возраста, то интервалы будут выглядеть как `[30, 59]` и `[60,74]`.
Чтобы собрать имена в массив, возьмём агрегатную функцию [groupArray](../../sql-reference/aggregate-functions/reference/grouparray.md#agg_function-grouparray). Она принимает один аргумент. В нашем случае, это столбец `name`. Функция `groupArrayResample` должна использовать столбец `age` для агрегирования имён по возрасту. Чтобы определить необходимые интервалы, передадим в функцию `groupArrayResample` аргументы `30, 75, 30`.
``` sql
SELECT groupArrayResample(30, 75, 30)(name, age) from people
```
``` text
┌─groupArrayResample(30, 75, 30)(name, age)─────┐
│ [['Alice','Mary','Evelyn'],['David','Brian']] │
└───────────────────────────────────────────────┘
```
Посмотрим на результаты.
`Jonh` не попал в выдачу, поскольку слишком молод. Остальные распределены согласно заданным возрастным интервалам.
Теперь посчитаем общее количество людей и их среднюю заработную плату в заданных возрастных интервалах.
``` sql
SELECT
countResample(30, 75, 30)(name, age) AS amount,
avgResample(30, 75, 30)(wage, age) AS avg_wage
FROM people
```
``` text
┌─amount─┬─avg_wage──────────────────┐
│ [3,2] │ [11.5,12.949999809265137] │
└────────┴───────────────────────────┘
```