データ分析をSQLだけでやる【DuckDB + Turso + jq + yq 完全活用ガイド】
データ分析の現場では、PythonのPandasが広く使われています。しかし、Python環境の構築やライブラリ管理は手間がかかるものです。大規模データではメモリ不足に悩むこともあります。
もし、これらの課題をコマンドラインで解決できるとしたらどうでしょうか。Pythonに頼らず、軽量かつ高速なデータ分析環境を構築できます。本記事では、DuckDB、Turso、jq、yqを組み合わせた新しいデータ分析手法を紹介します。CSV、JSON、YAMLといった様々な形式のデータをSQLで自在に操る方法を解説します。
このツールの組み合わせが最強な理由
このツール群の組み合わせは、シェルスクリプトだけで完結するデータ処理パイプラインを構築します。 DuckDBは、高速なSQL分析エンジンとして中核を担います。ファイルデータを直接クエリできる点が強みです。 jqとyqは、JSONやYAMLなどの構造化データを柔軟に前処理・後処理します。複雑なデータ形式も適切に整形できます。 Tursoは、分析結果をクラウド上で永続化し、共有するための軽量なデータベースを提供します。 これら全てがコマンドラインで連携し、Pandasに依存しない高速かつ効率的なデータ分析を実現します。
各ツールの役割と担当領域
DuckDB
DuckDBは、高性能な分析データベースシステムです。特にOLAP(オンライン分析処理)に特化しています。
Pythonなどの言語に依存せず、単体のCLIアプリケーションとして動作します。
CSVやParquetファイルなどをSQLのFROM句で直接参照できます。これにより、データのインポートが非常に簡単です。
標準的なSQLだけでなく、複雑なサブクエリやウィンドウ関数もサポートしています。
ファイルベースでのデータ分析において、高速かつ強力なSQLエンジンとして機能します。
Turso (libSQL)
Tursoは、オープンソースのSQLiteフォークであるlibSQLを基盤としています。軽量なクラウドデータベースサービスです。 SQLite互換のAPIを提供し、エッジ環境やサーバーレス環境での利用に適しています。 分析結果を永続化したり、他のアプリケーションと共有する用途に最適です。 データベースはRustで再実装されており、高いパフォーマンスと信頼性を誇ります。 DuckDBで処理したデータをTursoに保存し、ウェブサービスなどから利用する連携が可能です。
jq
jqは、コマンドラインでJSONデータを処理するための強力なツールです。sedやawkのようにJSONを扱います。
JSONデータのフィルタリング、変換、整形を柔軟に行えます。
複雑にネストしたJSON構造から特定の情報を抽出する際に非常に便利です。 jq -rオプションを使うと、生データ(raw output)として出力できます。
これにより、JSONデータをCSV形式に変換し、DuckDBで読み込めるようにする前処理が可能です。
yq
yqは、jqライクな構文でYAMLファイルを扱うコマンドラインツールです。JSON、XML、INIなどもサポートします。
YAML形式の設定ファイルの読み書きや、特定のキーの値の抽出、更新が容易です。
JSONとYAML間の相互変換も得意としています。
データ分析の条件をYAMLファイルで管理し、それをyqで読み込んでSQLクエリに組み込む、といった使い方ができます。
様々な構造化データを統一的なインターフェースで処理できる点が強みです。
実際のワークフロー:ECサイトの注文データ分析
ここでは、ECサイトの注文データを想定した分析ワークフローを紹介します。
Web APIからJSON形式で取得したデータをjqで整形します。
その後、DuckDBでSQL分析を行い、結果をTursoに保存します。
分析のパラメータはyqを使ってYAMLファイルから読み込みます。
1. 注文データをJSON形式で取得し、整形する(jq)
まず、架空のWeb APIから注文データをJSON形式で取得します。
ここではorders.jsonというファイルに保存されたデータがあると仮定します。
[
{
"order_id": "A001",
"user_id": "U101",
"items": [
{"product_id": "P001", "name": "Laptop", "price": 120000, "quantity": 1},
{"product_id": "P002", "name": "Mouse", "price": 3000, "quantity": 2}
],
"total_amount": 126000,
"order_date": "2023-01-15T10:30:00Z"
},
{
"order_id": "A002",
"user_id": "U102",
"items": [
{"product_id": "P003", "name": "Keyboard", "price": 8000, "quantity": 1}
],
"total_amount": 8000,
"order_date": "2023-01-16T14:00:00Z"
}
]
このJSONデータをDuckDBで扱いやすいフラットなCSV形式に変換します。jqを使って、注文ID、ユーザーID、商品名、単価、数量、注文日を抽出します。
ネストしたitems配列を展開し、各商品を行として出力します。
jq -r '
.[] | .order_id as $order_id | .user_id as $user_id | .order_date as $order_date |
.items[] | [$order_id, $user_id, .product_id, .name, .price, .quantity, $order_date] | @csv
' orders.json > orders.csv
このコマンドは、orders.jsonから以下の内容を持つorders.csvを生成します。
"A001","U101","P001","Laptop",120000,1,"2023-01-15T10:30:00Z"
"A001","U101","P002","Mouse",3000,2,"2023-01-15T10:30:00Z"
"A002","U102","P003","Keyboard",8000,1,"2023-01-16T14:00:00Z"
2. DuckDBでCSVデータをSQL分析する
生成されたorders.csvをDuckDBで読み込み、様々な集計を行います。
例えば、日別の売上合計と注文数を計算してみましょう。
duckdb orders.duckdb <<EOF
CREATE OR REPLACE TABLE daily_sales AS
SELECT
STRFTIME(order_date, '%Y-%m-%d') AS sales_date,
SUM(price * quantity) AS total_sales,
COUNT(DISTINCT order_id) AS total_orders
FROM 'orders.csv'
GROUP BY sales_date
ORDER BY sales_date;
SELECT * FROM daily_sales;
EOF
このDuckDBスクリプトは、orders.csvから日別の売上と注文数を計算します。
結果はdaily_salesというテーブルに保存され、その内容が表示されます。
DuckDBはインメモリで高速に処理を進めます。
3. 分析結果をTursoに保存する
DuckDBで計算したdaily_salesテーブルをTursoに保存します。
まず、Turso CLIをインストールし、データベースを作成します。
# Turso CLIのインストール (homebrewの場合)
# brew install turso-cli
# Tursoデータベースの作成 (your-db-name は任意の名前)
turso db create your-db-name
データベースURLと認証トークンを取得し、環境変数に設定します。
export TURSO_DB_URL=$(turso db show your-db-name --url)
export TURSO_AUTH_TOKEN=$(turso account tokens create "cli-token")
次に、DuckDBのdaily_salesテーブルをCSVファイルとしてエクスポートします。
duckdb orders.duckdb <<EOF
COPY daily_sales TO 'daily_sales.csv' (HEADER, DELIMITER ',');
EOF
Tursoデータベースにテーブルを作成し、CSVデータをインポートします。
turso db shell $TURSO_DB_URL -u $TURSO_AUTH_TOKEN <<EOF
CREATE TABLE IF NOT EXISTS daily_sales (
sales_date TEXT PRIMARY KEY,
total_sales INTEGER,
total_orders INTEGER
);
.mode csv
.import daily_sales.csv daily_sales
EOF
これで、日別売上データがTursoデータベースに保存されました。 他のアプリケーションからこのデータにアクセスできるようになります。
4. 分析条件をYAMLで管理する(yq)
分析の条件をYAMLファイルで管理する例を見てみましょう。analysis_config.yamlというファイルを作成します。
analysis:
min_total_sales: 50000
start_date: "2023-01-01"
end_date: "2023-01-31"
この設定ファイルからmin_total_salesの値を取得し、DuckDBのクエリに適用します。
MIN_SALES=$(yq '.analysis.min_total_sales' analysis_config.yaml)
duckdb orders.duckdb <<EOF
SELECT *
FROM daily_sales
WHERE total_sales > ${MIN_SALES};
EOF
これにより、YAMLファイルの設定に基づいて動的にクエリを調整できます。 設定変更のたびにシェルスクリプトを編集する手間が省けます。
設定ファイル・dotfilesの例
データ分析作業を効率化するための設定例を紹介します。~/.bashrcや~/.zshrcに以下のエイリアスや関数を追加すると便利です。
# ~/.bashrc または ~/.zshrc に追加
# DuckDB CLIを起動するエイリアス
alias ddb='duckdb'
# jqでJSONをCSV形式に変換する汎用関数
# 最初の行にヘッダー、以降にデータを出力
json_to_csv() {
if [ -z "$1" ]; then
echo "Usage: json_to_csv <json_file>"
return 1
fi
jq -r '
if type == "array" then
(.[0] | keys_unsorted) as $keys |
$keys,
(.[] | map(. as $val | if $val | type == "object" or $val | type == "array" then ($val | tojson) else ($val | tostring) end))
else
(keys_unsorted) as $keys |
$keys,
(map(. as $val | if $val | type == "object" or $val | type == "array" then ($val | tojson) else ($val | tostring) end))
end | @csv
' "$1"
}
# yqでYAMLから特定パスの値を取得する関数
get_yaml_value() {
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: get_yaml_value <yaml_file> <jq_path>"
return 1
fi
yq "$2" "$1"
}
# Tursoデータベースへの接続情報を環境変数に設定(セキュリティに注意)
# export TURSO_DB_URL="libsql://your-db-name-your-org.turso.io"
# export TURSO_AUTH_TOKEN="your_auth_token"
# Turso CLIをより簡潔に使うエイリアス
# alias turso_shell='turso db shell $TURSO_DB_URL -u $TURSO_AUTH_TOKEN'
これらの設定を反映させるには、シェルを再起動するか、source ~/.bashrc(または~/.zshrc)を実行してください。
よくあるトラブルと解決法
1. jq/yqのパースエラーが発生する
症状: JSON/YAMLファイルの形式が正しくない、またはエスケープ文字の問題。解決法:
jqの場合:jq -rで生データ出力、jq . file.jsonで構造を確認します。yqの場合:yq -Pで出力の整形、yq e . file.yamlでエラーメッセージを確認します。- 多くの場合、入力データに予期しない文字が含まれています。
2. DuckDBでファイルが読み込めない
症状: SELECT * FROM 'myfile.csv';でエラーが発生する。解決法:
- ファイルパスが正しいか確認してください。絶対パスを指定すると確実です。
- ファイル名に特殊文字が含まれていないか確認します。
read_csv_auto('myfile.csv')のように関数を使うと、より柔軟に読み込めます。- CSVの区切り文字やヘッダーの有無が原因の場合もあります。
DESCRIBE 'myfile.csv';でDuckDBがどのようにファイルを認識しているか確認できます。
3. jq/yqの出力が期待と異なる
症状: 抽出したい値が取れない、または余計なクォーテーションが付く。解決法:
jqのクエリパスを再確認してください。配列は.[n]、オブジェクトは.keyでアクセスします。-rオプション(--raw-output)を付与すると、文字列のクォーテーションが削除されます。- 複雑な変換には
to_entriesやfrom_entries、mapなどを活用します。 jqlang.orgやyqの公式ドキュメントで構文を再確認しましょう。
4. Tursoへのデータインポートが失敗する
症状: turso db shell .importコマンドでエラーが出る。解決法:
- インポート先のテーブルスキーマがCSVの列と一致しているか確認します。
- CSVファイルのヘッダー行を
CREATE TABLEの列名と揃えるのが基本です。 turso db shellに入った後、.mode csvを設定してから.importを実行します。- TursoのデータベースURLや認証トークンが正しく設定されているか確認してください。
5. dotfilesの変更が反映されない
症状: ~/.bashrcや~/.zshrcに書いたエイリアスや関数が使えない。解決法:
- シェルを再起動してください。
source ~/.bashrc(またはsource ~/.zshrc)を実行し、設定を手動で読み込み直してください。- 記述ミスがないか、ファイルの内容を再度確認しましょう。
今日からできる実行プラン
ステップ1: 必要なツールをインストールする
まずは、本記事で紹介したツールをあなたの環境にインストールしましょう。
- DuckDB: 公式サイトのインストールページを参照してください。
- jq: GitHubリリースページからバイナリをダウンロードするか、パッケージマネージャーでインストールします。
- yq: GitHubリリースページからバイナリをダウンロードするか、パッケージマネージャーでインストールします。
- Turso CLI: 公式ドキュメントに従ってインストールしてください。
ステップ2: 小さなデータで各ツールを試す
各ツールの基本的な操作を理解するために、小さなサンプルデータを作成し試してみましょう。
- 簡単なCSVファイルを作成し、DuckDBで
SELECT * FROM 'sample.csv';を実行します。 - 簡単なJSONファイルを作成し、
jq '.' sample.jsonやjq '.key' sample.jsonを試します。 - 簡単なYAMLファイルを作成し、
yq '.' sample.yamlやyq '.key' sample.yamlを試します。
ステップ3: パイプライン連携を体験する
ステップ2で作成したサンプルデータを使って、ツールの連携を試します。
- JSONデータを
jqでCSVに変換し、そのCSVをDuckDBで読み込むパイプラインを構築します。 - DuckDBで集計した結果をCSVとして出力し、そのCSVをTursoにインポートする流れを試します。
- YAMLファイルに設定値を書き、
yqでその値を読み取ってDuckDBのクエリに組み込んでみましょう。
これらのステップを踏むことで、Pandasなしでのデータ分析スキルが身につきます。 コマンドラインでデータ分析を完結させる快適さをぜひ体験してください。