メインコンテンツへスキップ
ClickHouse では、半構造化データや動的なデータ向けに設計されたネイティブの JSON カラム型を利用できるようになりました。ここで重要なのは、これはデータフォーマットではなく、カラム型であるという点です。JSON は文字列として、または JSONEachRow のような対応フォーマット経由で ClickHouse に挿入できますが、それは JSON カラム型を使っていることを意味するわけではありません。JSON 型を使うべきなのは、単に JSON を保存したい場合ではなく、データ構造が動的な場合に限られます。

JSON 型を使用する場面

JSON 型は、動的または予測しにくい構造を持つ JSON オブジェクト内の特定のフィールドに対して、クエリ、フィルタリング、集計を行うために設計されています。これは、JSON オブジェクトを個別のサブカラムに分割することで実現されます。これにより、Map や文字列のパースなどの代替手段と比べて、読み取るデータ量を大幅に削減し、選択したフィールドに対するクエリを高速化できます。 ただし、これには重要なトレードオフがあります。
  • INSERT が遅くなる - JSON をサブカラムに分割し、型推論を行い、柔軟なストレージ構造を管理する必要があるため、JSON を単純な String カラムとして保存する場合に比べて、INSERT は遅くなります。
  • オブジェクト全体を読み取る場合は遅くなる - 完全な JSON ドキュメント全体を取得する必要がある場合 (特定のフィールドではなく) 、JSON 型は String カラムから読み取るよりも低速です。個別のサブカラムからオブジェクトを再構築するオーバーヘッドは、フィールドレベルのクエリを行わない場合にはメリットがありません。
  • ストレージのオーバーヘッド - 個別のサブカラムを維持するため、JSON を単一の文字列値として保存する場合に比べて、構造上のオーバーヘッドが増えます。

JSON 型を使用するケース:

  • データの構造が動的または予測しにくく、ドキュメントごとにキーが異なる
  • フィールドの型やスキーマが時間の経過とともに変化する、またはレコードごとに異なる
  • JSON オブジェクト内の特定のパスに対して、構造を事前に予測できなくてもクエリ、フィルタ、集計を行う必要がある
  • ユースケースに、スキーマに一貫性のないログ、イベント、ユーザー生成コンテンツなどの半構造化データが含まれる

String カラム (または構造化型) を使用するのは次のような場合です:

  • データ構造が明確で一貫している場合。この場合は、代わりに通常のカラム、TupleArrayDynamic、または Variant 型を使用します
  • JSON ドキュメントを不透明なブロブとして扱い、フィールド単位で解析せず、全体をそのまま保存・取得するだけの場合
  • データベース内で個々の JSON フィールドに対してクエリやフィルタを行う必要がない場合
  • JSON が単なる転送/保存用のフォーマットであり、ClickHouse 内では解析しない場合
JSON がデータベース内で解析しない不透明なドキュメントで、保存してそのまま取り出すだけであれば、String フィールドとして保存すべきです。JSON 型の利点が発揮されるのは、動的な JSON 構造内の特定のフィールドに対して、効率的にクエリ、フィルタ、または集計を行う必要がある場合に限られます。また、これらの方法を組み合わせることもできます。予測可能なトップレベルのフィールドには標準のカラムを使用し、ペイロードの動的な部分には JSON カラムを使用します。

JSON を使用する際の考慮事項とヒント

JSON 型は、パスをサブカラムにフラット化することで、効率的な列指向ストレージを実現します。ただし、柔軟性が高いぶん、適切な使い方が重要です。効果的に活用するには、次の点に注意してください。
  • 既知のサブカラムについては、不要な型推論を避けるため、カラム定義内のヒントを使ってパスの型を指定します。
  • 値が不要な場合は、SKIP と SKIP REGEXPパスをスキップし、ストレージ使用量を削減してパフォーマンスを向上させます。
  • max_dynamic_paths は高くしすぎないでください。値を大きくするとリソース消費が増え、効率が低下します。目安として、10,000 未満に抑えてください。
型ヒント型ヒントは、不要な型推論を避けるためだけのものではありません。ストレージや処理における間接性そのものを完全になくします。型ヒントが付いた JSON パスは、常に従来のカラムと同様に格納されるため、discriminator columnsや、クエリ時の動的な解決を必要としません。つまり、型ヒントを適切に定義しておけば、ネストされた JSON フィールドでも、最初からトップレベルのフィールドとして定義されていたかのように、同等のパフォーマンスと効率を得られます。そのため、大部分は一貫している一方で JSON の柔軟性も活かしたいデータセットでは、スキーマや取り込みパイプラインを作り直すことなくパフォーマンスを維持するうえで、型ヒントは便利な手段になります。

高度な機能

  • JSONカラムは、他のカラムと同様に主キーに使用できます。サブカラム に codecs は指定できません。
  • JSONAllPathsWithTypes() and JSONDynamicPaths() のような関数によるイントロスペクションをサポートしています。
  • .^ 構文を使って、ネストされた sub-objects を読み取れます。
  • クエリ構文は標準SQLと異なる場合があり、ネストされたフィールドには特別なキャストや演算子が必要になることがあります。
詳細については、 ClickHouse JSON ドキュメント またはブログ記事 ClickHouse 向けの強力な新しい JSON データ型を参照してください。

以下の JSON サンプルは、Python PyPI dataset の1行を表したものです。
このスキーマは固定的で、型を明確に定義できると仮定します。データがNDJSONフォーマット (各行が1つのJSONオブジェクト) であっても、そのようなスキーマにJSON型を使う必要はありません。スキーマは従来の型で定義すれば十分です。
JSON形式の行を挿入します:
250万本の学術論文を含む arXiv データセット を考えてみましょう。このデータセットは NDJSON 形式で配布されており、各行が公開された学術論文 1 本を表しています。以下に行の例を示します。
ここで扱う JSON はネストした構造を含む複雑なものですが、構造は予測可能です。フィールドの数や型が変わることはありません。この例では JSON 型を使うこともできますが、Tuples 型と Nested 型を使って構造を明示的に定義することもできます。
ここでも、データを JSON として insert できます:
tags という別のカラムが追加されたとします。これが単なる文字列のリストであれば、Array(String) で表現できますが、ここでは型が混在した任意のタグ構造を追加できると仮定します (score が文字列または整数になっていることに注目してください) 。修正後の JSON ドキュメントは次のとおりです。
この場合、arXiv のドキュメントは、すべてを JSON として扱うことも、JSON の tags カラムを追加するだけにすることもできます。以下に両方の例を示します。
update_date カラムは順序付け/主キーで使用するため、JSON 定義でその型ヒントを指定しています。これにより、ClickHouse はこのカラムが null にならないことを認識でき、さらにどの update_date サブカラムを使うべきかも判断できます (型ごとに複数存在する可能性があるため、そうしないと曖昧になります) 。
このテーブルに insert し、その後に推論されたスキーマを、JSONAllPathsWithTypes 関数と PrettyJSONEachRow 出力フォーマットを使って確認できます。
別の方法として、先ほどのスキーマと JSON の tags カラムを使ってこれを表現することもできます。一般的にはこちらのほうが好ましく、ClickHouse 側で必要となる推論を最小限に抑えられます。
これで、tags サブカラムの型を推論できるようになりました。
最終更新日 2026年7月1日