Dynamic
这种类型允许在内部存储任何类型的值,而无需事先知道它们的所有类型。
要声明一个 Dynamic
类型的列,请使用以下语法:
其中 N
是一个可选参数,范围在 0
到 254
之间,指示在一个 Dynamic
类型的列内,能够作为独立子列存储的不同数据类型数量,该列跨单个存储在单独数据块中的数据(例如,跨 MergeTree 表的单个数据部分)。如果超过此限制,所有具有新类型的值将一起存储在一个特殊的共享数据结构中,以二进制形式存储。max_types
的默认值为 32
。
Creating Dynamic
在表列定义中使用 Dynamic
类型:
使用 CAST 从普通列:
使用 CAST 从 Variant
列:
Reading Dynamic nested types as subcolumns
Dynamic
类型支持使用类型名称作为子列,从 Dynamic
列中读取单个嵌套类型。
因此,如果您有列 d Dynamic
,您可以使用语法 d.T
来读取任何有效类型 T
的子列,
该子列将具有类型 Nullable(T)
,如果 T
可以在 Nullable
内部;否则为 T
。该子列的大小
将与原始 Dynamic
列相同,并且在原始 Dynamic
列没有类型 T
的所有行中将包含 NULL
值(或者如果 T
不能在 Nullable
内部,则包含空值)。
Dynamic
子列也可以使用函数 dynamicElement(dynamic_column, type_name)
进行读取。
示例:
要知道每行存储的变体,可以使用函数 dynamicType(dynamic_column)
。它返回每行的值类型名称的 String
(如果行是 NULL
,则返回 'None'
)。
示例:
Conversion between Dynamic column and other columns
可以对 Dynamic
列进行四种可能的转换。
Converting an ordinary column to a Dynamic column
Converting a String column to a Dynamic column through parsing
要从 String
列解析 Dynamic
类型值,您可以启用设置 cast_string_to_dynamic_use_inference
:
Converting a Dynamic column to an ordinary column
可以将 Dynamic
列转换为普通列。在这种情况下,所有嵌套类型将被转换为目标类型:
Converting a Variant column to Dynamic column
Converting a Dynamic(max_types=N) column to another Dynamic(max_types=K)
如果 K >= N
,那么在转换过程中数据不会改变:
如果 K < N
,则具有最稀有类型的值将被插入到单个特殊子列中,但仍然可以访问:
函数 isDynamicElementInSharedData
对存储在 Dynamic
内部特殊共享数据结构中的行返回 true
,如我们所见,结果列仅包含未存储在共享数据结构中的 2 种类型。
如果 K=0
,所有类型将被插入到单个特殊子列中:
Reading Dynamic type from the data
所有文本格式(TSV, CSV, CustomSeparated, Values, JSONEachRow 等)支持读取 Dynamic
类型。在数据解析过程中,ClickHouse 尝试推断每个值的类型并在插入到 Dynamic
列时使用它。
示例:
Using Dynamic type in functions
大多数函数支持类型为 Dynamic
的参数。在这种情况下,该函数将分别在存储在 Dynamic
列内部的每种内部数据类型上执行。
当函数的结果类型依赖于参数类型时,使用 Dynamic
参数执行的结果将是 Dynamic
。当函数的结果类型不依赖于参数类型时,结果将是 Nullable(T)
,其中 T
是该函数的常规结果类型。
示例:
如果函数无法在 Dynamic
列中的某个类型上执行,将抛出异常:
我们可以过滤掉不需要的类型:
或提取所需类型作为子列:
Using Dynamic type in ORDER BY and GROUP BY
在 ORDER BY
和 GROUP BY
期间,Dynamic
类型的值与 Variant
类型的值的比较方式相同:
操作符 <
对于值 d1
(底层类型为 T1
)和 d2
(底层类型为 T2
)的 Dynamic
类型的结果定义如下:
- 如果
T1 = T2 = T
,结果将是d1.T < d2.T
(基础值将被比较)。 - 如果
T1 != T2
,结果将是T1 < T2
(类型名称将被比较)。
默认情况下,GROUP BY
/ORDER BY
键中不允许使用 Dynamic
类型,如果您想使用它,请考虑其特殊比较规则,并启用 allow_suspicious_types_in_group_by
/allow_suspicious_types_in_order_by
设置。
示例:
**注意:**具有不同数值类型的动态类型值被视为不同的值,并且不会相互比较,而是比较它们的类型名称。
示例:
**注意:**所描述的比较规则在执行比较函数时并不适用,例如 <
/>
/=
等,因为 特殊工作 的函数具有 Dynamic
类型。
Reaching the limit in number of different data types stored inside Dynamic
Dynamic
数据类型只能存储有限数量的不同数据类型作为独立子列。默认情况下,这个限制是 32
,但您可以在类型声明中使用语法 Dynamic(max_types=N)
来更改它,其中 N 的范围在 0 到 254 之间(由于实现细节,无法存储超过 254 种不同数据类型作为独立子列)。
当达到限制时,所有插入到 Dynamic
列的新数据类型将插入到一个共享数据结构中,该结构以二进制形式存储具有不同数据类型的值。
让我们看看当达到限制时在不同场景下会发生什么。
Reaching the limit during data parsing
在从数据解析 Dynamic
值的过程中,当当前数据块达到限制时,所有新值将插入到共享数据结构中:
如我们所见,插入 3 种不同的数据类型 Int64
、Array(Int64)
和 String
后,所有新类型均被插入到特殊共享数据结构中。
During merges of data parts in MergeTree table engines
在 MergeTree 表中合并多个数据部分时,结果数据部分中的 Dynamic
列可能达到可以存储在独立子列中的不同数据类型的限制,并且将无法存储所有来自源部分的类型作为子列。
在这种情况下,ClickHouse 将选择哪些类型在合并后保持为独立子列,哪些类型将插入到共享数据结构中。在大多数情况下,ClickHouse 尝试保留最常见的类型,并将最稀有的类型存储在共享数据结构中,但这取决于具体实现。
让我们看看这样的合并示例。首先,让我们创建一个带有 Dynamic
列的表,将不同数据类型的限制设置为 3
,并插入具有 5
种不同类型的值:
每次插入将创建一个包含单一类型的独立数据部分:
现在,让我们将所有部分合并为一个并看看会发生什么:
如我们所见,ClickHouse 保留了最常见的类型 UInt64
和 Array(UInt64)
作为子列,并将所有其他类型插入到共享数据中。
JSONExtract functions with Dynamic
所有 JSONExtract*
函数支持 Dynamic
类型:
Binary output format
在 RowBinary 格式中,Dynamic
类型的值以以下格式进行序列化: