> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ClickHouse 中 Dynamic 数据类型的文档，它可在单个列中存储不同类型的 值

# Dynamic

该类型允许在其中存储任意类型的值，而无需预先知晓所有类型。

要声明 `Dynamic` 类型的列，请使用以下语法：

```sql theme={null}
<column_name> Dynamic(max_types=N)
```

其中，`N` 是一个介于 `0` 到 `254` 之间的可选参数，用于指定在单个独立存储的数据块中 (例如，对于 MergeTree 表，即单个数据分区片段中) ，`Dynamic` 类型的列最多可将多少种不同的数据类型作为独立子列存储。如果超过此限制，所有新类型的值都会以二进制形式统一存储在一种特殊的共享数据结构中。`max_types` 的默认值为 `32`。

<div id="creating-dynamic">
  ## 创建 Dynamic
</div>

在表的列定义中使用 `Dynamic` 类型：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ          │ None           │
│ 42            │ Int64          │
│ Hello, World! │ String         │
│ [1,2,3]       │ Array(Int64)   │
└───────────────┴────────────────┘
```

对普通列使用 CAST：

```sql theme={null}
SELECT 'Hello, World!'::Dynamic AS d, dynamicType(d);
```

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┐
│ Hello, World! │ String         │
└───────────────┴────────────────┘
```

对 `Variant` 列使用 CAST：

```sql theme={null}
SET use_variant_as_common_type = 1;
SELECT multiIf((number % 3) = 0, number, (number % 3) = 1, range(number + 1), NULL)::Dynamic AS d, dynamicType(d) FROM numbers(3)
```

```text theme={null}
┌─d─────┬─dynamicType(d)─┐
│ 0     │ UInt64         │
│ [0,1] │ Array(UInt64)  │
│ ᴺᵁᴸᴸ  │ None           │
└───────┴────────────────┘
```

<div id="reading-dynamic-nested-types-as-subcolumns">
  ## 将 Dynamic 嵌套类型读取为子列
</div>

`Dynamic` 类型支持将类型名称用作子列，从 `Dynamic` 列中读取单个嵌套类型。
因此，如果你有一列 `d Dynamic`，就可以使用语法 `d.T` 读取任意有效类型 `T` 的子列。
如果 `T` 可以包含在 `Nullable` 中，则该子列的类型为 `Nullable(T)`；否则类型为 `T`。该子列的大小将
与原始 `Dynamic` 列相同，并且在原始 `Dynamic` 列不属于类型 `T` 的所有行中，其值将为 `NULL` (如果 `T` 不能包含在 `Nullable` 中，则为空值) 。

