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

动态

Beta feature. Learn more.

此类型允许在其中存储任意类型的值,而无需提前了解所有类型。

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

其中 N 是一个可选参数,范围在 0254 之间,表示在类型为 Dynamic 的列中可以存储多少种不同的数据类型作为独立的子列,这些数据类型存储在统一的数据块中(例如,MergeTree 表的单个数据分片)。如果超过此限制,所有新的数据类型的值将一起以二进制格式存储在一个特殊的共享数据结构中。max_types 的默认值为 32

备注

Dynamic 数据类型是一个测试功能。要使用它,请设置 enable_dynamic_type = 1

创建动态

在表列定义中使用 Dynamic 类型:

使用普通列进行类型转换:

Variant 列进行类型转换:

以子列方式读取动态嵌套类型

Dynamic 类型支持从 Dynamic 列中以类型名称作为子列读取单一嵌套类型。 因此,如果有列 d Dynamic,您可以使用语法 d.T 读取任何有效类型 T 的子列, 这个子列将具有类型 Nullable(T) (如果 T 可以在 Nullable 中存在)或类型 T,否则。这个子列的大小将与原始 Dynamic 列相同,并且在原始 Dynamic 列没有类型 T 的所有行中将包含 NULL 值(或如果 T 不能在 Nullable 中存在,则包含空值)。

也可以使用函数 dynamicElement(dynamic_column, type_name) 读取 Dynamic 子列。

示例:

要知道每一行存储的变体,可以使用函数 dynamicType(dynamic_column)。它将返回 String,每一行的值类型名称(如果行是 NULL 则返回 'None')。

示例:

动态列与其他列之间的转换

可以对 Dynamic 列执行 4 种可能的转换。

将普通列转换为动态列

通过解析将字符串列转换为动态列

要从 String 列中解析出 Dynamic 类型值,您可以启用设置 cast_string_to_dynamic_use_inference

将动态列转换为普通列

可以将 Dynamic 列转换为普通列。在这种情况下,所有嵌套类型将转换为目标类型:

将变体列转换为动态列

将动态(max_types=N) 列转换为另一个动态(max_types=K) 列

如果 K >= N ,则在转换时数据不会改变:

如果 K < N,则最稀有类型的值将插入到一个特殊的子列中,但仍然可以访问:

函数 isDynamicElementInSharedData 对于存储在 Dynamic 内部特殊共享数据结构中的行返回 true,可以看到,结果列只包含 2 种没有存储在共享数据结构中的类型。

如果 K=0,所有类型将插入到一个特殊的子列中:

从数据中读取动态类型

所有文本格式(TSV、CSV、自定义分隔、值、JSONEachRow 等)支持读取 Dynamic 类型。在数据解析过程中,ClickHouse 尝试推断每个值的类型,并在插入到 Dynamic 列中时使用它。

示例:

在函数中使用动态类型

大多数函数支持类型为 Dynamic 的参数。在这种情况下,函数会在 Dynamic 列中单独执行,每个内部数据类型。 当函数的结果类型依赖于参数类型时,使用 Dynamic 参数执行的该函数的结果将是 Dynamic。当函数的结果类型不依赖于参数类型时,结果将是 Nullable(T),其中 T 是该函数的常规结果类型。

示例:

如果无法在 Dynamic 列中的某些类型下执行函数,将抛出异常:

可以过滤掉不需要的类型:

或者提取所需类型作为子列:

在 ORDER BY 和 GROUP BY 中使用动态类型

ORDER BYGROUP BY 中,Dynamic 类型的值的比较与 Variant 类型的值类似: 值 d1 的类型为 T1 和值 d2 的类型 T2 的操作符 < 结果的定义如下:

  • 如果 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 数据类型只能将有限数量的不同数据类型作为独立子列存储。默认情况下,此限制为 32,但您可以使用语法 Dynamic(max_types=N) 修改它,其中 N 在 0 和 254 之间(由于实现细节,不可能有超过 254 种不同的数据类型可以存储为 Dynamic 中的独立子列)。 当达到限制时,插入到 Dynamic 列中的所有新数据类型将被插入到一个单一的共享数据结构中,该结构以二进制形式存储不同数据类型的值。

让我们看看在不同场景下达到限制时会发生什么。

在数据解析过程中达到限制

在从数据中解析 Dynamic 值时,当当前数据块的限制达到时,所有新的值将被插入到共享数据结构中:

如我们所见,在插入的 3 种不同数据类型 Int64Array(Int64)String 之后,所有的新类型都被插入到特殊的共享数据结构中。

在 MergeTree 表引擎中合并数据部分时

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

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

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

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

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

动态中的 JSONExtract 函数

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

二进制输出格式

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