mcpField
S
sql-queries

sql-queries

github.com/anthropicsデータ・データベース33.6k4.5k 回インストール1 日前に更新
リポジトリ

sql-queries について

主要なデータウェアハウスの方言を扱う分析担当者やデータ実務者向けの、SQL作成スキルです。方言別の構文と性能に関する指針に加え、分析パターンやデバッグ方法をまとめています。

主な機能

  1. 01方言別SQLリファレンス
  2. 02データウェアハウスの性能指針
  3. 03ウィンドウ関数とCTEのパターン
  4. 04コホート・ファネル分析
  5. 05重複排除の例
  6. 06クエリのデバッグ方法

活用シーン

  1. 01データウェアハウス間でクエリを調整
  2. 02リテンションやファネル分析の構築
  3. 03SQLエラーや性能問題の調査

インストール

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

$npx skills add https://github.com/anthropics/knowledge-work-plugins --skill sql-queries

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

詳細

SQL クエリスキル

主要なすべてのデータウェアハウスの方言で、正確で高性能かつ読みやすい SQL を記述します。

方言別リファレンス

PostgreSQL(Aurora、RDS、Supabase、Neon を含む)

日付/時刻:

-- Current date/time
CURRENT_DATE, CURRENT_TIMESTAMP, NOW()

-- Date arithmetic
date_column + INTERVAL '7 days'
date_column - INTERVAL '1 month'

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
EXTRACT(YEAR FROM created_at)
EXTRACT(DOW FROM created_at)  -- 0=Sunday

-- Format
TO_CHAR(created_at, 'YYYY-MM-DD')

文字列関数:

-- Concatenation
first_name || ' ' || last_name
CONCAT(first_name, ' ', last_name)

-- Pattern matching
column ILIKE '%pattern%'  -- case-insensitive
column ~ '^regex_pattern$'  -- regex

-- String manipulation
LEFT(str, n), RIGHT(str, n)
SPLIT_PART(str, delimiter, position)
REGEXP_REPLACE(str, pattern, replacement)

配列と JSON:

-- JSON access
data->>'key'  -- text
data->'nested'->'key'  -- json
data#>>'{path,to,key}'  -- nested text

-- Array operations
ARRAY_AGG(column)
ANY(array_column)
array_column @> ARRAY['value']

パフォーマンスのヒント:

  • クエリのプロファイリングには EXPLAIN ANALYZE を使用します
  • 頻繁にフィルターや結合に使う列にインデックスを作成します
  • 相関サブクエリでは IN より EXISTS を使用します
  • よく使うフィルター条件には部分インデックスを使用します
  • 同時アクセスにはコネクションプーリングを使用します

Snowflake

日付/時刻:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP(), SYSDATE()

-- Date arithmetic
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
YEAR(created_at), MONTH(created_at), DAY(created_at)
DAYOFWEEK(created_at)

-- Format
TO_CHAR(created_at, 'YYYY-MM-DD')

文字列関数:

-- Case-insensitive by default (depends on collation)
column ILIKE '%pattern%'
REGEXP_LIKE(column, 'pattern')

-- Parse JSON
column:key::string  -- dot notation for VARIANT
PARSE_JSON('{"key": "value"}')
GET_PATH(variant_col, 'path.to.key')

-- Flatten arrays/objects
SELECT f.value FROM table, LATERAL FLATTEN(input => array_col) f

半構造化データ:

-- VARIANT type access
data:customer:name::STRING
data:items[0]:price::NUMBER

-- Flatten nested structures
SELECT
    t.id,
    item.value:name::STRING as item_name,
    item.value:qty::NUMBER as quantity
FROM my_table t,
LATERAL FLATTEN(input => t.data:items) item

パフォーマンスのヒント:

  • 大規模なテーブルでは、従来のインデックスではなくクラスタリングキーを使用します
  • パーティションプルーニングのため、クラスタリングキーの列でフィルターします
  • クエリの複雑さに応じて適切なウェアハウスサイズを設定します
  • 高コストのクエリを再実行しないよう、RESULT_SCAN(LAST_QUERY_ID()) を使用します
  • ステージングや一時データには一時テーブルを使用します

BigQuery(Google Cloud)

日付/時刻:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP()

-- Date arithmetic
DATE_ADD(date_column, INTERVAL 7 DAY)
DATE_SUB(date_column, INTERVAL 1 MONTH)
DATE_DIFF(end_date, start_date, DAY)
TIMESTAMP_DIFF(end_ts, start_ts, HOUR)

