Mark的技术博客

SAP顾问 | 咨询专家 | IT与AI技术探索者

吴让宇(Mark)

吴让宇(Mark)

SAP顾问 | 咨询专家 | IT与AI技术探索者

吴让宇(Mark)的个人技术博客,专注于SAP系统实施、行业方案咨询、IT技术与AI技术的研究与分享。

SAP Fiori ABAP CDS OData Dify Docker AI LLM

最新文章

局部类 — 预览 (★★★)

#abap #abap-modularization

概览表

部分 关键字 用途
定义部分 CLASS … DEFINITION 声明组件(方法、属性)
实现部分 CLASS … IMPLEMENTATION 实现方法逻辑

局部类的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
" ---- 定义部分 ----
CLASS lcl_example DEFINITION.
PUBLIC SECTION.
METHODS: get_result
IMPORTING iv_input TYPE i
EXPORTING ev_output TYPE i.

PRIVATE SECTION.
DATA: mv_value TYPE i.
ENDCLASS.

" ---- 实现部分 ----
CLASS lcl_example IMPLEMENTATION.
METHOD get_result.
mv_value = iv_input * 2.
ev_output = mv_value.
ENDMETHOD.
ENDCLASS.

可见性区域

区域 访问范围
PUBLIC SECTION 类的所有使用者
PROTECTED SECTION 类 + 子类(继承)
PRIVATE SECTION 仅类内部

使用局部类

1
2
3
4
5
6
DATA: go_obj TYPE REF TO lcl_example.

CREATE OBJECT go_obj.
CALL METHOD go_obj->get_result
EXPORTING iv_input = 5
IMPORTING ev_output = gv_result.
💡这只是一个预览
完整的 ABAP Objects 面向对象编程在 BC401 课程中讲解。本预览仅涵盖将局部类作为模块化技术所需的基础知识。

考试/测试模式

场景/关键词 答案
“定义与实现的区别?” DEFINITION 声明接口;IMPLEMENTATION 包含代码
“公有与私有区域?” 公有 = 外部可访问;私有 = 仅内部
“局部类的作用范围?” 仅在定义它的程序中可用

相关笔记

第 4 单元:模块化练习(10 道题)

#practice #abap #abap-modularization

相关概念

📌关键模式(点击展开)
关键词 答案
局部技术 子程序 + 局部类方法
全局技术 函数模块 + 全局类方法
子程序参数 仅 USING/CHANGING
BAPI 异常 不得抛出 — 使用 RETURN 参数
静态选择符 =>
实例选择符 ->

第 1 题 - 模块化类型 [recall]

说出 ABAP 中两种局部和两种全局模块化技术。

📌显示答案
局部:子程序(FORM 例程)和局部类中的方法。全局:函数模块(在函数组中)和全局类中的方法。

第 2 题 - 数据封装 [recall]

什么是数据封装?为什么它很重要?

📌显示答案
数据封装意味着模块化单元不直接访问调用程序的数据对象,反之亦然。所有数据交换通过接口(参数)进行。它提高了透明性、可维护性,并防止数据不一致。

第 3 题 - 子程序的局限 [recall]

与函数模块相比,子程序参数有什么局限?

📌显示答案
子程序仅支持 USING 和 CHANGING 参数。它们没有 IMPORTING、EXPORTING 和 RETURNING 参数。这严重限制了对数据传输方向的控制。

第 4 题 - 跨程序调用子程序 [recall]

可以从另一个程序调用子程序吗?应该这样做吗?

📌显示答案
技术上可行(由于历史原因),但你不应该这样做。它违反了封装原则,被认为是不良实践。

第 5 题 - 函数组加载 [application]

当一个程序两次调用同一个函数组中的函数模块时,函数组被加载到程序上下文中几次?

📌显示答案
函数组在每个主程序上下文中仅加载一次。组中的所有函数模块共享相同的全局数据。后续调用复用已加载的函数组。

第 6 题 - BAPI 属性 [recall]

列出 BAPI 函数模块必须满足的四个技术要求。

📌显示答案
  1. 命名规范:BAPI_<对象>_<方法>
  2. 必须支持远程调用
  3. 不得包含用户对话或消息
  4. 不得抛出异常 — 通过 RETURN 参数报告错误
  5. 参数使用带 BAPI_ 前缀的结构类型

