> ## 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="querycontexts">
  ## QueryContexts
</div>

ClickHouse Connect는 표준 쿼리를 `QueryContext` 내에서 실행합니다. `QueryContext`에는 ClickHouse 데이터베이스에 대해 쿼리를 구성하는 데 사용되는 핵심 데이터 구조와, 결과를 `QueryResult` 또는 다른 응답 데이터 구조로 처리하는 데 사용되는 구성이 포함됩니다. 여기에는 쿼리 자체, 매개변수, 설정, 읽기 포맷, 기타 속성이 포함됩니다.

`QueryContext`는 클라이언트의 `create_query_context` 메서드를 사용해 생성할 수 있습니다. 이 메서드는 핵심 쿼리 메서드와 동일한 매개변수를 받습니다. 이렇게 생성한 쿼리 컨텍스트는 `query`, `query_df`, `query_np` 메서드에 `context` 키워드 인수로 전달할 수 있으며, 이때 해당 메서드의 다른 인수 일부 또는 전체를 대체할 수 있습니다. 메서드 호출 시 추가로 지정한 인수는 QueryContext의 속성을 재정의합니다.

`QueryContext`의 가장 명확한 사용 사례는 바인딩 매개변수 값만 바꿔 같은 쿼리를 보내는 것입니다. 모든 매개변수 값은 딕셔너리를 사용해 `QueryContext.set_parameters` 메서드를 호출하여 업데이트할 수 있으며, 개별 값은 원하는 `key`, `value` 쌍과 함께 `QueryContext.set_parameter`를 호출하여 업데이트할 수 있습니다.

```python theme={null}
qc = client.create_query_context(
    query="SELECT {k:Int32}",
    parameters={"k": 13},
)
result = client.query(context=qc)
assert result.first_row == (13,)

qc.set_parameter("k", 79)
result = client.query(context=qc)
assert result.first_row == (79,)
```

`QueryContext`는 스레드 안전하지 않으므로, 멀티스레드 환경에서는 `QueryContext.updated_copy` 메서드를 호출해 복사본을 얻을 수 있다는 점에 유의하십시오.

<div id="streaming-queries">
  ## 스트리밍 쿼리
</div>

ClickHouse Connect Client는 데이터를 스트림으로 가져오는 여러 메서드를 제공합니다(Python generator로 구현됨).

* `query_column_block_stream` -- 네이티브 Python 객체를 사용해 쿼리 데이터를 컬럼 시퀀스 형태의 블록으로 반환합니다
* `query_row_block_stream` -- 네이티브 Python 객체를 사용해 쿼리 데이터를 행 블록으로 반환합니다
* `query_rows_stream` -- 네이티브 Python 객체를 사용해 쿼리 데이터를 행 시퀀스로 반환합니다
* `query_np_stream` -- 쿼리 데이터의 각 ClickHouse 블록을 NumPy 배열로 반환합니다
* `query_df_stream` -- 쿼리 데이터의 각 ClickHouse 블록을 Pandas 데이터프레임으로 반환합니다
* `query_arrow_stream` -- 쿼리 데이터를 PyArrow `RecordBatch` 객체로 반환합니다
* `query_df_arrow_stream` -- 각 Arrow 배치를 `dataframe_library`에서 선택한 Pandas 또는 Polars 데이터프레임으로 반환합니다

각 메서드는 `with` 문으로 열어야 하는 `StreamContext`를 반환합니다. async 클라이언트 스트리밍 메서드는 `await`로 대기한 후 `async with`로 엽니다.

<div id="data-blocks">
  ### 데이터 블록
</div>