`Dynamic` 子列也可以通过函数 \`dynamicElement(dynamic\_column, type\_name)\`\` 读取。

示例：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT d, dynamicType(d), d.String, d.Int64, d.`Array(Int64)`, d.Date, d.`Array(String)` FROM test;
```

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┬─d.String──────┬─d.Int64─┬─d.Array(Int64)─┬─d.Date─┬─d.Array(String)─┐
│ ᴺᵁᴸᴸ          │ None           │ ᴺᵁᴸᴸ          │    ᴺᵁᴸᴸ │ []             │   ᴺᵁᴸᴸ │ []              │
│ 42            │ Int64          │ ᴺᵁᴸᴸ          │      42 │ []             │   ᴺᵁᴸᴸ │ []              │
│ Hello, World! │ String         │ Hello, World! │    ᴺᵁᴸᴸ │ []             │   ᴺᵁᴸᴸ │ []              │
│ [1,2,3]       │ Array(Int64)   │ ᴺᵁᴸᴸ          │    ᴺᵁᴸᴸ │ [1,2,3]        │   ᴺᵁᴸᴸ │ []              │
└───────────────┴────────────────┴───────────────┴─────────┴────────────────┴────────┴─────────────────┘
```

```sql theme={null}
SELECT toTypeName(d.String), toTypeName(d.Int64), toTypeName(d.`Array(Int64)`), toTypeName(d.Date), toTypeName(d.`Array(String)`)  FROM test LIMIT 1;
```

```text theme={null}
┌─toTypeName(d.String)─┬─toTypeName(d.Int64)─┬─toTypeName(d.Array(Int64))─┬─toTypeName(d.Date)─┬─toTypeName(d.Array(String))─┐
│ Nullable(String)     │ Nullable(Int64)     │ Array(Int64)               │ Nullable(Date)     │ Array(String)               │
└──────────────────────┴─────────────────────┴────────────────────────────┴────────────────────┴─────────────────────────────┘
```

````sql theme={null}
SELECT d, dynamicType(d), dynamicElement(d, 'String'), dynamicElement(d, 'Int64'), dynamicElement(d, 'Array(Int64)'), dynamicElement(d, 'Date'), dynamicElement(d, 'Array(String)') FROM test;```
````

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┬─dynamicElement(d, 'String')─┬─dynamicElement(d, 'Int64')─┬─dynamicElement(d, 'Array(Int64)')─┬─dynamicElement(d, 'Date')─┬─dynamicElement(d, 'Array(String)')─┐
│ ᴺᵁᴸᴸ          │ None           │ ᴺᵁᴸᴸ                        │                       ᴺᵁᴸᴸ │ []                                │                      ᴺᵁᴸᴸ │ []                                 │
│ 42            │ Int64          │ ᴺᵁᴸᴸ                        │                         42 │ []                                │                      ᴺᵁᴸᴸ │ []                                 │
│ Hello, World! │ String         │ Hello, World!               │                       ᴺᵁᴸᴸ │ []                                │                      ᴺᵁᴸᴸ │ []                                 │
│ [1,2,3]       │ Array(Int64)   │ ᴺᵁᴸᴸ                        │                       ᴺᵁᴸᴸ │ [1,2,3]                           │                      ᴺᵁᴸᴸ │ []                                 │
└───────────────┴────────────────┴─────────────────────────────┴────────────────────────────┴───────────────────────────────────┴───────────────────────────┴────────────────────────────────────┘
```

要了解每一行中存储的是哪种 Variant，可以使用函数 `dynamicType(dynamic_column)`。它会为每一行返回一个 `String`，其值为该行的类型名称 (如果该行为 `NULL`，则返回 `'None'`) 。

示例：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('Hello, World!'), ([1, 2, 3]);
SELECT dynamicType(d) FROM test;
```

```text theme={null}
┌─dynamicType(d)─┐
│ None           │
│ Int64          │
│ String         │
│ Array(Int64)   │
└────────────────┘
```

<div id="conversion-between-dynamic-column-and-other-columns">
  ## Dynamic 列与其他列之间的转换
</div>

`Dynamic` 列与其他列之间共有 4 种可能的转换方式。

<div id="converting-an-ordinary-column-to-a-dynamic-column">
  ### 将普通列转换为 Dynamic 列
</div>

```sql theme={null}
SELECT 'Hello, World!'::Dynamic AS d, dynamicType(d);
```

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┐
│ Hello, World! │ String         │
└───────────────┴────────────────┘
```

<div id="converting-a-string-column-to-a-dynamic-column-through-parsing">
  ### 通过解析将 String 列转换为 Dynamic 列
</div>

要从 `String` 列解析出 `Dynamic` 类型的值，可以启用设置 `cast_string_to_dynamic_use_inference`：

```sql theme={null}
SET cast_string_to_dynamic_use_inference = 1;
SELECT CAST(materialize(map('key1', '42', 'key2', 'true', 'key3', '2020-01-01')), 'Map(String, Dynamic)') as map_of_dynamic, mapApply((k, v) -> (k, dynamicType(v)), map_of_dynamic) as map_of_dynamic_types;
```

```text theme={null}
┌─map_of_dynamic──────────────────────────────┬─map_of_dynamic_types─────────────────────────┐
│ {'key1':42,'key2':true,'key3':'2020-01-01'} │ {'key1':'Int64','key2':'Bool','key3':'Date'} │
└─────────────────────────────────────────────┴──────────────────────────────────────────────┘
```

<div id="converting-a-dynamic-column-to-an-ordinary-column">
  ### 将 Dynamic 列转换为普通列
</div>

可以将 `Dynamic` 列转换为普通列。在这种情况下，所有嵌套类型都会转换为目标类型：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('42.42'), (true), ('e10');
SELECT d::Nullable(Float64) FROM test;
```

```text theme={null}
┌─CAST(d, 'Nullable(Float64)')─┐
│                         ᴺᵁᴸᴸ │
│                           42 │
│                        42.42 │
│                            1 │
│                            0 │
└──────────────────────────────┘
```

