Skip to content

子程序(Subroutine)开发

一、子程序概要

  • 核心作用‌:封装可重用代码逻辑,提升代码维护性
  • 执行方式‌:通过PERFORM语句显式调用
  • 存储位置‌:在程序本地或全局包含文件中定义
  • 生命周期‌:仅在主程序运行时有效

二、子程序定义语法

abap
FORM <subroutine_name> [USING ... CHANGING ...].
  " 业务逻辑
ENDFORM.

三、参数传递类型

参数类型内存操作方式修改影响范围适用场景
USING 传值值传递局部变量副本输入型参数保护原始数据(如查询条件、只读配置)
USING VALUE值传递局部变量副本输入型参数显式声明值传递(强调不修改原值,常用于简单类型参数)
CHANGING引用传递直接修改原变量需要返回处理结果(如计算结果、状态更新)
RETURNING值传递返回新变量函数方法返回值(单值输出场景,需配合EXPORTING使用)

1.USING 传值:

abap
METHODS: calculate_price
  IMPORTING
    iv_base TYPE p DECIMALS 2.  " 值传递,原始数据保护

" 调用时原值不受影响
DATA(lv_base) = '100.00'.
calculate_price( iv_base = lv_base ). " lv_base 值仍为 100.00

2.USING VALUE 显式值传递:

abap
METHODS: format_data
  IMPORTING
    VALUE(iv_text) TYPE string.  " 显式声明值传递

" 方法内部修改不影响外部变量
DATA(lv_input) = 'Original'.
format_data( iv_text = lv_input ). " lv_input 保持 'Original'

3.CHANGING 引用传递:

abap
METHODS: update_status
  CHANGING
    cv_status TYPE char1.  " 引用传递,直接修改原变量

" 调用后原变量被修改
DATA(lv_status) = 'A'.
update_status( CHANGING cv_status = lv_status ). " lv_status 可能变为 'B'

4.RETURNING 返回值:

abap
METHODS: get_tax_rate
  RETURNING
    VALUE(rv_rate) TYPE p DECIMALS 2.  " 单值返回

" 直接接收返回值
DATA(lv_tax) = get_tax_rate( ).

5.性能与安全对比:

维度USING 传值(值传递)CHANGING(引用传递)RETURNING(返回值)
内存占用较高(创建数据副本)低(仅传递内存地址引用)中等(返回新创建的值或结构)
数据安全高(原始数据不受修改影响)低(直接操作原始数据)高(返回独立的新值,不影响输入参数)
适用数据量小规模数据(如基本类型、简单结构)大数据对象(如内表、复杂结构体)单值或简单结构(如函数结果返回值)

6.示例代码‌:

abap
FORM calculate_tax USING iv_amount TYPE p
                CHANGING cv_tax TYPE p.
  cv_tax = iv_amount * '0.13'. " 13%税率计算
ENDFORM.

四、子程序调用方式

abap
" 常规调用
PERFORM calculate_total USING 1000 CHANGING lv_total.

" 动态调用(需异常处理)
DATA(lv_form) = 'CALCULATE_TAX'.
PERFORM (lv_form) IN PROGRAM sy-repid IF FOUND.

" 条件调用
IF lv_condition = abap_true.
  PERFORM validate_data.
ENDIF.

五、异常处理

1. 异常分类体系

异常类型典型场景对应类名
算术运算异常除零错误(如分母为0的除法运算)
数值溢出(超出数据类型范围的计算结果)
CX_SY_ARITHMETIC_ERROR
数据类型异常非法类型转换(如字符串转数值失败)
不兼容数据操作(文本与数字直接运算)
CX_SY_CONVERSION_ERROR
动态调用异常调用不存在的方法(动态方法名错误)
参数类型/数量不匹配(动态传参错误)
CX_SY_DYN_CALL_ERROR
资源访问异常数据库锁冲突(并发事务导致锁定)
文件IO错误(路径无效/权限不足)
CX_SY_OPEN_SQL_ERROR
  1. 算术运算异常
    当程序执行除法运算且分母为0时,会触发CX_SY_ARITHMETIC_ERROR。数值溢出常见于大整数运算超出i/p类型范围

  2. 数据类型异常
    强制转换非法字符到数值类型(如MOVE 'ABC' TO lv_number)将抛出CX_SY_CONVERSION_ERROR

  3. 动态调用异常
    使用CALL METHOD动态调用未定义方法时,系统抛出CX_SY_DYN_CALL_ERROR,参数校验失败也属于此类别

  4. 资源访问异常
    数据库操作中出现锁冲突(如ENQUEUE失败)或文件打开失败(如路径不存在),触发CX_SY_OPEN_SQL_ERROR

