巷で噂の DuckDB で遊んでみたよ！

# はじめに

GPUで日本を変えるべくオンプレ界隈で主活動中の [@\_\_\_nix___](https://twitter.com/___nix___)  です。

# 背景

開発の人「AWS を利用していると S3 にログ溜めてるけどどうやって調査するの？」
運用の人「Athena を使うのさ♡」
開発の人「昨日XX:XX頃の状況を調べたいのだけど」
運用の人「ちょっと待ってね、Athena テーブル作成するところから始めるから！」
開発の人「ありがとう！」
運用の人「ゴリゴリ、ポチポチ... できたぁ！」
開発の人「どうやって調査すれば良いの？」
運用の人「あ、まずは IAMユーザーの権限が必要だから待っててね」
開発の人「は～い」
運用の人「設定したから確認してみて♡」
開発の人「えーっと、マネコン入りましたが何をすれば良いですか？」
運用の人「運用手順準備するね！」
開発の人「あ、はい...（まだ調査できないのかな... λ...）」


# 概要

運用の人「手軽に DuckDB 使って要件満たせれば楽かも！」（のびたの画像）
　　　　「しかもCUIなので使いなれた Cursor のターミナルで利用してみるぜ！」
↑全角スペース使ってますw

https://duckdb.org/

# 導入

ここでは for Windows 向けに解説しますが、使い方に関しては大きく違いは無いと思います。

## 1. インストール

[DuckDB Installation – DuckDB](https://duckdb.org/docs/installation/) にアクセスして確認します。
ここでは PowerShell を使って以下のコマンドを実行しました。

```
winget install DuckDB.cli
```

## 2. 確認

バージョンを確認してみます。

```
PS C:\Users\hoge> duckdb --version
v1.1.3 19864453f7
```

## 3. Cursor で PATH を通す

まずはどこにインストールされたのかを `Get-Command <コマンド名>` で確認します。
もはやこのコマンドは私にとって隠しコマンド、いや裏技レベルです。

```
PS C:\Users\hoge> Get-Command duckdb

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Application     duckdb.exe                                         1.1.3.0    C:\Users\hoge\AppData\Local\Microsoft\WinGet\Packages\DuckDB.cli_Microsoft.Winget.Source_8wekyb3d8bbwe\duckdb.exe
```

異様に深いところにインストールしてくれてますが、設定してしまえば今後意識する必要がありません。調べたパスを Cursor ワークスペースのトップディレクトリに配置してあげましょう。

```{workspace}/.vscode/settings/json
"terminal.integrated.env.windows": {
    "PATH": "C:\\Users\\hoge\\AppData\\Local\\Microsoft\\WinGet\\Packages\\DuckDB.cli_Microsoft.Winget.Source_8wekyb3d8bbwe;${env:PATH}"
}
```

:::note notice
スラッシュの書き方に注意してくださいね。
:::

設定したら Cursor を再起動しましょう。

## 4. Cursor で動作確認

問題無ければ Cursor のターミナルで duckdb コマンドが認識されます。

```
$ duckdb --version
v1.1.3 19864453f7
```

# 使い方

## 1. 接続

```
$ duckdb
v1.1.3 19864453f7
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D 
```

※ `D` はプロンプトです。（何故？）

## 2. 設定

httpfs拡張機能をインストールしてロードします。分からなくても呪文を唱えればOKです。

```
INSTALL httpfs;
LOAD httpfs;
```

## 3. 認証情報の設定

今回、S3 を対象としていますので、アクセスキー等の情報を設定します。

```
SET s3_access_key_id = '<AWS_ACCESS_KEY_ID>';
SET s3_secret_access_key = '<AWS_SECRET_ACCESS_KEY>';
SET s3_region = '<AWS_REGION>';
```

しかし、私は固定クレデンシャルはリスクがあるので使いません。そんな時にも対応してくれる DuckDB は素晴らしい。

```
SET s3_access_key_id = '<AWS_ACCESS_KEY_ID>';
SET s3_secret_access_key = '<AWS_SECRET_ACCESS_KEY>';
SET s3_session_token = '<AWS_SESSION_TOKEN>';
SET s3_region = '<AWS_REGION>';
```

:::note warn
Cursor のターミナルに環境変数で認証情報を設定しても意味が無いので注意です。
:::

## 4. SELECT してみる

`read_json_auto()` の紹介サイトが多いですが、私の環境では parquet形式 なのでどうするのだろうと思って調べてみると `read_parquet()` でOKのようです。

```
D SELECT * FROM read_parquet('s3://{bucket}/{path}/{file}.parquet');
100% ??????????????????????????????????????????????????????????????
┌─────────────────────┬───────────┬──────────┬───────────┬───────────┬─────────┬───────────┐
│        time         │  column1  │ column2  │  column3  │   column4 │ column5 │ column6   │
│      timestamp      │  varchar  │ varchar  │  varchar  │   varchar │  int64  │  double   │
├─────────────────────┼───────────┼──────────┼───────────┼───────────┼─────────┼───────────┤
│ 2024-11-15 09:00:00 │ data      │ 1        │ data      │ data      │       5 │   9543.87 │
...
```

※ `????` は本当にそう表示されているのでそのままを再現

# Tips

SELECT まではとてもスムーズでしたが、このクエリだけだと「現場で使えねぇ」で終わってしまいそうなので少し掘り下げた使い方も紹介しておきます。

## DATE() 関数を使いたい

以下のように怒られますが、これが使えないようでは役立たずですね。

```
LINE 1: ...{file}.parquet') where date >= DATE('2024-11-15') and foo in ('S...
```

1. 日付リテラルの使用
   DuckDBでは、日付をリテラルとして指定することができます。ISO 8601形式（YYYY-MM-DD）で日付を指定することで、日付型の値を直接扱うことができます。
   ```
   SELECT DATE '2024-11-15';　
   ```
   　
2. strptime関数の使用
文字列を日付に変換するためには、strptime関数を使用できます。この関数は、指定したフォーマットに基づいて文字列を日付型に変換します。
   ```
   SELECT strptime('2024-11-15', '%Y-%m-%d');
   ```
   　
3. current_date関数の使用
   現在の日付を取得するには、current_date関数を使用します。この関数は、トランザクションの開始時点での現在の日付を返します。
   ```
   SELECT current_date;
   ```
   　
4. 日付の演算
   日付の加算や減算を行う場合、DATE型に対して整数を加算することができます。例えば、5日後の日付を取得するには以下のようにします。
   ```
   SELECT DATE '2024-11-15' + 5;  -- 2024-11-19
   ```
   　
5. date_add関数の使用
   特定の間隔を日付に加算する場合は、date_add関数を使用することもできます。
   ```
   SELECT date_add(DATE '2024-11-15', INTERVAL 5 DAY);
   ```

## ディレクトリ配下を対象にしたい

正直対象にしたファイルしか読めないようではそれも役に立ちませんね。
はい、以下の指定でOKです。

```
D SELECT * FROM read_parquet('s3://{bucket}/*/*/*/*');
```

## パーティション

「そんな非効率な保存方法じゃないんだぜ」な人達向け
`/date=2024-11-15/` のような date のパーティションの場合は以下でOKです。

```
D SELECT * FROM read_parquet('s3://{bucket}/*/*/*/*') WHERE date = '2024-11-15';
```

## 外部ファイルへの出力

多いですよね、「お客さんにエビデンスを提示しないといけないのよ」
これも `COPY('{SQL}') TO '{output_file}' (HEADER, DELIMITER ',')` いけます。

```
D COPY (SELECT * FROM read_parquet('s3://{bucket}/*/*/*/*') WHERE date = '2024-11-15') TO 'output.csv' (HEADER, DELIMITER ',');
100% ??????????????????????????????????????????????????????????????
```

`????` も本当に表示されますが、Cursor のディレクトリツリーに output.csv が無事生成されました。

```output.csv
time,column1,column2,column3,column4,column5,column6
2024-11-15 09:00:00,data,1,data,data,5,9543.87
...
```

## プロンプトが「月」に変身

これは DuckDB あるあるになっていくことでしょう。

```
D qui
ﾂｷ 
ﾂｷ 
ﾂｷ 
ﾂｷ select
ﾂｷ select * from hoge;
Parser Error: syntax error at or near "qui"
LINE 1: qui
        ^
D 
```

※ だから何？w

# 終わりに

かなり駆け足でのご紹介でしたが、その割に濃度の濃い内容になったのではないでしょうか。今回はかなり限定的なデータソースや利用環境でしたが、この流れだけ把握できれば十分に応用ができるはずです。

これからも色々な使われ方をしていくことを考えると楽しみが広がりますね。

# 一言

華金に記事を書く確率が高く、とてもエンジニアらしい生活をしていますが、その分週末は派手に遊んでます。

この記事良かったと少しでも思って頂けたら是非 [@\_\_\_nix___](https://twitter.com/___nix___) をフォローしてあげてください。或いは記事に対してリアクションをお願い致します。