Skip to main content

説明

RowBinary フォーマットは、バイナリ形式のデータを行単位で解析します。 行と値は、区切り文字なしで連続して並びます。 データがバイナリ形式であるため、FORMAT RowBinary の後の区切り文字は以下のとおり厳密に定められています。
  • 任意の数の空白文字:
    • ' ' (スペース - コード 0x20)
    • '\t' (タブ - コード 0x09)
    • '\f' (フォームフィード - コード 0x0C)
  • その後に、ちょうど 1 つの改行シーケンス:
    • Windows 形式 "\r\n"
    • または Unix 形式 '\n'
  • その直後にバイナリデータ。
このフォーマットは行ベースであるため、Native フォーマットよりも効率が低くなります。

データ型のワイヤ形式

例で示しているクエリの多くは、出力をファイルに保存して curl で実行できます。
その後、データはヘックスエディタで確認できます。

符号なし LEB128 (リトルエンディアン Base 128)

StringArrayMap などの可変サイズのデータ型の長さを符号化するために使われる、符号なしリトルエンディアンの可変長整数エンコーディングです。実装例は LEB128 の Wikipedia ページで確認できます。

(U)Int8, (U)Int16, (U)Int32, (U)Int64, (U)Int128, (U)Int256

すべての整数型は、適切なバイト数で リトルエンディアン 形式にエンコードされます。符号付き型 (Int8 から Int256) では、2 の補数 表現が使われます。ほとんどの言語では、組み込み機能または広く使われているライブラリを利用して、このような整数をバイト配列から取り出せます。大半の言語のネイティブな整数サイズを超える Int128/Int256 および UInt128/UInt256 については、独自にデシリアライズする必要がある場合があります。

Bool

ブール値は1バイトでエンコードされ、UInt8と同様にデシリアライズできます。
  • 0false
  • 1true

Float32, Float64

リトルエンディアン形式の浮動小数点数で、Float32 は4バイト、Float64 は8バイトでエンコードされます。整数と同様、ほとんどの言語にはこれらの値を適切にデシリアライズするための手段が用意されています。

BFloat16

BFloat16 (Brain Floating Point) は、Float32 と同じ範囲を持ちながら精度を抑えた 16 ビット浮動小数点フォーマットで、機械学習のワークロードに適しています。ワイヤ形式は、基本的に Float32 値の上位 16 ビットです。使用している言語がこれをネイティブでサポートしていない場合は、UInt16 として読み書きし、Float32 との間で相互変換するのが最も簡単です。 BFloat16 を Float32 に変換するには (疑似コード) :
Float32 を BFloat16 に変換するには (擬似コード) :
BFloat16 の内部値の例:

Decimal32, Decimal64, Decimal128, Decimal256

Decimal 型は、それぞれのビット幅に対応するリトルエンディアンの整数として表現されます。
  • Decimal32 - 4 バイト、つまり Int32
  • Decimal64 - 8 バイト、つまり Int64
  • Decimal128 - 16 バイト、つまり Int128
  • Decimal256 - 32 バイト、つまり Int256
Decimal 値をデシリアライズする際は、整数部と小数部を次の擬似コードで求められます。
ここで trunc は 0 に向かって切り捨てを行い (負の値では結果が異なる床除算ではなく) 、scale は小数点以下の桁数を表します。たとえば、Decimal(10, 2) (Decimal32(2) と同等) では、scale2 で、値 12345(123, 45) として表現されます。 シリアライゼーションでは、この逆の操作が必要です:
詳細は、ClickHouseドキュメントのDecimal型を参照してください。

String

ClickHouse の文字列は、任意のバイト列です。有効な UTF-8 である必要はありません。長さのプレフィックスはバイト長であり、文字数ではありません。 エンコードは次の 2 つの部分で構成されます。
  1. 文字列の長さをバイト単位で示す可変長整数 (LEB128) 。
  2. 文字列の生のバイト列。
たとえば、文字列 foobar は次のように 7 バイトでエンコードされます。

FixedString