<div id="converting-a-variant-column-to-dynamic-column">
  ### 将 Variant 列转换为 Dynamic 列
</div>

```sql theme={null}
CREATE TABLE test (v Variant(UInt64, String, Array(UInt64))) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), ('String'), ([1, 2, 3]);
SELECT v::Dynamic AS d, dynamicType(d) FROM test; 
```

```text theme={null}
┌─d───────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ    │ None           │
│ 42      │ UInt64         │
│ String  │ String         │
│ [1,2,3] │ Array(UInt64)  │
└─────────┴────────────────┘
```

<div id="converting-a-dynamicmax_typesn-column-to-another-dynamicmax_typesk">
  ### 将 Dynamic(max\_types=N) 列转换为另一个 Dynamic(max\_types=K)
</div>

如果 `K >= N`，那么转换过程中数据不会发生变化：

```sql theme={null}
CREATE TABLE test (d Dynamic(max_types=3)) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), (43), ('42.42'), (true);
SELECT d::Dynamic(max_types=5) as d2, dynamicType(d2) FROM test;
```

```text theme={null}
┌─d─────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ  │ None           │
│ 42    │ Int64          │
│ 43    │ Int64          │
│ 42.42 │ String         │
│ true  │ Bool           │
└───────┴────────────────┘
```

如果 `K < N`，则具有最少见类型的值会被插入到一个特殊的子列中，但仍可访问：

```text theme={null}
CREATE TABLE test (d Dynamic(max_types=4)) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), (43), ('42.42'), (true), ([1, 2, 3]);
SELECT d, dynamicType(d), d::Dynamic(max_types=2) as d2, dynamicType(d2), isDynamicElementInSharedData(d2) FROM test;
```

```text theme={null}
┌─d───────┬─dynamicType(d)─┬─d2──────┬─dynamicType(d2)─┬─isDynamicElementInSharedData(d2)─┐
│ ᴺᵁᴸᴸ    │ None           │ ᴺᵁᴸᴸ    │ None            │ false                            │
│ 42      │ Int64          │ 42      │ Int64           │ false                            │
│ 43      │ Int64          │ 43      │ Int64           │ false                            │
│ 42.42   │ String         │ 42.42   │ String          │ false                            │
│ true    │ Bool           │ true    │ Bool            │ true                             │
│ [1,2,3] │ Array(Int64)   │ [1,2,3] │ Array(Int64)    │ true                             │
└─────────┴────────────────┴─────────┴─────────────────┴──────────────────────────────────┘
```

函数 `isDynamicElementInSharedData` 会对存储在 `Dynamic` 内部特殊共享数据结构中的行返回 `true`。正如我们所见，结果列中只包含 2 种未存储在共享数据结构中的类型。

如果 `K=0`，所有类型都会被插入到单个特殊子列中：

```text theme={null}
CREATE TABLE test (d Dynamic(max_types=4)) ENGINE = Memory;
INSERT INTO test VALUES (NULL), (42), (43), ('42.42'), (true), ([1, 2, 3]);
SELECT d, dynamicType(d), d::Dynamic(max_types=0) as d2, dynamicType(d2), isDynamicElementInSharedData(d2) FROM test;
```

```text theme={null}
┌─d───────┬─dynamicType(d)─┬─d2──────┬─dynamicType(d2)─┬─isDynamicElementInSharedData(d2)─┐
│ ᴺᵁᴸᴸ    │ None           │ ᴺᵁᴸᴸ    │ None            │ false                            │
│ 42      │ Int64          │ 42      │ Int64           │ true                             │
│ 43      │ Int64          │ 43      │ Int64           │ true                             │
│ 42.42   │ String         │ 42.42   │ String          │ true                             │
│ true    │ Bool           │ true    │ Bool            │ true                             │
│ [1,2,3] │ Array(Int64)   │ [1,2,3] │ Array(Int64)    │ true                             │
└─────────┴────────────────┴─────────┴─────────────────┴──────────────────────────────────┘
```

<div id="reading-dynamic-type-from-the-data">
  ## 从数据中读取 Dynamic 类型
</div>

所有文本格式 (TSV、CSV、CustomSeparated、Values、JSONEachRow 等) 都支持读取 `Dynamic` 类型。解析数据时，ClickHouse 会尝试推断每个值的类型，并在插入到 `Dynamic` 列时使用该类型。

示例：

