ClickHouse Connect를 사용한 데이터 삽입: 고급 사용법
InsertContexts
ClickHouse Connect는 Native 형식 삽입 작업과 insert, insert_df 메서드를 InsertContext 내에서 실행합니다. insert_arrow, insert_df_arrow, raw_insert 메서드는 payload를 직접 전송하며 InsertContext를 사용하지 않습니다. InsertContext에는 클라이언트 insert 메서드에 인수로 전달되는 모든 값이 포함됩니다. 또한 InsertContext가 처음 생성될 때 ClickHouse Connect는 효율적인 Native 형식 삽입에 필요한 대상 컬럼의 데이터 타입을 가져옵니다. 여러 번의 삽입에 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] == 113InsertContexts에는 삽입 과정에서 갱신되는 가변 상태가 포함되어 있으므로 스레드에 안전하지 않습니다.
쓰기 포맷
쓰기 포맷은 일부 타입에 대해서만 구현되어 있습니다. 대부분의 경우 ClickHouse Connect는 컬럼의 첫 번째 NULL이 아닌 데이터 값을 기준으로 올바른 쓰기 포맷을 자동으로 결정합니다. 예를 들어 DateTime 컬럼의 첫 번째 값이 정수이면 클라이언트는 이를 epoch 초로 처리합니다.
일반적으로 쓰기 포맷을 재정의할 필요는 없지만, clickhouse_connect.datatypes.format의 메서드를 사용하면 전역으로 설정할 수 있습니다. Array, Nullable, LowCardinality와 같은 컨테이너 래퍼는 내부 타입의 포맷 동작을 그대로 유지합니다.
쓰기 포맷 옵션
| ClickHouse 유형 | 네이티브 Python 유형 | 쓰기 포맷 | 설명 |
|---|---|---|---|
| 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 | 하나의 컬럼에는 텍스트 또는 bytes 중 하나만 일관되게 포함되어야 합니다. | |
| FixedString | bytes | string | String 값은 0 바이트로 채워집니다. 빈 bytes는 모두 0 바이트로 기록됩니다. |
| Enum[8,16] | str or int | 레이블은 문자열 또는 해당 내부 정수 값으로 삽입합니다. | |
| Date | datetime.date | int | 정수 값은 1970-01-01 이후 경과한 일수로 해석됩니다. |
| Date32 | datetime.date | int | 정수 값은 부호 있는 일 오프셋으로 해석됩니다. |
| DateTime | datetime.datetime | int | 정수 값은 epoch 초로 해석됩니다. |
| DateTime64 | datetime.datetime | int | 정수 값은 컬럼 precision 기준의 틱으로 해석됩니다. |
| Time | datetime.timedelta | int, string, time | 정수 값은 초 단위로 해석됩니다. |
| Time64 | datetime.timedelta | int, string, time | 정수 값은 컬럼 precision 기준의 틱으로 해석됩니다. |
| 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 데이터프레임을 컬럼 지향 네이티브 데이터로 삽입합니다. 명시적인 컬럼명/타입 또는 재사용 가능한InsertContext도 지원합니다.insert_arrow– ClickHouse Arrow 입력 형식을 사용하여 PyArrow Table을 삽입합니다.insert_df_arrow– Arrow 기반 Pandas DataFrame 또는 Polars DataFrame을 삽입합니다. Pandas 컬럼은 모두 Arrow 기반 dtype을 사용해야 합니다.
세 가지 메서드 모두 database, settings, 그리고 요청별 HTTP transport_settings를 허용합니다.
Pandas 데이터프레임 삽입
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 문을 생성합니다. 이 매핑은 signed 및 unsigned 정수, 부동소수점 값, 불리언, 문자열, 날짜, 타임스탬프를 지원합니다. 의도적으로 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는 값을 epoch로 변환하기 전에 대상 tzinfo를 적용합니다. IANA 시간대에는 일광 절약 시간 전환에 관한 표준 라이브러리 규칙을 따릅니다. 가을철 중복 구간에서는 datetime's fold 값을 사용합니다. 기본값인 fold=0은 전환 전 오프셋을 선택하고, fold=1은 전환 후 오프셋을 선택합니다. 봄철 공백 구간에도 동일한 오프셋 선택이 적용되며, 거부되거나 정규화되지 않습니다.
존재하지 않는 봄철 공백 구간의 wall time은 ClickHouse 텍스트 파싱에서 다른 오프셋이 선택될 수 있으므로 wall-mode 쿼리 매개변수를 거치면 왕복 변환되지 않을 수 있습니다. 특정 시점이 중요하다면 시간대 정보를 포함하는 datetime 또는 유효한 wall time을 사용하십시오.
이 옵션은 datetime 값의 네이티브 Python 객체 삽입과 DateTime64에서 허용하는 시간대 정보가 없는 ISO 문자열에만 적용됩니다. 시간대 정보가 없는 datetime64-dtype NumPy 및 Pandas 컬럼은 기존 UTC wall time 변환을 유지합니다.
두 모드와 관계없이 특정 시점을 나타내려면 의도한 시간대를 적용하거나 epoch 정수를 명시적으로 제공하십시오.
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" 모드에서는 호스트 로컬 시간으로 변환하지 않고 wall 필드를 전송합니다. 매개변수 인수 섹션을 참조하십시오.
시간대 메타데이터가 있는 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에 맡깁니다.
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
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 인수를 참조하십시오. |
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")비동기 헬퍼는 raw_insert를 await하기 전에 worker thread에서 파일을 읽기 때문에, 파일 내용이 메모리에 유지됩니다.