.claude/skills/writing-bigquery/SKILL.md
BigQuery SQLの作成・レビュー・実行時に適用する規約とベストプラクティス。 BigQuery SQLファイルの編集、BigQueryクエリの作成、データ分析クエリの設計・実行時に自動で有効化される。 「BigQueryで〜」「BQで集計して」「SQLを書いて」「データを分析して」「クエリを作って」 「dbtモデルを確認して」「テーブルを調べて」といった発言があれば、このスキルを使うこと。 bqコマンドによるクエリ実行、スキーマ調査、データ探索にも適用される。
npx skillsauth add blackawa/dotfiles writing-bigqueryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
BigQuery SQLを書く際は、このスキルに従って規約の遵守と分析→設計→テストのワークフローを実行する。
snake_case を基本とするtmp1 のような名前は避ける)-- Good
WITH daily_revenue AS (...),
user_segments AS (...)
-- Bad
WITH t1 AS (...),
t2 AS (...)
括弧はBigQueryのエイリアスとして利用不可。ユーザーが括弧を含むカラム名を指定した場合、アンダースコアに自動変更し、変更した旨を報告する。
例: 売上(税込) → `売上_税込`
SELECT, FROM, WHERE, GROUP BY 等)WITH monthly_sales AS (
SELECT
DATE_TRUNC(order_date, MONTH) AS order_month
,customer_id
,SUM(amount) AS total_amount
FROM `project.dataset.orders`
WHERE order_date >= '2024-01-01'
GROUP BY 1, 2
)
SELECT
order_month
,COUNT(DISTINCT customer_id) AS `顧客数`
,SUM(total_amount) AS `売上合計`
FROM monthly_sales
GROUP BY 1
ORDER BY 1
タイムスタンプを人が読む日時に変換する場合は、必ず Asia/Tokyo を指定して一貫させる。
SELECT
DATETIME(created_at, 'Asia/Tokyo') AS `作成日時`
,DATE(created_at, 'Asia/Tokyo') AS `作成日`
BigQueryはスキャンしたバイト数で課金されるため、読み取るデータ量を最小化することがコスト削減とパフォーマンス向上の両方に直結する。
必要なカラムのみを明示的に指定する。SELECT * はスキャン量を不必要に増やし、コストとパフォーマンスの両方に悪影響がある。
-- Good
SELECT user_id, email, created_at
FROM `project.dataset.users`
-- Bad
SELECT *
FROM `project.dataset.users`
パーティションされたテーブルに対しては、WHERE句で必ずパーティションカラムをフィルタする。これによりスキャン範囲が限定され、コストが大幅に下がる。
-- パーティションプルーニングが効く
WHERE DATE(created_at) BETWEEN '2024-01-01' AND '2024-01-31'
-- パーティションプルーニングが効かない(関数でラップしている)
WHERE EXTRACT(MONTH FROM created_at) = 1
行間の比較が必要な場合、自己結合は出力行数を二乗的に増やすリスクがある。代わりにウィンドウ関数を使う。
-- Good: ウィンドウ関数で前回値を取得
SELECT
user_id
,order_date
,amount
,LAG(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS prev_amount
FROM `project.dataset.orders`
-- Bad: 自己結合
SELECT a.user_id, a.amount, b.amount AS prev_amount
FROM orders a
JOIN orders b ON a.user_id = b.user_id AND ...
CROSS JOINが必要な場合は、結合前にデータを可能な限り集約し、爆発的な行数増加を防ぐ。
厳密な値が不要な場面では近似集約関数を使うと高速になる。
-- 厳密なカウント
COUNT(DISTINCT user_id)
-- 概算で十分な場合(大規模データで高速)
APPROX_COUNT_DISTINCT(user_id)
本番実行前にドライランでスキャン量を確認する習慣をつける。
bq query --use_legacy_sql=false --dry_run "
SELECT user_id, created_at
FROM \`project.dataset.large_table\`
WHERE DATE(created_at) = '2024-06-01'
"
サブクエリなしでウィンドウ関数の結果をフィルタできる。重複排除パターンで特に有用。
-- Good: QUALIFYでシンプルに書く
SELECT
user_id
,email
,updated_at
FROM `project.dataset.users`
QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) = 1
-- Bad: サブクエリが必要
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn
FROM `project.dataset.users`
) WHERE rn = 1
割り算でゼロ除算エラーを回避したい場合、SAFE_DIVIDE を使うとゼロ除算時にNULLを返す。
-- Good
SAFE_DIVIDE(revenue, cost) AS roi
-- Bad: ゼロ除算でエラー
revenue / cost AS roi
-- これもOK(明示的にデフォルト値を設定したい場合)
IFNULL(SAFE_DIVIDE(revenue, cost), 0) AS roi
IFNULL(expr, default) — 単一のNULLチェックに。シンプルで意図が明確COALESCE(expr1, expr2, ...) — 複数候補からフォールバックする場合にNULLIF(expr, value) — 特定の値をNULLに変換したい場合にSELECT
IFNULL(phone, 'N/A') AS phone
,COALESCE(mobile, home_phone, office_phone) AS best_phone
,NULLIF(status, '') AS status -- 空文字をNULLに
1対多のデータを1行にまとめる場合に有効。
SELECT
customer_id
,ARRAY_AGG(
STRUCT(order_id, order_date, amount)
ORDER BY order_date DESC
) AS orders
FROM `project.dataset.orders`
GROUP BY customer_id
-- 日付の切り捨て
DATE_TRUNC(order_date, MONTH)
-- タイムスタンプ → 日付(タイムゾーン付き)
DATE(created_at, 'Asia/Tokyo')
-- 日付差分
DATE_DIFF(end_date, start_date, DAY)
-- 日付の加減算
DATE_ADD(start_date, INTERVAL 7 DAY)
DATE_SUB(end_date, INTERVAL 1 MONTH)
-- 現在日時(Asia/Tokyo)
CURRENT_DATETIME('Asia/Tokyo')
以下は避けるべきパターン。レビュー時にこれらを発見した場合は修正を提案する。
| アンチパターン | 問題 | 対策 |
|---|---|---|
| SELECT * | 不要なカラムスキャンでコスト増 | 必要カラムのみ指定 |
| パーティションフィルタ忘れ | フルスキャンになる | WHERE句でパーティションカラムを必ずフィルタ |
| 自己結合 | 出力行数が二乗的に増加 | ウィンドウ関数・PIVOT |
| ORDER BY without LIMIT | 大規模データのソートでスロット消費 | 最終出力以外のORDER BYには必ずLIMIT |
| CTEの過度なネスト | 可読性低下 | 5階層を超える場合は中間テーブルを検討 |
| JOINキーのデータ偏り | 特定スロットに負荷集中 | 事前フィルタ・分割クエリ |
| 関数内でパーティションカラムを加工 | プルーニング無効化 | 加工せず直接比較 |
新しい分析クエリを実装する場合、以下のステップを順に実行する。
まずデータの所在と構造を正確に把握する。
コードベースの確認: 可能であればdbtモデルなどデータリネージを確認できるソースを読み込み、テーブル間の関係性を理解する。
スキーマの走査: 実際のDWH内を探索し、分析対象のデータが存在するテーブル・カラムを特定する。
# テーブル一覧
bq ls project:dataset
# スキーマ確認
bq show --schema --format=prettyjson project:dataset.table
# INFORMATION_SCHEMAでカラム一覧
bq query --use_legacy_sql=false --max_rows=50 "
SELECT column_name, data_type, is_nullable
FROM \`project.dataset.INFORMATION_SCHEMA.COLUMNS\`
WHERE table_name = 'target_table'
ORDER BY ordinal_position
"
実データの調査: テーブル内のデータのバリエーションや分布を調査し、ユーザーの認識と合っているか確認する。特にNULLの割合やカーディナリティに注目する。
bq query --use_legacy_sql=false --max_rows=20 "
SELECT
status
,COUNT(*) AS cnt
,ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 1) AS pct
FROM \`project.dataset.target_table\`
GROUP BY status
ORDER BY cnt DESC
"
現状分析の結果を踏まえてクエリを作成する。本スキルのSQL記述規約・パフォーマンス規約をすべて遵守すること。
書いたクエリがユーザーの要求を満たすか、2段階で検証する。
テスト実行: クエリが構文的に正しく実行可能か確認する。
bq query --use_legacy_sql=false --max_rows=20 "
<作成したクエリ>
"
実行結果に以下のような異常がないか確認し、あればユーザーに報告する:
サンプルテスト: テスト実行結果から平均的な行を1つ選び、その行の内訳を個別に確認するクエリを書いて、同じ結果が得られるか検証する。集計ロジックの正しさを担保するための重要なステップ。
クエリ実行やスキーマ調査には bq コマンドを積極的に利用する。
# クエリ実行(結果を表示)
bq query --use_legacy_sql=false --max_rows=20 "SELECT 1"
# ドライラン(スキャン量確認、実行しない)
bq query --use_legacy_sql=false --dry_run "SELECT * FROM \`project.dataset.table\`"
# テーブル一覧
bq ls project:dataset
# テーブルスキーマ確認
bq show --schema --format=prettyjson project:dataset.table
# テーブル情報(行数・サイズ等)
bq show --format=prettyjson project:dataset.table
# クエリ結果をCSV出力
bq query --use_legacy_sql=false --format=csv "SELECT ..." > output.csv
development
X(Twitter)の特定投稿URLから原文を直接取得するスキル。 fxtwitter API(APIキー不要・無料)を使用し、ロングポスト(記事形式)の全文取得にも対応。 以下のようなリクエストで発動する: 「この投稿を取得」「ツイートの内容」「このURLの投稿を見せて」 「このXの投稿を読んで」「このツイートを取得して」。 X/TwitterのURLが含まれるメッセージで、検索ではなく特定投稿の内容取得が目的の場合に使う。 x-ai-search との棲み分け: - 検索(キーワードで複数投稿を探す)→ x-ai-search - 特定投稿の取得(URLやIDで1件取得)→ x-tweet-fetch
development
TypeScript / JavaScript コードの作成・レビュー時に適用する規約とベストプラクティス。 .ts, .tsx, .js, .jsx ファイルの編集、Node.js/Deno プロジェクトのセットアップ、 vitest/biome/tsc の実行時に自動で有効化される。
business
Slackメッセージの作成・送信時に適用する規約とベストプラクティス。 slack_send_message / slack_send_message_draft の実行時に自動で有効化される。 「Slackで連絡して」「Slackに投稿して」「スレッドに返信して」 といった発言があれば、このスキルを使うこと。
development
Python コードの作成・レビュー時に適用する規約とベストプラクティス。 Pythonファイルの編集、Pythonプロジェクトのセットアップ、 pytest/ruff/mypyの実行時に自動で有効化される。