Агрегатные функции

count()

Считает количество строк. Принимает ноль аргументов, возвращает UInt64. Не поддерживается синтаксис COUNT(DISTINCT x) - для этого есть отдельная агрегатная функция uniq.

Запрос вида SELECT count() FROM table не оптимизируется, так как количество записей в таблице нигде не хранится отдельно - из таблицы будет выбран какой-нибудь достаточно маленький столбец, и будет посчитано количество значений в нём.

any(x)

Выбирает первое попавшееся значение. Порядок выполнения запроса может быть произвольным и даже каждый раз разным, поэтому результат данной функции недетерминирован. Для получения детерминированного результата, можно использовать функции min или max вместо any.

В некоторых случаях, вы всё-таки можете рассчитывать на порядок выполнения запроса. Это - случаи, когда SELECT идёт из подзапроса, в котором используется ORDER BY.

При наличии в запросе SELECT секции GROUP BY или хотя бы одной агрегатной функции, ClickHouse (в отличие от MySQL) требует, чтобы все выражения в секциях SELECT, HAVING, ORDER BY вычислялись из ключей или из агрегатных функций. То есть, каждый выбираемый из таблицы столбец, должен использоваться либо в ключах, либо внутри агрегатных функций. Чтобы получить поведение, как в MySQL, вы можете поместить остальные столбцы в агрегатную функцию any.

anyLast(x)

Выбирает последнее попавшееся значение. Результат так же недетерминирован, как и для функции any.

min(x)

Вычисляет минимум.

max(x)

Вычисляет максимум.

argMin(arg, val)

Вычисляет значение arg при минимальном значении val. Если есть несколько разных значений arg для минимальных значений val, то выдаётся первое попавшееся из таких значений.

argMax(arg, val)

Вычисляет значение arg при максимальном значении val. Если есть несколько разных значений arg для максимальных значений val, то выдаётся первое попавшееся из таких значений.

sum(x)

Вычисляет сумму. Работает только для чисел.

avg(x)

Вычисляет среднее. Работает только для чисел. Результат всегда - Float64.

uniq(x)

Приближённо вычисляет количество различных значений аргумента. Работает для чисел, строк, дат, дат-с-временем, для нескольких аргументов и аргументов-кортежей.

Используется алгоритм типа adaptive sampling: в качестве состояния вычислений используется выборка значений хэшей элементов, размером до 65 536. Алгоритм является очень точным для множеств небольшой кардинальности (до 65 536) и очень эффективным по CPU (при расчёте не слишком большого количества таких функций, использование uniq почти так же быстро, как использование других агрегатных функций).

Результат детерминирован (не зависит от порядка выполнения запроса).

uniqCombined(x)

Приближённо вычисляет количество различных значений аргумента. Работает для чисел, строк, дат, дат-с-временем, для нескольких аргументов и аргументов-кортежей.

Используется комбинация трёх алгоритмов: массив, хэш-таблица и HyperLogLog с таблицей коррекции погрешности. Расход памяти в несколько раз меньше, чем у функции uniq, а точность в несколько раз выше. Скорость работы чуть ниже, чем у функции uniq, но иногда может быть даже выше - в случае распределённых запросов, в которых по сети передаётся большое количество состояний агрегации. Максимальный размер состояния составляет 96 KiB (HyperLogLog из 217 6-битовых ячеек).

Результат детерминирован (не зависит от порядка выполнения запроса).

Функция uniqCombined является хорошим выбором по умолчанию для подсчёта количества различных значений.

uniqHLL12(x)

Приближённо вычисляет количество различных значений аргумента, используя алгоритм HyperLogLog. Используется 212 5-битовых ячеек. Размер состояния чуть больше 2.5 КБ.

Результат детерминирован (не зависит от порядка выполнения запроса).

В большинстве случаев, используйте функцию uniq или uniqCombined.

uniqExact(x)

Вычисляет количество различных значений аргумента, точно. Не стоит бояться приближённых расчётов. Поэтому, используйте лучше функцию uniq. Функцию uniqExact следует использовать, если вам точно нужен точный результат.

