query について
DuckDB でファイルや保存済みセッションのデータを検索するスキルです。自然言語から SQL を作成し、結果サイズを確認してから実行し、結果の解釈やエラー対応も行います。
主な機能
- 01保存済みセッションと単発モードの判定
- 02自然言語から SQL を生成
- 03実行前の結果サイズ推定
- 04ファイルクエリのサンドボックス実行
- 05結果の解釈とエラー対応
活用シーン
- 01CSV や Parquet ファイルの検索
- 02保存済みセッションのテーブル調査
- 03自然言語の質問への SQL による回答
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ duckdb/ duckdb-skills --skill queryインストール後、Claude Code で/queryを入力して読み込まれたか確認します。
詳細
DuckDB を使用してユーザーのデータ検索を支援します。
入力:$@
以下の手順を順番に実行します。
ステップ 1 — 状態を解決し、モードを決定する
次のいずれかの場所に既存の状態ファイルがあるか確認します。
STATE_DIR=""
test -f .duckdb-skills/state.sql && STATE_DIR=".duckdb-skills"
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
PROJECT_ID="$(echo "$PROJECT_ROOT" | tr '/' '-')"
test -f "$HOME/.duckdb-skills/$PROJECT_ID/state.sql" && STATE_DIR="$HOME/.duckdb-skills/$PROJECT_ID"
見つかった場合は、参照先のデータベースに引き続きアクセスできることを確認します。
duckdb -init "$STATE_DIR/state.sql" -c "SHOW DATABASES;"
次に、モードを決定します。
- アドホックモード:
--fileフラグが指定されている場合、SQL でファイルパス/リテラルが参照されている場合(例:FROM 'data.csv')、またはSTATE_DIRが空の場合。 - セッションモード:
STATE_DIRが設定されており、入力でテーブル名が参照されている場合、自然言語の場合、またはファイル参照を含まない SQL の場合。
状態ファイルが存在せず、ファイルも参照されていない場合は、:memory: を対象とするアドホックモードに切り替えます。この場合、ユーザーは SQL でファイルを直接参照する必要があります。
状態ファイルが存在するものの、ファイル内の ATTACH が失敗した場合は、ユーザーに警告してアドホックモードに切り替えます。
ステップ 2 — DuckDB がインストールされているか確認する
command -v duckdb
見つからない場合は、/duckdb-skills:install-duckdb に処理を委任してから続行します。
ステップ 3 — 必要に応じて SQL を生成する
入力が自然言語(有効な SQL ではない)の場合は、以下の Friendly SQL リファレンスを使用して SQL を生成します。
セッションモードでは、クエリ生成に必要な情報を得るため、最初にスキーマを取得します。
duckdb -init "$STATE_DIR/state.sql" -csv -c "
SELECT table_name FROM duckdb_tables() ORDER BY table_name;
"
次に、関連するテーブルについて以下を実行します。
duckdb -init "$STATE_DIR/state.sql" -csv -c "DESCRIBE <table_name>;"
スキーマの情報と Friendly SQL リファレンスを使用して、最適なクエリを生成します。
ステップ 4 — 結果のサイズを推定する
実行前に、クエリが非常に大きな結果を生成し、この会話に返す際に過剰なトークンを消費する可能性があるか推定します。
セッションモード — 対象テーブルの行数を確認します。
duckdb -init "$STATE_DIR/state.sql" -csv -c "
SELECT table_name, estimated_size, column_count
FROM duckdb_tables()
WHERE table_name IN ('<table1>', '<table2>');
"
アドホックモード — データソースを調べます。
duckdb :memory: -csv -c "
SET allowed_paths=['FILE_PATH'];
SET enable_external_access=false;
SET allow_persistent_secrets=false;
SET lock_configuration=true;
SELECT count() AS row_count FROM 'FILE_PATH';
"
評価:
- クエリに
LIMIT、count()、または出力を制限するその他の集計がすでに含まれている場合 → 安全です。そのまま続行します。 - データソースが 100 万行を超え、クエリに
LIMITまたは集計が含まれていない場合は、ユーザーに次のように伝えます。 「このクエリは非常に大きな結果セットを返す可能性があります。ここに表示すると多くのトークンを消費し、コストも増加します。LIMIT 1000または集計を追加して、出力を扱いやすくすることをおすすめします。」 そのまま実行してよいか、確認を求めます。 - データサイズが 10 GB を超える場合は、さらに次のように警告します。 「このテーブルは 10 GB を超えているため、クエリの完了に時間がかかる場合があります。」 ユーザーが確認したら続行します。
DESCRIBE、SUMMARIZE、集計、count() など、本質的に結果が制限されているクエリでは、この手順を省略します。
ステップ 5 — クエリを実行する
アドホックモード(サンドボックス化され、参照されたファイルのみアクセス可能):
duckdb :memory: -csv <<'SQL'
SET allowed_paths=['FILE_PATH'];
SET enable_external_access=false;
SET allow_persistent_secrets=false;
SET lock_configuration=true;
<QUERY>;
SQL
FILE_PATH を、クエリまたは --file 引数から抽出した実際のファイルパスに置き換えます。
複数のファイルが参照されている場合は、すべてのパスを allowed_paths リストに含めます。
セッションモード(ユーザーが信頼するデータベース):
duckdb -init "$STATE_DIR/state.sql" -csv -c "<QUERY>"
複数行のクエリでは、-init とヒアドキュメントを使用します。
duckdb -init "$STATE_DIR/state.sql" -csv <<'SQL'
<QUERY>;
SQL
シェルのクォートに関する問題を避けるため、複数行のクエリには必ずヒアドキュメント(<<'SQL')を使用します。
ステップ 6 — エラーに対処する
- 構文エラー:エラーを表示し、修正したクエリを提案して再実行します。
- 拡張機能が見つからない場合(例:
Extension "X" not loaded):/duckdb-skills:install-duckdb <ext>に処理を委任してから再試行します。 - テーブルが見つからない場合(セッションモード):
FROM duckdb_tables()を使って利用可能なテーブルを一覧表示し、修正案を提案します。 - ファイルが見つからない場合(アドホックモード):
find "$PWD" -name "<filename>" 2>/dev/nullを使ってファイルを検索し、修正したパスを提案します。 - DuckDB エラーが解消しない場合、または内容が不明確な場合:
/duckdb-skills:duckdb-docs <error message or relevant keywords>を使用して、解決方法をドキュメントで検索してから、修正を適用して再試行します。
ステップ 7 — 結果を提示する
クエリの出力をユーザーに表示します。結果が 100 行を超える場合は、出力が切り詰められていることを伝え、クエリに LIMIT を追加するよう提案します。
自然言語の質問の場合は、結果の簡単な解釈も提示します。
DuckDB Friendly SQL リファレンス
SQL を生成する際は、以下の DuckDB の慣用的な構文を優先します。
コンパクトな句
- FROM を先に記述:
FROM table WHERE x > 10(暗黙のSELECT *) - GROUP BY ALL:集約関数以外のすべての列で自動的にグループ化します
- ORDER BY ALL:決定的な結果を得るため、すべての列で並べ替えます
- SELECT * EXCLUDE (col1, col2):ワイルドカードから列を除外します
- SELECT * REPLACE (expr AS col):列をその場で変換します
- UNION ALL BY NAME:列の順序が異なるテーブルを結合します
- 割合で指定する LIMIT:
LIMIT 10%は行の割合を返します - プレフィックスエイリアス:
SELECT 42 AS xの代わりにSELECT x: 42 - SELECT リストでは末尾のカンマを使用できます
クエリ機能
- count():
count(*)は不要です - 再利用可能なエイリアス:WHERE / GROUP BY / HAVING で列エイリアスを使用します
- ラテラル列エイリアス:
SELECT i+1 AS j, j+2 AS k - COLUMNS(*):列全体に式を適用します。正規表現、EXCLUDE、REPLACE、ラムダに対応しています
- FILTER 句:条件付き集約には
count() FILTER (WHERE x > 10)を使用します - GROUPING SETS / CUBE / ROLLUP:高度な複数レベルの集約
- グループごとの上位 N 件:
max(col, 3)は上位 3 件をリストで返します。arg_max(arg, val, n)、min_by(arg, val, n)も使用できます - DESCRIBE table_name:スキーマの概要(列名と型)
- SUMMARIZE table_name:統計プロファイルをすぐに表示します
- PIVOT / UNPIVOT:ワイド形式とロング形式を相互に変換します
- SET VARIABLE x = expr:SQL レベルの変数を定義し、
getvariable('x')で参照します
データのインポート
- ファイルを直接クエリ:
FROM 'file.csv'、FROM 'data.parquet' - グロブ:
FROM 'data/part-*.parquet'で複数のファイルを読み込みます - 自動検出:CSV のヘッダーとスキーマを自動的に推定します
式と型
- ドット演算子の連結:
'hello'.upper()またはcol.trim().lower() - リスト内包表記:
[x*2 FOR x IN list_col] - リスト/文字列のスライス:
col[1:3]、負のインデックスcol[-1] - STRUCT. 記法*:
SELECT s.* FROM (SELECT {'a': 1, 'b': 2} AS s) - 角括弧のリスト:
[1, 2, 3] - format():文字列の書式設定には
format('{}->{}', a, b)を使用します
結合
- ASOF 結合:順序付けされたデータ(タイムスタンプなど)で近似一致させます
- POSITIONAL 結合:キーではなく位置で行を対応付けます
- LATERAL 結合:サブクエリ内で先行するテーブル式を参照します
データの変更
- CREATE OR REPLACE TABLE:事前の
DROP TABLE IF EXISTSは不要です - CREATE TABLE ... AS SELECT(CTAS):クエリ結果からテーブルを作成します
- INSERT INTO ... BY NAME:位置ではなく名前で列を対応付けます
- INSERT OR IGNORE INTO / INSERT OR REPLACE INTO:アップサートのパターン