mcpField
D
duckdb

duckdb

github.com/silvainfmデータ・データベース3314 回インストール13 週間前に更新
リポジトリ

duckdb について

データファイルやPythonのDataFrameをSQLで扱う、インプロセス分析データベースDuckDBの利用スキルです。データ探索、変換、結合、永続データベースの運用を扱います。

主な機能

  1. 01CSV、Parquet、JSONをSQLでクエリ
  2. 02pandasとPolarsのDataFrameをSQLで処理
  3. 03分析用の結合とウィンドウ関数
  4. 04永続データベースファイルの作成
  5. 05クエリ結果のエクスポート

活用シーン

  1. 01SQLでデータファイルを探索
  2. 02SQLでDataFrameを分析
  3. 03複数ファイルやDataFrameのデータを結合
  4. 04データ変換パイプラインの構築

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/silvainfm/claude-skills --skill duckdb

インストール後、Claude Code で/duckdbを入力して読み込まれたか確認します。

同梱ファイルreferences/integration.md

詳細

DuckDB

概要

DuckDB は高性能なインプロセス分析データベース管理システムです(「分析用 SQLite」と呼ばれることもあります)。データをインポートしたり、別のデータベースサーバーを起動したりせずに、CSV、Parquet、JSON ファイルや Python の DataFrame(pandas、Polars)に対して複雑な SQL クエリを直接実行できます。

このスキルを使用する場面

次のような場合に有効化してください。

  • データファイル(CSV、Parquet、JSON)に対して SQL クエリを実行したい
  • 複雑な分析クエリ(集計、結合、ウィンドウ関数)を実行する必要がある
  • pandas や Polars の DataFrame を SQL でクエリしたい
  • データをメモリに読み込まずに探索または分析したい
  • 中規模から大規模のデータセットを高速に分析する必要がある
  • DuckDB を明示的に指定している、または OLAP 形式の分析を行いたい

インストール

DuckDB がインストールされているか確認します。

python3 -c "import duckdb; print(duckdb.__version__)"

インストールされていない場合:

pip3 install duckdb

Polars との統合の場合:

pip3 install duckdb 'polars[pyarrow]'

主な機能

1. データファイルへの直接クエリ

DuckDB では、データをメモリに読み込まずにファイルをクエリできます。

import duckdb

# Query CSV file
result = duckdb.sql("SELECT * FROM 'data.csv' WHERE age > 25")
print(result.df())  # Convert to pandas DataFrame

# Query Parquet file
result = duckdb.sql("""
    SELECT category, SUM(amount) as total
    FROM 'sales.parquet'
    GROUP BY category
    ORDER BY total DESC
""")

# Query JSON file
result = duckdb.sql("SELECT * FROM 'users.json' LIMIT 10")

# Query multiple files with wildcards
result = duckdb.sql("SELECT * FROM 'data/*.parquet'")
2. Pandas DataFrame の操作

DuckDB では pandas の DataFrame を直接クエリできます。

import duckdb
import pandas as pd

# Create or load a DataFrame
df = pd.read_csv('data.csv')

# Query the DataFrame using SQL
result = duckdb.sql("""
    SELECT
        category,
        AVG(price) as avg_price,
        COUNT(*) as count
    FROM df
    WHERE price > 100
    GROUP BY category
    HAVING count > 5
""")

# Convert result to pandas DataFrame
result_df = result.df()
print(result_df)
3. Polars DataFrame の操作

DuckDB は Apache Arrow を使用して Polars とシームレスに統合されます。

import duckdb
import polars as pl

# Create or load a Polars DataFrame
df = pl.read_csv('data.csv')

# Query Polars DataFrame with DuckDB
result = duckdb.sql("""
    SELECT
        date_trunc('month', date) as month,
        SUM(revenue) as monthly_revenue
    FROM df
    GROUP BY month
    ORDER BY month
""")

# Convert result to Polars DataFrame
result_df = result.pl()

# For lazy evaluation, use lazy=True
lazy_result = result.pl(lazy=True)
4. 永続データベースの作成

永続ストレージ用のデータベースファイルを作成します。

import duckdb

# Connect to a persistent database (creates file if doesn't exist)
con = duckdb.connect('my_database.duckdb')

# Create table and insert data
con.execute("""
    CREATE TABLE users AS
    SELECT * FROM 'users.csv'
""")

# Query the database
result = con.execute("SELECT * FROM users WHERE age > 30").fetchdf()

# Close connection
con.close()
5. 複雑な分析クエリ

DuckDB は分析クエリに優れています。

import duckdb

# Window functions
result = duckdb.sql("""
    SELECT
        name,
        department,
        salary,
        AVG(salary) OVER (PARTITION BY department) as dept_avg,
        RANK() OVER (PARTITION BY department ORDER BY salary DESC) as dept_rank
    FROM 'employees.csv'
""")

# CTEs and subqueries
result = duckdb.sql("""
    WITH monthly_sales AS (
        SELECT
            date_trunc('month', sale_date) as month,
            product_id,
            SUM(amount) as total_sales
        FROM 'sales.parquet'
        GROUP BY month, product_id
    )
    SELECT
        m.month,
        p.product_name,
        m.total_sales,
        LAG(m.total_sales) OVER (
            PARTITION BY m.product_id
            ORDER BY m.month
        ) as prev_month_sales
    FROM monthly_sales m
    JOIN 'products.csv' p ON m.product_id = p.id
    ORDER BY m.month DESC, m.total_sales DESC
""")
6. 異なるデータソース間の結合

複数のファイルや DataFrame のデータを結合します。

