DuckDBをブラウザで使う ― Duck-UIをDockerで構築してCSV・ParquetをSQL分析する

DuckDBをブラウザで使う ― Duck-UIをDockerで構築してCSV・ParquetをSQL分析する

以前の記事では、Pandasより高速なDataFrameライブラリとしてPolarsを取り上げました。

Polarsは単なる「高速なPandas」ではなく、Lazy実行やクエリ最適化を備えた、非常に強力なデータ処理ライブラリです。

今回は、会社のシステム研修で紹介されて気になった DuckDB を調べてみました。

DuckDBもPolarsと同じくCSVやParquetなどの大量データを高速に扱えるツールですが、Polarsとの大きな違いがあります。

Polarsが、

PythonからDataFrameを操作する

ことを中心としたツールなのに対し、DuckDBは、

SQLを使ってデータを分析する

ことを中心とした分析用データベースです。

さらに、DuckDBをブラウザから利用できる Duck-UI というWebアプリケーションがあります。

Duck-UIをDockerで社内サーバーに用意すれば、社員のPCにPythonやDuckDBをインストールしなくても、

  1. ブラウザを開く
  2. 自分のPCにあるCSVを読み込む
  3. SQLを書く
  4. 集計結果を見る

という使い方ができます。

しかも通常のDuck-UIの構成では、CSVをサーバーにアップロードして処理するのではありません。

DuckDBそのものが各社員のブラウザ内で動作します。

今回はDuckDBとPolarsの違いから、Duck-UIをDockerで構築する方法まで紹介します。


DuckDBとは

DuckDBは、分析処理に特化した組み込み型のSQLデータベースです。

よく、

SQLiteの分析処理版

のように説明されます。

PostgreSQLやMySQLのように別途データベースサーバーを構築しなくても利用でき、CSVやParquetファイルを直接SQLから参照できます。

例えば、sales.csvがある場合、

SELECT *
FROM 'sales.csv'
LIMIT 100;

だけでCSVの内容を検索できます。

Parquetも同様です。

SELECT *
FROM 'sales.parquet'
LIMIT 100;

さらにDuckDBなので、もちろん集計もできます。

SELECT
    department,
    SUM(amount) AS total_amount
FROM 'sales.csv'
GROUP BY department
ORDER BY total_amount DESC;

一般的なRDBMSで使っているSQLの知識を、そのままデータ分析に利用できるのが大きな特徴です。


PolarsとDuckDBは何が違うのか

以前紹介したPolarsとDuckDBは、CSVやParquetを高速に扱えるという点ではかなり似ています。

しかし、中心となる考え方が異なります。

項目PolarsDuckDB
種類DataFrameライブラリ分析用DBMS
主な操作Python ExpressionSQL
CSV
Parquet
JOIN
GROUP BY
Lazy実行・最適化
Pythonとの連携
SQL
DBとしてデータを保存

例えば、Polarsで、

result = (
    pl.scan_parquet("orders.parquet")
      .filter(pl.col("revenue") > 5000)
      .group_by("user_id")
      .agg(pl.col("revenue").sum())
      .sort("revenue", descending=True)
      .head(10)
      .collect()
)

と書いていた処理は、DuckDBなら次のようになります。

SELECT
    user_id,
    SUM(revenue) AS revenue_sum
FROM 'orders.parquet'
WHERE revenue > 5000
GROUP BY user_id
ORDER BY revenue_sum DESC
LIMIT 10;

どちらが良いというより、

Pythonによるデータ加工を中心にするならPolars

SQLによる検索・JOIN・集計を中心にするならDuckDB

という使い分けが分かりやすいと思います。

また、両者は排他的なものではなく、DuckDBで大量データをSQL集計したあと、その結果をPolars DataFrameとして処理することもできます。


DuckDBが社内のCSV集計に向いている

今回特に面白いと感じたのは、社内で日常的に行われているCSVの集計です。

例えば、

売上一覧.csv
契約一覧.csv
社員一覧.csv
アクセスログ.csv

