Dynamic 型のカラムを宣言するには、次の構文を使用します。
N は 0 から 254 までの省略可能なパラメーターで、個別に格納される単一のデータブロック内 (たとえば MergeTree テーブルの単一のデータパーツ内) において、Dynamic 型のカラムの中に個別のサブカラムとして格納できる異なるデータ型の数を示します。この上限を超えると、新しい型を持つすべての値は、特別な共有データ構造にバイナリ形式でまとめて格納されます。max_types のデフォルト値は 32 です。
Dynamic の作成
Dynamic 型を使用する場合:
Variant カラムで CAST を使用する場合:
Dynamic のネストされた型をサブカラムとして読み取る
Dynamic 型では、型名をサブカラムとして使うことで、Dynamic カラムから単一のネストされた型を読み取れます。
つまり、d Dynamic というカラムがある場合、構文 d.T を使って任意の有効な型 T のサブカラムを読み取れます。
このサブカラムの型は、T を Nullable の内側に含められる場合は Nullable(T)、それ以外の場合は T になります。このサブカラムは
元の Dynamic カラムと同じサイズになり、元の Dynamic カラムが型 T ではないすべての行では、NULL 値 (または T を Nullable の内側に含められない場合は空の値) が入ります。
Dynamic のサブカラムは、関数 dynamicElement(dynamic_column, type_name) を使って読み取ることもできます。
例:
dynamicType(dynamic_column) を使用できます。この関数は、各行の値の型名を表す String を返します (行が NULL の場合は 'None' を返します) 。
例:
Dynamic カラムと他のカラム間の変換
Dynamic カラムでは、4 種類の変換を行えます。
通常のカラムを Dynamic カラムに変換する
パースを通じて String カラムを 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 の場合、最も出現頻度の低い型の値は 1 つの特別なサブカラムに挿入されますが、引き続きアクセスできます:
isDynamicElementInSharedData は、Dynamic 内の特別な共有データ構造に格納されている行に対して true を返します。ご覧のとおり、結果のカラムには、共有データ構造に格納されていない型が 2 つだけ含まれています。
K=0 の場合、すべての型が 1 つの特別なサブカラムに挿入されます:
データから Dynamic 型を読み取る
Dynamic 型を読み取れます。データのパース時に、ClickHouse は各値の型を推論し、Dynamic カラムへの挿入時にその型を使用します。
例:
関数でのDynamic型の使用
Dynamic 型の引数をサポートしています。この場合、関数は Dynamic カラム内に格納された各内部データ型に対して個別に実行されます。
関数の結果型が引数の型に依存する場合、Dynamic 引数で実行した関数の結果は Dynamic になります。関数の結果型が引数の型に依存しない場合、結果は Nullable(T) (T はその関数の通常の結果型) になります。
例:
Dynamic カラム内の一部の型に対して関数を実行できない場合、例外が発生します:
型の不一致時の動作
dynamic_throw_on_type_mismatch は、関数を Dynamic カラムに適用した際、各行に実際に格納されている型がその関数に対応していない場合の挙動を制御します。
true(デフォルト) — 最初に見つかった非互換の行で例外 (ILLEGAL_TYPE_OF_ARGUMENT) をスローします。false— 非互換の行にはNULLを返し、互換性のある行については結果をそのまま返します。
ORDER BY と GROUP BY で Dynamic 型を使用する
ORDER BY および GROUP BY では、Dynamic 型の値は Variant 型の値と同様に比較されます。
基になる型が T1 の値 d1 と、基になる型が T2 の値 d2 について、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 内に格納される異なるデータ型数の上限に達した場合
Dynamic データ型で、異なるデータ型を個別のサブカラムとして格納できる数には上限があります。デフォルトの上限は 32 ですが、型宣言で Dynamic(max_types=N) という構文を使用して変更できます。ここで N には 0 から 254 までの値を指定できます (実装の詳細上、Dynamic 内で個別のサブカラムとして格納できる異なるデータ型は 254 種類までです) 。
この上限に達すると、Dynamic カラムに新たに挿入されるすべてのデータ型は、異なるデータ型の値をバイナリ形式で格納する 1 つの共有データ構造に挿入されます。
では、さまざまなシナリオでこの上限に達したときに何が起こるのかを見ていきましょう。
データのパース中に上限に達した場合
Dynamic値をパースする際、現在のデータブロックで上限に達した場合、新しい値はすべて共有データ構造に挿入されます:
Int64、Array(Int64)、String を挿入すると、これらの新しい型はすべて特別な共有データ構造に格納されました。
MergeTree テーブルエンジンにおけるデータパーツのマージ中
Dynamic カラムが、内部で個別のサブカラムに格納できる異なるデータ型の上限に達し、元のパーツに含まれるすべての型をサブカラムとして保持できなくなることがあります。
この場合、ClickHouse は、マージ後にどの型を個別のサブカラムとして残し、どの型を共有データ構造に格納するかを選択します。多くの場合、ClickHouse は出現頻度の高い型を優先して残し、まれな型を共有データ構造に格納しようとしますが、これは実装に依存します。
このようなマージの例を見てみましょう。まず、Dynamic カラムを持つテーブルを作成し、異なるデータ型の上限を 3 に設定したうえで、5 種類の異なる型の値を挿入します:
Dynamic カラムを持つ別個のデータパーツが作成されます:
UInt64 と Array(UInt64) をサブカラムとして保持し、それ以外の型はすべて共有データに格納しました。
Dynamic 型での JSONExtract 関数
JSONExtract* 関数は Dynamic 型をサポートしています。
バイナリ出力フォーマット
Dynamic 型の値は次の形式でシリアライズされます。