ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ABAP二分查找(BINARY SEARCH)优化内表查询性能全指南

ABAP二分查找(BINARY SEARCH)优化内表查询性能全指南 搞过SAP开发的朋友多半都有过这样的经历内表里存了几十万行数据在程序里做一次普通查询结果整个报表卡得让人怀疑人生。打开ST05一看数据库也不慢问题就出在ABAP内存里的数据检索上。这时候最该想到的就是二分查找算法也就是ABAP里的BINARY SEARCH。这算得上是SAP开发里最基础、也最容易被忽视的优化手段之一今天我就结合自己做过的项目把这块彻底讲透。这篇文章适合所有做ABAP开发的同事无论你是刚接触内表操作的新人还是在SAP项目实施里被性能问题折磨过的老手。我会从二分查找的原理讲起把ABAP里READ TABLE和BINARY SEARCH的正确用法、表类型的选择、排序的坑、以及实际排障过程中踩过的那些细节全部掰开揉碎说清楚。1. 为什么在SAP里要谈二分查找1.1 先从一个典型场景说起之前做过一个采购报表的优化业务逻辑不复杂从采购订单表、物料主数据、供应商主数据里抓数据放在内表里做关联匹配。最开始图省事全部用普通内表循环里套READ TABLE直接线性扫描。数据量小的时候一点问题没有但客户数据一涨一张报表从十几秒直接飙到两分多钟。ST05看了半天发现数据库层面SQL都很正常瓶颈全在ABAP内表的查询上。这种场景在SAP的MM、SD、FICO模块里太常见了。MD07跑物料需求清单、CO里做成本分摊报表、ALV输出前做数据匹配几乎都会遇到。很多人第一反应是改SQL、加索引但有时候数据源本身已经落地到内表里了这时候从ABAP层面优化查询方式成本最低、见效最快。1.2 二分查找到底在做什么二分查找的思想其实特别简单就像你翻字典找字不会从第一页开始一页页翻而是先翻到中间看看目标字是在前面还是后面然后缩小范围继续翻。放到数据检索里前提就是数据必须有序。每次比较都能排除掉一半数据所以二分查找的时间复杂度是O(log n)。读者可能对这个符号没感觉我直接给个直观对比一个32万行的内表线性查找平均比较次数是16万次二分查找最多只需要19次比较。差距是几千倍。这就是为什么在数据量大、频繁查询的场景下BINARY SEARCH的收益会非常夸张。1.3 选型对比什么时候该用二分查找检索方式前提条件复杂度适用场景线性查找默认READ TABLE无O(n)小内表几百行以下、无需排序二分查找BINARY SEARCH内表已升序排序O(log n)大内表、多次重复查询哈希表HASHED TABLE无排序要求O(1)大内表、单条精确匹配、无需按顺序访问并不是所有情况都应该用二分查找。我个人的经验是几百行的小内表线性查找和二分查找差距可以忽略不计反而排序还会消耗时间。但内表超过万行而且同一个查询要在循环中被反复执行这时候BINARY SEARCH就是刚需。要是匹配键稳定且只是精确取值直接换成HASHED TABLE效果更好这个后面细讲。2. ABAP里正确使用BINARY SEARCH2.1 最基础的写法READ TABLE ... BINARY SEARCHABAP里使用二分查找的标准写法就是READ TABLE语句加BINARY SEARCH附加项配合WITH KEY指定查找字段。基本结构如下READ TABLE lt_mara WITH KEY matnr lv_matnr BINARY SEARCH. IF sy-subrc 0. 找到了 ENDIF.这里有个极其重要的前提内表必须按照WITH KEY里指定的字段做升序排序。ABAP标准要求是升序降序不行没排序更不行。READ TABLE加BINARY SEARCH时若发现表未排序结果完全是未定义的可能找错行也可能直接找不到而且不会报错提示你。2.2 排序才是真正的关键很多人写BINARY SEARCH翻车不是二分查找本身的问题而是前面的SORT没做对。最常见的就是只排序了一个字段但READ TABLE时用了两个字段做查找键结果找出来的数据错乱。正确做法是这样的SORT lt_mara BY matnr. READ TABLE lt_mara WITH KEY matnr lv_matnr BINARY SEARCH.如果是多键查找排序字段顺序必须和READ TABLE的查找字段顺序保持一致SORT lt_mara BY matnr werks. READ TABLE lt_mara WITH KEY matnr lv_matnr werks lv_werks BINARY SEARCH.一个很容易犯的错是SORT的时候用了matnr、werks两个字段但READ TABLE时只按matnr一个字段查找这种情况下二分查找的匹配结果不一定是你想要的那行。因为此时内表是按matnrwerks排序的同一matnr下的记录并不保证连续有序排列到你预期的位置。所以我的建议是要么查什么字段就按什么字段排序要么就保持查找键和排序键完全一致。2.3 使用SORTED TABLE自动获得二分查找其实ABAP本身提供了一种更优雅的方案把内表直接定义成SORTED TABLE。这种表类型在数据插入时自动按Key排序READ TABLE查询时自动使用二分查找不需要手动写BINARY SEARCH也不用担心忘记排序。DATA: lt_mara TYPE SORTED TABLE OF mara WITH UNIQUE KEY matnr. READ TABLE lt_mara WITH KEY matnr lv_matnr TRANSPORTING mtart.SORTED TABLE适合什么场景数据在程序生命周期内基本不变、查询频繁、且习惯用键值访问的场景。比如配置表、汇率表、物料主数据快照、文本描述表都很合适。相比STANDARD TABLESORTED TABLE的插入性能会差一些因为每次插入都要维护排序位置但读性能非常好。2.4 查找键冲突和TRANSPORTING的坑先说说查找键冲突。BINARY SEARCH的设计是“找到第一条匹配的记录”当内表存在多条相同键值时它返回的是排序后位置最靠前的那一条。如果你的业务逻辑期望拿到的是第一条以外的某条那就要额外处理。比如同一个物料在同一个工厂下有多条记录你只想取最近创建的那条那就得在SORT时把创建日期按降序排再通过恰当的方式控制读取行为。再看TRANSPORTING它能控制READ TABLE取哪些字段只取自己需要的那几个字段减少内存移动开销READ TABLE lt_mara WITH KEY matnr lv_matnr TRANSPORTING mtart BINARY SEARCH.注意TRANSPORTING后面不能带查找键字段本身因为你都拿它当查找条件了还赋值干什么。另外如果用TRANSPORTING NO FIELDS就只判断是否存在不传输任何字段内容这在只想知道“这个物料存不存在”的场景下非常高效。3. 排序策略与表类型选择的实操经验3.1 一次SORT多次查询是核心套路在实际项目中最划算的做法是程序开始时一次性把大内表排序好后面在循环里反复使用BINARY SEARCH查询。比如我之前做CO的报表主数据内表先SORT好随后LOOP里每次需要查物料描述、文本、负责人全都用BINARY SEARCH循环一万次就是一万次O(log n)查询整体性能完全扛得住。反过来如果在循环里反复SORT那就是灾难。SORT时间复杂度是O(n log n)一万人循环每人排一次几万行的表这个操作足以让程序跑几分钟。所以一定要把SORT提到循环外面能一次排好就一次排好。3.2 用HASHED TABLE替代部分二分查找如果查找方式是精确匹配、且查找键能唯一确定数据那么其实还有比SORTED TABLE更快的路——HASHED TABLE。它的时间复杂度是O(1)不管表有多大一次哈希计算就能定位到数据。它的适用前提是不需要按顺序读取内表也不需要范围查询只做等值查询。这里有个很容易踩的坑HASHED TABLE不能用BINARY SEARCH。因为哈希表的存储方式和排序表完全不同它不维护排序顺序二分查找在语义上就不适用。ABAP里这样写会直接报语法错误或者运行期出错。很多人第一次写的时候老想着在HASHED TABLE后面加BINARY SEARCH这里提醒大家别加直接用READ TABLE WITH KEY就行。3.3 选择标准读多还是写多表类型选择的基本原则其实可以用一句话总结读多写少用SORTED TABLE或HASHED TABLE边写边读、顺序访问多就用STANDARD TABLE。举两个实际例子。第一个程序需要批量导入数据一边读Excel一边往内表里插入然后用顺序循环处理这种场景用STANDARD TABLE最顺手。第二个组装一段报表输出时需要根据一个主键反复去另一张内表里取描述信息这个“另一张内表”就很适合SORTED TABLE或HASHED TABLE。记住一点HASHED TABLE和SORTED TABLE的插入开销比较大因为要维护结构STANDARD TABLE的APPEND则非常快。选型是个平衡问题性能和代码可读性都要兼顾。3.4 关于SECONDARY TABLE KEY的补充如果你用的是SAP的新语法STANDARD TABLE上还可以定义Secondary Table Key次要表键。简单说就是给STANDARD TABLE额外定义一套访问路径让它在访问不同字段组合时也能得到接近SORTED TABLE的效率。TYPES: BEGIN OF ty_mara, matnr TYPE mara-matnr, mtart TYPE mara-mtart, END OF ty_mara. DATA: lt_mara TYPE STANDARD TABLE OF ty_mara WITH NON-UNIQUE SORTED KEY k1 COMPONENTS matnr mtart.定义了辅助排序键之后可以用新语法直接读取READ TABLE lt_mara WITH TABLE KEY k1 COMPONENTS matnr lv_matnr mtart lv_mtart.这个方案的优势是主内表仍然是STANDARD TABLE不影响顺序访问和APPEND的灵活性同时给特定字段组合建立了一套排序索引。适合那种主访问路径是顺序处理、偶尔需要按别的字段快速查找的场景。不过这个特性对很多老项目来说相对冷门用的人不多了解即可。4. 常见问题与排障实录4.1 忘记SORT导致查不到数据这是BINARY SEARCH最高发的问题。现象是同样的内表用普通READ TABLE能找到数据加BINARY SEARCH反而SY-SUBRC返回4什么都查不到。很多人第一反应是怀疑BINARY SEARCH写错了其实代码没问题就是没排序或者排序方式不对。我在一个物料分类报表里遇到过一次。当时内表数据来自好几个地方中间用APPEND往表里加数据最后做匹配时加了BINARY SEARCH结果大量匹配不上。排查时先检查SORT发现SORT在循环后面而且只排序了一个字段而查找用了三个字段组合键排序不完整导致查找失败。修正方式很简单先查哪些字段就按哪些字段SORT并且SORT语句要在所有APPEND完成之后执行。4.2 重复键导致取错数据二分查找只保证能找到“某一条满足条件的记录”不保证是你要的那一条。业务上如果同一个键对应多条数据就得自己控制排序方向。举个例子销售订单行项目里同一订单号可能对应多个行号。若你想按“订单号”取最后一个行项目正确做法是先把内表按订单号升序、行号降序排列然后BINARY SEARCH取到的就是该订单的最后一个行项目。这个技巧在处理“只取最新状态”一类需求时非常实用。4.3 LOOP内循环查询的性能隐蔽问题有一种情况特别容易让人误判循环外层只有几百行内表也就一两万行看起来“不应该慢”。但如果你在循环里用默认线性READ TABLE去查这个大内表总比较次数是几百乘以几万量级直接上去。光靠感觉优化是靠不住的。我一般建议用事务码SE30或者新的事务码SAT跑一下运行时间分析或者直接看代码里复杂循环里面的READ TABLE有没有加BINARY SEARCH。如果循环次数多、内表又大这类问题几乎是肉眼可见的性能杀手。4.4 排序稳定性与中西文字段排序差异ABAP的SORT在默认情况下对字符型字段的排序规则并不总是和业务预期一致。比如物料号如果是混合编码排序结果可能和数据库的ORDER BY有差异这也会导致BINARY SEARCH结果和SQL查询结果不一致。经验做法是在SORT时明确指定排序规则比如使用STABLE选项保持稳定排序。如果要按特定语言排序还可以考虑设置locale。虽然这些细节在大多数项目里不会碰到但一旦碰到排查成本很高提前心里有数比较好。4.5 把二分查找思维延伸到数据库层其实二分查找不只存在于ABAP内存SAP的数据库查询优化器也会用类似的思想选择执行计划比如索引查找就是某种意义上的“数据库版二分查找”。所以从更大视角看优化内表查询的终极方案是把尽可能多的关联操作下推到数据库里用OPEN SQL的JOIN完成让HANA数据库的列式存储和索引去发挥作用。有时候你会发现与其把所有数据拉到ABAP层用BINARY SEARCH处理不如直接改一条SQL关联逻辑交给数据库代码更简洁性能可能还更好。HANA时代尤其如此内存数据库的强项就是快速处理大量关联查询。但也要注意HANA性能虽好不代表无脑全下推就好复杂业务逻辑放在应用层可维护性更强权衡点在于数据量和查询复杂度。4.6 排查BINARY SEARCH问题的小工具最后分享几个我实际排查性能问题时的常用手段事务码SAT新版本SAP的性能分析工具可以看ABAP语句耗时非常直观地展示READ TABLE到底花了多少时间。事务码SE30老牌性能分析工具适合分析整个程序的时间消耗分布。断点调试看SY-SUBRC如果BINARY SEARCH总是匹配不上断点停在READ TABLE语句后直接检查内表排序状态比瞎猜效率高多了。这几个工具配合使用基本能覆盖90%以上的内表查询性能问题。很多时候问题不是“算法不存在”而是“算法用错了地方”或“前提条件没满足”。二分查找这个算法简单到大学课本第一学期就学但真正在SAP项目里用对、用好、用出价值靠的还是对这些细节的敬畏。我个人在实际操作中的体会是每次写READ TABLE之前先问自己三个问题——表排好序了没有排序键和查找键一致吗这个内表类型本身是否还有更优的替代方案想清楚这三件事大部分性能隐患都能在编码阶段就避开。BINARY SEARCH不是什么高深技术但它值得每个ABAP开发者真正重视起来。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进