などのCSVをExcelで開き、

  • フィルター
  • SUMIF
  • COUNTIF
  • XLOOKUP
  • ピボットテーブル

などを使って集計しているケースは多いと思います。

DuckDBなら、これらをSQLで処理できます。

例えば部署ごとの件数なら、

SELECT
    "部署",
    COUNT(*) AS "件数"
FROM data
GROUP BY "部署"
ORDER BY "件数" DESC;

部署ごとの合計金額なら、

SELECT
    "部署",
    SUM("金額") AS "合計金額"
FROM data
GROUP BY "部署"
ORDER BY "合計金額" DESC;

条件を付ける場合も簡単です。

SELECT *
FROM data
WHERE "部署" = '営業部'
  AND "金額" >= 100000;

SQLを知っている社員であれば、Excelで複雑な関数を組むより分かりやすいケースもあります。


しかし社員全員にDuckDBをインストールするのは大変

DuckDBそのものを使うだけなら、それほど難しくありません。

しかし、内勤社員全員に利用してもらうとなると、

  • DuckDBのインストール
  • Pythonのインストール
  • VS Codeなどの設定
  • アップデート管理
  • PCごとのトラブル対応

などが必要になります。

そこで便利なのが Duck-UI です。


Duck-UIとは

Duck-UIは、DuckDBをWebブラウザから利用できるオープンソースのWebアプリケーションです。

SQL Editorのほか、

  • CSV
  • TSV
  • JSON
  • Parquet
  • Arrow
  • Excel
  • DuckDBデータベース

などのファイルを扱うことができます。

CSV、JSON、Parquet、ArrowについてはImport Filesから読み込むことができ、SQL Editorにはシンタックスハイライトや補完、Query Historyなども用意されています。

重要なのは、通常のDuck-UIではサーバーにDuckDBサーバーを用意する必要がないことです。

Duck-UIは DuckDB-Wasm を使用します。

DuckDB-WasmはDuckDBをWebAssembly化したもので、DuckDB公式ドキュメントでも、DuckDBエンジン自体がブラウザ内で動作し、クエリやデータをサーバーへ送る必要がない構成であることが説明されています。

構成としては次のようになります。

                  社内サーバー
              ┌───────────┐
              │ Docker          │
              │                 │
              │   Duck-UI       │
              │                 │
              │ HTML / JS /     │
              │ WASMを配信       │
              └───────────┘
                       │
             社内ネットワーク
                       │
        ┌─────────┼─────────┐
        │              │              │
        ▼              ▼              ▼
     社員A PC        社員B PC       社員C PC

     Browser          Browser         Browser
       │                │               │
   DuckDB-Wasm      DuckDB-Wasm     DuckDB-Wasm
       │                │               │
    A.csv            B.csv           C.csv

つまり、SQLを実行しているのはDockerサーバーではありません。

各社員のPCのブラウザです。


社内利用でDuck-UIが面白い理由

この構成には、社内利用でいくつか大きなメリットがあります。

社員PCへのインストールが不要

社員は、

https://duckdb.example.local/

などの社内URLをブラウザで開くだけです。

PythonやDuckDBを社員PCへインストールする必要がありません。


CSVを中央サーバーに集約する必要がない

社員Aが自分のPCにあるCSVを読み込んでも、そのCSVをDuck-UIサーバー側で分析するのではありません。

DuckDB-Wasmによる通常のローカルモードでは、処理はクライアント側で行われます。Duck-UI自身も「Privacy First」としてクライアントサイド処理を特徴として挙げています。

そのため、

社員A.csv → 社員Aのブラウザ

社員B.csv → 社員Bのブラウザ

社員C.csv → 社員Cのブラウザ

という形になります。

社内のCSV分析ツールとして、これはかなり扱いやすい設計です。


利用者が増えてもSQL処理がサーバーへ集中しない

例えば100人の社員がDuck-UIを利用した場合でも、100人分のSQLを中央のDuckDBサーバーが処理する構成ではありません。

