1. 背景介绍
AMDP是ABAP Managed Database Procedure
的缩写,译为ABAP托管数据库过程
。
AMDP 将 HANA 数据库的 SQLScript
功能集成到 ABAP 中,允许在 ABAP 类中创建和调用托管数据库过程。
2. 什么时候使用AMDP技术?
AMDP的特性在于,数据处理的push-down,也即将处理逻辑下沉到数据库端执行,这样减少ABAP和数据库间的交互,从何获得更好的性能。
但并不是所有场景都适合使用AMDP技术。因为相较于传统的ABAP代码,AMDP需要开发人员了解HANA以及SQLScript技术,同时AMDP程序的调试不如ABAP程序直观简单,例如需要借助HANA studio等工具完成performance trace等。而且在运维时,AMDP代码与ABAP代码混合在一个类中,导致代码的可读性下降,加剧了运维的难度。
SAP的官方建议:在可以使用
Open SQL
实现需要的功能或优化目标的时候,不建议使用AMDP。而在需要使用Open SQL不支持的特性
(例如原生HANA SQL的一些功能,比如fuzzy search),或者是大量处理流
和分析导致了数据库和应用服务器之间有重复的大量数据传输的情况下(例如对于海量数据进行汇总、过滤、排序),则应当使用AMDP。
3. AMDP的实现
ABAP在 7.4 SP05后提供了AMDP技术的支持,具体的实现方式很简单,通过实现一个IF_AMDP_MARKER_HDB
接口即可,虽然原则上AMDP是可以做成类似OSQL这种对不同数据库的支持,但到目前,其只支持SAP HANA数据库(个人感觉,以后应该也不会做对其他数据库的扩展了,let's see...)。
实现了IF_AMDP_MARKER_HDB
接口的类,我们可以称之为AMDP类。
AMDP类中,可以包含普通的方法,可以包含AMDP方法,AMDP方法在实现时会通过BY DATABASE PROCEDURE...
或BY DATABASE FUNCTION...
这样的关键字标识,这样这个特殊的method,就可以做为AMDP实现的容器了。
AMDP方法的管理是在ABAP服务器上进行的,包括:
- 通过ADT进行AMDP源代码的编辑
- 对应SQLScript代码的静态语法检查以及语法高亮
- 存储过程procedure的创建会在第一次调用时,在DB上进行deploy
- 对于运行时错误,可以用ST22进行监控
- 对于AMDP的传输与普通ABAP对象是一样的
例如, 下面是一个AMDP类的示例伪代码
, 我们来介绍下AMDP的基础语法:
js
CLASS ZCL_AMDP_SAMPLE DEFINITION.
PUBLIC SECTION.
INTERFACES IF_AMDP_MAKER_HDB.
* Only ABAP code possible:
METHODS process IMPORTING it_param TYPE type1
EXPORTING et_param TYPE type2.
* Only SQLScript possible:
METHODS execute IMPORTING VALUE(it_param) TYPE type1
EXPORTING VALUE(et_param) TYPE type2.
ENDCLASS.
CLASS ZCL_AMDP_SAMPLE IMPLEMENTATION.
METHOD process.
" any abap code here...
ENDMETHOD.
METHOD execute BY DATABASE PROCEDURE / DATABASE FUNCTION
FOR db_platform
LANGUAGE db_language
[OPTIONS READ-ONLY]
USING name1 name2 etc...
-- Write SQLScript code here...
select * from dummy;
ENDMETHOD.
ENDCLASS.
3.1 AMDP方法的参数:
对于AMDP method,其参数有一定的限制,必须是数据库支持的数据类型。可以是简单的标量类型(如INT4、DECIMAL、CHAR等)或表类型。
- 例如对于table类型的参数,其必须使用基于TABLE TYPE定义的表类型,且列的定义必须与数据库支持的格式一致。表参数的类型需要使用LINE TYPE明确其结构。类型必须在
ABAP字典
(Data Dictionary,DDIC)中维护(比如通过DDIC中的类型或表定义)。 - AMDP method的参数不支持
FIELD-SYMBOL
或DATA REFERENCE
类型作为参数。 - 对于
STRING 类型
,要注意,STRING 类型不能用于声明数据库内部表结构(即 TABLE 类型字段不能是 STRING 类型)。对于 AMDP 方法中的参数,STRING 类型只能用于传递或返回值,不能直接用作数据库操作的表列值。ABAP 的 STRING 类型 会映射到 SAP HANA 数据库的NVARCHAR 类型
(可变长度 Unicode 字符串)。STRING:动态长度(可超过 255)。数据库限制:NVARCHAR 的最大长度为5000
个字符。如果参数长度可能超过 5000 字符,直接使用 STRING 会引起数据截断,需要使用 CLOB 类型或其他可扩展方案。 - AMDP的参数必须声明为
值引用
。
3.2 Database Procedure vs. Database Function
对于AMDP方法的实现部分,BY DATABASE PROCEDURE
和 BY DATABASE FUNCTION
是用于区分 AMDP 的实现形式
和使用目的
的关键字。
- Database Procedure : 是一个数据库存储过程,存储过程通常用作一系列SQLScript语句操作的容器,能够执行复杂的逻辑,可以返回结果集,并
支持IN/OUT参数
。具体来说,其支持IMPORTING (输入参数)
、EXPORTING (输出参数)
、不支持CHANGING (输入输出参数) ,不支持RETURNING参数。 - Database Function : 是一种数据库函数,用于执行某些操作,并返回一个明确的标量值(通常是单一值)或特定的表值(table type)。也即,仅支持
RETURNING参数
,返回值可以是标量值(如INT4,CHAR)或者表类型(定义在 DDIC 中的结构表)。不支持 EXPORTING 和 CHANGING 参数。
补充知识:
存储过程
就是作为可执行对象存放在数据库中的一个或多个SQL命令。通俗来讲:存储过程其实就是能完成一定操作的一组SQL语句,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块
取一个名字,在用到这个功能的时候调用他就行了。
为何要用存储过程?
存储过程只在
创造时进行编译
,以后每次执行存储过程都不需重新编译
,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。当对数据库进行复杂操作时,可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
3.3 其它关键字
- FOR db_platform : 指定数据库平台。目前仅支持
HDB
表示 SAP HANA 数据库。 - LANGUAGE db_language : 指定编写存储过程的脚本语言。在 SAP HANA 上,系统支持 SQLScript,因此这里指定
SQLSCRIPT
。 - OPTIONS READ-ONLY : 指定数据处理选项。
READ-ONLY
表示方法不会修改数据库中的数据,只能读取。如果省略,则可能允许写操作,但这种定义要谨慎。该选项有助于优化存储过程的执行。
4 示例
4.1 使用Database Procedure完成批量更新
示例场景:根据输入的员工数据,将新员工插入数据库表。如果该员工已存在,则更新其信息。
代码实现:
js
CLASS zcl_amdp_employee_update DEFINITION.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb. " 标记接口
METHODS update_employee_data
IMPORTING it_employee_data TYPE STANDARD TABLE
EXPORTING ev_status TYPE STRING. " 批处理状态
ENDCLASS.
CLASS zcl_amdp_employee_update IMPLEMENTATION.
METHOD update_employee_data BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT.
-- 批量更新或插入员工数据
DECLARE lv_count INT; -- 计数器,用于跟踪批量处理的记录数量
-- 遍历所有传入的数据
FOR cur_employee IN :it_employee_data DO
-- 对现有数据执行更新操作
UPDATE zemployee
SET FirstName = cur_employee.FirstName,
LastName = cur_employee.LastName,
Department = cur_employee.Department
WHERE EmployeeID = cur_employee.EmployeeID;
-- 如果数据不存在 (更新未成功),则插入新数据
IF ROW_COUNT = 0 THEN
INSERT INTO zemployee (EmployeeID, FirstName, LastName, Department)
VALUES (
cur_employee.EmployeeID,
cur_employee.FirstName,
cur_employee.LastName,
cur_employee.Department
);
END IF;
-- 记录处理的记录数
lv_count = lv_count + 1;
END FOR;
-- 返回处理状态
ev_status = 'Processed records: ' || lv_count;
ENDMETHOD.
ENDCLASS.
在ABAP代码中调用AMDP方法:
js
DATA: lt_employee_data TYPE TABLE OF zemployee,
lv_status TYPE string.
lt_employee_data = VALUE #(
( EmployeeID = 'E001' FirstName = 'John' LastName = 'Doe' Department = 'IT' )
( EmployeeID = 'E002' FirstName = 'Jane' LastName = 'Smith' Department = 'HR' )
).
CALL METHOD zcl_amdp_employee_update=>update_employee_data
EXPORTING
it_employee_data = lt_employee_data
IMPORTING
ev_status = lv_status.
WRITE: / lv_status.
4.2 使用Database Function完成复杂数据的聚合
示例场景:计算特定部门的员工工资总额和人数。
代码实现:
js
CLASS zcl_amdp_salary_function DEFINITION.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb. " 标记接口
METHODS get_salary_statistics
IMPORTING iv_department TYPE string
RETURNING VALUE(rv_stats) TYPE string. " 返回工资统计
ENDCLASS.
CLASS zcl_amdp_salary_function IMPLEMENTATION.
METHOD get_salary_statistics BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT.
-- 返回值是一个字符串,格式为:人数 | 工资总额
RETURN SELECT
COUNT(*) || ' | ' || SUM(Salary)
FROM zemployee_salary
WHERE Department = :iv_department;
ENDMETHOD.
ENDCLASS.
在ABAP代码中调用AMDP:
js
DATA: lv_statistics TYPE string.
CALL METHOD zcl_amdp_salary_function=>get_salary_statistics
EXPORTING
iv_department = 'IT'
RETURNING
rv_stats = lv_statistics.
WRITE: / lv_statistics. " 示例结果:'25 | 100000'
5 小结
本文介绍了ABAP AMDP的基本概念和用法,在接下来的文章中,会进一步介绍如何debug AMDP,以及一些其他技巧。