db-query-patterns について
db-query-patternsは、ORMソースと生SQLのクエリ形状を調べるデータベース監査スキルです。性能に影響し得るアクセスパターンを見つけ、静的な検出結果は実行計画で確認されるまで傾向として扱います。
主な機能
- 01ホットパスのSELECT *を検出
- 02構造的なN+1クエリを検出
- 03深いOFFSETページングを検出
- 04非SARGableな述語を特定
- 05EXPLAINによる検証方法を提示
活用シーン
- 01ORMとSQLの非効率なクエリ形状の確認
- 02深くなるほど遅くなるページングの特定
- 03実行計画によるクエリ問題の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ hainrixz/ claude-db --skill db-query-patternsインストール後、Claude Code で/db-query-patternsを入力して読み込まれたか確認します。
詳細
db-query-patterns (M13)
クエリの書き方によって、M11 のインデックスを使用できるかどうかが決まります。このモジュールでは、ORM のソースコードと生の SQL におけるクエリの形を調べます。パフォーマンスとスケーラビリティスコア(軸 performance、リレーショナルの Query w18、M3/M19 と共通)に反映されます。
チェック項目
SELECT *— すべての列を取得すると、カバリングインデックスの効果が損なわれ、ネットワークやキャッシュを圧迫し、コードが列の順序に依存します。頻繁に実行される処理で検出します。- 構造上の N+1 — ループ内のクエリ、または単一の JOIN やバッチ処理の
IN句にすべき、行ごとの遅延リレーション読み込みです。静的検出では方向性のみを示します(ループの実行時の件数は不明です)。コード構造を指摘するにとどめ、行数を断定することはありません。 - OFFSET によるページネーション —
LIMIT n OFFSET mはページが深くなるほど線形に遅くなります。深いページの取得にはキーセット/シーク方式(WHERE id > $last ORDER BY id LIMIT n)を使用してください。 - SARG 不可能な述語 — インデックス対象の列を関数で囲む(
WHERE lower(email)=…、WHERE date(created_at)=…、先頭ワイルドカードのLIKE '%x'、暗黙の型キャスト)と、インデックスを使用できません。式インデックスの作成、または述語の書き換えを推奨します。先頭ワイルドカードのLIKE '%x%'の場合、B-tree では対応できません。対処法として、列に対するpg_trgmの GIN/GiST インデックス(または、大規模な全文検索向けの専用検索エンジン)を明記します。
スコア/軸
パフォーマンスのみに反映されます(リレーショナルの Query w18、ドキュメント/時系列/グラフの各プロファイルの Query カテゴリー)。
Tier-0(静的解析)
ORM の呼び出し箇所と生の SQL を検索し、SELECT *、関数で囲まれたインデックス対象の列、先頭ワイルドカードの LIKE、OFFSET を検出します。また、N+1 の検出では、ループ内のクエリ呼び出し、.map、または各項目のリレーションアクセスを検出します。クエリパターンに関する検出結果はすべて、ソースコードから判断できるのは最大でも directional までです。実際の実行計画やコストの確認には、実行時の情報(needs_api/Tier-2)が必要です。
Tier-1/2(検証クエリ)
EXPLAIN (ANALYZE, BUFFERS) <the suspect query>;
メソッド explain_plan。インデックスが存在するのに Seq Scan が発生していれば、SARG 不可能な述語であることが確認できます。actual rows × loops から、N+1 による増幅を確認できます。Tier-2 の pg_stat_statements(total_exec_time の降順)では、実際に負荷の高いクエリを特定できます。これがない場合、負荷の高さは directional として扱います。
検出結果
schema/finding.schema.json に従って検出結果を出力します。例:
M13.orders.select_star_hot_path— 頻繁に実行される読み取り処理でのSELECT *(severity:2、warn、軸performance、confidencedirectional、fixable: proposed)。M13.users.n_plus_one_posts— ループ内での行ごとのリレーション読み込み(severity:3、warn、directional、fixable: advisory— アプリ側での先行読み込み/バッチ処理が必要です)。M13.feed.offset_deep_pagination—OFFSETによる深いページの取得(severity:2、warn、directional、fixable: proposed— キーセット方式に書き換えます)。M13.users.non_sargable_lower_email— 関数で囲まれたインデックス対象の列(severity:3、warn、directional、fixable: proposed— 式インデックス、または述語の書き換え)。
各検出結果では、evidence.observed にクエリ/呼び出し箇所をそのまま引用します(シークレットはマスキングします)。verification.reproduce には、$DATABASE_URL を参照する上記の EXPLAIN、またはパターンを検索する grep を指定します。expected_impact は、範囲と信頼度を付けて示します(根拠のないパーセンテージは禁止です)。
留意点
- N+1 は仕様上、方向性のみを示します。ループが 2 回実行されるなら問題ありませんが、同じループが 10k 行に対して実行される場合は問題になります。静的解析ではどちらかを判断できないため、上限を設定したり、倍率を示したりすることはありません。
- 小規模な参照処理や一度だけ実行する管理用クエリでは、
SELECT *に問題はありません。重要度は、頻繁に実行される処理に限定してください。 - 結果の意味が変わる書き換え(キーセット方式、述語の構成変更)は
proposed/advisoryとし、autoにはしません。式インデックスの追加はproposedにできます。