2. 异常处理语法结构

abap
TRY.
    " 可能抛出异常的代码
    DATA(lv_result) = 1 / 0.
    
  CATCH cx_sy_arithmetic_error INTO DATA(lx_error).
    WRITE: / '错误代码:', lx_error->get_text( ).
    
  CATCH cx_sy_dyn_call_error INTO DATA(lx_dyn_error).
    WRITE: / '动态调用错误:', lx_dyn_error->error_message.
    
  CLEANUP.
    " 资源释放代码
ENDTRY. 

"➊ 函数模块异常定义
FUNCTION Z_MATERIAL_UPDATE.
  EXCEPTIONS:
    material_not_found = 1
    quantity_error    = 2 
    OTHERS            = 3.

"➋ 调用时异常捕获
CALL FUNCTION 'Z_MATERIAL_UPDATE'
  EXPORTING
    iv_matnr = lv_matnr
  EXCEPTIONS
    material_not_found = 1
    OTHERS            = 3.
    
IF sy-subrc <> 0.
  CASE sy-subrc.
    WHEN 1.
      MESSAGE e001(zmm) WITH '物料不存在'.
    WHEN 3.
      MESSAGE e002(zmm) WITH '未知错误'.
  ENDCASE.
ENDIF.

3. 传统异常处理(兼容旧版本)

abap
CATCH SYSTEM-EXCEPTIONS arithmetic_errors = 4.
  DATA(lv_value) = 1 / 0.
ENDCATCH.

IF sy-subrc = 4.
  WRITE: / '捕获到算术错误'.
ENDIF. ‌

4.高级异常处理技巧

自定义异常类

abap
CLASS cx_custom_error DEFINITION INHERITING FROM cx_static_check.
  PUBLIC SECTION.
    METHODS:
      constructor IMPORTING iv_msg TYPE string,
      get_text REDEFINITION.
ENDCLASS.

METHOD get_text.
  result = me->msg.
ENDMETHOD. ‌

异常传播机制

abap
METHOD process_data.
  TRY.
      validate_input( ).
    CATCH cx_input_error INTO DATA(lx_error).
      RAISE EXCEPTION TYPE cx_application_error
        EXPORTING
          previous = lx_error.
ENDMETHOD. ‌

全局异常处理器

abap
CLASS global_handler DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS:
      handle_exceptions FOR EVENT if_oo_adt_classrun~main
                        IMPORTING sender. 
ENDCLASS.

METHOD handle_exceptions.
  " 统一处理未捕获异常
ENDMETHOD. ‌

六、子程序结束控制

1.子程序终止机制基本原理‌,标准终止流程

abap
FORM <subroutine_name>.
  "业务逻辑代码
  [...] 
  "程序执行到ENDFORM自动终止
ENDFORM. 

FUNCTION <function_module>.
  [...] 
  "执行到ENDFUNCTION自动退出
ENDFUNCTION.

当执行流到达ENDFORM/ENDFUNCTION时自动执行以下操作:

  • 释放子程序内部局部变量内存‌
  • 将CHANGING参数值回传主程序‌
  • 更新SY-SUBRC系统字段状态‌

2.强制终止方法

abap
" 强制退出子程序
IF lv_error = abap_true.
  EXIT.      " 退出当前子程序
  CHECK 1=0. " 条件退出
ENDIF.

3.参数传递与结束控制‌

1. 参数类型对终止的影响
  • ‌传值参数(USING VALUE)‌:终止时不回传修改值‌7
  • ‌传址参数(CHANGING)‌:终止时自动同步最新值‌7

2. 表参数特殊处理

abap
  FORM process_data TABLES tt_data.
  LOOP AT tt_data ASSIGNING <fs_data>.
    IF <fs_data>-value IS INITIAL.
      DELETE tt_data.    "直接操作原内表
      CONTINUE.
    ENDIF.
    [...] 
  ENDLOOP.
  ENDFORM.
  • 表参数始终采用传址方式
  • 任何修改在子程序终止时立即生效‌

4.异常终止场景处理‌

1. 函数模块异常控制

abap
    FUNCTION z_check_material.
    [...] 
    IF sy-subrc <> 0.
      RAISE material_not_found.  "触发异常终止
    ENDIF.
  ENDFUNCTION.

  "调用方处理
  CALL FUNCTION 'Z_CHECK_MATERIAL'
    EXCEPTIONS
      material_not_found = 1 
      OTHERS            = 2.
  IF sy-subrc = 1.
    [...] "异常处理逻辑
  ENDIF.

2. CHECK 与 EXIT 对比

语句作用域执行特点典型使用场景
CHECK循环/程序块- 在循环中:条件不满足时跳过当前循环次,继续下一轮循环(类似CONTINUE
- 在程序块中:终止当前处理块(FORM/METHOD/EVENT)
批量数据处理时跳过无效记录
在子程序中前置参数有效性校验
EXIT循环/程序块- 在循环中:立即终止当前层循环(嵌套循环仅退出当前层)
- 在程序块中:终止当前处理块并返回调用点
发现致命错误时终止处理
多层循环中提前退出指定层级
abap
  FORM validate_input.
  CHECK sy-uname IS NOT INITIAL.  "条件不满足直接终止
  [...] "后续代码不会执行
  ENDFORM.

  FORM emergency_exit.
    IF sy-datum > '20251231'.
      EXIT.  "立即终止子程序
    ENDIF.
    [...] 
  ENDFORM.

七、临时子程序(Local Subroutine)

可以定义在主内存中执行的动态子程序。 语法:

abap
GENERATE Subroutine POOL <itab> NAME <prog> .

上面语句有在执行中程序的主内存里创建子程序池的功能。将子程序池的代码插入到内表<itab>中,创建8位类型为℃的子程序名<prog>。即当需要使用动态的子程序时利用子程序池(程序),动态定义内表时经常使用此语句。例如,假设做一本根据用户指定的期间查询该期间销售额的程序。此时用户每次指定的日期是不一样的,因此无法事先预测内表的字段个数。这时用 GENERATE语句就可以根据日期数动态地定义内表,此外还有很多动态定义内表的方法,在此不一一列举。子程序池只存在于程序执行过程中,在一个程序中可以创建 36个子程序池。发生错误时SY-SUBRC返回8。

abap
REPORT Z04 14.
DATA:gt code(72)OCCURS 10,
     gv_prog(8),
     gv msg(120).
APPEND 'PROGRAM SUBPOOL.' TO gt_code.
APPEND 'FORM dynamic subr.' TO gt code.
APPEND"WRITE /"Dynamic Subroutine is called".' TO gt code
APPEND 'ENDFORM' TO gt code.

GENERATE SUBROUTINE POOL gt code NAME gv prog
MESSAGE gv msg

IF sy-subrc <> 0.
  WRITE: /'Subroutine POOL is failed'.
ELSE.
  WRITE: /'Subroutine POOL name : ', gv_prog.
  SKIP 1.
  PERFORM dynamic subr IN PROGRAM (gv_prog).
ENDIF.

Subroutine POOL name:%_T002S0
Dynamic Subroutine is called

八、PERFORM ON COMMIT 特殊用法

1. 功能概述

PERFORM ON COMMIT 是SAP ABAP中用于延迟执行子程序的特殊语法,主要应用于需要与数据库提交/回滚操作保持事务一致性的场景‌:ml-citation{ref="1,3" data="citationList"}。
核心特性‌:

  • 将子程序执行与LUW(逻辑工作单元)绑定,仅当显式执行COMMIT WORKROLLBACK WORK时触发‌
  • 支持多层级事务控制(COMMIT LEVEL n)‌
  • 适用于需要与数据库操作保持原子性的业务逻辑‌:ml-citation

2. 语法结构

基础语法格式
abap
PERFORM <subroutine> [ON COMMIT|ON ROLLBACK] [LEVEL <n>]

3、执行逻辑

基础执行流程

abap
DATA: gs_data TYPE scarr.

PERFORM update_db ON COMMIT.  "ⓘ 声明延迟执行‌
gs_data-carrid = 'AA'.
gs_data-connid = '0017'.

PERFORM update_params ON COMMIT.  "ⓘ 第二个延迟任务‌
gs_data-connid = '0064'.  "ⓘ 变量值会被覆盖‌

COMMIT WORK AND WAIT.  "ⓘ 触发所有ON COMMIT子程序执行‌

‌变量覆盖说明‌:由于全局变量gs_data在COMMIT前被多次修改,最终执行时仅保留最后一次赋值结果‌

多层级事务控制

abap
PERFORM phase1 ON COMMIT LEVEL 1.  "ⓘ 一级提交任务‌
PERFORM phase2 ON COMMIT LEVEL 2.  "ⓘ 二级提交任务‌

COMMIT WORK LEVEL 1.  "ⓘ 仅执行LEVEL<=1的子程序

4. 开发示例

增强开发中的数据一致性
abap
DATA: gt_log TYPE TABLE OF zaudit_log.

FORM update_data.
  "ⓘ 主数据更新
  UPDATE zmain_table SET field = lv_value WHERE id = lv_id.
  
  "ⓘ 声明审计日志写入(延迟到COMMIT时执行)‌
  PERFORM write_audit_log ON COMMIT.
ENDFORM.

FORM write_audit_log.
  "ⓘ 实际写入操作(使用最终状态数据)
  INSERT zaudit_log FROM TABLE gt_log.
ENDFORM.
事务完整性保障
abap
PERFORM delete_old_data ON COMMIT.  "ⓘ 声明删除操作‌
PERFORM insert_new_data ON COMMIT.   "ⓘ 声明插入操作‌

FORM delete_old_data.
  DELETE FROM ztemp_table WHERE session = sy-uname.
ENDFORM.

FORM insert_new_data.
  INSERT ztemp_table FROM TABLE gt_buffer.
ENDFORM.

IF lv_success = abap_true.
  COMMIT WORK.  "ⓘ 同时执行删除和插入‌
ELSE.
  ROLLBACK WORK.  "ⓘ 撤销所有操作
ENDIF.

5. ‌宏(Macro)概述

宏是 ABAP 语言中用于代码复用的预编译级模块化工具,通过文本替换机制简化重复逻辑的编写,提升代码紧凑性。

定义与语法

宏通过 DEFINE...END-OF-DEFINITION 语句定义,语法如下:

abap
DEFINE macro_name.  
  " 宏逻辑(如 &1 + &2)  
END-OF-DEFINITION.

参数传递‌:使用 &1, &2 等占位符接收调用时传入的参数,按顺序匹配‌

宏调用

直接通过宏名调用,参数以空格分隔:

abap
macro_name param1 param2.

核心特性

特性说明
编译时展开宏在编译阶段直接替换为代码文本,无运行时开销,适用于高频简单逻辑
作用域限制宏仅在其定义位置之后的代码中可用,且仅在当前程序内有效
参数类型仅支持文本替换,无法处理复杂数据类型(如结构体、内表)
特性详解
  1. 编译时展开:

宏在程序编译阶段通过文本替换实现逻辑复用,避免了运行时调用(如函数模块或子程序)的性能损耗。
适用于高频调用的简单操作(如字段计算、条件判断等),但对复杂逻辑的支持有限。

  1. 作用域限制:

宏的作用域受限于定义位置,只能在定义后的代码中被调用。
无法跨程序使用,且不支持嵌套定义(如宏内调用其他宏)。

  1. 参数类型:

宏通过 &1, &2 等占位符接收参数,仅支持直接文本替换,无法传递结构化数据或动态变量。

宏的优缺点

维度优点缺点
执行效率无运行时调用开销,性能高逻辑复杂度受限,仅适合简单场景
维护性修改需重定义,全局影响代码调试困难(错误定位到展开后的代码)
可读性减少冗余代码,提升简洁性过度使用可能导致代码可读性下降

典型应用场景

  • 重复代码简化‌:例如批量字段计算、格式化输出等高频操作‌。
  • 性能敏感逻辑‌:对执行效率要求高的场景(如循环内简单计算)‌。

‌注意事项‌

1.‌ 调试限制‌:宏错误需检查展开后的代码,建议优先使用子程序或函数模块调试‌。
‌2. 作用域管理‌:避免在跨程序或嵌套逻辑中使用宏(作用域不可穿透)‌。
‌3. 参数数量‌:需严格匹配宏定义的参数数量,否则导致编译错误‌。

示例代码

abap
DEFINE sum_three.  
  &3 = &1 + &2.  
END-OF-DEFINITION.  

DATA: a TYPE i VALUE 10,  
      b TYPE i VALUE 20,  
      result TYPE i.  

" 调用宏  
sum_three a b result.  
WRITE: / 'Result:', result.  " 输出:30