import duckdb
import pandas as pd

# Load DataFrame
customers_df = pd.read_csv('customers.csv')

# Join DataFrame with Parquet file
result = duckdb.sql("""
    SELECT
        c.customer_name,
        c.email,
        o.order_date,
        o.total_amount
    FROM customers_df c
    JOIN 'orders.parquet' o ON c.customer_id = o.customer_id
    WHERE o.order_date >= '2024-01-01'
    ORDER BY o.order_date DESC
""")

よく使われるパターン

パターン 1:データの簡易探索
import duckdb

# Get table schema
duckdb.sql("DESCRIBE SELECT * FROM 'data.parquet'").show()

# Quick statistics
duckdb.sql("""
    SELECT
        COUNT(*) as rows,
        COUNT(DISTINCT user_id) as unique_users,
        MIN(created_at) as earliest_date,
        MAX(created_at) as latest_date
    FROM 'data.csv'
""").show()

# Sample data
duckdb.sql("SELECT * FROM 'large_file.parquet' USING SAMPLE 1000").show()
パターン 2:データ変換パイプライン
import duckdb

# ETL pipeline using DuckDB
con = duckdb.connect('analytics.duckdb')

# Extract and transform
con.execute("""
    CREATE TABLE clean_sales AS
    SELECT
        date_trunc('day', timestamp) as sale_date,
        UPPER(TRIM(product_name)) as product_name,
        quantity,
        price,
        quantity * price as total_amount,
        CASE
            WHEN quantity > 10 THEN 'bulk'
            ELSE 'retail'
        END as sale_type
    FROM 'raw_sales.csv'
    WHERE price > 0 AND quantity > 0
""")

# Create aggregated view
con.execute("""
    CREATE VIEW daily_summary AS
    SELECT
        sale_date,
        sale_type,
        COUNT(*) as num_sales,
        SUM(total_amount) as revenue
    FROM clean_sales
    GROUP BY sale_date, sale_type
""")

result = con.execute("SELECT * FROM daily_summary ORDER BY sale_date DESC").fetchdf()
con.close()
パターン 3:最適なパフォーマンスを実現する DuckDB と Polars の併用
import duckdb
import polars as pl

# Read multiple parquet files with Polars
df = pl.read_parquet('data/*.parquet')

# Use DuckDB for complex SQL analytics
result = duckdb.sql("""
    SELECT
        customer_segment,
        product_category,
        COUNT(DISTINCT customer_id) as customers,
        SUM(revenue) as total_revenue,
        AVG(revenue) as avg_revenue,
        PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY revenue) as median_revenue
    FROM df
    WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY customer_segment, product_category
    HAVING total_revenue > 10000
    ORDER BY total_revenue DESC
""").pl()  # Return as Polars DataFrame

# Continue processing with Polars
final_result = result.with_columns([
    (pl.col('total_revenue') / pl.col('customers')).alias('revenue_per_customer')
])
パターン 4:クエリ結果のエクスポート
import duckdb

# Export to CSV
duckdb.sql("""
    COPY (
        SELECT * FROM 'input.parquet' WHERE status = 'active'
    ) TO 'output.csv' (HEADER, DELIMITER ',')
""")

# Export to Parquet
duckdb.sql("""
    COPY (
        SELECT date, category, SUM(amount) as total
        FROM 'sales.csv'
        GROUP BY date, category
    ) TO 'summary.parquet' (FORMAT PARQUET)
""")

# Export to JSON
duckdb.sql("""
    COPY (SELECT * FROM users WHERE age > 21)
    TO 'filtered_users.json' (FORMAT JSON)
""")

パフォーマンスのヒント

  1. 大規模なデータセットには Parquet を使用する:Parquet は列指向で圧縮されており、分析クエリに最適です
  2. 早い段階でフィルタリングする:可能な場合は、ファイル読み込み時にフィルタを適用します
  3. 大規模なファイルをパーティション分割する:大規模なデータセットには DuckDB の自動パーティション分割を使用します
  4. 列を絞り込む:必要な列だけを選択します
  5. インデックスを活用する:永続データベースでは、頻繁にクエリする列にインデックスを作成します
# Good: Filter and project early
duckdb.sql("SELECT name, age FROM 'users.parquet' WHERE age > 25")

# Less efficient: Select all then filter
duckdb.sql("SELECT * FROM 'users.parquet'").df()[lambda x: x['age'] > 25]

Polars との統合

DuckDB と Polars は Apache Arrow を介してシームレスに連携します。

import duckdb
import polars as pl

# Polars for data loading and transformation
df = (
    pl.scan_parquet('data/*.parquet')
    .filter(pl.col('date') >= '2024-01-01')
    .collect()
)

# DuckDB for complex SQL analytics
result = duckdb.sql("""
    SELECT
        user_id,
        COUNT(*) as sessions,
        SUM(duration) as total_duration,
        AVG(duration) as avg_duration,
        MAX(duration) as max_duration
    FROM df
    GROUP BY user_id
    HAVING sessions > 5
""").pl()

# Back to Polars for final processing
top_users = result.top_k(10, by='total_duration')

Polars 固有の操作については polars スキルを参照してください。詳しい統合例については references/integration.md ファイルを参照してください。

エラー処理

よくある問題と解決方法:

import duckdb

try:
    result = duckdb.sql("SELECT * FROM 'data.csv'")
except duckdb.Error as e:
    print(f"DuckDB error: {e}")
except FileNotFoundError:
    print("File not found")
except Exception as e:
    print(f"Unexpected error: {e}")

リソース

コメント