```sql theme={null}
SELECT
    d,
    dynamicType(d),
    dynamicElement(d, 'String') AS str,
    dynamicElement(d, 'Int64') AS num,
    dynamicElement(d, 'Float64') AS float,
    dynamicElement(d, 'Date') AS date,
    dynamicElement(d, 'Array(Int64)') AS arr
FROM format(JSONEachRow, 'd Dynamic', $$
{"d" : "Hello, World!"},
{"d" : 42},
{"d" : 42.42},
{"d" : "2020-01-01"},
{"d" : [1, 2, 3]}
$$)
```

```text theme={null}
┌─d─────────────┬─dynamicType(d)─┬─str───────────┬──num─┬─float─┬───────date─┬─arr─────┐
│ Hello, World! │ String         │ Hello, World! │ ᴺᵁᴸᴸ │  ᴺᵁᴸᴸ │       ᴺᵁᴸᴸ │ []      │
│ 42            │ Int64          │ ᴺᵁᴸᴸ          │   42 │  ᴺᵁᴸᴸ │       ᴺᵁᴸᴸ │ []      │
│ 42.42         │ Float64        │ ᴺᵁᴸᴸ          │ ᴺᵁᴸᴸ │ 42.42 │       ᴺᵁᴸᴸ │ []      │
│ 2020-01-01    │ Date           │ ᴺᵁᴸᴸ          │ ᴺᵁᴸᴸ │  ᴺᵁᴸᴸ │ 2020-01-01 │ []      │
│ [1,2,3]       │ Array(Int64)   │ ᴺᵁᴸᴸ          │ ᴺᵁᴸᴸ │  ᴺᵁᴸᴸ │       ᴺᵁᴸᴸ │ [1,2,3] │
└───────────────┴────────────────┴───────────────┴──────┴───────┴────────────┴─────────┘
```

<div id="using-dynamic-type-in-functions">
  ## 在函数中使用 Dynamic 类型
</div>

大多数函数支持 `Dynamic` 类型的参数。在这种情况下，函数会针对 `Dynamic` 列中存储的每种内部数据类型分别执行。
当函数的结果类型取决于参数类型时，以 `Dynamic` 参数执行该函数所得的结果类型也将为 `Dynamic`。当函数的结果类型与参数类型无关时，结果类型将为 `Nullable(T)`，其中 `T` 为该函数通常的返回类型。

