Перейти к основному содержимому
Перейти к основному содержимому

Динамический

Beta feature. Learn more.

Этот тип позволяет хранить значения любого типа внутри себя без необходимости заранее знать все из них.

Чтобы объявить колонку типа Dynamic, используйте следующий синтаксис:

Где N - это необязательный параметр от 0 до 254, указывающий, сколько различных типов данных может храниться в виде отдельных подколонок внутри колонки типа Dynamic в пределах одного блока данных, который хранится отдельно (например, в пределах одной части данных для таблицы MergeTree). Если этот лимит превышен, все значения с новыми типами будут храниться вместе в специальной общей структуре данных в двоичном формате. Значение по умолчанию для max_types равно 32.

примечание

Тип данных Dynamic - это бета-функция. Чтобы использовать его, установите enable_dynamic_type = 1.

Создание Динамического

Использование типа Dynamic в определении колонки таблицы:

Использование CAST из обычной колонки:

Использование CAST из колонки Variant:

Чтение вложенных типов Dynamic как подколонок

Тип Dynamic поддерживает чтение одного вложенного типа из колонки Dynamic, используя имя типа в качестве подколонки. Таким образом, если у вас есть колонка d Dynamic, вы можете прочитать подколонку любого допустимого типа T, используя синтаксис d.T, эта подколонка будет иметь тип Nullable(T), если T может находиться внутри Nullable, и T в противном случае. Эта подколонка будет такой же по размеру, как оригинальная колонка Dynamic, и будет содержать значения NULL (или пустые значения, если T не может находиться внутри Nullable) во всех строках, где оригинальная колонка Dynamic не имеет типа T.

Подколонки Dynamic также можно читать с помощью функции dynamicElement(dynamic_column, type_name).

Примеры:

Чтобы узнать, какой вариант хранится в каждой строке, можно использовать функцию dynamicType(dynamic_column). Она возвращает String с именем типа для каждой строки (или 'None', если строка NULL).

Пример:

Конверсия между колонкой Dynamic и другими колонками

Существует 4 возможных конверсии, которые можно выполнить с колонкой Dynamic.

Конвертация обычной колонки в колонку Dynamic

Конвертация колонки String в колонку Dynamic через парсинг

Чтобы распарсить значения типа Dynamic из колонки String, вы можете включить настройку cast_string_to_dynamic_use_inference:

Конвертация колонки Dynamic в обычную колонку

Можно конвертировать колонку Dynamic в обычную колонку. В этом случае все вложенные типы будут преобразованы в целевой тип:

Конвертация колонки Variant в колонку Dynamic

Конвертация колонки Dynamic(max_types=N) в другую колонку Dynamic(max_types=K)

Если K >= N, то во время конверсии данные не изменяются:

Если K < N, то значения с самыми редкими типами будут помещены в одну специальную подколонку, но все еще будут доступны:

Функция isDynamicElementInSharedData возвращает true для строк, которые хранятся в специальной общей структуре данных внутри Dynamic, и, как видно, результирующая колонка содержит только 2 типа, которые не хранятся в общей структуре данных.

Если K=0, все типы будут помещены в одну специальную подколонку:

Чтение типа Dynamic из данных

Все текстовые форматы (TSV, CSV, CustomSeparated, Values, JSONEachRow и т.д.) поддерживают чтение типа Dynamic. Во время парсинга данных ClickHouse пытается определить тип каждого значения и использовать его при вставке в колонку Dynamic.

Пример:

Использование типа Dynamic в функциях

Большинство функций поддерживают аргументы типа Dynamic. В этом случае функция выполняется отдельно для каждого внутреннего типа данных, хранящегося внутри колонки Dynamic. Когда тип результата функции зависит от типов аргументов, результат такой функции, выполненной с аргументами Dynamic, будет Dynamic. Когда тип результата функции не зависит от типов аргументов - результат будет Nullable(T), где T - обычный тип результата этой функции.

Примеры:

Если функция не может быть выполнена для какого-либо типа внутри колонки Dynamic, будет выдано исключение:

Мы можем отфильтровать ненужные типы:

Или извлечь требуемый тип как подколонку:

Использование типа Dynamic в ORDER BY и GROUP BY

Во время ORDER BY и GROUP BY значения типов Dynamic сравниваются аналогично значениям типа Variant: Результат оператора < для значений d1 с базовым типом T1 и d2 с базовым типом T2 типа Dynamic определяется следующим образом:

  • Если T1 = T2 = T, результат будет d1.T < d2.T (подлежащие значения будут сравниваться).
  • Если T1 != T2, результат будет T1 < T2 (имена типов будут сравниваться).

По умолчанию тип Dynamic не разрешен в ключах GROUP BY/ORDER BY, если вы хотите использовать его, учитывайте его специальное правило сравнения и включите настройки allow_suspicious_types_in_group_by/allow_suspicious_types_in_order_by.

Примеры:

Примечание: значения динамических типов с различными числовыми типами рассматриваются как разные значения и не сравниваются друг с другом, их имена типов сравниваются вместо этого.

Пример:

Примечание: описанное правило сравнения не применяется во время выполнения функций сравнения, таких как </>/= и других из-за особой работы функций с типом Dynamic.

Достижение предела в количестве различных типов данных, хранящихся внутри Dynamic

Тип данных Dynamic может хранить только ограниченное количество различных типов данных в виде отдельных подколонок. По умолчанию этот лимит равен 32, но вы можете изменить его в объявлении типа, используя синтаксис Dynamic(max_types=N), где N находится в пределах от 0 до 254 (из-за деталей реализации невозможно иметь более 254 различных типов данных, которые могут храниться в виде отдельных подколонок внутри Dynamic). Когда лимит достигнут, все новые типы данных, вставленные в колонку Dynamic, будут вставлены в одну общую структуру данных, которая хранит значения с разными типами данных в двоичном формате.

Давайте посмотрим, что происходит, когда лимит достигается в разных сценариях.

Достижение лимита во время парсинга данных

Во время парсинга значений Dynamic из данных, когда лимит достигнут для текущего блока данных, все новые значения будут вставлены в общую структуру данных:

Как видно, после вставки 3 различных типов Int64, Array(Int64) и String все новые типы были вставлены в специальную общую структуру данных.

Во время слияний частей данных в таблицах двигателей MergeTree

Во время слияния нескольких частей данных в таблице MergeTree колонка Dynamic в результирующей части данных может достичь лимита различных типов данных, которые могут храниться в отдельных подколонках внутрь и не сможет хранить все типы в виде подколонок из исходных частей. В этом случае ClickHouse определяет, какие типы останутся как отдельные подколонки после слияния, а какие типы будут вставлены в общую структуру данных. В большинстве случаев ClickHouse старается сохранить самые частые типы и сохранить наименее частые типы в общей структуре данных, однако это зависит от реализации.

Давайте посмотрим на пример такого слияния. Сначала создадим таблицу с колонкой Dynamic, установим лимит различных типов данных равным 3 и вставим значения с 5 различными типами:

Каждое вставка создаст отдельную часть данных с колонкой Dynamic, содержащей один тип:

Теперь давайте объединим все части в одну и посмотрим, что произойдет:

Как видно, ClickHouse сохранил самые частые типы UInt64 и Array(UInt64) как подколонки и вставил все остальные типы в общую структуру данных.

Функции JSONExtract с Dynamic

Все функции JSONExtract* поддерживают тип Dynamic:

Формат бинарного вывода

В формате RowBinary значения типа Dynamic сериализуются в следующем формате: