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をインストールしなくても、
- ブラウザを開く
- 自分のPCにあるCSVを読み込む
- SQLを書く
- 集計結果を見る
という使い方ができます。
しかも通常の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を高速に扱えるという点ではかなり似ています。
しかし、中心となる考え方が異なります。
| 項目 | Polars | DuckDB |
|---|---|---|
| 種類 | DataFrameライブラリ | 分析用DBMS |
| 主な操作 | Python Expression | SQL |
| 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ファイルをダウンロードして試してみてください。


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は、普段使いのツールとしてもかなり便利そうです。