关于 debug-slow-sql
此技能通过 ARC-1 诊断 ABAP SQL 或 Fiori Elements OData 请求变慢的原因,面向维护 SAP 应用的开发人员。它按成本逐步检查请求耗时、CDS SQL 与代码路径,并在需要时升级到性能追踪。
主要功能
- 01检查 OData 请求耗时分布
- 02查看 CDS 编译后的 SQL
- 03测量查询执行时间与结果行数
- 04分析 ABAP 追踪中的热点路径
- 05启用并引导读取 ST05 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才能启动,还需要S_ADT_RES对跟踪资源具有URI覆盖权限,以及跟踪工具的后端授权 (使用 STAUTHTRACE 确认)——且 NW 7.50 上没有这些工具。如果启动时返回 writes disabled 或403,请说明情况并停在第 2 级:odata_perf+cds_sql+SAPQuery已足以在无需 GUI 的情况下确定数据库相关的根本原因。第 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 或框架生成,而非手写,因此用grep/where-used 搜索字面的SELECT往往一无所获。应追踪生成器:对于值帮助 / 类型预输入界面,通过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)。绑定(SRVB)名称通常与 URL 中的服务路径不同——如果SRVB返回 404,请使用SAPSearch搜索服务名称以找到实际绑定,或直接读取服务定义(SRVD)中的expose … as实体。传统 SEGW / Gateway V2 服务没有 CDS → 查找 DPC 类(SAPSearch "<SERVICE>_DPC*",读取其*_get_entityset/*_get_entity/ expand /resolve_navigation_path方法),并跳过第 2 级。(不要将 SE91/WBMESSAGES与搜索帮助路径混淆:前者会将一个消息类加载到内存中并使用CS搜索;搜索帮助路径则会发起真正的数据库LIKE查询。)
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 逻辑,不是数据库。不要 用 ST05 查找慢查询——找出实现该服务的内容(第 0 阶),并对其进行跟踪(第 3 阶)。典型 问题是$expandN+1——分别使用和不使用$expand/$select对 URL 重新探测,并观察gwapp的变化,即可隔离问题。framework(gwfw;在 7.50 上通常只有gwhub)→ 元数据 / 首次调用 / 冷缓存 → 缓存预热后重新探测。 如果只有gwhub,说明你还无法区分数据库和应用;如果不只是冷缓存,就继续深入进行跟踪。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,因此请使用 Network 选项卡中的
实体/列表请求,而不是 $batch POST。)
2. 数据库受限 → 查看并测量实际 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,那么成本在数据库查询;如果 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 本身会提示两项注意事项,请务必留意:(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 ≫ 可见行数(选择性)。
如果性能端点返回 403,提示“Service cannot be reached”,ARC-1 会显示
icf-service-inactive提示 — 在 SICF 事务码中激活指定的 SICF 节点(跟踪界面使用/sap/bc/stmc)。
5. 静态检查(随时可用)
SAPDiagnose(action="atc", type="<type>", name=…, variant="PERFORMANCE_DB") — 静态标记性能反模式(它无法捕获依赖数据的运行时 LIKE 扫描,但免费使用,而且能发现明显问题)。
SAP GUI / Fiori 升级处理(当 ARC-1 的无 GUI 信号不足以解决问题时)
ARC-1 在读取 SQL 跟踪记录和执行计划之前都无需 GUI——遇到这两种情况时,请升级处理。 明确告知用户需要打开什么,以及需要查看什么;或者,如果你有桌面端/Chrome MCP 且已获授权, 也可以自行操作(未经明确批准,绝不在 PROD 上操作)。
| 工具 | 位置 | 它能提供哪些 ARC-1 无法提供的信息 |
|---|---|---|
| ST05(SQL/RFC/缓冲区/enqueue 跟踪) | SAP GUI | 记录的 SELECT 语句及其耗时和行数;“Explain” → 数据库执行计划;相同/相似语句分组;缓冲区命中情况。判断“是哪条语句以及原因”的确凿依据。 |
| SQL Trace Analysis | Fiori 应用(sql_trace_directory 深层链接) | 在浏览器中查看相同的 ST05 记录(TMC)——SAP GUI 不可用时使用。需要启用 /sap/bc/stmc SICF。 |
| ST12 / SAT | SAP GUI | 聚合后的 ABAP+SQL 组合跟踪——最适合通过一次捕获来判断应用和数据库的耗时分布。 |
| 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+) | 感知 RAP/OData 的跨层跟踪,包括 OData V4 请求类型——战略性的 ADT 原生记录读取器(ARC-1 后续工作;尚非工具)。 |
为 odata_perf 捕获 OData 请求: 浏览器开发者工具 → 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 显示对某张表的访问次数极多 | 使用 FOR ALL ENTRIES / JOIN / 先全部读取再循环;RAP:预取 |
执行 SELECT *,但只使用 2 个字段 | 过量获取 | cds_sql / 源代码 | 仅选择所需字段;精简 CDS 投影 |
| 在 DEV 中很快,在 PROD 中很慢 | 数据量 / 统计信息过时 / 执行计划不同 | 比较 EXPLAIN 和表大小(DBACOCKPIT) | 刷新优化器统计信息;添加索引;分区 |
gwappdb 数据量小,但 OData 很慢 | SADL / 判定逻辑 / 虚拟元素 / 授权(DCL) | odata_perf 判定为 app/auth;分析器 hitlist | 将逻辑移到数据库/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/应用/框架/认证问题,并提供证据数值(例如,"
gwappdb为 412 ms,gwtotal为 480 ms")。引用 SAP 数值(gwtotal/gwappdb),不要引用wallClockMs:墙上时钟时间还包括网络/MCP/代理耗时(odata_perf会通过clientWaitMs返回这部分差值),因此当clientWaitMs远大于gwtotal时,延迟来自整个系统环境,而非查询。 - 语句——问题 SQL(来自
cds_sql/ST05)以及它扫描的内容(SAPQuery totalRows/ST05 获取的行数、表、缺失的索引)。 - 根本原因——用一句话概述,并对应上述目录。
- 修复方案——具体且精简(要添加的索引、要下推的筛选条件、要合并的 N+1),优先选择成本最低的方案,并说明权衡。
- 证据——实际运行的 ARC-1 调用,以及任何 ST05/HANA 执行计划捕获结果,以便用户重新验证。
务必停用所有已启用的跟踪。在 PROD 上,优先使用只读信号(odata_perf、cds_sql、SAPQuery、只读跟踪),启用 ST05 跟踪或更改状态前,务必取得明确批准。