String とは異なり、FixedString はスキーマで定義される固定長を持ちます。バイト列としてエンコードされ、値が N より短い場合は末尾がゼロバイトで埋められます。
FixedString を読み取る際、末尾のゼロバイトはパディングである場合も、データ中の実際の \0 文字である場合もあり、ワイヤ上では区別できません。ClickHouse 自体は N バイトをすべてそのまま保持します。
空の FixedString(3) にはパディングのゼロだけが含まれます:
文字列 hi が入った空でない FixedString(3):
文字列 bar を含む、空でない FixedString(3):
最後の例では、3 バイトすべてを使用しているため、パディングは不要です。

Date

1970-01-01 からの日数を表す UInt16 (2 バイト) として格納されます。 サポートされる値の範囲: [1970-01-01, 2149-06-06] Date の内部値の例:

Date32

1970-01-01前後の日数を表す Int32 (4バイト) として格納されます。 対応する値の範囲: [1900-01-01, 2299-12-31] Date32 の内部値の例:
エポック以前の日時:

DateTime

1970-01-01 00:00:00 UTC からの 経過秒数を表す UInt32 (4バイト) として格納されます。 構文:
たとえば、DateTimeDateTime('UTC') です。
バイナリ値は常に UTC エポックオフセットです。タイムゾーンによってエンコーディングが変わることはありません。ただし、文字列値が INSERT 時にどのように解釈されるかにはタイムゾーン 影響します。つまり、DateTime('America/New_York') カラムに '2024-01-15 10:30:00' を挿入すると、同じ文字列を DateTime('UTC') カラムに挿入した場合とは異なるエポック値が保存されます。これは、その文字列がカラムのタイムゾーンにおける現地時刻として解釈されるためです。ワイヤ上では、どちらも単なる UInt32 のエポック秒です。
サポートされる値の範囲: [1970-01-01 00:00:00, 2106-02-07 06:28:15] DateTime の内部値の例:

DateTime64

1970-01-01 00:00:00 UTCを基準としたティック数を表すInt64 (8バイト) として格納されます。値はその前後のいずれも取り得ます。ティックの精度はprecisionパラメータで定義されます。以下の構文を参照してください。
precision0 から 9 までの整数です。通常使われるのは、3 (ミリ秒) 、6 (マイクロ秒) 、 9 (ナノ秒) のみです。 有効な DateTime64 定義の例: DateTime64(0)DateTime64(3)DateTime64(6, 'UTC')DateTime64(9, 'Europe/Amsterdam')
DateTime と同様に、バイナリ値は常に UTC エポックからのオフセットです。タイムゾーンは、INSERT 時に文字列値をどのように解釈するかに影響します (DateTime の注記を参照) が、エンコード自体は常に UTC エポックからの Int64 ティックです。
DateTime64 型の基になる Int64 値は、UNIX エポックの前後における以下の単位数として解釈できます。
  • DateTime64(0) - 秒。
  • DateTime64(3) - ミリ秒。
  • DateTime64(6) - マイクロ秒。
  • DateTime64(9) - ナノ秒。
サポートされる値の範囲: [0000-01-01 00:00:00, 9999-12-31 23:59:59.999999999] (precision が 7 までの場合。precision 8 および 9 ではより狭くなります。詳細は以下の注記を参照してください)。 DateTime64 の内部値の例:
  • DateTime64(3): 値 15463008000002019-01-01 00:00:00 UTC を表します。
  • DateTime64(6): 値 17053146001234562024-01-15 10:30:00.123456 UTC を表します。
  • DateTime64(9): 値 17053146001234567892024-01-15 10:30:00.123456789 UTC を表します。
基になる Int64 ティックの範囲は精度が高くなるほど狭くなるため、サポートされる最大値も小さくなります。precision 8 では 4892-10-07precision 9 (ナノ秒) では UTC で 2262-04-11 23:47:16 です。

Time

秒単位の時刻値を表す Int32 として格納されます。負の値も有効です。 サポートされる値の範囲は [-999:59:59, 999:59:59] (つまり [-3599999, 3599999] 秒) です。
現時点では、Time または Time64 を使用するには、設定 enable_time_time64_type1 に設定する必要があります。
Time の内部値の例:

