1. 项目概述:SUBMIT调用在ABAP生态中的核心价值
在ABAP开发领域,SUBMIT语句是一个既基础又强大的工具,它允许一个程序去调用并执行另一个独立的ABAP报表程序。这听起来简单,但在实际的大型企业SAP项目实施与运维中,SUBMIT的合理运用直接关系到代码的复用性、流程的自动化程度以及系统性能的边界。很多新手开发者最初接触SUBMIT,可能只是为了在一个报表里触发另一个报表的屏幕输出,但随着经验的积累,你会发现它的真正威力在于“后台执行”、“数据传递”和“流程编排”。
想象一下这样的场景:你需要开发一个综合性的物料分析报表,这个报表需要先后执行“物料主数据查询”、“库存情况统计”以及“采购历史分析”。这三个功能在SAP标准系统中可能分别由三个不同的标准报表(如MM60,MB52,ME2N或其变体)提供。一种笨办法是手动依次执行这三个报表,然后手工整合数据。而高效的做法,就是通过一个自开发的Z程序,使用SUBMIT语句,按顺序、甚至并行地调用这些标准报表,并自动捕获它们输出的数据(内表),最后在你的程序中进行统一处理和展示。这就是SUBMIT程序相互调用的核心价值——将离散的功能模块串联成自动化的工作流。
从网络热词如“abap 从内表select”、“abap 获取标准程序的内表数据”可以看出,开发者们不仅满足于调用,更迫切希望获取被调用程序执行后的结果数据,进行深度加工。而“abap中可以循环调用submit rfob5200吗”这类问题,则直接指向了复杂业务流程的自动化需求。因此,深入掌握SUBMIT的机制、参数传递、后台执行与数据回传技术,是ABAP开发者从实现单一功能迈向设计复杂解决方案的关键一步。
2. SUBMIT语句的语法核心与调用模式深度解析
SUBMIT语句的语法远比它表面上看起来复杂,其灵活性就隐藏在众多的附加选项之中。一个完整的SUBMIT调用,其核心结构如下:
SUBMIT <program_name> [WITH <sel_screen_field> EQ <value> ...] “传递选择屏幕参数 [VIA SELECTION-SCREEN] “是否显示选择屏幕 [AND RETURN] “调用后是否返回 [EXPORTING LIST TO MEMORY] “将输出列表保存到内存 [TO SAP-SPOOL] ... “输出到假脱机 [USER <user> VIA JOB <jobname> ...] “后台作业执行理解这些选项的组合,是精准控制被调用程序行为的前提。我们可以将调用模式归纳为以下几类:
2.1 前台同步调用:最基础的交互模式
这是最简单的形式,直接触发被调用程序,并等待其执行完毕。
SUBMIT ZMY_REPORT AND RETURN.- 行为:程序
ZMY_REPORT会立即开始执行。如果它有选择屏幕,则会弹出屏幕让用户输入。执行完成后,控制权返回到调用程序。 - 应用场景:在自定义事务码或屏幕中,提供一个快捷入口来启动另一个报表。但这种方式会中断当前程序的流程,用户体验是割裂的。
2.2 后台异步调用:实现批处理与自动化
这是SUBMIT在自动化处理中最重要的应用。通过VIA SELECTION-SCREEN和USER ... VIA JOB等参数,可以实现无需人工干预的后台执行。
SUBMIT ZMY_REPORT WITH p_werks EQ ‘1000’ WITH p_matnr IN so_matnr VIA SELECTION-SCREEN “后台模式的关键,不显示屏幕 AND RETURN TO SAP-SPOOL SPOOL PARAMETERS print_parameters “输出到假脱机 WITHOUT SPOOL DYNPRO “不显示假脱机设置屏幕 USER SY-UNAME VIA JOB ‘Z_MY_BATCH’ NUMBER lv_jobcount.- 关键参数解析:
VIA SELECTION-SCREEN:这个参数在后台作业中至关重要。它告诉系统“模拟一个选择屏幕会话”,并用WITH子句传递的参数来填充屏幕字段。如果没有这个参数,对于需要选择输入的程序,后台作业会因缺少输入而失败。USER ... VIA JOB:指定作业以哪个用户身份运行,并赋予作业名称和编号。你可以通过JOB_OPEN,JOB_SUBMIT,JOB_CLOSE函数来更精细地控制作业提交。TO SAP-SPOOL:将程序的输出(列表)直接发送到假脱机系统,而不是屏幕。这对于生成批量打印件或PDF文件非常有用。
- 应用场景:夜间批量报表生成、定期数据清理、大批量数据接口处理等。你可以通过ABAP计划器(
SM36)或程序内动态创建后台作业,让这些任务在系统空闲时自动完成。
2.3 内存列表调用:捕获输出数据的桥梁
这是实现数据回传的经典方法。通过EXPORTING LIST TO MEMORY,被调用程序的整个输出列表(即你在SE38执行报表时看到的那个ALV或传统列表)会被保存到ABAP内存的一个特定区域。
SUBMIT ZMY_REPORT WITH ... AND RETURN EXPORTING LIST TO MEMORY.执行后,被调用程序的输出列表就静静地躺在内存里了。但这只是第一步,你得到的是一个原始的列表数据流,要从中提取结构化的内表数据,还需要使用LIST_FROM_MEMORY、LIST_TO_ASCI等函数进行复杂的解析。这个过程比较繁琐,容易出错,通常用于获取标准报表的最终输出文本,而不是中间的结构化数据。对于获取内表数据,有更优的方案。
实操心得:
EXPORTING LIST TO MEMORY更适合抓取标准报表的最终文本结果用于归档或二次展示。如果你想获取程序内部处理好的内表,应该优先考虑下文介绍的“内存ID参数传递”或“RFC封装”方案。
3. 跨越程序边界:数据传递的三大实战策略
仅仅调用程序是不够的,更重要的是如何在调用程序和被调用程序之间传递输入参数和获取输出结果。这是SUBMIT调用的精髓所在。
3.1 选择屏幕参数传递:精准控制输入
使用WITH子句可以直接为被调用程序的选择屏幕字段赋值。这是最直接、最常用的参数传递方式。
DATA: lv_plant TYPE werks_d VALUE ‘1000’, lt_matnr RANGE OF matnr. lt_matnr = VALUE #( ( sign = ‘I’ option = ‘EQ’ low = ‘MAT001’ ) ). SUBMIT ZMM_MATERIAL_REPORT WITH s_werks EQ lv_plant “为单选参数赋值 WITH s_matnr IN lt_matnr “为范围参数赋值 AND RETURN.- 注意事项:
- 字段名必须完全匹配:
WITH后面的字段名必须是目标程序选择屏幕上定义的字段名(如s_werks、p_date)。大小写不敏感,但拼写必须一致。一个快速查看标准报表选择屏幕字段名的方法是,在SE38中打开报表,按F1键查看技术信息。 - 参数类型匹配:传递的值必须与选择屏幕字段的数据类型兼容。
- 处理复杂选择屏幕:对于动态生成的、或带有子屏幕的选择屏幕,直接
WITH赋值可能无效。此时可能需要研究程序的逻辑,或考虑其他交互方式(如CALL TRANSACTION)。
- 字段名必须完全匹配:
3.2 内存ID(EXPORT/IMPORT)传递:高效共享结构化数据
这是程序间传递复杂数据(特别是内表)的首选方法。它利用ABAP的共享内存(EXPORT/IMPORT)机制。
在调用程序中:
DATA: lt_input_data TYPE TABLE OF zmaterial, lt_output_data TYPE TABLE OF zmaterial_output. “ 1. 准备要传递的数据 SELECT * FROM mara INTO CORRESPONDING FIELDS OF TABLE lt_input_data UP TO 100 ROWS. “ 2. 将数据放入特定ID的内存区域 EXPORT lt_input_data TO MEMORY ID ‘ZMAT_DATA_INPUT’. “ 3. 调用子程序,并告知其去哪个内存区域读取输入数据 SUBMIT ZPROCESS_MATERIAL WITH p_memid EQ ‘ZMAT_DATA_INPUT’ “通过参数传递内存ID AND RETURN. “ 4. 调用完成后,从另一个内存区域读取子程序处理的结果 IMPORT lt_output_data FROM MEMORY ID ‘ZMAT_DATA_OUTPUT’. IF sy-subrc = 0. “ 成功获取到输出数据 ENDIF.在被调用程序(ZPROCESS_MATERIAL)中:
PARAMETERS: p_memid TYPE char20. “接收内存ID参数 START-OF-SELECTION. DATA: lt_input TYPE TABLE OF zmaterial, lt_output TYPE TABLE OF zmaterial_output. “ 根据传入的ID,从内存中读取输入数据 IMPORT lt_input FROM MEMORY ID p_memid. IF sy-subrc = 0. “ 处理lt_input数据... “ 将处理结果放入输出内存区域 EXPORT lt_output TO MEMORY ID ‘ZMAT_DATA_OUTPUT’. ELSE. MESSAGE ‘未能读取输入数据’ TYPE ‘E’. ENDIF.- 优势:这种方式极其灵活,可以传递任何能被
EXPORT/IMPORT支持的数据对象(内表、结构、简单变量等)。数据在内存中交换,速度快,且保持了数据的结构。 - 关键点:双方程序必须就“内存ID”这个钥匙达成一致。这个ID通常是一个硬编码的字符串,或者像上面例子一样,通过选择屏幕参数动态传递。
3.3 数据库或应用层缓冲表传递:处理海量数据与异步作业
当需要传递的数据量非常大,或者调用是异步的(如后台作业),共享内存可能因为会话结束而丢失数据。此时,使用数据库表或应用层缓冲表(如INDX表)是更可靠的选择。
- 定义中转表:创建一个Z表,包含关键字段(如
GUID、PROCESS_ID、CREATED_BY、CREATED_AT)和数据字段。 - 调用程序写入:在调用
SUBMIT前,将数据写入该表,并生成一个唯一的PROCESS_ID。 - 传递PROCESS_ID:通过
WITH参数或内存ID,将PROCESS_ID传递给被调用程序。 - 被调用程序读取:被调用程序根据
PROCESS_ID从表中读取需要处理的数据。 - 结果回写:处理完成后,将结果数据写回该表的另一字段或另一张结果表,状态更新为‘已处理’。
- 调用程序轮询或读取:调用程序可以等待一段时间后,通过
PROCESS_ID去查询结果表获取数据。
- 应用场景:长时间运行的后台作业、需要断点续传的数据处理流程、多个程序协作处理同一批主数据。
避坑技巧:使用数据库表传递时,务必注意数据清理机制。可以写一个定期作业,删除超过一定时间的、状态为‘已完成’的中间数据,避免表无限制膨胀。
4. 获取被调用程序的内表数据:破解标准报表的黑盒
“如何从SUBMIT调用的标准报表里获取它生成的内表数据?”这是ABAP开发中的高频痛点。标准报表的内部内表通常不通过接口暴露。这里介绍几种渐进式的破解思路。
4.1 方案一:内存窥探(谨慎使用)
如果标准报表在运行结束后,其内表仍然留在ABAP会话内存中(未释放),且你知道该内表的具体名称和结构,可以尝试直接访问。
SUBMIT RM07MLBD “物料凭证列表 WITH ... AND RETURN. FIELD-SYMBOLS: <lt_data> TYPE STANDARD TABLE. ASSIGN (‘(SAPLM07D)MC_TAB[]’) TO <lt_data>. “尝试分配标准报表的内表 IF sy-subrc = 0. “ 成功分配到内表,可以读取<lt_data> ELSE. “ 分配失败,内表可能已释放或名称不对 ENDIF.- 风险与限制:这种方法极不稳定。内表名称可能随SAP版本变化;程序执行逻辑可能导致内表提前释放;直接访问外部程序内存违背封装原则,容易引发运行时错误
ASSIGN_CASTING_ILLEGAL_CAST。仅作为最后手段,且必须有充分的错误处理。
4.2 方案二:复制并修改标准程序(常见做法)
这是最可靠、最常用的方法。以标准报表RFOB5200(总账科目余额)为例。
- 复制标准程序:在SE38中,找到
RFOB5200,选择“复制”到你的Z或Y命名空间,例如ZRFOB5200_COPY。 - 分析并暴露数据:在复制的程序里,找到生成最终输出数据的内表(例如可能叫
GT_DATA)。在程序的适当位置(如END-OF-SELECTION之后),添加代码将这部分数据EXPORT到共享内存或写入自定义表。“ 在复制的标准程序末尾添加 EXPORT gt_data TO MEMORY ID ‘GL_BALANCE_DATA’. - 调用修改后的副本:在你的主程序中,
SUBMIT这个修改过的副本程序ZRFOB5200_COPY。SUBMIT ZRFOB5200_COPY WITH ... AND RETURN. IMPORT lt_gl_data FROM MEMORY ID ‘GL_BALANCE_DATA’.
- 优势:数据获取稳定、可控。你可以完全控制数据输出的格式和时机。
- 缺点:违反了“不修改SAP标准对象”的原则。当SAP升级标准程序
RFOB5200时,你的副本不会自动更新,可能导致功能缺失或错误。需要建立手工比对和更新的流程。
4.3 方案三:封装为标准函数或RFC模块(推荐架构)
这是最优雅、最符合SAP设计理念的方案。虽然前期工作量稍大,但长期来看可维护性最好。
- 创建函数模块:新建一个函数模块(SE37),例如
Z_GET_GL_ACCOUNT_BALANCE。 - 移植核心逻辑:将标准报表
RFOB5200中从数据读取、处理到填充内表GT_DATA的核心逻辑(通常集中在某个FORM或方法里)复制到你的函数模块中。 - 定义接口:将报表的选择屏幕参数定义为函数的输入参数(
IMPORTING)。将最终的内表GT_DATA定义为函数的输出参数(EXPORTING或TABLES)。 - 调用函数:在你的主程序中,直接调用这个函数模块来获取数据。
CALL FUNCTION ‘Z_GET_GL_ACCOUNT_BALANCE’ EXPORTING i_bukrs = ‘1000’ i_gjahr = sy-datum(4) TABLES et_data = lt_balance_data.
- 优势:
- 完全解耦:你的主程序不再依赖
SUBMIT和具体的报表执行环境。 - 可复用性高:任何其他程序或Web Service都可以方便地调用这个函数。
- 易于测试:函数模块可以单独进行单元测试。
- 升级友好:如果未来SAP改变了底层逻辑,你只需要在一个地方(函数模块内)调整核心逻辑的移植部分。
- 完全解耦:你的主程序不再依赖
- 挑战:需要深入理解标准报表的内部逻辑,准确剥离出核心计算部分,这要求开发者有较强的ABAP程序和业务理解能力。
5. 循环调用、后台作业与性能调优实战
网络热词中提到了“循环调用submit rfob5200”,这引出了批量自动化处理的场景,同时也必须考虑性能影响。
5.1 实现循环调用与批量处理
假设你需要为100个公司代码分别执行RFOB5200并汇总结果。
DATA: lt_bukrs TYPE RANGE OF bukrs, ls_bukrs LIKE LINE OF lt_bukrs, lt_all_balances TYPE TABLE OF ty_balance. “ 获取需要处理的100个公司代码 SELECT bukrs INTO TABLE @DATA(lt_companies) FROM t001 UP TO 100 ROWS. LOOP AT lt_companies INTO DATA(ls_company). CLEAR: lt_bukrs. lt_bukrs = VALUE #( ( sign = ‘I’ option = ‘EQ’ low = ls_company-bukrs ) ). “ 方案A:直接SUBMIT(不推荐,性能差) “ SUBMIT rfob5200 WITH s_bukrs IN lt_bukrs ... AND RETURN. “ 方案B:更好的方式是调用封装好的函数 CALL FUNCTION ‘Z_GET_GL_ACCOUNT_BALANCE’ EXPORTING i_bukrs = ls_company-bukrs i_gjahr = sy-datum(4) TABLES et_data = lt_company_balance. “ 汇总数据 APPEND LINES OF lt_company_balance TO lt_all_balances. “ 方案C:提交后台作业(异步,适合超大量) “ 这里可以累积一定数量(如10个)后,生成一个后台作业ID,统一提交 ENDLOOP.- 关键决策点:
- 同步 vs 异步:如果每个调用执行很快,且需要即时结果,用同步循环。如果每个调用耗时很长,或不需要即时结果,应使用后台作业异步提交。
- 性能考量:在循环内直接
SUBMIT会频繁创建/销毁会话,开销巨大。使用函数调用或将对SUBMIT的调用封装在后台作业中,能显著减少开销。
5.2 后台作业的精细控制
对于大批量任务,手动循环SUBMIT不可取。应该使用ABAP后台作业API来管理。
DATA: lv_jobname TYPE tbtcjob-jobname VALUE ‘Z_MASS_RFOB5200’, lv_jobcount TYPE tbtcjob-jobcount, lv_step. CALL FUNCTION ‘JOB_OPEN’ EXPORTING jobname = lv_jobname IMPORTING jobcount = lv_jobcount EXCEPTIONS cant_create_job = 1 invalid_job_data = 2 OTHERS = 3. IF sy-subrc = 0. LOOP AT lt_companies INTO ls_company. lv_step = lv_step + 10. “步骤号递增 “ 为每个公司代码定义一个后台作业步骤 SUBMIT rfob5200 WITH s_bukrs EQ ls_company-bukrs VIA SELECTION-SCREEN “关键! AND RETURN USER sy-uname VIA JOB lv_jobname NUMBER lv_jobcount AND PRINT “可以设置打印参数 TO SAP-SPOOL SPOOL PARAMETERS print_params WITHOUT SPOOL DYNPRO AND SAVE LIST TO DATABASE ‘BAL’ “可选:保存列表到数据库 EXPORTING LIST TO MEMORY “可选:如果需要后续处理列表 AND SET TASKNAME ‘STEP’ && lv_step. “设置步骤名 IF sy-subrc <> 0. “ 处理作业步骤提交错误 ENDIF. ENDLOOP. “ 关闭并提交作业 CALL FUNCTION ‘JOB_CLOSE’ EXPORTING jobname = lv_jobname jobcount = lv_jobcount strtimmed = abap_true “立即开始 EXCEPTIONS cant_start_immediate = 1 invalid_startdate = 2 OTHERS = 3. ENDIF.5.3 性能调优与资源管理
循环调用或批量SUBMIT极易引发性能问题。
- 减少调用频率:这是最有效的优化。评估是否真的需要为每个条目单独调用?能否修改被调用程序,使其一次性接受一个范围参数(如公司代码区间)进行处理?这是根本性优化。
- 使用后台作业并限制并发:通过
SM36或DBMS_SCHEDULER(如果支持)创建作业时,可以设置作业的优先级和并行进程数限制。避免一次性提交数百个作业拖垮应用服务器。 - 优化被调用程序:如果被调用程序是你可控的(如自开发程序),确保其本身是高效的:使用正确的索引,避免
SELECT *,使用FOR ALL ENTRIES或JOIN时注意性能,及时关闭游标等。 - 监控与预警:使用
SM50(工作进程概览)和STAD(统计记录)监控批量运行时的系统负载。为关键后台作业设置作业日志监控(SM37),失败时发送警报。 - 资源清理:使用
EXPORT TO MEMORY后,如果数据很大,记得在不再需要时使用FREE MEMORY ID ‘your_id’释放内存。使用数据库表中转数据时,要有归档和清理策略。
6. 常见陷阱、调试技巧与最佳实践汇总
即使理解了所有原理,在实际操作中依然会遇到各种“坑”。下面是一些高频问题的排查思路和最佳实践。
6.1 常见问题与排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
SUBMIT后程序立即返回,被调用程序似乎没执行 | 缺少AND RETURN参数,或程序以LEAVE PROGRAM结束。 | 1. 检查SUBMIT语句是否包含AND RETURN。2. 在被调用程序的最后(如 END-OF-SELECTION后)设置断点,看是否执行到。3. 检查被调用程序中是否有 LEAVE PROGRAM、LEAVE TO TRANSACTION等跳出语句。 |
| 后台作业状态为“已取消”或“错误” | 选择屏幕参数未正确传递;程序需要交互;权限不足。 | 1.最关键:确保SUBMIT语句中包含了VIA SELECTION-SCREEN。2. 检查 WITH参数赋值是否正确,字段名是否与选择屏幕完全一致。3. 检查程序逻辑中是否有 MESSAGE … TYPE ‘E’导致中止,或AUTHORITY-CHECK失败。4. 在 SM37中查看作业日志,通常会有更详细的错误信息。 |
| 无法通过内存ID获取数据 | 内存ID不一致;数据未成功导出;程序异常终止。 | 1. 在调用程序和被调用程序中,打印或调试查看使用的内存ID字符串是否完全一致(包括前导/尾随空格)。 2. 在被调用程序 EXPORT语句后设置断点,确认是否执行到该行。3. 检查被调用程序是否在 EXPORT前因错误退出。 |
直接ASSIGN标准程序内表失败 | 内表名称错误;内表已释放;作用域问题。 | 1. 使用/h启动调试,在被调用程序执行时,查看其内部真正使用的内表名称和结构。2. 考虑更稳定的方案:复制程序或封装函数。 |
| 循环调用性能极差 | 频繁创建会话;被调用程序本身效率低;数据库负载高。 | 1. 改用函数调用替代SUBMIT。2. 将循环调用改为批量参数调用(如果程序支持)。 3. 将任务拆分为多个后台作业,并错峰执行。 4. 分析被调用程序的SQL性能( ST05跟踪)。 |
6.2 调试与跟踪技巧
- 调试被调用程序:在
SUBMIT语句前设置外部断点(/h激活调试后执行),或者在SUBMIT语句中直接插入调试命令(不推荐用于生产代码)。更常用的方法是在被调用程序的关键位置(如START-OF-SELECTION)设置用户断点(BREAK-POINT)或动态断点(在调试器中设置)。 - 查看内存内容:在调试器中,使用系统变量
MEMORY ID查看特定内存ID的内容,或者使用EXPORT/IMPORT的测试代码片段来验证数据是否正确写入/读出。 - 分析后台作业:使用
SM37详细查看作业的步骤、状态和日志。对于出错的作业,日志是首要排查点。 - SQL跟踪:如果怀疑性能问题在数据库层,使用
ST05SQL跟踪来查看被调用程序执行的所有SQL语句及其耗时。
6.3 最佳实践总结
- 明确目标,选择最优方案:
- 仅需触发执行:简单
SUBMIT ... AND RETURN。 - 需要后台/批量执行:
SUBMIT ... VIA SELECTION-SCREEN ... VIA JOB。 - 需要获取数据:优先考虑封装为函数模块(RFC),其次是复制程序并暴露数据接口,最后才是风险较高的内存共享或窥探。
- 仅需触发执行:简单
- 参数传递规范化:使用
WITH子句传递选择参数,使用内存ID或数据库表传递大批量或结构化数据。确保接口清晰、一致。 - 异常处理必须健全:对
SUBMIT的sy-subrc进行检查,对IMPORT的sy-subrc进行检查,对后台作业的状态进行监控。程序必须具备处理失败情况的能力。 - 性能与资源意识:避免在紧密循环中调用重量级报表。使用后台作业分担负载,并合理设置并发控制。及时清理临时内存数据和数据库表。
- 遵守SAP标准:尽可能不对标准程序进行修改。如果必须获取标准程序数据,优先通过官方发布的BAPI、RFC或增强点(Enhancement Spot/User Exit)来实现。复制标准程序作为最后手段,并明确记录和管控。
- 代码可读性与维护性:在调用
SUBMIT的地方添加清晰的注释,说明被调用程序的目的、传递的关键参数以及数据交换的方式。将复杂的SUBMIT逻辑封装在专门的方法或函数中。
掌握SUBMIT程序相互调用,意味着你掌握了在SAP庞大而复杂的报表海洋中组建自己舰队的能力。从简单的脚本自动化,到复杂的数据流水线编排,这项技术是ABAP开发者提升效率、实现复杂业务逻辑的基石。在实际项目中,多思考“为什么要调用”和“如何更优雅地获取结果”,远比机械地编写SUBMIT语句更重要。