第 7 题 - 类与函数组 [analysis]

函数组和全局类在数据管理方面的关键区别是什么?

📌显示答案
函数组在每个上下文中只能加载一次(单一全局数据集)。全局类可以有多个实例(对象),每个实例都有自己的属性值。这允许不同的数据状态同时存在。

第 8 题 - 异常处理 [application]

一个方法使用基于类的异常。写出调用该方法并处理异常的代码结构。

📌显示答案
1
2
3
4
5
6
TRY.
CALL METHOD cl_class=>method
EXPORTING iv_param = lv_val.
CATCH cx_exception_class.
" 处理错误
ENDTRY.

调用必须位于 TRY 和 ENDTRY 之间。异常在 CATCH 块中捕获。


第 9 题 - 静态与实例 [analysis]

比较静态方法和实例方法。分别在什么情况下使用?

📌显示答案
静态方法:无需实例化即可调用(class=>method)。当函数不需要对象特定的状态时使用。实例方法:需要先执行 CREATE OBJECT(ref->method)。当每次调用需要独立的属性值时使用。

第 10 题 - 构造函数 [recall]

什么是构造函数?它在什么时候执行?

📌显示答案
CONSTRUCTOR 是一种特殊方法,在 CREATE OBJECT 后自动执行。它用于初始化实例的属性。构造函数的输入参数在 CREATE OBJECT 语句中提供。

📌模式总结(点击展开)
关键词 答案
局部模块化 子程序 + 局部类方法
全局模块化 函数模块 + 全局类方法
封装性 仅通过接口交换数据
子程序参数 USING/CHANGING
BAPI 异常 不可抛出 — 使用 RETURN 参数
函数组实例 每个上下文一个
类实例 多个(CREATE OBJECT)
静态选择符 =>
实例选择符 ->

Open SQL 数据库访问 (★★★★)

#abap #abap-sql

概览表

访问方式 语法 用途
单条记录 SELECT SINGLE 读取一行
SELECT 循环 SELECT ... ENDSELECT 逐行处理
数组读取 SELECT ... INTO TABLE 批量读取到内表
表连接 SELECT ... INNER JOIN 组合多个表

Open SQL 与 Native SQL

特性 Open SQL Native SQL
数据库无关性 否(依赖特定数据库)
语法 ABAP 特有 特定数据库的 SQL
缓冲 自动
建议 推荐使用 仅在必要时使用

SELECT SINGLE(单条记录)

1
2
3
4
5
6
7
8
9
SELECT SINGLE carrid connid cityfrom cityto
FROM spfli
INTO ls_connection
WHERE carrid = iv_carrid
AND connid = iv_connid.

IF sy-subrc = 0.
" 找到记录
ENDIF.
  • 返回第一条匹配的记录
  • 必须检查 sy-subrc 以确认是否成功
  • sy-dbcnt = 读取的行数(0 或 1)

SELECT 循环(逐行处理)

1
2
3
4
5
6
7
8
9
10
SELECT carrid connid fldate seatsmax seatsocc
FROM sflight
INTO ls_flight
WHERE carrid = iv_carrid
AND connid = iv_connid.

ls_flight-percentage = ls_flight-seatsocc / ls_flight-seatsmax * 100.
APPEND ls_flight TO et_flights.

ENDSELECT.
  • 每次循环将一行数据填入工作区
  • 多次数据库访问(每行一次)

数组读取(批量读取)

1
2
3
4
5
SELECT carrid connid fldate seatsmax seatsocc
FROM sflight
INTO TABLE et_flights
WHERE carrid = iv_carrid
AND connid = iv_connid.
  • 单次数据库访问填充整个内表
  • 比 SELECT 循环更高效
📌数组读取与 SELECT 循环对比
数组读取(INTO TABLE)= 对所有行只需一次数据库访问。SELECT 循环 = 多次数据库访问(每行一次)。出于性能考虑,应始终优先使用数组读取。

INNER JOIN(多表访问)

1
2
3
4
5
6
SELECT s~carrid s~connid s~fldate c~carrname
FROM scarr AS c
INNER JOIN sflight AS s
ON c~carrid = s~carrid
INTO TABLE gt_combined
WHERE s~carrid = iv_carrid.

SQL 相关系统字段