Time64

内部的には Decimal64 (Int64 として格納) で保存され、小数秒を含む時刻値を表します。精度は設定可能です。負の値も有効です。 構文:
ここで、precision0 から 9 までの整数です。一般的な値は、3 (ミリ秒) 、6 (マイクロ秒) 、9 (ナノ秒) です。 サポートされる値の範囲は [-999:59:59.xxxxxxxxx, 999:59:59.xxxxxxxxx] です。
現時点では、Time または Time64 を使用するには、設定 enable_time_time64_type1 に設定する必要があります。
基になる Int64 の値は、秒の小数部分を 10^precision 倍した値を表します。 Time64 の内部値の例:

インターバル型

すべてのインターバル型は Int64 (8バイト、リトルエンディアン) として格納されます。値は、それぞれの時間単位の個数を表します。負の値も有効です。 インターバル型は次のとおりです: IntervalNanosecondIntervalMicrosecondIntervalMillisecondIntervalSecondIntervalMinuteIntervalHourIntervalDayIntervalWeekIntervalMonthIntervalQuarterIntervalYear
インターバルの型名 (例: IntervalSecondIntervalDay) によって、格納される値の単位が決まります。ワイヤ形式でのエンコーディングは常に同じです。
内部値の例:

Enum8、Enum16

enum 定義内での enum 値のインデックスを表す 1 バイト (Enum8 == Int8) または 2 バイト (Enum16 == Int16) として格納されます。ストレージ型は signed である点に注意してください。つまり、enum 値には負の値も指定できます (例: Enum8('a' = -128, 'b' = 0)) 。 Enum は次のように簡単に定義できます。
上で定義したEnum8は、クライアント側では次の値にマッピングされます。
あるいは、次のような、より複雑な方法もあります:
上で定義した Enum16 は、クライアント側では次の値にマッピングされます:
データ型パーサーにとっての主な課題は、\' のような enum 定義内のエスケープされた記号や、引用符で囲まれた文字列内に現れる可能性のある = のような特殊記号を正しく追跡することです。

UUID

16 バイトの並びとして表現されます。UUID は、2 つのリトルエンディアン UInt64として格納されます。標準的な UUID 表現の最初の 8 バイトはバイト順が逆になり、後半の 8 バイトもそれぞれ独立してバイト順が逆になります。 たとえば、UUID 61f0c404-5cb3-11e7-907b-a6006ad3dba0 の場合:
  • 標準的なバイト表現: 61 f0 c4 04 5c b3 11 e7 | 90 7b a6 00 6a d3 db a0
  • 前半を逆順にしたもの (LE UInt64): e7 11 b3 5c 04 c4 f0 61
  • 後半を逆順にしたもの (LE UInt64): a0 db d3 6a 00 a6 7b 90
UUID の内部値の例:
  • 61f0c404-5cb3-11e7-907b-a6006ad3dba0 は次のように表現されます:
  • デフォルトの UUID 00000000-0000-0000-0000-000000000000 は、16 個のゼロバイトで表されます:
新しいレコードが挿入されたが、UUIDの値が指定されていない場合に使用できます。

IPv4

4バイトの UInt32 として、リトルエンディアン のバイト順で格納されます。これは、IP アドレスで一般的に使用される従来のネットワークバイトオーダー (ビッグエンディアン) とは異なる点に注意してください。IPv4 の内部値の例:

IPv6

ビッグエンディアン / ネットワークバイトオーダー (MSB 先頭) で 16 バイトに格納されます。IPv6 の内部値の例:

Nullable

Nullable データ型は、次のようにエンコードされます。
  1. 値が NULL かどうかを示す 1 バイト:
    • 0x00 は、値が NULL ではないことを示します。
    • 0x01 は、値が NULL であることを示します。
  2. 値が NULL でない場合は、基になるデータ型が通常どおりエンコードされます。値が NULL の場合、基になる型については 追加のバイトは一切 書き込まれません。
たとえば、Nullable(UInt32) 型の値:

LowCardinality

