動的
このタイプは、すべての値のタイプを事前に知らなくても、任意のタイプの値をその中に格納することを可能にします。
Dynamic
タイプのカラムを宣言するには、以下の構文を使用します:
ここで N
は、Dynamic
タイプのカラム内に別々のサブカラムとして格納できる異なるデータタイプの数を示すオプションのパラメータで、 0
から 254
の範囲内です。この制限を超えた場合、新しいタイプのすべての値は、バイナリ形式で特別な共有データ構造に一緒に格納されます。デフォルト値の max_types
は 32
です。
Dynamic データタイプはベータ機能です。使用するには、enable_dynamic_type = 1
を設定してください。
Dynamic の作成
テーブルのカラム定義で Dynamic
タイプを使用するには:
通常のカラムからのキャストを使用する:
Variant
カラムからのキャストを使用する:
Dynamic ネストされたタイプをサブカラムとして読み取る
Dynamic
タイプは、型名をサブカラムとして使用して、Dynamic
カラムから単一のネストされたタイプを読み取ることをサポートしています。
したがって、カラム d Dynamic
がある場合、任意の有効なタイプ T
のサブカラムを d.T
構文を使用して読み取ることができます。このサブカラムは、T
が Nullable
内に存在できる場合は Nullable(T)
型となり、そうでなければ T
型となります。このサブカラムは、元の Dynamic
カラムと同じサイズで、元の Dynamic
カラムがタイプ T
を持たないすべての行には NULL
値(または T
が Nullable
内に存在できない場合は空の値)が含まれます。
Dynamic
サブカラムは dynamicElement(dynamic_column, type_name)
関数を使用して読み取ることもできます。
例:
各行に格納されているバリアントを知るためには、dynamicType(dynamic_column)
関数を使用できます。各行の値タイプ名の文字列を返します(または行が NULL
の場合は 'None'
を返します)。
例:
Dynamic カラムと他のカラム間の変換
Dynamic
カラムに対して実行できる変換は4つあります。
通常のカラムを Dynamic カラムに変換する
文字列カラムを Dynamic カラムに変換するための解析
String
カラムから Dynamic
タイプの値を解析するには、cast_string_to_dynamic_use_inference
を設定できます:
Dynamic カラムを通常のカラムに変換する
Dynamic
カラムを通常のカラムに変換することが可能です。この場合、すべてのネストされたタイプは、目的のタイプに変換されます:
Variant カラムを Dynamic カラムに変換する
Dynamic(max_types=N) カラムを他の Dynamic(max_types=K) に変換する
K >= N
の場合、変換中にデータは変わりません:
K < N
の場合、最も珍しいタイプの値は単一の特別なサブカラムに挿入されますが、引き続きアクセスは可能です:
isDynamicElementInSharedData
関数は、Dynamic
内の特別な共有データ構造に格納されている行に対して true
を返します。結果のカラムには、共有データ構造に格納されていない2種類のタイプのみが含まれています。
K=0
の場合、すべてのタイプは単一の特別なサブカラムに挿入されます:
データから Dynamic タイプを読み取る
すべてのテキスト形式(TSV、CSV、CustomSeparated、Values、JSONEachRow など)は、Dynamic
タイプの読み取りをサポートしています。データ解析中に、ClickHouse は各値のタイプを推測して、それを Dynamic
カラムへの挿入に使用しようとします。
例:
Dynamic タイプを関数で使用する
ほとんどの関数は Dynamic
タイプの引数をサポートします。この場合、関数は、Dynamic
カラムに格納された内部データタイプそれぞれに対して別々に実行されます。
関数の結果タイプが引数タイプに依存する場合、そのような関数の Dynamic
引数を使用して実行された結果は Dynamic
になります。結果のタイプが引数のタイプに依存しない場合、結果は Nullable(T)
になります、ここで T
はこの関数の通常の結果タイプです。
例:
関数が Dynamic
カラム内のいくつかのタイプに対して実行できない場合、例外がスローされます:
不要なタイプをフィルタリングすることができます:
あるいは、必要なタイプをサブカラムとして抽出します:
Dynamic タイプを使用した ORDER BY や GROUP BY
ORDER BY
および GROUP BY
において、Dynamic
タイプの値は Variant
タイプの値と同様に比較されます:
値 d1
の基になるタイプが T1
で、値 d2
の基になるタイプが T2
の Dynamic
の間の <
演算子の結果は次のように定義されます:
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
の間です(実装の詳細により、Dynamic
内に異なるデータ型が254を超えて保存されることは不可能です)。
制限に達した場合、新しく挿入されたすべてのデータ型は、バイナリ形式で異なるデータ型の値を格納する単一の共有データ構造に挿入されます。
制限に達したときに異なるシナリオで何が起こるかを見てみましょう。
データ解析中に制限に達する
データから Dynamic
値を解析中に、現在のデータブロックの制限が達された場合、新しいすべての値は共有データ構造に挿入されます:
3つの異なるデータタイプ Int64
、Array(Int64)
、String
を挿入した後、他のすべての新しいタイプは特別な共有データ構造に挿入されたことがわかります。
MergeTree テーブルエンジンのデータパーツのマージ中に
MergeTree テーブルでいくつかのデータパーツをマージする際、結果のデータパート内の Dynamic
カラムは、異なるサブカラムとして格納できる異なるデータタイプの制限に達する可能性があります。これは、ソースパーツからのすべてのタイプをサブカラムとして保存できません。
この場合、ClickHouse は、マージ後にどのタイプがサブカラムとして残るか、そしてどのタイプが共有データ構造に挿入されるかを決定します。ほとんどの場合、ClickHouse は最も一般的なタイプを保持し、珍しいタイプを共有データ構造に保存しようとしますが、これは実装によって異なります。
そのようなマージの例を見てみましょう。まず、Dynamic
カラムを持つテーブルを作成し、異なるデータタイプの制限を 3
に設定して、5
つの異なるタイプの値を挿入します:
各挿入は、単一のタイプを含む Dynamic
カラムを持つ別のデータパートを作成します:
次に、すべてのパートを1つにマージし、何が起こるか見てみましょう:
ClickHouse が最も一般的なタイプ UInt64
と Array(UInt64)
をサブカラムとして保持し、他のすべてのタイプを共有データ構造に挿入したことがわかります。
Dynamic を用いた JSONExtract 関数
すべての JSONExtract*
関数は Dynamic
タイプをサポートします:
バイナリ出力形式
RowBinary 形式では、Dynamic
タイプの値は次の形式でシリアライズされます: