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

Dynamic

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

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

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

Создание Dynamic

Используя тип 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 сериализуются в следующем формате: