跳到主要内容
跳到主要内容

Dynamic

这种类型允许在内部存储任何类型的值,而无需事先知道它们的所有类型。

要声明一个 Dynamic 类型的列,请使用以下语法:

其中 N 是一个可选参数,范围在 0254 之间,指示在一个 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 BYGROUP 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 种不同的数据类型 Int64Array(Int64)String 后,所有新类型均被插入到特殊共享数据结构中。

During merges of data parts in MergeTree table engines

在 MergeTree 表中合并多个数据部分时,结果数据部分中的 Dynamic 列可能达到可以存储在独立子列中的不同数据类型的限制,并且将无法存储所有来自源部分的类型作为子列。 在这种情况下,ClickHouse 将选择哪些类型在合并后保持为独立子列,哪些类型将插入到共享数据结构中。在大多数情况下,ClickHouse 尝试保留最常见的类型,并将最稀有的类型存储在共享数据结构中,但这取决于具体实现。

让我们看看这样的合并示例。首先,让我们创建一个带有 Dynamic 列的表,将不同数据类型的限制设置为 3,并插入具有 5 种不同类型的值:

每次插入将创建一个包含单一类型的独立数据部分:

现在,让我们将所有部分合并为一个并看看会发生什么:

如我们所见,ClickHouse 保留了最常见的类型 UInt64Array(UInt64) 作为子列,并将所有其他类型插入到共享数据中。

JSONExtract functions with Dynamic

所有 JSONExtract* 函数支持 Dynamic 类型:

Binary output format

在 RowBinary 格式中,Dynamic 类型的值以以下格式进行序列化: