> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.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 테이블의 단일 데이터 파트(data part)) 전체에서 `Dynamic` 타입 컬럼 안에 서로 다른 데이터 타입을 별도의 서브컬럼으로 몇 개까지 저장할 수 있는지를 나타냅니다. 이 한도를 초과하면 새 타입의 모든 값은 특수한 공유 데이터 구조(shared data structure)에 바이너리 형식으로 함께 저장됩니다. `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)` 함수를 사용할 수 있습니다. 이 함수는 각 행에 대해 값의 type name이 담긴 `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}
Received exception:
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` type 함수의 [특수한 동작](#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);
```

각 삽입 작업마다 단일 유형만 포함하는 `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 │
└─────────┴─────────────────────┴─────────────────────────────────┴───────────┘
```

이제 모든 파트를 하나로 머지한 뒤 어떤 일이 일어나는지 살펴보겠습니다:

```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 format에서 `Dynamic` 타입 값은 다음과 같은 형식으로 직렬화됩니다:

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