DuckDBを使ってみた感想を書いてみた！（オープンソースの組み込み型OLAP）

# はじめに

2024年12月31日のはてぶの人気エントリーにこちらの記事があり

https://b.hatena.ne.jp/entry/s/wvlet.org/wvlet/blog/release-2024-9/

こちらを使ってみて
「ふーん」と思ってときに見つけた「DuckDB」

> Cross-SQL Engine Support
> Wvlet consumes the differences between SQL engines, such as DuckDB, Trino, Hive, etc. You can switch the SQL engines without changing the query, depending on the data size and the expected query performance.

これ調べてみたら便利だなと思ったので、使ってみた感想含めて記事にしてます。


# DuckDBとは？

DuckDBは、オープンソースの組み込み型OLAP (Online Analytical Processing) SQLデータベース管理システム(DBMS)です。

組み込み型DBだと、SQLiteとかありますが、DuckDBの主な特徴は以下です。

- 組み込み型
単一のプロセス内で動作し、環境構築なしで手軽に利用できます

- 高速処理
列指向ストレージを採用し、データ分析クエリに対して最適なパフォーマンスを提供

- 多様なデータ形式対応
CSV、JSON、Parquet、Excelなど、様々なファイル形式のインポート・エクスポートに対応

- インメモリ処理
データをメモリ内で操作することで、大規模なデータセットでも迅速に処理が可能

- 軽量設計
シングルバイナリで動作し、他のプログラムに組み込みやすい設計

※ シングルバイナリってGoあたりから一気に流行った気がしますが、どうなんですかね？

# 実際に使ってみた感じ

## インストール方法と実行方法

シングルバイナリなので、ダウンロードして配置でも大丈夫ですが、今回は公式のインストール方法を使ってインストールしました。

  ```bash
  brew install duckdb
  ```

インストールしたら、以下で実行可能です。
```bash
duckdb
```

## 簡単に使ってみる

DuckDBが便利なのは、CSV含めて色んなデータフォーマットに対応していることと、それをネット経由でインポートすることが可能という点です。

サンプルとして、以下のデータについて扱うとして場合に一々ダウンロードしなくても

https://raw.githubusercontent.com/bnokoro/Data-Science/refs/heads/master/countries%20of%20the%20world.csv

```bash
D SELECT count(*) FROM 'https://github.com/bnokoro/Data-Science/raw/master/countries%20of%20the%20world.csv';
┌──────────────┐
│ count_star() │
│    int64     │
├──────────────┤
│          227 │
└──────────────┘
```

とSQLで実行することが可能です。
実際にインポートも可能で、

```bash
D CREATE TABLE mytable AS SELECT * FROM read_csv_auto('https://raw.githubusercontent.com/bnokoro/Data-Science/master/countries%20of%20the%20world.csv');
```

こんな感じでインポートができて

```bash
D .tables
mytable
D SELECT * FROM mytable LIMIT 5;
┌─────────────────┬──────────────────────┬────────────┬────────────────┬───┬───────────┬─────────────┬──────────┬─────────┐
│     Country     │        Region        │ Population │ Area (sq. mi.) │ … │ Deathrate │ Agriculture │ Industry │ Service │
│     varchar     │       varchar        │   int64    │     int64      │   │  varchar  │   varchar   │ varchar  │ varchar │
├─────────────────┼──────────────────────┼────────────┼────────────────┼───┼───────────┼─────────────┼──────────┼─────────┤
│ Afghanistan     │ ASIA (EX. NEAR EAS…  │   31056997 │         647500 │ … │ 20,34     │ 0,38        │ 0,24     │ 0,38    │
│ Albania         │ EASTERN EUROPE    …  │    3581655 │          28748 │ … │ 5,22      │ 0,232       │ 0,188    │ 0,579   │
│ Algeria         │ NORTHERN AFRICA   …  │   32930091 │        2381740 │ … │ 4,61      │ 0,101       │ 0,6      │ 0,298   │
│ American Samoa  │ OCEANIA           …  │      57794 │            199 │ … │ 3,27      │             │          │         │
│ Andorra         │ WESTERN EUROPE    …  │      71201 │            468 │ … │ 6,25      │             │          │         │
├─────────────────┴──────────────────────┴────────────┴────────────────┴───┴───────────┴─────────────┴──────────┴─────────┤
│ 5 rows                                                                                             20 columns (8 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
D SELECT count(*) FROM mytable;
┌──────────────┐
│ count_star() │
│    int64     │
├──────────────┤
│          227 │
└──────────────┘
```

