
1. 为什么要在 SE11 里手搓带 Exit 的搜索帮助在 SAP 里做报表、做自建表维护、做选择屏幕只要涉及让用户“选一个值”搜索帮助Search Help就是绕不开的一环。很多人第一反应是去 SE11 建个带 Check Table 的字段让它自动带出 F4这种做法在简单字典表场景下确实快但一旦遇到“用户输入条件后要动态过滤候选值”“要按权限裁剪可见范围”“要把多个字段拼成一个展示列”“选完之后顺带把另外三个字段一起带出来”这类需求字典表那套就明显不够用了。这时候就轮到带 Search Help Exit 的搜索帮助出场。它本质上是一个函数模块Function Module在搜索帮助被唤起的那一刻接手控制权你可以在这里接管候选数据的读取、行的过滤、显示列表的定制、返回值字典的拼装。说得直白一点普通搜索帮助是“从表里捞数据”带 Exit 的搜索帮助是“你说了算你想捞什么、怎么显示、怎么回填都由你决定”。标题里这个SE11创建search help (带search help exit function)说的就是在 SE11 事务码里新建一个搜索帮助对象把 Exit 参数挂上去再实现对应的函数模块。听着只有两步实际上踩坑点非常密集参数名对不上会直接 DUMP、事件顺序搞错会导致值取不到、返回结构字段不匹配会让 F4 选了没反应、受限的帮助Restrict和附加字段Append配置不当会让用户一头雾水。这篇内容适合谁看三类人最合适一是刚接触 ABAP 搜索帮助、想从“会配字典表”进阶到“能自己控制搜索逻辑”的初学者二是手上有具体需求、需要一个能直接抄作业的模板的开发者三是被别人的 Exit 代码坑过、想看看到底哪些地方容易翻车的维护人员。我会按照“整体设计思路 → 核心参数与事件解析 → 完整实操过程 → 常见问题排查”的顺序来讲中间会把我自己踩过的坑和实测有效的做法都摊开说尽量让你看完就能上手做一个能跑的东西。需要先说明一点下面的实操步骤和参数配置一部分来自标题和关键词的直接指向另一部分是基于我在实际项目里做这类对象的常见做法补充出来的因为标题本身只给了对象名和“带 Exit”这个范围没给具体的业务表。我会用一个虚构的物料辅助信息表做例子你把它换成自己的表就行。2. 整体设计与方案选型为什么不直接用 Check Table动手之前先把路线定清楚不然写到一半发现方向错了返工成本很高。做搜索帮助大致有三条路字典表直接带出、手工建搜索帮助但不用 Exit、手工建搜索帮助并挂 Exit。三者的差别不在“能不能用”而在“需求有多复杂”。2.1 三种搜索帮助实现路径的取舍逻辑字典字段挂 Check Table 是最省事的在 SE11 建表时给字段设了参考表和外键F4 自动就出来了。它的好处是零代码、维护成本低坏处也明显——固定只能按外键字段精确匹配展示列只能是描述字段没法做模糊匹配、没法按条件过滤、没法组合多个来源。适合的场景是纯字典型数据比如国家代码、单位、货币这类。手工建搜索帮助但不挂 Exit比上一种灵活一些你可以自己决定 Selection Method 是哪张表或者哪个视图可以选多个 Parameter 做输入限制可以指定显示哪些列。这类方案适合“单表或者单视图能覆盖”的场景比如从一张客户主数据表里按姓名模糊查编号。缺点是数据源仍然是静态的一旦要跨表拼接、要按用户角色裁剪、要在运行时做二次加工它就无能为力。挂 Exit 的方案是最后一张牌。它的逻辑是搜索帮助在触发时系统先走正常的 Selection Method 取数流程然后在几个关键节点回调你指定的函数模块你可以选择在取数前改 LP 参数、在取数后过滤内表、在显示前重排显示顺序、在返回值生成前改写回填内容。代价是要写代码、要注册函数模块、要处理各事件的参数对接调试也比前两种麻烦。方案是否需要写代码灵活度维护成本典型适用场景字段带 Check Table否低低国家、单位、货币等纯字典值手工搜索帮助无 Exit否中中单表/单视图模糊查询手工搜索帮助 Exit是高中高跨表拼接、权限裁剪、动态过滤、多字段回填判断标准很简单如果你的需求里出现了“动态”“按权限”“跨表”“拼接”“选一个带出多个”这类词直接上 Exit如果只是“给个下拉列表”前两种完全够用不要为了炫技去写 Exit那纯属给自己找维护负担。2.2 带 Exit 帮助的内部执行模型要写好 Exit必须搞清楚系统在唤起搜索帮助的那一瞬间做了什么。大致的调用顺序是这样的用户按下 F4系统检查字段绑定的搜索帮助加载搜索帮助定义然后把当前屏幕上的输入值整理成选择条件接着判断是否绑定了 Exit 函数模块——如果绑了就开始依次回调它每个回调携带一个SHLP结构和几个事件标识最后由 Exit 决定是继续走默认取数还是自己接管。这个过程里有几个关键点值得提前记住。第一Exit 函数模块的接口是固定的参数名不能自己改一改就对接不上轻则事件不触发重则直接短转储第二系统通过CALLCONTROL-STEP这个字段告诉你当前处于哪个阶段你的代码主体就是一个大的CASE CALLCONTROL-STEP第三候选数据是通过RECORD_TAB和SHLP_TAB这两个内表在系统和 Exit 之间传递的它们的结构和行含义是固定的不能随便改字段。理解了这个模型后面所有配置和代码就都顺了SE11 里配的那些东西本质上是在告诉系统“去哪取数、显示哪些列、把哪个字段回填给哪个屏幕字段”而 Exit 干的事情是在这些默认流程上插一脚。2.3 一个虚构但完整的业务场景为了让后面的步骤有落脚点我设一个场景某公司自建了一张物料辅助信息表就叫它ZMAT_AUX吧字段包括物料号MATNR、工厂WERKS、辅助类别AUX_TYPE、备注REMARK、启用标志ACTIVE。业务方要求在选择屏幕上按物料号搜索时只能看到当前用户有权限的工厂下的数据且已停用ACTIVE 的记录不能被选中同时展示列表要把“物料号 工厂 备注”拼成一列方便肉眼辨认选中后除了物料号还要自动带出工厂。这四个要求分别对应了 Exit 的四类典型用法按权限裁剪过滤行、排除无效数据过滤行、定制显示列改显示内表、多字段回填改返回值。正好把 Exit 能干的活全用上了作为一个完整例子很合适。3. 核心细节拆解参数、事件与关键结构这一部分是全文的地基配置错一个参数后面代码写得再漂亮也跑不通。我把最容易出错的地方拆开讲。3.1 Exit 函数模块的固定接口带 Exit 的搜索帮助必须绑定一个函数模块且这个函数模块的接口必须严格符合系统约定。标准接口是这几个参数参数名方向类型作用说明SHLP_TAB传入/传出表类型行结构为SHLP_DESCR搜索帮助描述表包含帮助定义、选择方法等信息RECORD_TAB传入/传出表类型行结构为DDSHRETVAL搜索结果记录表即候选数据CALLCONTROL传入/传出结构类型DDSHCC控制结构STEP字段标识当前事件阶段SHLP传入/传出结构类型SHLP_DESCR当前处理的搜索帮助描述INTERFACE传入/传出结构类型DDSHF4CTRL界面控制控制是否显示对话框等SELECTIONS可选表类型选择条件传递这里最容易翻车的是参数名和类型必须一字不差。我见过有同事图省事把RECORD_TAB写成RECORD_TABLE函数激活没问题但搜索帮助挂上去后事件完全不触发排查了半天才发现在这儿。还有人把CALLCONTROL的类型从DDSHCC改成别的结果一按 F4 就是短转储。建议直接用 SE37 新建函数模块后手动按标准接口写别信自己记的。注意SHLP_TAB和SHLP这两个参数在较新的系统版本里都建议保留即使你的逻辑只用到RECORD_TAB也不要删掉任何标准参数否则接口校验不通过。3.2 CALLCONTROL-STEP 的事件顺序Exit 的所有逻辑都围绕CALLCONTROL-STEP这个字段展开它是系统告诉你的“现在该干啥”的信号灯。常见的取值和对应阶段如下STEP 值阶段含义你可以做的事SELECT准备执行取数修改选择条件、跳过默认取数自行填充RECORD_TABCHECK取数完成、展示前过滤RECORD_TAB、排除无效记录DISP准备显示列表定制显示内表、调整列顺序、拼接展示内容RETURN生成返回值决定选中后回填哪些字段、回填到什么位置EXIT结束清理工作一般不做业务处理顺序大致是SELECT → CHECK → DISP → RETURN → EXIT。但要注意如果你的代码在某个阶段调用了EXIT或者把STEP置成空后续阶段就不会再走了。所以每个分支处理完务必把CALLCONTROL-STEP推进到下一阶段否则会出现“点了执行但界面卡住不动”的诡异现象。这里有个非常实用的技巧在SELECT阶段如果你判断默认取数不满足需求比如要跨表联合查可以自己用SELECT语句把数据读进RECORD_TAB然后把CALLCONTROL-STEP置为CHECK让流程继续。这样系统就不会再去执行 Selection Method 的那套取数逻辑完全由你接管。这个做法在性能要求高、或者数据源根本不在 Selection Method 里时特别管用。3.3 DDSHRETVAL 的行结构与字段含义RECORD_TAB的每一行是一个DDSHRETVAL它承载的是一条候选记录的数据和元信息。几个关键字段必须搞清楚字段含义使用要点FIELDNAME字段名标识这行数据对应搜索帮助的哪个 ParameterFIELDVAL字段值实际存储的值回填时用的就是它STRING字符串值用于长文本或拼接展示SELOPT是否作为选择条件控制该行是否参与筛选SPRAS语言多语言场景下用于区分FIELDNAME这个字段是很多新手迷惑的地方。RECORD_TAB不是“一行一条业务记录”的扁平结构而是“一行一个字段值”的纵向结构——也就是说如果搜索帮助有物料号和工厂两个 Parameter那么一条候选记录会对应两行RECORD_TAB两行的FIELDNAME分别标识。这意味着你在CHECK阶段过滤记录时不能简单地DELETE RECORD_TAB WHERE ...必须按某种维度通常是行号或者成组判断来处理否则会把一条记录的两个字段拆散。我个人的做法是在CHECK阶段先把RECORD_TAB按FIELDNAME重组一遍拿到业务主键和值的映射判断哪些主键该保留再整体删除不该保留的那些行。听起来绕但这比无脑DELETE稳得多。3.4 显示列的定制入口如果需要在显示列表里自己拼一列比如前面场景里的“物料号工厂备注”入口在DISP阶段。系统在这里已经准备好显示用的字段描述内表你可以往里面插入新的列定义同时往RECORD_TAB里补上对应FIELDNAME的行。这里有个细节新增的显示字段其FIELDNAME必须和你在RECORD_TAB里补的行严格对应且字段必须出现在搜索帮助定义的 Parameter 里哪怕是隐藏的、不对外暴露的否则显示列会是空白。我踩过一次显示列加上了但一直是空的查了两个小时才发现是 Parameter 没建全。4. 完整实操从建表到跑通 F4理论说得差不多了现在开始动手。整个过程分五步准备数据源 → 建搜索帮助骨架 → 配参数和界面 → 建函数模块并写 Exit → 绑定并测试。4.1 准备数据源与测试数据先在 SE11 建好ZMAT_AUX这张表字段按前面场景设。然后塞几条测试数据至少要有不同工厂的同一物料、已停用的记录、备注为空的记录这样在测试过滤逻辑时能看出效果。数据量建议先放二三十条太少看不出过滤和显示效果太多调试起来反而慢。提示测试数据里一定要有一条ACTIVE 的并且它所在工厂是当前用户有权限的这样才能验证“停用记录被排除”这个逻辑真的生效而不是恰好被权限过滤掉了。4.2 在 SE11 创建搜索帮助对象事务码 SE11选择“搜索帮助”单选输入对象名比如ZSH_MAT_AUX点创建。弹出的对话框里输入描述选择“基本搜索帮助”作为类型。这一步比较傻瓜没什么坑唯一要注意的是命名规范——建议统一加项目前缀后面在屏幕上绑定的时候好找。创建完进到定义界面接下来是重头戏配置 Selection Method、Parameters、Exit。4.3 配置 Selection Method 与 ParameterSelection Method 填ZMAT_AUX这是默认取数的来源。然后定义 Parameter我一般建这几个MATNR物料号必须、WERKS工厂非必须、REMARK备注非必须、隐藏或仅显示、AUX_DISP拼接展示列非必须、仅显示。Parameter 的“导入”和“导出”勾选很关键作为输入条件的要勾导入作为回填值的要勾导出。MATNR和WERKS是既导入又导出既能作为查询条件选中后又要回填REMARK和AUX_DISP只做显示勾导出即可它们通常也要在选中后传给调用方只是你的屏幕字段可能没绑定而已。Parameter导入导出说明MATNR是是主查询条件也是主要回填值WERKS是是辅助条件选中后一并回填REMARK否是仅展示回填供参考AUX_DISP否是展示列由 Exit 填充配置完这几行把“对话框类型”按需要设成“显示值列表”或者“带限制的对话框”后者的搜索体验更好但要求 Parameter 配置更严谨。4.4 编写并绑定 Exit 函数模块用 SE37 新建函数模块比如叫ZFM_SH_EXIT_MAT_AUX。接口严格按 3.1 节的表格来一个参数都不能少、不能改名。源码主体就是一个CASE CALLCONTROL-STEP结构各分支的逻辑我下面拆开说。SELECT分支我一般只做一件事把用户输入的筛选条件取出来存到全局变量或者通过SHLP-SELOPT拿。如果确实要自己接管取数就在这里SELECT出数据填进RECORD_TAB然后把STEP推到CHECK。大多数场景下不需要接管取数让系统按 Selection Method 走就行代码更少、出错更少。CHECK分支是过滤的主战场。这里要做两件事排除ACTIVE 的记录以及按当前用户的工厂权限裁剪。权限判断我一般用AUTHORITY-CHECK配合自定义权限对象但为了例子简单可以直接用一个内表模拟“当前用户可访问的工厂列表”然后据此过滤。DISP分支负责展示定制。这里需要往RECORD_TAB里补充AUX_DISP对应的行值就是MATNR、WERKS、REMARK的拼接。注意拼接时要处理REMARK为空的情况不然会拼出难看的尾巴。RETURN分支决定回填。如果默认逻辑已经能正确回填MATNR和WERKS这个分支可以只写一句推进STEP。如果需要回填到特殊位置或者做值转换就在这里改RECORD_TAB里对应行的FIELDVAL。代码写完后回头到 SE11 的搜索帮助定义里找到 Exit 相关配置区把函数模块名填进去。填完激活搜索帮助再去 SE37 激活函数模块。这个顺序反过来也行但建议先激活函数模块否则搜索帮助激活时可能报“Exit 函数不存在”。4.5 在屏幕上绑定并验证最直接的验证方式是在 SE38 建一个测试程序选择屏幕上定义一个MATNR字段用MATCHCODE OBJECT ZSH_MAT_AUX绑定。运行程序按 F4看候选列表是不是只有当前用户工厂下的、ACTIVE为空的记录是不是消失了、展示列是不是拼接好的。验证回填效果就在选择屏幕的事件里WRITE出MATNR和WERKS的值看选中一条后两个字段是否都被正确填充。如果只填了MATNR而WERKS没动多半是 Parameter 的导出没勾或者RETURN阶段的代码把值覆盖掉了。5. 代码模板可直接抄改的 Exit 骨架下面这份代码是按前面的场景写的你替换掉表名和字段名就能用。我尽量把注释写细每一段为什么这么写都标出来。FUNCTION zfm_sh_exit_mat_aux. *---------------------------------------------------------------------- * 搜索帮助 Exit物料辅助信息 * 接口严格按系统约定不可改名 *---------------------------------------------------------------------- TYPE-POOLS: shlp. DATA: ls_record TYPE ddshretval, lt_matnr TYPE STANDARD TABLE OF zmat_aux-matnr, lt_auth_wrk TYPE STANDARD TABLE OF zmat_aux-werks, lv_matnr TYPE zmat_aux-matnr, lv_werks TYPE zmat_aux-werks, lv_remark TYPE zmat_aux-remark, lv_disp TYPE char100, lv_idx TYPE i. * 模拟当前用户的工厂权限实际项目换成权限检查或配置表 APPEND 1000 TO lt_auth_wrk. APPEND 2000 TO lt_auth_wrk. CASE callcontrol-step. WHEN SELECT. * 这里不接管取数让系统走 Selection Method 默认逻辑 callcontrol-step CHECK. WHEN CHECK. * 过滤逻辑先按主键归组再整体判断保留与否 * 注意 RECORD_TAB 是纵向结构一行一个字段值 SORT record_tab BY fieldname fieldval. LOOP AT record_tab INTO ls_record WHERE fieldname MATNR. lv_matnr ls_record-fieldval. READ TABLE record_tab INTO ls_record WITH KEY fieldname WERKS fieldval lv_matnr. * 实际应按主键组合查表判断这里简化演示 SELECT SINGLE active FROM zmat_aux INTO DATA(lv_active) WHERE matnr lv_matnr. IF sy-subrc 0 OR lv_active X. * 标记为待删除 DELETE record_tab WHERE fieldval lv_matnr AND fieldname IN (MATNR,WERKS,REMARK,AUX_DISP). ENDIF. ENDLOOP. * 简化版工厂权限过滤 LOOP AT record_tab INTO ls_record WHERE fieldname WERKS. IF line_exists( lt_auth_wrk[ table_line ls_record-fieldval ] ). CONTINUE. ELSE. DELETE record_tab WHERE fieldname WERKS AND fieldval ls_record-fieldval. ENDIF. ENDLOOP. callcontrol-step DISP. WHEN DISP. * 补充拼接展示列 AUX_DISP CLEAR lv_idx. LOOP AT record_tab INTO ls_record. lv_idx lv_idx 1. IF ls_record-fieldname MATNR. * 取配套的 WERKS 和 REMARK 拼串 READ TABLE record_tab INTO DATA(ls_w) INDEX lv_idx 1. READ TABLE record_tab INTO DATA(ls_r) INDEX lv_idx 2. lv_disp |{ ls_record-fieldval }/{ ls_w-fieldval }/{ ls_r-fieldval }|. ENDIF. ENDLOOP. * 实际项目建议用独立内表按主键组拼避免索引错位 callcontrol-step RETURN. WHEN RETURN. * 默认回填已够用直接推进 callcontrol-step EXIT. WHEN EXIT. * 清理 ENDCASE. ENDFUNCTION.上面这份代码是骨架有两个地方我刻意留了简化一是CHECK里的主键归组用了不严谨的WHERE匹配二是DISP里用索引偏移取配套行实际项目中一旦RECORD_TAB的顺序变化就会错位。正确做法是先按业务主键把RECORD_TAB重组成“一行一条记录”的临时内表处理完再铺回纵向结构。这里写出来是想让你知道为什么不能这么写而不是让你照抄。更稳的写法是在CHECK阶段先建一个结构清晰的临时内表lt_data按MATNR唯一键去重把WERKS、REMARK、ACTIVE都读进来过滤完再把lt_data重新铺成RECORD_TAB的纵向格式。这样所有过滤、拼接、回填判断都在扁平结构上做逻辑清晰且不易错位。6. 常见问题与排查技巧实录这部分是纯粹的踩坑记录。我在做这类对象时遇到的问题基本都集中在这几个点上。6.1 按 F4 没反应或直接短转储出现短转储九成是函数模块接口不对。优先检查三件事参数名是否和标准完全一致SHLP_TAB、RECORD_TAB、CALLCONTROL、SHLP、INTERFACE类型是否分别是SHLP_DESCR、DDSHRETVAL、DDSHCC、SHLP_DESCR、DDSHF4CTRL是否漏了某个参数。这三点排完还不行就去看 SE11 里绑定的函数模块名有没有拼错、有没有激活。如果没短转储但 F4 完全没反应通常是因为CALLCONTROL-STEP没有被正确推进。比如在CHECK阶段做完过滤忘了赋DISP流程就停在原地界面上什么都不显示。我的习惯是在每个分支末尾加一行日志输出用MESSAGE到状态栏或者写WRITE到调试确认流程走到哪一步断了。6.2 过滤逻辑生效了但记录错乱这是最隐蔽的问题根源在于RECORD_TAB的纵向结构。如果你直接DELETE RECORD_TAB WHERE FIELDVAL xxx很可能只删掉了匹配的那一行而同一条记录的其他字段行还留着导致显示列表里出现“有值但缺字段”的怪行。注意任何针对RECORD_TAB的删除操作都必须按“整条记录”为单位把该记录涉及的所有FIELDNAME行一起删掉不能只按值删。正确的做法就是我前面提的按主键归组判断这条记录该不该保留然后成批删除。判断主键的维度取决于你的搜索帮助有多少个“键级” Parameter通常取MATNR或者MATNR WERKS组合。6.3 显示列总是空白显示列空白有两个常见原因。一是DISP阶段没往RECORD_TAB里补对应FIELDNAME的行——显示系统是按FIELDNAME找值渲染的你没补自然空白。二是补了行但字段名拼写和 Parameter 不一致比如 Parameter 叫AUX_DISP你补的行FIELDNAME写成了AUXDISP系统找不到就留空。排查方法很土但有效在DISP阶段末尾加一个BREAK-POINT进入调试后看RECORD_TAB里到底有没有那个FIELDNAME的行、值是不是空的。一步到位比瞎猜快得多。6.4 选中后只回填了一个字段多字段回填失效先查 Parameter 的导出勾选。WERKS如果只勾了导入没勾导出它就只能作为查询条件不会在选中后传给调用方。这是配置层面的问题代码写得再对也没用。如果是导出也勾了还不回填就去看RETURN阶段的代码有没有把RECORD_TAB改坏。有些实现会在RETURN里重新SELECT一遍数据覆盖RECORD_TAB如果新数据里缺了某个字段回填自然就少了。建议RETURN阶段尽量少动数据只做必要的值转换。现象最可能原因排查动作按 F4 短转储接口参数名或类型错误逐项比对标准接口F4 无反应CALLCONTROL-STEP未推进在每分支末尾打日志记录错乱/缺字段按值删除破坏了纵向结构改为按主键成组删除显示列空白未补行或字段名拼错调试看RECORD_TAB内容只回填一个字段Parameter 未勾导出回 SE11 检查导出标记6.5 性能问题数据量大时 F4 卡顿如果底层表数据量大默认取数会把整表捞出来再在CHECK里过滤F4 会明显卡顿。解决办法有两个方向一是缩小 Selection Method 的数据范围比如用视图替代表或者给 Selection Method 加默认筛选条件二是在SELECT阶段接管取数用SELECT ... UP TO N ROWS限制条数只捞必要字段直接填RECORD_TAB并跳到CHECK。第二个方向更彻底但要注意自己接管取数后系统的默认筛选界面就失效了用户输入的查询条件需要你自己在 Exit 里解析并拼进 SQL 的WHERE。所以如果你的需求是“用户要先按条件查再选”接管取数反而增加复杂度这时候更推荐第一个方向。7. 几个让代码更稳的实践心得按前面流程走完基本能跑通一个带 Exit 的搜索帮助。但“能跑”和“好维护”之间还有段距离这几个习惯是我做了多个类似对象后固定下来的。第一所有参数和字段名用常量定义。FIELDNAME的字符串值比如MATNR会在代码里出现好几次散着写极容易拼错且难改。我通常在函数开头用CONSTANTS统一声明后面全引用常量改起来一处就行。第二CHECK阶段的过滤逻辑和DISP阶段的展示逻辑尽量解耦。前者只负责判定记录存废后者只负责拼展示值两者都基于同一个中间内表操作。这样一旦需求变化比如加个过滤条件或改展示格式改的地方是单一的不会牵一发动全身。第三留一个开关式的调试入口。比如在函数开头读一个自定义参数或环境变量为真时就把关键阶段的RECORD_TAB内容写到一个内表里最后统一输出。线上排查问题时有这个开关能省下大量时间——尤其是那种“用户说选不出来但我本地复现不了”的场景。第四别在 Exit 里做重活。搜索帮助是高频触发的交互用户在 F4 界面上敲一个字符就可能触发一次刷新里面如果放了耗时的数据库操作或者复杂的循环计算体验会非常差。能在SELECT阶段一次性搞定的绝不放到DISP或RETURN里反复做。第五权限检查放在最前面。如果你的 Exit 要按权限裁剪数据尽量在CHECK阶段一开始就把权限范围确定好用一个内表缓存住后面所有过滤都基于这个内表判断。别在每个循环里重复调AUTHORITY-CHECK那玩意儿开销不小几十条数据看不出问题上万条就是明显的性能瓶颈。最后再补一个容易被忽略的点测试时一定要测“无结果”场景。把过滤条件设成查不到任何数据看 F4 界面是正常显示“无匹配记录”还是报错。有些 Exit 实现会在RECORD_TAB为空时出问题比如在DISP里按索引访问空内表这种边界情况不测就上线早晚会被用户撞上。