@TOC
在日常 ABAP 开发里,数据库访问往往决定一份报表或接口的整体响应时间。许多性能瓶颈并非源自数据库本身,而是由看似“无害”的编码习惯造成,例如在循环里反复 SELECT SINGLE,或者给标准表叠加过多二级索引。
本文按照误区 → 危害 → 正确姿势的脉络,根据笔者 2007~2025 18年的 ABAP 开发经验,结合社区讨论、官方指南与生产事故回顾,归纳出一套易于落地的优化清单,并给出可独立运行的示例程序,帮助开发者在写下第一行 SQL 前就远离陷阱。
将 SELECT 或 SELECT SINGLE 直接写在 LOOP 中,每循环一次就访问一次数据库,轻则导致网络往返次数激增,重则让应用服务器进程长时间占用 work‑process。社区帖子统计过典型案例:3000 行内部表迭代触发 3000 次单行查询,执行时间从 0.2 秒暴涨至 30 秒。另有 Stack Overflow 讨论指出,“1 × N” 查询模式也阻断了后续并行化与批量缓冲的可能。
正确思路
SELECT … INTO TABLE 或 FOR ALL ENTRIES 将所需行一次拉取进内表。 在标准表随意新建索引,会让数据库优化器为了维护索引而付出写入开销;当选择条件与索引列次序不符时,查询甚至不会走刚建好的索引,反而拖慢性能 。
正确思路
ST05 跟踪结果评估是否需要索引; WHERE 条件的列顺序与索引列顺序一致; 
SELECT * 获取整行字段一次性拉取所有字段会导致不必要的 I/O 与网络传输,尤其是宽表如 BSEG;官方文档建议只选择业务真正使用的列。
正确思路
列出确切字段列表,并在代码旁留注释说明业务含义,便于后续审计。
对大表使用非等值过滤(如 LIKE '%pattern%')或在索引列上做函数运算,会让数据库放弃索引走全表扫描。
正确思路
BETWEEN 而非逐日循环筛选。 FOR ALL ENTRIES当内部表记录数过大,或包含空表、重复键时,会触发性能下降甚至短 dump。博客案例显示,单次传入 230 万行键值,查询耗时 16 分钟。
正确思路
DELETE ADJACENT DUPLICATES FROM itab 去重并判断是否为空; ORDER BY除非 ORDER BY PRIMARY KEY 且已有匹配索引,否则数据库端排序可能比在内表 SORT 更慢。
正确思路
ORDER BY; 对高并发读取且少量写入的主数据表(如 T001)不开缓冲,会产生重复物理读;相反,将频繁更新的事务表误设为缓冲则会带来一致性风险。
正确思路
BYPASSING BUFFER,但要谨慎。 双重循环按 N × M 次比较执行,随着数据量增长呈平方级膨胀。Parallel Cursor 技术通过预排序和二分查找,将复杂度降至 N + M。
正确思路
READ … BINARY SEARCH 找到内表对应子集,再用 LOOP … FROM idx 顺序处理。 以下示例可直接在 SAP NetWeaver 7.50 上执行,用于对比错误与优化后的做法。
REPORT zperf_demo.
*--- 数据类型定义
TYPES: BEGIN OF ty_flight,
carrid TYPE sflight-carrid,
connid TYPE sflight-connid,
fldate TYPE sflight-fldate,
END OF ty_flight.
DATA: gt_flight TYPE STANDARD TABLE OF ty_flight,
gt_booking TYPE STANDARD TABLE OF sbook,
gt_booking_ok TYPE STANDARD TABLE OF sbook.
*--- 准备测试数据:一次性取 10 000 航班作为外层循环基础
SELECT carrid connid fldate
FROM sflight
UP TO 10000 ROWS
INTO TABLE gt_flight.
************************************************************************
* 误区示范:在 LOOP 内使用 SELECT SINGLE
************************************************************************
DATA lv_start TYPE syuzeit.
lv_start = sy-uzeit.
LOOP AT gt_flight INTO DATA(ls_f).
SELECT SINGLE * FROM sbook
INTO DATA(ls_bk_wrong)
WHERE carrid = ls_f-carrid
AND connid = ls_f-connid
AND fldate = ls_f-fldate.
APPEND ls_bk_wrong TO gt_booking.
ENDLOOP.
WRITE: / `错误做法耗时(秒):`,
( sy-uzeit - lv_start ).
************************************************************************
* 优化做法:一次性批量读取
************************************************************************
CLEAR gt_booking.
lv_start = sy-uzeit.
SELECT * FROM sbook
INTO TABLE gt_booking_ok
FOR ALL ENTRIES IN gt_flight
WHERE carrid = gt_flight-carrid
AND connid = gt_flight-connid
AND fldate = gt_flight-fldate.
WRITE: / `正确做法耗时(秒):`,
( sy-uzeit - lv_start ).在实际系统里,两段代码对同样 10 000 条数据的获取耗时可能出现 20 × 以上差距,且第一段引入 10 000 次独立数据库 round‑trip。
ZFIN_RPT03 报表在循环读取 BSEG。将代码改写为单次 SELECT … INTO TABLE 并建立覆盖 BUKRS, GJAHR, BELNR 的二级索引后,执行时间从 48 分钟下降到 3 分钟。 MARA 添加了 8 个二级索引,导致日常主数据接口写入性能骤降。根据 SQL Trace 发现只有一个索引用于查询,其余均无命中却带来高写锁竞争。清理无效索引后,接口写单条物料主数据的平均事务时间缩短 60 %。 高性能 ABAP 不在于掌握多复杂的语法,而是从设计阶段就遵守“最少访问数据库、一次访问取尽必要数据”的原则。通过 ST05 量化、索引设计对齐、合理利用缓冲与批量技术,大多数查询性能问题都能在编码阶段被扼杀在摇篮里。希望本文的误区清单与优化范式,能成为大家编写每一条 SELECT 时的提醒,让生产系统保持轻盈而稳定。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。