ABAP AMDP 是一项什么技术?

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-SYMBOLDATA 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 PROCEDUREBY 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,以及一些其他技巧。