> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# chDB 시작하기

> chDB는 ClickHouse 기반의 in-process SQL OLAP Engine입니다

이 가이드에서는 chDB의 Python 버전을 사용해 빠르게 시작하는 방법을 설명합니다.
먼저 S3의 JSON 파일에 쿼리한 다음, 해당 JSON 파일을 기반으로 chDB에 테이블(table)을 생성하고 데이터에 대해 몇 가지 쿼리를 수행합니다.
또한 Apache Arrow와 Panda를 포함해 쿼리 결과를 다양한 포맷으로 반환하는 방법을 살펴보고, 마지막으로 Pandas 데이터프레임을 쿼리하는 방법을 알아봅니다.

## 환경 설정

먼저 가상 환경을 생성합니다:

```bash theme={null}
python -m venv .venv
source .venv/bin/activate
```

이제 chDB를 설치합니다.
버전 2.0.3 이상인지 확인하세요:

```bash theme={null}
pip install "chdb>=2.0.2"
```

이제 [ipython](https://ipython.org/)을 설치하겠습니다:

```bash theme={null}
pip install ipython
```

이 가이드의 나머지 부분에서 명령을 실행할 때는 `ipython`을 사용합니다. 다음을 실행하여 시작할 수 있습니다:

```bash theme={null}
ipython
```

이 가이드에서는 Pandas와 Apache Arrow도 사용하므로, 해당 라이브러리도 함께 설치하세요:

```bash theme={null}
pip install pandas pyarrow
```

## S3의 JSON 파일 쿼리하기

이제 S3 버킷에 저장된 JSON 파일을 어떻게 쿼리하는지 살펴보겠습니다.
[YouTube dislikes 데이터셋](https://clickhouse.com/docs/ko/get-started/sample-datasets/youtube-dislikes.md)에는 2021년까지 YouTube 동영상의 dislikes에 대한 40억 개 이상의 행이 포함되어 있습니다.
이 데이터셋에 포함된 JSON 파일 중 하나를 사용하겠습니다.

chdb를 가져옵니다:

```python theme={null}
import chdb
```

다음 쿼리를 사용하여 JSON 파일 하나의 구조를 확인할 수 있습니다:

```python theme={null}
chdb.query(
  """
  DESCRIBE s3(
    's3://clickhouse-public-datasets/youtube/original/files/' ||
    'youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst',
    'JSONLines'
  )
  SETTINGS describe_compact_output=1
  """
)
```

```text theme={null}
"id","Nullable(String)"
"fetch_date","Nullable(String)"
"upload_date","Nullable(String)"
"title","Nullable(String)"
"uploader_id","Nullable(String)"
"uploader","Nullable(String)"
"uploader_sub_count","Nullable(Int64)"
"is_age_limit","Nullable(Bool)"
"view_count","Nullable(Int64)"
"like_count","Nullable(Int64)"
"dislike_count","Nullable(Int64)"
"is_crawlable","Nullable(Bool)"
"is_live_content","Nullable(Bool)"
"has_subtitles","Nullable(Bool)"
"is_ads_enabled","Nullable(Bool)"
"is_comments_enabled","Nullable(Bool)"
"description","Nullable(String)"
"rich_metadata","Array(Tuple(
    call Nullable(String),
    content Nullable(String),
    subtitle Nullable(String),
    title Nullable(String),
    url Nullable(String)))"
"super_titles","Array(Tuple(
    text Nullable(String),
    url Nullable(String)))"
"uploader_badges","Nullable(String)"
"video_badges","Nullable(String)"
```

해당 파일에 있는 행 수를 셀 수도 있습니다:

```python theme={null}
chdb.query(
  """
  SELECT count()
  FROM s3(
    's3://clickhouse-public-datasets/youtube/original/files/' ||
    'youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst',
    'JSONLines'
  )"""
)
```

```text theme={null}
336432
```

이 파일에는 300,000개를 조금 넘는 레코드가 들어 있습니다.

chdb는 아직 쿼리 매개변수를 전달하는 기능을 지원하지 않지만, 경로를 추출해 f-String으로 전달할 수 있습니다.

```python theme={null}
path = 's3://clickhouse-public-datasets/youtube/original/files/youtubedislikes_20211127161229_18654868.1637897329_vid.json.zst'
```

```python theme={null}
chdb.query(
  f"""
  SELECT count()
  FROM s3('{path}','JSONLines')
  """
)
```

> [!WARNING]
>
> 프로그램에서 정의한 변수에는 이렇게 해도 괜찮지만, 사용자가 제공한 입력값에는 이렇게 하면 안 됩니다. 그렇지 않으면 쿼리가 SQL 인젝션에 취약해집니다.

## 출력 형식 구성

기본 출력 형식은 `CSV`이지만, `output_format` 매개변수로 변경할 수 있습니다.
chDB는 ClickHouse 데이터 포맷과 [자체 포맷 일부](https://clickhouse.com/docs/ko/chdb/reference/data-formats.md)를 지원하며, 여기에는 Pandas 데이터프레임을 반환하는 `DataFrame`도 포함됩니다:

```python theme={null}
result = chdb.query(
  f"""
  SELECT is_ads_enabled, count()
  FROM s3('{path}','JSONLines')
  GROUP BY ALL
  """,
  output_format="DataFrame"
)

print(type(result))
print(result)
```

```text theme={null}
<class 'pandas.core.frame.DataFrame'>
   is_ads_enabled  count()
0           False   301125
1            True    35307
```

또는 Apache Arrow 테이블을 다시 가져오려면:

```python theme={null}
result = chdb.query(
  f"""
  SELECT is_live_content, count()
  FROM s3('{path}','JSONLines')
  GROUP BY ALL
  """,
  output_format="ArrowTable"
)

print(type(result))
print(result)
```

```text theme={null}
<class 'pyarrow.lib.Table'>
pyarrow.Table
is_live_content: bool
count(): uint64 not null
----
is_live_content: [[false,true]]
count(): [[315746,20686]]
```

## JSON 파일에서 테이블 생성하기

다음으로 chDB에서 테이블을 생성하는 방법을 살펴보겠습니다.
이를 위해서는 다른 API를 사용해야 하므로, 먼저 해당 API를 import하겠습니다:

```python theme={null}
from chdb import session as chs
```

다음으로 세션을 초기화하겠습니다.
세션을 디스크에 유지하려면 디렉터리 이름을 지정해야 합니다.
비워 두면 세션은 임시 상태가 됩니다. chDB는 데이터를 디스크의 임시 디렉터리에 보관하고 세션이 종료되면 삭제하므로 데이터가 유지되지 않습니다.

```python theme={null}
sess = chs.Session("gettingStarted.chdb")
```

> [!NOTE]
>
> chDB는 프로세스당 하나의 내장 엔진만 실행하므로 데이터베이스 디렉터리(경로)에는 몇 가지 규칙이 적용됩니다:
>
> * **경로당 하나의 프로세스**: chDB 데이터 디렉터리는 한 번에 하나의 운영 체제 프로세스만 열 수 있습니다. 첫 번째 프로세스가 닫힐 때까지 동일한 경로를 열려는 두 번째 프로세스는 실패합니다.
> * **프로세스당 하나의 경로**: 세션 또는 연결이 열려 있는 동안 *다른* 경로에 대한 세션이나 연결을 추가로 열면 실패합니다. 오류를 방지하려면 먼저 열려 있는 세션이나 연결을 닫으십시오. *동일한* 경로의 여러 세션이나 연결은 함께 사용할 수 있습니다.
> * **동시성**: 동일한 경로의 세션이나 연결은 쿼리를 동시에 실행할 수 있습니다.
> * **경로 없는 세션은 공유됩니다**: 경로를 지정하지 않으면 세션은 프로세스 전체에서 공유되는 `:memory:` 데이터베이스를 사용하므로 빈 세션에서도 서로의 테이블을 볼 수 있으며, 임시 디렉터리는 마지막 세션이 닫힐 때만 제거됩니다.

다음으로 데이터베이스를 생성합니다:

```python theme={null}
sess.query("CREATE DATABASE IF NOT EXISTS youtube")
```

이제 `CREATE...EMPTY AS` 기법을 사용해 JSON 파일의 스키마를 기반으로 `dislikes` 테이블을 생성할 수 있습니다.
모든 컬럼 타입이 `Nullable`로 지정되지 않도록 [`schema_inference_make_columns_nullable`](https://clickhouse.com/docs/ko/reference/settings/formats/schema-inference#schema_inference_make_columns_nullable) 설정을 사용합니다.

```python theme={null}
sess.query(f"""
  CREATE TABLE youtube.dislikes
  ORDER BY fetch_date 
  EMPTY AS 
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)
```

그런 다음 `DESCRIBE` 절을 사용해 스키마를 살펴볼 수 있습니다:

```python theme={null}
sess.query(f"""
   DESCRIBE youtube.dislikes
   SETTINGS describe_compact_output=1
   """
)
```

```text theme={null}
"id","String"
"fetch_date","String"
"upload_date","String"
"title","String"
"uploader_id","String"
"uploader","String"
"uploader_sub_count","Int64"
"is_age_limit","Bool"
"view_count","Int64"
"like_count","Int64"
"dislike_count","Int64"
"is_crawlable","Bool"
"is_live_content","Bool"
"has_subtitles","Bool"
"is_ads_enabled","Bool"
"is_comments_enabled","Bool"
"description","String"
"rich_metadata","Array(Tuple(
    call String,
    content String,
    subtitle String,
    title String,
    url String))"
"super_titles","Array(Tuple(
    text String,
    url String))"
"uploader_badges","String"
"video_badges","String"
```

다음으로, 해당 테이블에 데이터를 입력해 보겠습니다:

```python theme={null}
sess.query(f"""
  INSERT INTO youtube.dislikes
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)
```

`CREATE...AS` 기법을 사용하면 이 두 단계를 한 번에 처리할 수도 있습니다.
이 기법을 사용해 다른 테이블을 만들어 보겠습니다:

```python theme={null}
sess.query(f"""
  CREATE TABLE youtube.dislikes2
  ORDER BY fetch_date 
  AS 
  SELECT * 
  FROM s3('{path}','JSONLines')
  SETTINGS schema_inference_make_columns_nullable=0
  """
)
```

## 테이블 쿼리하기

마지막으로, 테이블에 쿼리를 실행해 보겠습니다:

```sql theme={null}
df = sess.query("""
  SELECT uploader, sum(view_count) AS viewCount, sum(like_count) AS likeCount, sum(dislike_count) AS dislikeCount
  FROM youtube.dislikes
  GROUP BY ALL
  ORDER BY viewCount DESC
  LIMIT 10
  """,
  "DataFrame"
)
df
```

```text theme={null}
                             uploader  viewCount  likeCount  dislikeCount
0                             Jeremih  139066569     812602         37842
1                     TheKillersMusic  109313116     529361         11931
2  LetsGoMartin- Canciones Infantiles  104747788     236615        141467
3                    Xiaoying Cuisine   54458335    1031525         37049
4                                Adri   47404537     279033         36583
5                  Diana and Roma IND   43829341     182334        148740
6                      ChuChuTV Tamil   39244854     244614        213772
7                            Cheez-It   35342270        108            27
8                            Anime Uz   33375618    1270673         60013
9                    RC Cars OFF Road   31952962     101503         49489
```

예를 들어, 좋아요와 싫어요의 비율을 계산하기 위해 DataFrame에 컬럼을 하나 더 추가한다고 가정해 보겠습니다.
다음과 같은 코드를 작성할 수 있습니다.

```python theme={null}
df["likeDislikeRatio"] = df["likeCount"] / df["dislikeCount"]
```

## Pandas 데이터프레임 쿼리하기

이제 chDB에서 해당 데이터프레임을 쿼리할 수 있습니다:

```python theme={null}
chdb.query(
  """
  SELECT uploader, likeDislikeRatio
  FROM Python(df)
  """,
  output_format="DataFrame"
)
```

```text theme={null}
                             uploader  likeDislikeRatio
0                             Jeremih         21.473548
1                     TheKillersMusic         44.368536
2  LetsGoMartin- Canciones Infantiles          1.672581
3                    Xiaoying Cuisine         27.842182
4                                Adri          7.627395
5                  Diana and Roma IND          1.225857
6                      ChuChuTV Tamil          1.144275
7                            Cheez-It          4.000000
8                            Anime Uz         21.173296
9                    RC Cars OFF Road          2.051021
```

Pandas 데이터프레임을 쿼리하는 방법에 대한 자세한 내용은 [Pandas 데이터프레임 쿼리 개발자 가이드](https://clickhouse.com/docs/ko/chdb/guides/querying-pandas.md)에서 확인할 수 있습니다.

## 다음 단계

이 가이드를 통해 chDB를 전반적으로 이해하는 데 도움이 되었기를 바랍니다.
사용 방법을 더 자세히 알아보려면 다음 개발자 가이드를 참조하십시오:

* [Pandas 데이터프레임 쿼리하기](https://clickhouse.com/docs/ko/chdb/guides/querying-pandas.md)
* [Apache Arrow 쿼리하기](https://clickhouse.com/docs/ko/chdb/guides/querying-apache-arrow.md)
* [JupySQL에서 chDB 사용하기](https://clickhouse.com/docs/ko/chdb/guides/jupysql.md)
* [기존 clickhouse-local 데이터베이스에서 chDB 사용하기](https://clickhouse.com/docs/ko/chdb/guides/clickhouse-local.md)
