> ## 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 提供支持的进程内 SQL OLAP 引擎

在本指南中，我们将快速上手 chDB 的 Python 版本。
我们将先查询 S3 上的一个 JSON 文件，然后基于该 JSON 文件在 chDB 中创建一张表，并对其中的数据执行一些查询。
我们还会了解如何让查询以不同格式返回数据，包括 Apache Arrow 和 Panda，最后学习如何查询 Pandas DataFrame。

## 准备

首先，创建一个虚拟环境：

```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 bucket 中的 JSON 文件。
[YouTube dislikes 数据集](https://clickhouse.com/docs/zh/get-started/sample-datasets/youtube-dislikes.md)包含截至 2021 年 YouTube 视频上超过 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
```

这个文件包含 30 多万条记录。

chdb 目前还不支持传入查询参数，但我们可以把 path 提取出来，并通过 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/zh/chdb/reference/data-formats.md)，其中包括 `DataFrame`；它会返回一个 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，所以先导入它：

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

接下来，我们将初始化一个会话。
如果希望将该会话持久化到磁盘，就需要提供一个目录名。
如果留空，该会话将是临时的：chDB 会将其数据保存在磁盘上的临时目录中，并在会话关闭时删除，因此数据不会持久化。

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

> [!NOTE]
>
> chDB 每个进程运行一个嵌入式 引擎，因此 database 目录 (其 path) 需遵循以下规则：
>
> * **每个 path 对应一个进程**：一个 chDB 数据目录一次只能由一个操作系统进程打开。第二个进程打开同一 path 会失败，直到第一个进程关闭为止。
> * **每个进程对应一个 path**：当会话或 connection 处于打开状态时，打开另一个具有*不同* path 的会失败；请先关闭已打开的会话或 connection，以避免出现 error。同一 path 上可以同时存在多个会话或 connection。
> * **并发**：同一 path 上的会话或 connection 可以并发运行查询。
> * **无 path 的会话是共享的**：不提供 path 时，会话将使用进程范围内的 `:memory:` database (因此空白会话可以看到彼此的表) ，且其临时目录仅会在最后一个会话关闭时被移除。

接下来，我们来创建一个 database：

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

现在我们可以根据 JSON 文件 中的 schema，使用 `CREATE...EMPTY AS` 方法创建一个 `dislikes` 表。
我们将使用 [`schema_inference_make_columns_nullable`](https://clickhouse.com/docs/zh/reference/settings/formats/schema-inference#schema_inference_make_columns_nullable) 设置，这样列类型就不会全部被设为 `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` 子句来查看 schema：

```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 DataFrame

然后，我们就可以通过 chDB 查询该 DataFrame：

```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 DataFrame 开发者指南](https://clickhouse.com/docs/zh/chdb/guides/querying-pandas.md)中进一步了解如何查询 Pandas DataFrame。

## 后续步骤

希望通过本指南，你已经对 chDB 有了较为全面的了解。
如需进一步了解其使用方法，请参阅以下开发者指南：

* [查询 Pandas DataFrame](https://clickhouse.com/docs/zh/chdb/guides/querying-pandas.md)
* [查询 Apache Arrow](https://clickhouse.com/docs/zh/chdb/guides/querying-apache-arrow.md)
* [在 JupySQL 中使用 chDB](https://clickhouse.com/docs/zh/chdb/guides/jupysql.md)
* [结合现有 clickhouse-local database 使用 chDB](https://clickhouse.com/docs/zh/chdb/guides/clickhouse-local.md)