-- Truncate to period
DATE_TRUNC(created_at, MONTH)
TIMESTAMP_TRUNC(created_at, HOUR)

-- Extract parts
EXTRACT(YEAR FROM created_at)
EXTRACT(DAYOFWEEK FROM created_at)  -- 1=Sunday

-- Format
FORMAT_DATE('%Y-%m-%d', date_column)
FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', ts_column)

文字列関数:

-- No ILIKE, use LOWER()
LOWER(column) LIKE '%pattern%'
REGEXP_CONTAINS(column, r'pattern')
REGEXP_EXTRACT(column, r'pattern')

-- String manipulation
SPLIT(str, delimiter)  -- returns ARRAY
ARRAY_TO_STRING(array, delimiter)

配列と構造体:

-- Array operations
ARRAY_AGG(column)
UNNEST(array_column)
ARRAY_LENGTH(array_column)
value IN UNNEST(array_column)

-- Struct access
struct_column.field_name

パフォーマンスのヒント:

  • スキャンするバイト数を減らすため、必ずパーティション列(通常は日付)でフィルターします
  • パーティション内で頻繁にフィルターする列にはクラスタリングを使用します
  • 大規模なカーディナリティの推定には APPROX_COUNT_DISTINCT() を使用します
  • SELECT * は避けます。料金はスキャンしたバイト数に基づきます
  • パラメーター化スクリプトには DECLARE と SET を使用します
  • 大規模なクエリを実行する前に、ドライランでクエリコストを確認します

Redshift(Amazon)

日付/時刻:

-- Current date/time
CURRENT_DATE, GETDATE(), SYSDATE

-- Date arithmetic
DATEADD(day, 7, date_column)
DATEDIFF(day, start_date, end_date)

-- Truncate to period
DATE_TRUNC('month', created_at)

-- Extract parts
EXTRACT(YEAR FROM created_at)
DATE_PART('dow', created_at)

文字列関数:

-- Case-insensitive
column ILIKE '%pattern%'
REGEXP_INSTR(column, 'pattern') > 0

-- String manipulation
SPLIT_PART(str, delimiter, position)
LISTAGG(column, ', ') WITHIN GROUP (ORDER BY column)

パフォーマンスのヒント:

  • データを同じノードに配置する結合向けに分散キー(DISTKEY)を設計します
  • 頻繁にフィルターする列にはソートキー(SORTKEY)を使用します
  • クエリプランの確認には EXPLAIN を使用します
  • ノード間のデータ移動を避けます(DS_BCAST と DS_DIST に注意します)
  • 定期的に ANALYZE と VACUUM を実行します
  • スキーマの柔軟性には遅延バインディングビューを使用します

Databricks SQL

日付/時刻:

-- Current date/time
CURRENT_DATE(), CURRENT_TIMESTAMP()

-- Date arithmetic
DATE_ADD(date_column, 7)
DATEDIFF(end_date, start_date)
ADD_MONTHS(date_column, 1)

-- Truncate to period
DATE_TRUNC('MONTH', created_at)
TRUNC(date_column, 'MM')

-- Extract parts
YEAR(created_at), MONTH(created_at)
DAYOFWEEK(created_at)

Delta Lake の機能:

-- Time travel
SELECT * FROM my_table TIMESTAMP AS OF '2024-01-15'
SELECT * FROM my_table VERSION AS OF 42

-- Describe history
DESCRIBE HISTORY my_table

-- Merge (upsert)
MERGE INTO target USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

パフォーマンスのヒント:

  • クエリのパフォーマンス向上には、Delta Lake の OPTIMIZE と ZORDER を使用します
  • 計算負荷の高いクエリには Photon エンジンを活用します
  • 頻繁にアクセスするデータセットには CACHE TABLE を使用します
  • カーディナリティの低い日付列でパーティション分割します

よく使われる SQL パターン

ウィンドウ関数
-- Ranking
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)
RANK() OVER (PARTITION BY category ORDER BY revenue DESC)
DENSE_RANK() OVER (ORDER BY score DESC)

