
1. 从一次紧急的报表需求说起那天下午业务部门的同事急匆匆地跑过来说他们需要一份新的销售分析报表这份报表需要整合三个不同模块的数据销售订单抬头信息、行项目明细以及后续的出货状态。他们希望在一个界面上选择公司代码和日期范围然后一键生成一份整合的Excel文件。听起来是个很常规的需求对吧我一开始也是这么想的脑子里立刻蹦出了几个方案写一个庞大的程序用JOIN把三张表关联起来或者用ALV的REUSE_ALV_GRID_DISPLAY做个复杂的输出。但当我仔细看了他们的要求后发现事情没那么简单。这三个数据源分别对应着SAP里三个不同的标准报表VA05销售订单清单、VL06O外向交货监控和VF05开票凭证清单。业务用户平时就是分别跑这三个报表然后把数据手工粘贴到Excel里做对比。他们需要的本质上是一个“报表的报表”——一个能自动串联起这三个独立查询流程的控制器。这时SUBMIT这个关键字就自然而然地浮现在了我的脑海里。在ABAP的世界里SUBMIT不是一个陌生的命令它就像是一个系统级的“遥控器”允许你的程序去启动另一个完整的报表程序。这不仅仅是调用一个函数那么简单它意味着你的程序可以接管另一个程序的整个生命周期从选择屏幕的参数输入到后台作业的提交再到最终列表结果的获取与处理。对于这种需要串联多个标准事务码或报表的场景SUBMIT几乎是唯一优雅的解决方案。它让你能站在巨人的肩膀上复用SAP系统内经过千锤百炼的标准逻辑和权限检查而不是自己从头去造轮子甚至去破解那些复杂的数据表关联关系。2. SUBMIT的本质程序边界的穿越与掌控在深入代码之前我们有必要先厘清SUBMIT在ABAP体系中的定位。它不是一个简单的函数调用CALL FUNCTION也不是一个模块调用CALL TRANSACTION。你可以把它理解为一种特殊的“程序执行请求”。2.1 与普通调用的核心区别想象一下你的ABAP程序就像一个独立的房间。CALL FUNCTION相当于你打开门请一位专家函数模块进来帮你完成一项特定任务任务结束他就离开你的房间程序上下文基本不变。而SUBMIT则是你构建了一个通往另一个完整房间的传送门你不仅要把自己“传送”过去还要带着一堆行李选择屏幕参数在那个房间里生活执行一段时间最后可能还要把那个房间里的特产输出数据带回来。这个比喻揭示了SUBMIT的几个关键特性独立的程序上下文被SUBMIT调用的程序我们称之为目标程序在一个全新的内部会话中运行。它拥有自己独立的内存空间INTERNAL TABLE 变量与你主程序的内存空间是隔离的。这意味着你不能直接在主程序里访问目标程序里声明的内表IT_VBAK反之亦然。完整的选择屏幕流程目标程序的选择屏幕会被触发。你可以通过SUBMIT语句的参数向这个选择屏幕传递值模拟用户输入。这是复用标准报表逻辑的核心。列表输出的定向捕获目标程序运行后通常会产生列表输出LIST。SUBMIT允许你通过EXPORTING LIST TO MEMORY等选项将这个列表“捕获”到内存中然后主程序可以再将其读出、分析或转换。2.2 核心语法结构与参数解析一个最基本的SUBMIT语句骨架如下SUBMIT program_name [WITH sel_screen_field EQ value] [AND RETURN] [VIA SELECTION-SCREEN] [EXPORTING LIST TO MEMORY] [TO SAP-SPOOL ...] [USER user VIA JOB jobname NUMBER jobcount].我们来拆解其中最常用、也最容易出错的几个部分program_name这是目标报表的程序名。可以是自定义报表Z或Y开头也可以是任何标准SAP报表如RFKKEP00。关键点这里填的是报表的程序名而不是事务码。例如事务码VA05对应的报表程序可能是SAPMV45A但直接SUBMIT ‘VA05’是行不通的。你需要通过SE93事务码查询或者直接查看报表属性来获取正确的程序名。WITH ... EQ ...这是向目标程序的选择屏幕传递参数的灵魂。sel_screen_field必须严格匹配选择屏幕上字段的技术名称Technical Name而不是你在屏幕上看到的标签Label。这个技术名称通常可以在选择屏幕的“字段列表”或通过F1帮助中的“技术信息”找到。例如公司代码字段可能叫S_BUKRS-LOW日期范围可能叫S_ERDAT-LOW和S_ERDAT-HIGH。注意对于选择选项SELECT-OPTIONS和参数PARAMETERS其传递方式有细微差别。选择选项通常有-LOW和-HIGH后缀而参数直接使用其名称。AND RETURN这个选项至关重要。它告诉系统当目标程序执行完毕后控制权应该返回给主调程序即你的程序。如果你省略了AND RETURN那么目标程序执行完后系统会停留在目标程序的上下文或列表界面你的主程序逻辑就中断了。在绝大多数需要继续处理结果的场景下都必须加上AND RETURN。VIA SELECTION-SCREEN如果加上这个选项系统会真的弹出目标程序的选择屏幕等待用户交互。这通常用于调试或需要用户临时调整参数的场景。在完全自动化的后台调用中我们一般不使用它而是通过WITH参数直接传值。EXPORTING LIST TO MEMORY这是将目标程序的列表输出捕获到内存的关键。捕获后你可以使用LIST_FROM_MEMORY函数族来读取这些数据。我们会在后面详细展开。3. 实战串联销售报表并导出Excel让我们回到开头的业务场景。假设我们已经查明报表VA05的程序名是SAPMV45A(这是一个简化示例实际更复杂)。报表VL06O的程序名是RM06EL00。我们需要传递公司代码(S_BUKRS-LOW)和创建日期范围(S_ERDAT-LOW,S_ERDAT-HIGH)。我们的目标是主程序提供一个选择屏幕用户输入条件后程序依次调用这三个报表捕获它们的列表数据整合后通过ALV输出并提供一个“下载Excel”按钮。3.1 主程序框架与选择屏幕首先我们创建主程序Z_SUBMIT_SALES_REPORT。REPORT z_submit_sales_report. * 选择屏幕定义 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001. PARAMETERS: p_bukrs TYPE bukrs OBLIGATORY. SELECT-OPTIONS: s_erdat FOR vbak-erdat OBLIGATORY. SELECTION-SCREEN END OF BLOCK b1. * 用于存储从各个报表捕获的数据 DATA: gt_va05_data TYPE TABLE OF ty_va05_output, 需要自定义类型 gt_vl06o_data TYPE TABLE OF ty_vl06o_output, gt_vf05_data TYPE TABLE OF ty_vf05_output. DATA: gt_final_data TYPE TABLE OF ty_final_structure. DATA: go_alv TYPE REF TO cl_salv_table.3.2 分步调用与内存列表捕获这里以调用RM06EL00(VL06O)为例展示最完整的流程。对于标准报表其列表输出结构是固定的我们需要先知道这个结构。步骤1确定列表结构一个笨但有效的方法是手动运行一次VL06O输出列表然后使用系统功能SYSTEM - LIST - SAVE - LOCAL FILE将其保存或者直接使用LIST_FROM_MEMORY实验性读取。更专业的方法是查阅SAP相关文档或通过SLIN等工具分析该报表的列表输出内表。假设我们通过分析确定其列表对应的一个结构是LIS_VL06O这是一个示例实际需要探查。步骤2调用并捕获列表FORM call_vl06o. DATA: lt_list_data TYPE TABLE OF abaplist WITH HEADER LINE. SUBMIT rm06el00 WITH s_bukrs-low EQ p_bukrs WITH s_erdat-low EQ s_erdat-low WITH s_erdat-high EQ s_erdat-high AND RETURN EXPORTING LIST TO MEMORY AND RETURN. IF sy-subrc 0. 成功提交并返回 CALL FUNCTION LIST_FROM_MEMORY TABLES listobject lt_list_data[] EXCEPTIONS not_found 1 OTHERS 2. IF sy-subrc 0. 成功从内存获取列表原始数据 现在需要解析 lt_list_data 中的行将其转换为我们需要的内表 gt_vl06o_data PERFORM parse_list_to_internal_table USING lt_list_data[] CHANGING gt_vl06o_data[]. ELSE. MESSAGE 无法从内存读取VL06O列表 TYPE E. ENDIF. ELSE. MESSAGE 提交VL06O报表失败 TYPE E. ENDIF. ENDFORM.步骤3解析列表数据这是整个过程中最复杂、最容易踩坑的一步。LIST_FROM_MEMORY返回的是列表的原始行数据ABAPLIST它包含了格式、颜色等信息我们需要从中提取出纯文本数据行并按照已知的列位置进行解析。FORM parse_list_to_internal_table USING it_list TYPE tab_abaplist CHANGING ct_data TYPE table. DATA: lw_list LIKE LINE OF it_list. DATA: lw_data TYPE ty_vl06o_output. 自定义结构 DATA: lv_line TYPE string. DATA: lv_delivery TYPE vbeln_vl, lv_material TYPE matnr, lv_qty TYPE menge_d. LOOP AT it_list INTO lw_list. 跳过表头、分页符等非数据行通常数据行从特定行开始 例如跳过前5行作为表头 IF sy-tabix 5. lv_line lw_list-line. **关键根据列表的固定列宽进行拆分** 假设我们知道列表布局交货单号在1-10列物料号在11-20列数量在21-30列 这是一个简化的例子实际需要非常精确地定位 lv_delivery lv_line0(10). lv_material lv_line10(10). lv_qty lv_line20(10). 清洗数据去除前导零、转换数据类型等 CALL FUNCTION CONVERSION_EXIT_ALPHA_INPUT EXPORTING input lv_delivery IMPORTING output lw_data-vbeln. CALL FUNCTION CONVERSION_EXIT_MATN1_INPUT EXPORTING input lv_material IMPORTING output lw_data-matnr. lw_data-menge lv_qty. APPEND lw_data TO ct_data. CLEAR lw_data. ENDIF. ENDLOOP. ENDFORM.重要提示这种基于列位置的解析极其脆弱。如果目标报表的布局因任何原因用户个性化、SAP版本升级发生变化解析就会失败。因此这通常被视为最后的手段。更优的方案是如果目标程序提供了将数据输出到内表或内存的接口例如通过EXPORT ... TO MEMORY ID应优先使用。3.3 更优方案探寻标准程序的“后门”对于重要的标准报表SAP有时会提供一些未公开的“后门”参数或内存导出点允许你直接获取内表数据而无需解析列表。这需要一些“侦探工作”搜索EXPORT ... TO MEMORY ID在目标报表的源代码SE38中搜索EXPORT或TO MEMORY。你可能会发现类似EXPORT itab TO MEMORY ID Z_MY_DATA的语句。如果找到在你的主程序中就可以用IMPORT itab FROM MEMORY ID Z_MY_DATA直接获取数据。查找隐藏的选择屏幕参数有些标准程序有隐藏参数用于控制输出目的地。例如参数P_MEMORY或P_CALLBACK。这需要查阅SAP注释或相关社区经验。使用SUBMIT ... WITH ...传递特殊参数例如某些报表支持P_DISPLAY ‘X’来抑制屏幕输出或者支持P_CALLBACK ‘X’并配合一个回调子程序来接收数据。以我们搜索到的热词“abap 获取标准程序的内表数据”为例这正是所有ABAP开发者面对SUBMIT时最渴望解决的痛点。遗憾的是并没有通用方法。这需要针对每个目标程序进行个案分析有时甚至需要创建SAP增强或修改标准程序不推荐除非万不得已。3.4 整合数据与ALV展示在分别获取了三个报表的数据后我们需要根据关键字段如销售订单号、交货单号进行关联和整合填充到最终的内表gt_final_data中。这个过程就是常规的ABAP内表操作LOOP,READ TABLE,MODIFY等。之后使用CL_SALV_TABLE来展示最终结果FORM display_alv. TRY. cl_salv_tablefactory( IMPORTING r_salv_table go_alv CHANGING t_table gt_final_data[] ). 启用工具栏功能特别是导出功能 go_alv-get_functions( )-set_all( abap_true ). 创建并添加一个自定义的“下载Excel”按钮 DATA(lo_functions) go_alv-get_functions( ). DATA(lo_toolbar) lo_functions-get_toolbar( ). ... (添加自定义按钮的代码) go_alv-display( ). CATCH cx_salv_msg INTO DATA(lx_msg). MESSAGE lx_msg-get_text( ) TYPE E. ENDTRY. ENDFORM.在自定义按钮的事件处理程序中你可以调用CL_SALV_EXPORT相关类或者更直接地使用GUI_DOWNLOAD函数将gt_final_data下载为Excel文件。4. 避坑指南SUBMIT调用中的“雷区”在实际项目中SUBMIT调用远比示例复杂。下面是我总结的几个关键“雷区”及应对策略。4.1 权限与授权检查的穿透问题当你SUBMIT一个标准报表时目标程序会执行其自身的权限检查AUTHORITY-CHECK。这意味着即使用户有权限执行你的主程序但如果他没有权限执行目标报表SUBMIT就会失败SY-SUBRC 0或者列表为空。应对策略提前模拟检查在SUBMIT之前尝试用相同的参数手动执行一次目标事务码确认当前用户是否有权限看到预期数据。错误处理必须完善地处理SUBMIT的返回码SY-SUBRC并给出明确的错误提示例如“您无权执行报表XXX请联系系统管理员”。使用技术用户对于完全自动化的后台作业可以考虑用一个拥有足够权限的技术用户USER ... VIA JOB来提交作业。但这涉及后台作业配置和密码安全存储问题需谨慎。4.2 选择屏幕的动态性与复杂性很多标准报表的选择屏幕非常复杂包含标签页、动态屏幕元素、互锁字段等。通过WITH参数传递值时可能会遇到字段名不匹配如前所述必须使用精确的技术名称。字段依赖字段B的值依赖于字段A。如果你只传了B没传A或者传的A值无效B可能不会被正确处理。屏幕变式用户通常使用变式Variant来存储常用筛选值。SUBMIT语句支持USING SELECTION-SET variant来直接使用一个变式这比逐个传递参数更可靠。SUBMIT rm06el00 USING SELECTION-SET ZMY_VARIANT 预设的变式名 AND RETURN EXPORTING LIST TO MEMORY.4.3 后台执行与SPOOL管理如果报表执行时间很长或者你希望完全脱离对话进程运行就需要用到后台作业。SUBMIT rm06el00 WITH ... USER sy-uname VIA JOB Z_JOB NUMBER lv_jobcount TO SAP-SPOOL SPOOL PARAMETERS lv_print_params WITHOUT SPOOL DYNPRO AND RETURN.这里涉及作业的创建JOB_OPEN,JOB_SUBMIT,JOB_CLOSE、监控和SPOOL输出假脱机的管理。你需要处理作业号JOBCOUNT的获取、作业状态的查询以及最终如何从SPOOL中获取输出列表使用RSPO_RETURN_ABAP_SPOOLJOB等函数。这是一个更高级的话题一旦涉足就必须考虑作业调度、异常处理和日志记录。4.4 性能陷阱与资源争用连续SUBMIT多个大型报表尤其是在前台对话进程中可能导致长时间等待用户界面会卡住直到所有SUBMIT执行完毕。内存消耗每个SUBMIT都会启动新的内部模式占用内存。如果捕获大型列表到内存消耗更大。数据库负载多个报表可能重复扫描相同的大表造成数据库压力。优化建议异步与后台化将整个串联逻辑放到后台作业中执行完成后通知用户如发站内信或邮件附带结果。数据缓存如果某些基础数据变化不频繁考虑先SUBMIT一次将结果暂存到Z表或集群数据库中后续调用直接读取缓存。精简数据仔细检查传递给每个报表的选择条件确保只获取最小必要数据集。5. 进阶思考SUBMIT的替代方案与架构权衡虽然SUBMIT功能强大但它本质上是一种“黑盒”集成存在耦合度高、稳定性依赖目标程序、解析输出困难等缺点。在现代ABAP开发中我们需要权衡利弊。何时使用SUBMIT需要100%复用标准报表的复杂业务逻辑和选择屏幕。没有其他可用的标准BAPI或函数接口来获取相同数据。需求是“自动化执行现有报表流程”而非创建新的数据视图。何时寻找替代方案直接表访问如果逻辑简单直接读取底层透明表如VBAK,VBAP,LIPS并自己实现关联性能更好控制力更强。这就是热词“abap select”所代表的直接路径。调用标准函数/BAPI许多标准报表背后都有对应的函数模块。例如某些清单报表可能调用了REUSE_ALV_GRID_DISPLAY其数据准备函数可能是可重用的。通过ST05SQL跟踪和运行时分析SE30可以尝试反推报表的数据来源。增强与出口在标准报表的数据准备阶段END-OF-SELECTION之前通常有用户出口USER-EXIT或BADI。你可以通过这些增强点将数据直接导出到你的全局内存或数据库中供主程序读取。这比解析列表更优雅。OData/CDS View对于S/4 HANA等新系统考虑通过CDS View暴露数据再通过Fiori或OData服务消费。这是更面向未来、更解耦的架构。一个折中的架构模式你可以创建一个“报表控制器”程序它使用SUBMIT来执行标准报表但目的不是解析其列表而是利用其完整的业务逻辑将结果数据写入一个自定义的Z结果表。然后另一个纯粹的展示程序从这个Z表中读取数据用ALV或任何其他方式呈现。这样就将不稳定的“数据获取”层和稳定的“数据展示”层分离开了。最后关于热词中提到的“abap中可以循环调用submit rfob5200吗”答案是肯定的。你完全可以在一个循环中针对不同的凭证号码范围多次SUBMIT RFOB5200一个清账凭证报表。但必须非常小心地管理每次调用之间的内存清理使用FREE MEMORY ID释放之前捕获的列表并妥善处理循环中的异常避免一个调用失败导致整个循环中断。这再次印证了SUBMIT是一把强大的双刃剑它赋予你串联系统流程的能力同时也要求你具备精细的资源管理和错误处理意识。