RowBinary フォーマットでは、low-cardinality マーカーはワイヤ形式に影響しません。たとえば、LowCardinality(String) は通常の String と同じようにエンコードされます。
これは RowBinary にのみ当てはまります。Native format では、LowCardinality は Dictionary ベースの別のエンコードを使用します。
カラムは LowCardinality(Nullable(T)) として定義できますが、Nullable(LowCardinality(T)) として定義することはできません。常に server から error になります。
テスト時には、カバレッジを広げるために、allow_suspicious_low_cardinality_types1 に設定すると、LowCardinality 内でほとんどの data types を許可できます。

Array

Array は次のようにエンコードされます。
  1. 配列の要素数を示す 可変長整数 (LEB128)
  2. 配列の各要素。基になるデータ型と同じ方法でエンコードされます。
たとえば、UInt32 型の値を持つ配列は次のとおりです。
やや複雑な例:
Array には Nullable な値を含めることができますが、Array 自体を Nullable にすることはできません。
以下は有効です。
そして、次のようにエンコードされます。
多次元配列の扱いの例は、Geo セクションに記載されています。

Tuple

タプルは、追加のメタ情報や区切り文字を使わず、各要素をそれぞれ対応するワイヤ形式で順に並べてエンコードされます。
Tuple データ型の文字列エンコーディングでは、エスケープされた記号や特殊文字を追跡する必要があるなど、Enum 型と同様の課題があります。さらに、Tuple では開き括弧と閉じ括弧も追跡する必要があります。加えて、複雑な Tuple には、ネストされた別の Tuple、Array、Map、さらには enum が含まれることもある点に注意してください。 たとえば次の表では、Tuple に名前の中にバッククォートと括弧を含む enum が含まれており、適切に処理しないとパースの問題を引き起こす可能性があります。

Map

map は Array(Tuple(K, V)) とみなすことができ、ここで K はキーの型、V は値の型です。map は次のようにエンコードされます。
  1. map 内の要素数を示す 可変長整数 (LEB128)
  2. map の各要素を、対応する型でエンコードされたキー・バリューのペアとして格納します。
たとえば、キーが String、値が UInt32 の map は次のとおりです。
Map(String, Map(Int32, Array(Nullable(String)))) のように、深くネストされた構造を持つ Map も可能で、その場合も上記と同様にエンコードされます。

Variant

この型は、ほかのデータ型のユニオンを表します。型 Variant(T1, T2, ..., TN) は、この型の各行が T1T2、…、TN のいずれかの型、またはそのどれにも該当しない値 (NULL 値) を持つことを意味します。
エンドユーザーにとっては Variant(T1, T2)Variant(T2, T1) はまったく同じ意味ですが、ワイヤ形式では定義内の型の順序が重要です。定義内の型は常にアルファベット順にソートされます。これは、どのバリアントかが “判別子”、つまり定義内のデータ型の索引によってエンコードされるためです。
次の例を見てみましょう。
NULL 値は、判別子バイト 0xFF でエンコードされます:
allow_suspicious_variant_types 設定を使用すると、Variant 型をより網羅的にテストできます。

Dynamic

Dynamic 型は、実行時に決まる任意の型の値を保持できます。RowBinary フォーマットでは、各値は自己記述的です。最初の部分には、このフォーマットで表される型指定が入ります。続いて、このドキュメントで説明している値エンコードに従った内容が続きます。したがって、値をパースするには、型の索引を使って適切なパーサーを判断し、あとはすでに別の場所で使っている RowBinary のパース処理を再利用するだけです。
ここで、BinaryTypeIndex は型を識別する 1 バイトの値です。型インデックスとパラメータについては、こちら のリファレンスを参照してください。 NULL の Dynamic 値は、BinaryTypeIndex 0x00 (Nothing 型) としてエンコードされ、追加のバイトはありません:
例:

JSON

JSON型はデータを2つの異なるカテゴリにエンコードします:
  1. 型付きパス - スキーマ内で型を明示して宣言されたパス (例: JSON(user_id UInt32, name String))
  2. 動的パス/動的パスの上限を超えた場合のオーバーフローパス - Dynamic 型として保存される実行時検出パス。値のエンコーディングの前に型定義が付きます。
