在 ABAP/4 编程语言中,提供了两种类型的 SQL。
第一种:Open SQL
(1)SAP 为 ABAP 定义的数据库无关的 SQL 标准, 为 SAP 支持的所有数据库系统提供了统一的语法和语义。
(2)ABAP中的Database Interface层负责将Open SQL在运行时由系统动态的转化成对应数据库使用的本地SQL。
(3)ABAPer在编写程序时不需要考虑 SAP 系统使用的数据库差异,操作的结果和任何错误消息都与正在使用的数据库系统无关。
(4)基于Open SQL开发的ABAP程序,可以在SAP系统中复用,而不用关心底层数据库的类型。
(5)Open SQL 语句只能访问在 ABAP 字典中创建的数据库表、视图,而不能操作那些直接通过数据库工具创建的表。
(6)Open SQL具有一些功能限制,即只有标准SQL的DML有对应的Open SQL,如:SELECT、INSERT, UPDATE, DELETE。
第二种:Native SQL
(1)每种关系型数据库都有其对应的 SQL,是数据库相关的。
(2)Native SQL绕过ABAP中的Database Interface层,不加转换的直接访问数据库层的Database Interface层。
(3)不同的 SAP 系统可能使用各种不同的数据库,使用Native SQL 的 ABAP 程序无法适应所有的 SAP 系统。
(4)Native SQL 通过设定的数据库链接,可以访问特定的数据库,可以访问不受 ABAP 字典管理的数据库对象,如表、视图、存储过程,以及外部的第三方数据库系统。
(5)Native SQL不会用到缓存,会直接发送给数据库,而不会经过ABAP数据访问层。除开DML,它一般用于DDL、DCL,可以用来维护数据库对象。
(6)可直接对数据库表进行修改删除等操作,有一定安全风险 。
说明及注意事项:
① SAP升级到S4之后,只支持SAP HANA数据库。理论上HANA数据库是向下兼容的,所以基于Hana的Native SQL的ABAP程序也具备复用性。
② Native SQL不会像Open SQL会默认过滤当前客户端号,所有语句都需要自己指定。
③ 本文仅做学习研究用,在SAP日常开发中,建议非必要不使用NativeSQL。
语法:
EXEC SQL [PERFORMING ].
ENDEXEC.
SE11查看测试表SCARR的数据:
REPORT yz_demo1.
DATA: lv_count TYPE i.
EXEC SQL.select count(1) into :lv_countfrom saphanadb.scarrwhere mandt = :sy-MANDT ;
ENDEXEC.
WRITE:/ lv_count.
REPORT yz_demo2.
DATA: lv_carrid TYPE scarr-carrid,lv_carrname TYPE scarr-carrname.
EXEC SQL PERFORMING Write_data.select carrid, carrname into :lv_carrid,:lv_carrnamefrom saphanadb.scarrwhere mandt = :sy-MANDT ;
ENDEXEC.FORM Write_data.WRITE:/ lv_carrid,'',lv_carrname.
ENDFORM.
REPORT yz_demo3.
DATA: BEGIN OF gs_scarr,carrid TYPE scarr-carrid,carrname TYPE scarr-carrname,END OF gs_scarr.
EXEC SQL.OPEN cursor FORselect carrid, carrnamefrom saphanadb.scarrwhere mandt = :sy-MANDT
ENDEXEC.
DO.CLEAR gs_scarr.
EXEC SQL.FETCH NEXT cursor INTO :gs_scarrENDEXEC.IF sy-subrc <> 0.EXIT.ELSE.WRITE:/ gs_scarr-carrid,'',gs_scarr-carrname.ENDIF.
ENDDO.
EXEC SQL.CLOSE cursor
ENDEXEC.
REPORT yz_demo4.
DATA lv_store_dbs TYPE dbcon-con_name.
DATA: lv_exc_ref TYPE REF TO cx_sy_native_sql_error,lv_error_text TYPE string.
TRY.EXEC SQL.CONNECT TO :lv_store_dbsENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.
CATCH cx_sy_native_sql_error INTO lv_exc_ref.CLEAR: lv_error_text.lv_error_text = lv_exc_ref->get_text( ).MESSAGE lv_error_text TYPE 'I'.
ENDTRY.
REPORT yz_demo5.DATA conn TYPE dbcon-con_name.
cl_demo_input=>request( CHANGING field = conn ).
*SELECT SINGLE dbms
*FROM dbcon
*WHERE con_name = @conn AND dbms = 'HDB'
*INTO @DATA(dbtype).
*
*IF sy-subrc <> 0.
* RETURN.
*ENDIF.
TRY.EXEC SQL.CONNECT TO :connENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_errorEXPORTINGtextid = cx_sy_native_sql_error=>cx_sy_native_sql_error.ENDIF.EXEC SQL.OPEN dbcur FORSELECT carridFROM scarrWHERE mandt = :sy-mandtENDEXEC.DATA carrid TYPE scarr-carrid.DO.EXEC SQL.FETCH NEXT dbcur INTO :carridENDEXEC.IF sy-subrc <> 0.EXIT.ELSE.cl_demo_output=>write( |{ carrid }| ).ENDIF.ENDDO.EXEC SQL.CLOSE dbcurENDEXEC.EXEC SQL.DISCONNECT :connENDEXEC.CATCH cx_sy_native_sql_error INTO DATA(exc).cl_demo_output=>write( exc->get_text( ) ).
ENDTRY.
cl_demo_output=>display( ).
REPORT yz_demo6.
DATA: lv_count TYPE i.
DATA: lv_exc_ref TYPE REF TO cx_sy_native_sql_error,lv_error_text TYPE string.
TRY.EXEC SQL.drop table ZStudent;ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.create table ZStudent(ZNo int,ZName nvarchar(10),ZSex nvarchar(1),ZAge int,primary key (ZNo));ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.insert into ZStudentvalues( 1, '张三', '男', 18 );ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.insert into saphanadb.ZStudentvalues( 2, '李四', '女', 19 );ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.update ZStudentset zsex = '男', zage = 18where zname = '李四';ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.select count(1) into :lv_countfrom ZStudentwhere zage = 18;ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.EXEC SQL.commit;ENDEXEC.CATCH cx_sy_native_sql_error INTO lv_exc_ref.CLEAR: lv_error_text.lv_error_text = lv_exc_ref->get_text( ).MESSAGE lv_error_text TYPE 'I'.EXEC SQL.rollback;ENDEXEC.
ENDTRY.
WRITE:/ '18岁的同学共有', lv_count, '位。' .
第1步:一般我们没有SAPHANADB的修改权限,也不建议在其中新建任何自定义对象,所以在SAPHANADB对应租户中新建用户NewUser,在新建用户名对应Schema下,定义如下存储过程:
create or replace procedure newuser.zp_scarr4
(IN I_CLIENT int default 300, -- 传入参数:客户端号OUT O_CARRID varchar(10), -- 传出参数:航空公司IDOUT O_CARRNAME varchar(50) -- 传出参数:航空公司名称
)
as
beginselect top 1 CARRID, CARRNAME --注意事项:使用select into给参数或内部变量赋值,不需要 “:”into O_CARRID, O_CARRNAMEfrom saphanadb.scarr where mandt = :I_CLIENT;
end;
--在Hana studio中执行存储过程
call newuser.zp_scarr4(200,?,?);
--或者
call newuser.zp_scarr4(I_CLIENT=>200, O_CARRID => ?, O_CARRNAME => ?);
第2步:还需要设置SAPHANADB具有新建用户NewUser的执行权限:
第3步:开发ABAP程序,调用上述存储过程:
REPORT yz_demo7.
DATA: lv_carrid TYPE scarr-carrid,lv_carrname TYPE scarr-carrname.
DATA: lv_exc_ref TYPE REF TO cx_sy_native_sql_error,lv_error_text TYPE string.
TRY.EXEC SQL.EXECUTE PROCEDURE newuser.zp_scarr4 ( IN :sy-mandt, OUT :lv_carrid, OUT :lv_carrname )ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.
CATCH cx_sy_native_sql_error INTO lv_exc_ref.CLEAR: lv_error_text.lv_error_text = lv_exc_ref->get_text( ).MESSAGE lv_error_text TYPE 'I'.
ENDTRY.
WRITE: / lv_carrid, ' ', lv_carrname.
第1步:在新建用户名NewUser对应Schema下,定义如下函数:
CREATE FUNCTION newuser.selfunc( mandt char(3), input CHAR(3) )
RETURNs output char(20) as
beginSELECT carrnameINTO outputFROM saphanadb.scarrWHERE mandt = mandt AND carrid = input;
ENd;
--在Hana studio中调用函数
select newuser.selfunc( '200', 'AA') as carrname from dummy;
第2步:还需要设置SAPHANADB具有新建用户NewUser的查询权限:SELECT
第3步:开发ABAP程序,调用上述函数:
REPORT yz_demo8.
DATA: lv_carrid TYPE scarr-carrid VALUE 'AA',lv_carrname TYPE scarr-carrname.
DATA: lv_exc_ref TYPE REF TO cx_sy_native_sql_error,lv_error_text TYPE string.
TRY.EXEC SQL.select newuser.selfunc( :sy-mandt, :lv_carrid) into :lv_carrname from dummy;ENDEXEC.IF sy-subrc <> 0.RAISE EXCEPTION TYPE cx_sy_native_sql_error.ENDIF.
CATCH cx_sy_native_sql_error INTO lv_exc_ref.CLEAR: lv_error_text.lv_error_text = lv_exc_ref->get_text( ).MESSAGE lv_error_text TYPE 'I'.
ENDTRY.
WRITE: / lv_carrid, ' ', lv_carrname.
ADBC(ABAP Database Connectivity,ABAP 数据库链接)的本质就是Native SQL,只是用类做了封装,更加符合面向对象的开发理念,相对开发更友好一些。
SAP为大家提供了 Native SQL接口API,该接口主要由四个类组成:
• CL_SQL_STATEMENT - Execution of SQL Statements
• CL_SQL_PREPARED_STATEMENT - Prepared SQL Statements
• CL_SQL_CONNECTION - Administration of Database Connections
• CX_SQL_EXCEPTION - Exception Class
SAP系统自带的DEMO程序:
REPORT yz_demo9.
TYPES:BEGIN OF result_t,ZNo TYPE i,ZName TYPE string,ZSex TYPE string,ZAge TYPE i,END OF result_t.
DATA:
* connection TYPE dbcon-con_name VALUE 'HAN',stmt_ref TYPE REF TO cl_sql_statement,cx_sql_exception TYPE REF TO cx_sql_exception,lv_text TYPE string,res_ref TYPE REF TO cl_sql_result_set,d_ref TYPE REF TO data,result_tab TYPE TABLE OF result_t,result_line TYPE result_t,row_cnt TYPE i,con_ref TYPE REF TO cl_sql_connection.con_ref = cl_sql_connection=>get_connection( ). "connection,不传参则使用系统当前链接
stmt_ref = con_ref->create_statement( ).TRY.stmt_ref->execute_ddl( 'create table saphanadb.ZStudent2( ZNo int,ZName nvarchar(10),ZSex nvarchar(1),ZAge int, primary key (ZNo) );').stmt_ref->execute_update( 'insert into saphanadb.ZStudent2 values( 1, ''张三'', ''男'', 18 );' ).stmt_ref->execute_update( 'insert into saphanadb.ZStudent2 values( 2, ''李四'', ''女'', 19 );').stmt_ref->execute_update( 'update saphanadb.ZStudent2 set zsex = ''男'', zage = 18 where zname = ''李四'';' ).stmt_ref->execute_update( 'delete from saphanadb.ZStudent2 where zname = ''张三'';' ).res_ref = stmt_ref->execute_query( 'SELECT * FROM saphanadb.ZStudent2' ).GET REFERENCE OF result_tab INTO d_ref.res_ref->set_param_table( d_ref ).row_cnt = res_ref->next_package( ).stmt_ref->execute_ddl( 'DROP TABLE saphanadb.ZStudent2' ).
CATCH cx_sql_exception INTO cx_sql_exception.lv_text = cx_sql_exception->get_text( ).WRITE:/ 'Error:' , lv_text.
ENDTRY.LOOP AT result_tab INTO result_line.WRITE:/ 'NO:' , result_line-ZNo, ' NAME:', result_line-ZName, ' SEX:', result_line-ZSex, ' AGE:', result_line-ZAge.
ENDLOOP.
AMDP(ABAP-Managed Database Procedure),就是在ABAP层进行HANA数据库过程的实现和生命周期的管理。ABAP开发人员可以在 Eclipse 中使用 ADT 开发工具来创建、更改或删除 AMDP方法,AMDP类作为其管理容器,可以使用ABAP 传输机制来管理。
AMDP支持ABAP开发人员在ADT工具中编写HANA SQLScrip,即所谓的数据库存储过程。
实现步骤1:定义一个AMDP全局类
class YCL_AMDP_HDB_INVENTORY definitionpublicfinalcreate public .PUBLIC SECTION.INTERFACES IF_AMDP_MARKER_HDB . "集成系统AMDP接口TYPES:BEGIN OF TY_INVENTORY,MATNR TYPE MATNR_D, "物料号WERKS TYPE WERKS_D, "工厂LGORT TYPE LGORT_D, "库存地点SOBKZ TYPE SOBKZ, "特殊采购类型CHARG TYPE CHARG_D, "批次LIFNR TYPE LIFNR, "供应商KUNNR TYPE KUNNR, "客户VBELN TYPE VBELN, "销售凭证POSNR TYPE POSNR, "销售凭证行项目FQTY TYPE LABST, "库存量END OF TY_INVENTORY .TYPES:TT_INVENTORY TYPE TABLE OF TY_INVENTORY WITH EMPTY KEY ."AMDP PROCEDURE实现,直接写SQLScript从HDB获取数据,也可以调用已封装好的数据库存储过程CLASS-METHODS GET_INVENTORYIMPORTINGVALUE(P_CLNT) TYPE SY-MANDTVALUE(p_MATNR) TYPE MATNRVALUE(p_WERKS) TYPE WERKS_DVALUE(p_LGORT) TYPE LGORT_DVALUE(p_SOBKZ) TYPE SOBKZVALUE(p_CHARG) TYPE CHARG_DVALUE(p_LIFNR) TYPE LIFNRVALUE(p_KUNNR) TYPE KUNNRVALUE(p_VBELN) TYPE VBELNVALUE(p_POSNR) TYPE POSNREXPORTINGVALUE(ET_INVENTORY) TYPE TT_INVENTORY.PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.CLASS YCL_AMDP_HDB_INVENTORY IMPLEMENTATION.METHOD GET_INVENTORYBY DATABASE PROCEDURE FOR HDBLANGUAGE SQLSCRIPTOPTIONS READ-ONLYUSING NSDM_V_MCHB NSDM_V_mkol NSDM_V_MSKANSDM_V_MSLB NSDM_V_MCSD NSDM_V_MARD NSDM_V_MARC.ET_INVENTORY =SELECTMATNR, WERKS, LGORT, '' AS SOBKZ, CHARG,'' AS LIFNR, '' AS KUNNR, '' AS VBELN, '' AS POSNR,CLABS AS FQTYFROM NSDM_V_MCHB AS MCHB --MCHB-批次库存WHERE CLABS <> 0AND MANDT = :P_CLNTAND ( MATNR = :p_MATNR OR :p_MATNR = '' )AND ( WERKS = :p_WERKS or :p_WERKS = '' )AND ( LGORT = :p_LGORT or :p_LGORT = '' )AND ( CHARG = :p_CHARG or :p_CHARG = '' )UNIONSELECTMATNR, WERKS, LGORT, SOBKZ, CHARG,LIFNR, '' as KUNNR, '' as VBELN, '' as POSNR, SLABS as FQTYFROM NSDM_V_mkol as MKOL --MKOL-寄售库存WHERE SLABS <> 0 and SOBKZ not in ( 'T', 'O' )AND MANDT = :P_CLNTAND ( MATNR = :p_MATNR or :p_MATNR = '' )AND ( WERKS = :p_WERKS or :p_WERKS = '' )AND ( LGORT = :p_LGORT or :p_LGORT = '' )AND ( SOBKZ = :p_SOBKZ or :p_SOBKZ = '' )AND ( CHARG = :p_CHARG or :p_CHARG = '' )AND ( LIFNR = :p_LIFNR or :p_LIFNR = '' )UNIONSELECTMATNR, WERKS, LGORT, SOBKZ, CHARG,'' as LIFNR, '' as KUNNR, VBELN, POSNR, KALAB as FQTYfrom NSDM_V_MSKA as MSKA --MSKA-销售订单库存where KALAB <> 0 and SOBKZ not in ( 'T', 'O' )AND MANDT = :P_CLNTAND ( MATNR = :p_MATNR or :p_MATNR = '' )AND ( WERKS = :p_WERKS or :p_WERKS = '' )AND ( LGORT = :p_LGORT or :p_LGORT = '' )AND ( SOBKZ = :p_SOBKZ or :p_SOBKZ = '' )AND ( CHARG = :p_CHARG or :p_CHARG = '' )AND ( VBELN = :p_VBELN or :p_VBELN = '' )AND ( POSNR = :p_POSNR or :p_POSNR = '000000' )UNIONSELECTMATNR, WERKS, '' as LGORT, SOBKZ, CHARG,LIFNR, '' as KUNNR, '' as VBELN, '' as POSNR, LBLAB as FQTYfrom NSDM_V_MSLB as MSLB --MSLB-供应商外包库存where LBLAB <> 0 and SOBKZ not in ( 'T', 'O' )AND MANDT = :P_CLNTAND ( MATNR = :p_MATNR or :p_MATNR = '' )AND ( WERKS = :p_WERKS or :p_WERKS = '' )AND ( SOBKZ = :p_SOBKZ or :p_SOBKZ = '' )AND ( CHARG = :p_CHARG or :p_CHARG = '' )AND ( LIFNR = :p_LIFNR or :p_LIFNR = '' )--* and LGORT in @LR_LGORTUNIONSELECTMATNR, WERKS, LGORT, SOBKZ, CHARG,'' as LIFNR, KUNNR, '' as VBELN, '' as POSNR, SDLAB as FQTYfrom NSDM_V_MCSD as MCSD --MCSD-客户库存where SDLAB <> 0 and SOBKZ not in ( 'T', 'O' )AND MANDT = :P_CLNTAND ( MATNR = :p_MATNR or :p_MATNR = '' )AND ( WERKS = :p_WERKS or :p_WERKS = '' )AND ( LGORT = :p_LGORT or :p_LGORT = '' )AND ( SOBKZ = :p_SOBKZ or :p_SOBKZ = '' )AND ( CHARG = :p_CHARG or :p_CHARG = '' )AND ( KUNNR = :p_KUNNR or :p_KUNNR = '' )UNIONSELECTMARD.MATNR, MARD.WERKS, MARD.LGORT, '' as SOBKZ, '' as CHARG,'' as LIFNR, '' as KUNNR, '' as VBELN, '' as POSNR, MARD.LABST AS FQTYFROM NSDM_V_MARD AS MARD --MARD-仓库库存INNER JOIN NSDM_V_MARC as MARC ON MARD.MATNR = MARC.MATNR AND MARC.WERKS = MARD.WERKS AND MARD.MANDT = MARC.MANDTWHERE MARD.LABST <> 0AND MARD.MANDT = :P_CLNTAND MARC.XCHAR = ''AND ( MARD.MATNR = :p_MATNR or :p_MATNR = '' )AND ( MARD.WERKS = :p_WERKS or :p_WERKS = '' )AND ( MARD.LGORT = :p_LGORT or :p_LGORT = '' );ENDMETHOD.ENDCLASS.
实现步骤2:定义一个ABAP程序来调用AMDP全局类
*&---------------------------------------------------------------------*
*& Report YZ_AMDP_DEMO
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT yz_amdp_demo.TYPES:BEGIN OF ty_inventory,matnr TYPE matnr_d, "物料号werks TYPE werks_d, "工厂lgort TYPE lgort_d, "库存地点sobkz TYPE sobkz, "特殊采购类型charg TYPE charg_d, "批次lifnr TYPE lifnr, "供应商kunnr TYPE kunnr, "客户vbeln TYPE vbeln, "销售凭证posnr TYPE posnr, "销售凭证行项目fqty TYPE labst, "库存量END OF ty_inventory .
TYPES:tt_inventory TYPE TABLE OF ty_inventory WITH EMPTY KEY .DATA: gt_inventory TYPE tt_inventory.DATA: iv_matnr TYPE matnr,iv_werks TYPE werks_d,iv_lgort TYPE lgort_d,iv_sobkz TYPE sobkz,iv_charg TYPE charg_d,iv_lifnr TYPE lifnr,iv_kunnr TYPE kunnr,iv_vbeln TYPE vbeln,iv_posnr TYPE posnr.DATA: gt_fieldcat TYPE lvc_t_fcat WITH HEADER LINE. " 字段目录START-OF-SELECTION.IF cl_abap_dbfeatures=>use_features(EXPORTINGrequested_features = VALUE #( ( cl_abap_dbfeatures=>call_amdp_method )( cl_abap_dbfeatures=>amdp_table_function ) ) ).TRY ."异常捕捉ycl_amdp_hdb_inventory=>get_inventory(EXPORTINGp_clnt = sy-mandtp_matnr = iv_matnrp_werks = iv_werksp_lgort = iv_lgortp_sobkz = iv_sobkzp_charg = iv_chargp_lifnr = iv_lifnrp_kunnr = iv_kunnrp_vbeln = iv_vbelnp_posnr = iv_posnrIMPORTINGet_inventory = gt_inventory ).CATCH cx_ai_system_fault INTO DATA(zcl_cx_ai_system_fault).
* EV_STATUS = 'E'.
* EV_MESSAGE = ZCL_CX_AI_SYSTEM_FAULT->GET_TEXT( ).EXIT.CATCH cx_ai_application_fault INTO DATA(zcl_cx_ai_application_fault).
* EV_STATUS = 'E'.
* EV_MESSAGE = ZCL_CX_AI_APPLICATION_FAULT->GET_TEXT( ).EXIT.ENDTRY."cl_demo_output=>display( GT_INVENTORY ). " 报错,改用ALV显示PERFORM frm_alv_show.ELSE.cl_demo_output=>display( '警告!当前系统不支持AMDP.' ).ENDIF.FORM frm_alv_show .DATA: w_layout TYPE lvc_s_layo.CLEAR w_layout.w_layout-zebra = 'X'.w_layout-col_opt = 'X'.PERFORM frm_build_fieldcat.CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'EXPORTINGi_callback_program = sy-repidis_layout_lvc = w_layoutit_fieldcat_lvc = gt_fieldcat[]
* i_default = 'X'
* i_save = 'A'TABLESt_outtab = gt_inventoryEXCEPTIONSprogram_error = 1OTHERS = 2.IF sy-subrc <> 0.MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgnoWITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.ENDIF.
ENDFORM. " FRM_ALV_SHOWFORM frm_build_fieldcat.DEFINE mac_field.gt_fieldcat-fieldname = &1. " 内表字段名gt_fieldcat-reptext = &2.APPEND gt_fieldcat.end-OF-DEFINITION.mac_field: 'MATNR' '物料号','WERKS' '工厂','LGORT' '库存地点','SOBKZ' '特殊采购类型','CHARG' '批次','LIFNR' '供应商','KUNNR' '客户','VBELN' '销售凭证','POSNR' '销售凭证行项目','FQTY' '库存量'.ENDFORM.
更多详细介绍,请参考待发布的专题文章《SAP AMDP实现方法简介》。
原创文章,转载请注明来源-X档案
上一篇:Maven仓库集成与使用
下一篇:剑指offer