ClickHouse Connect는 기본 `query` 메서드의 모든 데이터를 ClickHouse 서버에서 수신한 블록 스트림으로 처리합니다. 이러한 블록은 ClickHouse와 주고받을 때 사용자 정의 "Native" 포맷으로 전송됩니다. "블록"은 바이너리 데이터 컬럼의 시퀀스이며, 각 컬럼에는 지정된 데이터 타입의 값이 동일한 개수로 들어 있습니다. (컬럼형 데이터베이스인 ClickHouse는 이 데이터를 유사한 형태로 저장합니다.) 쿼리에서 반환되는 블록 크기는 여러 수준(사용자 프로필, 사용자, 세션 또는 쿼리)에서 설정할 수 있는 두 가지 사용자 설정에 따라 결정됩니다. 다음과 같습니다.

* [max\_block\_size](/ko/reference/settings/session-settings#max_block_size) -- 행 수 기준 최대 블록 크기입니다.
* [preferred\_block\_size\_bytes](/ko/reference/settings/session-settings#preferred_block_size_bytes) -- 바이트 기준 선호 블록 크기입니다.

`preferred_block_size_bytes`와 관계없이 각 블록은 `max_block_size`행을 초과하지 않습니다. 실제 크기는 더 작을 수 있으며, 고정된 값으로 간주해서는 안 됩니다.

Client `query_*_stream` 메서드 중 하나를 사용하면 결과가 블록 단위로 반환됩니다. ClickHouse Connect는 한 번에 하나의 블록만 로드합니다. 따라서 큰 result set 전체를 메모리에 로드하지 않고도 대량의 데이터를 처리할 수 있습니다. 애플리케이션은 블록 수에 관계없이 처리할 수 있도록 준비되어 있어야 하며, 각 블록의 정확한 크기는 제어할 수 없다는 점에 유의하십시오.

<div id="http-data-buffer-for-slow-processing">
  ### 느린 처리 시 HTTP 데이터 버퍼
</div>

애플리케이션이 서버가 생성하는 블록을 훨씬 더 느리게 읽어들이면 처리가 완료되기 전에 HTTP 연결이 닫힐 수 있습니다. 애플리케이션에 더 많은 응답 데이터를 버퍼링할 수 있을 만큼 충분한 메모리가 있다면 공통 `http_buffer_size` 설정 값을 늘리십시오. 기본값은 10 MiB입니다. 이 버퍼에서는 lz4 및 zstd 응답 바이트가 압축된 상태로 유지되므로 실질적인 용량이 커집니다.

<div id="streamcontexts">
  ### StreamContexts
</div>

`query_*_stream` 메서드(예: `query_row_block_stream`)는 각각 Python 컨텍스트와 제너레이터가 결합된 ClickHouse `StreamContext` 객체를 반환합니다. 기본 사용법은 다음과 같습니다.

```python theme={null}
with client.query_row_block_stream(
    "SELECT pickup, dropoff, pickup_longitude, pickup_latitude FROM taxi_trips"
) as stream:
    for block in stream:
        for row in block:
            process_trip(row)
```

`with` 문과 함께 사용하지 않고 `StreamContext`를 사용하려고 하면 오류가 발생한다는 점에 유의하십시오. Python 컨텍스트를 사용하면 스트림(이 경우 스트리밍 HTTP 응답)이 모든 데이터가 소비되지 않거나 처리 중 예외가 발생하더라도 올바르게 닫히도록 보장됩니다. 또한 `StreamContext`는 스트림 소비에 한 번만 사용할 수 있습니다. `StreamContext`가 종료된 후 다시 사용하려고 하면 `StreamClosedError`가 발생합니다.

결과를 읽는 동안 연결이 실패하면 잘린 결과를 조용히 반환하는 대신 `StreamFailureError`가 발생합니다. 해당 메시지는 클라이언트의 `show_clickhouse_errors` 설정을 따릅니다.

`StreamContext`의 `source` 속성을 사용하면 상위 결과 객체에 접근할 수 있으며, 여기에는 컬럼 이름과 타입이 포함됩니다. 대부분의 스트림에서는 이것이 `QueryResult`이며, `query_np_stream` 및 `query_df_stream` 메서드는 대신 `NumpyResult`를 노출합니다.

<div id="stream-types">
  ### 스트림 유형
</div>

`query_column_block_stream` 메서드는 블록을 네이티브 Python 데이터 타입으로 저장된 컬럼 데이터 시퀀스로 반환합니다. 위의 `taxi_trips` 쿼리를 사용하면, 반환되는 데이터는 각 요소가 해당 컬럼의 모든 데이터를 담은 또 다른 리스트(또는 튜플)인 리스트입니다. 따라서 `block[0]`은 문자열만 담고 있는 튜플이 됩니다. 컬럼 지향 포맷은 총 운임을 합산하는 것처럼 특정 컬럼의 모든 값에 대해 집계 연산을 수행할 때 가장 많이 사용됩니다.

`query_row_block_stream` 메서드는 블록을 전통적인 관계형 데이터베이스처럼 행 시퀀스로 반환합니다. 택시 운행 데이터의 경우, 반환되는 데이터는 각 요소가 데이터의 한 행을 나타내는 또 다른 리스트인 리스트입니다. 따라서 `block[0]`에는 첫 번째 택시 운행의 모든 필드가 순서대로 포함되고, `block[1]`에는 두 번째 택시 운행의 모든 필드가 포함된 행이 들어가며, 이후에도 같은 방식으로 이어집니다. 행 지향 결과는 일반적으로 표시 또는 변환 작업에 사용됩니다.

`query_rows_stream` 메서드는 자동으로 다음 블록으로 이동하면서 한 번에 한 행씩 반환합니다. 이 메서드는 `query_row_block_stream`의 행 단위 대응 메서드입니다.

`query_np_stream` 메서드는 각 블록을 NumPy 배열로 반환합니다. 모든 결과 컬럼이 동일한 NumPy dtype을 공유하면, 배열은 shape가 `(rows, columns)`인 2차원 배열이 됩니다. 결과 타입이 혼합된 경우에는 1차원 구조화 배열로 반환되거나 object dtype이 사용됩니다.

`query_df_stream` 메서드는 각 ClickHouse Block을 2차원 Pandas 데이터프레임으로 반환합니다. 다음은 `StreamContext` 객체를 지연된 방식으로 컨텍스트로 사용할 수 있음을 보여주는 예시입니다(단, 한 번만 사용할 수 있습니다).

```python theme={null}
df_stream = client.query_df_stream("SELECT * FROM hits")
column_names = df_stream.source.column_names
with df_stream:
    for df in df_stream:
        process_dataframe(df)
```

`query_df_arrow_stream` 메서드는 Arrow batch를 Pandas 또는 Polars 데이터프레임으로 변환합니다. 라이브러리는 `dataframe_library`로 선택하며, 기본값은 `"pandas"`입니다.

마지막으로, `query_arrow_stream` 메서드는 ClickHouse `ArrowStream` 응답을 `StreamContext`로 래핑합니다. 각 반복에서는 PyArrow `RecordBatch`를 반환합니다.

<div id="streaming-examples">
  ### 스트리밍 예시
</div>

<div id="stream-rows">
  #### 행 단위로 스트리밍
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream large result sets row by row
with client.query_rows_stream("SELECT number, number * 2 as doubled FROM system.numbers LIMIT 100000") as stream:
    for row in stream:
        print(row)  # Process each row
        # Output:
        # (0, 0)
        # (1, 2)
        # (2, 4)
        # Additional rows follow
```

<div id="stream-row-blocks">
  #### 행 블록 스트리밍하기
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream in blocks of rows (more efficient than row-by-row)
with client.query_row_block_stream("SELECT number, number * 2 FROM system.numbers LIMIT 100000") as stream:
    for block in stream:
        print(f"Received block with {len(block)} rows")
```

<div id="stream-pandas-dataframes">
  #### Pandas 데이터프레임 스트리밍
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream query results as Pandas DataFrames
with client.query_df_stream("SELECT number, toString(number) AS str FROM system.numbers LIMIT 100000") as stream:
    for df in stream:
        # Process each DataFrame block
        print(f"Received DataFrame with {len(df)} rows")
        print(df.head(3))
```

<div id="stream-arrow-batches">
  #### Arrow 배치 스트리밍
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Stream query results as Arrow record batches
with client.query_arrow_stream("SELECT * FROM large_table") as stream:
    for arrow_batch in stream:
        # Process each Arrow batch
        print(f"Received Arrow batch with {arrow_batch.num_rows} rows")
```

<div id="async-stream-rows">
  #### 비동기 스트림의 행
</div>

```python theme={null}
import asyncio

import clickhouse_connect


async def main():
    async_client = await clickhouse_connect.get_async_client()
    async with await async_client.query_rows_stream(
        "SELECT number FROM numbers(100000)"
    ) as stream:
        async for row in stream:
            print(row)


asyncio.run(main())
```

<div id="numpy-pandas-and-arrow-queries">
  ## NumPy, Pandas, and Arrow 쿼리
</div>

ClickHouse Connect는 NumPy, Pandas, Arrow 데이터 구조를 다루기 위한 전용 쿼리 메서드를 제공합니다. 이러한 메서드를 사용하면 별도의 수동 변환 없이 쿼리 결과를 이러한 널리 사용되는 데이터 포맷으로 직접 가져올 수 있습니다.

<div id="numpy-queries">
  ### NumPy 쿼리
</div>

`query_np` 메서드는 쿼리 결과를 ClickHouse Connect `QueryResult` 대신 NumPy 배열로 반환합니다.

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a NumPy array
np_array = client.query_np("SELECT number, number * 2 AS doubled FROM system.numbers LIMIT 5")

print(type(np_array))
# Output:
# <class 'numpy.ndarray'>

print(np_array)
# Output:
# [[0 0]
#  [1 2]
#  [2 4]
#  [3 6]
#  [4 8]]
```

<div id="pandas-queries">
  ### Pandas 쿼리
</div>

`query_df` 메서드는 쿼리 결과를 ClickHouse Connect의 `QueryResult`가 아니라 Pandas 데이터프레임으로 반환합니다.

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a Pandas DataFrame
df = client.query_df("SELECT number, number * 2 AS doubled FROM system.numbers LIMIT 5")

print(type(df))
# Output: <class 'pandas.core.frame.DataFrame'>
print(df)
# Output:
#    number  doubled
# 0       0        0
# 1       1        2
# 2       2        4
# 3       3        6
# 4       4        8
```

<div id="pyarrow-queries">
  ### PyArrow 쿼리
</div>

`query_arrow` 메서드는 ClickHouse의 `Arrow` 출력 형식을 직접 사용하여 PyArrow Table을 반환합니다. 이 메서드는 `query`, `parameters`, `settings`, `external_data`, `transport_settings`를 인수로 받습니다. `use_strings` 옵션은 ClickHouse `String` 컬럼을 Arrow 문자열로 내보낼지, 아니면 바이너리 값으로 내보낼지를 제어합니다.

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a PyArrow Table
arrow_table = client.query_arrow("SELECT number, toString(number) AS str FROM system.numbers LIMIT 3")

print(type(arrow_table))
# Output:
# <class 'pyarrow.lib.Table'>

print(arrow_table)
# Output:
# pyarrow.Table
# number: uint64 not null
# str: string not null
# ----
# number: [[0,1,2]]
# str: [["0","1","2"]]
```

<div id="arrow-backed-dataframes">
  ### Arrow 기반 데이터프레임
</div>

ClickHouse Connect는 `query_df_arrow` 및 `query_df_arrow_stream`을 통해 Arrow 결과로부터 데이터프레임을 효율적으로 생성할 수 있도록 지원합니다. 이러한 메서드는 Python 행 객체로 변환하는 과정을 거치지 않으며, 대상 라이브러리에서 지원하는 경우 Arrow 버퍼를 재사용합니다:

* `query_df_arrow`: ClickHouse `Arrow` 출력 형식을 사용해 쿼리를 실행하고 데이터프레임을 반환합니다.
  * `dataframe_library="pandas"`는 `pd.ArrowDtype`을 사용하는 Pandas 2.0 이상 데이터프레임을 반환합니다.
  * `dataframe_library="polars"`는 `pl.from_arrow`를 통해 생성된 Polars 데이터프레임을 반환합니다.
* `query_df_arrow_stream`: Arrow 배치를 Pandas 또는 Polars 데이터프레임으로 스트리밍합니다.

<div id="query-to-arrow-backed-dataframe">
  #### 쿼리를 Arrow 기반 데이터프레임으로 변환
</div>

```python theme={null}
import clickhouse_connect

client = clickhouse_connect.get_client()

# Query returns a Pandas DataFrame with Arrow dtypes (requires pandas 2.x)
df = client.query_df_arrow(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 3",
    dataframe_library="pandas"
)

print(df.dtypes)
# Output:
# number    uint64[pyarrow]
# str       string[pyarrow]
# dtype: object

# Or use Polars
polars_df = client.query_df_arrow(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 3",
    dataframe_library="polars"
)
print(polars_df.dtypes)
# Output:
# [UInt64, String]

# Streaming into batches of DataFrames (polars shown)
with client.query_df_arrow_stream(
    "SELECT number, toString(number) AS str FROM system.numbers LIMIT 100000", dataframe_library="polars"
) as stream:
    for df_batch in stream:
        print(f"Received {type(df_batch)} batch with {len(df_batch)} rows and dtypes: {df_batch.dtypes}")
```

<div id="notes-and-caveats">
  #### 참고 사항 및 주의점
</div>

* ClickHouse가 Arrow 스키마를 제어합니다. Arrow에서 직접 표현할 수 없는 타입은 바이너리 필드를 포함한 호환 가능한 물리 타입으로 반환될 수 있습니다. 애플리케이션별 변환을 적용하기 전에 `table.schema` 또는 DataFrame dtype을 확인하십시오.
* Arrow 기반 Pandas 결과를 사용하려면 Pandas 2.0 이상이 필요합니다.
* `use_strings`는 서버가 `output_format_arrow_string_as_string`을 지원할 때 ClickHouse `String` 컬럼이 Arrow 문자열 필드 또는 바이너리 필드를 사용할지 제어합니다.
* `tz_mode="schema"`는 아직 Arrow 기반 쿼리 메서드에서 지원되지 않습니다. 이 메서드는 경고를 표시하고 Arrow 응답에서 제공된 시간대 메타데이터를 유지합니다.

<div id="read-formats">
  ## 읽기 포맷
</div>

읽기 포맷은 `query`, `query_np`, `query_df`가 반환하는 값을 제어합니다. raw 메서드나 Arrow 메서드에는 적용되지 않습니다. 이러한 메서드는 서버 출력 형식을 직접 사용하기 때문입니다. 예를 들어, UUID 읽기 포맷을 `"string"`으로 설정하면 `uuid.UUID` 객체 대신 UUID 문자열이 반환됩니다.

모든 formatting function의 "데이터 타입" 인수에는 와일드카드를 포함할 수 있습니다. 포맷은 소문자 문자열 하나로 지정합니다. `Array`, `Nullable`, `LowCardinality`와 같은 컨테이너 래퍼는 해당 타입에 대해 선택한 포맷을 유지합니다.

읽기 포맷은 여러 수준에서 설정할 수 있습니다.

* `clickhouse_connect.datatypes.format` 패키지에 정의된 메서드를 사용해 전역으로 설정할 수 있습니다. 그러면 구성된 데이터 타입의 포맷이 모든 쿼리에 적용됩니다.

```python theme={null}
from clickhouse_connect.datatypes.format import set_read_format

# Return both IPv6 and IPv4 values as strings
set_read_format("IPv*", "string")

# Return all Date types as the underlying epoch second or epoch day
set_read_format("Date*", "int")
```

* 전체 쿼리에는 선택적 `query_formats` 딕셔너리 인수를 사용할 수 있습니다. 이 경우 지정된 데이터 타입의 모든 컬럼(또는 하위 컬럼)에 구성된 포맷이 적용됩니다.

```python theme={null}
# Return any UUID column as a string
client.query(
    "SELECT user_id, user_uuid, device_uuid FROM users",
    query_formats={"UUID": "string"},
)
```

* 특정 결과 컬럼에는 선택 사항인 `column_formats` 딕셔너리를 사용합니다. 각 키는 반환된 컬럼명입니다. 값은 포맷 문자열이거나 ClickHouse 타입 이름을 포맷에 매핑하는 중첩 매핑이며, 이는 튜플, 맵, 기타 컨테이너 타입에 유용합니다.

```python theme={null}
# Return IPv6 values in the `dev_address` column as strings
client.query(
    "SELECT device_id, dev_address, gw_address FROM devices",
    column_formats={"dev_address": "string"},
)
```

<div id="read-format-options-python-types">
  ### 읽기 포맷 옵션 (Python 타입)
</div>

| ClickHouse 타입           | 네이티브 Python 타입          | 읽기 포맷             | 비고                                                                    |
| ----------------------- | ----------------------- | ----------------- | --------------------------------------------------------------------- |
| Int\[8-64], UInt\[8-32] | int                     | string            |                                                                       |
| UInt64                  | int                     | signed            | Superset는 현재 큰 unsigned UInt64 값을 처리하지 못합니다                           |
| \[U]Int\[128,256]       | int                     | string            | Pandas와 NumPy의 int 값은 최대 64비트이므로 문자열로 반환될 수 있습니다                      |
| BFloat16                | float                   | -                 | 모든 Python float는 내부적으로 64비트입니다                                        |
| Float32                 | float                   | string            | 모든 Python float는 내부적으로 64비트입니다                                        |
| Float64                 | float                   | string            |                                                                       |
| Decimal                 | decimal.Decimal         | -                 |                                                                       |
| String                  | str                     | bytes             | ClickHouse String 컬럼에는 고유한 인코딩이 없으므로 가변 길이 바이너리 데이터에도 사용됩니다           |
| FixedString             | bytes                   | string            | FixedString은 고정 크기의 바이트 배열이지만, 경우에 따라 Python 문자열로도 처리됩니다              |
| Enum\[8,16]             | str                     | int               | 네이티브 포맷은 레이블을 반환하며, `int`는 내부 정수값을 반환합니다.                             |
| Date                    | datetime.date           | int               | 정수 포맷은 1970-01-01부터의 일 수를 반환합니다.                                      |
| Date32                  | datetime.date           | int               | 정수 포맷은 더 넓은 signed 일 오프셋을 반환합니다.                                      |
| DateTime                | datetime.datetime       | int               | 정수 포맷은 epoch 초를 반환합니다.                                                |
| DateTime64              | datetime.datetime       | int               | 정수 포맷은 컬럼 precision 기준의 틱을 반환합니다. Python `datetime`은 마이크로초까지로 제한됩니다.  |
| Time                    | datetime.timedelta      | int, string, time | 정수 포맷은 초를 반환합니다. `time` 포맷은 `datetime.time`에 담을 수 있는 값으로 제한됩니다.       |
| Time64                  | datetime.timedelta      | int, string, time | 정수 포맷은 컬럼 precision 기준의 틱을 반환합니다. Python `timedelta`는 마이크로초까지로 제한됩니다. |
| IPv4                    | `ipaddress.IPv4Address` | string, int       | IP 주소는 문자열 또는 정수로 읽을 수 있습니다.                                          |
| IPv6                    | `ipaddress.IPv6Address` | string            | IP 주소는 문자열로 읽을 수 있으며, 올바른 포맷이면 IP 주소로 삽입할 수 있습니다                      |
| Tuple                   | dict or tuple           | tuple, dict, json | 이름이 지정된 튜플은 기본적으로 딕셔너리를 반환하고, 이름이 없는 튜플은 튜플을 반환합니다.                   |
| Map                     | dict                    | -                 |                                                                       |
| Nested                  | Sequence\[dict]         | -                 |                                                                       |
| UUID                    | uuid.UUID               | string            | UUID는 RFC 4122 형식의 문자열로 읽을 수 있습니다<br />                               |
| JSON                    | dict                    | string            | 기본적으로 Python 딕셔너리가 반환됩니다. `string` 포맷은 JSON 문자열을 반환합니다                |
| Variant                 | object                  | typed             | `typed`는 `TypedVariant(value, type_name)`를 반환하므로 원래 멤버 타입이 유지됩니다.     |
| Dynamic                 | object                  | -                 | 값에 저장된 ClickHouse 데이터 타입에 맞는 Python 타입을 반환합니다                         |
| QBit                    | list\[float]            | -                 | 설치되어 있으면 더 빠른 비트 전치를 위해 NumPy가 자동으로 사용됩니다.                            |

<div id="external-data">
  ## 외부 데이터
</div>

ClickHouse 쿼리는 지원되는 모든 입력 형식의 외부 데이터를 받을 수 있습니다. 클라이언트는 요청의 일부로 데이터를 전송하며, 쿼리는 이를 임시 외부 테이블로 참조할 수 있습니다. [ClickHouse 외부 데이터 문서](/ko/reference/engines/table-engines/special/external-data)를 참조하십시오. 클라이언트 쿼리 메서드는 `external_data` 매개변수를 통해 `clickhouse_connect.driver.external.ExternalData` 객체를 받습니다.

| 이름         | 유형                | 설명                                                                                    |
| ---------- | ----------------- | ------------------------------------------------------------------------------------- |
| file\_path | str               | 외부 데이터를 읽어올 로컬 시스템의 파일 경로입니다. `file_path` 또는 `data` 중 하나는 필수입니다                       |
| file\_name | str               | 외부 데이터 "파일"의 이름입니다. 제공하지 않으면 `file_path`의 파일명 부분을 사용합니다. 외부 테이블 이름은 확장자를 제외한 파일명입니다   |
| data       | bytes             | 파일에서 읽는 대신 바이너리 형식으로 제공하는 외부 데이터입니다. `data` 또는 `file_path` 중 하나는 필수입니다                |
| fmt        | str               | 데이터의 ClickHouse [입력 형식](/ko/reference/formats)입니다. 기본값은 `TSV`입니다                      |
| types      | str or seq of str | 외부 데이터의 컬럼 데이터 타입 목록입니다. 문자열인 경우 타입은 쉼표로 구분해야 합니다. `types` 또는 `structure` 중 하나는 필수입니다 |
| structure  | str or seq of str | 데이터의 컬럼명 + 데이터 타입 목록입니다(예시 참조). `structure` 또는 `types` 중 하나는 필수입니다                    |
| mime\_type | str               | 파일 데이터의 선택적 MIME 타입입니다. 현재 ClickHouse는 이 HTTP 하위 헤더를 무시합니다                            |

다음 예시는 외부 CSV 파일을 서버에 저장된 `directors` 테이블과 조인합니다:

```python theme={null}
import clickhouse_connect
from clickhouse_connect.driver.external import ExternalData

client = clickhouse_connect.get_client()
ext_data = ExternalData(
    file_path="/data/movies.csv",
    fmt="CSV",
    structure=[
        "movie String",
        "year UInt16",
        "rating Decimal32(3)",
        "director String",
    ],
)
result = client.query(
    "SELECT name, avg(rating) "
    "FROM directors INNER JOIN movies ON directors.name = movies.director "
    "GROUP BY directors.name",
    external_data=ext_data,
).result_rows
```

추가적인 외부 데이터 파일은 생성자와 동일한 매개변수를 받는 `add_file` 메서드를 사용해 초기 `ExternalData` 객체에 추가할 수 있습니다. HTTP에서는 모든 외부 데이터가 `multi-part/form-data` 파일 업로드의 일부로 전송됩니다.

chDB 백엔드는 외부 데이터를 지원하지 않습니다.

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

ClickHouse `DateTime` 및 `DateTime64` 값은 epoch 기반 숫자 값으로 전송됩니다. ClickHouse Connect는 컬럼 메타데이터(metadata), 쿼리 재정의, 그리고 클라이언트의 시간대 정책을 사용해 이를 Python `datetime` 객체로 변환합니다.

클라이언트에는 서로 독립적으로 동작하는 두 가지 시간대 옵션이 있습니다.

* `tz_source`는 명시적인 시간대 메타데이터가 없는 컬럼에 사용할 폴백 시간대를 선택합니다.
  * `"auto"`가 기본값입니다. 클라이언트가 일광 절약 시간제 전환 구간에서도 이를 안전하게 확인할 수 있으면 server timezone을 사용하고, 그렇지 않으면 로컬 시간대를 사용합니다.
  * `"server"`는 항상 server timezone을 사용합니다.
  * `"local"`은 항상 로컬 프로세스 시간대를 사용합니다.
* `tz_mode`는 시간대 인식 여부를 제어합니다.
  * `"naive_utc"`가 기본값입니다. UTC 및 UTC와 동등한 결과는 이전 버전과의 호환성을 위해 naive `datetime` 객체로 반환됩니다.
  * `"aware"`는 UTC `tzinfo`를 유지하며 시간대 인식 UTC 값을 반환합니다.
  * `"schema"`는 컬럼 타입에 시간대가 선언된 경우에만 시간대 인식 값을 반환하고, 시간대가 지정되지 않은 `DateTime`/`DateTime64` 컬럼에는 naive 값을 반환합니다.

일반적인 `"naive_utc"` 및 `"aware"` 쿼리에서는 활성 시간대가 다음 순서로 선택됩니다.

1. 컬럼별 `column_tzs` 재정의
2. ClickHouse 컬럼 타입의 시간대 메타데이터
3. 쿼리 전체에 적용되는 `query_tz` 재정의
4. HTTP 응답과 함께 반환되는 시간대 정보
5. `tz_source`에서 선택한 폴백

`tz_mode="schema"`는 쿼리 시간대와 폴백 시간대를 무시하지만, 명시적인 `column_tzs` 재정의는 여전히 우선 적용됩니다.

```python theme={null}
result = client.query(
    "SELECT "
    "toDateTime('2026-01-15 12:00:00', 'UTC') AS utc_time, "
    "toDateTime('2026-01-15 12:00:00', 'America/Denver') AS denver_time",
    tz_mode="aware",
)

assert result.first_row[0].tzinfo is not None
assert result.first_row[1].tzinfo is not None
```

시간대 이름은 표준 라이브러리 `zoneinfo` 모듈로 해석됩니다. Windows 설치 환경에는 `tzdata`가 자동으로 제공됩니다. IANA 시간대 데이터베이스가 없는 최소 Linux 이미지에서는 `clickhouse-connect[tzdata]`를 설치하십시오.

Pandas 결과는 `DateTime`의 `datetime64[s]`, `DateTime64(3)`의 `datetime64[ms]`처럼 각 ClickHouse 타입의 고유한 해상도를 유지합니다. Arrow 기반 DataFrame 메서드 `query_df_arrow` 및 `query_df_arrow_stream`은 아직 `tz_mode="schema"`를 구현하지 않았으며, 이를 요청하면 경고를 발생시킵니다. `query_arrow` 및 `query_arrow_stream`은 Arrow 응답의 시간대 메타데이터를 변경하지 않고 그대로 반환합니다.
