ABAP模糊查询从入门到实战:LIKE语法、动态拼接与性能优化
做ABAP开发这些年被业务问到最多的需求之一就是在报表里加一个模糊查询。客户名称记不全了只记得中间两个字物料描述太长只想输个关键词供应商编号忘了但记得城市名——这些场景每天都在SAP里上演。很多新人一上来就写LIKE %关键字%跑通了就以为完事结果数据量大一点就卡到怀疑人生或者用户输入个特殊字符直接把程序搞dump。这篇就把ABAP模糊查询从基础语法到实战坑点完整过一遍包括动态WHERE拼接、ALV界面上的F4搜索、多字段组合检索怎么做以及我踩过几次之后总结出的性能和处理思路。1. 先把LIKE吃透模糊查询的基石用法1.1 为什么业务总爱问模糊查询先说一个最简单的场景。供应商主数据存在LFA1表里几千条记录业务顾问问你我忘了完整编号只知道供应商名字里有宏字怎么查这种需求看起来平淡无奇但它牵涉到一个核心问题精确查询在业务软件里往往不顶用,因为人的记忆是模糊的。物料编码带前导零客户名称带公司后缀描述字段里夹杂着型号、规格、厂商——这些数据靠根本匹配不了。模糊查询解决的正是我知道一部分信息但拼不出完整键值的场景。所以在我看来模糊查询不是SAP查数据偷懒的写法而是业务系统里必不可少的人机交互手段。它本身没有高低之分用得好坏取决于你对通配符、索引、数据特征的理解深度。1.2 %、_和ESCAPE三种符号覆盖所有匹配场景ABAP里的模糊查询核心是LIKE操作符配套通配符用得最多的是%和_。%匹配任意长度字符序列包括0个字符_只匹配单个字符ESCAPE转义符用来匹配数据里真实存在的%和_字符举个实际例子。LIKE 宏%能匹配所有以宏开头的名称LIKE %宏匹配以宏结尾的名称LIKE %宏%匹配中间任何位置包含宏的名称。_用得少但确实有场景——你记得客户编号是5位数字第一位和第二位是10后三位不记得那就可以写LIKE 10___。ESCAPE这个很多人会忽略。假如你搜索的关键字本身包含%符号比如用户输入了利润率50%直接拼进LIKE条件里50%里的%会被当成通配符查出来的结果完全不是预期。ABAP里标准写法是配合ESCAPE \IF lv_key CS %. REPLACE ALL OCCURRENCES OF % IN lv_key WITH \%. ENDIF. SELECT lifnr name1 FROM lfa1 WHERE name1 LIKE lv_pattern ESCAPE \ INTO TABLE DATA(lt_result).中文和大多数业务用户输入很少真的带%但一旦你做过报关单号、税率相关的查询就会意识到这个坑务必提前堵上。用户在那头怎么输入你这个头如果不做字符转义查出来的就是全表数据或者错误集。1.3 一个能立刻上手的查询示例用PARAMETERS接收关键字然后拼接LIKE条件的写法长这样PARAMETERS: p_key TYPE c LENGTH 40. DATA: lv_pattern TYPE string. lv_pattern % p_key %. SELECT lifnr name1 ort01 FROM lfa1 WHERE name1 LIKE lv_pattern INTO TABLE DATA(lt_vendor) UP TO 100 ROWS. IF sy-subrc 0. cl_demo_outputdisplay( lt_vendor ). ENDIF.这里有几个我常强调的细节用UP TO 100 ROWS做行数上限防止用户搜个空字符串把整张表拉出来。用户随手按回车p_key是空的%加空串等于%%等价于查全表这在生产环境会把DB load瞬间打满。字符串拼接用ABAP 7.40之后很顺手老系统用CONCATENATE ... INTO ...效果一致不涉及功能差异。lv_pattern是ABAP 7.40引入的转义宿主变量写法推荐使用避免把外部值直接塞进SQL文本里省去很多引号转义的麻烦。这段代码就是一个最小可用的模糊查询骨架。接下来真正的复杂性不在SQL本身而在条件不是写死的这个现实问题上。2. 动态WHERE拼接从写死条件到用户自由输入2.1 什么时候必须上动态SQL固定LIKE条件只能应对参数固定的场景。实际报表的查询界面通常是用户可以选择只查名称也可以只查城市还可以名称城市同时筛更可以一个关键字同时打多个字段。这种条件组合无穷无尽你不可能为每个组合都写一条静态SQL。SELECT-OPTIONS能解决一部分区间查询问题但处理三个字段任选其一这种需求时大家还是习惯拼WHERE字符串。这时候就要用到动态OPEN SQL把WHERE子句组装成一个字符串变量然后直接跟在WHERE后面。DATA(lv_where) NAME1 LIKE % lv_key %. SELECT lifnr name1 ort01 FROM lfa1 WHERE (lv_where) INTO TABLE DATA(lt_vendor).注意这里WHERE后面是括号包裹的字符串变量ABAP会把它当成动态条件解析。这是最常见的动态查询写法但也是最容易翻车的写法下面几个坑我一个个说。2.2 动态拼接的三个常见坑单引号、空值、多余空格第一个坑单引号。如果lv_key来自用户输入内容里包含单引号比如公司名是OBrien直接拼进动态SQL里就有语法风险。动态SQL是把它当ABAP源代码解析的字符串字面量里的单引号必须翻倍转义REPLACE ALL OCCURRENCES OF IN lv_key WITH .第二坑空值条件。用户没有输入城市但你的WHERE里有ORT01 LIKE %等于所有供应商的城市都满足——跟不过滤完全一样但DB还得老老实实扫一遍。正确做法是用户没填时压根不加这个条件到WHERE字符串里。简单的思路是分段APPENDDATA: lt_where TYPE TABLE OF string. IF p_name IS NOT INITIAL. APPEND |NAME1 LIKE %{ p_name }%| TO lt_where. ENDIF. IF p_city IS NOT INITIAL. APPEND |ORT01 LIKE %{ p_city }%| TO lt_where. ENDIF. CONCATENATE LINES OF lt_where INTO lv_where SEPARATED BY AND .第三坑多余空格。用户经常从Excel里拷贝搜索词过来尾部或者中间夹着全角空格、回车换行。我见过有人搜了半天查不出数据debug发现p_key后面挂了两个不可见字符。稳妥做法是进WHERE之前CONDENSE一下并且把全角空格转半角lv_key condense( lv_key ). REPLACE ALL OCCURRENCES OF IN lv_key WITH .写动态SQL本质上是在内存里生成一段半代码。你越控制用户输入的形状运行时就越不容易出乱子。2.3 用RANGES表代替手拼LIKE字符串其实不只是动态拼接这一条路。ABAP里SELECT-OPTIONS生成的范围表本身就支持CP操作符它的语义和LIKE完全一致用户甚至可以在选择屏幕上直接输入通配符。这种方式更安全因为组件自己处理了值和类型。DATA: lr_name TYPE RANGE OF lfa1-name1. lr_name VALUE #( sign I option CP low *宏* ). SELECT lifnr name1 ort01 FROM lfa1 WHERE name1 IN lr_name INTO TABLE DATA(lt_vendor).这段代码的好处是范围表可以无限追加多行比如同时匹配宏和华两个模式。配合屏幕上的SELECT-OPTIONS用户自己就能输入带*的模糊条件连程序都不用改。如果你的模糊查询只是单字段我甚至建议优先考虑范围表方案而不是手动拼动态SQL,毕竟范围表是ABAP官方推荐的参数化查询手段对DB更友好也没有引号转义问题。2.4 输入合法性校验别让abc进入LIKE条件热搜词里有abap判断字符串是否是数字这其实和模糊查询的落地关系很大——用户在查询界面上输入物料号、供应商编号时应该校验他填的是不是数字。经常有人在一个模式为数字的字段上做模糊查询结果用户输入了字母查出来的结果是空他还不信。建议在进入SQL之前先判断一下DATA(lv_input) 123. TRY. DATA(lv_num) CONV i( lv_input ). CATCH cx_sy_conversion_no_number. MESSAGE 请输入数字 TYPE E. ENDTRY.TRY-CATCH的方案能兼容负数、前导空格、科学计数法这些场景。如果要在老系统上回避异常也可以用字符集合逐个校验的写法但我个人更推荐异常捕获ABAP在这方面的异常机制处理得还算干净。把这一层校验放在查询前面能挡掉相当一部分用户误操作。3. 越查越慢的真相全表扫描、索引失效与应对三板斧3.1 为什么LIKE %关键字%不走索引这是模糊查询绕不开的硬伤。数据库索引的基本原理是B树有序查找你想让索引发挥作用查询条件必须能告诉你从哪开始找。而LIKE %宏%的含义是字段任意位置出现了宏——数据库不知道宏在字段开头、中间还是结尾所以只能一行一行把整张表的数据拉出来逐个检查字段内容。这就是全表扫描。几千条数据没感觉几十万、上百万的物料/客户主数据配合报表里的JOB并发效果就是查询响应时间从毫秒级变成秒级甚至分钟级DB的CPU和IO飙升。很多性能事故的根源不在硬件而在一个不起眼的%关键字%。3.2 三板斧之提前缩圈先等值过滤再做模糊对付全表扫描的第一招不是优化模糊查询本身而是减少参与模糊查询的数据量。用一个生活类比你要在一栋楼里找一个名字带宏的人你不会挨着全楼一家一家敲门而是先确定他在哪层哪侧——先缩圈再细查。放到SQL里就是SELECT lifnr name1 ort01 FROM lfa1 WHERE bukrs p_companycode AND name1 LIKE lv_pattern INTO TABLE DATA(lt_vendor).bukrs公司代码是等值条件能走索引首先把记录集缩小到该公司代码的供应商再在这个相对小的集合里做LIKE模糊匹配代价小得多。报表开发时我习惯先看看表里有没有公司代码、工厂、销售组织这类高选择性字段能筛选就先筛选这往往比你在模糊查询本身技术上花力气更有效。3.3 三板斧之前缀化索引和LIKE并非水火不容LIKE 宏%是走索引的因为数据库能根据宏这个前缀在索引里定位到所有以宏开头的记录。所以如果业务场景本身允许用户从开头匹配——比如物料号、客户号这类有明确编码规则的字段——就把%只放在后面SELECT matnr FROM mara WHERE matnr LIKE lv_prefix lv_prefix 1000% INTO TABLE DATA(lt_material) UP TO 100 ROWS.注意ABAP物料号在表里是内部存储格式带前导零。用户输入1000你要转成内部格式000000000001000再拼前缀否则匹配不出来。这类内部表示和外部表示的坑在SAP业务表里特别多主数据字段几乎是重灾区。3.4 三板斧之冗余搜索字段及HANA模糊搜索的方向如果业务确实需要%关键字%的任意位置匹配索引这条路基本走不通那就换思路别在查询时算要在写入时算。我参与过的项目中有一种很有效的做法是给表增加一个搜索冗余字段。比如在Z表里建一个ZSEARCH字段内容是供应商编号、名称、城市、联系人拼接后的大写全文本。数据维护或同步的时候拼好存进去查询的时候只对这个字段做LIKE %关键字%。虽然字段级任意位置匹配还是全表扫但扫描单字段比扫描5个字段拼接、而且不用在SQL里做OR运算执行计划简单效率高一个量级。更进一步如果你的系统是S/4HANA可以考虑用CDS视图配合数据库层的模糊搜索能力比如HANA的CONTAINS或词法搜索。不过这个方向依赖具体版本和权限不是所有ABAP环境都能用。个人经验是传统ERP数据量大到冗余字段都吃力时再考虑上HANA原生搜索常规项目里冗余字段范围表已经能覆盖九成需求。4. ALV报表里的模糊查询落地F4搜索帮助与自定义检索4.1 回答热搜问题REUSE_ALV_GRID_DISPLAY能加F4吗有不少人搜abap reuse_alv_grid_display 可以加f4吗我的回答是可以但要分清楚你要的是哪种F4。如果你只是想让ALV的某个字段回车或点击时自动弹出SAP标准的搜索帮助那不用写任何事件代码字段目录里配置引用就可以。如果你想在F4弹窗里塞一个带模糊查询条件的自定义搜索面板那REUSE_ALV_GRID_DISPLAY本身没有直接挂回调的参数绕不开OO ALV。这两个方向我下面各说一套方案。4.2 方案A标准字段目录自动带出F4ALV输出物料字段时只要在字段目录里配置REF_TABLE和REF_FIELD运行时ALV就会自动带上该表字段对应的数据元素搜索帮助DATA: ls_fieldcat TYPE lvc_s_fcat. ls_fieldcat-fieldname MATNR. ls_fieldcat-ref_table MARA. ls_fieldcat-ref_field MATNR. ls_fieldcat-coltext 物料. APPEND ls_fieldcat TO lt_fieldcat.这样用户点物料字段的F4弹出来的是SAP标准物料搜索帮助里面天然支持按描述、按编码模糊搜索体验比你自己写对话框好得多。说实话这是最简单的落地方式很多需求到这里就已经完成。如果业务要求弹出来的搜索面板要按物料描述物料组同时筛标准搜索帮助也能配置成组合搜索。所以我的建议永远是先看标准搜索帮助能否满足实在不行再写自定义。4.3 方案B自定义F4事件弹窗里做模糊查询自定义F4的思路是这样的不依赖REUSE_ALV_GRID_DISPLAY的参数而是把ALV从函数模块换成OO写法用CL_GUI_ALV_GRID实例注册F4事件。DATA: gr_grid TYPE REF TO cl_gui_alv_grid, lt_f4 TYPE lvc_t_f4. DATA(ls_f4) VALUE lvc_s_f4( fieldname NAME1 register abap_true ). APPEND ls_f4 TO lt_f4. gr_grid-register_f4_for_field( it_f4 lt_f4 ).注册完之后用户在该字段按下F4就会触发ONF4事件。在事件处理器里你可以弹出一个自建查询对话框对话框里放几个输入框——名称模糊、城市模糊、编号模糊——查询结果以内表形式返回ALV。OO ALV我这里不展开全部代码但有一个坑要提醒REGISTER_F4_FOR_FIELD注册时需要注意,这个方法是基于字段的如果你想让整个报表多个字段都支持自定义F4需要在lt_f4里全部注册。另外事件处理器要用全局类或者可被引用的处理类实例否则事件触发时会找不到方法。从工程角度讲方案B适合那些有交互复杂度、需要给用户展示多个输入框的查询场景。功能实现不难难的是把搜索面板里的逻辑和ALV列表刷新做好衔接——通常是在F4返回后重新填充ALV显示的数据内表即可。5. 多字段组合检索与搜索帮助复用少造轮子的进阶思路5.1 一个关键字同时命中编号、名称、备注的三种做法最后说一个我几乎每个项目都会遇到的需求用户只给一个关键字希望它同时去匹配编号、名称、城市、备注哪个字段命中都算。这个需求看起来只是多加几个OR但实现方式有好有坏。最直白的做法是SQL里拼ORDATA(lv_pattern) % lv_key %. SELECT lifnr name1 ort01 FROM lfa1 WHERE lifnr LIKE lv_pattern OR name1 LIKE lv_pattern OR ort01 LIKE lv_pattern INTO TABLE DATA(lt_vendor) UP TO 100 ROWS.这种写法逻辑最清楚但性能取决于OR条件下表的扫描范围。三个字段全做任意位置匹配等于把全表按最坏路径扫三遍。好在UP TO 100限制住了返回量DB在找到足够行数后可能提前停止——不过DB内部行为没法保证数据量大时依然不推荐在主表上硬来。第二种做法就是前面提的冗余搜索字段。把需要检索的字段拼接存成一个ZSEARCH界面只对这一个字段做LIKE代码短、执行计划稳定。它的维护成本在数据写入侧——通过BADI、增强或者同步程序保证拼接字段的一致性。对这个字段建索引没太大帮助因为还是%关键字%但单字段扫描无论如何比多字段OR要轻快。第三种做法是用CDS视图封装一层查询逻辑把本来要在ABAP层做的字段拼接和模糊匹配下沉到数据库层。如果你在S/4HANA上CDS还支持LIKE_REGEXPR或CONTAINS这类更高级的匹配功能。这个方向的工作量主要在前期建模但到了运行阶段ABAP程序只需要一句SELECT就能拿到结果整体架构会清爽很多。5.2 自定义搜索帮助从报表内查询到全局复用再强调一次我对模糊查询的建议优先使用搜索帮助Search Help而不是在自己的程序里从头写一套模糊查询。SAP的标准搜索帮助在全球范围内被几万家公司在用是经过大量打磨的成熟组件比你在一张报表里手搓的LIKE条件健壮得多。如果你在SE11里看数据元素很多字段已经关联了搜索帮助。直接把这个搜索帮助配到报表字段上等于白捡了一套模糊查询能力。比如MATNR关联的搜索帮助支持按物料描述、物料类型、基本计量单位等多维度搜索。如果标准搜索帮助没有满足需求的也可以自己建事务码SE11创建搜索帮助指定搜索帮助参数对应到表字段保存后在字段的数据元素上关联搜索帮助。然后所有引用这个数据元素的地方自动获得搜索能力报表、交式事务、Web界面全都生效。我自己做模糊查询越做越深的体会是模糊查询不是一个SELECT技巧而是一条交互链路。从用户输入、字段校验、通配符转义、SQL构造、索引选择到ALV上的F4帮助、搜索帮助复用——每一环都做好用户才会觉得系统好用只盯着一句LIKE写程序跑通了也只会被嫌弃怎么这么慢怎么查不到。如果你在做一个查询报表别急着动手写代码先花十分钟确认那几个关键问题用户会在哪些字段模糊查数据量级多大有没有公司代码等前置筛选条件有没有标准搜索帮助可用答案越清楚后面返工越少。最后分享一个小技巧收尾给LIKE模式做转换时建议把%和关键字拼接的逻辑统一收口到一个方法或FORM里不要在每个查询程序里各写一遍。统一入口之后转义处理、通配符规范化、空值保护都只维护一份换数据库或换HANA时也只需要改这个入口。用SAP久了你会发现大部分稳定性不来自你写的那条SQL多漂亮而来自你对公用代码耐心的维护。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →