ABAP基础语法:变量、系统变量、数据类型与结构体变量详解

ABAP基础语法:变量、系统变量、数据类型与结构体变量详解
一、教程概述
ABAP 开发中最基础、也最重要的内容,就是理解变量、系统变量、数据类型和结构体变量。无论是编写报表、ALV、接口、BAPI 调用,还是做增强开发,这些概念都会反复出现。
本文将从实际开发角度介绍:
- ABAP 变量是什么
- ABAP 变量如何声明和赋值
- 常用 ABAP 系统变量有哪些
- ABAP 常见数据类型
- ABAP 结构体变量如何定义和使用
- 每种写法的典型使用场景
二、什么是 ABAP 变量
变量是程序运行过程中用来保存数据的对象。
例如:
DATA lv_name TYPE string.
lv_name = '张三'.
这里:
DATA表示声明变量lv_name是变量名TYPE string表示变量类型是字符串'张三'是赋给变量的值
在 ABAP 开发中,变量通常用于保存用户输入、数据库查询结果、中间计算结果、BAPI 返回消息、ALV 输出内容、接口请求和响应数据。
三、ABAP 变量命名习惯
虽然 ABAP 不强制要求变量前缀,但实际项目中通常会使用命名规范。
| 前缀 | 含义 | 示例 |
|---|---|---|
lv_ | Local Variable,普通变量 | lv_name |
ls_ | Local Structure,结构体变量 | ls_customer |
lt_ | Local Table,内表变量 | lt_customer |
lr_ | Reference,引用变量 | lr_data |
lo_ | Object,对象变量 | lo_alv |
gv_ | Global Variable,全局变量 | gv_count |
gs_ | Global Structure,全局结构 | gs_header |
gt_ | Global Table,全局内表 | gt_item |
示例:
DATA lv_count TYPE i.
DATA lv_name TYPE string.
DATA ls_mara TYPE mara.
DATA lt_mara TYPE STANDARD TABLE OF mara.
良好的命名规范可以让代码更容易阅读,也能帮助开发者快速判断变量的用途和数据形态。
四、ABAP 变量声明方式
1. 使用 DATA 声明变量
DATA lv_name TYPE string.
DATA lv_age TYPE i.
这是最常见的变量声明方式,适合定义程序中的普通变量。
业务示例:
DATA lv_total TYPE p LENGTH 15 DECIMALS 2.
DATA lv_msg TYPE string.
lv_total 可以用于保存金额合计,lv_msg 可以用于保存处理消息。
2. 声明变量并赋初始值
DATA lv_name TYPE string VALUE '张三'.
DATA lv_age TYPE i VALUE 20.
这种写法适合程序开始时就有默认值的变量,例如默认状态、默认语言、默认页大小等。
3. 使用 CONSTANTS 定义常量
CONSTANTS lc_status_success TYPE c LENGTH 1 VALUE 'S'.
CONSTANTS lc_status_error TYPE c LENGTH 1 VALUE 'E'.
常量声明后不应该再被修改。它适合保存状态值、固定代码、默认配置等。
使用示例:
IF lv_type = lc_status_success.
WRITE '处理成功'.
ENDIF.
4. 使用内联声明
现代 ABAP 支持内联声明,可以在使用变量的位置直接声明变量。
DATA(lv_name) = '张三'.
DATA(lv_count) = 100.
数据库查询时也很常见:
SELECT SINGLE matnr, mtart
FROM mara
INTO @DATA(ls_mara)
WHERE matnr = @lv_matnr.
循环时也可以这样写:
LOOP AT lt_mara INTO DATA(ls_mara).
WRITE: / ls_mara-matnr, ls_mara-mtart.
ENDLOOP.
内联声明适合临时变量、SELECT 查询结果、LOOP 循环变量和方法返回值接收。
五、什么是 ABAP 系统变量
ABAP 系统变量一般以 sy- 开头,由 SAP 系统自动维护。
系统变量用于表示当前运行环境、执行结果、用户信息、日期时间、循环状态等。开发中经常会用系统变量判断程序逻辑是否继续执行。
六、常用 ABAP 系统变量
1. sy-datum:当前日期
DATA lv_date TYPE d.
lv_date = sy-datum.
sy-datum 的格式是 YYYYMMDD,例如 20260803。
常见使用场景:
- 创建日期默认值
- 过账日期默认值
- 查询当天数据
- 记录日志日期
示例:
WRITE: / '当前日期:', sy-datum.
2. sy-uzeit:当前时间
DATA lv_time TYPE t.
lv_time = sy-uzeit.
sy-uzeit 的格式是 HHMMSS。
常见使用场景:
- 记录操作时间
- 生成日志
- 统计程序执行时间
3. sy-uname:当前登录用户
WRITE: / '当前用户:', sy-uname.
常见使用场景:
- 记录创建人
- 权限判断
- 日志追踪
- 自动填充操作用户
例如:
ls_log-created_by = sy-uname.
4. sy-mandt:当前客户端
WRITE: / '当前客户端:', sy-mandt.
SAP 系统通常存在多个 Client,sy-mandt 可以标识当前客户端。
常见使用场景:
- 多客户端系统中区分数据环境
- 日志记录
- 调试数据来源
5. sy-langu:当前登录语言
WRITE: / '当前语言:', sy-langu.
常见使用场景:
- 多语言文本读取
- 消息语言判断
- 描述文本查询
示例:
SELECT SINGLE maktx
FROM makt
INTO @DATA(lv_maktx)
WHERE matnr = @lv_matnr
AND spras = @sy-langu.
6. sy-subrc:上一条语句执行结果
sy-subrc 是 ABAP 中非常重要的系统变量,用来判断上一条语句是否执行成功。
| 值 | 含义 |
|---|---|
0 | 成功 |
4 | 未找到数据或条件不满足 |
| 其他 | 根据具体语句含义不同而不同 |
示例:
SELECT SINGLE *
FROM mara
INTO @DATA(ls_mara)
WHERE matnr = @lv_matnr.
IF sy-subrc = 0.
WRITE '物料存在'.
ELSE.
WRITE '物料不存在'.
ENDIF.
常见使用场景:
- 判断 SELECT 是否查到数据
- 判断 READ TABLE 是否读取成功
- 判断 AUTHORITY-CHECK 是否通过
- 判断函数或方法执行结果
7. sy-tabix:当前内表行号
LOOP AT lt_mara INTO DATA(ls_mara).
WRITE: / '当前行号:', sy-tabix.
ENDLOOP.
sy-tabix 常用于标准表和排序表的循环处理中。对于哈希表,不应依赖 sy-tabix。
常见使用场景:
- LOOP 中获取当前行号
- 修改内表指定行
- 调试数据处理过程
8. sy-index:当前循环次数
DO 5 TIMES.
WRITE: / '当前循环次数:', sy-index.
ENDDO.
常见使用场景:
- DO 循环计数
- WHILE 循环辅助判断
- 生成序号
9. sy-tcode:当前事务码
WRITE: / '当前事务码:', sy-tcode.
常见使用场景:
- 判断程序从哪个事务码进入
- 不同事务码执行不同逻辑
- 日志记录操作入口
10. sy-repid:当前程序名
WRITE: / '当前程序:', sy-repid.
常见使用场景:
- 日志记录
- 动态调用
- ALV 标识当前报表
11. sy-dbcnt:数据库影响行数
UPDATE ztable
SET status = 'S'
WHERE flag = 'X'.
WRITE: / '更新行数:', sy-dbcnt.
常见使用场景:
- 判断 UPDATE 修改了多少行
- 判断 DELETE 删除了多少行
- 记录数据库处理数量
12. 消息相关系统变量
| 系统变量 | 含义 |
|---|---|
sy-msgid | 消息类 |
sy-msgno | 消息编号 |
sy-msgty | 消息类型 |
sy-msgv1 | 消息变量 1 |
sy-msgv2 | 消息变量 2 |
sy-msgv3 | 消息变量 3 |
sy-msgv4 | 消息变量 4 |
这些变量常用于捕获 BAPI 返回消息、记录错误日志、构造异常信息。
七、ABAP 常用数据类型
ABAP 数据类型可以分为基础类型、字符类型、数值类型、日期时间类型、引用类型、结构类型和内表类型。
理解数据类型的关键是:类型规定了变量可以保存什么样的数据,以及这个数据如何参与计算、比较、赋值和输出。
八、字符相关类型
1. C:固定长度字符
DATA lv_code TYPE c LENGTH 10.
lv_code = 'A001'.
C 类型是固定长度字符,不足长度时右侧补空格。
常见使用场景:
DATA lv_bukrs TYPE c LENGTH 4. "公司代码
DATA lv_werks TYPE c LENGTH 4. "工厂
实际项目中更推荐使用 DDIC 类型:
DATA lv_bukrs TYPE bukrs.
DATA lv_werks TYPE werks_d.
2. STRING:动态字符串
DATA lv_text TYPE string.
lv_text = '这是一段动态长度文本'.
STRING 长度可变,适合长文本和拼接消息。
示例:
DATA lv_message TYPE string.
lv_message = |物料 { lv_matnr } 不存在|.
常见使用场景:
- 日志消息
- 错误提示
- 接口报文
- 拼接文本
3. N:数字字符
DATA lv_posnr TYPE n LENGTH 6.
lv_posnr = '10'.
结果通常表现为:
000010
N 本质是字符,不是数学数值,通常用于编号字段。
常见使用场景:
- 销售订单行项目号
- 采购订单行项目号
- 流水号
- 编号字段
九、日期和时间类型
1. D:日期类型
DATA lv_date TYPE d.
lv_date = sy-datum.
格式为 YYYYMMDD。
常见使用场景:创建日期、交货日期、凭证日期、过账日期。
2. T:时间类型
DATA lv_time TYPE t.
lv_time = sy-uzeit.
格式为 HHMMSS。
常见使用场景:创建时间、日志时间、操作时间。
十、数值类型
1. I:整数
DATA lv_count TYPE i.
lv_count = 100.
常见使用场景:计数、循环次数、内表行数、数量统计。
示例:
DATA lv_lines TYPE i.
DESCRIBE TABLE lt_mara LINES lv_lines.
2. P:压缩数
DATA lv_amount TYPE p LENGTH 15 DECIMALS 2.
lv_amount = '1234.56'.
P 类型可以设置小数位,常用于金额、数量、单价、重量和税额。
业务开发中建议优先参考标准字段类型:
DATA lv_wrbtr TYPE bseg-wrbtr.
DATA lv_menge TYPE ekpo-menge.
这样可以避免长度、小数位不一致的问题。
3. F:浮点数
DATA lv_rate TYPE f.
lv_rate = '3.14159'.
F 适合浮点计算,但可能存在精度问题,不建议用于财务金额。
常见使用场景:科学计算、比例计算、非金额类小数计算。
4. DECFLOAT16 和 DECFLOAT34
DATA lv_value TYPE decfloat34.
lv_value = '123456.789'.
常见使用场景:高精度小数计算、汇率计算、复杂价格模拟、精度要求较高的业务场景。
十一、优先使用 DDIC 类型
在 SAP 项目中,强烈建议优先使用数据字典字段类型。
推荐:
DATA lv_matnr TYPE mara-matnr.
DATA lv_kunnr TYPE kna1-kunnr.
DATA lv_vbeln TYPE vbak-vbeln.
不推荐随意写成:
DATA lv_matnr TYPE c LENGTH 18.
DATA lv_kunnr TYPE c LENGTH 10.
因为 DDIC 字段已经维护好了字段长度、数据元素、域、小数位、转换出口和业务语义。使用 DDIC 类型可以减少错误,提高代码稳定性。
十二、什么是 ABAP 结构体变量
结构体变量用于保存一组相关字段。
普通变量保存一个值:
DATA lv_name TYPE string.
结构体变量保存一条完整业务记录:
TYPES: BEGIN OF ty_customer,
kunnr TYPE kna1-kunnr,
name1 TYPE kna1-name1,
land1 TYPE kna1-land1,
ort01 TYPE kna1-ort01,
END OF ty_customer.
DATA ls_customer TYPE ty_customer.
这里:
ty_customer是结构体类型ls_customer是结构体变量kunnr、name1、land1、ort01是结构体字段
十三、结构体变量赋值
1. 逐字段赋值
ls_customer-kunnr = '10000001'.
ls_customer-name1 = '测试客户'.
ls_customer-land1 = 'CN'.
ls_customer-ort01 = 'Shanghai'.
这种写法最直观,适合字段较少、逻辑较清楚的场景。
2. 使用 VALUE 赋值
ls_customer = VALUE ty_customer(
kunnr = '10000001'
name1 = '测试客户'
land1 = 'CN'
ort01 = 'Shanghai'
).
这种写法更集中,适合构造临时结构和初始化结构变量。
3. 从数据库查询赋值
SELECT SINGLE kunnr, name1, land1, ort01
FROM kna1
INTO @ls_customer
WHERE kunnr = @lv_kunnr.
更稳妥的写法是按字段名匹配:
SELECT SINGLE kunnr, name1, land1, ort01
FROM kna1
INTO CORRESPONDING FIELDS OF @ls_customer
WHERE kunnr = @lv_kunnr.
十四、结构体变量的使用场景
结构体变量非常适合表示一条业务数据。
| 业务对象 | 结构体字段示例 |
|---|---|
| 客户 | 客户号、名称、国家、城市 |
| 物料 | 物料号、物料类型、基本单位、物料组 |
| 销售订单 | 订单号、客户、销售组织、订单日期 |
| 采购订单 | 采购订单号、供应商、采购组织、公司代码 |
| ALV 输出 | 数据库字段、状态字段、消息字段 |
| 接口数据 | 请求头、业务字段、返回状态 |
当一个对象有多个属性时,就应该优先考虑使用结构体,而不是定义很多零散变量。
十五、结构体变量与内表
结构体通常表示一行数据,内表表示多行数据。
TYPES: BEGIN OF ty_material,
matnr TYPE mara-matnr,
mtart TYPE mara-mtart,
matkl TYPE mara-matkl,
meins TYPE mara-meins,
END OF ty_material.
DATA ls_material TYPE ty_material.
DATA lt_material TYPE STANDARD TABLE OF ty_material.
添加一行数据:
ls_material-matnr = '000000000000000001'.
ls_material-mtart = 'FERT'.
ls_material-matkl = '001'.
ls_material-meins = 'PC'.
APPEND ls_material TO lt_material.
现代写法:
APPEND VALUE ty_material(
matnr = '000000000000000001'
mtart = 'FERT'
matkl = '001'
meins = 'PC'
) TO lt_material.
查询多条数据:
SELECT matnr, mtart, matkl, meins
FROM mara
INTO TABLE @lt_material
UP TO 100 ROWS.
十六、典型业务示例:ALV 输出结构
在报表开发中,经常需要定义一个 ALV 输出结构。
这个结构通常由两部分组成:
数据库字段 + 程序处理字段
示例:
TYPES: BEGIN OF ty_alv,
matnr TYPE mara-matnr,
maktx TYPE makt-maktx,
mtart TYPE mara-mtart,
matkl TYPE mara-matkl,
status TYPE c LENGTH 10,
msg TYPE string,
END OF ty_alv.
DATA lt_alv TYPE STANDARD TABLE OF ty_alv.
DATA ls_alv TYPE ty_alv.
查询物料数据:
SELECT a~matnr,
b~maktx,
a~mtart,
a~matkl
FROM mara AS a
INNER JOIN makt AS b
ON b~matnr = a~matnr
INTO CORRESPONDING FIELDS OF TABLE @lt_alv
UP TO 100 ROWS
WHERE b~spras = @sy-langu.
处理状态字段:
LOOP AT lt_alv INTO ls_alv.
IF ls_alv-mtart = 'FERT'.
ls_alv-status = '成品'.
ls_alv-msg = '该物料是成品物料'.
ELSE.
ls_alv-status = '其他'.
ls_alv-msg = '该物料不是成品物料'.
ENDIF.
MODIFY lt_alv FROM ls_alv.
ENDLOOP.
更推荐的写法:
LOOP AT lt_alv ASSIGNING FIELD-SYMBOL(<ls_alv>).
IF <ls_alv>-mtart = 'FERT'.
<ls_alv>-status = '成品'.
<ls_alv>-msg = '该物料是成品物料'.
ELSE.
<ls_alv>-status = '其他'.
<ls_alv>-msg = '该物料不是成品物料'.
ENDIF.
ENDLOOP.
ASSIGNING FIELD-SYMBOL 可以直接修改内表当前行,不需要再执行 MODIFY。
十七、结构体之间的赋值
1. 相同结构直接赋值
DATA ls_customer2 TYPE ty_customer.
ls_customer2 = ls_customer.
2. 不同结构按相同字段名赋值
现代写法:
ls_target = CORRESPONDING #( ls_source ).
老写法:
MOVE-CORRESPONDING ls_source TO ls_target.
常见使用场景:
- 数据库结构转 ALV 结构
- 接口结构转业务结构
- BAPI 返回结构转日志结构
- 抬头结构转输出结构
示例:
DATA ls_mara TYPE mara.
DATA ls_alv TYPE ty_alv.
ls_alv = CORRESPONDING #( ls_mara ).
十八、嵌套结构体
结构体中可以包含另一个结构体。
TYPES: BEGIN OF ty_address,
country TYPE land1,
city TYPE string,
street TYPE string,
END OF ty_address.
TYPES: BEGIN OF ty_customer_deep,
kunnr TYPE kna1-kunnr,
name1 TYPE kna1-name1,
address TYPE ty_address,
END OF ty_customer_deep.
DATA ls_customer_deep TYPE ty_customer_deep.
赋值:
ls_customer_deep-kunnr = '10000001'.
ls_customer_deep-name1 = '测试客户'.
ls_customer_deep-address-country = 'CN'.
ls_customer_deep-address-city = 'Shanghai'.
ls_customer_deep-address-street = 'Nanjing Road'.
常见使用场景:
- 接口 JSON 数据
- OData 深层结构
- RAP 实体扩展
- 复杂业务对象建模
十九、变量、系统变量、类型、结构体的关系
可以这样理解:
数据类型:规定数据长什么样
变量:根据数据类型创建出来的具体数据对象
系统变量:SAP 系统自动维护的特殊变量
结构体变量:由多个字段组成的变量,表示一条业务记录
内表:由多条结构体记录组成的数据集合
完整示例:
DATA lv_matnr TYPE mara-matnr. "普通变量
DATA ls_mara TYPE mara. "结构体变量
DATA lt_mara TYPE TABLE OF mara. "内表变量
lv_matnr = '000000000000000001'.
SELECT SINGLE *
FROM mara
INTO @ls_mara
WHERE matnr = @lv_matnr.
IF sy-subrc = 0.
WRITE: / '物料号:', ls_mara-matnr.
WRITE: / '物料类型:', ls_mara-mtart.
ELSE.
WRITE: / '物料不存在'.
ENDIF.
这个例子中:
lv_matnr是普通变量ls_mara是结构体变量lt_mara是内表变量mara-matnr是 DDIC 类型sy-subrc是系统变量
二十、实际开发建议
1. 优先使用 DDIC 类型
推荐:
DATA lv_kunnr TYPE kna1-kunnr.
DATA lv_matnr TYPE mara-matnr.
DATA lv_vbeln TYPE vbak-vbeln.
不推荐:
DATA lv_kunnr TYPE c LENGTH 10.
DATA lv_matnr TYPE c LENGTH 18.
2. 金额和数量字段不要随便自定义
推荐参考标准字段:
DATA lv_amount TYPE bseg-wrbtr.
DATA lv_qty TYPE ekpo-menge.
这样可以避免长度、小数位不一致的问题。
3. 查询数据库后及时判断 sy-subrc
SELECT SINGLE *
FROM kna1
INTO @DATA(ls_kna1)
WHERE kunnr = @lv_kunnr.
IF sy-subrc <> 0.
MESSAGE '客户不存在' TYPE 'E'.
ENDIF.
4. 修改内表时优先使用 Field Symbol
LOOP AT lt_alv ASSIGNING FIELD-SYMBOL(<ls_alv>).
<ls_alv>-status = 'S'.
ENDLOOP.
它比 LOOP INTO + MODIFY 更简洁,也更适合直接修改当前行。
5. 结构体适合表达业务对象
如果一个对象有多个属性,就应该使用结构体。
TYPES: BEGIN OF ty_order,
vbeln TYPE vbak-vbeln,
kunnr TYPE vbak-kunnr,
erdat TYPE vbak-erdat,
netwr TYPE vbak-netwr,
END OF ty_order.
这种方式比定义一堆零散变量更清晰。
二十一、总结
ABAP 开发中的数据建模可以概括为四层:
基础类型:保存单个值
变量:程序中实际使用的数据对象
结构体变量:保存一条业务记录
内表变量:保存多条业务记录
系统变量则是 ABAP 运行时自动提供的上下文信息,例如当前用户、当前日期、执行结果、当前行号等。
实际项目中最常见的写法是:
DATA lv_matnr TYPE mara-matnr.
TYPES: BEGIN OF ty_alv,
matnr TYPE mara-matnr,
maktx TYPE makt-maktx,
mtart TYPE mara-mtart,
status TYPE c LENGTH 10,
msg TYPE string,
END OF ty_alv.
DATA lt_alv TYPE STANDARD TABLE OF ty_alv.
SELECT a~matnr,
b~maktx,
a~mtart
FROM mara AS a
INNER JOIN makt AS b
ON b~matnr = a~matnr
INTO CORRESPONDING FIELDS OF TABLE @lt_alv
WHERE b~spras = @sy-langu.
IF sy-subrc = 0.
LOOP AT lt_alv ASSIGNING FIELD-SYMBOL(<ls_alv>).
<ls_alv>-status = 'S'.
<ls_alv>-msg = '读取成功'.
ENDLOOP.
ENDIF.
掌握变量、系统变量、数据类型和结构体变量,是学习 ABAP 报表、接口、增强、BAPI、ALV 和 RAP 开发的基础。