という形で利用することが可能です。

なお、カンマがあると数値（int64）ではなく、文字列（varchar）で取り込まれてしまうためこのあとで集計等する場合は以下のようにしてvarcharからint64へ変換してあげると便利です。

```bash
# 例：Birthrate（出生率）をint64へ変換する
D DESCRIBE mytable;
┌────────────────────────────────────┬─────────────┬─────────┬─────────┬─────────┬─────────┐
│            column_name             │ column_type │  null   │   key   │ default │  extra  │
│              varchar               │   varchar   │ varchar │ varchar │ varchar │ varchar │
├────────────────────────────────────┼─────────────┼─────────┼─────────┼─────────┼─────────┤
│ Country                            │ VARCHAR     │ YES     │         │         │         │
│ Region                             │ VARCHAR     │ YES     │         │         │         │
│ Population                         │ BIGINT      │ YES     │         │         │         │
・・・・・
│ Birthrate                          │ VARCHAR     │ YES     │         │         │         │
・・・・・
└──────────────────────────────────────────────────────────────────────────────────────────┘

# 色んなやり方があるのですが、数値用カラムをつくってUPDATEかけるやり方が一番カンタンかも
# -- 1. 新しい数値型の列を追加
D ALTER TABLE mytable ADD COLUMN Birthrate_int INTEGER;

# -- 2. データを変換して新しい列に挿入
# カンマがあると失敗するので、カンマは削除する
D UPDATE mytable SET Birthrate_int = TRY_CAST(REPLACE(Birthrate, ',', '') AS INTEGER);

# Birthrate_int列が作成され、データが格納されてる
D SELECT Birthrate, Birthrate_int FROM mytable LIMIT 5;
┌───────────┬───────────────┐
│ Birthrate │ Birthrate_int │
│  varchar  │     int32     │
├───────────┼───────────────┤
│ 46,6      │           466 │
│ 15,11     │          1511 │
│ 17,14     │          1714 │
│ 22,46     │          2246 │
│ 8,71      │           871 │
└───────────┴───────────────┘
```

ここまでできればあとは、SQLの世界なので、例えば世界で一番出生率が高い国を出したいときは

```bash
D SELECT * FROM mytable ORDER BY Birthrate_int DESC LIMIT 1;
┌─────────┬──────────────────────┬────────────┬────────────────┬──────────────────────┬──────────────────────┬───┬─────────┬───────────┬───────────┬─────────────┬──────────┬─────────┬───────────────┐
│ Country │        Region        │ Population │ Area (sq. mi.) │ Pop. Density (per …  │ Coastline (coast/a…  │ … │ Climate │ Birthrate │ Deathrate │ Agriculture │ Industry │ Service │ Birthrate_int │
│ varchar │       varchar        │   int64    │     int64      │       varchar        │       varchar        │   │ varchar │  varchar  │  varchar  │   varchar   │ varchar  │ varchar │     int32     │
├─────────┼──────────────────────┼────────────┼────────────────┼──────────────────────┼──────────────────────┼───┼─────────┼───────────┼───────────┼─────────────┼──────────┼─────────┼───────────────┤
│ Niger   │ SUB-SAHARAN AFRICA…  │   12525094 │        1267000 │ 9,9                  │ 0,00                 │ … │ 1       │ 50,73     │ 20,91     │ 0,39        │ 0,17     │ 0,44    │          5073 │
├─────────┴──────────────────────┴────────────┴────────────────┴──────────────────────┴──────────────────────┴───┴─────────┴───────────┴───────────┴─────────────┴──────────┴─────────┴───────────────┤
│ 1 rows                                                                                                                                                                        21 columns (13 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```

となります。

# まとめ

昔こういうのをやろうとすると、ExcelやGoogleシートへインポートしてデータ変になってるところ直してとかしてましたが、こんな気軽にデータ分析できるようになってホント便利な時代になったなと思いました。

なお、もっと詳しいことを知りたい人は、「DuckDB in Action」本があるので是非！

※ PDF版だと無料でダウンロード可能
https://motherduck.com/duckdb-book-brief/

# 参考サイト

- [Documentation – DuckDB](https://duckdb.org/docs/)