-- Running totals / moving averages
SUM(revenue) OVER (ORDER BY date_col ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total
AVG(revenue) OVER (ORDER BY date_col ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7d

-- Lag / Lead
LAG(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as prev_value
LEAD(value, 1) OVER (PARTITION BY entity ORDER BY date_col) as next_value

-- First / Last value
FIRST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
LAST_VALUE(status) OVER (PARTITION BY user_id ORDER BY created_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)

-- Percent of total
revenue / SUM(revenue) OVER () as pct_of_total
revenue / SUM(revenue) OVER (PARTITION BY category) as pct_of_category
読みやすさのための CTE
WITH
-- Step 1: Define the base population
base_users AS (
    SELECT user_id, created_at, plan_type
    FROM users
    WHERE created_at >= DATE '2024-01-01'
      AND status = 'active'
),

-- Step 2: Calculate user-level metrics
user_metrics AS (
    SELECT
        u.user_id,
        u.plan_type,
        COUNT(DISTINCT e.session_id) as session_count,
        SUM(e.revenue) as total_revenue
    FROM base_users u
    LEFT JOIN events e ON u.user_id = e.user_id
    GROUP BY u.user_id, u.plan_type
),

-- Step 3: Aggregate to summary level
summary AS (
    SELECT
        plan_type,
        COUNT(*) as user_count,
        AVG(session_count) as avg_sessions,
        SUM(total_revenue) as total_revenue
    FROM user_metrics
    GROUP BY plan_type
)

SELECT * FROM summary ORDER BY total_revenue DESC;
コホートリテンション
WITH cohorts AS (
    SELECT
        user_id,
        DATE_TRUNC('month', first_activity_date) as cohort_month
    FROM users
),
activity AS (
    SELECT
        user_id,
        DATE_TRUNC('month', activity_date) as activity_month
    FROM user_activity
)
SELECT
    c.cohort_month,
    COUNT(DISTINCT c.user_id) as cohort_size,
    COUNT(DISTINCT CASE
        WHEN a.activity_month = c.cohort_month THEN a.user_id
    END) as month_0,
    COUNT(DISTINCT CASE
        WHEN a.activity_month = c.cohort_month + INTERVAL '1 month' THEN a.user_id
    END) as month_1,
    COUNT(DISTINCT CASE
        WHEN a.activity_month = c.cohort_month + INTERVAL '3 months' THEN a.user_id
    END) as month_3
FROM cohorts c
LEFT JOIN activity a ON c.user_id = a.user_id
GROUP BY c.cohort_month
ORDER BY c.cohort_month;
ファネル分析
WITH funnel AS (
    SELECT
        user_id,
        MAX(CASE WHEN event = 'page_view' THEN 1 ELSE 0 END) as step_1_view,
        MAX(CASE WHEN event = 'signup_start' THEN 1 ELSE 0 END) as step_2_start,
        MAX(CASE WHEN event = 'signup_complete' THEN 1 ELSE 0 END) as step_3_complete,
        MAX(CASE WHEN event = 'first_purchase' THEN 1 ELSE 0 END) as step_4_purchase
    FROM events
    WHERE event_date >= CURRENT_DATE - INTERVAL '30 days'
    GROUP BY user_id
)
SELECT
    COUNT(*) as total_users,
    SUM(step_1_view) as viewed,
    SUM(step_2_start) as started_signup,
    SUM(step_3_complete) as completed_signup,
    SUM(step_4_purchase) as purchased,
    ROUND(100.0 * SUM(step_2_start) / NULLIF(SUM(step_1_view), 0), 1) as view_to_start_pct,
    ROUND(100.0 * SUM(step_3_complete) / NULLIF(SUM(step_2_start), 0), 1) as start_to_complete_pct,
    ROUND(100.0 * SUM(step_4_purchase) / NULLIF(SUM(step_3_complete), 0), 1) as complete_to_purchase_pct
FROM funnel;
重複排除
-- Keep the most recent record per key
WITH ranked AS (
    SELECT
        *,
        ROW_NUMBER() OVER (
            PARTITION BY entity_id
            ORDER BY updated_at DESC
        ) as rn
    FROM source_table
)
SELECT * FROM ranked WHERE rn = 1;

エラー処理とデバッグ

クエリが失敗した場合:

  1. 構文エラー:方言固有の構文を確認します(例:BigQuery では ILIKE を使用できず、SAFE_DIVIDE は BigQuery でのみ使用できます)
  2. 列が見つからない:スキーマと照合して列名を確認します。タイプミスや大文字と小文字の区別を確認します(PostgreSQL では、引用符で囲まれた識別子の大文字と小文字が区別されます)
  3. 型の不一致:異なる型を比較する場合は明示的にキャストします(CAST(col AS DATE)、col::DATE)
  4. ゼロ除算:NULLIF(denominator, 0) または方言固有の安全な除算を使用します
  5. 列名が曖昧:JOIN では常にテーブルエイリアスで列名を修飾します
  6. GROUP BY エラー:集約されていない列はすべて GROUP BY に含める必要があります(エイリアスでのグループ化が許可される BigQuery を除く)

コメント