Функция uniqExact расходует больше оперативки, чем функция uniq, так как размер состояния неограниченно растёт по мере роста количества различных значений.

groupArray(x)

Составляет массив из значений аргумента. Значения в массив могут быть добавлены в любом (недетерминированном) порядке.

В некоторых случаях, вы всё-таки можете рассчитывать на порядок выполнения запроса. Это - случаи, когда SELECT идёт из подзапроса, в котором используется ORDER BY.

groupUniqArray(x)

Составляет массив из различных значений аргумента. Расход оперативки такой же, как у функции uniqExact.

quantile(level)(x)

Приближённо вычисляет квантиль уровня level. level - константа, число с плавающей запятой от 0 до 1. Рекомендуется использовать значения level в диапазоне 0.01 .. 0.99. Не используйте значения level, равные 0 или 1 - для таких случаев есть функции min и max.

В этой функции, равно как и во всех функциях для расчёта квантилей, параметр level может быть не указан. В таком случае, он принимается равным 0.5 - то есть, функция будет вычислять медиану.

Работает для чисел, дат, дат-с-временем. Для чисел возвращает Float64, для дат - дату, для дат-с-временем - дату-с-временем.

Используется reservoir sampling с размером резервуара до 8192. При необходимости, результат выдаётся с линейной аппроксимацией из двух соседних значений. Этот алгоритм обеспечивает весьма низкую точность расчёта. Смотрите также функции quantileTiming, quantileTDigest, quantileExact.

Результат зависит от порядка выполнения запроса, и является недетерминированным.

При использовании нескольких функций quantile (и аналогичных) с разными уровнями в запросе, внутренние состояния не объединяются (то есть, запрос работает менее эффективно, чем мог бы). В этом случае, используйте функцию quantiles (и аналогичные).

quantileDeterministic(level)(x, determinator)

Работает аналогично функции quantile, но, в отличие от неё, результат является детерминированным и не зависит от порядка выполнения запроса.

Для этого, функция принимает второй аргумент - “детерминатор”. Это некоторое число, хэш от которого используется вместо генератора случайных чисел в алгоритме reservoir sampling. Для правильной работы функции, одно и то же значение детерминатора не должно встречаться слишком часто. В качестве детерминатора вы можете использовать идентификатор события, идентификатор посетителя и т. п.

Не используйте эту функцию для рассчёта таймингов. Для этого есть более подходящая функции - quantileTiming.

quantileTiming(level)(x)

Вычисляет квантиль уровня level с фиксированной точностью. Работает для чисел. Предназначена для расчёта квантилей от времени загрузки страницы в миллисекундах.

Если значение больше 30 000 (соответствует времени загрузки страницы большем 30 секундам.) - результат приравнивается к 30 000.

Если всего значений не больше примерно 5670, то вычисление точное.

Иначе:
  • если время меньше 1024 мс., то вычисление точное.
  • иначе вычисление идёт с округлением до числа, кратного 16 мс.

При передаче в функцию отрицательных значений, поведение не определено.

Возвращаемое значение имеет тип Float32. Когда в функцию не было передано ни одного значения (при использовании quantileTimingIf), возвращается nan. Это сделано, чтобы отличать такие случаи от нулей. Смотрите замечание о сортировке NaN-ов в разделе “Секция ORDER BY”.

Результат детерминирован (не зависит от порядка выполнения запроса).

Для своей задачи (расчёт квантилей времени загрузки страниц), использование этой функции эффективнее и результат точнее, чем для функции quantile.

quantileTimingWeighted(level)(x, weight)

Отличается от функции medianTiming наличием второго аргумента - “веса”. Вес - неотрицательное целое число. Результат считается так же, как если бы в функцию medianTiming значение x было передано weight количество раз.

quantileExact(level)(x)

Вычисляет квантиль уровня level точно. Для этого, все переданные значения складываются в массив, который затем частично сортируется. Поэтому, функция потребляет O(n) памяти, где n - количество переданных значений. Впрочем, для случая маленького количества значений, функция весьма эффективна.

