刚接触SAP系统开发时,第一次听说"后台Job"这个概念让我一头雾水。直到有次用户抱怨每月报表总要手动点击执行,我才意识到后台Job的重要性。简单来说,后台Job就是让程序在后台自动运行的技术,就像设置手机闹钟一样,设定好时间和任务后就可以自动执行。
ABAP中实现后台Job调度的核心是三个函数:JOB_OPEN、SUBMIT和JOB_CLOSE。这三个函数就像接力赛的三个选手:
举个例子,我们有个每月执行的物料报表ZPPR0010_C,传统方式是每月1号早上9点,用户手动登录系统执行。而通过后台Job,可以设置系统在每月1号凌晨自动运行,等用户上班时结果已经生成好了。
Job名称就像身份证号,必须唯一标识每个Job。我见过有人直接用固定名称导致Job冲突,后来发现日期时间戳是最可靠的方案:
abap复制DATA: lv_jobname TYPE tbtcjob-jobname,
lv_jobcount TYPE tbtcjob-jobcount.
lv_jobname = sy-datum && sy-uzeit && '_ZPPR0010_' && sy-uname.
这里用系统日期(sy-datum)+时间(sy-uzeit)+程序名+用户名的组合,确保即使同一用户同一秒提交多个Job也不会重名。曾经有个项目因为Job名称重复导致数据混乱,排查了整整两天才发现是这个原因。
JOB_OPEN函数看似简单,但参数配置直接影响后续操作:
abap复制CALL FUNCTION 'JOB_OPEN'
EXPORTING
jobname = lv_jobname
IMPORTING
jobcount = lv_jobcount
EXCEPTIONS
cant_create_job = 1
invalid_job_data = 2
jobname_missing = 3
OTHERS = 4.
关键点:
后台执行程序时,如何传递参数是个常见难题。通过rsparams表可以完美解决:
abap复制DATA: lt_rsparams LIKE TABLE OF rsparams,
ls_rsparams LIKE rsparams.
LOOP AT s_pcode. "假设s_pcode是选择屏幕字段
MOVE-CORRESPONDING s_pcode TO ls_rsparams.
ls_rsparams-selname = 'S_PCODE'.
ls_rsparams-kind = 'S'. "S表示选择条件
APPEND ls_rsparams TO lt_rsparams.
CLEAR ls_rsparams.
ENDLOOP.
这种方式的优势在于:
大多数人只知道用SUBMIT调用程序,其实它有很多实用参数:
abap复制SUBMIT zppr0010_c
WITH SELECTION-TABLE lt_rsparams
VIA JOB lv_jobname
NUMBER lv_jobcount
AND RETURN.
关键参数说明:
不像SM36只能设置固定时间,程序化控制提供了更灵活的选择:
abap复制CALL FUNCTION 'JOB_CLOSE'
EXPORTING
jobcount = lv_jobcount
jobname = lv_jobname
strtimmed = 'X'. "立即执行标志
abap复制DATA: lv_start_date TYPE sy-datum,
lv_start_time TYPE sy-uzeit.
lv_start_date = sy-datum + 1. "明天
lv_start_time = '080000'. "早上8点
CALL FUNCTION 'JOB_CLOSE'
EXPORTING
jobcount = lv_jobcount
jobname = lv_jobname
sdlstrtdt = lv_start_date
sdlstrttm = lv_start_time.
完善的错误处理是健壮程序的标志。JOB_CLOSE有8种异常情况需要处理:
abap复制IF sy-subrc EQ 0.
MESSAGE s715(db) WITH lv_jobname. "成功消息
ELSE.
CASE sy-subrc.
WHEN 1. "无法立即启动
WHEN 2. "无效的启动日期
...
WHEN 8. "其他错误
ENDCASE.
ENDIF.
建议为每种错误编写详细的日志记录,方便后续排查。我曾经实现过一个Job监控系统,自动收集所有错误信息并发送邮件提醒。
处理大量Job时,这些技巧很实用:
调试后台Job不像普通程序那么简单,我常用的方法:
下面是一个增强版的Job调度程序模板,包含所有最佳实践:
abap复制REPORT zjob_scheduler.
DATA: lv_jobname TYPE tbtcjob-jobname,
lv_jobcount TYPE tbtcjob-jobcount,
lt_params TYPE TABLE OF rsparams,
ls_params LIKE LINE OF lt_params.
"1. 生成唯一Job名称
lv_jobname = |{ sy-datum }{ sy-uzeit }_ZREPORT_{ sy-uname }|.
"2. 创建Job容器
CALL FUNCTION 'JOB_OPEN'
EXPORTING
jobname = lv_jobname
IMPORTING
jobcount = lv_jobcount
EXCEPTIONS
OTHERS = 4.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
RETURN.
ENDIF.
"3. 构建参数表
ls_params-selname = 'P_MONTH'.
ls_params-kind = 'P'.
ls_params-sign = 'I'.
ls_params-option = 'EQ'.
ls_params-low = sy-datum+0(6). "当前年月
APPEND ls_params TO lt_params.
"4. 提交程序到Job
SUBMIT zmonthly_report
WITH SELECTION-TABLE lt_params
VIA JOB lv_jobname NUMBER lv_jobcount
AND RETURN.
IF sy-subrc <> 0.
"错误处理
ENDIF.
"5. 立即执行Job
CALL FUNCTION 'JOB_CLOSE'
EXPORTING
jobcount = lv_jobcount
jobname = lv_jobname
strtimmed = 'X'
EXCEPTIONS
OTHERS = 8.
IF sy-subrc = 0.
MESSAGE s398(00) WITH 'Job' lv_jobname '已成功调度'.
ENDIF.
这个模板经过多个项目验证,可以直接复用。关键改进包括: