Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Движок таблицы AggregatingMergeTree

Этот движок наследуется от MergeTree, изменяя логику слияния частей данных. ClickHouse заменяет все строки с одинаковым первичным ключом (или, точнее, с одинаковым ключом сортировки) одной строкой (в пределах одной части данных), которая хранит комбинацию состояний агрегатных функций.

Таблицы AggregatingMergeTree можно использовать для инкрементальной агрегации данных, в том числе для агрегированных materialized view.

Ниже в видео показан пример использования AggregatingMergeTree и агрегатных функций:

Движок обрабатывает все столбцы следующих типов:

AggregatingMergeTree целесообразно использовать, если это позволяет уменьшить количество строк на порядки.

Создание таблицы

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = AggregatingMergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[SAMPLE BY expr]
[TTL expr]
[SETTINGS name=value, ...]

Описание параметров запроса см. в разделе описание запроса.

Секции запроса

При создании таблицы AggregatingMergeTree требуются те же секции, что и при создании таблицы MergeTree.

Устаревший метод создания таблицы
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE [=] AggregatingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity)

Все параметры имеют тот же смысл, что и в MergeTree.

SELECT и INSERT

Чтобы вставить данные, используйте запрос INSERT SELECT с агрегатными функциями со суффиксом -State. При выборке данных из таблицы AggregatingMergeTree используйте оператор GROUP BY и те же агрегатные функции, что и при вставке данных, но с суффиксом -Merge.

В результатах запроса SELECT значения типа AggregateFunction имеют зависящее от реализации двоичное представление во всех форматах вывода ClickHouse. Например, если вы выгрузите данные в формат TabSeparated с помощью запроса SELECT, этот дамп можно затем загрузить обратно с помощью запроса INSERT.

Пример агрегированного materialized view

В следующем примере предполагается, что у вас есть база данных с именем test. Если она ещё не создана, выполните команду ниже:

CREATE DATABASE test;

Теперь создайте таблицу test.visits, содержащую исходные данные:

CREATE TABLE test.visits
 (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Sign Nullable(Int32),
    UserID Nullable(Int32)
) ENGINE = MergeTree ORDER BY (StartDate, CounterID);

Далее необходимо создать таблицу AggregatingMergeTree, которая будет хранить AggregationFunctions для отслеживания общего числа посещений и количества уникальных пользователей.

Создайте materialized view AggregatingMergeTree, который отслеживает таблицу test.visits и использует тип AggregateFunction:

CREATE TABLE test.agg_visits (
    StartDate DateTime64 NOT NULL,
    CounterID UInt64,
    Visits AggregateFunction(sum, Nullable(Int32)),
    Users AggregateFunction(uniq, Nullable(Int32))
)
ENGINE = AggregatingMergeTree() ORDER BY (StartDate, CounterID);

Создайте materialized view, который заполняет test.agg_visits данными из test.visits:

CREATE MATERIALIZED VIEW test.visits_mv TO test.agg_visits
AS SELECT
    StartDate,
    CounterID,
    sumState(Sign) AS Visits,
    uniqState(UserID) AS Users
FROM test.visits
GROUP BY StartDate, CounterID;

Вставьте данные в таблицу test.visits:

INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
VALUES (1667446031000, 1, 3, 4), (1667446031000, 1, 6, 3);

Данные вставляются в обе таблицы: test.visits и test.agg_visits.

Чтобы получить агрегированные данные, выполните запрос вида SELECT ... GROUP BY ... из materialized view test.visits_mv:

SELECT
    StartDate,
    sumMerge(Visits) AS Visits,
    uniqMerge(Users) AS Users
FROM test.visits_mv
GROUP BY StartDate
ORDER BY StartDate;
┌───────────────StartDate─┬─Visits─┬─Users─┐
│ 2022-11-03 03:27:11.000 │      9 │     2 │
└─────────────────────────┴────────┴───────┘

Добавьте ещё несколько записей в test.visits, но на этот раз укажите другую временную метку для одной из записей:

INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
VALUES (1669446031000, 2, 5, 10), (1667446031000, 3, 7, 5);

Выполните запрос SELECT ещё раз — он вернёт следующий результат:

┌───────────────StartDate─┬─Visits─┬─Users─┐
│ 2022-11-03 03:27:11.000 │     16 │     3 │
│ 2022-11-26 07:00:31.000 │      5 │     1 │
└─────────────────────────┴────────┴───────┘

В некоторых случаях может потребоваться избежать предварительной агрегации строк при вставке, чтобы перенести затраты на агрегацию с момента вставки на момент слияния. Как правило, чтобы избежать ошибки, необходимо включать в конструкцию GROUP BY определения materialized view те столбцы, которые не участвуют в агрегации. Однако этого можно добиться с помощью функции initializeAggregation и параметра optimize_on_insert = 0 (по умолчанию он включён). В таком случае использование GROUP BY больше не требуется:

CREATE MATERIALIZED VIEW test.visits_mv TO test.agg_visits
AS SELECT
    StartDate,
    CounterID,
    initializeAggregation('sumState', Sign) AS Visits,
    initializeAggregation('uniqState', UserID) AS Users
FROM test.visits;

Агрегация элементов Tuple

Когда включена настройка allow_tuple_element_aggregation, столбцы Tuple рекурсивно разворачиваются в плоскую структуру, так что каждый конечный элемент независимо участвует в агрегации. Это означает, что подстолбцы AggregateFunction или SimpleAggregateFunction внутри Tuple агрегируются в соответствии со своими функциями, как если бы они были столбцами верхнего уровня.

Подстолбцы, входящие в Tuple в ключе сортировки, исключаются из агрегации. Неагрегатные подстолбцы обрабатываются как обычные столбцы (сохраняется их первое значение).

CREATE TABLE agg_tuples
(
    key UInt32,
    metrics Tuple(
        total_visits SimpleAggregateFunction(sum, UInt64),
        unique_users SimpleAggregateFunction(max, UInt64)
    )
) ENGINE = AggregatingMergeTree()
ORDER BY key
SETTINGS allow_tuple_element_aggregation = 1;

INSERT INTO agg_tuples VALUES (1, (100, 5));
INSERT INTO agg_tuples VALUES (1, (200, 8));
INSERT INTO agg_tuples VALUES (2, (50, 3));

OPTIMIZE TABLE agg_tuples FINAL;

SELECT key, metrics.total_visits, metrics.unique_users FROM agg_tuples ORDER BY key;
┌─key─┬─metrics.total_visits─┬─metrics.unique_users─┐
│   1 │                  300 │                    8 │
│   2 │                   50 │                    3 │
└─────┴──────────────────────┴──────────────────────┘

total_visits агрегируется функцией sum (100 + 200 = 300), а unique_users — функцией max (max(5, 8) = 8).

Navigation