字段 含义
sy-subrc 0 = 成功,4 = 未找到数据,8 = 错误
sy-dbcnt 处理的行数

WHERE 子句运算符

运算符 示例
= WHERE carrid = 'LH'
<> WHERE carrid <> 'LH'
> / < WHERE seatsocc > 100
LIKE WHERE cityfrom LIKE 'New%'
IN WHERE carrid IN ('LH', 'AA')
BETWEEN WHERE fldate BETWEEN '20240101' AND '20241231'

考试/测试模式

场景/关键词 答案
“SELECT SINGLE 保证有结果?” — 需检查 sy-subrc
“INTO TABLE 与 INTO 循环?” 数组读取(一次访问)与循环(多次访问)
“SELECT 后 sy-subrc = 4?” 未找到数据
“Open SQL 的优势?” 数据库无关

相关笔记

子程序 (★★★)

#abap #abap-modularization

概览表

方面 详情
定义 FORM name USING ... CHANGING ...
调用 PERFORM name USING ... CHANGING ...
作用范围 仅在当前程序中局部有效
参数 USING(输入),CHANGING(双向)

定义与调用

1
2
3
4
5
6
7
8
9
10
11
12
13
" 定义
FORM calc_percentage USING pv_act TYPE i
pv_max TYPE i
CHANGING cv_result TYPE decfloat16.
IF pv_max = 0.
cv_result = 0.
ELSE.
cv_result = pv_act / pv_max * 100.
ENDIF.
ENDFORM.

" 调用
PERFORM calc_percentage USING gv_act gv_max CHANGING gv_result.

参数传递

类型 方向 传递方式
USING 输入到子程序 引用传递(默认)或值传递
CHANGING 双向 引用传递(默认)或值结果传递
⚠️默认引用传递
默认情况下,参数通过引用传递。子程序可以直接修改调用者的数据。为防止这种情况,在 FORM 定义中使用 VALUE()。