基本的には、

社員AのSQL → 社員A PC

社員BのSQL → 社員B PC

社員CのSQL → 社員C PC

となります。

Duck-UIを動かしているDockerコンテナは、主にWebアプリケーションを社員のブラウザへ配信します。

セルフサービス型のCSV分析環境として非常に相性のよい構成です。


Duck-UIをDockerで構築する

それでは実際にDockerでDuck-UIを起動してみます。

Duck-UI公式でもDockerによる利用が推奨されており、公式イメージがGitHub Container Registryで公開されています。

Dockerで簡単に起動する

最も簡単なのは次のコマンドです。

docker run --name duck-ui \
  -p 5522:5522 \
  ghcr.io/caioricciuti/duck-ui:latest

起動後、

http://localhost:5522

へアクセスします。

別のPCからアクセスする場合は、

http://DockerホストのIPアドレス:5522

です。


Docker Composeで構築する

継続して利用するのであればDocker Composeにしておいた方が管理しやすいでしょう。

ディレクトリを作成します。

mkdir duck-ui
cd duck-ui

compose.yamlを作成します。

services:
  duck-ui:
    image: ghcr.io/caioricciuti/duck-ui:latest
    container_name: duck-ui
    restart: unless-stopped
    ports:
      - "5522:5522"
    environment:
      DUCK_UI_DUCKDB_WASM_USE_CDN: "false"

起動します。

docker compose up -d

確認します。

docker compose ps

ログを確認する場合は、

docker compose logs -f duck-ui

です。

停止する場合は、

docker compose down

です。

なお、現在のDuck-UIではDuckDB-WasmのローカルアセットがDockerイメージに含まれており、DUCK_UI_DUCKDB_WASM_USE_CDNのデフォルトもfalseです。したがって上記設定は必須ではありませんが、社内向けに外部CDNを利用しない方針を明示する意味で記載しています。


CSVをDuck-UIに読み込む

