QueryContexts
QueryContext 内で実行します。QueryContext には、ClickHouse データベースに対するクエリの構築に使用される主要な構造と、結果を QueryResult またはその他の応答データ構造に変換するための設定が含まれます。これには、クエリ自体、パラメータ、設定、読み取りフォーマット、その他のプロパティが含まれます。
QueryContext は、クライアントの create_query_context メソッドを使用して取得できます。このメソッドは、中核となるクエリメソッドと同じパラメータを受け取ります。取得したクエリコンテキストは、その後 query、query_df、または query_np メソッドに対して、これらのメソッドの他の引数の一部またはすべての代わりに、context キーワード引数として渡すことができます。なお、メソッド呼び出しで追加指定した引数は、QueryContext の各プロパティより優先されます。
QueryContext の最もわかりやすいユースケースは、同じクエリを異なるバインドパラメータ値で送信することです。すべてのパラメータ値は、辞書を指定して QueryContext.set_parameters メソッドを呼び出すことで更新できます。また、個々の値は、目的の key、value の組を指定して QueryContext.set_parameter を呼び出すことで更新できます。
QueryContext はスレッドセーフではありませんが、マルチスレッド環境では QueryContext.updated_copy メソッドを呼び出すことで、そのコピーを取得できます。
ストリーミングクエリ
query_column_block_stream— クエリデータを、Python ネイティブオブジェクトを使用して、カラムのシーケンスとしてブロック単位で返しますquery_row_block_stream— クエリデータを、Python ネイティブオブジェクトを使用して、行のブロックとして返しますquery_rows_stream— クエリデータを、Python ネイティブオブジェクトを使用して、行のシーケンスとして返しますquery_np_stream— クエリデータの各 ClickHouse ブロックを NumPy 配列として返しますquery_df_stream— クエリデータの各 ClickHouse ブロックを Pandas DataFrame として返しますquery_arrow_stream— クエリデータを PyArrow RecordBlocks として返しますquery_df_arrow_stream— kwargdataframe_libraryに応じて、クエリデータの各 ClickHouse ブロックを Arrow バックエンドの Pandas DataFrame または Polars DataFrame として返します (デフォルトは “pandas” です) 。
ContextStream オブジェクトを返し、ストリームの読み取りを開始するには with ステートメントで開く必要があります。
データブロック
query メソッドから取得されるすべてのデータを、ClickHouse server から受信するブロックのストリームとして処理します。これらのブロックは、ClickHouse との間でカスタムの「Native」フォーマットを使って送受信されます。「block」は、指定されたデータ型のバイナリデータで構成されたカラムの並びにすぎず、各カラムには同じ数のデータ値が含まれます。 (列指向データベースである ClickHouse では、データもこれに近い形式で保存されます。) クエリから返されるブロックのサイズは、複数のレベル (user profile、user、session、または query) で設定できる 2 つのユーザー設定によって決まります。設定項目は次のとおりです。
- max_block_size — ブロックサイズの上限 (行数) 。デフォルトは 65536。
- preferred_block_size_bytes — ブロックサイズのソフトリミット (バイト数) 。デフォルトは 1,000,0000。
preferred_block_size_setting にかかわらず、各ブロックが max_block_size 行を超えることはありません。返される実際のブロックサイズは、クエリの種類によってさまざまです。たとえば、多数の分片をまたぐ分散テーブルに対するクエリでは、各分片から直接取得された小さめのブロックが含まれる場合があります。
Client の query_*_stream メソッドのいずれかを使用すると、結果はブロック単位で返されます。ClickHouse Connect は、一度に 1 つのブロックだけを読み込みます。これにより、大きな結果セット全体をメモリに読み込むことなく、大量のデータを処理できます。アプリケーションは、任意の数のブロックを処理できるようにしておく必要があり、各ブロックの正確なサイズは制御できない点に注意してください。
処理が遅い場合の HTTP データバッファ
http_buffer_size 設定を使用して HTTP ストリーミングバッファ (デフォルトでは 10 メガバイト) のバッファサイズを増やすことで、ある程度軽減できます。アプリケーションで利用可能なメモリが十分にある場合、このような状況では http_buffer_size を大きくしても問題ありません。lz4 または zstd 圧縮を使用している場合、バッファ内のデータは圧縮された状態で保存されるため、これらの圧縮タイプを使用すると、実質的に利用可能なバッファ容量が増えます。
StreamContexts
query_*_stream メソッド (query_row_block_stream など) は、Python のコンテキストマネージャーとジェネレーターを組み合わせた ClickHouse の StreamContext オブジェクトを返します。基本的な使い方は次のとおりです。
with ステートメントを使わずに StreamContext を使用しようとすると、エラーが発生する点に注意してください。Python のコンテキストを使うことで、ストリーム (この場合はストリーミング HTTP レスポンス) は、すべてのデータが消費されなかった場合や、処理中に例外が発生した場合でも、適切にクローズされます。また、StreamContext はストリームの消費に一度しか使用できません。終了後の StreamContext を使用しようとすると、StreamClosedError が発生します。
StreamContext の source プロパティを使うと、カラム名と型を含む親の QueryResult オブジェクトにアクセスできます。
ストリームの種類
query_column_block_stream メソッドは、ブロックを、ネイティブの Python データ型として格納されたカラムデータのシーケンスとして返します。上記の taxi_trips クエリを使うと、返されるデータはリストになり、その各要素は対応するカラムのすべてのデータを含む別のリスト (またはタプル) になります。したがって block[0] は、文字列だけを含むタプルになります。カラム指向のフォーマットは、運賃の合計を足し上げるといった、あるカラム内のすべての値に対する集計処理で最もよく使われます。
query_row_block_stream メソッドは、従来のリレーショナルデータベースのように、ブロックを行のシーケンスとして返します。タクシー乗車データでは、返されるデータはリストになり、その各要素はデータの1行を表す別のリストになります。したがって block[0] には最初のタクシー乗車のすべてのフィールドが (順番どおりに) 含まれ、block[1] には2番目のタクシー乗車のすべてのフィールドを含む行が入ります。以降も同様です。行指向の結果は、通常、表示や変換処理に使われます。
query_row_stream は、ストリームを反復処理するときに自動的に次のブロックへ進むための便利なメソッドです。それ以外は query_row_block_stream と同じです。
query_np_stream メソッドは、各ブロックを2次元の NumPy Array として返します。内部的には、NumPy 配列は (通常) カラムとして格納されるため、行用とカラム用で別々のメソッドは必要ありません。NumPy 配列の “shape” は (columns, rows) として表されます。NumPy ライブラリには、NumPy 配列を操作するためのメソッドが数多く用意されています。なお、クエリ内のすべてのカラムが同じ NumPy dtype を共有している場合、返される NumPy 配列の dtype も1つだけになり、内部構造を実際に変更することなく reshape / rotate できます。
query_df_stream メソッドは、各 ClickHouse Block を2次元の Pandas DataFrame として返します。以下の例は、StreamContext オブジェクトを遅延的にコンテキストとして使用できることを示しています (ただし1回のみ) 。
query_df_arrow_stream メソッドは、各 ClickHouse Block を PyArrow の dtype バックエンドを持つ DataFrame として返します。このメソッドは、dataframe_library パラメータを通じて Pandas (2.x 以降) と Polars の両方の DataFrame をサポートしており、既定値は "pandas" です。各反復では、PyArrow の record batch から変換された DataFrame が返されるため、特定のデータ型ではパフォーマンスとメモリ効率が向上します。
最後に、query_arrow_stream メソッドは、ClickHouse の ArrowStream フォーマットの結果を、StreamContext でラップされた pyarrow.ipc.RecordBatchStreamReader として返します。ストリームの各反復では、PyArrow RecordBlock が返されます。
ストリーミングの使用例
行のストリーミング
行ブロックをストリーミングする
Pandas DataFrame をストリーミングする
Arrow バッチをストリーミングする
NumPy、Pandas、Arrow クエリ
NumPy クエリ
query_np メソッドは、ClickHouse Connect の QueryResult ではなく、NumPy 配列としてクエリ結果を返します。
Pandas クエリ
query_df メソッドは、ClickHouse Connect の QueryResult ではなく、クエリ結果を Pandas の DataFrame として返します。
PyArrow クエリ
query_arrow メソッドは、クエリ結果を PyArrow Table として返します。ClickHouse の Arrow フォーマットを直接利用するため、メインの query メソッドと共通して受け付ける引数は query、parameters、settings の 3 つだけです。これに加えて use_strings という引数もあり、Arrow Table が ClickHouse の String 型を文字列として扱うか (True の場合) 、bytes として扱うか (False の場合) を指定します。
Arrow バックエンドのDataFrame
query_df_arrow および query_df_arrow_stream メソッドを通じて、Arrow の結果から高速かつメモリ効率よく DataFrame を作成できます。これらは Arrow クエリメソッドの軽量なラッパーであり、可能な場合は DataFrame へのゼロコピー変換を行います。
query_df_arrow: ClickHouse のArrow出力フォーマットを使用してクエリを実行し、DataFrame を返します。dataframe_library='pandas'の場合、Arrow バックエンドの dtype (pd.ArrowDtype) を使用する pandas 2.x の DataFrame を返します。これには pandas 2.x が必要で、可能な場合はゼロコピーバッファを活用することで、優れたパフォーマンスと低いメモリオーバーヘッドを実現します。dataframe_library='polars'の場合、Arrow テーブルから作成された Polars DataFrame (pl.from_arrow) を返します。これも同様に効率的で、データによってはゼロコピーが可能です。
query_df_arrow_stream: Arrow ストリームのバッチから変換された DataFrame (pandas 2.x または Polars) のシーケンスとして結果をストリーミングします。
ArrowバックエンドのDataFrameへのクエリ
注意事項と留意点
- Arrow の型マッピング: Arrow フォーマットでデータを返す場合、ClickHouse は各型を、サポートされている中で最も近い Arrow 型にマッピングします。一部の ClickHouse 型には Arrow にネイティブな対応型がないため、Arrow のフィールドでは生のバイト列 (通常は
BINARYまたはFIXED_SIZE_BINARY) として返されます。- 例:
IPv4は Arrow のUINT32として表現されます。IPv6と大きな整数 (Int128/UInt128/Int256/UInt256) は、多くの場合、生のバイト列を含むFIXED_SIZE_BINARY/BINARYとして表現されます。 - このような場合、DataFrame のカラムには Arrow フィールドに基づくバイト値が格納されます。これらのバイト列を ClickHouse のセマンティクスに従ってどう解釈・変換するかは、クライアントコード側で対応する必要があります。
- 例:
- サポートされていない Arrow の data types (例: 真の Arrow 型としての UUID/ENUM) は出力されません。代わりに、出力時には最も近いサポート対象の Arrow 型 (多くの場合はバイナリのバイト列) で表現されます。
- Pandas の要件: Arrow バックエンドの dtype を使うには pandas 2.x が必要です。古い pandas バージョンでは、代わりに
query_df(非 Arrow) を使用してください。 - String とバイナリ:
use_stringsオプション (server settingoutput_format_arrow_string_as_stringがサポートされている場合) は、ClickHouse のStringカラムを Arrow の文字列として返すか、バイナリとして返すかを制御します。
ClickHouse/Arrow 型変換の不一致の例
FIXED_SIZE_BINARY や BINARY) として返す場合、これらのバイト列を適切な Python 型に変換するのは application code 側の責任です。以下の例は、一部の変換は DataFrame ライブラリの API で可能である一方、別の変換では struct.unpack のような pure Python の手法が必要になる場合があることを示しています (柔軟性は保てますが、性能は犠牲になります) 。
Date カラムは UINT16 (Unix エポック 1970‑01‑01 からの日数) として返されることがあります。DataFrame 内での変換は効率的で簡単です:
Int128 のようなカラムは、生のバイト列を含む FIXED_SIZE_BINARY として渡されることがあります。Polars は 128 ビット整数をネイティブにサポートしています。
dtype はないため、純粋な Python にフォールバックして、たとえば次のようにできます。
読み取りフォーマット
query、query_np、query_df メソッドから返される値のデータ型を制御します。 (raw_query と query_arrow は ClickHouse から受信したデータを変更しないため、フォーマット制御は適用されません。) たとえば、UUID の読み取りフォーマットをデフォルトの native フォーマットから代替の string フォーマットに変更すると、UUID カラムに対する ClickHouse クエリの結果は、Python の UUID オブジェクトではなく文字列値 (標準の 8-4-4-4-12 RFC 1422 形式を使用) として返されます。
どのフォーマット関数でも、「data type」引数にはワイルドカードを含めることができます。フォーマットは小文字 1 つの文字列です。
読み取りフォーマットは、複数のレベルで設定できます。
clickhouse_connect.datatypes.formatパッケージで定義されたメソッドを使用して、グローバルに設定できます。これにより、すべてのクエリで、設定したデータ型のフォーマットが制御されます。
- クエリ全体に対しては、省略可能な
query_formats辞書引数を使用します。この場合、指定したデータ型の任意のカラム (またはサブカラム) に、設定したフォーマットが適用されます。
- 特定のカラム内の値に対しては、オプションの
column_formats辞書引数を使用します。キーには ClickHouse が返すカラム名を指定し、値にはそのデータカラムのフォーマット、または ClickHouse の型名をキー、クエリのフォーマットを値とする第2レベルの “format” 辞書を指定します。この二次辞書は、Tuples や Maps などのネストされたカラム型に使用できます。
読み取りフォーマットのオプション (Python 型)
| ClickHouse 型 | ネイティブ Python 型 | 読み取りフォーマット | コメント |
|---|---|---|---|
| Int[8-64], UInt[8-32] | int | - | |
| UInt64 | int | signed | Superset は現在、大きな符号なし UInt64 値を処理できません |
| [U]Int[128,256] | int | string | Pandas と NumPy の int 値は最大 64 ビットまでのため、これらは文字列として返すことができます |
| BFloat16 | float | - | Python の float はすべて内部的に 64 ビットです |
| Float32 | float | - | Python の float はすべて内部的に 64 ビットです |
| Float64 | float | - | |
| Decimal | decimal.Decimal | - | |
| String | string | bytes | ClickHouse の String カラムには固有のエンコーディングがないため、可変長のバイナリデータにも使われます |
| FixedString | bytes | string | FixedString は固定長のバイト配列ですが、Python の文字列として扱われることもあります |
| Enum[8,16] | string | string, int | Python の enum は空文字列を受け付けないため、すべての enum は文字列または基になる int 値として返されます。 |
| Date | datetime.date | int | ClickHouse は Date を 01/01/1970 からの日数として保存します。この値は int として利用できます |
| Date32 | datetime.date | int | Date と同様ですが、より広い日付範囲に対応します |
| DateTime | datetime.datetime | int | ClickHouse は DateTime を epoch 秒として保存します。この値は int として利用できます |
| DateTime64 | datetime.datetime | int | Python の datetime.datetime はマイクロ秒精度に制限されています。生の 64 ビット int 値を利用できます |
| Time | datetime.timedelta | int, string, time | 時点は Unix timestamp として保存されます。この値は int として利用できます |
| Time64 | datetime.timedelta | int, string, time | Python の datetime.timedelta はマイクロ秒精度に制限されています。生の 64 ビット int 値を利用できます |
| IPv4 | ipaddress.IPv4Address | string | IP アドレスは文字列として読み取ることができ、適切な形式の文字列は IP アドレスとして挿入できます |
| IPv6 | ipaddress.IPv6Address | string | IP アドレスは文字列として読み取ることができ、適切な形式の文字列は IP アドレスとして挿入できます |
| Tuple | dict or tuple | tuple, json | 名前付き tuple はデフォルトで辞書として返されます。名前付き tuple は JSON 文字列として返すこともできます |
| Map | dict | - | |
| Nested | Sequence[dict] | - | |
| UUID | uuid.UUID | string | UUID は RFC 4122 に従った形式の文字列として読み取ることができます |
| JSON | dict | string | デフォルトでは Python の辞書が返されます。string フォーマットでは JSON 文字列が返されます |
| Variant | object | - | 値に格納されている ClickHouse データ型に対応する Python 型を返します |
| Dynamic | object | - | 値に格納されている ClickHouse データ型に対応する Python 型を返します |
外部データ
query*メソッドは、この機能を利用するための省略可能なexternal_dataパラメーターを受け付けます。external_dataパラメーターの値には、clickhouse_connect.driver.external.ExternalDataオブジェクトを指定する必要があります。このオブジェクトのコンストラクタは、以下の引数を受け取ります。
| Name | Type | Description |
|---|---|---|
| file_path | str | 外部データの読み取り元となる、ローカルシステム上のファイルへのパスです。file_pathまたはdataのいずれかが必要です |
| file_name | str | 外部データの”ファイル”名です。指定しない場合は、file_pathから (拡張子を除いて) 決定されます |
| data | bytes | バイナリ形式の外部データです (ファイルから読み取る代わりに指定します) 。dataまたはfile_pathのいずれかが必要です |
| fmt | str | データのClickHouse入力フォーマットです。デフォルトはTSVです |
| types | str or seq of str | 外部データ内のカラムのデータ型の一覧です。文字列の場合、型はカンマ区切りで指定する必要があります。typesまたはstructureのいずれかが必要です |
| structure | str or seq of str | データ内のカラム名 + データ型の一覧です (例を参照) 。structureまたはtypesのいずれかが必要です |
| mime_type | str | ファイルデータの省略可能なMIMEタイプです。現在、ClickHouseはこのHTTPサブヘッダーを無視します |
directorsテーブルと結合するには、次のようにします。
add_file メソッドを使って、既存の ExternalData オブジェクトに追加できます。HTTP では、すべての外部データが multi-part/form-data によるファイルアップロードの一部として送信されます。
タイムゾーン
DateTime64 オブジェクトを常に、エポックである 1970-01-01 00:00:00 UTC からの秒数を表すタイムゾーンなしの数値として保存します。DateTime64 の値では、精度に応じて、エポックからのミリ秒、マイクロ秒、またはナノ秒で表現されます。そのため、タイムゾーン情報の適用は常にクライアント側で行われます。これには無視できない追加の計算コストがかかるため、パフォーマンスが重要なアプリケーションでは、ユーザー向け表示や変換を除き、DateTime 型はエポック timestamp として扱うことを推奨します (たとえば Pandas の Timestamps は、パフォーマンス向上のため、常にエポックのナノ秒を表す 64 ビット整数です) 。
クエリでタイムゾーン対応のデータ型を使用する場合、特に Python の datetime.datetime オブジェクトでは、clickhouse-connect は次の優先順位ルールに従ってクライアント側のタイムゾーンを適用します。
- クエリに
client_tzsクエリメソッド parameter が指定されている場合は、そのカラム固有のタイムゾーンが適用されます - ClickHouse のカラムに timezone metadata がある場合 (つまり、DateTime64(3, ‘America/Denver’) のような型である場合) 、ClickHouse のカラムのタイムゾーンが適用されます。 (なお、この timezone metadata は ClickHouse version 23.2 より前の DateTime カラムでは clickhouse-connect から利用できません)
- クエリに
query_tzクエリメソッド parameter が指定されている場合は、「クエリタイムゾーン」が適用されます。 - タイムゾーン設定がクエリまたは session に適用されている場合は、そのタイムゾーンが適用されます。 (この機能は ClickHouse server ではまだリリースされていません)
- 最後に、クライアントの
apply_server_timezoneparameter が True (デフォルト) に設定されている場合は、ClickHouse server のタイムゾーンが適用されます。
clickhouse-connect は 常に タイムゾーン情報を持たない Python の datetime.datetime オブジェクトを返す点に注意してください。その後、必要に応じて、このタイムゾーン情報を持たないオブジェクトにアプリケーションコード側で追加のタイムゾーン情報を付与できます。