debug-slow-sql について
ARC-1 の診断機能で、ABAP SQL や Fiori Elements の OData リクエストが遅い原因を調べるスキルです。SAP アプリケーション開発者向けに、時間計測や CDS SQL の確認から、必要に応じたコード・SQL トレースまで段階的に進めます。
主な機能
- 01OData リクエストの時間内訳を確認
- 02CDS から生成された SQL を表示
- 03クエリ時間と返却行数を計測
- 04ABAP トレースのホットパスを分析
- 05ST05 SQL トレースの設定と確認案内
- 06性能向け ATC 静的チェック
活用シーン
- 01遅い OData や Fiori リストの原因調査
- 02長時間実行される CDS ビューやレポートの調査
- 03高負荷時のタイムアウトやダンプの分析
MCP 対応
ARC-1 で SAP 診断を実行し、性能トレースへ段階的に進みます。トレース記録や実行計画の確認には SAP GUI または Fiori が必要な場合があります。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ arc-mcp/ arc-1 --skill debug-slow-sqlインストール後、Claude Code で/debug-slow-sqlを入力して読み込まれたか確認します。
詳細
遅い SQL / OData のデバッグ
遅い ABAP SQL または Fiori-Elements OData リクエストの根本原因を特定し、修正方法を提案します。まず ARC-1 の診断を実施し(GUI 不要)、より深いシグナルが必要な場合に限り、SAP GUI / Fiori アプリへと進みます。目指すのは、単に「遅い」と説明することではなく、その理由、つまり、どのステートメントが何をスキャンし、何を変更すべきかを明らかにすることです。
遅いレポート/トランザクション、遅い OData/Fiori リスト、実行に時間のかかる CDS ビュー、負荷時のタイムアウト/ダンプ、または「このクエリは…の後から遅くなった」とユーザーが報告したときに使用してください。
入力(不足しているものだけを確認)
- 遅い対象 — 次のいずれか:OData URL(ブラウザーの Network タブからコピー)、CDS ビュー/DDLS 名、ABAP プログラム/クラス/レポート、トランザクションコード、またはテーブル+アクセスパターン。
- 遅さの程度/頻度 — 単発の遅い呼び出しか、負荷時に遅いのか、断続的なのか。(リクエスト単位の分析と集計分析のどちらを行うか判断します。)
- 再現可能か? — ユーザーが任意のタイミングで再実行できますか(ライブトレースを開始するために必要です)? どのシステム(DEV/QA/PROD)で、どの SAP ユーザーとして実行しますか?
- 最近の変更は? — 新しいコード、新しいデータ量、移送、インデックスの削除など。すばやく調査範囲を絞れます。
「X が遅い」という曖昧な情報しかない場合は、まず OData URL またはオブジェクト名を確認してください。以下の調査はすべてそこを起点にします。
診断の段階 — 原因を説明できた時点で止める
上から順に進めます。各段階は次の段階よりコストが低く、通常は次に進むべきかどうかも判断できます。
到達可能性 — 実行できない計画を約束しないでください。 段階 3~4(プロファイラー、ST05)を開始するには、
SAP_ALLOW_WRITES、トレースリソースを対象とするURI範囲を持つS_ADT_RES、およびトレースツールのバックエンド認可が必要です(STAUTHTRACE で確認してください)。また、NW 7.50 にはこれらの機能がありません。開始時に writes disabled または403が返された場合は、その旨を伝え、段階 2 で止めてください。odata_perf+cds_sql+SAPQueryで、GUI を使わずに DB に起因する根本原因をすでに特定できます。段階 3~4(正確なステートメント+実行計画)まで進められるかは、その後 Basis/設定に依存します。そのため、実行計画まで到達したかのように装うのではなく、以下の ST05/SAT の正確な手順をユーザーに案内してください。
0. 状況を把握する(実行なし)
SAPContext(action="deps", type="<type>", name=…)/SAPRead(type="DDLS", name=…)— CDS/ABAP ソースを読み取ります。計測する前に、よくある原因がないか目視で確認してください。LIKE '%term%'(先頭にワイルドカードがあるとインデックスが使われません)、キーに対するWHEREのないSELECT … FROM、SELECT *、LOOP内のネストされたSELECT(N+1)、クライアント側のフィルタリング、FOR ALL ENTRIESの事前チェック漏れ、全件スキャンを引き起こす計算フィールドなどです。SAPContext(action="impact", type="DDLS", name=…)— CDS スタック(プロジェクション → ベースビュー → テーブル)を確認します。遅いビューは、負荷の大きなベースビューの上にある薄いプロジェクションであることがよくあります。- SQL の文字列だけでなく、生成元を探してください。 遅い
LIKE '%…%'/スキャンは、手書きのコードではなく、検索ヘルプ、SADL/RAP、またはフレームワークによって生成されることがよくあります。そのため、文字列SELECTをgrep/where-used で探しても見つからないことがあります。生成元を追ってください。値ヘルプ/入力補完画面の場合は、SAPSearch/SAPNavigate/SAPReadで検索ヘルプ(DD30L.SELMETHOD+FUZZY_SEARCH、DD32Sフィールド)と、その DSH/SADL クラス(CL_DSH_*、F4→WHERE 変換)を確認します。OData サービスの場合は、その実装を見つけてください。V4/RAP バインディングなら、SAPRead(type="SRVB", name=…)→ サービス定義 → CDS ビューの順にたどり(その後、段階 2 のcds_sqlを使用します)、classic SEGW / Gateway V2 サービスなら CDS はありません。DPC クラスを探し(SAPSearch "<SERVICE>_DPC*")、その*_get_entityset/*_get_entity/ expand /resolve_navigation_pathメソッドを読み取り、段階 2 はスキップしてください。(メッセージクラスをメモリーに読み込んでCSで検索する SE91/WBMESSAGESと、実際の DBLIKEを発行する検索ヘルプの経路を混同しないでください。)
1. 時間はどこにかかっていますか?(低コストの呼び出しを1回)
OData / Fiori のリクエストの場合:
SAPDiagnose(action="odata_perf", url="/sap/opu/odata4/sap/…/Entity?$filter=…")
verdict を確認します:
db(gwappdbが支配的)→ コストは CDS/SQL クエリです → 次の段階 2 に進みます。app(gwapp≫gwappdb、またはgwappdbなし)→ ABAP/SADL ロジックであり、DB ではありません。低速なクエリを探すために ST05 で調査してはいけません。サービスを実装しているものを見つけ(段階 0)、トレースします(段階 3)。典型的な原因は$expandの N+1 です。URL を$expand/$selectありとなしで再プローブし、gwappの変化を確認して切り分けます。framework(gwfw、7.50 では多くの場合gwhubのみ)→ メタデータ/初回呼び出し/コールドキャッシュです → ウォーム状態で再プローブします。gwhubしかない場合は、まだ DB とアプリの内訳がわかりません。単にコールドキャッシュの問題でなければ、トレースに進みます。auth(icfauth)→ ICF/DCL の認可オーバーヘッドです。unknown→ 利用可能な Gateway のタイミング情報がありません。URL が SAP ホスト上の OData/Gateway サービスであることを確認してください。
(odata_perf には SAP_ALLOW_DATA_PREVIEW が必要です。url は、ARC-1 が接続する SAP ホスト上の /sap/opu/odata または /sap/opu/odata4 配下のホスト相対 OData パスである必要があります。それ以外のパスは拒否されます。GET を実行するため、$batch の POST ではなく、Network タブにあるエンティティ/リストのリクエストを使用してください。)
2. DB がボトルネックの場合 → 実際の SQL を確認して測定する
SAPDiagnose(action="cds_sql", name="I_TheView")— CDS がコンパイルするネイティブのCREATE VIEW(読み取り専用。7.50/758/816 で検証済み)。実際の結合、CAST、COALESCE、サブビューが追加のテーブルを引き込んでいるかどうかを確認できます。SAPQuery(sql="SELECT … FROM <cds-or-base> WHERE <the filter>")—columns/rowsと、データプレビューのメトリクスqueryExecutionTimeMs、totalRows(一致した合計数)、rowsReturned、executedQueryStringを返します。低速なリクエストの実際のフィルター値を使って実行します。有用な結果が少ないのにtotalRows/queryExecutionTimeMsが大きい場合は、スキャン/選択性の問題です。HANA の実行計画やバッファーの状態は公開されないため、それらには ST05/HANA を使用してください。SAPQueryがタイムアウトすること自体が、無制限スキャンの証拠です。単にmaxRowsを小さくして再試行するのではなく、タイムアウトを兆候として記録してください。(フリースタイル SQL にはSAP_ALLOW_FREE_SQLが必要です。SAPRead(type="TABLE_QUERY")による複数列のWHEREにはSAP_ALLOW_DATA_PREVIEWが必要です。)- 等価条件でも遅い場合は、フィルターではなくビューが原因です。 完全一致の
WHERE key = '…'を指定したSAPQueryがLIKE '%…%'と同じくらい遅い(どちらも数十秒)場合、LIKEは見当違いです。コストの原因は CDS 自体です。幅広いSELECT DISTINCT、フィルターから処理を開始できない深い結合(フィルター対象のフィールドがインデックスの先頭列ではない)、または集計/$count=trueが考えられます。cds_sqlで結合順序を確認します。修正するには、選択性の高いテーブルが先頭になるように結合を逆転する、リストのプロジェクションからDISTINCT/ 集計を削る、または大規模スキャンで$count=trueを外します。LIKEを変更するのではありません。 - シグナルを比較します。まず OData URL をプローブし、次に基盤となる CDS/テーブルに対して
SAPQueryを実行します。queryExecutionTimeMsが OData のgwappdbに近ければ、コストは DB クエリです。OData は遅いのにクエリが速ければ、その上位にある SADL/フレームワーク層が原因です。正確なステートメントと実行計画を確認するには、ST05 に進みます。 - HANA の全文検索/
CONTAINS/ファジー検索による修正を推奨する場合、SAPQueryで A/B テストはできません。ADT のフリースタイルエンドポイントは Open SQL のみ対応しており、CONTAINS(...)/… CP '*x*'は拒否されます(400 "(" is not allowed)。代わりに、実際の機能をライブで再現しながら ST05 トレースを取得するか、単発の ABAP プローブレポートで検証してください。まず、テーブルに全文検索インデックスがあることを確認します:SAPQuery(sql="SELECT indexname, full_text FROM dd12l WHERE sqltab = '<TABLE>'")→full_text = 'X'。
3. アプリにひも付けた ABAP プロファイラートレース(どのコード、どのテーブルか)
SAPDiagnose(action="traces") # list recent profiler traces
SAPDiagnose(action="traces", id="<id>", analysis="hitlist") # hottest call paths
SAPDiagnose(action="traces", id="<id>", analysis="dbAccesses") # which tables, counts, buffered?
dbAccesses ビューでは、リクエストがどのテーブルに何回アクセスしたかを確認できます(あるテーブルの回数が極端に多ければ、N+1 が見つかります)。hitlist では ABAP のホットパスを確認できます。ARC-1 はプロファイラートレースのリクエストを自ら開始できます。SAPDiagnose(action="trace_start", …) を実行し、trace_requests で一覧表示してから、trace_cancel でクリーンアップします。または SAT/ST12 で記録し、上記の traces アクションで一覧表示・分析します。
trace_start 自体が示す 2 つの注意点に留意してください。(1)HTTP トレースは、ユーザーが次に行う一致する HTTP 呼び出しを記録します。そのため、トレースを開始した後、ユーザーが再現するまでは、ほかの ARC-1/MCP 呼び出しを一切行わないでください。ご自身の ADT 呼び出しがトラップを消費してしまいます。(2)HANA 上の HTTP/OData トレースでは、プロファイラーの情報は限定的です。通常、hitlist/statements は空(または 400 "Data is invalid…")になり、業務テーブルは <DB Access from Kernel> バケットの中に隠れます。dbAccesses は「テーブル X にアクセスしたか、何回か」の確認のみに使い、OData の実際の SQL とタイミングには ST05(段階 4)を使ってください。プロファイラーで最も詳しい情報が得られるのは、ABAP 側のコードのダイアログ/レポート/RFC トレースです。
4. 正確な SQL と実行計画 → ST05 SQL トレース
ARC-1 は ST05 SQL トレースを開始/停止し、記録へのリンクを示すことができます(ADT 経由で記録を読み取ることはできません。SAP には SQL 記録 API がありません。記録の表示には TMC Fiori アプリまたは SAP GUI ST05 を使います)。
SAPDiagnose(action="sql_trace_state") # is a trace already on?
SAPDiagnose(action="set_sql_trace_state", sqlOn=true, user="<SAPUSER>") # arm, filtered to the user (needs SAP_ALLOW_WRITES)
# → user reproduces the slow request now ←
SAPDiagnose(action="sql_trace_directory") # SAP's "SQL Trace Analysis" deep-link
SAPDiagnose(action="set_sql_trace_state", sqlOn=false) # always disarm when done
その後、記録を読み取ります(「SAP GUI / Fiori へのエスカレーション」を参照)。記録一覧では、正確な SELECT、所要時間、取得行数、オブジェクト、さらに ST05 では EXPLAIN/実行計画とバッファー状態を確認できます。758/816 で利用可能です。NW 7.50 では利用できません(/st05/trace ADT API は 404 を返します。その場合は SAP GUI ST05 を使ってください)。
記録の読み取り方(sql_trace_records/sql_trace_read ADT アクションはありません。sql_trace_directory が返すのはビューアーへのリンクのみです):ディープリンク(TMC の「SQL Trace Analysis」)を開くか、SAP GUI で ST05 → Display Trace を選択し、アプリケーションサーバーとトレースファイルを選びます。Duration の降順で並べ替え、各行の SQL テキスト、Duration、Records(取得行数)、Object(テーブル/ビュー)を確認します。最も遅い行をダブルクリックし、Explain で HANA の実行計画を確認します。注目するのは、関連付け/テキストテーブルへの余分なアクセス(SADL の展開)、同一の SELECT の繰り返し(N+1)、または表示行数に比べて Records が大幅に多いケース(選択性)です。
パフォーマンス用エンドポイントが "Service cannot be reached" で 403 を返す場合、ARC-1 は
icf-service-inactiveのヒントを表示します。該当する SICF ノード(トレース UI では/sap/bc/stmc)を tcode SICF で有効化してください。
5. 静的チェック(いつでも実行可能)
SAPDiagnose(action="atc", type="<type>", name=…, variant="PERFORMANCE_DB") — パフォーマンス上のアンチパターンを静的に検出します(データに依存する実行時の LIKE スキャンは検出できませんが、無料で実行でき、明らかな問題を見つけられます)。
SAP GUI / Fiori へのエスカレーション(ARC-1 の GUI 不要なシグナルだけでは不十分な場合)
ARC-1 は、SQL トレースレコードと実行計画の読み取りまでは GUI 不要で対応できますが、これらについてはエスカレーションしてください。 ユーザーに、何を開いて何を確認すべきかを正確に伝えてください。または、デスクトップ/Chrome MCP を利用でき、権限がある場合は、自分で操作してください(明示的な承認なしに、本番環境では決して実行しないでください)。
| ツール | 場所 | ARC-1 では得られない情報 |
|---|---|---|
| ST05(SQL/RFC/バッファー/エンキュートレース) | SAP GUI | 記録された SELECT の実行時間と行数、「説明」 → DB 実行計画、同一/類似ステートメントのグループ化、バッファーヒット。「どのステートメントが、なぜ実行されたか」を確認するための真実の情報です。 |
| SQL Trace Analysis | Fiori アプリ(sql_trace_directory のディープリンク) | ブラウザー(TMC)で確認できる、ST05 と同じレコードです。SAP GUI を利用できない場合に使用します。/sap/bc/stmc の SICF が有効である必要があります。 |
| ST12 / SAT | SAP GUI | ABAP と SQL を組み合わせた集計付きトレースです。1 回の記録でアプリケーションと DB 全体の「実際に時間がかかっている箇所」を確認するのに最適です。 |
| DBACOCKPIT / HANA | SAP GUI / HANA Studio / DBeaver | EXPLAIN PLAN、PlanViz、M_SQL_PLAN_CACHE、テーブル/インデックスのサイズ、インデックス不足のヒント、オプティマイザー統計の鮮度です。HANA 側の根本原因(カラムストアのスキャン、プルーニングなし、古い統計)を確認できます。 |
| ST22 / SM50 / SM66 | SAP GUI | ダンプ(例:TIME_OUT、TSV_* メモリー)と、負荷時にワークプロセスが処理している内容です。 |
| SE11 / SE14 | SAP GUI | テーブルのインデックスとそのフィールド、および遅い WHERE 条件が利用可能なインデックスのプレフィックスに一致するかどうかです。 |
| ABAP Cross Trace | ADT(/sap/bc/adt/crosstrace/*、758+) | OData V4 のリクエストタイプを含む、RAP/OData 対応のレイヤー横断トレースです。戦略的な ADT ネイティブのレコードリーダーです(ARC-1 の後続機能。まだツールではありません)。 |
odata_perf 用の OData リクエストを取得する方法: ブラウザーの DevTools → Network → 遅い $batch/エンティティリクエストをクリック → ホストの後ろのパスをコピーします(例:/sap/opu/odata4/sap/zsrv/…/Entity?$filter=…&$top=…)。このパスを url 引数に指定します。
根本原因カタログ(パターン → 確認 → 修正)
| トレース/SQL の症状 | 考えられる原因 | 確認方法 | 典型的な修正 |
|---|---|---|---|
取得した rows fetched が表示行数よりはるかに多い。実行時間が長い | フルスキャン/選択性が低い | cds_sql にインデックス付きの WHERE がないことを確認します。SAPQuery totalRows が大きいか、ST05 の取得行数が多いことを確認します | インデックス付きフィールドに WHERE を追加します。セカンダリーインデックスを追加します(SE11)。フィルターを CDS に適用します |
LIKE '%term%' | 先頭にワイルドカードがあるためインデックスを使用できない | DDLS/ABAP ソースを確認します | 検索ヘルプ/ファジー検索(HANA)/全文インデックスを使用します。パターンの先頭を固定します。事前にフィルターを適用します |
| 同じテーブルへのアクセスが数千回発生する | N+1(LOOP 内の SELECT) | traces dbAccesses で、1 つのテーブルへのアクセス数が非常に多いことを確認します | FOR ALL ENTRIES/JOIN/全件読み込み後にループする方法を使用します。RAP の場合は事前取得します |
SELECT * の後に 2 つのフィールドを使用する | 過剰なデータ取得 | cds_sql/ソースを確認します | 必要なフィールドだけを選択します。CDS のプロジェクションを絞り込みます |
| DEV では高速だが、PROD では遅い | データ量/古い統計/異なる実行計画 | EXPLAIN とテーブルサイズを比較します(DBACOCKPIT) | オプティマイザー統計を更新します。インデックスを追加します。パーティション分割します |
gwappdb は小さいが、OData が遅い | SADL/判定処理/仮想エレメント/認可(DCL) | odata_perf の判定が app/auth であること、またはプロファイラーの hitlist を確認します | ロジックを DB/CDS に移します。DCL を簡素化します。キャッシュします。行ごとの ABAP 仮想エレメントを避けます |
| 初回の呼び出しだけ遅い | メタデータ/キャッシュが空 | odata_perf の判定が framework であることを確認します。ウォーム状態で再計測します | 想定される動作です。ウォームアップします。クエリは最適化しないでください |
負荷時に TIME_OUT/メモリダンプが発生する | 上限のない結果/ページングの欠如 | ST22 と ST05 を確認します | サーバー側ページング($top/$skip)を使用します。フィルターを追加します。処理を分割します |
実例(実環境、a4h S/4HANA 2023)
「メッセージテキスト検索画面が遅い」という問い合わせです。その背後にあるクエリは次のとおりです。
SAPQuery(sql="SELECT sprsl, arbgb, msgnr, text FROM t100 WHERE text LIKE '%error%'")
→ { totalRows: 12549, queryExecutionTimeMs: 49.086, rowsReturned: 100,
executedQueryString: "SELECT … FROM T100 WHERE TEXT LIKE '%error%' … UP TO 100 ROWS" }
見てみましょう。インデックスのないテキスト列に対する先頭ワイルドカード付きの LIKE '%…%' により、12 549 行が一致しています。つまり、テーブル全体がスキャンされています(パターンが % で始まるため、インデックスは使用できません)。ユーザーが実際に必要としていたのは、ほんの数行です。**修正方法:**先頭のワイルドカードを削除して検索条件を固定する/検索ヘルプまたは全文検索インデックスを追加する/まずインデックス付きフィールドで事前に絞り込む。HANA の実行計画と取得行数を確認するには、ST05 トレース(段階 4)を有効にして再現し、ディレクトリのディープリンクを開きます。
これは単純なケースです。先頭ワイルドカードが本当にボトルネックとなっている、単一テーブルのスキャンです。幅広い CDS ビューでは、そうとは限りません。完全一致の WHERE key = '…' でも LIKE と同じくらい遅い場合、原因はワイルドカードではなく、ビューの結合/DISTINCT/$count です(段階 2、「等価条件でも遅い場合」)。LIKE のせいにする前に、両方を測定してください。
出力
ツールのログではなく、簡潔な診断を提示してください。
- 判定 — DB/アプリ/フレームワーク/認証のどれが原因かと、それを裏付ける数値を示します(例:
gwtotal480 ms のうちgwappdbが 412 ms)。SAP の数値(gwtotal/gwappdb)を引用し、wallClockMsは使わないでください。ウォールクロック時間にはネットワーク/MCP/プロキシも含まれます(odata_perfはその差をclientWaitMsとして返します)。そのため、clientWaitMsがgwtotalを大幅に上回る場合、遅延の原因はクエリではなくランドスケープにあります。 - SQL 文 — 問題のある SQL(
cds_sql/ST05 から)と、そのスキャン対象(SAPQuery totalRows/ST05 の取得行数、テーブル、欠けているインデックス)を示します。 - 根本原因 — 上記のカタログに対応づけて、1 文で説明します。
- 修正方法 — 具体的かつ最小限の対策(追加するインデックス、プッシュダウンするフィルター、まとめる N+1 など)を示します。最も低コストな方法を最初に挙げ、トレードオフも明記してください。
- 根拠 — 実行した ARC-1 の正確な呼び出しと、ST05/HANA の実行計画の取得内容があれば、それも示します。これにより、ユーザーが再検証できます。
有効にしたトレースは必ず停止してください。PROD では、読み取り専用のシグナル(odata_perf、cds_sql、SAPQuery、読み取り専用トレース)を優先し、ST05 トレースの有効化や状態の変更を行う前に、明示的な承認を得てください。