ブラウザでDuck-UI(http://localhost:5522)を開いたら、Import DataからCSVを選択します。

CSVの場合、

  • ヘッダーの有無
  • 型の自動判定
  • 区切り文字
  • エラー処理

などを設定できます。

例えば、

sales.csv

dataというテーブル名で取り込んだとします。

あとはSQL Editorから、

SELECT *
FROM data
LIMIT 100;

とすれば内容を確認できます。

サンプルデータ

CSVファイルとParquetファイルのサンプルを用意しました。
データがない方は、下記のZipファイルをダウンロードして試してみてください。

duckui_employee_master.parquet
duckui_sales_sample.csv

SQLのサンプル

SELECT
    e."部署",
    COUNT(*) AS "取引件数",
    SUM(s."純売上") AS "純売上合計",
    SUM(s."粗利益") AS "粗利益合計"
FROM duckui_sales_sample AS s
JOIN duckui_employee_master AS e
  ON s.employee_id = e.employee_id
GROUP BY e."部署"
ORDER BY "純売上合計" DESC;

CSVを集計する

例えばCSVが、

日付,部署,担当者,商品,金額
2026-09-01,営業部,田中,A商品,120000
2026-09-01,営業部,佐藤,B商品,85000
2026-09-01,総務部,鈴木,C商品,30000

のようなデータだったとします。

部署別の金額を集計するなら、

SELECT
    "部署",
    SUM("金額") AS "合計金額"
FROM data
GROUP BY "部署"
ORDER BY "合計金額" DESC;

担当者別なら、

SELECT
    "担当者",
    COUNT(*) AS "件数",
    SUM("金額") AS "合計金額",
    AVG("金額") AS "平均金額"
FROM data
GROUP BY "担当者"
ORDER BY "合計金額" DESC;

このような処理をブラウザだけで行えます。


複数のCSVをJOINする

DuckDBのメリットがさらに分かりやすいのがJOINです。

例えば、

sales.csv

社員番号,金額
10001,100000
10002,200000

と、

employees.csv

社員番号,氏名,部署
10001,田中,営業部
10002,佐藤,総務部

があったとします。

それぞれを、

sales
employees

というテーブルとしてImportすれば、

SELECT
    e."部署",
    SUM(s."金額") AS "合計金額"
FROM sales AS s
JOIN employees AS e
    ON s."社員番号" = e."社員番号"
GROUP BY e."部署"
ORDER BY "合計金額" DESC;

と書けます。

ExcelであればXLOOKUPなどを使ってデータを結合してからピボットテーブルで集計するような処理も、SQLなら1つのクエリで記述できます。


Parquetも扱える

Duck-UIではParquetファイルもImportできます。

ParquetはCSVとは異なり列指向でデータを保存する形式で、大量データの分析に向いています。

以前のPolarsの記事でも、

users.parquet
items.parquet
orders.parquet

を作成しました。

このとき作成したParquetファイルも、そのままDuck-UIで試すことができます。

PolarsではPythonコードを書いて処理しましたが、DuckDBならSQLで同じデータを分析できます。

例えば、

SELECT
    user_id,
    SUM(revenue) AS revenue
FROM orders
WHERE revenue > 5000
GROUP BY user_id
ORDER BY revenue DESC
LIMIT 10;

です。

同じデータをPolarsとDuckDBの両方で処理してみると、両者の思想の違いがよく分かります。


Chrome・Edgeならローカルフォルダも利用できる

Duck-UIにはPersistent Folder Accessという機能もあります。

ChromeやEdgeなどのChromium系ブラウザではFile System Access APIを利用して、PC上のフォルダをDuck-UIへ登録できます。

登録したフォルダはIndexedDBを利用して記憶され、フォルダ内の対応ファイルをツリー形式で確認してDuckDBへ取り込めます。

社内PCがEdgeに統一されているような環境であれば便利そうです。

なお、標準的なファイルImportについてはFirefoxやSafariでも利用できますが、このフォルダアクセス機能についてはChromium系ブラウザが必要です。


DuckDB-Wasmの制限

非常に便利なDuck-UIですが、Native版のDuckDBとまったく同じではありません。

DuckDB公式ドキュメントでは、DuckDB-Wasmには主に次の制約があるとされています。

  • デフォルトではシングルスレッド
  • WebAssemblyで利用できるメモリには4GBの上限がある
  • ブラウザによってはさらに厳しいメモリ制限がある

そのため、

数十MB
数百MB

程度の一般的な業務CSVを集計する用途では非常に便利ですが、

10GB
50GB
数億レコード

といった大規模データ分析では、Native版DuckDBの利用を検討した方がよいでしょう。


DuckDBには公式Web UIもある

現在のDuckDBには、DuckDB公式のWeb UIもあります。

CALL start_ui();

あるいは、

duckdb -ui

で起動できます。

こちらはDuckDB-Wasmではなく、ローカルで動作しているNative版DuckDBへWeb UIから接続する仕組みです。

そのためPCのCPU、メモリ、ファイルをNative DuckDBから利用できます。

大容量データを処理する場合はこちらの方が向いています。

一方で今回考えている、

内勤社員全員が、自分のPCにある任意のCSVをブラウザから簡単に集計する

という用途では、Duck-UIの方が配布・運用しやすいと感じました。


社内展開する場合の構成

実際に社内へ提供するなら、例えば次のような構成が考えられます。

                    社員
                     │
                     ▼
              https://duckdb/
                     │
                     ▼
          Nginx / Reverse Proxy
                     │
                     ▼
             Duck-UI Docker
                     │
          HTML / JS / WASM配信
                     │
       ┌────────┼────────┐
       ▼             ▼             ▼

    社員A PC       社員B PC      社員C PC
       │             │             │
   DuckDB-Wasm   DuckDB-Wasm   DuckDB-Wasm
       │             │             │
     A.csv         B.csv         C.csv

実際の社内環境では、

  • HTTPS
  • 社内ネットワークからのみアクセス
  • 必要に応じたリバースプロキシでの認証
  • Dockerイメージのバージョン固定
  • Dockerイメージの脆弱性チェック
  • アップデート前の動作確認

なども検討した方がよいでしょう。

特に業務利用ではlatestを永久に使うのではなく、検証したイメージをバージョンまたはdigestで固定して運用する方が安全です。

また、Duck-UIはDuckDB本体とは別のサードパーティ製Web UIである点も理解した上で導入する必要があります。


SQLを知らない社員にはSQLレシピを用意する

Duck-UIを導入しても、社員全員がSQLを書けるとは限りません。

そこで例えば社内ポータルに、

データを10件見る

SELECT *
FROM data
LIMIT 10;

件数を数える

SELECT COUNT(*)
FROM data;

条件で絞る

SELECT *
FROM data
WHERE "部署" = '営業部';

項目ごとの件数を数える

SELECT
    "部署",
    COUNT(*) AS "件数"
FROM data
GROUP BY "部署";

金額を集計する

SELECT
    "部署",
    SUM("金額") AS "合計"
FROM data
GROUP BY "部署";

重複を除いた一覧を作る

SELECT DISTINCT "部署"
FROM data;

といった「コピペして使えるSQL集」を用意すると、SQL初心者でも利用しやすくなります。

Excel関数を1つずつ覚えるのと同じように、必要なSQLから覚えてもらう形でもよいと思います。


PolarsとDuckDBの使い分け

ここまで試してみると、以前紹介したPolarsとの使い分けも見えてきます。

Polarsが向いている用途

CSV / Parquet
      ↓
   Polars
      ↓
データ加工・変換
      ↓
Pythonプログラム
  • Pythonアプリケーションの中で利用する
  • 複雑なデータ加工をする
  • データ処理を自動化する
  • 機械学習の前処理をする

といった用途ではPolarsが便利です。

DuckDBが向いている用途

CSV / Parquet
      ↓
    DuckDB
      ↓
     SQL
      ↓
検索・JOIN・集計
  • SQLでデータを調べたい
  • CSVを手軽に集計したい
  • 複数ファイルをJOINしたい
  • データベース経験者が分析したい

といった用途ではDuckDBが非常に分かりやすいです。

そしてDuck-UIを組み合わせることで、

CSV
 ↓
Webブラウザ
 ↓
DuckDB
 ↓
SQL
 ↓
集計結果

という環境を社員へ提供できます。


まとめ

DuckDBは、高速な分析処理をSQLで行える組み込み型のデータベースです。

CSVやParquetを直接扱えるため、

Excelでは少し大変だが、本格的なデータベースを用意するほどでもない

というデータ分析に非常に向いています。

さらにDuck-UIを使えばDuckDB-Wasmがブラウザ内で動作するため、社員PCへPythonやDuckDBをインストールしなくても利用できます。

今回特に面白いと思ったのは、

社員PCのCSV
     ↓
ブラウザ
     ↓
DuckDB-Wasm
     ↓
SQL
     ↓
集計結果

という構成です。

DockerサーバーはWebアプリを配信するだけで、通常のローカルWASM利用では各社員のデータ処理はそれぞれのブラウザで行われます。

内勤社員向けの簡易的なデータ分析環境として、

Duck-UI + DuckDB-Wasm

はかなり面白い選択肢だと思います。

一方、大容量データや本格的なバッチ処理ではNative DuckDB、Pythonプログラム内でのデータ加工ではPolarsというように、それぞれの特徴を理解して使い分けるのがよさそうです。

以前の記事でPolarsを試しましたが、DuckDBを触ってみることで、

PolarsはPythonからデータを処理するための強力なDataFrameエンジン
DuckDBはSQLからデータを分析するための強力な分析エンジン

という違いが、より分かりやすくなりました。

SQLを使える人にとっては、CSVファイルを受け取ってから数秒で分析を始められるDuckDBは、普段使いのツールとしてもかなり便利そうです。