quantileExactWeighted(level)(x, weight)

Вычисляет квантиль уровня level точно. При этом, каждое значение учитывается с весом weight - как будто оно присутствует weight раз. Аргументы функции можно рассматривать как гистограммы, где значению x соответствует “столбик” гистограммы высоты weight, а саму функцию можно рассматривать как суммирование гистограмм.

В качестве алгоритма используется хэш-таблица. Из-за этого, в случае, если передаваемые значения часто повторяются, функция потребляет меньше оперативки, чем quantileExact. Вы можете использовать эту функцию вместо quantileExact, указав в качестве веса число 1.

quantileTDigest(level)(x)

Вычисляет квантиль уровня level приближённо, с использованием алгоритма t-digest. Максимальная погрешность составляет 1%. Расход памяти на состояние пропорционален логарифму от количества переданных значений.

Производительность функции ниже quantile, quantileTiming. По соотношению размера состояния и точности, функция существенно лучше, чем quantile.

Результат зависит от порядка выполнения запроса, и является недетерминированным.

median

Для всех quantile-функций, также присутствуют соответствующие median-функции: median, medianDeterministic, medianTiming, medianTimingWeighted, medianExact, medianExactWeighted, medianTDigest. Они являются синонимами и их поведение ничем не отличается.

quantiles(level1, level2, ...)(x)

Для всех quantile-функций, также присутствуют соответствующие quantiles-функции: quantiles, quantilesDeterministic, quantilesTiming, quantilesTimingWeighted, quantilesExact, quantilesExactWeighted, quantilesTDigest. Эти функции за один проход вычисляют все квантили перечисленных уровней и возвращают массив вычисленных значений.

varSamp(x)

Вычисляет величину Σ((x - x̅)2) / (n - 1), где n - размер выборки, x̅ - среднее значение x.

Она представляет собой несмещённую оценку дисперсии случайной величины, если переданные в функцию значения являются выборкой этой случайной величины.

Возвращает Float64. В случае, когда n <= 1, возвращается +∞.

varPop(x)

Вычисляет величину Σ((x - x̅)2) / n, где n - размер выборки, x̅ - среднее значение x.

То есть, дисперсию для множества значений. Возвращает Float64.

stddevSamp(x)

Результат равен квадратному корню от varSamp(x).

stddevPop(x)

Результат равен квадратному корню от varPop(x).

covarSamp(x, y)

Вычисляет величину Σ((x - x̅)(y - y̅)) / (n - 1).

Возвращает Float64. В случае, когда n <= 1, возвращается +∞.

covarPop(x, y)

Вычисляет величину Σ((x - x̅)(y - y̅)) / n.

corr(x, y)

Вычисляет коэффициент корреляции Пирсона: Σ((x - x̅)(y - y̅)) / sqrt(Σ((x - x̅)2) * Σ((y - y̅)2)).

Параметрические агрегатные функции

Некоторые агрегатные функции могут принимать не только столбцы-аргументы (по которым производится свёртка), но и набор параметров - констант для инициализации. Синтаксис - две пары круглых скобок вместо одной. Первая - для параметров, вторая - для аргументов.

sequenceMatch(pattern)(time, cond1, cond2, ...)

Сопоставление с образцом для цепочки событий.

pattern - строка, содержащая шаблон для сопоставления. Шаблон похож на регулярное выражение.

time - время события, тип DateTime

cond1, cond2 ... - от одного до 32 аргументов типа UInt8 - признаков, было ли выполнено некоторое условие для события.

Функция собирает в оперативке последовательность событий. Затем производит проверку на соответствие этой последовательности шаблону. Возвращает UInt8 - 0, если шаблон не подходит и 1, если шаблон подходит.

Пример: sequenceMatch('(?1).*(?2)')(EventTime, URL LIKE '%company%', URL LIKE '%cart%')

  • была ли цепочка событий, в которой посещение страницы с адресом, содержащим company было раньше по времени посещения страницы с адресом, содержащим cart.

