Динамический
Этот тип позволяет хранить значения любого типа внутри себя без необходимости заранее знать все из них.
Чтобы объявить колонку типа 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 сериализуются в следующем формате: