ClickHouse Connectを使ったデータの挿入: 高度な使い方
InsertContexts
ClickHouse Connect は、Native-format の挿入である insert および insert_df メソッドを、InsertContext 内で実行します。insert_arrow、insert_df_arrow、raw_insert の各メソッドはペイロードを直接送信するため、これを使用しません。InsertContext には、クライアントの insert メソッドに引数として渡すすべての値が含まれます。さらに、InsertContext の初回作成時には、効率的な Native format での挿入に必要な対象カラムのデータ型を ClickHouse Connect が取得します。複数回の挿入で InsertContext を再利用すると、この"事前クエリ"を省略できるため、挿入をより高速かつ効率的に実行できます。
InsertContext は、クライアントの create_insert_context メソッドを使って取得できます。このメソッドは、context 自体を除き、insert 関数と同じ引数を受け取ります。再利用時に変更すべきなのは、InsertContext の data プロパティだけである点に注意してください。これは、同じテーブルに新しいデータを繰り返し挿入するための再利用可能なオブジェクトを提供するという、本来の目的に沿ったものです。
test_data = [[13, "v1", "v2"], [79, "v3", "v4"]]
ic = client.create_insert_context(table="test_table", data=test_data)
client.insert(context=ic)
assert client.command("SELECT count() FROM test_table") == 2
new_data = [[101, "v5", "v6"], [113, "v7", "v8"]]
ic.data = new_data
client.insert(context=ic)
qr = client.query("SELECT * FROM test_table ORDER BY key DESC")
assert qr.row_count == 4
assert qr.first_row[0] == 113InsertContextには、挿入処理中に更新される可変状態が含まれているため、スレッドセーフではありません。
書き込みフォーマット
書き込みフォーマットは、一部の型に対してのみ実装されています。ほとんどの場合、ClickHouse Connect はカラムの最初の非 NULL 値に基づいて、適切な書き込みフォーマットを自動的に判定します。たとえば、DateTime カラムの最初の値が整数であれば、クライアントはそれをエポック秒として扱います。
通常、書き込みフォーマットを上書きする必要はありませんが、clickhouse_connect.datatypes.format のメソッドを使うとグローバルに設定できます。Array、Nullable、LowCardinality などのコンテナーラッパーは、要素型のフォーマット動作を保持します。
書き込みフォーマットのオプション
| ClickHouse Type | Native Python Type | 書き込みフォーマット | Comments |
|---|---|---|---|
| Int[8-64], UInt[8-32] | int | ||
| UInt64 | int | ||
| [U]Int[128,256] | int | ||
| BFloat16 | float | ||
| Float32 | float | ||
| Float64 | float | ||
| Decimal | decimal.Decimal | ||
| String | str or bytes | 1つのカラムには、テキストまたはバイト列のいずれか一方だけを一貫して含める必要があります。 | |
| FixedString | bytes | string | 文字列値はゼロバイトで埋められます。空のバイト列はすべてゼロバイトとして書き込まれます。 |
| Enum[8,16] | str or int | ラベルは文字列、または対応する整数値として挿入します。 | |
| Date | datetime.date | int | 整数値は、1970-01-01 からの日数として解釈されます。 |
| Date32 | datetime.date | int | 整数値は、符号付きの日オフセットとして解釈されます。 |
| DateTime | datetime.datetime | int | 整数値は、エポック秒として解釈されます。 |
| DateTime64 | datetime.datetime | int | 整数値は、カラムの精度におけるティックとして解釈されます。 |
| Time | datetime.timedelta | int, string, time | 整数値は、秒として解釈されます。 |
| Time64 | datetime.timedelta | int, string, time | 整数値は、カラムの精度におけるティックとして解釈されます。 |
| IPv4 | ipaddress.IPv4Address |
string | 正しい形式の文字列は IPv4 アドレスとして挿入できます |
| IPv6 | ipaddress.IPv6Address |
string | 正しい形式の文字列は IPv6 アドレスとして挿入できます |
| Tuple | dict or tuple | ||
| Map | dict | ||
| Nested | Sequence[dict] | ||
| UUID | uuid.UUID | string | 正しい形式の文字列は ClickHouse UUID として挿入できます |
| JSON | dict | string | 辞書および JSONオブジェクト文字列がサポートされます。従来の Object('json') 型はサポートされていません。 |
| Variant | object | 値には各メンバー型のネイティブなシリアライゼーションが使用されます。Python の型が曖昧な場合は clickhouse_connect.datatypes.dynamic.typed_variant を使用してください。 |
|
| Dynamic | object | 値は現在、String 表現を介して挿入されます。 | |
| QBit | Sequence[float] | インストールされている場合、より高速なビット転置のために NumPy が自動的に使用されます。 |
専用の 挿入 メソッド
ClickHouse Connect では、一般的なデータフォーマット向けに専用の 挿入 メソッドが用意されています。
insert_df– Pandas DataFrame をカラム指向の Native データとして 挿入 します。明示的なカラム名 / 型、または再利用可能なInsertContextもサポートします。insert_arrow– ClickHouse の Arrow input format を使用して PyArrow Table を 挿入 します。insert_df_arrow– Arrow ベースの Pandas DataFrame または Polars DataFrame を 挿入 します。Pandas のカラムはすべて Arrow ベースの dtype を使用している必要があります。
これら 3 つのメソッドはいずれも、database、settings、およびリクエストごとの HTTP transport_settings を受け付けます。
Pandas DataFrame の挿入
import clickhouse_connect
import pandas as pd
client = clickhouse_connect.get_client()
df = pd.DataFrame({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
})
client.insert_df("users", df)PyArrow Table を使った挿入
import clickhouse_connect
import pyarrow as pa
client = clickhouse_connect.get_client()
arrow_table = pa.table({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
})
client.insert_arrow("users", arrow_table)ArrowベースのDataFrame挿入 (pandas 2.x)
import clickhouse_connect
import pandas as pd
client = clickhouse_connect.get_client()
# Convert to Arrow-backed dtypes for better performance
df = pd.DataFrame({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
}).convert_dtypes(dtype_backend="pyarrow")
client.insert_df_arrow("users", df)PyArrow スキーマからテーブルを作成する
create_table_from_arrow_schema は、一般的なスカラー Arrow フィールドから CREATE TABLE ステートメントを生成します。このマッピングは、符号付き整数、符号なし整数、浮動小数点値、ブール値、文字列、日付、タイムスタンプをカバーします。意図的に NULL を許容しない ClickHouse カラムを作成し、サポートされていない Arrow 型に対しては TypeError を送出するため、実行前に生成された DDL を確認してください。
import clickhouse_connect
import pyarrow as pa
from clickhouse_connect.driver.ddl import create_table_from_arrow_schema
client = clickhouse_connect.get_client()
schema = pa.schema(
[
("id", pa.uint32()),
("name", pa.string()),
("event_time", pa.timestamp("ms", tz="UTC")),
]
)
ddl = create_table_from_arrow_schema(
table_name="arrow_events",
schema=schema,
engine="MergeTree",
engine_params={"ORDER BY": "id"},
)
client.command(ddl)タイムゾーン
Python の datetime オブジェクトを DateTime または DateTime64 カラムに挿入する際、ClickHouse Connect はそれらを epoch 値に変換します。
タイムゾーン対応の datetime オブジェクト
タイムゾーン対応のオブジェクトは、表している時点を保持します。ソースのタイムゾーンは、ClickHouse のカラムで宣言されているタイムゾーンと一致している必要はありません。
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
client.command("CREATE TABLE events (event_time DateTime) ENGINE Memory")
data = [
[datetime(2023, 6, 15, 10, 30, tzinfo=timezone.utc)],
[datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("America/Denver"))],
[datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("Asia/Tokyo"))],
]
client.insert("events", data, column_names=["event_time"])
results = client.query(
"SELECT event_time FROM events ORDER BY event_time",
query_tz="UTC",
tz_mode="aware",
)
assert [row[0].hour for row in results.result_rows] == [1, 10, 16]タイムゾーン情報を持たない datetime オブジェクト
グローバル設定 naive_datetime_insert は、タイムゾーン情報を持たない datetime 値をネイティブ Python オブジェクトとして挿入する際の動作を制御します。また、DateTime64 カラムで受け付けられるタイムゾーン情報を持たない ISO 文字列にも適用されます。
"local"は 1.x でのデフォルトです。Python は.timestamp()の呼び出し時に、プロセスのタイムゾーンで値を解釈します。これにより既存の動作が維持されます。"server"は、値をDateTimeまたはDateTime64カラムで宣言されたタイムゾーンの実時間として解釈します。カラムにタイムゾーンが指定されていない場合は、クライアント接続時に取得したサーバーのタイムゾーンを使用します。
挿入前にこのオプションを設定してください。Python の datetime オブジェクトまたは DateTime64 ISO 文字列を含むネイティブ挿入の各カラムをシリアル化する際に読み取られるため、変更は既存のクライアントおよび再利用可能な挿入コンテキストにも適用されます。
from datetime import datetime
from clickhouse_connect import common
common.set_setting("naive_datetime_insert", "server")
naive_time = datetime(2023, 6, 15, 10, 30)
client.insert("events", [[naive_time]], column_names=["event_time"])"server" では、ClickHouse Connect は値をエポックに変換する前に、対象の tzinfo を付与します。IANA タイムゾーンでは、夏時間切り替えに関する標準ライブラリの規則に従います。秋の重複時間では、datetime の fold 値が使用されます。デフォルトの fold=0 は切り替え前のオフセットを選択し、fold=1 は切り替え後のオフセットを選択します。春のギャップでも同じオフセット選択が使用され、拒否や正規化は行われません。
春のギャップにある存在しない実時間は、ClickHouse のテキストパースで別のオフセットが選択される可能性があるため、実時間モードのクエリパラメータを介してラウンドトリップできない場合があります。時点が重要な場合は、タイムゾーン対応の datetime または有効な実時間を使用してください。
このオプションは、datetime 値をネイティブ Python オブジェクトとして insert する場合と、DateTime64 が受け付けるタイムゾーン情報のない ISO 文字列にのみ適用されます。タイムゾーン情報のない datetime64 dtype の NumPy および Pandas カラムでは、既存の UTC 実時間変換が維持されます。
どちらのモードにも依存せずに特定の時点を表すには、意図したタイムゾーンを付与するか、エポック整数を明示的に指定してください。
from datetime import datetime, timezone
utc_time = datetime(2023, 6, 15, 10, 30, tzinfo=timezone.utc)
client.insert("events", [[utc_time]], column_names=["event_time"])
naive_time = datetime(2023, 6, 15, 10, 30)
epoch_timestamp = int(naive_time.replace(tzinfo=timezone.utc).timestamp())
client.insert("events", [[epoch_timestamp]], column_names=["event_time"])タイムゾーン情報を持たない datetime のクエリパラメータには、個別の naive_datetime_binding 設定が使用されます。デフォルトの "wall" モードでは、ホストのローカル時刻への変換を行わずに日時フィールドが送信されます。Parameters argument セクションを参照してください。
タイムゾーン メタデータを持つ DateTime カラム
ClickHouse のカラムでは、DateTime('America/Denver') や DateTime64(3, 'Asia/Tokyo') のようにタイムゾーン メタデータを宣言できます。このメタデータは、クエリ時に値がどのように表示されるかを制御します。
タイムゾーン対応の値を挿入する際、ClickHouse Connect はその値が表す時点を保持します。タイムゾーン情報のない値の場合、naive_datetime_insert 設定によってプロセスのタイムゾーンとカラムのタイムゾーンのどちらを使用するかが決まります。クエリ時には、column_tzs 引数でカラムごとの上書きを指定しない限り、結果にはそのカラムのタイムゾーンが使用されます。query_tz 引数では、カラムに宣言されたタイムゾーンは上書きされません。
from datetime import datetime
from zoneinfo import ZoneInfo
client.command(
"CREATE TABLE events_with_timezone "
"(event_time DateTime('America/Los_Angeles')) "
"ENGINE Memory"
)
data = datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("America/New_York"))
client.insert("events_with_timezone", [[data]], column_names=["event_time"])
result = client.query("SELECT event_time FROM events_with_timezone")
returned = result.first_row[0]
assert returned.hour == 7
assert returned.tzinfo == ZoneInfo("America/Los_Angeles")ファイルの挿入
clickhouse_connect.driver.tools.insert_file はローカルファイルを既存のテーブルにストリームで挿入し、パースは ClickHouse に委ねます。
| Parameter | Type | Default | Description |
|---|---|---|---|
client |
Client |
必須 | 挿入に使用する同期 Client。 |
table |
str | 必須 | 単純名、またはデータベース修飾されたターゲットテーブル。 |
file_path |
str | 必須 | 入力ファイルへのローカルパス。 |
fmt |
str | "CSV" or "CSVWithNames" |
入力フォーマット。column_names が指定されている場合のデフォルトは "CSV"、それ以外の場合は "CSVWithNames" です。 |
column_names |
Sequence[str] | None |
ファイル内のカラム。名前を含むフォーマットでは不要です。 |
database |
str | None |
テーブルが修飾されていない場合のターゲットデータベース。 |
settings |
dict | None |
Settings argument を参照してください。 |
compression |
str | None |
"zstd"、"lz4"、"gzip" などの既存のファイル圧縮。gzip は .gz および .gzip のファイル名から推論されます。 |
input_format_allow_errors_ratio や input_format_allow_errors_num などの入力フォーマット設定は、settings 経由で渡せます。
import clickhouse_connect
from clickhouse_connect.driver.tools import insert_file
client = clickhouse_connect.get_client()
insert_file(
client,
"example_table",
"my_data.csv",
settings={
"input_format_allow_errors_ratio": 0.2,
"input_format_allow_errors_num": 5,
},
)AsyncClient の場合は、同じ引数を使って insert_file_async を await します:
from clickhouse_connect.driver.tools import insert_file_async
await insert_file_async(async_client, "example_table", "my_data.csv")async ヘルパーは、raw_insert を await する前にワーカースレッドでファイルを読み込むため、ファイルの内容はメモリ上に保持されます。