Это вырожденный пример. Его можно записать с помощью других агрегатных функций:

minIf(EventTime, URL LIKE '%company%') < maxIf(EventTime, URL LIKE '%cart%').

Но в более сложных случаях, такого решения нет.

Синтаксис шаблонов:

(?1) - ссылка на условие (вместо 1 - любой номер);

.* - произвольное количество любых событий;

(?t>=1800) - условие на время;

за указанное время допускается любое количество любых событий;

вместо >= могут использоваться операторы <, >, <=;

вместо 1800 может быть любое число;

События, произошедшие в одну секунду, могут оказаться в цепочке в произвольном порядке. От этого может зависеть результат работы функции.

sequenceCount(pattern)(time, cond1, cond2, ...)

Аналогично функции sequenceMatch, но возвращает не факт наличия цепочки событий, а UInt64 - количество найденных цепочек. Цепочки ищутся без перекрытия. То есть, следующая цепочка может начаться только после окончания предыдущей.

uniqUpTo(N)(x)

Вычисляет количество различных значений аргумента, если оно меньше или равно N. В случае, если количество различных значений аргумента больше N, возвращает N + 1.

Рекомендуется использовать для маленьких N - до 10. Максимальное значение N - 100.

Для состояния агрегатной функции используется количество оперативки равное 1 + N * размер одного значения байт. Для строк запоминается некриптографический хэш, имеющий размер 8 байт. То есть, для строк вычисление приближённое.

Функция также работает для нескольких аргументов.

Работает максимально быстро за исключением патологических случаев, когда используется большое значение N и количество уникальных значений чуть меньше N.

Пример применения:

Задача: показывать в отчёте только поисковые фразы, по которым было хотя бы 5 уникальных посетителей.
Решение: пишем в запросе GROUP BY SearchPhrase HAVING uniqUpTo(4)(UserID) >= 5

Комбинаторы агрегатных функций

К имени агрегатной функции может быть приписан некоторый суффикс. При этом, работа агрегатной функции некоторым образом модифицируется. Существуют комбинаторы If и Array. Смотрите разделы ниже.

Комбинатор -If. Условные агрегатные функции

К имени любой агрегатной функции может быть приписан суффикс -If. В этом случае, агрегатная функция принимает ещё один дополнительный аргумент - условие (типа UInt8). Агрегатная функция будет обрабатывать только те строки, для которых условие сработало. Если условие ни разу не сработало - возвращается некоторое значение по умолчанию (обычно - нули, пустые строки).

Примеры: sumIf(column, cond), countIf(cond), avgIf(x, cond), quantilesTimingIf(level1, level2)(x, cond), argMinIf(arg, val, cond) и т. п.

С помощью условных агрегатных функций, вы можете вычислить агрегаты сразу для нескольких условий, не используя подзапросы и JOIN-ы. Например, в Яндекс.Метрике, условные агрегатные функции используются для реализации функциональности сравнения сегментов.

Комбинатор -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 не должен быть массивом.

Комбинатор -State.

В случае применения этого комбинатора, агрегатная функция возвращает не готовое значение (например, в случае функции uniq - количество уникальных значений), а промежуточное состояние агрегации (например, в случае функции uniq - хэш-таблицу для рассчёта количества уникальных значений), которое имеет тип AggregateFunction(...) и может использоваться для дальнейшей обработки или может быть сохранено в таблицу для последующей доагрегации - смотрите разделы “AggregatingMergeTree” и “функции для работы с промежуточными состояниями агрегации”.

Комбинатор -Merge.

В случае применения этого комбинатора, агрегатная функция будет принимать в качестве аргумента промежуточное состояние агрегации, доагрегировать (объединять вместе) эти состояния, и возвращать готовое значение.

Комбинатор -MergeState.

Выполняет слияние промежуточных состояний агрегации, аналогично комбинатору -Merge, но возвращает не готовое значение, а промежуточное состояние агрегации, аналогично комбинатору -State.