From 88f2908800742d9d0f01222ae7964e9a77d7b271 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Fri, 7 Sep 2018 16:55:38 +0300 Subject: [PATCH 1/8] Update of english version of descriprion of the table function `file`. --- .../en/query_language/table_functions/file.md | 45 ++++++++++++++++--- docs/ru/operations/configuration_files.md | 2 +- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/docs/en/query_language/table_functions/file.md b/docs/en/query_language/table_functions/file.md index 67eb5742988..a6b9006d69f 100644 --- a/docs/en/query_language/table_functions/file.md +++ b/docs/en/query_language/table_functions/file.md @@ -2,17 +2,48 @@ # file -`file(path, format, structure)` - returns a table created from a path file with a format type, with columns specified in structure. +Creates a table from a file. -path - a relative path to a file from [user_files_path](../../operations/server_settings/settings.md#user_files_path). +``` +file(path, format, structure) +``` -format - file [format](../../interfaces/formats.md#formats). +**Input parameters** -structure - table structure in 'UserID UInt64, URL String' format. Determines column names and types. +- `path` — The relative path to the file from [user_files_path](../../operations/server_settings/settings.md#user_files_path). +- `format` — The [format](../../interfaces/formats.md#formats) of the file. +- `structure` — Structure of the table. Format `'colunmn1_name column1_ype, column2_name column2_type, ...'`. + +**Returned value** + +A table with the specified structure for reading or writing data in the specified file. **Example** -```sql --- getting the first 10 lines of a table that contains 3 columns of UInt32 type from a CSV file -SELECT * FROM file('test.csv', 'CSV', 'column1 UInt32, column2 UInt32, column3 UInt32') LIMIT 10 +Setting `user_files_path` and the contents of the file `test.csv`: + +```bash +$ grep user_files_path /etc/clickhouse-server/config.xml + /var/lib/clickhouse/user_files/ + +$ cat /var/lib/clickhouse/user_files/test.csv + 1,2,3 + 3,2,1 + 78,43,45 ``` + +Table from`test.csv` and selection of the first two rows from it: + +```sql +SELECT * +FROM file('test.csv', 'CSV', 'column1 UInt32, column2 UInt32, column3 UInt32') +LIMIT 2 +``` + +``` +┌─column1─┬─column2─┬─column3─┐ +│ 1 │ 2 │ 3 │ +│ 3 │ 2 │ 1 │ +└─────────┴─────────┴─────────┘ +``` + diff --git a/docs/ru/operations/configuration_files.md b/docs/ru/operations/configuration_files.md index ab4f3d4ebd5..97670cd9118 100644 --- a/docs/ru/operations/configuration_files.md +++ b/docs/ru/operations/configuration_files.md @@ -16,7 +16,7 @@ Также в конфиге могут быть указаны "подстановки". Если у элемента присутствует атрибут `incl`, то в качестве значения будет использована соответствующая подстановка из файла. По умолчанию, путь к файлу с подстановками - `/etc/metrika.xml`. Он может быть изменён в конфигурации сервера в элементе [include_from](server_settings/settings.md#server_settings-include_from). Значения подстановок указываются в элементах `/yandex/имя_подстановки` этого файла. Если подстановка, заданная в `incl` отсутствует, то в лог попадает соответствующая запись. Чтобы ClickHouse не писал в лог об отсутствии подстановки, необходимо указать атрибут `optional="true"` (например, настройка [macros](server_settings/settings.md#server_settings-macros)). -Подстановки могут также выполняться из ZooKeeper. Для этого укажите у элемента атрибут `from_zk="/path/to/node"`. Значение элемента заменится на содержимое узла `/path/to/node` в ZooKeeper. В ZooKeeper-узел также можно положить целое XML-поддерево, оно будет целиком вставлено в исходный элемент. +Подстановки могут также выполняться из ZooKeeper. Для этого укажите у элемента атрибут `from_zk = "/path/to/node"`. Значение элемента заменится на содержимое узла `/path/to/node` в ZooKeeper. В ZooKeeper-узел также можно положить целое XML-поддерево, оно будет целиком вставлено в исходный элемент. В `config.xml` может быть указан отдельный конфиг с настройками пользователей, профилей и квот. Относительный путь к нему указывается в элементе users_config. По умолчанию - `users.xml`. Если `users_config` не указан, то настройки пользователей, профилей и квот, указываются непосредственно в `config.xml`. From 90b1f5f71b7b74942b541f9536d05627e50b4d57 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Wed, 19 Sep 2018 11:20:17 +0300 Subject: [PATCH 2/8] New syntax for ReplacingMergeTree. Some improvements in text. --- .../table_engines/replacingmergetree.md | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/docs/ru/operations/table_engines/replacingmergetree.md b/docs/ru/operations/table_engines/replacingmergetree.md index 8e09810ca2b..305971f553a 100644 --- a/docs/ru/operations/table_engines/replacingmergetree.md +++ b/docs/ru/operations/table_engines/replacingmergetree.md @@ -1,17 +1,40 @@ # ReplacingMergeTree -Движок таблиц отличается от `MergeTree` тем, что выполняет удаление дублирующихся записей с одинаковым значением первичного ключа. +Движок отличается от [MergeTree](mergetree.md#table_engines-mergetree) тем, что выполняет удаление дублирующихся записей с одинаковым значением первичного ключа. -Последний, необязательный параметр движка таблицы — столбец с версией. При слиянии для всех строк с одинаковым значением первичного ключа оставляет только одну строку: если задан столбец версии — строку с максимальной версией, иначе — последнюю строку. +Дедупликация данных производится лишь во время слияний. Слияние происходят в фоне в неизвестный момент времени, на который вы не можете ориентироваться. Некоторая часть данных может остаться необработанной. Хотя вы можете вызвать внеочередное слияние с помощью запроса `OPTIMIZE`, на это не стоит рассчитывать, так как запрос `OPTIMIZE` приводит к чтению и записи большого объёма данных. -Столбец с версией должен иметь тип из семейства `UInt`, или `Date`, или `DateTime`. +Таким образом, `ReplacingMergeTree` подходит для фоновой чистки дублирующихся данных в целях экономии места, но не даёт гарантии отсутствия дубликатов. -```sql -ReplacingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, ver) +## Конфигурирование движка при создании таблицы + +``` +ENGINE [=] ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] ``` -Обратите внимание, что дедупликация данных производится лишь во время слияний. Слияние происходят в фоне в неизвестный момент времени, на который вы не можете ориентироваться. Некоторая часть данных может так и остаться необработанной. Хотя вы можете вызвать внеочередное слияние с помощью запроса OPTIMIZE, на это не стоит рассчитывать, так как запрос OPTIMIZE приводит к чтению и записи большого объёма данных. +**Параметры ReplacingMergeTree** -Таким образом, `ReplacingMergeTree` подходит для фоновой чистки дублирующихся данных в целях экономии места, но не даёт гарантий отсутствия дубликатов. +- `ver` — столбец с версией, тип `UInt*`, `Date` или `DateTime`. Необязательный параметр. -*Движок не используется в Яндекс.Метрике, но нашёл своё применение в других отделах Яндекса.* + При слиянии, из всех строк с одинаковым значением первичного ключа `ReplacingMergeTree` оставляет только одну: + + - Последнюю в выборке, если `ver` не задан. + - С максимальной версией, если `ver` задан. + +**Секции ENGINE** + +`ReplacingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. + + +### Устаревший способ конфигурирования движка + +!!!attention + Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. + +```sql +ReplacingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, [ver]) +``` + +Все параметры, кроме `ver` имеют то же значение, что в и `MergeTree`. + +- `ver` — столбец с версией. Необязательный параметр. Описание смотрите выше по тексту. From 0f2ba3043a5e5c936a75247dbea5cde9f6b08c7c Mon Sep 17 00:00:00 2001 From: BayoNet Date: Wed, 19 Sep 2018 11:21:53 +0300 Subject: [PATCH 3/8] Significantly change article about SummingMergeTree. Article is restructured, text is changed in many places of the document. New syntax for table creation is described. --- .../aggregatefunction.md | 2 + .../table_engines/aggregatingmergetree.md | 2 + docs/ru/operations/table_engines/mergetree.md | 1 + .../table_engines/summingmergetree.md | 105 +++++++++++++++--- .../query_language/agg_functions/reference.md | 2 + 5 files changed, 94 insertions(+), 18 deletions(-) diff --git a/docs/ru/data_types/nested_data_structures/aggregatefunction.md b/docs/ru/data_types/nested_data_structures/aggregatefunction.md index a15205cf120..c9161e63c04 100644 --- a/docs/ru/data_types/nested_data_structures/aggregatefunction.md +++ b/docs/ru/data_types/nested_data_structures/aggregatefunction.md @@ -1,3 +1,5 @@ + + # AggregateFunction(name, types_of_arguments...) Промежуточное состояние агрегатной функции. Чтобы его получить, используются агрегатные функции с суффиксом -State. Подробнее смотрите в разделе "AggregatingMergeTree". diff --git a/docs/ru/operations/table_engines/aggregatingmergetree.md b/docs/ru/operations/table_engines/aggregatingmergetree.md index cff9116988d..86b1fe56896 100644 --- a/docs/ru/operations/table_engines/aggregatingmergetree.md +++ b/docs/ru/operations/table_engines/aggregatingmergetree.md @@ -1,3 +1,5 @@ + + # AggregatingMergeTree Отличается от `MergeTree` тем, что при слиянии, выполняет объединение состояний агрегатных функций, хранимых в таблице, для строчек с одинаковым значением первичного ключа. diff --git a/docs/ru/operations/table_engines/mergetree.md b/docs/ru/operations/table_engines/mergetree.md index f41e165f7b4..b05a6506820 100644 --- a/docs/ru/operations/table_engines/mergetree.md +++ b/docs/ru/operations/table_engines/mergetree.md @@ -25,6 +25,7 @@ При необходимости можно задать способ сэмплирования данных в таблице. + ## Конфигурирование движка при создании таблицы diff --git a/docs/ru/operations/table_engines/summingmergetree.md b/docs/ru/operations/table_engines/summingmergetree.md index 6aa2f116d72..b42576b1dc5 100644 --- a/docs/ru/operations/table_engines/summingmergetree.md +++ b/docs/ru/operations/table_engines/summingmergetree.md @@ -2,33 +2,104 @@ # SummingMergeTree -Отличается от `MergeTree` тем, что суммирует данные при слиянии. +Движок наследует функциональность [MergeTree](mergetree.md#table_engines-mergetree). Отличие заключается в том, что для таблиц `SummingMergeTree` при слиянии кусков данных ClickHouse все строки с одинаковым первичным ключом старается заменить на одну, которая хранит только суммы столбцов с цифровым типом данных. Если первичный ключ подобран таким образом, что одному значению ключа соответствует много строк, это значительно уменьшает объем хранения и ускоряет последующую выборку данных. -```sql -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192) +Мы рекомендуем использовать движок в паре с `MergeTree`. В `MergeTree` храните полные данные, а `SummingMergeTree` используйте для хранения агрегированных данных, например, при подготовке отчетов. Такой подход позволит не утратить ценные данные из-за неправильно выбранного первичного ключа. + +## Конфигурирование движка при создании таблицы + +``` +ENGINE [=] SummingMergeTree([columns]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] ``` -Столбцы для суммирования заданы неявно. При слиянии, для всех строчек с одинаковым значением первичного ключа (в примере - OrderID, EventDate, BannerID, ...), производится суммирование значений в числовых столбцах, не входящих в первичный ключ. +**Параметры SummingMergeTree** + +- `columns` — кортеж с именами столбцов для суммирования данных. Необязательный параметр. + Столбцы должны иметь числовой тип и не должны входить в первичный ключ. + +**Секции ENGINE** + +`SummingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. + + +### Устаревший способ конфигурирования движка + +!!!attention + Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, (Shows, Clicks, Cost, ...)) +SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, [columns]) ``` -Явно заданные столбцы для суммирования (последний параметр - Shows, Clicks, Cost, ...). При слиянии, для всех строчек с одинаковым значением первичного ключа, производится суммирование значений в указанных столбцах. Указанные столбцы также должны быть числовыми и не входить в первичный ключ. +Все параметры, кроме `columns` имеют то же значение, что в и `MergeTree`. -Если значения во всех таких столбцах оказались нулевыми, то строчка удаляется. +- `columns` — кортеж с именами столбцов для суммирования данных. Необязательный параметр. Описание смотрите выше по тексту. -Для остальных столбцов, не входящих в первичный ключ, при слиянии выбирается первое попавшееся значение. Но для столбцов типа AggregateFunction выполняется агрегация согласно заданной функции, так что этот движок фактически ведёт себя как `AggregatingMergeTree`. -При чтении, суммирование не делается само по себе. Если оно необходимо - напишите соответствующий GROUP BY. +## Пример использования -Дополнительно, таблица может иметь вложенные структуры данных, которые обрабатываются особым образом. -Если название вложенной таблицы заканчивается на Map и она содержит не менее двух столбцов, удовлетворяющих следующим критериям: +Рассмотрим следующую таблицу: -- первый столбец - числовой ((U)IntN, Date, DateTime), назовем его условно key, -- остальные столбцы - арифметические ((U)IntN, Float32/64), условно (values...), +```sql +CREATE TABLE summtt +( + key UInt32, + value UInt32 +) +ENGINE = SummingMergeTree() +ORDER BY key +``` -то такая вложенная таблица воспринимается как отображение key `=>` (values...) и при слиянии ее строк выполняется слияние элементов двух множеств по key со сложением соответствующих (values...). +Добавим в неё данные: + +``` +:) INSERT INTO summtt Values(1,1),(1,2),(2,1) +``` + +ClickHouse может не просуммировать данные или просуммировать их не полностью (смотрите ниже по тексту), поэтому при запросе мы используем агрегатную функцию `sum` и секцию `GROUP BY`. + +```sql +SELECT key, sum(value) FROM summtt GROUP BY key +``` +``` +┌─key─┬─sum(value)─┐ +│ 2 │ 1 │ +│ 1 │ 3 │ +└─────┴────────────┘ +``` + +## Особенности обработки параметров и данных + +При вставке данных в таблицу они сохраняются как есть. Периодически ClickHouse выполняет слияние вставленных кусков данных и именно в этот момент производится суммирование и замена многих строк с одинаковым первичным ключом на одну. + +ClickHouse рассчитывает расход ресурсов и может не выполнить суммирование или выполнить его не полностью, если посчитает эту операцию слишком затратной. Поэтому, при выборке данных (`SELECT`) необходимо использовать агрегатную функцию [sum()](../../query_language/agg_functions/reference.md#agg_function-sum) и секцию `GROUP BY` как описано в примере выше. + +### Суммирование простых данных + +Если столбцы для суммирования не заданы, то ClickHouse суммирует значения в столбцах с числовым типом данных, не входящих в первичный ключ. + +Если столбцы для суммирования заданы, то для всех строчек с одинаковым значением первичного ключа ClickHouse суммирует значения в указанных столбцах. Столбцы должны обязательно иметь числовой тип данных. + +Если значения во всех столбцах для суммирования оказались нулевыми, то строчка удаляется. + +Для столбцов, не входящих в первичный ключ и не суммирующихся, выбирается произвольное значение из имеющихся. + +Столбцы, входящие в первичный ключ не суммируются. + +### Суммирование в столбцах AggregateFunction + +Для столбцов типа [AggregateFunction](../../data_types/nested_data_structures/aggregatefunction.md#data_type-aggregatefunction) ClickHouse выполняет агрегацию согласно заданной функции, повторяя поведение движка [AggregatingMergeTree](aggregatingmergetree.md#table_engine-aggregatingmergetree). + +### Обработка вложенных структур + +Таблица может иметь вложенные структуры данных, которые обрабатываются особым образом. + +Если название вложенной таблицы заканчивается на `Map` и она содержит не менее двух столбцов, удовлетворяющих критериям: + +- первый столбец - числовой `(*Int*, Date, DateTime)`, назовем его условно `key`, +- остальные столбцы - арифметические `(*Int*, Float32/64)`, условно `(values...)`, + +то вложенная таблица воспринимается как отображение `key => (values...)` и при слиянии её строк выполняется слияние элементов двух множеств по `key` со сложением соответствующих `(values...)`. Примеры: @@ -39,8 +110,6 @@ SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, (Shows, C [(1, 100), (2, 150)] + [(1, -100)] -> [(2, 150)] ``` -Для агрегации Map используйте функцию sumMap(key, value). +При запросе данных используйте функцию [sumMap(key, value)](../../query_language/agg_functions/reference.md#agg_function-summap) для агрегации `Map`. -Для вложенных структур данных не нужно указывать её столбцы в качестве списка столбцов для суммирования. - -Этот движок таблиц разработан по просьбе БК, и является мало полезным. Помните, что при хранении лишь предагрегированных данных, вы теряете часть преимуществ системы. +Для вложенной структуры данных не нужно указывать её столбцы в кортеже столбцов для суммирования. diff --git a/docs/ru/query_language/agg_functions/reference.md b/docs/ru/query_language/agg_functions/reference.md index 35f79533003..4c084eb35bd 100644 --- a/docs/ru/query_language/agg_functions/reference.md +++ b/docs/ru/query_language/agg_functions/reference.md @@ -73,6 +73,7 @@ FROM ontime Вычисляет значение arg при максимальном значении val. Если есть несколько разных значений arg для максимальных значений val, то выдаётся первое попавшееся из таких значений. + ## sum(x) @@ -86,6 +87,7 @@ FROM ontime Работает только для чисел. + ## sumMap(key, value) From 3a53ba0cb0e31be404b134ed3b66824a09d98eb9 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Tue, 25 Sep 2018 00:24:03 +0300 Subject: [PATCH 4/8] Descriptions of AggregateFunction and AggregatingMergeTree are updated. Russian version. --- .../aggregatefunction.md | 60 +++++++++++- .../table_engines/aggregatingmergetree.md | 94 ++++++++----------- docs/ru/operations/table_engines/mergetree.md | 10 +- .../table_engines/replacingmergetree.md | 4 +- .../table_engines/summingmergetree.md | 26 ++--- .../agg_functions/combinators.md | 2 + .../query_language/agg_functions/reference.md | 6 +- 7 files changed, 128 insertions(+), 74 deletions(-) diff --git a/docs/ru/data_types/nested_data_structures/aggregatefunction.md b/docs/ru/data_types/nested_data_structures/aggregatefunction.md index c9161e63c04..9d86c26efed 100644 --- a/docs/ru/data_types/nested_data_structures/aggregatefunction.md +++ b/docs/ru/data_types/nested_data_structures/aggregatefunction.md @@ -2,4 +2,62 @@ # AggregateFunction(name, types_of_arguments...) -Промежуточное состояние агрегатной функции. Чтобы его получить, используются агрегатные функции с суффиксом -State. Подробнее смотрите в разделе "AggregatingMergeTree". +Промежуточное состояние агрегатной функции. Чтобы его получить, используются агрегатные функции с суффиксом `-State`. Чтобы в дальнейшем получить агрегированные данные необходимо использовать те же агрегатные функции с суффиксом `-Merge`. + +`AggregateFunction` — параметрический тип данных. + +**Параметры** + +- Имя агрегатной функции. + + Для параметрических агрегатных функций указываются их параметры. + +- Типы аргументов агрегатной функции. + +**Пример** + +```sql +CREATE TABLE t +( + column1 AggregateFunction(uniq, UInt64), + column2 AggregateFunction(anyIf, String, UInt8), + column3 AggregateFunction(quantiles(0.5, 0.9), UInt64) +) ENGINE = ... +``` + +[uniq](../../query_language/agg_functions/reference.md#agg_function-uniq), anyIf ([any](../../query_language/agg_functions/reference.md#agg_function-any)+[If](../../query_language/agg_functions/combinators.md#agg-functions-combinator-if)) и [quantiles](../../query_language/agg_functions/reference.md#agg_function-quantiles) — агрегатные функции, поддержанные в ClickHouse. + +## Особенности использования + +### Вставка данных + +Для вставки данных используйте `INSERT SELECT` с агрегатными `-State`-функциями. + +**Примеры функций** + +``` +uniqState(UserID) +quantilesState(0.5, 0.9)(SendTiming) +``` + +В отличие от соответствующих функций `uniq` и `quantiles`, `-State`-функциями возвращают не готовое значение, а состояние. То есть, значение типа `AggregateFunction`. + +В запросах `SELECT` значения типа `AggregateFunction` выводятся во всех форматах, которые поддерживает ClickHouse, в виде implementation-specific бинарных данных. Если с помощью `SELECT` выполнить дамп данных, например, в формат `TabSeparated`, то потом этот дамп можно загрузить обратно с помощью запроса `INSERT`. + +### Выборка данных + +При выборке данных из таблицы `AggregatingMergeTree`, используйте `GROUP BY` и те же агрегатные функции, что и при вставке данных, но с суффиксом `-Merge`. + +Агрегатная функция с суффиксом `-Merge` берёт множество состояний, объединяет их, и возвращает результат полной агрегации данных. + +Например, следующие два запроса возвращают один и тот же результат: + +```sql +SELECT uniq(UserID) FROM table + +SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP BY RegionID) +``` + +## Пример использования + +Смотрите в описании движка [AggregatingMergeTree](../../operations/table_engines/aggregatingmergetree.md#table_engine-aggregatingmergetree). diff --git a/docs/ru/operations/table_engines/aggregatingmergetree.md b/docs/ru/operations/table_engines/aggregatingmergetree.md index 86b1fe56896..d17484ed9e1 100644 --- a/docs/ru/operations/table_engines/aggregatingmergetree.md +++ b/docs/ru/operations/table_engines/aggregatingmergetree.md @@ -2,61 +2,51 @@ # AggregatingMergeTree -Отличается от `MergeTree` тем, что при слиянии, выполняет объединение состояний агрегатных функций, хранимых в таблице, для строчек с одинаковым значением первичного ключа. - -Чтобы это работало, используются: тип данных `AggregateFunction`, а также модификаторы `-State` и `-Merge` для агрегатных функций. Рассмотрим подробнее. - -Существует тип данных `AggregateFunction`. Это параметрический тип данных. В качестве параметров передаются: имя агрегатной функции, затем типы её аргументов. - -Примеры: - -```sql -CREATE TABLE t -( - column1 AggregateFunction(uniq, UInt64), - column2 AggregateFunction(anyIf, String, UInt8), - column3 AggregateFunction(quantiles(0.5, 0.9), UInt64) -) ENGINE = ... -``` - -Столбец такого типа хранит состояние агрегатной функции. - -Чтобы получить значение такого типа, следует использовать агрегатные функции с суффиксом `State`. - -Пример: -`uniqState(UserID), quantilesState(0.5, 0.9)(SendTiming)` - -В отличие от соответствующих функций `uniq`, `quantiles`, такие функции возвращают не готовое значение, а состояние. То есть, значение типа `AggregateFunction`. - -Значение типа `AggregateFunction` нельзя вывести в Pretty-форматах. В других форматах, значения такого типа выводятся в виде implementation-specific бинарных данных. То есть, значения типа `AggregateFunction` не предназначены для вывода, сохранения в дамп. - -Единственную полезную вещь, которую можно сделать со значениями типа `AggregateFunction` — это объединить состояния и получить результат, по сути — доагрегировать до конца. Для этого используются агрегатные функции с суффиксом Merge. -Пример: `uniqMerge(UserIDState)`, где `UserIDState` имеет тип `AggregateFunction`. - -То есть, агрегатная функция с суффиксом Merge берёт множество состояний, объединяет их, и возвращает готовый результат. -Для примера, эти два запроса возвращают один и тот же результат: - -```sql -SELECT uniq(UserID) FROM table - -SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP BY RegionID) -``` - -Существует движок `AggregatingMergeTree`. Он занимается тем, что при слияниях, выполняет объединение состояний агрегатных функций из разных строчек таблицы с одним значением первичного ключа. - -В таблицу, содержащую столбцы типа `AggregateFunction` невозможно вставить строчку обычным запросом INSERT, так как невозможно явно указать значение типа `AggregateFunction`. Вместо этого, для вставки данных, следует использовать `INSERT SELECT` с агрегатными функциями `-State`. - -При SELECT-е из таблицы `AggregatingMergeTree`, используйте GROUP BY и агрегатные функции с модификатором -Merge, чтобы доагрегировать данные. +Движок наследует функциональность [MergeTree](mergetree.md#table_engines-mergetree), изменяя логику слияния кусков данных. Все строки с одинаковым первичным ключом ClickHouse заменяет на одну (в пределах одного куска данных), которая хранит объединение состояний агрегатных функций. Таблицы типа `AggregatingMergeTree` могут использоваться для инкрементальной агрегации данных, в том числе, для агрегирующих материализованных представлений. -Пример: +Движок обрабатывает все столбцы типа [AggregateFunction](../../data_types/nested_data_structures/aggregatefunction.md#data_type-aggregatefunction). + +Использование `AggregatingMergeTree` оправдано только в том случае, когда это уменьшает количество строк на порядки. + +## Конфигурирование движка при создании таблицы + +``` +ENGINE [=] AggregatingMergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +``` + +**Подчиненные секции ENGINE** + +`AggregatingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. + + +### Устаревший способ конфигурирования движка + +!!!attention + Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. + +```sql +AggregatingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity) +``` + +Все параметры имеют то же значение, что в и `MergeTree`. + +## SELECT/INSERT данных + +Для вставки данных используйте `INSERT SELECT` с агрегатными `-State`-функциями. + +При выборке данных из таблицы `AggregatingMergeTree`, используйте `GROUP BY` и те же агрегатные функции, что и при вставке данных, но с суффиксом `-Merge`. + +В запросах `SELECT` значения типа `AggregateFunction` выводятся во всех форматах, которые поддерживает ClickHouse, в виде implementation-specific бинарных данных. Если с помощью `SELECT` выполнить дамп данных, например, в формат `TabSeparated`, то потом этот дамп можно загрузить обратно с помощью запроса `INSERT`. + +## Пример агрегирущего материализованного представления Создаём материализованное представление типа `AggregatingMergeTree`, следящее за таблицей `test.visits`: ```sql CREATE MATERIALIZED VIEW test.basic -ENGINE = AggregatingMergeTree(StartDate, (CounterID, StartDate), 8192) +ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(StartDate) ORDER BY (CounterID, StartDate) AS SELECT CounterID, StartDate, @@ -66,13 +56,15 @@ FROM test.visits GROUP BY CounterID, StartDate; ``` -Вставляем данные в таблицу `test.visits`. Данные будут также вставлены в представление, где они будут агрегированы: +Вставляем данные в таблицу `test.visits`: ```sql INSERT INTO test.visits ... ``` -Делаем `SELECT` из представления, используя `GROUP BY`, чтобы доагрегировать данные: +Данные окажутся и в таблице и в представлении `test.basic`, которое выполнит агрегацию. + +Чтобы получить агрегированные данные, выполним запрос вида `SELECT ... GROUP BY ...` из представления `test.basic`: ```sql SELECT @@ -83,7 +75,3 @@ FROM test.basic GROUP BY StartDate ORDER BY StartDate; ``` - -Вы можете создать такое материализованное представление и навесить на него обычное представление, выполняющее доагрегацию данных. - -Заметим, что в большинстве случаев, использование `AggregatingMergeTree` является неоправданным, так как можно достаточно эффективно выполнять запросы по неагрегированным данным. diff --git a/docs/ru/operations/table_engines/mergetree.md b/docs/ru/operations/table_engines/mergetree.md index b05a6506820..453a2ecc211 100644 --- a/docs/ru/operations/table_engines/mergetree.md +++ b/docs/ru/operations/table_engines/mergetree.md @@ -33,12 +33,12 @@ ENGINE [=] MergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] ``` -**Секции ENGINE** +**Подчиненные секции ENGINE** - `ORDER BY` — первичный ключ. Кортеж столбцов или произвольных выражений. Пример: `ORDER BY (CounerID, EventDate)`. - Если используется ключ сэмплирования, то первичный ключ должен содержать его. Пример: `ORDER BY (CounerID, EventDate, intHash32(UserID))`. + Если используется выражение для сэмплирования, то первичный ключ должен содержать его. Пример: `ORDER BY (CounerID, EventDate, intHash32(UserID))`. - `PARTITION BY` — [ключ партиционирования](custom_partitioning_key.md#table_engines-custom_partitioning_key). @@ -92,7 +92,7 @@ MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID) При вставке в таблицу создаются отдельные куски данных, каждый из которых лексикографически отсортирован по первичному ключу. Например, если первичный ключ — `(CounterID, Date)`, то данные в куске будут лежать в порядке `CounterID`, а для каждого `CounterID` в порядке `Date`. -Данные, относящиеся к разным партициям, разбиваются на разные куски. В фоновом режиме ClickHouse выполняет слияния (merge) кусков данных для более эффективного хранения. Куски, относящиеся к разным партициям не объединяются. +Данные, относящиеся к разным партициям, разбиваются на разные куски. В фоновом режиме ClickHouse выполняет слияния (merge) кусков данных для более эффективного хранения. Куски, относящиеся к разным партициям не объединяются. Механизм слияния не гарантирует, что все строки с одинаковым первичным ключом окажутся в одном куске. Для каждого куска данных ClickHouse создаёт индексный файл, который содержит значение первичного ключа для каждой индексной строки («засечка»). Номера индексных строк определяются как `n * index_granularity`, а максимальное значение `n` равно целой части от деления общего количества строк на `index_granularity`. Для каждого столбца также пишутся «засечки» для тех же индексных строк, что и для первичного ключа, эти «засечки» позволяют находить непосредственно данные в столбцах. @@ -140,9 +140,9 @@ ClickHouse не требует уникального первичного кл ClickHouse сортирует данные по первичному ключу, поэтому чем выше однородность, тем лучше сжатие. -- Обеспечить дополнительную логику при слиянии в движках [CollapsingMergeTree](collapsingmergetree.md#table_engine-collapsingmergetree) и [SummingMergeTree](summingmergetree.md#table_engine-summingmergetree). +- Обеспечить дополнительную логику при слиянии кусков данных в движках [CollapsingMergeTree](collapsingmergetree.md#table_engine-collapsingmergetree) и [SummingMergeTree](summingmergetree.md#table_engine-summingmergetree). - Может потребоваться иметь много полей в первичном ключе, даже если они не нужны для выполнения предыдущих пунктов. + Может потребоваться много полей в первичном ключе, даже если они не нужны для выполнения предыдущих пунктов. Длинный первичный ключ будет негативно влиять на производительность вставки и потребление памяти, однако на производительность ClickHouse при запросах `SELECT` лишние столбцы в первичном ключе не влияют. diff --git a/docs/ru/operations/table_engines/replacingmergetree.md b/docs/ru/operations/table_engines/replacingmergetree.md index 305971f553a..b74b513a4d7 100644 --- a/docs/ru/operations/table_engines/replacingmergetree.md +++ b/docs/ru/operations/table_engines/replacingmergetree.md @@ -21,7 +21,7 @@ ENGINE [=] ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE - Последнюю в выборке, если `ver` не задан. - С максимальной версией, если `ver` задан. -**Секции ENGINE** +**Подчиненные секции ENGINE** `ReplacingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. @@ -32,7 +32,7 @@ ENGINE [=] ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql -ReplacingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, [ver]) +ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver]) ``` Все параметры, кроме `ver` имеют то же значение, что в и `MergeTree`. diff --git a/docs/ru/operations/table_engines/summingmergetree.md b/docs/ru/operations/table_engines/summingmergetree.md index b42576b1dc5..12a48670398 100644 --- a/docs/ru/operations/table_engines/summingmergetree.md +++ b/docs/ru/operations/table_engines/summingmergetree.md @@ -2,7 +2,7 @@ # SummingMergeTree -Движок наследует функциональность [MergeTree](mergetree.md#table_engines-mergetree). Отличие заключается в том, что для таблиц `SummingMergeTree` при слиянии кусков данных ClickHouse все строки с одинаковым первичным ключом старается заменить на одну, которая хранит только суммы столбцов с цифровым типом данных. Если первичный ключ подобран таким образом, что одному значению ключа соответствует много строк, это значительно уменьшает объем хранения и ускоряет последующую выборку данных. +Движок наследует функциональность [MergeTree](mergetree.md#table_engines-mergetree). Отличие заключается в том, что для таблиц `SummingMergeTree` при слиянии кусков данных ClickHouse все строки с одинаковым первичным ключом заменяет на одну, которая хранит только суммы значений из столбцов с цифровым типом данных. Если первичный ключ подобран таким образом, что одному значению ключа соответствует много строк, это значительно уменьшает объем хранения и ускоряет последующую выборку данных. Мы рекомендуем использовать движок в паре с `MergeTree`. В `MergeTree` храните полные данные, а `SummingMergeTree` используйте для хранения агрегированных данных, например, при подготовке отчетов. Такой подход позволит не утратить ценные данные из-за неправильно выбранного первичного ключа. @@ -17,9 +17,11 @@ ENGINE [=] SummingMergeTree([columns]) [PARTITION BY expr] [ORDER BY expr] [SAMP - `columns` — кортеж с именами столбцов для суммирования данных. Необязательный параметр. Столбцы должны иметь числовой тип и не должны входить в первичный ключ. -**Секции ENGINE** + Если `columns` не задан, то ClickHouse суммирует значения во всех столбцах с числовым типом данных, не входящих в первичный ключ. -`SummingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. +**Подчиненные секции ENGINE** + +`SummingMergeTree` использует те же [подчиненные секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. ### Устаревший способ конфигурирования движка @@ -28,7 +30,7 @@ ENGINE [=] SummingMergeTree([columns]) [PARTITION BY expr] [ORDER BY expr] [SAMP Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, [columns]) +SummingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [columns]) ``` Все параметры, кроме `columns` имеют то же значение, что в и `MergeTree`. @@ -56,7 +58,7 @@ ORDER BY key :) INSERT INTO summtt Values(1,1),(1,2),(2,1) ``` -ClickHouse может не просуммировать данные или просуммировать их не полностью (смотрите ниже по тексту), поэтому при запросе мы используем агрегатную функцию `sum` и секцию `GROUP BY`. +ClickHouse может не полностью просуммировать все строки ([смотрите ниже по тексту](#summingmergetree-data-processing)), поэтому при запросе мы используем агрегатную функцию `sum` и секцию `GROUP BY`. ```sql SELECT key, sum(value) FROM summtt GROUP BY key @@ -68,17 +70,17 @@ SELECT key, sum(value) FROM summtt GROUP BY key └─────┴────────────┘ ``` -## Особенности обработки параметров и данных + -При вставке данных в таблицу они сохраняются как есть. Периодически ClickHouse выполняет слияние вставленных кусков данных и именно в этот момент производится суммирование и замена многих строк с одинаковым первичным ключом на одну. +## Обработка данных -ClickHouse рассчитывает расход ресурсов и может не выполнить суммирование или выполнить его не полностью, если посчитает эту операцию слишком затратной. Поэтому, при выборке данных (`SELECT`) необходимо использовать агрегатную функцию [sum()](../../query_language/agg_functions/reference.md#agg_function-sum) и секцию `GROUP BY` как описано в примере выше. +При вставке данных в таблицу они сохраняются как есть. Периодически ClickHouse выполняет слияние вставленных кусков данных и именно в этот момент производится суммирование и замена многих строк с одинаковым первичным ключом на одну в пределах одного куска данных. -### Суммирование простых данных +ClickHouse может слить куски данных таким образом, что не все строки с одинаковым первичным ключом окажутся в одном финальном куске, т.е. суммирование будет не полным. Поэтому, при выборке данных (`SELECT`) необходимо использовать агрегатную функцию [sum()](../../query_language/agg_functions/reference.md#agg_function-sum) и секцию `GROUP BY` как описано в примере выше. -Если столбцы для суммирования не заданы, то ClickHouse суммирует значения в столбцах с числовым типом данных, не входящих в первичный ключ. +### Общие правила суммирования -Если столбцы для суммирования заданы, то для всех строчек с одинаковым значением первичного ключа ClickHouse суммирует значения в указанных столбцах. Столбцы должны обязательно иметь числовой тип данных. +Суммируются столбцы с числовым типом данных. Набор столбцов определяется параметром `columns`. Если значения во всех столбцах для суммирования оказались нулевыми, то строчка удаляется. @@ -90,7 +92,7 @@ ClickHouse рассчитывает расход ресурсов и может Для столбцов типа [AggregateFunction](../../data_types/nested_data_structures/aggregatefunction.md#data_type-aggregatefunction) ClickHouse выполняет агрегацию согласно заданной функции, повторяя поведение движка [AggregatingMergeTree](aggregatingmergetree.md#table_engine-aggregatingmergetree). -### Обработка вложенных структур +### Вложенные структуры Таблица может иметь вложенные структуры данных, которые обрабатываются особым образом. diff --git a/docs/ru/query_language/agg_functions/combinators.md b/docs/ru/query_language/agg_functions/combinators.md index ba08f46e4ea..16678b075be 100644 --- a/docs/ru/query_language/agg_functions/combinators.md +++ b/docs/ru/query_language/agg_functions/combinators.md @@ -4,6 +4,8 @@ К имени агрегатной функции может быть приписан некоторый суффикс. При этом, работа агрегатной функции некоторым образом модифицируется. + + ## -If К имени любой агрегатной функции может быть приписан суффикс -If. В этом случае, агрегатная функция принимает ещё один дополнительный аргумент - условие (типа UInt8). Агрегатная функция будет обрабатывать только те строки, для которых условие сработало. Если условие ни разу не сработало - возвращается некоторое значение по умолчанию (обычно - нули, пустые строки). diff --git a/docs/ru/query_language/agg_functions/reference.md b/docs/ru/query_language/agg_functions/reference.md index 4c084eb35bd..8f3ca142349 100644 --- a/docs/ru/query_language/agg_functions/reference.md +++ b/docs/ru/query_language/agg_functions/reference.md @@ -11,6 +11,8 @@ Запрос вида `SELECT count() FROM table` не оптимизируется, так как количество записей в таблице нигде не хранится отдельно - из таблицы будет выбран какой-нибудь достаточно маленький столбец, и будет посчитано количество значений в нём. + + ## any(x) Выбирает первое попавшееся значение. @@ -53,7 +55,6 @@ FROM ontime Выбирает последнее попавшееся значение. Результат так же недетерминирован, как и для функции `any`. - ## min(x) Вычисляет минимум. @@ -132,6 +133,9 @@ GROUP BY timeslot Работает только для чисел. Результат всегда - Float64. + + + ## uniq(x) Приближённо вычисляет количество различных значений аргумента. Работает для чисел, строк, дат, дат-с-временем, для нескольких аргументов и аргументов-кортежей. From e5183df03847a7469172b0edfb551c7df4121d96 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Fri, 28 Sep 2018 15:18:42 +0300 Subject: [PATCH 5/8] New syntax for new syntax of CREATE TABLE --- .../table_engines/aggregatingmergetree.md | 24 ++++++++++--- docs/ru/operations/table_engines/mergetree.md | 36 ++++++++++++++----- .../table_engines/replacingmergetree.md | 29 +++++++++++---- docs/ru/query_language/create.md | 3 +- 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/docs/ru/operations/table_engines/aggregatingmergetree.md b/docs/ru/operations/table_engines/aggregatingmergetree.md index d17484ed9e1..34c6eb3ce94 100644 --- a/docs/ru/operations/table_engines/aggregatingmergetree.md +++ b/docs/ru/operations/table_engines/aggregatingmergetree.md @@ -10,13 +10,24 @@ Использование `AggregatingMergeTree` оправдано только в том случае, когда это уменьшает количество строк на порядки. -## Конфигурирование движка при создании таблицы +## Создание таблицы ``` -ENGINE [=] AggregatingMergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = MergeTree() +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` -**Подчиненные секции ENGINE** +Описание параметров запроса смотрите в [описании запроса](../../query_language/create.md#query_language-queries-create_table). + +**Секции запроса** `AggregatingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. @@ -27,7 +38,12 @@ ENGINE [=] AggregatingMergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql -AggregatingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity) +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]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`. diff --git a/docs/ru/operations/table_engines/mergetree.md b/docs/ru/operations/table_engines/mergetree.md index 453a2ecc211..eecbbbf345a 100644 --- a/docs/ru/operations/table_engines/mergetree.md +++ b/docs/ru/operations/table_engines/mergetree.md @@ -25,15 +25,28 @@ При необходимости можно задать способ сэмплирования данных в таблице. - + -## Конфигурирование движка при создании таблицы +## Создание таблицы ``` -ENGINE [=] MergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = MergeTree() +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` -**Подчиненные секции ENGINE** +Описание параметров запроса смотрите в [описании запроса](../../query_language/create.md#query_language-queries-create_table). + +**Секции запроса** + +- `ENGINE` — Имя и параметры движка. `ENGINE = MergeTree()`. Движок `MergeTree` не имеет параметров. - `ORDER BY` — первичный ключ. @@ -44,13 +57,13 @@ ENGINE [=] MergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SET Для партиционирования по месяцам используйте выражение `toYYYYMM(date_column)`, где `date_column` — столбец с датой типа [Date](../../data_types/date.md#data_type-date). В этом случае имена партиций имеют формат `"YYYYMM"`. -- `SAMPLE BY` — выражение для сэмплирования (не обязательно). Пример: `intHash32(UserID))`. +- `SAMPLE BY` — выражение для сэмплирования. Пример: `intHash32(UserID))`. -- `SETTINGS` — дополнительные параметры, регулирующие поведение `MergeTree` (не обязательно): +- `SETTINGS` — дополнительные параметры, регулирующие поведение `MergeTree`: - `index_granularity` — гранулярность индекса. Число строк данных между «засечками» индекса. По умолчанию — 8192. -**Пример** +**Пример задания секций** ``` ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192 @@ -62,13 +75,18 @@ ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDa `index_granularity` можно было не указывать, поскольку 8192 — это значение по умолчанию. -### Устаревший способ конфигурирования движка +### Устаревший способ создания таблицы !!!attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ``` -ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity) +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity) ``` **Параметры MergeTree()** diff --git a/docs/ru/operations/table_engines/replacingmergetree.md b/docs/ru/operations/table_engines/replacingmergetree.md index b74b513a4d7..f523af5d67d 100644 --- a/docs/ru/operations/table_engines/replacingmergetree.md +++ b/docs/ru/operations/table_engines/replacingmergetree.md @@ -6,12 +6,23 @@ Таким образом, `ReplacingMergeTree` подходит для фоновой чистки дублирующихся данных в целях экономии места, но не даёт гарантии отсутствия дубликатов. -## Конфигурирование движка при создании таблицы +## Создание таблицы ``` -ENGINE [=] ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = ReplacingMergeTree([ver]) +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` +Описание параметров запроса смотрите в [описании запроса](../../query_language/create.md#query_language-queries-create_table). + **Параметры ReplacingMergeTree** - `ver` — столбец с версией, тип `UInt*`, `Date` или `DateTime`. Необязательный параметр. @@ -21,18 +32,22 @@ ENGINE [=] ReplacingMergeTree([ver]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE - Последнюю в выборке, если `ver` не задан. - С максимальной версией, если `ver` задан. -**Подчиненные секции ENGINE** +**Секции запроса** -`ReplacingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. +При создании таблицы `ReplacingMergeTree` используются те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и при создании таблицы `MergeTree`. - -### Устаревший способ конфигурирования движка +### Устаревший способ создания таблицы !!!attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql -ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver]) +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver]) ``` Все параметры, кроме `ver` имеют то же значение, что в и `MergeTree`. diff --git a/docs/ru/query_language/create.md b/docs/ru/query_language/create.md index a33692d12b1..ee3847e4fb0 100644 --- a/docs/ru/query_language/create.md +++ b/docs/ru/query_language/create.md @@ -43,6 +43,8 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ... Во всех случаях, если указано `IF NOT EXISTS`, то запрос не будет возвращать ошибку, если таблица уже существует. В этом случае, запрос будет ничего не делать. +После секции ENGINE в запросе могут использоваться и другие секции в зависимости от движка. Подробную документацию по созданию таблиц смотрите в описаниях [движков](../operations/table_engines/index.md#table_engines). + ### Значения по умолчанию @@ -152,4 +154,3 @@ SELECT a, b, c FROM (SELECT ...) Представления выглядят так же, как обычные таблицы. Например, они перечисляются в результате запроса `SHOW TABLES`. Отсутствует отдельный запрос для удаления представлений. Чтобы удалить представление, следует использовать `DROP TABLE`. - From 78e044906c0e8a6ca6e42be17bd862e79ef68a1e Mon Sep 17 00:00:00 2001 From: BayoNet Date: Tue, 2 Oct 2018 17:42:33 +0300 Subject: [PATCH 6/8] Added english docs on Aggregating, Replacing and SummingMergeTree. --- .../aggregatefunction.md | 62 ++++++++- .../table_engines/aggregatingmergetree.md | 111 ++++++++-------- docs/en/operations/table_engines/mergetree.md | 51 +++++--- .../table_engines/replacingmergetree.md | 58 +++++++-- .../table_engines/summingmergetree.md | 123 +++++++++++++++--- .../agg_functions/combinators.md | 5 +- .../query_language/agg_functions/reference.md | 10 +- docs/en/query_language/create.md | 2 + .../aggregatefunction.md | 2 +- .../table_engines/aggregatingmergetree.md | 6 +- .../table_engines/replacingmergetree.md | 2 +- .../table_engines/summingmergetree.md | 39 ++++-- .../agg_functions/combinators.md | 2 +- docs/ru/query_language/create.md | 2 +- 14 files changed, 353 insertions(+), 122 deletions(-) diff --git a/docs/en/data_types/nested_data_structures/aggregatefunction.md b/docs/en/data_types/nested_data_structures/aggregatefunction.md index 33e7b4f316c..c1977abbf22 100644 --- a/docs/en/data_types/nested_data_structures/aggregatefunction.md +++ b/docs/en/data_types/nested_data_structures/aggregatefunction.md @@ -1,4 +1,64 @@ + + # AggregateFunction(name, types_of_arguments...) -The intermediate state of an aggregate function. To get it, use aggregate functions with the '-State' suffix. For more information, see "AggregatingMergeTree". +The intermediate state of an aggregate function. To get it, use aggregate functions with the `-State` suffix. To get aggregated data in the future, you must use the same aggregate functions with the `-Merge`suffix. + +`AggregateFunction` — parametric data type. + +**Parameters** + +- Name of the aggregate function. + + If the function is parametric specify its parameters too. + +- Types of the aggregate function arguments. + +**Example** + +```sql +CREATE TABLE t +( + column1 AggregateFunction(uniq, UInt64), + column2 AggregateFunction(anyIf, String, UInt8), + column3 AggregateFunction(quantiles(0.5, 0.9), UInt64) +) ENGINE = ... +``` + +[uniq](../../query_language/agg_functions/reference.md#agg_function-uniq), anyIf ([any](../../query_language/agg_functions/reference.md#agg_function-any)+[If](../../query_language/agg_functions/combinators.md#agg-functions-combine-if)) and [quantiles](../../query_language/agg_functions/reference.md#agg_function-quantiles) are the aggregate functions supported in ClickHouse. + +## Usage + +### Data Insertion + +To insert data, use `INSERT SELECT` with aggregate `-State`- functions. + +**Function examples** + +``` +uniqState(UserID) +quantilesState(0.5, 0.9)(SendTiming) +``` + +In contrast to the corresponding functions `uniq` and `quantiles`, `-State`- functions return the state, instead the final value. In other words, they return a value of `AggregateFunction` type . + +In the results of `SELECT` query the values of `AggregateFunction` type have implementation-specific binary representation for all of the ClickHouse output formats. If dump data into, for example, `TabSeparated` format with `SELECT` query then this dump can be loaded back using `INSERT` query. + +### Data Selection + +When selecting data from `AggregatingMergeTree` table, use `GROUP BY` clause and the same aggregate functions as when inserting data, but using `-Merge`suffix. + +An aggregate function with `-Merge` suffix takes a set of states, combines them, and returns the result of complete data aggregation. + +For example, the following two queries return the same result: + +```sql +SELECT uniq(UserID) FROM table + +SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP BY RegionID) +``` + +## Usage Example + +See [AggregatingMergeTree](../../operations/table_engines/aggregatingmergetree.md#table_engine-aggregatingmergetree) engine description. diff --git a/docs/en/operations/table_engines/aggregatingmergetree.md b/docs/en/operations/table_engines/aggregatingmergetree.md index 0f99da076b6..d0c89dd9c39 100644 --- a/docs/en/operations/table_engines/aggregatingmergetree.md +++ b/docs/en/operations/table_engines/aggregatingmergetree.md @@ -1,59 +1,66 @@ + + # AggregatingMergeTree -This engine differs from `MergeTree` in that the merge combines the states of aggregate functions stored in the table for rows with the same primary key value. - -For this to work, it uses the `AggregateFunction` data type, as well as `-State` and `-Merge` modifiers for aggregate functions. Let's examine it more closely. - -There is an `AggregateFunction` data type. It is a parametric data type. As parameters, the name of the aggregate function is passed, then the types of its arguments. - -Examples: - -```sql -CREATE TABLE t -( - column1 AggregateFunction(uniq, UInt64), - column2 AggregateFunction(anyIf, String, UInt8), - column3 AggregateFunction(quantiles(0.5, 0.9), UInt64) -) ENGINE = ... -``` - -This type of column stores the state of an aggregate function. - -To get this type of value, use aggregate functions with the `State` suffix. - -Example:`uniqState(UserID), quantilesState(0.5, 0.9)(SendTiming)` - -In contrast to the corresponding `uniq` and `quantiles` functions, these functions return the state, rather than the prepared value. In other words, they return an `AggregateFunction` type value. - -An `AggregateFunction` type value can't be output in Pretty formats. In other formats, these types of values are output as implementation-specific binary data. The `AggregateFunction` type values are not intended for output or saving in a dump. - -The only useful thing you can do with `AggregateFunction` type values is to combine the states and get a result, which essentially means to finish aggregation. Aggregate functions with the 'Merge' suffix are used for this purpose. -Example: `uniqMerge(UserIDState)`, where `UserIDState` has the `AggregateFunction` type. - -In other words, an aggregate function with the 'Merge' suffix takes a set of states, combines them, and returns the result. -As an example, these two queries return the same result: - -```sql -SELECT uniq(UserID) FROM table - -SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP BY RegionID) -``` - -There is an `AggregatingMergeTree` engine. Its job during a merge is to combine the states of aggregate functions from different table rows with the same primary key value. - -You can't use a normal INSERT to insert a row in a table containing `AggregateFunction` columns, because you can't explicitly define the `AggregateFunction` value. Instead, use `INSERT SELECT` with `-State` aggregate functions for inserting data. - -With SELECT from an `AggregatingMergeTree` table, use GROUP BY and aggregate functions with the '-Merge' modifier in order to complete data aggregation. +The engine inherits from [MergeTree](mergetree.md#table_engines-mergetree), altering the logic for data parts merging. ClickHouse replaces all rows with the same primary key with a single row (within a one data part) that stores a combination of states of aggregate functions. You can use `AggregatingMergeTree` tables for incremental data aggregation, including for aggregated materialized views. -Example: +The engine processes all columns with [AggregateFunction](../../data_types/nested_data_structures/aggregatefunction.md#data_type-aggregatefunction) type. -Create an `AggregatingMergeTree` materialized view that watches the `test.visits` table: +It is appropriate to use `AggregatingMergeTree` if it reduces the number of rows by orders. + +## Creating a Table + +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]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] +[SETTINGS name=value, ...] +``` + +For a description of request parameters, see [request description](../../query_language/create.md#query_language-queries-create_table). + +**Query clauses** + +When creating a `ReplacingMergeTree` table the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. + +### Deprecated Method for Creating a Table + +!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. + +```sql +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]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) +``` + +All of the parameters have the same meaning as in `MergeTree`. + +## SELECT and INSERT + +To insert data, use `INSERT SELECT` query with aggregate `- State`- functions. + +When selecting data from `AggregatingMergeTree` table, use `GROUP BY` clause and the same aggregate functions as when inserting data, but using `- Merge` suffix. + +In the results of `SELECT` query the values of `AggregateFunction` type have implementation-specific binary representation for all of the ClickHouse output formats. If dump data into, for example, `TabSeparated` format with `SELECT` query then this dump can be loaded back using `INSERT` query. + +## Example of an Aggregated Materialized View + +`AggregatingMergeTree` materialized view that watches the `test.visits` table: ```sql CREATE MATERIALIZED VIEW test.basic -ENGINE = AggregatingMergeTree(StartDate, (CounterID, StartDate), 8192) +ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(StartDate) ORDER BY (CounterID, StartDate) AS SELECT CounterID, StartDate, @@ -63,13 +70,15 @@ FROM test.visits GROUP BY CounterID, StartDate; ``` -Insert data in the `test.visits` table. Data will also be inserted in the view, where it will be aggregated: +Inserting of data into the `test.visits` table. ```sql INSERT INTO test.visits ... ``` -Perform `SELECT` from the view using `GROUP BY` in order to complete data aggregation: +The data are inserted in both the table and view `test.basic` that will perform the aggregation. + +To get the aggregated data, we need to execute a query such as `SELECT ... GROUP BY ...` from the view `test.basic`: ```sql SELECT @@ -81,7 +90,3 @@ GROUP BY StartDate ORDER BY StartDate; ``` -You can create a materialized view like this and assign a normal view to it that finishes data aggregation. - -Note that in most cases, using `AggregatingMergeTree` is not justified, since queries can be run efficiently enough on non-aggregated data. - diff --git a/docs/en/operations/table_engines/mergetree.md b/docs/en/operations/table_engines/mergetree.md index 3863c63b40e..448753eef28 100644 --- a/docs/en/operations/table_engines/mergetree.md +++ b/docs/en/operations/table_engines/mergetree.md @@ -4,7 +4,7 @@ The `MergeTree` engine and other engines of this family (`*MergeTree`) are the most robust ClickHousе table engines. -!!! info +!!!info The [Merge](merge.md#table_engine-merge) engine does not belong to the `*MergeTree` family. Main features: @@ -25,29 +25,44 @@ Main features: If necessary, you can set the data sampling method in the table. -## Engine Configuration When Creating a Table + + +## Creating a Table ``` -ENGINE [=] MergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = MergeTree() +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` -**ENGINE clauses** +For a description of request parameters, see [request description](../../query_language/create.md#query_language-queries-create_table). + +**Query clauses** + +- `ENGINE` - Name and parameters of the engine. `ENGINE = MergeTree()`. `MergeTree` engine does not have parameters. - `ORDER BY` — Primary key. A tuple of columns or arbitrary expressions. Example: `ORDER BY (CounterID, EventDate)`. -If a sampling key is used, the primary key must contain it. Example: `ORDER BY (CounerID, EventDate, intHash32(UserID))`. +If a sampling expression is used, the primary key must contain it. Example: `ORDER BY (CounerID, EventDate, intHash32(UserID))`. - `PARTITION BY` — The [partitioning key](custom_partitioning_key.md#table_engines-custom_partitioning_key). For partitioning by month, use the `toYYYYMM(date_column)` expression, where `date_column` is a column with a date of the type [Date](../../data_types/date.md#data_type-date). The partition names here have the `"YYYYMM"` format. -- `SAMPLE BY` — An expression for sampling (optional). Example: `intHash32(UserID))`. +- `SAMPLE BY` — An expression for sampling. Example: `intHash32(UserID))`. -- `SETTINGS` — Additional parameters that control the behavior of the `MergeTree` (optional): +- `SETTINGS` — Additional parameters that control the behavior of the `MergeTree`: - `index_granularity` — The granularity of an index. The number of data rows between the "marks" of an index. By default, 8192. -**Example** +**Example of sections setting** ``` ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity=8192 @@ -59,13 +74,18 @@ We also set an expression for sampling as a hash by the user ID. This allows you `index_granularity` could be omitted because 8192 is the default value. -### Deprecated Method for Engine Configuration +### Deprecated Method for Creating a Table -!!! attention +!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ``` -ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity) +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] MergeTree(date-column [, sampling_expression], (primary, key), index_granularity) ``` **MergeTree() parameters** @@ -89,7 +109,7 @@ A table consists of data *parts* sorted by primary key. When data is inserted in a table, separate data parts are created and each of them is lexicographically sorted by primary key. For example, if the primary key is `(CounterID, Date)`, the data in the part is sorted by `CounterID`, and within each `CounterID`, it is ordered by `Date`. -Data belonging to different partitions are separated into different parts. In the background, ClickHouse merges data parts for more efficient storage. Parts belonging to different partitions are not merged. +Data belonging to different partitions are separated into different parts. In the background, ClickHouse merges data parts for more efficient storage. Parts belonging to different partitions are not merged. The merge mechanism does not guarantee that all rows with the same primary key will be in the same data part. For each data part, ClickHouse creates an index file that contains the primary key value for each index row ("mark"). Index row numbers are defined as `n * index_granularity`. The maximum value `n` is equal to the integer part of dividing the total number of rows by the `index_granularity`. For each column, the "marks" are also written for the same index rows as the primary key. These "marks" allow you to find the data directly in the columns. @@ -136,13 +156,13 @@ The number of columns in the primary key is not explicitly limited. Depending on ClickHouse sorts data by primary key, so the higher the consistency, the better the compression. -- To provide additional logic when merging in the [CollapsingMergeTree](collapsingmergetree.md#table_engine-collapsingmergetree) and [SummingMergeTree](summingmergetree.md#table_engine-summingmergetree) engines. +- Provide additional logic when data parts merging in the [CollapsingMergeTree](collapsingmergetree.md#table_engine-collapsingmergetree) and [SummingMergeTree](summingmergetree.md#table_engine-summingmergetree) engines. - You may need to have many fields in the primary key even if they are not necessary for the previous steps. + You may need many fields in the primary key even if they are not necessary for the previous steps. A long primary key will negatively affect the insert performance and memory consumption, but extra columns in the primary key do not affect ClickHouse performance during `SELECT` queries. -### Usage of Indexes and Partitions in Queries +### Use of Indexes and Partitions in Queries For`SELECT` queries, ClickHouse analyzes whether an index can be used. An index can be used if the `WHERE/PREWHERE` clause has an expression (as one of the conjunction elements, or entirely) that represents an equality or inequality comparison operation, or if it has `IN` or `LIKE` with a fixed prefix on columns or expressions that are in the primary key or partitioning key, or on certain partially repetitive functions of these columns, or logical relationships of these expressions. @@ -181,4 +201,3 @@ The key for partitioning by month allows reading only those data blocks which co For concurrent table access, we use multi-versioning. In other words, when a table is simultaneously read and updated, data is read from a set of parts that is current at the time of the query. There are no lengthy locks. Inserts do not get in the way of read operations. Reading from a table is automatically parallelized. - diff --git a/docs/en/operations/table_engines/replacingmergetree.md b/docs/en/operations/table_engines/replacingmergetree.md index 92f2ffb34bf..bf44d2fa313 100644 --- a/docs/en/operations/table_engines/replacingmergetree.md +++ b/docs/en/operations/table_engines/replacingmergetree.md @@ -1,18 +1,54 @@ # ReplacingMergeTree -This engine table differs from `MergeTree` in that it removes duplicate entries with the same primary key value. +The engine differs from [MergeTree](mergetree.md#table_engines-mergetree) in that it removes duplicate entries with the same primary key value. -The last optional parameter for the table engine is the version column. When merging, it reduces all rows with the same primary key value to just one row. If the version column is specified, it leaves the row with the highest version; otherwise, it leaves the last row. - -The version column must have a type from the `UInt` family, `Date`, or `DateTime`. - -```sql -ReplacingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, ver) -``` - -Note that data is only deduplicated during merges. Merging occurs in the background at an unknown time, so you can't plan for it. Some of the data may remain unprocessed. Although you can run an unscheduled merge using the OPTIMIZE query, don't count on using it, because the OPTIMIZE query will read and write a large amount of data. +Data deduplication occurs only during a merge. Merging occurs in the background at an unknown time, so you can't plan for it. Some of the data may remain unprocessed. Although you can run an unscheduled merge using the `OPTIMIZE` query, don't count on using it, because the `OPTIMIZE` query will read and write a large amount of data. Thus, `ReplacingMergeTree` is suitable for clearing out duplicate data in the background in order to save space, but it doesn't guarantee the absence of duplicates. -*This engine is not used in Yandex.Metrica, but it has been applied in other Yandex projects.* +## Creating a Table + +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = ReplacingMergeTree([ver]) +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] +``` + +For a description of request parameters, see [request description](../../query_language/create.md#query_language-queries-create_table). + +**ReplacingMergeTree Parameters** + +- `ver` — column with version. Type `UInt*`, `Date` or `DateTime`. Optional parameter. + + When merging, `ReplacingMergeTree` from all the rows with the same primary key leaves only one: + - Last in the selection, if `ver` not set. + - With the maximum version, if `ver` specified. + +**Query clauses** + +When creating a `ReplacingMergeTree` table the same [clausesmergetree.md](#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. + +### Deprecated Method for Creating a Table + +!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. + +```sql +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] ReplacingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [ver]) +``` + +All of the parameters excepting `ver` have the same meaning as in `MergeTree`. + +- `ver` - column with the version. Optional parameter. For a description, see the text above. diff --git a/docs/en/operations/table_engines/summingmergetree.md b/docs/en/operations/table_engines/summingmergetree.md index c75d09aae96..97e003597d6 100644 --- a/docs/en/operations/table_engines/summingmergetree.md +++ b/docs/en/operations/table_engines/summingmergetree.md @@ -2,33 +2,120 @@ # SummingMergeTree -This engine differs from `MergeTree` in that it totals data while merging. +The engine inherits from [MergeTree](mergetree.md#table_engines-mergetree). The difference is that when merging data parts for `SummingMergeTree` tables ClickHouse replaces all the rows with the same primary key with one row which contains summarized values for the columns with the numeric data type. If the primary key is composed in a way that a single key value corresponds to large number of rows, this significantly reduces storage volume and speeds up data selection. -```sql -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192) +We recommend to use the engine together with `MergeTree`. Store complete data in `MergeTree` table, and use `SummingMergeTree` for aggregated data storing, for example, when preparing reports. Such an approach will prevent you from losing valuable data due to an incorrectly composed primary key. + +## Creating a Table + +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = MergeTree() +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` -The columns to total are implicit. When merging, all rows with the same primary key value (in the example, OrderId, EventDate, BannerID, ...) have their values totaled in numeric columns that are not part of the primary key. +For a description of request parameters, see [request description](../../query_language/create.md#query_language-queries-create_table). -```sql -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, ...), 8192, (Shows, Clicks, Cost, ...)) +**Parameters of SummingMergeTree** + +- `columns` - a tuple with the names of columns where values will be summarized. Optional parameter. +The columns must be of a numeric type and must not be in the primary key. + + If `columns` not specified, ClickHouse summarizes the values in all columns with a numeric data type that are not in the primary key. + +**Query clauses** + +When creating a `SummingMergeTree` table the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. + +### Deprecated Method for Creating a Table + +!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. + +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] SummingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [columns]) ``` -The columns to total are set explicitly (the last parameter – Shows, Clicks, Cost, ...). When merging, all rows with the same primary key value have their values totaled in the specified columns. The specified columns also must be numeric and must not be part of the primary key. +All of the parameters excepting `columns` have the same meaning as in `MergeTree`. -If the values were zero in all of these columns, the row is deleted. +- `columns` — tuple with names of columns values of which will be summarized. Optional parameter. For a description, see the text above. -For the other columns that are not part of the primary key, the first value that occurs is selected when merging. But for the AggregateFunction type of columns, aggregation is performed according to the set function, so this engine actually behaves like `AggregatingMergeTree`. +## Usage Example -Summation is not performed for a read operation. If it is necessary, write the appropriate GROUP BY. +Consider the following table: -In addition, a table can have nested data structures that are processed in a special way. -If the name of a nested table ends in 'Map' and it contains at least two columns that meet the following criteria: +```sql +CREATE TABLE summtt +( + key UInt32, + value UInt32 +) +ENGINE = SummingMergeTree() +ORDER BY key +``` -- The first table is numeric ((U)IntN, Date, DateTime), which we'll refer to as the 'key'. -- The other columns are arithmetic ((U)IntN, Float32/64), which we'll refer to as '(values...)'. +Insert data to it: -Then this nested table is interpreted as a mapping of key `=>` (values...), and when merging its rows, the elements of two data sets are merged by 'key' with a summation of the corresponding (values...). +``` +:) INSERT INTO summtt Values(1,1),(1,2),(2,1) +``` + +ClickHouse may sum all the rows not completely ([see below](#summary-data-processing)), so we use an aggregate function `sum` and `GROUP BY` clause in the query. + +```sql +SELECT key, sum(value) FROM summtt GROUP BY key +``` + +``` +┌─key─┬─sum(value)─┐ +│ 2 │ 1 │ +│ 1 │ 3 │ +└─────┴────────────┘ +``` + + + +## Data Processing + +When data are inserted into a table, they are saved as-is. Clickhouse merges the inserted parts of data periodically and this is when rows with the same primary key are summed and replaced with one for each resulting part of data. + +ClickHouse can merge the data parts so that different resulting parts of data cat consist rows with the same primary key, i.e. the summation will be incomplete. Therefore (`SELECT`) an aggregate function [sum()](../../query_language/agg_functions/reference.md#agg_function-sum) and `GROUP BY` clause should be used in a query as described in the example above. + +### Common rules for summation + +The values in the columns with the numeric data type are summarized. The set of columns is defined by the parameter `columns`. + +If the values were 0 in all of the columns for summation, the row is deleted. + +If column is not in the primary key and is not summarized, an arbitrary value is selected from the existing ones. + +The values are not summarized for columns in the primary key. + +### The Summation in the AggregateFunction Columns + +For columns of [AggregateFunction type](../../data_types/nested_data_structures/aggregatefunction.md#data_type-aggregatefunction) ClickHouse behaves as [AggregatingMergeTree](aggregatingmergetree.md#table_engine-aggregatingmergetree) engine aggregating according to the function. + +### Nested Structures + +Table can have nested data structures that are processed in a special way. + +If the name of a nested table ends with `Map` and it contains at least two columns that meet the following criteria: + +- the first column is numeric `(*Int*, Date, DateTime)`, let's call it `key`, +- the other columns are arithmetic `(*Int*, Float32/64)`, let's call it `(values...)`, + +then this nested table is interpreted as a mapping of `key => (values...)`, and when merging its rows, the elements of two data sets are merged by `key` with a summation of the corresponding `(values...)`. Examples: @@ -39,9 +126,7 @@ Examples: [(1, 100), (2, 150)] + [(1, -100)] -> [(2, 150)] ``` -For aggregation of Map, use the function sumMap(key, value). +When requesting data, use the [sumMap(key, value)](../../query_language/agg_functions/reference.md#agg_function-summary) function for aggregation of `Map`. -For nested data structures, you don't need to specify the columns as a list of columns for totaling. - -This table engine is not particularly useful. Remember that when saving just pre-aggregated data, you lose some of the system's advantages. +For nested data structure, you do not need to specify its columns in the tuple of columns for summation. diff --git a/docs/en/query_language/agg_functions/combinators.md b/docs/en/query_language/agg_functions/combinators.md index 3b7e372b324..e6cf9b70212 100644 --- a/docs/en/query_language/agg_functions/combinators.md +++ b/docs/en/query_language/agg_functions/combinators.md @@ -4,6 +4,8 @@ The name of an aggregate function can have a suffix appended to it. This changes the way the aggregate function works. + + ## -If The suffix -If can be appended to the name of any aggregate function. In this case, the aggregate function accepts an extra argument – a condition (Uint8 type). The aggregate function processes only the rows that trigger the condition. If the condition was not triggered even once, it returns a default value (usually zeros or empty strings). @@ -24,7 +26,7 @@ Example 2: `uniqArray(arr)` – Count the number of unique elements in all 'arr' ## -State -If you apply this combinator, the aggregate function doesn't return the resulting value (such as the number of unique values for the 'uniq' function), but an intermediate state of the aggregation (for `uniq`, this is the hash table for calculating the number of unique values). This is an AggregateFunction(...) that can be used for further processing or stored in a table to finish aggregating later. See the sections "AggregatingMergeTree" and "Functions for working with intermediate aggregation states". +If you apply this combinator, the aggregate function doesn't return the resulting value (such as the number of unique values for the `uniq` function), but an intermediate state of the aggregation (for `uniq`, this is the hash table for calculating the number of unique values). This is an AggregateFunction(...) that can be used for further processing or stored in a table to finish aggregating later. See the sections "AggregatingMergeTree" and "Functions for working with intermediate aggregation states". ## -Merge @@ -37,4 +39,3 @@ Merges the intermediate aggregation states in the same way as the -Merge combina ## -ForEach Converts an aggregate function for tables into an aggregate function for arrays that aggregates the corresponding array items and returns an array of results. For example, `sumForEach` for the arrays `[1, 2]`, `[3, 4, 5]`and`[6, 7]`returns the result `[10, 13, 5]` after adding together the corresponding array items. - diff --git a/docs/en/query_language/agg_functions/reference.md b/docs/en/query_language/agg_functions/reference.md index 357f3d622a2..1ba6c68a25e 100644 --- a/docs/en/query_language/agg_functions/reference.md +++ b/docs/en/query_language/agg_functions/reference.md @@ -9,6 +9,8 @@ The syntax `COUNT(DISTINCT x)` is not supported. The separate `uniq` aggregate f A `SELECT count() FROM table` query is not optimized, because the number of entries in the table is not stored separately. It will select some small column from the table and count the number of values in it. + + ## any(x) Selects the first encountered value. @@ -67,6 +69,8 @@ Calculates the 'arg' value for a minimal 'val' value. If there are several diffe Calculates the 'arg' value for a maximum 'val' value. If there are several different values of 'arg' for maximum values of 'val', the first of these values encountered is output. + + ## sum(x) Calculates the sum. @@ -78,6 +82,8 @@ Computes the sum of the numbers, using the same data type for the result as for Only works for numbers. + + ## sumMap(key, value) Totals the 'value' array according to the keys specified in the 'key' array. @@ -120,6 +126,8 @@ Calculates the average. Only works for numbers. The result is always Float64. + + ## uniq(x) Calculates the approximate number of different values of the argument. Works for numbers, strings, dates, date-with-time, and for multiple arguments and tuple arguments. @@ -253,7 +261,7 @@ A hash table is used as the algorithm. Because of this, if the passed values ​ Approximates the quantile level using the [t-digest](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf) algorithm. The maximum error is 1%. Memory consumption by State is proportional to the logarithm of the number of passed values. -The performance of the function is lower than for `quantile`, `quantileTiming`. In terms of the ratio of State size to precision, this function is much better than `quantile`. +The performance of the function is lower than for `quantile` or `quantileTiming`. In terms of the ratio of State size to precision, this function is much better than `quantile`. The result depends on the order of running the query, and is nondeterministic. diff --git a/docs/en/query_language/create.md b/docs/en/query_language/create.md index 9369c3a5536..0cd60df7e5d 100644 --- a/docs/en/query_language/create.md +++ b/docs/en/query_language/create.md @@ -44,6 +44,8 @@ Creates a table with a structure like the result of the `SELECT` query, with the In all cases, if `IF NOT EXISTS` is specified, the query won't return an error if the table already exists. In this case, the query won't do anything. +There can be other clauses after the `ENGINE` clause in the query. See detailed documentation on how to create tables in the descriptions of [table engines](../operations/table_engines/index.md#table_engines). + ### Default Values The column description can specify an expression for a default value, in one of the following ways:`DEFAULT expr`, `MATERIALIZED expr`, `ALIAS expr`. diff --git a/docs/ru/data_types/nested_data_structures/aggregatefunction.md b/docs/ru/data_types/nested_data_structures/aggregatefunction.md index 9d86c26efed..26196695346 100644 --- a/docs/ru/data_types/nested_data_structures/aggregatefunction.md +++ b/docs/ru/data_types/nested_data_structures/aggregatefunction.md @@ -10,7 +10,7 @@ - Имя агрегатной функции. - Для параметрических агрегатных функций указываются их параметры. + Для параметрических агрегатных функций указываются также их параметры. - Типы аргументов агрегатной функции. diff --git a/docs/ru/operations/table_engines/aggregatingmergetree.md b/docs/ru/operations/table_engines/aggregatingmergetree.md index 34c6eb3ce94..5b4b1621a05 100644 --- a/docs/ru/operations/table_engines/aggregatingmergetree.md +++ b/docs/ru/operations/table_engines/aggregatingmergetree.md @@ -18,7 +18,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... -) ENGINE = MergeTree() +) ENGINE = AggregatingMergeTree() [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] @@ -29,10 +29,10 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] **Секции запроса** -`AggregatingMergeTree` использует те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. +При создании таблицы `AggregatingMergeTree` используются те же [секции](mergetree.md#table_engines-mergetree-configuring), что и при создании таблицы `MergeTree`. -### Устаревший способ конфигурирования движка +### Устаревший способ создания таблицы !!!attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. diff --git a/docs/ru/operations/table_engines/replacingmergetree.md b/docs/ru/operations/table_engines/replacingmergetree.md index f523af5d67d..68848993d11 100644 --- a/docs/ru/operations/table_engines/replacingmergetree.md +++ b/docs/ru/operations/table_engines/replacingmergetree.md @@ -34,7 +34,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] **Секции запроса** -При создании таблицы `ReplacingMergeTree` используются те же [секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и при создании таблицы `MergeTree`. +При создании таблицы `ReplacingMergeTree` используются те же [секции](mergetree.md#table_engines-mergetree-configuring), что и при создании таблицы `MergeTree`. ### Устаревший способ создания таблицы diff --git a/docs/ru/operations/table_engines/summingmergetree.md b/docs/ru/operations/table_engines/summingmergetree.md index 12a48670398..73cafad3605 100644 --- a/docs/ru/operations/table_engines/summingmergetree.md +++ b/docs/ru/operations/table_engines/summingmergetree.md @@ -6,31 +6,46 @@ Мы рекомендуем использовать движок в паре с `MergeTree`. В `MergeTree` храните полные данные, а `SummingMergeTree` используйте для хранения агрегированных данных, например, при подготовке отчетов. Такой подход позволит не утратить ценные данные из-за неправильно выбранного первичного ключа. -## Конфигурирование движка при создании таблицы +## Создание таблицы ``` -ENGINE [=] SummingMergeTree([columns]) [PARTITION BY expr] [ORDER BY expr] [SAMPLE BY expr] [SETTINGS name=value, ...] +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = MergeTree() +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] ``` +Описание параметров запроса смотрите в [описании запроса](../../query_language/create.md#query_language-queries-create_table). + **Параметры SummingMergeTree** -- `columns` — кортеж с именами столбцов для суммирования данных. Необязательный параметр. +- `columns` — кортеж с именами столбцов, в которых будут суммироваться данные. Необязательный параметр. Столбцы должны иметь числовой тип и не должны входить в первичный ключ. Если `columns` не задан, то ClickHouse суммирует значения во всех столбцах с числовым типом данных, не входящих в первичный ключ. -**Подчиненные секции ENGINE** +**Секции запроса** -`SummingMergeTree` использует те же [подчиненные секции ENGINE](mergetree.md#table_engines-mergetree-configuring), что и `MergeTree`. +При создании таблицы `SummingMergeTree` использутся те же [секции](mergetree.md#table_engines-mergetree-configuring) запроса, что и при создании таблицы `MergeTree`. - -### Устаревший способ конфигурирования движка +### Устаревший способ создания таблицы !!!attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. -```sql -SummingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [columns]) +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] SummingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, [columns]) ``` Все параметры, кроме `columns` имеют то же значение, что в и `MergeTree`. @@ -74,19 +89,19 @@ SELECT key, sum(value) FROM summtt GROUP BY key ## Обработка данных -При вставке данных в таблицу они сохраняются как есть. Периодически ClickHouse выполняет слияние вставленных кусков данных и именно в этот момент производится суммирование и замена многих строк с одинаковым первичным ключом на одну в пределах одного куска данных. +При вставке данных в таблицу они сохраняются как есть. Периодически ClickHouse выполняет слияние вставленных кусков данных и именно в этот момент производится суммирование и замена многих строк с одинаковым первичным ключом на одну для каждого результирующего куска данных. ClickHouse может слить куски данных таким образом, что не все строки с одинаковым первичным ключом окажутся в одном финальном куске, т.е. суммирование будет не полным. Поэтому, при выборке данных (`SELECT`) необходимо использовать агрегатную функцию [sum()](../../query_language/agg_functions/reference.md#agg_function-sum) и секцию `GROUP BY` как описано в примере выше. ### Общие правила суммирования -Суммируются столбцы с числовым типом данных. Набор столбцов определяется параметром `columns`. +Суммируются значения в столбцах с числовым типом данных. Набор столбцов определяется параметром `columns`. Если значения во всех столбцах для суммирования оказались нулевыми, то строчка удаляется. Для столбцов, не входящих в первичный ключ и не суммирующихся, выбирается произвольное значение из имеющихся. -Столбцы, входящие в первичный ключ не суммируются. +Значения для столбцов, входящих в первичный ключ, не суммируются. ### Суммирование в столбцах AggregateFunction diff --git a/docs/ru/query_language/agg_functions/combinators.md b/docs/ru/query_language/agg_functions/combinators.md index 16678b075be..75276b0132c 100644 --- a/docs/ru/query_language/agg_functions/combinators.md +++ b/docs/ru/query_language/agg_functions/combinators.md @@ -27,7 +27,7 @@ ## -State -В случае применения этого комбинатора, агрегатная функция возвращает не готовое значение (например, в случае функции uniq - количество уникальных значений), а промежуточное состояние агрегации (например, в случае функции `uniq` - хэш-таблицу для рассчёта количества уникальных значений), которое имеет тип AggregateFunction(...) и может использоваться для дальнейшей обработки или может быть сохранено в таблицу для последующей доагрегации - смотрите разделы «AggregatingMergeTree» и «функции для работы с промежуточными состояниями агрегации». +В случае применения этого комбинатора, агрегатная функция возвращает не готовое значение (например, в случае функции `uniq` — количество уникальных значений), а промежуточное состояние агрегации (например, в случае функции `uniq` — хэш-таблицу для расчёта количества уникальных значений), которое имеет тип AggregateFunction(...) и может использоваться для дальнейшей обработки или может быть сохранено в таблицу для последующей доагрегации - смотрите разделы «AggregatingMergeTree» и «функции для работы с промежуточными состояниями агрегации». ## -Merge diff --git a/docs/ru/query_language/create.md b/docs/ru/query_language/create.md index ee3847e4fb0..1b819830a5f 100644 --- a/docs/ru/query_language/create.md +++ b/docs/ru/query_language/create.md @@ -43,7 +43,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ... Во всех случаях, если указано `IF NOT EXISTS`, то запрос не будет возвращать ошибку, если таблица уже существует. В этом случае, запрос будет ничего не делать. -После секции ENGINE в запросе могут использоваться и другие секции в зависимости от движка. Подробную документацию по созданию таблиц смотрите в описаниях [движков](../operations/table_engines/index.md#table_engines). +После секции `ENGINE` в запросе могут использоваться и другие секции в зависимости от движка. Подробную документацию по созданию таблиц смотрите в описаниях [движков](../operations/table_engines/index.md#table_engines). ### Значения по умолчанию From 53a89c0dd4666c99f00c364c5c5c64648eb4389d Mon Sep 17 00:00:00 2001 From: BayoNet Date: Thu, 4 Oct 2018 12:46:17 +0300 Subject: [PATCH 7/8] CollapsingMergeTree docs. English version. --- .../table_engines/collapsingmergetree.md | 109 +++++++++++++----- docs/en/operations/table_engines/mergetree.md | 6 +- .../table_engines/replacingmergetree.md | 6 +- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/docs/en/operations/table_engines/collapsingmergetree.md b/docs/en/operations/table_engines/collapsingmergetree.md index c0d0f33d39e..56e8e27c63f 100644 --- a/docs/en/operations/table_engines/collapsingmergetree.md +++ b/docs/en/operations/table_engines/collapsingmergetree.md @@ -2,37 +2,92 @@ # CollapsingMergeTree -*This engine is used specifically for Yandex.Metrica.* +The engine inherits from [MergeTree](mergetree.md#table_engines-mergetree) and adds to data parts merge algorithm the logic of rows collapsing. -It differs from `MergeTree` in that it allows automatic deletion, or "collapsing" certain pairs of rows when merging. +`CollapsingMergeTree` deletes rows collapsing certain pairs of them into one row. The engine may significantly reduce the volume of storage and efficiency of `SELECT` query as a consequence. + +## Creating a Table + +``` +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE = CollapsingMergeTree(sign) +[PARTITION BY expr] +[ORDER BY expr] +[SAMPLE BY expr] +[SETTINGS name=value, ...] +``` + +For a description of request parameters, see [request description](../../query_language/create.md#query_language-queries-create_table). + +**CollapsingMergeTree Parameters** + +- `sign` — column with a sign of event. + + Type — `Int8`. Possible values are 1 and -1. + +**Query clauses** + +When creating a `CollapsingMergeTree` table the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. + +### Deprecated Method for Creating a Table + +!!!attention + Do not use this method in new projects and, if possible, switch the old projects to the method described above. + +```sql +CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], + ... +) ENGINE [=] CollapsingMergeTree(date-column [, sampling_expression], (primary, key), index_granularity, sign) +``` + +All of the parameters excepting `sign` have the same meaning as in `MergeTree`. + +- `sign` — column with a sign of event. + + Type — `Int8`. Possible values are 1 and -1. + +## Collapsing + +### Data + +`CollapsingMergeTree` works effectively with the data represented in the form of change log. Change logs are used for incrementally calculating statistics on the data that is constantly changing. Examples are the log of session changes, or logs of changes to user histories. Each row of such a log describes some object and contains additional `sign` field that indicates the values in this row are intended for increment or for decrement. If object is deleted then `sign = -1`, if object is created, then `sign = 1`, if object is changed `sign` can be `1` or `-1` depending on the kind of change. + +### Algorithm + +When ClickHouse merges data parts, each group of consecutive rows with identical primary key is reduced to not more than two rows, one with `sign = 1` ("increment" row) and another with `sign = -1` ("decrement" row). In other words, entries are collapsed for each resulting data part. + +ClickHouse saves: + + 1. The first "decrement" and the last "increment" rows, if the number of "increment" and "decrement" rows matches. + 1. The last "increment" row, if there is one more "increment" row than "decrement" rows. + 1. The first "decrement" row, if there is one more "decrement" row than "increment" rows. + 1. None of rows, in all other cases. + + ClickHouse treats this situation as a logical error and record it in the server log, the merge continues. This error can occur if the same data were accidentally inserted more than once. + +!!! Я так и не понял, что в точности происходит при коллапсировании. Что происходит с данными в строке, которые не в первичном ключе и не sign? Какого они должны быть типа или что вообще должно происходить, чтобы при схлопывании рассчитывалась инкрементальная статистика? + +Thus, collapsing should not change the results of calculating statistics. +Changes are gradually collapsed so that in the end only the last value of almost every object is left. + +Merging algorithm doesn't guarantee than all of the rows with the same primary key will be in one resulting data part. This means that additional aggregation is required if there is a need to get completely "collapsed" data from `CollapsingMergeTree` table. + +Ways to complete aggregation: + +1. Write a query with `GROUP BY` clause and aggregate functions that accounts for the sign. For example, to calculate quantity, use `sum(sign)` instead of `count()`. To calculate the sum of something, use `sum(sign * x)` instead of `sum(x)`, and so on, and also add `HAVING sum(sign) > 0`. Not all amounts can be calculated this way. For example, the aggregate functions `min` and `max` can't be rewritten. +2. If you must extract data without aggregation (for example, to check whether rows are present whose newest values match certain conditions), you can use the `FINAL` modifier for the `FROM` clause. This approach is significantly less efficient. + +## Use in Yandex.Metrica Yandex.Metrica has normal logs (such as hit logs) and change logs. Change logs are used for incrementally calculating statistics on data that is constantly changing. Examples are the log of session changes, or logs of changes to user histories. Sessions are constantly changing in Yandex.Metrica. For example, the number of hits per session increases. We refer to changes in any object as a pair (?old values, ?new values). Old values may be missing if the object was created. New values may be missing if the object was deleted. If the object was changed, but existed previously and was not deleted, both values are present. In the change log, one or two entries are made for each change. Each entry contains all the attributes that the object has, plus a special attribute for differentiating between the old and new values. When objects change, only the new entries are added to the change log, and the existing ones are not touched. The change log makes it possible to incrementally calculate almost any statistics. To do this, we need to consider "new" rows with a plus sign, and "old" rows with a minus sign. In other words, incremental calculation is possible for all statistics whose algebraic structure contains an operation for taking the inverse of an element. This is true of most statistics. We can also calculate "idempotent" statistics, such as the number of unique visitors, since the unique visitors are not deleted when making changes to sessions. This is the main concept that allows Yandex.Metrica to work in real time. - -CollapsingMergeTree accepts an additional parameter - the name of an Int8-type column that contains the row's "sign". Example: - -```sql -CollapsingMergeTree(EventDate, (CounterID, EventDate, intHash32(UniqID), VisitID), 8192, Sign) -``` - -Here, `Sign` is a column containing -1 for "old" values and 1 for "new" values. - -When merging, each group of consecutive identical primary key values (columns for sorting data) is reduced to no more than one row with the column value 'sign_column = -1' (the "negative row") and no more than one row with the column value 'sign_column = 1' (the "positive row"). In other words, entries from the change log are collapsed. - -If the number of positive and negative rows matches, the first negative row and the last positive row are written. -If there is one more positive row than negative rows, only the last positive row is written. -If there is one more negative row than positive rows, only the first negative row is written. -Otherwise, there will be a logical error and none of the rows will be written. (A logical error can occur if the same section of the log was accidentally inserted more than once. The error is just recorded in the server log, and the merge continues.) - -Thus, collapsing should not change the results of calculating statistics. -Changes are gradually collapsed so that in the end only the last value of almost every object is left. -Compared to MergeTree, the CollapsingMergeTree engine allows a multifold reduction of data volume. - -There are several ways to get completely "collapsed" data from a `CollapsingMergeTree` table: - -1. Write a query with GROUP BY and aggregate functions that accounts for the sign. For example, to calculate quantity, write 'sum(Sign)' instead of 'count()'. To calculate the sum of something, write 'sum(Sign * x)' instead of 'sum(x)', and so on, and also add 'HAVING sum(Sign) `>` 0'. Not all amounts can be calculated this way. For example, the aggregate functions 'min' and 'max' can't be rewritten. -2. If you must extract data without aggregation (for example, to check whether rows are present whose newest values match certain conditions), you can use the FINAL modifier for the FROM clause. This approach is significantly less efficient. - diff --git a/docs/en/operations/table_engines/mergetree.md b/docs/en/operations/table_engines/mergetree.md index 448753eef28..366fc7c52d6 100644 --- a/docs/en/operations/table_engines/mergetree.md +++ b/docs/en/operations/table_engines/mergetree.md @@ -4,8 +4,7 @@ The `MergeTree` engine and other engines of this family (`*MergeTree`) are the most robust ClickHousе table engines. -!!!info - The [Merge](merge.md#table_engine-merge) engine does not belong to the `*MergeTree` family. +The basic idea for `MergeTree` engines family is the following. When you have tremendous amount of a data that should be inserted into the table, you should write them quickly part by part and then merge parts by some rules in background. This method is much more efficient than constantly rewriting data in the storage at the insert. Main features: @@ -25,6 +24,9 @@ Main features: If necessary, you can set the data sampling method in the table. +!!!info + The [Merge](merge.md#table_engine-merge) engine does not belong to the `*MergeTree` family. + ## Creating a Table diff --git a/docs/en/operations/table_engines/replacingmergetree.md b/docs/en/operations/table_engines/replacingmergetree.md index bf44d2fa313..04533817256 100644 --- a/docs/en/operations/table_engines/replacingmergetree.md +++ b/docs/en/operations/table_engines/replacingmergetree.md @@ -33,11 +33,12 @@ For a description of request parameters, see [request description](../../query_l **Query clauses** -When creating a `ReplacingMergeTree` table the same [clausesmergetree.md](#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. +When creating a `ReplacingMergeTree` table the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. ### Deprecated Method for Creating a Table -!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. +!!!attention + Do not use this method in new projects and, if possible, switch the old projects to the method described above. ```sql CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] @@ -51,4 +52,3 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] All of the parameters excepting `ver` have the same meaning as in `MergeTree`. - `ver` - column with the version. Optional parameter. For a description, see the text above. - From 5954d904a11a94e6dc06bdd00872299e97f7caa1 Mon Sep 17 00:00:00 2001 From: BayoNet Date: Thu, 11 Oct 2018 16:48:13 +0300 Subject: [PATCH 8/8] 1. Update of CollapsingMergeTree. 2. Minor changes in markup --- .../table_engines/aggregatingmergetree.md | 3 +- .../table_engines/collapsingmergetree.md | 176 +++++++++++++++--- docs/en/operations/table_engines/mergetree.md | 2 +- .../table_engines/replacingmergetree.md | 2 +- .../table_engines/summingmergetree.md | 3 +- .../functions/functions_for_nulls.md | 3 +- .../table_engines/aggregatingmergetree.md | 2 +- docs/ru/operations/table_engines/mergetree.md | 2 +- .../table_engines/replacingmergetree.md | 2 +- .../table_engines/summingmergetree.md | 2 +- 10 files changed, 159 insertions(+), 38 deletions(-) diff --git a/docs/en/operations/table_engines/aggregatingmergetree.md b/docs/en/operations/table_engines/aggregatingmergetree.md index d0c89dd9c39..f04b6b098c9 100644 --- a/docs/en/operations/table_engines/aggregatingmergetree.md +++ b/docs/en/operations/table_engines/aggregatingmergetree.md @@ -33,7 +33,7 @@ When creating a `ReplacingMergeTree` table the same [clauses](mergetree.md#table ### Deprecated Method for Creating a Table -!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. +!!! attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ```sql CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] @@ -89,4 +89,3 @@ FROM test.basic GROUP BY StartDate ORDER BY StartDate; ``` - diff --git a/docs/en/operations/table_engines/collapsingmergetree.md b/docs/en/operations/table_engines/collapsingmergetree.md index 56e8e27c63f..7954ff9b359 100644 --- a/docs/en/operations/table_engines/collapsingmergetree.md +++ b/docs/en/operations/table_engines/collapsingmergetree.md @@ -4,7 +4,9 @@ The engine inherits from [MergeTree](mergetree.md#table_engines-mergetree) and adds to data parts merge algorithm the logic of rows collapsing. -`CollapsingMergeTree` deletes rows collapsing certain pairs of them into one row. The engine may significantly reduce the volume of storage and efficiency of `SELECT` query as a consequence. +`CollapsingMergeTree` deletes (collapses) pairs of rows by specific rules. The engine may significantly reduce the volume of storage and efficiency of `SELECT` query as a consequence. + +See [Collapsing](#collapsingmergetree-algorithm) section of document about `CollapsingMergeTree` input data and algorithm. ## Creating a Table @@ -25,17 +27,17 @@ For a description of request parameters, see [request description](../../query_l **CollapsingMergeTree Parameters** -- `sign` — column with a sign of event. +- `sign` — Name of the column with the type of row: `1` — "state" row, `-1` — "cancel" row. - Type — `Int8`. Possible values are 1 and -1. + Column data type — `Int8`. **Query clauses** -When creating a `CollapsingMergeTree` table the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. +When creating a `CollapsingMergeTree` table, the same [clauses](mergetree.md#table_engines-mergetree-configuring) are required, as when creating a `MergeTree` table. ### Deprecated Method for Creating a Table -!!!attention +!!! attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ```sql @@ -49,45 +51,167 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] All of the parameters excepting `sign` have the same meaning as in `MergeTree`. -- `sign` — column with a sign of event. +- `sign` — Name of the column with the type of row: `1` — "state" row, `-1` — "cancel" row. - Type — `Int8`. Possible values are 1 and -1. + Column Data Type — `Int8`. + + ## Collapsing ### Data -`CollapsingMergeTree` works effectively with the data represented in the form of change log. Change logs are used for incrementally calculating statistics on the data that is constantly changing. Examples are the log of session changes, or logs of changes to user histories. Each row of such a log describes some object and contains additional `sign` field that indicates the values in this row are intended for increment or for decrement. If object is deleted then `sign = -1`, if object is created, then `sign = 1`, if object is changed `sign` can be `1` or `-1` depending on the kind of change. +Consider the situation where you need to save continually changing data for some object. It sounds logical to have one row for an object and update it at any change, but update operation is expensive and slow for DBMS because it requires rewriting of the data in the storage. If you need to write data quickly, update not acceptable, but you can write the changes of an object sequentially as follows. + +Use the particular column `Sign` when writing row. If `Sign = 1` it means that the row is a state of an object, let's call it "state" row. If `Sign = -1` it means the cancellation of the state of an object with the same attributes, let's call it "cancel" row. + +For example, we want to calculate how much pages users checked at some site and how long they were there. At some moment of time we write the following row with the state of user activity: + +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +At some moment later we register the change of user activity and write it with the following two rows. + +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ -1 │ +│ 4324182021466249494 │ 6 │ 185 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +The first row cancels the previous state of the object (user). It should copy all of the fields of the canceled state excepting `Sign`. + +The second row contains the current state. + +As we need only the last state of user activity, the rows + +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ 1 │ +│ 4324182021466249494 │ 5 │ 146 │ -1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +can be deleted collapsing the invalid (old) state of an object. `CollapsingMergeTree` does this while merging of the data parts. + +Why we need 2 rows for each change read in the "Algorithm" paragraph. + +**Peculiar properties of such approach** + +1. The program that writes the data should remember the state of an object to be able to cancel it. "Cancel" string should be the copy of "state" string with the opposite `Sign`. It increases the initial size of storage but allows to write the data quickly. +2. Long growing arrays in columns reduce the efficiency of the engine due to load for writing. The more straightforward data, the higher efficiency. +3. `SELECT` results depend strongly on the consistency of object changes history. Be accurate when preparing data for inserting. You can get unpredictable results in inconsistent data, for example, negative values for non-negative metrics such as session depth. ### Algorithm -When ClickHouse merges data parts, each group of consecutive rows with identical primary key is reduced to not more than two rows, one with `sign = 1` ("increment" row) and another with `sign = -1` ("decrement" row). In other words, entries are collapsed for each resulting data part. +When ClickHouse merges data parts, each group of consecutive rows with the same primary key is reduced to not more than two rows, one with `Sign = 1` ("state" row) and another with `Sign = -1` ("cancel" row). In other words, entries collapse. -ClickHouse saves: +For each resulting data part ClickHouse saves: - 1. The first "decrement" and the last "increment" rows, if the number of "increment" and "decrement" rows matches. - 1. The last "increment" row, if there is one more "increment" row than "decrement" rows. - 1. The first "decrement" row, if there is one more "decrement" row than "increment" rows. - 1. None of rows, in all other cases. + 1. The first "cancel" and the last "state" rows, if the number of "state" and "cancel" rows matches. + 1. The last "state" row, if there is one more "state" row than "cancel" rows. + 1. The first "cancel" row, if there is one more "cancel" row than "state" rows. + 1. None of the rows, in all other cases. - ClickHouse treats this situation as a logical error and record it in the server log, the merge continues. This error can occur if the same data were accidentally inserted more than once. - -!!! Я так и не понял, что в точности происходит при коллапсировании. Что происходит с данными в строке, которые не в первичном ключе и не sign? Какого они должны быть типа или что вообще должно происходить, чтобы при схлопывании рассчитывалась инкрементальная статистика? + The merge continues, but ClickHouse treats this situation as a logical error and records it in the server log. This error can occur if the same data were inserted accidentally more than once. Thus, collapsing should not change the results of calculating statistics. -Changes are gradually collapsed so that in the end only the last value of almost every object is left. +Changes gradually collapsed so that in the end only the last state of almost every object left. -Merging algorithm doesn't guarantee than all of the rows with the same primary key will be in one resulting data part. This means that additional aggregation is required if there is a need to get completely "collapsed" data from `CollapsingMergeTree` table. +The `Sign` is required because the merging algorithm doesn't guarantee that all of the rows with the same primary key will be in the same resulting data part and even on the same physical server. ClickHouse process `SELECT` queries with multiple threads, and it can not predict the order of rows in the result. The aggregation is required if there is a need to get completely "collapsed" data from `CollapsingMergeTree` table. -Ways to complete aggregation: +To finalize collapsing write a query with `GROUP BY` clause and aggregate functions that account for the sign. For example, to calculate quantity, use `sum(Sign)` instead of `count()`. To calculate the sum of something, use `sum(Sign * x)` instead of `sum(x)`, and so on, and also add `HAVING sum(Sign) > 0`. -1. Write a query with `GROUP BY` clause and aggregate functions that accounts for the sign. For example, to calculate quantity, use `sum(sign)` instead of `count()`. To calculate the sum of something, use `sum(sign * x)` instead of `sum(x)`, and so on, and also add `HAVING sum(sign) > 0`. Not all amounts can be calculated this way. For example, the aggregate functions `min` and `max` can't be rewritten. -2. If you must extract data without aggregation (for example, to check whether rows are present whose newest values match certain conditions), you can use the `FINAL` modifier for the `FROM` clause. This approach is significantly less efficient. +The aggregates `count`, `sum` and `avg` could be calculated this way. The aggregate `uniq` could be calculated if an object has at list one state not collapsed. The aggregates `min` and `max` could not be calculated because `CollapsingMergeTree` does not save values history of the collapsed states. -## Use in Yandex.Metrica +If you need to extract data without aggregation (for example, to check whether rows are present whose newest values match certain conditions), you can use the `FINAL` modifier for the `FROM` clause. This approach is significantly less efficient. -Yandex.Metrica has normal logs (such as hit logs) and change logs. Change logs are used for incrementally calculating statistics on data that is constantly changing. Examples are the log of session changes, or logs of changes to user histories. Sessions are constantly changing in Yandex.Metrica. For example, the number of hits per session increases. We refer to changes in any object as a pair (?old values, ?new values). Old values may be missing if the object was created. New values may be missing if the object was deleted. If the object was changed, but existed previously and was not deleted, both values are present. In the change log, one or two entries are made for each change. Each entry contains all the attributes that the object has, plus a special attribute for differentiating between the old and new values. When objects change, only the new entries are added to the change log, and the existing ones are not touched. +## Example of use -The change log makes it possible to incrementally calculate almost any statistics. To do this, we need to consider "new" rows with a plus sign, and "old" rows with a minus sign. In other words, incremental calculation is possible for all statistics whose algebraic structure contains an operation for taking the inverse of an element. This is true of most statistics. We can also calculate "idempotent" statistics, such as the number of unique visitors, since the unique visitors are not deleted when making changes to sessions. +Example data: -This is the main concept that allows Yandex.Metrica to work in real time. +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ 1 │ +│ 4324182021466249494 │ 5 │ 146 │ -1 │ +│ 4324182021466249494 │ 6 │ 185 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +Creation of the table: + +```sql +CREATE TABLE UAct +( + UserID UInt64, + PageViews UInt8, + Duration UInt8, + Sign Int8 +) +ENGINE = CollapsingMergeTree(Sign) +ORDER BY UserID +``` + +Insertion of the data: + +```sql +INSERT INTO UAct VALUES (4324182021466249494, 5, 146, 1) +``` +```sql +INSERT INTO UAct VALUES (4324182021466249494, 5, 146, -1),(4324182021466249494, 6, 185, 1) +``` + +We use two `INSERT` queries to create two different data parts. If we insert the data with one query ClickHouse creates one data part and will not perform any merge ever. + +Getting the data + +``` +SELECT * FROM UAct +``` + +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ -1 │ +│ 4324182021466249494 │ 6 │ 185 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 5 │ 146 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +What do we see and where is collapsing? +With two `INSERT` queries, we created 2 data parts. The `SELECT` query was performed in 2 threads, and we got a random order of rows. +Collapsing not occurred because there was no merge of the data parts yet. ClickHouse merges data part in an unknown moment of time which we can not predict. + +Thus we need aggregation: + +```sql +SELECT + UserID, + sum(PageViews * Sign) AS PageViews, + sum(Duration * Sign) AS Duration +FROM UAct +GROUP BY UserID +HAVING sum(Sign) > 0 +``` +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┐ +│ 4324182021466249494 │ 6 │ 185 │ +└─────────────────────┴───────────┴──────────┘ +``` + +If we do not need aggregation and want to force collapsing, we can use `FINAL` modifier for `FROM` clause. + +```sql +SELECT * FROM UAct FINAL +``` +``` +┌──────────────UserID─┬─PageViews─┬─Duration─┬─Sign─┐ +│ 4324182021466249494 │ 6 │ 185 │ 1 │ +└─────────────────────┴───────────┴──────────┴──────┘ +``` + +This way of selecting the data is very inefficient. Don't use it for big tables. diff --git a/docs/en/operations/table_engines/mergetree.md b/docs/en/operations/table_engines/mergetree.md index 366fc7c52d6..bc6c337e600 100644 --- a/docs/en/operations/table_engines/mergetree.md +++ b/docs/en/operations/table_engines/mergetree.md @@ -78,7 +78,7 @@ We also set an expression for sampling as a hash by the user ID. This allows you ### Deprecated Method for Creating a Table -!!!attention +!!! attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ``` diff --git a/docs/en/operations/table_engines/replacingmergetree.md b/docs/en/operations/table_engines/replacingmergetree.md index 04533817256..e96cb74c681 100644 --- a/docs/en/operations/table_engines/replacingmergetree.md +++ b/docs/en/operations/table_engines/replacingmergetree.md @@ -37,7 +37,7 @@ When creating a `ReplacingMergeTree` table the same [clauses](mergetree.md#table ### Deprecated Method for Creating a Table -!!!attention +!!! attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ```sql diff --git a/docs/en/operations/table_engines/summingmergetree.md b/docs/en/operations/table_engines/summingmergetree.md index 97e003597d6..73382e72075 100644 --- a/docs/en/operations/table_engines/summingmergetree.md +++ b/docs/en/operations/table_engines/summingmergetree.md @@ -36,7 +36,7 @@ When creating a `SummingMergeTree` table the same [clauses](mergetree.md#table_e ### Deprecated Method for Creating a Table -!!!attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. +!!! attention Do not use this method in new projects and, if possible, switch the old projects to the method described above. ``` CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] @@ -129,4 +129,3 @@ Examples: When requesting data, use the [sumMap(key, value)](../../query_language/agg_functions/reference.md#agg_function-summary) function for aggregation of `Map`. For nested data structure, you do not need to specify its columns in the tuple of columns for summation. - diff --git a/docs/en/query_language/functions/functions_for_nulls.md b/docs/en/query_language/functions/functions_for_nulls.md index d52d0c840ec..c2a1e25aee7 100644 --- a/docs/en/query_language/functions/functions_for_nulls.md +++ b/docs/en/query_language/functions/functions_for_nulls.md @@ -8,7 +8,7 @@ Checks whether the argument is [NULL](../syntax.md#null-literal). isNull(x) ``` -**Parameters:** +**Parameters** - `x` — A value with a non-compound data type. @@ -292,4 +292,3 @@ SELECT toTypeName(toNullable(10)) │ Nullable(UInt8) │ └────────────────────────────┘ ``` - diff --git a/docs/ru/operations/table_engines/aggregatingmergetree.md b/docs/ru/operations/table_engines/aggregatingmergetree.md index 5b4b1621a05..42bff397e5e 100644 --- a/docs/ru/operations/table_engines/aggregatingmergetree.md +++ b/docs/ru/operations/table_engines/aggregatingmergetree.md @@ -34,7 +34,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] ### Устаревший способ создания таблицы -!!!attention +!!! attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql diff --git a/docs/ru/operations/table_engines/mergetree.md b/docs/ru/operations/table_engines/mergetree.md index eecbbbf345a..ce6b793ade5 100644 --- a/docs/ru/operations/table_engines/mergetree.md +++ b/docs/ru/operations/table_engines/mergetree.md @@ -77,7 +77,7 @@ ENGINE MergeTree() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDa ### Устаревший способ создания таблицы -!!!attention +!!! attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ``` diff --git a/docs/ru/operations/table_engines/replacingmergetree.md b/docs/ru/operations/table_engines/replacingmergetree.md index 68848993d11..b2397200abd 100644 --- a/docs/ru/operations/table_engines/replacingmergetree.md +++ b/docs/ru/operations/table_engines/replacingmergetree.md @@ -38,7 +38,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] ### Устаревший способ создания таблицы -!!!attention +!!! attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```sql diff --git a/docs/ru/operations/table_engines/summingmergetree.md b/docs/ru/operations/table_engines/summingmergetree.md index 73cafad3605..b169ad9833c 100644 --- a/docs/ru/operations/table_engines/summingmergetree.md +++ b/docs/ru/operations/table_engines/summingmergetree.md @@ -36,7 +36,7 @@ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster] ### Устаревший способ создания таблицы -!!!attention +!!! attention Не используйте этот способ в новых проектах и по возможности переведите старые проекты на способ описанный выше. ```