> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-trino-dialect.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ClickHouse Connect를 사용한 고급 데이터 삽입

# 고급 데이터 삽입

<div id="inserting-data-with-clickhouse-connect--advanced-usage">
  ## ClickHouse Connect를 사용한 데이터 삽입: 고급 사용법
</div>

<div id="insertcontexts">
  ### InsertContexts
</div>

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` 속성만 수정해야 합니다. 이는 동일한 테이블에 새 데이터를 반복적으로 삽입할 때 재사용 가능한 객체를 제공하려는 목적에 부합합니다.

```python theme={null}
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] == 113
```

`InsertContext`s에는 삽입 과정에서 갱신되는 가변 상태가 포함되어 있으므로 스레드에 안전하지 않습니다.

<div id="write-formats">
  ### 쓰기 포맷
</div>

쓰기 포맷은 일부 타입에 대해서만 구현되어 있습니다. 대부분의 경우 ClickHouse Connect는 컬럼의 첫 번째 NULL이 아닌 데이터 값을 기준으로 올바른 쓰기 포맷을 자동으로 결정합니다. 예를 들어 `DateTime` 컬럼의 첫 번째 값이 정수이면 클라이언트는 이를 epoch 초로 처리합니다.

일반적으로 쓰기 포맷을 재정의할 필요는 없지만, `clickhouse_connect.datatypes.format`의 메서드를 사용하면 전역으로 설정할 수 있습니다. `Array`, `Nullable`, `LowCardinality`와 같은 컨테이너 래퍼는 내부 타입의 포맷 동작을 그대로 유지합니다.

<div id="write-format-options">
  #### 쓰기 포맷 옵션
</div>

| 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가 자동으로 사용됩니다.                                                             |

<div id="specialized-insert-methods">
  ### 특수화된 삽입 메서드
</div>

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`를 허용합니다.

<Note>
  NumPy array는 유효한 시퀀스(Sequence)의 시퀀스이므로 기본 `insert` 메서드의 `data` 인수로 사용할 수 있습니다. 따라서 별도의 특수화된 메서드는 필요하지 않습니다.
</Note>

<div id="pandas-dataframe-insert">
  #### Pandas 데이터프레임 삽입
</div>

```python theme={null}
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)
```

<div id="pyarrow-table-insert">
  #### PyArrow Table 삽입
</div>

```python theme={null}
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)
```

<div id="arrow-backed-dataframe-insert-pandas-2">
  #### Arrow 기반 DataFrame 삽입 (pandas 2.x)
</div>

```python theme={null}
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)
```

<div id="create-table-from-pyarrow-schema">
  ### PyArrow 스키마에서 테이블 만들기
</div>

`create_table_from_arrow_schema`는 일반적인 스칼라 Arrow 필드를 바탕으로 `CREATE TABLE` 문을 생성합니다. 이 매핑은 signed 및 unsigned 정수, 부동소수점 값, 불리언, 문자열, 날짜, 타임스탬프를 지원합니다. 의도적으로 NULL을 허용하지 않는 ClickHouse 컬럼을 생성하며, 지원되지 않는 Arrow 타입에는 `TypeError`를 발생시키므로 실행하기 전에 생성된 DDL을 검토하십시오.

```python theme={null}
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)
```

<div id="time-zones">
  ### 시간대
</div>

Python `datetime` 객체를 `DateTime` 또는 `DateTime64` 컬럼에 삽입하면, ClickHouse Connect가 이를 epoch 값으로 변환합니다.

<div id="timezone-aware-datetime-objects">
  #### 시간대 정보를 포함한 datetime 객체
</div>

시간대 정보를 포함한 객체는 나타내는 시점을 그대로 유지합니다. 소스 시간대가 ClickHouse 컬럼에 선언된 시간대와 일치할 필요는 없습니다.

```python theme={null}
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]
```

<Note>
  ClickHouse Connect는 표준 라이브러리 `zoneinfo` 모듈을 사용합니다. 드라이버는 더 이상 `pytz`에 의존하지 않습니다.
</Note>

<div id="timezone-naive-datetime-objects">
  #### 시간대 정보가 없는 datetime 객체
</div>

전역 `naive_datetime_insert` 설정은 시간대 정보가 없는 `datetime` 값의 네이티브 Python 객체 삽입 방식을 제어합니다. 또한 `DateTime64` 컬럼에서 허용하는 시간대 정보가 없는 ISO 문자열에도 적용됩니다.

* `"local"`은 1.x의 기본값입니다. Python은 `.timestamp()`가 호출될 때 프로세스 시간대에 따라 값을 해석합니다. 이 설정은 기존 동작을 유지합니다.
* `"server"`는 `DateTime` 또는 `DateTime64` 컬럼에 선언된 시간대의 실제 시각으로 값을 해석합니다. 컬럼에 시간대가 없으면 클라이언트 연결 시 보고된 서버 시간대를 사용합니다.

삽입 전에 옵션을 설정하십시오. Python `datetime` 객체 또는 `DateTime64` ISO 문자열이 포함된 각 네이티브 삽입 컬럼을 직렬화할 때 이 옵션을 읽으므로, 변경 사항은 기존 클라이언트와 재사용 가능한 삽입 컨텍스트에도 적용됩니다.

```python theme={null}
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 정수를 명시적으로 제공하십시오.

```python theme={null}
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 필드를 전송합니다. [매개변수 인수](/ko/integrations/language-clients/python/driver-api#parameters-argument) 섹션을 참조하십시오.

<div id="datetime-columns-with-timezone-metadata">
  #### 시간대 메타데이터가 있는 DateTime 컬럼
</div>

ClickHouse 컬럼은 `DateTime('America/Denver')` 또는 `DateTime64(3, 'Asia/Tokyo')`처럼 시간대 메타데이터를 선언할 수 있습니다. 이 메타데이터는 쿼리 시 값이 어떻게 표시되는지를 제어합니다.

시간대 인식 값을 삽입할 때 ClickHouse Connect는 해당 값이 나타내는 시점을 유지합니다. 시간대 정보가 없는 값의 경우 `naive_datetime_insert` 설정이 프로세스 시간대와 컬럼 시간대 중 어느 것을 사용할지 제어합니다. 쿼리하면 `column_tzs` 인수로 컬럼별 재정의를 지정하지 않는 한 해당 컬럼의 시간대가 결과에 적용됩니다. `query_tz` 인수는 컬럼에 선언된 시간대를 재정의하지 않습니다.

```python theme={null}
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")
```

<div id="file-inserts">
  ## 파일 삽입
</div>

`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 인수](/ko/integrations/language-clients/python/driver-api#settings-argument-1)를 참조하십시오. |
| `compression`  | str            | `None`                      | `"zstd"`, `"lz4"`, `"gzip"`와 같은 기존 파일 압축 형식입니다. gzip은 파일 이름이 `.gz` 또는 `.gzip`이면 자동으로 추론됩니다.     |

`input_format_allow_errors_ratio`, `input_format_allow_errors_num`와 같은 입력 형식 설정은 `settings`를 통해 전달할 수 있습니다.

```python theme={null}
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하세요:

```python theme={null}
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에서 파일을 읽기 때문에, 파일 내용이 메모리에 유지됩니다.