この2つのカテゴリでは、ワイヤ形式とルールが異なります。 パスは3つのグループに分けてシリアライズされ、typed paths、dynamic paths、shared data (オーバーフロー) pathsの順に書き込まれます。typed pathsとdynamic pathsは実装定義の順序 (内部ハッシュマップのイテレーションによって決定) で書き込まれ、shared data pathsはアルファベット順で書き込まれます。読み取り側は特定のパス順序に依存しないでください。デシリアライザは各パスを位置ではなく名前によってディスパッチします。 RowBinary フォーマットにおける各 JSON 行は、次のようにシリアライズされます:
例: 1. 型付きパスのみを含むシンプルなJSON: スキーマ: JSON(user_id UInt32, active Bool) 行: {"user_id": 42, "active": true} バイナリエンコーディング (注釈付き16進数) :
2. 型付きパスと動的パスを持つシンプルなJSON: スキーマ: JSON(user_id UInt32, active Bool) 行: {"user_id": 42, "active": true, "name": "Alice"} バイナリエンコーディング (注釈付き16進数) :
3. NULLの処理: 型付きNullableカラムの場合、nullが返されます: スキーマ: JSON(score Nullable(Int32)) 行: {"score": null } バイナリエンコーディング (注釈付き16進数) :
型付き非Nullableカラムの場合、デフォルト値が返されます: Schema: JSON(name String) 行: {"name": null} バイナリエンコーディング:
動的パスの場合、これは無視されます: スキーマ: JSON(id UInt64) 行: {"id": 100, "metadata": null} バイナリエンコーディング:
注意: NULL 値を持つ metadata パスは、動的パスが非 null の場合にのみシリアライズされるため、含まれません。これは型付きパスとの重要な相違点です。 4. ネストされたJSONオブジェクト: スキーマ: JSON() 行: {"user": {"name": "Bob", "age": 30}} バイナリエンコーディング (注釈付き16進表記) :
注: ネストされたオブジェクトは、ネスト構造ではなくドット区切りのパスにフラット化されます (例: user.name) 。 代替: JSONをStringとして扱うモード 設定 output_format_binary_write_json_as_string=1 を有効にすると、JSONカラムは構造化されたバイナリフォーマットではなく、1つのJSONテキスト文字列としてシリアライズされます。JSONカラムへの書き込みには、対応する設定 input_format_binary_read_json_as_string もあります。ここでどちらの設定を使うかは、JSONをクライアント側でパースするか、サーバー側でパースするかによって決まります。

Geo 型

Geo は、地理データを表現するデータ型のカテゴリです。次の型が含まれます。
  • Point - Tuple(Float64, Float64) として表されます。
  • Ring - Array(Point) または Array(Tuple(Float64, Float64)) として表されます。
  • Polygon - Array(Ring) または Array(Array(Tuple(Float64, Float64))) として表されます。
  • MultiPolygon - Array(Polygon) または Array(Array(Array(Tuple(Float64, Float64)))) として表されます。
  • LineString - Array(Point) または Array(Tuple(Float64, Float64)) として表されます。
  • MultiLineString - Array(LineString) または Array(Array(Tuple(Float64, Float64))) として表されます。
Geo の値のワイヤ形式は、Tuple および Array の場合とまったく同じです。RowBinaryWithNamesAndTypes フォーマットのヘッダーには、これらの型の別名 (たとえば PointRingPolygonMultiPolygonLineStringMultiLineString) が含まれます。

Geometry

Geometry は、上記に挙げた任意の Geo 型を保持できる Variant 型です。ワイヤ上では Variant とまったく同じようにエンコードされ、後続する Geo 型を示す 判別子 バイトが付加されます。 Geometry の 判別子 インデックスは次のとおりです。 ワイヤ形式の構造:
PointGeometry としてエンコードした例:
RingGeometry としてエンコードした例:

Nested