示例：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE=Memory;
INSERT INTO test VALUES (NULL), (1::Int8), (2::Int16), (3::Int32), (4::Int64);
```

```sql theme={null}
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ │ None           │
│ 1    │ Int8           │
│ 2    │ Int16          │
│ 3    │ Int32          │
│ 4    │ Int64          │
└──────┴────────────────┘
```

```sql theme={null}
SELECT d, d + 1 AS res, toTypeName(res), dynamicType(res) FROM test;
```

```text theme={null}
┌─d────┬─res──┬─toTypeName(res)─┬─dynamicType(res)─┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ Dynamic         │ None             │
│ 1    │ 2    │ Dynamic         │ Int16            │
│ 2    │ 3    │ Dynamic         │ Int32            │
│ 3    │ 4    │ Dynamic         │ Int64            │
│ 4    │ 5    │ Dynamic         │ Int64            │
└──────┴──────┴─────────────────┴──────────────────┘
```

```sql theme={null}
SELECT d, d + d AS res, toTypeName(res), dynamicType(res) FROM test;
```

```text theme={null}
┌─d────┬─res──┬─toTypeName(res)─┬─dynamicType(res)─┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ Dynamic         │ None             │
│ 1    │ 2    │ Dynamic         │ Int16            │
│ 2    │ 4    │ Dynamic         │ Int32            │
│ 3    │ 6    │ Dynamic         │ Int64            │
│ 4    │ 8    │ Dynamic         │ Int64            │
└──────┴──────┴─────────────────┴──────────────────┘
```

```sql theme={null}
SELECT d, d < 3 AS res, toTypeName(res) FROM test;
```

```text theme={null}
┌─d────┬──res─┬─toTypeName(res)─┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ Nullable(UInt8) │
│ 1    │    1 │ Nullable(UInt8) │
│ 2    │    1 │ Nullable(UInt8) │
│ 3    │    0 │ Nullable(UInt8) │
│ 4    │    0 │ Nullable(UInt8) │
└──────┴──────┴─────────────────┘
```

```sql theme={null}
SELECT d, exp2(d) AS res, toTypeName(res) FROM test;
```

```sql theme={null}
┌─d────┬──res─┬─toTypeName(res)───┐
│ ᴺᵁᴸᴸ │ ᴺᵁᴸᴸ │ Nullable(Float64) │
│ 1    │    2 │ Nullable(Float64) │
│ 2    │    4 │ Nullable(Float64) │
│ 3    │    8 │ Nullable(Float64) │
│ 4    │   16 │ Nullable(Float64) │
└──────┴──────┴───────────────────┘
```

```sql theme={null}
TRUNCATE TABLE test;
INSERT INTO test VALUES (NULL), ('str_1'), ('str_2');
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d─────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ  │ None           │
│ str_1 │ String         │
│ str_2 │ String         │
└───────┴────────────────┘
```

```sql theme={null}
SELECT d, upper(d) AS res, toTypeName(res) FROM test;
```

```text theme={null}
┌─d─────┬─res───┬─toTypeName(res)──┐
│ ᴺᵁᴸᴸ  │ ᴺᵁᴸᴸ  │ Nullable(String) │
│ str_1 │ STR_1 │ Nullable(String) │
│ str_2 │ STR_2 │ Nullable(String) │
└───────┴───────┴──────────────────┘
```

```sql theme={null}
SELECT d, extract(d, '([0-3])') AS res, toTypeName(res) FROM test;
```

```text theme={null}
┌─d─────┬─res──┬─toTypeName(res)──┐
│ ᴺᵁᴸᴸ  │ ᴺᵁᴸᴸ │ Nullable(String) │
│ str_1 │ 1    │ Nullable(String) │
│ str_2 │ 2    │ Nullable(String) │
└───────┴──────┴──────────────────┘
```

```sql theme={null}
TRUNCATE TABLE test;
INSERT INTO test VALUES (NULL), ([1, 2]), ([3, 4]);
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d─────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ  │ None           │
│ [1,2] │ Array(Int64)   │
│ [3,4] │ Array(Int64)   │
└───────┴────────────────┘
```

```sql theme={null}
SELECT d, d[1] AS res, toTypeName(res), dynamicType(res) FROM test;
```

```text theme={null}
┌─d─────┬─res──┬─toTypeName(res)─┬─dynamicType(res)─┐
│ ᴺᵁᴸᴸ  │ ᴺᵁᴸᴸ │ Dynamic         │ None             │
│ [1,2] │ 1    │ Dynamic         │ Int64            │
│ [3,4] │ 3    │ Dynamic         │ Int64            │
└───────┴──────┴─────────────────┴──────────────────┘
```

如果函数无法在 `Dynamic` 列中的某些类型上执行，则会抛出异常：

```sql theme={null}
INSERT INTO test VALUES (42), (43), ('str_1');
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d─────┬─dynamicType(d)─┐
│ 42    │ Int64          │
│ 43    │ Int64          │
│ str_1 │ String         │
└───────┴────────────────┘
┌─d─────┬─dynamicType(d)─┐
│ ᴺᵁᴸᴸ  │ None           │
│ [1,2] │ Array(Int64)   │
│ [3,4] │ Array(Int64)   │
└───────┴────────────────┘
```

```sql theme={null}
SELECT d, d + 1 AS res, toTypeName(res), dynamicType(d) FROM test;
```

```text theme={null}
收到异常：
Code: 43. DB::Exception: Illegal types Array(Int64) and UInt8 of arguments of function plus: while executing 'FUNCTION plus(__table1.d : 3, 1_UInt8 :: 1) -> plus(__table1.d, 1_UInt8) Dynamic : 0'. (ILLEGAL_TYPE_OF_ARGUMENT)
```

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

```sql theme={null}
SELECT d, d + 1 AS res, toTypeName(res), dynamicType(res) FROM test WHERE dynamicType(d) NOT IN ('String', 'Array(Int64)', 'None')
```

```text theme={null}
┌─d──┬─res─┬─toTypeName(res)─┬─dynamicType(res)─┐
│ 42 │ 43  │ Dynamic         │ Int64            │
│ 43 │ 44  │ Dynamic         │ Int64            │
└────┴─────┴─────────────────┴──────────────────┘
```

或者将所需类型提取为子列：

```sql theme={null}
SELECT d, d.Int64 + 1 AS res, toTypeName(res) FROM test;
```

```text theme={null}
┌─d─────┬──res─┬─toTypeName(res)─┐
│ 42    │   43 │ Nullable(Int64) │
│ 43    │   44 │ Nullable(Int64) │
│ str_1 │ ᴺᵁᴸᴸ │ Nullable(Int64) │
└───────┴──────┴─────────────────┘
┌─d─────┬──res─┬─toTypeName(res)─┐
│ ᴺᵁᴸᴸ  │ ᴺᵁᴸᴸ │ Nullable(Int64) │
│ [1,2] │ ᴺᵁᴸᴸ │ Nullable(Int64) │
│ [3,4] │ ᴺᵁᴸᴸ │ Nullable(Int64) │
└───────┴──────┴─────────────────┘
```

<div id="dynamic-type-mismatch-behavior">
  ### 类型不匹配时的行为
</div>

设置 `dynamic_throw_on_type_mismatch` 用于控制：当某个函数应用于 `Dynamic` 列，而某一行实际存储的类型与该函数不兼容时，会如何处理：

* `true` (默认) — 在遇到第一行不兼容的行时抛出异常 (`ILLEGAL_TYPE_OF_ARGUMENT`) 。
* `false` — 对不兼容的行返回 `NULL`，同时保留兼容行的结果。

**示例：**

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE = Memory;
INSERT INTO test VALUES ('world'), (123), (456);

-- 默认行为（类型不匹配时抛出异常）：length() 不接受整数类型，因此查询将抛出异常。
SELECT length(d) FROM test;  -- 抛出 ILLEGAL_TYPE_OF_ARGUMENT

-- 禁用异常抛出后：不兼容的行返回 NULL。
SET dynamic_throw_on_type_mismatch = false;
SELECT d, length(d) FROM test ORDER BY d::String NULLS LAST;
```

