
你是不是也遇到过这种情况上线前夜运维丢过来一张Excel里面列了四十多个后台JOB的需求程序名、变式、调度时间全都有然后让你抓紧时间在SM36里一个个建出来。建到第十个的时候眼睛已经花了建到第三十个的时候开始怀疑人生。后来我自己写了一套ABAP批量设置后台JOB的小工具再遇到这种批量需求基本就是一张配置表导入、点一下执行、五分钟出活。这篇文章就把这件事从头到尾讲清楚覆盖常见场景、核心API、代码骨架、踩坑实录给同样被批量Job折磨过的人一个可以直接抄作业的参考。这套东西不挑行业只要你是做SAP ABAP开发或者BASIS运维的只要系统里存在一批Job要统一创建、统一调度、统一调整频率的需求都可以用同样的思路解决。我会尽量把每一步的为什么也讲明白而不是只丢一段能跑的代码这样你才能改造成适合自己团队的形式。1. 先弄明白批量设置后台JOB到底覆盖了哪些需求1.1 三种最常见的批量场景我做过的批量Job需求大体上可以分成三类。第一类是上线型批量创建。新工厂上线、新公司代码启用、新模块推广往往涉及几十个甚至上百个后台JOB的初始化。这些Job的程序已经开发好了变式也调试完了就差在正式环境里批量创建并调度。这类需求的特点是一次性、量大、时间窗口紧。第二类是周期调整型批量变更。比如月初发现某个月结程序跑得太慢需要在几十个成本中心对应的Job里统一把运行时间从凌晨两点改到凌晨一点半。或者某个报表Job的周期频率从每天一次改成每周一次。如果手工去SM37里一个个翻不仅慢还容易漏。第三类是复制型批量迁移。从开发系统把一个项目组维护好的一批Job定义同步到测试环境、正式环境。很多团队最开始用SCC1或者传输请求来做但Job这种数据跨Client迁移其实有不少限制反而是直接在目标系统用ABAP脚本按照配置表重新生成一次更可控。1.2 手工SM36操作到底痛在哪里手工创建单个Job本身不复杂SM36填个作业名、加程序步、设调度条件、保存五步就完了。但一旦这个动作变成批量问题就全出来了。第一个问题是效率低且无聊。四十个Job哪怕每个只操作三分钟也要两个小时起步而这期间你的注意力是被消耗的越到后面越容易出错。第二个问题是不可审计。手工建的Job到底谁建了什么、哪里填错了、漏了哪个变式事后很难追溯。特别是项目上线这种事如果不同的人各建一坨管理就乱套了。第三个问题是跨系统差异。你在测试环境一个个建完了验收通过到了正式环境还得再来一遍。两次手工操作不可能完全一致某些字段稍微差一位数字生产上跑出来的结果就完全不一样。批量化的本质其实就是把手工反复做的动作变成数据驱动的一次性执行。Job的差异点——程序名、变式、开始时间、周期、作业级别——全部抽象成配置表字段用一段通用的ABAP程序循环创建这才是可靠的解法。1.3 三条路线怎么选简单梳理一下市面上常见的做法方便你对号入座。方案思路优点缺点纯手工SM36逐个创建零开发成本效率低、易错、不可追溯SCC1/Client Copy跨Client复制整体搬迁动全库风险大、容易带入无关数据ABAP配置文件驱动配置表 API循环创建可审计、可复用、跨系统一致需要一定开发量要做变式校验和异常处理如果你只有一两个Job要建手工就完事了。但只要是批量两个字出现我建议直接走第三条路线。后面要讲的主体内容也是围绕这条路线展开的。2. 后台JOB的核心结构和标准API先吃透再动手2.1 后台JOB在SAP里到底是什么结构后台JOB这个概念SAP里的很多老顾问都习惯叫它后台作业但它本质就是一堆定义数据的组合存在系统表里。一个完整的后台JOB由三个层面的信息组成作业头信息记录这个Job叫什么名字、属于哪个作业组、作业级别是什么、当前状态如何作业步骤信息记录这个Job里要执行哪些程序和变式一个Job可以有多步调度条件信息记录这个Job什么时候开始、按什么周期执行、是否需要事件触发。用大白话说就是一个Job就是一个名字 一组干活的步骤 一个时间表。作业在运行过程中会按照时间表被Dispatcher取走放在Job队列里排队然后交给后台Work Process执行。整个过程都可以在SM37里监控。2.2 创建Job的三件套JOB_OPEN、JOB_SUBMIT、JOB_CLOSESAP提供了一个标准的函数组合专门用于通过ABAP代码创建后台JOB这三个函数就是创建Job的三件套顺序固定、缺一不可。第一步调用JOB_OPEN创建作业头。传作业名、作业组、作业级别系统返回一个作业编号JOBCOUNT。这个编号和作业名组合在一起才能唯一定位一个Job。第二步调用JOB_SUBMIT向这个作业头里添加步骤。传程序名、变式名。在一个Job里连续调用多次JOB_SUBMIT就是在给同一个Job添加多个执行步骤。第三步调用JOB_CLOSE设置调度条件并关闭作业。传作业名、作业编号、开始日期、开始时间、是否周期执行、周期单位参数。JOB_CLOSE执行完之后这个Job才算真正在SM37里可见可调度。这三个函数必须在同一个工作过程中连续调用中间不要乱提交或者回滚。JOB_OPEN之后如果直接退出程序作业头会残留在系统里变成一个空壳脏数据。2.3 为什么不能自己UPDATE TBTCO/TBTCP/TBTCT后台Job涉及的三张核心表TBTCO作业头、TBTCP作业步骤、TBTCT调度条件理论上都可以用UPDATE语句直接改。我见过有些同事图省事直接写ABAP去UPDATE TBTCO的STATUS字段释放作业这种路子千万不要学。原因有两点。第一Job表的维护逻辑很复杂除了主表数据之外还要维护作业日志、作业队列、应用服务器上的内存状态。直接UPDATE表数据应用服务器上的作业缓存不会同步更新经常出现你以为已经释放了SM37里却还是没动静。第二你绕过了SAP标准API系统的作业调度机制就无从感知这次变更如果后续系统升级、应用服务器切换、Job迁移还原这些手改出来的数据很可能变成不稳定因素。标准API能实现99%的批量需求老老实实走函数不要把学习和维护成本留给后来人。2.4 新旧API并存要不要拥抱CL_BATCH_JOBSAP在较新的版本里也提供了面向对象的Job操作方式比如CL_BATCH_JOB这个类用起来更贴近现代ABAP的编程习惯。老代码里广泛使用的是JOB_OPEN这一组函数。我的建议是如果是新项目、系统版本较新、团队ABAP水平普遍偏对象化可以考虑用CL_BATCH_JOB它封装的语义更清晰。如果系统里有存量代码、需要在低版本环境稳定运行、或者团队更习惯函数式编程那JOB_OPEN/JOB_SUBMIT/JOB_CLOSE这套三件套永远是保险选择。这篇文章的代码示例我用的是三件套因为这套API版本兼容性最好、资料最多、踩坑记录也最全。你在自己项目里替换成CL_BATCH_JOB逻辑是完全等价的。3. 动手写一个配置表驱动的批量Job发布器3.1 数据结构设计把Job定义变成一张表批量工具的核心是先把Job的定义设计成一张持久化配置表。这样不仅可以在测试环境验证还可以在正式环境直接用同一份配置跑所见即所得。我建议的表结构大概是这样的字段名类型说明MANDTMANDT客户端自动带JOB_NAMEBTCJOB作业名PROG_NAMEBTCPROGRAM要执行的程序VARIANTVARIANT变式名JOB_GROUPBTCJOBG作业组JOB_CLASSBTCJOBCL作业级别默认CSTRTDATEBTCXDATE开始日期STRTTIMEBTCXTIME开始时间FREQ_DAYSNUMC2每隔N天FREQ_HOURSNUMC2每隔N小时FREQ_MINSNUMC2每隔N分钟ACTIVECHAR1启用标记DESCRIPTIONCHAR100用途说明这张表用SE11建好配一个SM30维护视图运维同事就能直接在上面维护不需要改代码。批量创建程序只需要读取ACTIVE X的记录循环处理。作业名的命名规则我在表里就定死了建议统一加Z前缀格式类似ZJOB_工厂代码_业务类型_序号总长度不超过32位。不要小看命名这件事批量创建的Job如果不规范商场里几十个Job后期管理会让你崩溃。3.2 主流程代码实现三件套怎么配成循环核心程序其实就是三件事读配置表、循环调用三件套、收集日志。我贴一段能用的骨架代码你在实际项目里替换表名和字段就行。DATA: ls_cfg TYPE zbt_jobcfg, lv_jobcount TYPE btcjobcnt, lv_subrc TYPE sy-subrc, lt_log TYPE TABLE OF zbt_joblog. SELECT * FROM zbt_jobcfg INTO ls_cfg WHERE active X. CLEAR: lv_subrc. 第一步打开作业头 CALL FUNCTION JOB_OPEN EXPORTING jobname ls_cfg-job_name jobclass ls_cfg-job_class jobgroup ls_cfg-job_group IMPORTING jobcount lv_jobcount EXCEPTIONS cant_create_job 1 OTHERS 2. IF sy-subrc 0. 记录失败日志继续下一条 lv_subrc sy-subrc. PERFORM fill_log CHANGING lt_log. CONTINUE. ENDIF. 第二步添加程序步支持一个Job多步 CALL FUNCTION JOB_SUBMIT EXPORTING jobcount lv_jobcount jobname ls_cfg-job_name report ls_cfg-prog_name variant ls_cfg-variant EXCEPTIONS OTHERS 3. IF sy-subrc 0. lv_subrc sy-subrc. PERFORM fill_log CHANGING lt_log. CONTINUE. ENDIF. 第三步设置调度条件并关闭 CALL FUNCTION JOB_CLOSE EXPORTING jobcount lv_jobcount jobname ls_cfg-job_name strtimmed space startdate ls_cfg-strtdate starttime ls_cfg-strttime periodic cond( when ls_cfg-freq_days 0 or ls_cfg-freq_hours 0 or ls_cfg-freq_mins 0 then X else space ) prd_days ls_cfg-freq_days prd_hours ls_cfg-freq_hours prd_mins ls_cfg-freq_mins EXCEPTIONS OTHERS 4. IF sy-subrc 0. lv_subrc sy-subrc. PERFORM fill_log CHANGING lt_log. CONTINUE. ENDIF. PERFORM fill_success_log CHANGING lt_log. ENDSELECT.这段代码逻辑很直观按配置表一行行创建。注意几个细节JOB_CLOSE里strtimmed的用法很关键。如果你的Job是需要立即释放的把strtimmed设成X那么开始日期和开始时间就不会被调度器等待如果你的Job是定时执行的strtimmed必须留空同时传开始日期和开始时间。如果两个地方都传了系统会以strtimmed优先导致你的定时设置不生效。周期参数prd_days、prd_hours、prd_mins是互斥叠加关系SAP允许你按天、按小时、按分钟组合使用。我一般建议只用一个维度比如每天跑就传prd_days 1每4小时跑就传prd_hours 4组合使用会增加后续排查频率属性的难度。3.3 变式处理的几个坑JOB_SUBMIT里传变式名看着简单实际这里坑最多。变式在SAP里的存储逻辑比较特殊你程序执行时用的变式名和它真实物理存储的名字可能有差异。如果你的变式是以全局变式方式保存的JOB_SUBMIT里直接传变式名就行。但如果这个变式是用某个用户登录状态保存的普通变式作业运行时可能会提示变式不存在。我的建议是所有要在批量Job里使用的变式统一由操作账号在SAVE_VARIANT里保存为全局变式。这样批量程序跑的时候不管当前执行用户是谁都能顺利读取到变式参数。全局变式有个小问题会被所有用户看到所以别把敏感字段放在变式里。另外变式必须在配置阶段就逐项校验。我的习惯是在批量执行前做一轮预检查用一个类似RPY_VARIANT_F4的检索函数或者直接查变式表确认每个Job对应变式都存在。因为JOB_SUBMIT传了不存在的变式函数调用本身可能不会报错但Job一运行就会立即失败这种问题如果上线当晚批量建完了才发现排查起来特别被动。3.4 结果收集与日志输出批量工具跑完不能只给一句成功。你需要在程序里把每个Job是否创建成功、作业编号是多少、在哪一步失败的、返回码是多少全部记录到一张日志表里。日志字段建议包含执行时间、执行用户、作业名、作业编号、结果标识、失败阶段、失败消息。不要小看日志。上线当晚业务同事看着SM36里一堆Job问你这个时间点对不对、为什么那个Job没出来你能掏出日志说这个Job卡在JOB_SUBMIT了变式有问题这个专业度是完全不一样的。整个批量创建过程没有日志就相当于在黑暗里开一辆没有仪表盘的车。3.5 事务控制与异常清理前面提过JOB_OPEN和JOB_CLOSE之间不要做多余的事务控制。但在整轮批量循环里建议每个Job的处理独立提交一个Job失败不要影响下一个Job。具体做法是把单个Job的创建过程封装到一个独立的方法或者函数里里面通过异常和返回码控制流程。如果JOB_SUBMIT失败Job头已经开了一半这种残留作业需要调用标准的作业删除函数清理掉否则SM37里会出现一堆没有步骤的空作业。我这边的习惯是JOB_SUBMIT失败时先记日志再调用标准作业删除函数把刚打开的Job头删掉保证Job总数和日志里的成功数是严格对应的。4. 上线之后验证、监控、常见问题排查实录4.1 SM37里怎么快速验证这批Job程序跑完之后不能直接宣布完成。强制习惯是抽验。打开SM37输入自定义的作业组名就能看到这次批量创建的所有作业。重点核对三样东西。第一作业名是否完整对应。拿配置表清单和SM37作业列表做一次交叉比对确保没有漏建的。第二调度条件是否正确。点开一个Job查看步骤确认程序名、变式名是否与你预期一致日期时间是否填对了。第三状态是否为Scheduled或Released。如果Job的状态是奇怪的中间状态大概率是JOB_CLOSE没成功或者程序在OPEN和CLOSE之间中途退出了。我的经验是抽验数量不用太多但至少要覆盖以下三类一个立即释放的Job、一个定时执行的Job、一个周期执行的Job。这三类代表了三种调度条件分支覆盖了代码里最容易出错的三个逻辑点。4.2 常见问题速查表真实踩过的坑现象可能原因解决办法Job建好了但一直不跑strtimmed填了X或者开始时间还在未来SM37查看调度条件核对释放状态Job运行时立即红叉变式不存在或没有权限创建前预校验变式并统一保存为全局变式Job名在SM37里找不到JOB_CLOSE没有执行成功作业头残留在未调度状态用标准删除函数清理重跑同一个Job被创建了两次没有检查已有作业重复执行批量脚本创建前检查是否存在同名Job或删掉旧Job再建Job乱跳应用服务器没有指定服务器系统自动分配如果需要固定应用服务器在SM36或作业属性里设置批量建了几十个系统很卡后台Job排队等待WorkProcess被占满分批创建错峰调度开始时间4.3 并发与错峰批量脚本自己也要避雷批量创建的Job如果全部设成同一个释放时间点凌晨两点五十个Job同时释放后台队列瞬间被塞满每个Job都在抢后台WorkProcess。这种情况下你会发现有些优先级高的Job反而等很久才跑体感就是明明都释放了怎么这么慢。解决方式有两个。第一个是错峰在配置表里把不同Job的开始时间做微调比如每三分钟错开一批。第二个是控制调度密度不要让所有Job在同一秒释放。我们在配置表里专门加了一个开始时间偏移字段批量程序在JOB_CLOSE时读这个字段把各自的时间加上一个偏移秒数。另外Job级别JOB_CLASS很值得重视。大量普通批导和报表Job建议统一用C级别A级别才是关键任务用的。如果你把几十个Job全设成A级别一旦它们同时释放后台进程会优先处理这些Job导致对话式事务的响应时间飙升用户会直接感受到系统卡顿这种教训我踩过一次相当深刻。4.4 批量释放、批量删除这类衍生需求怎么处理批量创建解决的是从无到有的问题但运维中还有批量释放批量删除批量改变调度条件这些衍生需求。批量释放已有Job可以用标准函数BP_START按作业名和作业编号来释放也可以在SM37里按作业组多选后直接Release。如果有几十个Job只是掉了状态需要重新释放SM37的多选操作比写代码更快。批量删除则要注意不要直接DELETE TBTCO标准做法是通过SM37选中多个作业后删除或者用标准作业维护函数后提交。直接删表会留下很多脏关联记录系统里会出现幽灵Job的引用。至于批量修改调度条件比如统一把开始时间从凌晨两点改到一点半这类功能建议直接做在配置表驱动的发布器里修改配置表字段然后重新执行批量更新逻辑。注意更新逻辑不是再创建一遍同名Job而是先删除旧Job再按新配置创建否则系统里会出现同名但不同JOBCOUNT的重复作业。5. 批量化的进阶玩法5.1 从一次性脚本到自动化运维第一版批量发布器往往做成一个普通报表需要手动执行。用了几次之后你会发现既然配置表已经存在了完全可以把这个发布器本身也挂成后台Job定期去扫描配置表里的待发布记录实现运维同事维护配置表 - 后台自动生成Job的流程。这种情况下要注意权限问题。后台Job执行批量发布时运行用户需要具备S_BTCH_JOB授权对象的相关权限否则创建作业会报权限不足。这个权限问题在手工执行时一般不会被发现因为对话登录用户往往拥有较高权限但Job跑起来用的账号权限可能完全不同。5.2 从ABAP到跨系统整体运维如果你的团队管理着多套SAP环境比如DEV、QAS、PRD三套系统每套系统里的Job定义应该保持一致。用配置表驱动的方式天然支持这个诉求把所有Job的定义维护在一张全局配置表里或者用一个统一的上线检查程序去比对每套系统和配置表的差异。我们团队现在的做法是把批量Job配置表纳入标准传输流程配置表本身作为系统配置随传输请求移动。上线前在QAS验证无误上线时只需要在PRD执行一遍发布器所有Job一次性到位。5.3 和PI/PO集成场景的联动如果你是做SAP PO集成的可能也会遇到类似需求。PO接口在特定时间段会有大量待处理消息需要批量重跑处理程序或者定时去做代理消息的队列清理。这些重跑和清理任务同样可以用后台作业批量管理。尤其是一些通过SPROXY生成的代理类接口接口函数往往在映射配置里才能看到但作业调度层不关心你内部调用了什么代理函数它只关心你要在Job里跑哪个已实现的重跑程序。模式是相通的准备一个统一的重跑报表程序然后以配置表驱动的方式批量创建相关作业。我遇到过的PO重跑Job数量虽然不大但手工建也容易漏批量处理之后明显更可控。5.4 效果复盘批量方案到底省了什么我拿自己最近一次上线的数据做个简单复盘。当时需要建三十七个后台JOB涉及四个系统流程、每种Job的频率和开始时间还都不一样。手工建这批Job保守估计需要三个小时左右如果中间业务方再改一轮需求时间会翻倍。用配置表驱动的方式执行配置数据整理花了一个小时程序执行不到两分钟就结束了。验证阶段花了半小时。整个过程的边际成本主要是在配置整理上而不是在Job创建上。而且这套配置和发布器是资产下次项目上线还能继续用边际成本会越来越低。最后再说两句批量设置后台JOB这件事真正有意义的部分其实不在于那段三件套代码而在于把运维动作数据化的思维方式。你创建Job的一百次操作本质上不过是对一百行配置数据的不同组合执行。把这层窗户纸捅破了很多SAP运维里的痛点都能用同样思路解决。我个人在实际操作中有一个习惯每次批量创建完Job都会把配置表导出一份Excel按作业组归档到项目知识库里。下次无论是排查问题、季度Review还是团队有人离职交接这些记录比任何口头说明都管用。另外提醒一句上线前人工抽验的那几个Job一定别省自动化工具能帮你建得整齐但业务对调度时间的合理性判断还是需要人来把关。