Nested のワイヤ形式は、flatten_nested 設定に依存します。
1 行内のすべての部分配列は、同じ長さである必要があります。これはサーバー側で強制される制約です。長さが一致しない場合、挿入時にエラーが発生します。

flatten_nested = 1 (デフォルト)

デフォルト設定では、Nested は独立した配列にフラット化されます。各サブカラムは、ドット区切りの名前を持つ個別の Array カラムになります。
DESCRIBE TABLE foo を実行すると、フラット化されたカラムが表示されます。
各配列は、Array セクションで説明しているとおり、それぞれ独立してシリアライズされます。

flatten_nested = 0

flatten_nested = 0 の場合、NestedArray(Tuple(...)) 型の単一のカラムとして保持されます。カラム名はドット区切りではありません。
DESCRIBE TABLE foo では、1 つのカラムが表示されます。
エンコーディングは Array(Tuple(String, Int32)) です。最初に配列長のプレフィックスがあり、続いて各要素のタプルのフィールドが順に並びます。
フィールドが、フラット化された表現のようにカラムごとにまとめられている (a₁, a₂, b₁, b₂) のではなく、要素ごとに交互に並んでいる (a₁, b₁, a₂, b₂) ことに注目してください。

SimpleAggregateFunction

SimpleAggregateFunction(func, T) は、基になるデータ型 T と同一の形式でエンコードされます。集約関数名はワイヤ形式に影響しません。 たとえば、SimpleAggregateFunction(max, UInt32) は通常の UInt32 と同じ形式でエンコードされます。
RowBinaryWithNamesAndTypes のヘッダーでは型は SimpleAggregateFunction(max, UInt32) と示されますが、実際にワイヤ上で流れる値は単なる UInt32 です:

AggregateFunction

AggregateFunction(func, T) は、aggregate function の完全な中間状態を格納します。中間状態を格納する点は同じでも、それを基になるデータ型と同じ形式でエンコードする SimpleAggregateFunction とは異なり、AggregateFunction は各 aggregate function 固有のフォーマットを持つ不透明なバイナリブロブを格納します。
aggregate states には、RowBinary フォーマットでは長さプレフィックスがありません。そのため、何バイト読み取るべきかを判断するには、パーサーが各 aggregate function 固有の内部シリアライゼーションフォーマットを理解している必要があります。実際には、ほとんどのクライアントは aggregate states を不透明なものとして扱い、シリアライゼーションは server に任せるため、*State / *Merge コンビネーターを使用します。
内部フォーマットは関数ごとに異なります。簡単な例をいくつか示します。 countState — カウントを VarUInt (LEB128) として格納します。
sumState — 累積された合計を固定サイズの整数に格納します。ビット幅は引数の型に依存します (整数型の引数の場合は UInt64) :
minState / maxState — フラグバイトに続けて、基になる型の値を格納します。フラグは、空の状態 (値が1つもない) の場合は 0x00、値がある場合は 0x01 です:
空の状態 (集計された行がない) :
uniqquantilegroupArray のようなより複雑な関数では、実装固有のフォーマットが使用されます。これらの状態を読み書きする必要がある場合は、該当する関数の ClickHouse ソースコードを参照してください。

QBit

QBit は、異なる精度レベルで効率的にルックアップできるベクトル型です。内部的には転置フォーマットで保存されます。転送時には、QBit は単に基底要素型 (Int8Float32Float64、または BFloat16) の Array です。保存時のビット転置の最適化は RowBinary プロトコルではなく、サーバー側で行われます。 構文:
ここで、element_typeInt8Float32Float64、または BFloat16 で、dimension は固定のベクトル次元です。省略可能な stride は、サーバー側でビットプレーンを保存ストリームにどうグループ化するかだけを制御するもので、RowBinary のワイヤ形式には影響しません。ワイヤ形式は常に dimension 個の要素からなる完全な配列です。 ワイヤ形式: Array(element_type) と同一です。
[1.0, 2.0, 3.0, 4.0] を含む QBit(Float32, 4) のエンコード例:

フォーマット設定

以下の設定は、すべての RowBinary 系フォーマットに共通です。
最終更新日 2026年7月23日