```text theme={null}
┌─d─────┬─length(d)─┐
│ world │         5 │
│ 123   │      ᴺᵁᴸᴸ │
│ 456   │      ᴺᵁᴸᴸ │
└───────┴───────────┘
```

<div id="using-dynamic-type-in-order-by-and-group-by">
  ## 在 ORDER BY 和 GROUP BY 中使用 Dynamic 类型
</div>

在 `ORDER BY` 和 `GROUP BY` 中，`Dynamic` 类型的值会以与 `Variant` 类型值类似的方式进行比较：
对于 `Dynamic` 类型中底层类型为 `T1` 的值 `d1` 和底层类型为 `T2` 的值 `d2`，运算符 `<` 的结果定义如下：

* 如果 `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` 设置。

示例：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE=Memory;
INSERT INTO test VALUES (42), (43), ('abc'), ('abd'), ([1, 2, 3]), ([]), (NULL);
```

```sql theme={null}
SELECT d, dynamicType(d) FROM test;
```

```text theme={null}
┌─d───────┬─dynamicType(d)─┐
│ 42      │ Int64          │
│ 43      │ Int64          │
│ abc     │ String         │
│ abd     │ String         │
│ [1,2,3] │ Array(Int64)   │
│ []      │ Array(Int64)   │
│ ᴺᵁᴸᴸ    │ None           │
└─────────┴────────────────┘
```

```sql theme={null}
SELECT d, dynamicType(d) FROM test ORDER BY d SETTINGS allow_suspicious_types_in_order_by=1;
```

```sql theme={null}
┌─d───────┬─dynamicType(d)─┐
│ []      │ Array(Int64)   │
│ [1,2,3] │ Array(Int64)   │
│ 42      │ Int64          │
│ 43      │ Int64          │
│ abc     │ String         │
│ abd     │ String         │
│ ᴺᵁᴸᴸ    │ None           │
└─────────┴────────────────┘
```

**注意：** 对于底层数值类型不同的 Dynamic 类型值，系统会将其视为不同的值，不会相互比较，而是比较其类型名称。

示例：

```sql theme={null}
CREATE TABLE test (d Dynamic) ENGINE=Memory;
INSERT INTO test VALUES (1::UInt32), (1::Int64), (100::UInt32), (100::Int64);
SELECT d, dynamicType(d) FROM test ORDER BY d SETTINGS allow_suspicious_types_in_order_by=1;
```

```text theme={null}
┌─v───┬─dynamicType(v)─┐
│ 1   │ Int64          │
│ 100 │ Int64          │
│ 1   │ UInt32         │
│ 100 │ UInt32         │
└─────┴────────────────┘
```

```sql theme={null}
SELECT d, dynamicType(d) FROM test GROUP BY d SETTINGS allow_suspicious_types_in_group_by=1;
```

```text theme={null}
┌─d───┬─dynamicType(d)─┐
│ 1   │ Int64          │
│ 100 │ UInt32         │
│ 1   │ UInt32         │
│ 100 │ Int64          │
└─────┴────────────────┘
```

**注意：** 由于 `Dynamic` 类型的函数有其[特殊处理机制](#using-dynamic-type-in-functions)，上述比较规则不会应用于执行 `<`/`>`/`=` 等比较函数时

<div id="reaching-the-limit-in-number-of-different-data-types-stored-inside-dynamic">
  ## Dynamic 中存储的不同数据类型数量达到上限
</div>

`Dynamic` 数据类型只能将有限数量的不同数据类型作为独立子列存储。默认情况下，这个上限为 32，但你可以在类型声明中使用语法 `Dynamic(max_types=N)` 进行修改，其中 N 的取值范围为 0 到 254 (由于实现细节的限制，`Dynamic` 中作为独立子列存储的不同数据类型不可能超过 254 种) 。
当达到该上限时，所有新插入到 `Dynamic` 列中的数据类型都会被写入一个共享数据结构中，该结构以二进制形式存储不同数据类型的值。

下面来看一下，在不同场景下达到该上限时会发生什么。

<div id="reaching-the-limit-during-data-parsing">
  ### 在数据解析过程中达到限制
</div>

在从数据中解析 `Dynamic` 值时，如果当前数据块已达到限制，所有新值都将插入共享数据结构中：

```sql theme={null}
SELECT d, dynamicType(d), isDynamicElementInSharedData(d) FROM format(JSONEachRow, 'd Dynamic(max_types=3)', '
{"d" : 42}
{"d" : [1, 2, 3]}
{"d" : "Hello, World!"}
{"d" : "2020-01-01"}
{"d" : ["str1", "str2", "str3"]}
{"d" : {"a" : 1, "b" : [1, 2, 3]}}
')
```

```text theme={null}
┌─d──────────────────────┬─dynamicType(d)─────────────────┬─isDynamicElementInSharedData(d)─┐
│ 42                     │ Int64                          │ false                           │
│ [1,2,3]                │ Array(Int64)                   │ false                           │
│ Hello, World!          │ String                         │ false                           │
│ 2020-01-01             │ Date                           │ true                            │
│ ['str1','str2','str3'] │ Array(String)                  │ true                            │
│ (1,[1,2,3])            │ Tuple(a Int64, b Array(Int64)) │ true                            │
└────────────────────────┴────────────────────────────────┴─────────────────────────────────┘
```

正如我们所见，在插入 3 种不同的数据类型 `Int64`、`Array(Int64)` 和 `String` 后，所有新类型都会被插入到特殊的共享数据结构中。

<div id="during-merges-of-data-parts-in-mergetree-table-engines">
  ### 在 MergeTree 表引擎中合并数据分区片段时
</div>

在 MergeTree 表中合并多个数据分区片段时，结果数据分区片段中的 `Dynamic` 列可能会达到其内部可作为独立子列存储的不同数据类型数量上限，因此无法将源数据分区片段中的所有类型都作为子列存储。
在这种情况下，ClickHouse 会决定哪些类型在合并后继续保留为独立子列，哪些类型会被写入共享数据结构。大多数情况下，ClickHouse 会尽量保留最常见的类型，并将最少见的类型存入共享数据结构，但具体仍取决于实现方式。

下面来看一个这样的合并示例。首先，创建一个带有 `Dynamic` 列的表，将不同数据类型的数量上限设置为 `3`，然后插入包含 `5` 种不同类型的值：

```sql theme={null}
CREATE TABLE test (id UInt64, d Dynamic(max_types=3)) ENGINE=MergeTree ORDER BY id;
SYSTEM STOP MERGES test;
INSERT INTO test SELECT number, number FROM numbers(5);
INSERT INTO test SELECT number, range(number) FROM numbers(4);
INSERT INTO test SELECT number, toDate(number) FROM numbers(3);
INSERT INTO test SELECT number, map(number, number) FROM numbers(2);
INSERT INTO test SELECT number, 'str_' || toString(number) FROM numbers(1);
```

每次插入都会创建一个单独的数据 part，其中 `Dynamic` 列仅包含一种类型：

```sql theme={null}
SELECT count(), dynamicType(d), isDynamicElementInSharedData(d), _part FROM test GROUP BY _part, dynamicType(d), isDynamicElementInSharedData(d) ORDER BY _part, count();
```

```text theme={null}
┌─count()─┬─dynamicType(d)──────┬─isDynamicElementInSharedData(d)─┬─_part─────┐
│       5 │ UInt64              │ false                           │ all_1_1_0 │
│       4 │ Array(UInt64)       │ false                           │ all_2_2_0 │
│       3 │ Date                │ false                           │ all_3_3_0 │
│       2 │ Map(UInt64, UInt64) │ false                           │ all_4_4_0 │
│       1 │ String              │ false                           │ all_5_5_0 │
└─────────┴─────────────────────┴─────────────────────────────────┴───────────┘
```

现在，让我们将所有 parts 合并成一个，看看会发生什么：

```sql theme={null}
SYSTEM START MERGES test;
OPTIMIZE TABLE test FINAL;
SELECT count(), dynamicType(d), isDynamicElementInSharedData(d), _part FROM test GROUP BY _part, dynamicType(d), isDynamicElementInSharedData(d) ORDER BY _part, count() desc;
```

```text theme={null}
┌─count()─┬─dynamicType(d)──────┬─isDynamicElementInSharedData(d)─┬─_part─────┐
│       5 │ UInt64              │ false                           │ all_1_5_2 │
│       4 │ Array(UInt64)       │ false                           │ all_1_5_2 │
│       3 │ Date                │ false                           │ all_1_5_2 │
│       2 │ Map(UInt64, UInt64) │ true                            │ all_1_5_2 │
│       1 │ String              │ true                            │ all_1_5_2 │
└─────────┴─────────────────────┴─────────────────────────────────┴───────────┘
```

正如我们所见，ClickHouse 将最常见的类型 `UInt64` 和 `Array(UInt64)` 保留为子列，而将所有其他类型都写入共享数据中。

<div id="jsonextract-functions-with-dynamic">
  ## 支持 `Dynamic` 的 JSONExtract 函数
</div>

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

```sql theme={null}
SELECT JSONExtract('{"a" : [1, 2, 3]}', 'a', 'Dynamic') AS dynamic, dynamicType(dynamic) AS dynamic_type;
```

```text theme={null}
┌─dynamic─┬─dynamic_type───────────┐
│ [1,2,3] │ Array(Nullable(Int64)) │
└─────────┴────────────────────────┘
```

```sql theme={null}
SELECT JSONExtract('{"obj" : {"a" : 42, "b" : "Hello", "c" : [1,2,3]}}', 'obj', 'Map(String, Dynamic)') AS map_of_dynamics, mapApply((k, v) -> (k, dynamicType(v)), map_of_dynamics) AS map_of_dynamic_types
```

```text theme={null}
┌─map_of_dynamics──────────────────┬─map_of_dynamic_types────────────────────────────────────┐
│ {'a':42,'b':'Hello','c':[1,2,3]} │ {'a':'Int64','b':'String','c':'Array(Nullable(Int64))'} │
└──────────────────────────────────┴─────────────────────────────────────────────────────────┘
```

````sql theme={null}
SELECT JSONExtractKeysAndValues('{"a" : 42, "b" : "Hello", "c" : [1,2,3]}', 'Dynamic') AS dynamics, arrayMap(x -> (x.1, dynamicType(x.2)), dynamics) AS dynamic_types```
````

```text theme={null}
┌─dynamics───────────────────────────────┬─dynamic_types─────────────────────────────────────────────────┐
│ [('a',42),('b','Hello'),('c',[1,2,3])] │ [('a','Int64'),('b','String'),('c','Array(Nullable(Int64))')] │
└────────────────────────────────────────┴───────────────────────────────────────────────────────────────┘
```

<div id="binary-output-format">
  ### 二进制输出格式
</div>

在 RowBinary 格式中，`Dynamic` 类型的值按以下格式进行序列化：

```text theme={null}
<binary_encoded_data_type><value_in_binary_format_according_to_the_data_type>
```