形式参数与实际参数

  • 形式参数:在 FORM 语句中定义(如 pv_act
  • 实际参数:在 PERFORM 调用中传递(如 gv_actual_value

子程序中的全局与局部数据

1
2
3
4
5
6
程序(全局作用域)
├── gv_global ← 可在 FORM 中访问
├── FORM example.
│ ├── pv_param(形式参数) ← 仅在 FORM 中
│ └── lv_local (DATA) ← 仅在 FORM 中
└── ENDFORM.
⚠️遮蔽规则
如果子程序中的局部数据对象与全局数据对象同名,则在该子程序内部,局部变量会遮蔽全局变量。

为什么优先使用局部类而非子程序

  1. 通过 IMPORTING/EXPORTING/CHANGING/RETURNING 实现更好的封装
  2. 不直接访问全局程序数据
  3. 与 ABAP Objects 兼容
  4. 现代异常处理(基于类的异常)

考试/测试模式

场景/关键词 答案
“子程序的参数类型?” 仅 USING 和 CHANGING
“可以从另一个程序调用子程序吗?” 技术上可以,但不应该这样做(违反封装原则)
“遮蔽规则?” 同名局部变量会隐藏全局变量
“子程序的更好替代方案?” 局部类方法

相关笔记

权限检查 (★★★★)

#abap #abap-auth

概览表

概念 要点
权限对象 将字段分组用于访问控制
权限 分配给权限对象中字段的值
权限配置文件 包含权限,分配给用户
AUTHORITY-CHECK 用于验证权限的 ABAP 语句

SAP 权限概念

1
2
3
4
5
6
用户主记录
└── 权限配置文件
└── 权限
└── 权限对象
├── 字段 1 = 值
└── 字段 2 = 值
  • 权限对象:在对象类中定义,包含特定字段(无值)
  • 权限:分配给权限对象中字段的实际值
  • 权限配置文件:将权限分组,集成到用户主记录中

AUTHORITY-CHECK 语句

1
2
3
4
5
6
7
8
9
AUTHORITY-CHECK OBJECT 'S_CARRID'
ID 'CARRID' FIELD gv_carrid
ID 'ACTVT' FIELD '03'.

IF sy-subrc = 0.
" 用户有权限 → 执行功能
ELSE.
" 用户无权限 → 通知用户
ENDIF.
📌始终检查 sy-subrc
sy-subrc = 0 表示用户具有所需的权限。任何其他值表示权限被拒绝
💡权限检查的位置
权限检查应放在实际数据访问之前(例如在 SELECT 之前)。程序/事务级别的检查是补充,不能替代显式的 AUTHORITY-CHECK。

考试/测试模式

场景/关键词 答案
“sy-subrc = 0 表示?” 用户有权限
“AUTHORITY-CHECK 放在哪里?” 在受保护的操作之前
“事务级权限足够吗?” — 必须添加显式 AUTHORITY-CHECK

相关笔记

数据建模与透明表 (★★★★)

#abap #abap-sql #abap-data

概览表

概念 要点
ERM(实体关系模型) 对现实世界中的业务实体及其关系进行建模
透明表 ABAP 字典定义 → 创建实际的数据库表
主键 唯一标识每一行
索引 加速数据访问

数据建模流程

1
业务实体 → 实体关系模型 (ERM) → ABAP 字典(表定义)→ 激活 → 创建数据库表
  • 实体:业务对象(例如:航空公司、航班、预订)
  • 关系:实体之间的连接(1:1、1:N、N:M)
  • 数据模型:实现表定义的蓝图

透明表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
透明表 (ABAP 字典)
├── 字段定义
│ ├── 字段名称
│ ├── 数据元素(语义类型)
│ │ └── 域(技术类型)
│ ├── 键标志(主键)
│ └── 检查表(外键)
├── 技术设置
│ ├── 数据类(表空间)
│ └── 大小类别
└── 索引
├── 主索引(键字段)
└── 次索引

关键概念

术语 定义
数据元素 带有字段标签和文档的语义类型
技术类型(数据类型、长度、值范围)
主键 唯一标识每一行的键字段集合
主索引 在主键上自动创建的索引
次索引 为经常查询的非键字段手动创建的索引
💡查找表的方法
查找相关数据库表的方法:
  • 信息库信息系统(按字段、名称搜索)
  • 数据元素/结构体的使用处列表
  • ABAP 字典浏览

SAP 航班数据模型(培训示例)

关键表:SCARR(航空公司)、SPFLI(航线)、SFLIGHT(航班)、SBOOK(预订)


考试/测试模式

场景/关键词 答案
“什么是透明表?” 字典定义,会创建一个真实的数据库表
“主索引何时创建?” 在主键字段上自动创建
“次索引的作用?” 加速非键字段的查询
“数据元素与域的区别?” 元素 = 语义;域 = 技术

相关笔记

第6单元:数据建模与数据检索练习(10题)

#practice #abap #abap-sql

相关概念

📌关键模式(点击展开)
关键词 答案
SELECT SINGLE 可能无数据 — 需检查 sy-subrc
数组读取 INTO TABLE — 一次数据库访问
SELECT 循环 INTO wa … ENDSELECT — 多次访问
sy-subrc = 4 未找到数据
AUTHORITY-CHECK sy-subrc 0 = 有权限

问题 1 - 数据建模 [recall]

什么是实体关系模型(ERM),它的用途是什么?

📌显示答案
ERM 对现实世界中的业务实体(例如客户、订单)及其关系进行建模。它是在 ABAP 字典中实现表定义的蓝图。

问题 2 - 透明表 [recall]

在 ABAP 字典中激活透明表定义时会发生什么?

📌显示答案
会在底层数据库中自动创建对应的数据库表

问题 3 - 主索引与次索引 [recall]

主索引和次索引有什么区别?

📌显示答案
主索引:在主键字段上自动创建。次索引:在 WHERE 子句中频繁使用的非键字段上手动创建。

问题 4 - SELECT SINGLE [recall]

SELECT SINGLE 返回什么?需要检查结果吗?

📌显示答案
SELECT SINGLE 返回第一条匹配记录(也可能没有记录)。你必须检查 sy-subrc — 0 表示找到,4 表示未找到匹配。

问题 5 - 数组读取的优势 [recall]

SELECT … INTO TABLE 相比 SELECT 循环有什么优势?

📌显示答案
数组读取执行单次数据库访问。SELECT 循环执行多次(每行一次)。数组读取效率显著更高。

问题 6 - 读取方式对比 [analysis]

比较三种读取数据库数据的方式:SELECT SINGLE、SELECT 循环和数组读取。

📌显示答案
方式 适用场景 数据库访问次数
SELECT SINGLE 已知单条记录 1
SELECT 循环 读取时逐行处理 每行 1 次
数组读取 先读取全部,后处理 1

问题 7 - INNER JOIN [application]

你需要从 SFLIGHT 和 SCARR 中获取带有航空公司名称的航班数据。请编写一个使用 JOIN 的概念性 SELECT 语句。

📌显示答案
使用 INNER JOIN 连接 carrid:
1
2
3
4
SELECT s~carrid s~connid c~carrname
FROM sflight AS s
INNER JOIN scarr AS c ON c~carrid = s~carrid
INTO TABLE gt_result.

问题 8 - 权限检查 [recall]

编写 AUTHORITY-CHECK 来验证用户可以显示(活动 ‘03’)某个航空公司的数据。

📌显示答案
1
2
3
4
AUTHORITY-CHECK OBJECT 'S_CARRID'
ID 'CARRID' FIELD gv_carrid
ID 'ACTVT' FIELD '03'.
IF sy-subrc = 0. " 有权限。 ENDIF.

问题 9 - 权限检查位置 [analysis]

开发者在 SELECT 之后添加了 AUTHORITY-CHECK。这样做正确吗?

📌显示答案
不正确。 应该放在数据访问之前。否则无权限的用户仍然会检索到数据。

问题 10 - Open SQL 的优势 [recall]

Open SQL 相对于 Native SQL 的主要优势是什么?

📌显示答案
数据库无关性。 可在所有支持的数据库系统上运行,并具有自动缓冲功能。

📌模式总结(点击展开)
关键词 答案
ERM 用途 表定义的蓝图
透明表激活 创建数据库表
SELECT SINGLE 第一条匹配,需检查 sy-subrc
数组读取 INTO TABLE,一次数据库访问
AUTHORITY-CHECK 在受保护操作之前执行
Open SQL 优势 数据库无关

ABAP 的 Web Dynpro (★★★)

#abap #abap-dialog

概述表

概念 要点
MVC 模式 模型-视图-控制器架构
组件 自包含的 Web Dynpro 单元
视图 UI 布局定义
控制器 业务逻辑和事件处理
上下文 数据容器(层次结构)

MVC 架构

1
2
3
4
5
6
7
8
Web Dynpro 组件
├── 模型 (数据)
├── 控制器 (逻辑) ←→ 视图 (UI 布局)
└── 上下文 (数据容器)
├── 节点
│ ├── 属性
│ └── 属性
└── 节点

组件结构

元素 用途
组件 封装整个 MVC 单元
窗口 视图的容器,定义导航
视图 可视化布局(输入字段、表格、按钮)
控制器 处理事件和业务逻辑
上下文 层次化数据容器
节点 包含属性的上下文元素
属性 上下文中的单个数据字段

上下文数据绑定

  • 视图将 UI 元素绑定到上下文属性
  • 控制器管理上下文数据
  • 数据流:上下文 ↔ 视图(自动绑定)

考试/测试模式

场景/关键词 答案
“Web Dynpro 架构?” MVC (模型-视图-控制器)
“上下文是什么?” 层次化数据容器
“什么包含视图?” 窗口
“节点与属性的区别?” 节点 = 数据结构;属性 = 单个字段

相关笔记

代码检查器 (★★★)

#abap #abap-tools

概述表

概念 要点
代码检查器 (SCI) 静态分析工具,检查性能、安全性、错误
检查变体 定义要运行哪些检查
对象集 定义要检查哪些对象
检查 在对象集上执行变体

访问代码检查器

方法 方式
上下文菜单 对象列表 → 检查 → 代码检查器
编辑器菜单 程序 → 检查 → 代码检查器
事务代码 SCI

检查类别

类别 检查内容包括
常规 数据格式化、表名列表
性能 索引使用、循环中的 SELECT、缓冲区绕过
安全性 跨客户端查询、动态 WHERE、权限检查不足
语法 ABAP 语法检查、扩展程序检查
编程约定 命名约定
搜索 标记和语句搜索

关键概念

1
2
3
4
5
代码检查器 (SCI)
├── 检查变体 ─── 检查什么?(私有或公有)
├── 对象集 ─── 检查哪些对象?(私有或公有)
└── 检查 ─── 执行并保存结果
(命名 → 结果保存;未命名 → 临时)

伪注释

1
CALL TRANSACTION 'MM01'. "#EC CI_CALLT
  • 使用 "#EC * 抑制特定警告

    仅当代码是有意如此编写时才抑制警告

💡默认变体
标准检查使用默认变体。创建名为 DEFAULT 的自定义变体来覆盖(仅限您的用户)。更好的做法:创建命名变体。

考试/测试模式

场景/关键词 答案
“代码检查器的用途?” 分析性能、安全性、语义错误
“事务代码?” SCI
“如何抑制警告?” 伪注释 #EC *

相关笔记

第 7 单元:用户对话练习(10 道题)

#practice #abap #abap-dialog

相关概念

📌关键模式(点击展开)
关键词 答案
PBO 时机 屏幕显示之前
PAI 时机 用户操作之后
SET SCREEN 0 返回/退出
INITIALIZATION 选择屏幕之前
START-OF-SELECTION 主处理
简单 ALV CL_SALV_TABLE

问题 1 - PBO 与 PAI [recall]

解释屏幕处理中 PBO 和 PAI 的区别。

📌显示答案
PBO(输出前处理):在屏幕显示之前运行。用于设置默认值、填充字段。PAI(输入后处理):在用户执行操作后运行。用于处理输入、评估功能码、确定下一个屏幕。

问题 2 - OK 代码字段 [recall]

屏幕编程中 OK 代码字段的用途是什么?

📌显示答案
OK 代码字段捕获用户操作的功能码(按钮点击、菜单选择)。定义为 TYPE syucomm,处理完毕后必须清空。

问题 3 - 屏幕导航 [recall]

SET SCREEN 0 的作用是什么?

📌显示答案
返回调用屏幕退出程序(如果没有调用屏幕)。

问题 4 - 事件顺序 [recall]

列出可执行程序的事件处理顺序。

📌显示答案
  1. LOAD-OF-PROGRAM
  2. INITIALIZATION(设置默认值,屏幕之前)
  3. AT SELECTION-SCREEN(验证输入)
  4. START-OF-SELECTION(主处理)
  5. END-OF-SELECTION(可选)

问题 5 - INITIALIZATION 的用途 [application]

为什么 INITIALIZATION 适合设置动态默认值?

📌显示答案
因为它在选择屏幕显示之前运行。此处设置的变量内容会成为屏幕上的默认输入值

问题 6 - 选择文本 [recall]

PARAMETERS 字段的选择文本在哪里维护?

📌显示答案
选择文本属于程序的文本元素——通过对象导航器或”转到”菜单维护,而不是直接在选择屏幕上

问题 7 - Web Dynpro MVC [recall]

MVC 代表什么,各角色的作用是什么?

📌显示答案
模型-视图-控制器:模型 = 数据,视图 = UI 布局,控制器 = 逻辑/事件。上下文充当数据容器。

问题 8 - ALV 类 [analysis]

比较 CL_SALV_TABLE 和 CL_GUI_ALV_GRID。

📌显示答案
CL_SALV_TABLE:简单、最少代码的显示。内置排序/筛选功能。CL_GUI_ALV_GRID:自定义容器中的高级 ALV。对布局、事件、自定义工具栏有完全控制。

问题 9 - 对话事务 [application]

创建对话事务的步骤是什么?

📌显示答案
  1. 创建模块池程序(类型 M)
  2. 创建带 PBO/PAI 模块的屏幕
  3. 使用”程序和屏幕(对话事务)”创建事务
  4. 输入程序名和初始屏幕编号
  5. 设置 GUI 支持并保存

问题 10 - SELECT-OPTIONS 与 PARAMETERS [analysis]

比较选择屏幕中的 PARAMETERS 和 SELECT-OPTIONS。

📌显示答案
PARAMETERS:单值输入。用于简单选择。SELECT-OPTIONS:范围/复杂选择,支持多种选项(值、范围、排除条件)。用于灵活的条件筛选。

📌模式总结(点击展开)
关键词 答案
PBO 屏幕显示之前
PAI 用户操作之后
SET SCREEN 0 返回/退出
INITIALIZATION 屏幕之前 — 默认值
START-OF-SELECTION 主处理
简单 ALV CL_SALV_TABLE
高级 ALV CL_GUI_ALV_GRID
0%