19. 数据共享与传递
19.1. 程序调用、会话、SAP/ABAP内存 关系
“被调用程序插入型”是指:主调用程序(calling program)并不结束,当遇到Link 语句时,会去执行被调用程序(called program),当被调用程序结束后,调用程序回到调用处继续执行;
“调用程序中止型”是指:调用程序(calling program)当遇到Link 语句时会立即中止,然后去执行被调用程序(called program),即使被调用程序执行完毕后,也不会返回到主调程序的调用处继续执行;
SUBMIT<program> AND RETURN:中断(不终止)当前运行的程序,启动新的被调用程序<program>,当<program>运行完后,控制权又返回到被中断的调用程序,继续执行
CALL TRANSACTION <TCode>:可以插入一个具有事务代码的ABAP程序,中断当前运行的程序,待被调程序执行完后,再继续执行主调程序
SUBMIT <program>:结束当前运行的程序,启动新的被调用程序<program>
LEAVE TO TRANSACTION <TCode>:结束当前运行的程序,并启动由事务码<TCode>指定的ABAP程序。在程序中使用该语句的效果等同于用户直接在命令行输入“/n<TCode>”并执行的效果。
使用LEAVE TO TRANSACTION <TCode>调用另一程序时,可以在主调程序中使用“SET PARAMETER ID”将被传递的数据存储到SAP memory中,在被调用的Tcode中可以使用“GET PARAMETER ID”来获取,另外,也可以为被调用Tcode屏幕参数的data element设置parameter ID,这样会自动的获取与存储该屏幕参数(只设置Data Element中的Parameter ID是不起作用的,请看后面)。
只有LEAVE TO TRANSACTION <TCode> 不能使用ABAP MEMORY共享数据(其它调用方式均可以),而应该使用SAP Memory;注,SUBMIT <program>调用后虽然不会返回到主调程序,但也是可以通过ABAP memory来传递数据
异步的Link Program:当遇到这种Function时,将会重新打开一个external session(外部会话,窗口会话),它并行地、独立于当前的external session.
三种会话:
一个External session(外部会话,一个窗口就是一个外部会话)对应着一个ABAP MEMORY内存,所以同一个Window中的所有Internal session(内部会话,一个程序的调用就是一个内部会话)共用着同一个ABAP MEMORY,同一user logon session(用户终端会话,登录就会产生)中不同的window共用一个SAP MEMORY内存:
同一窗口中的不同程序共享同一个ABAP memory,同一用户的不同窗口之间共享同一个SAP memory
当系统用户登录后,就会产生一个与该用户对应的“用户终端会话”(User Terminal Session),用户可以开辟很多外部会话(即窗口,最多可打开6个窗口),在每个窗口会话(外部会话)中又可以先后执行多个ABAP程序(调用一个程序就会产生一个内部会话)
每一个外部会话都有一个叫ABAP Memory的内存区域,在该会话内部可以通过“EXPORT TO MEMORY”和“IMPORT FROM MEMORY”在该会话中的多个运行程序之间(即多个内部会话之间)进行数据共享,并可使用“FREE MEMORY ID”语句将共享数据从ABAP Memory中清除
SAP Memory是所有的会话(指同一用户登录会话中)都可以访问的内存区域,因此可以通过“SET PARAMETER”与“GET PARAMETER”把数据保存在SAP内存中进行数据共享
所以,在同一个external session的Link program我们使用ABAP Memory进行传递数据;而在不同的external session间的Link program我们使用SAP Memory进行数据传递。
19.2. ABAP Memory数据共享
19.2.1. EXPORT
EXPORT {p1 = dobj1 p2 = dobj2 ...} | {p1 FROM dobj1 p2 FROM dobj2 ...} | (ptab) TO | { MEMORY ID id } | { DATABASE dbtab(ar) [FROM wa] [CLIENT cl] ID id } | { SHARED MEMORY dbtab(ar) [FROM wa] [CLIENT cl] ID id } | { SHARED BUFFER dbtab(ar) [FROM wa] [CLIENT cl] ID id }
1. {p1 = dobj1 p2 = dobj2 ...}与 {p1 FROM dobj1 p2 FROM dobj2 ...}的意义一样,只是写法不一样,dobj1、dobj2…变量将会以p1、p2…名称存储到内存或数据库中。p1、p2…名称随便取,如果p1、p2…与将要存储的变量名相同时,只需写变量名即可,即等号与 FROM 后面可以省略。p1、p2…这些名称必须与IMPORT语句中相一致,否则读取不出
2. (ptab):为动态指定需要存储的变量,ptab内表结构要求是这样的:只需要两列,列名任意,但类型需要是字符型;第一列存储如上面的p1、p2…名称,第二列为上面的dobj1、dobj2…变量,如果变量与名称相同,则也可以像上面一样,省略第二列的值。两列的值都必需要大写,实例如下:
TYPES:BEGIN OF tab_type, para TYPE string,"列的名称任意,类型为字符型 dobj TYPE string, END OF tab_type.DATA:text1 TYPE string VALUE `TXT1`, text2 TYPE string VALUE `TXT2`, line TYPE tab_type, itab TYPE STANDARD TABLE OF tab_type.line-para = 'P1'."值都需要大写line-dobj = 'TEXT1'."值都需要大写APPEND line TO itab.line-para = 'P2'.line-dobj = 'TEXT2'.APPEND line TO itab.EXPORT (itab) TO MEMORY ID 'TEXTS'.IMPORT p1 = text2 p2 = text1 FROM MEMORY ID 'TEXTS'.WRITE: / text1,text2."TXT2 TXT1CLEAR: text1,text2.IMPORT (itab) FROM MEMORY ID 'TEXTS'.WRITE: / text1,text2."TXT1 TXT2
3. MEMORY ID:将变量存储到ABAP MEMORY内存(同一用户的同一窗口Session)
4. DATABASE:将变量存储到数据库中;dbtab为簇数据库表的名称(如系统提供的标准表INDX);ar的值为区域ID,它将数据库表的行分成若干区域,它必须被直接指定,且值是两位字符,被存储到簇数据库表中的RELID字段中;id 的值会存储到簇数据表中的RELID字段的下一用户自定义字段中:
TYPES:BEGIN OF tab_type, col1 TYPE i, col2 TYPE i, END OF tab_type.DATA:wa_indx TYPE demo_indx_table, wa_itab TYPE tab_type, itab TYPE STANDARD TABLE OF tab_type.WHILE sy-index < 100. wa_itab-col1 = sy-index. wa_itab-col2 = sy-index ** 2. APPEND wa_itab TO itab.ENDWHILE.wa_indx-timestamp = sy-datum && sy-uzeit.wa_indx-userid = sy-uname.EXPORT tab = itab TO DATABASE demo_indx_table(sq) FROM wa_indx ID 'TABLE'.
数据导入请参考后面IMPORT中的实例
TABLES: indx.DATA: BEGIN OF i_tab OCCURS 100, col1 TYPE i, col2 TYPE i,END OF i_tab.DO 3000 TIMES. i_tab-col1 = sy-index. i_tab-col2 = sy-index ** 2. APPEND i_tab.ENDDO.indx-aedat = sy-datum.indx-usera = sy-uname.indx-pgmid = sy-repid."省略了FROM选项,因为已经使用TABLES indx语句定义了名为indx的结构变量了"Export时会自动将表工作区indx变量中的用户字段存储到簇数据库表中EXPORT i_tab TO DATABASE indx(HK) ID 'Key'.WRITE: ' SRTF2',AT 20 'AEDAT',AT 35 'USERA',AT 50 'PGMID'.ULINE."注:下面完全可以使用 IMPORT FROM DATABASE TO wa 语句来读取用户区字段SELECT * FROM indx WHERE relid = 'HK'AND srtfd = 'Key'. WRITE: / indx-srtf2 UNDER 'SRTF2', indx-aedat UNDER 'AEDAT', indx-usera UNDER 'USERA', indx-pgmid UNDER 'PGMID'.ENDSELECT.
数据导入请参考后面IMPORT中的实例
SRTF2 AEDAT USERA PGMID
0 2011.10.12 ZHENGJUN YJZJ_TEST2
1 2011.10.12 ZHENGJUN YJZJ_TEST2
2 2011.10.12 ZHENGJUN YJZJ_TEST2
3 2011.10.12 ZHENGJUN YJZJ_TEST2
4 2011.10.12 ZHENGJUN YJZJ_TEST2
5. SHARED MEMORY/BUFFER :将数据存储到SAP应用服务器上的内存中,可共同一服务上的所有程序访问。两种的作用是一样的,最大不同是在数据达到最大内存限制时的处理方式不同:最大内存限制值分别是通过rsdb/esm/buffersize_kb (SHARED MEMORY)、rsdb/obj/buffersize (SHARED BUFFER)来设置的,当内存占用快满时,SHARED MEMORY必须通过DELETE FROM SHARED MEMORY来手动清理,而SHARED BUFFER会自动删除很少被使用到的数据(当然也可以通过DELETE FROM SHARED BUFFER手动及时的删除不用的数据)
6. FROM wa:wa工作区类型可以参照簇数据库dbtab类型,也可定义成只含有用户数据字段的结构,它是用来设置簇数据库表中SRTF2 与 CLUSTR两个字段之间的用户数据字段(参见簇数据表图中的编号为5的用户数据)的值,然后在Export时将相应的字段存储到SRTF2字段与CLUSTR字段间的相应字段中去。如果使用“TABLES dbtab.”定义语句,可以省略“[FROM wa]”,也会默认将其存储到数据库表中,但如果没有“TABLES dbtab.”这样的定义语句,也没有“[FROM wa]”选项时,将不会有数据存储到簇数据库表中的用户字段中去
7. CLIENT cl:默认为当前客户端,存储到簇数据库表中的MANDT字段中
19.2.2. IMPORT
IMPORT {p1 = dobj1 p2 = dobj2 ...} | {p1 TO dobj1 p2 TO dobj2 ...} | (ptab) FROM | { MEMORY ID id } | { DATABASE dbtab(ar) [TO wa] [CLIENT cl] ID id } | { SHARED MEMORY dbtab(ar) [TO wa] [CLIENT cl] ID id } | { SHARED BUFFER dbtab(ar) [TO wa] [CLIENT cl] ID id }
从簇数据表中读取数据,各项参数与EXPORT是一样的,请参考EXPORT各项解释
TYPES:BEGIN OF tab, col1 TYPE i, col2 TYPE i, END OF tab.DATA:wa_indx TYPE demo_indx_table, wa_itab TYPE tab, itab TYPE STANDARD TABLE OF tab.IMPORT tab = itab FROM DATABASE demo_indx_table(sq) TO wa_indx ID 'TABLE'.WRITE: wa_indx-timestamp, wa_indx-userid.ULINE.LOOP AT itab INTO wa_itab. WRITE: / wa_itab-col1, wa_itab-col2.ENDLOOP.
TABLES indx.DATA: BEGIN OF jtab OCCURS 100, col1 TYPE i, col2 TYPE i,END OF jtab."注意:i_tab的名称不能是其他的,必须与EXPORT语句中的分类存储标签名一样。如果i_tab本身又是一个jtab类型的内表,则TO后面的jtab可以省略IMPORT i_tab TO jtab FROM DATABASE indx(hk) ID 'Key'."注:在该程序中并没有明显的为indx工作区设置值,但由于使用了TABLES indx.语句定义了与indx簇数据库表同名的结构变量,所以上面IMPORT会默认加上使用TO indx 选项WRITE: / 'AEDAT:', indx-aedat, / 'USERA:', indx-usera, / 'PGMID:', indx-pgmid.SKIP.WRITE 'JTAB:'.LOOP AT jtab FROM 1 TO 5. WRITE: / jtab-col1, jtab-col2.ENDLOOP.
AEDAT: 2011.10.12
USERA: ZHENGJUN
PGMID: YJZJ_TEST2
JTAB:
1 1
2 4
3 9
4 16
5 25
19.2.3. DELETE
DELETE FROM { { MEMORY ID id} | { DATABASE dbtab(ar) [CLIENT cl] ID id} | { SHARED MEMORY dbtab(ar) [CLIENT cl] ID id} | { SHARED BUFFER dbtab(ar) [CLIENT cl] ID id} }.
用来清理EXPORT语句的存储的数据。如果是针对MEMORY ID,则还可以使用 FREE MEMORY IDid与 DELETE FROM MEMORY ID id 作用一样
DATA: id TYPE c LENGTH 4 VALUE 'TEXT', text1 TYPE string VALUE 'Tina', text2 TYPE string VALUE 'Mike'.EXPORT p1 = text1 p2 = text2 TO SHARED BUFFER demo_indx_table(xy) ID id.IMPORT p1 = text2 p2 = text1 FROM SHARED BUFFER demo_indx_table(xy) ID id.DELETE FROM SHARED BUFFER demo_indx_table(xy) ID id."此语句会执行后,sy-subrc返回4IMPORT p1 = text2 p2 = text1 FROM SHARED BUFFER demo_indx_table(xy) ID id.
19.3. SAP MEMORY数据共享
19.3.1. PARAMETERS/SELECT-OPTIONS选项MEMORY ID
Data Element中的Parameter ID的作用只是在屏幕设计时,起到一个辅助填充的作用,例如:当给某个屏幕字段命令时,如果这个字段的名称为“表名(或结构)- 字段名” 形式时,回车时系统会提示:
当点击Yes后,该字段的Parameter ID属性会自动的设置为 MARA-MATNR字段所对应Data Element所设置的Parameter ID MAT,另外,From dict.也会自动被钩上,但时,SET Parameter与GET Parameter 没有自动钩上,如果需要通过SAP Memory传递值,则还需要将这两个手动钩上(如上图)
Data Element中的Parameter ID对选择屏幕是没有任何作用的,如下面的语句不能用来在SAP Memory中传递值:
PARAMETERS: m TYPE mara-matnr.
除非使用MEMORY ID 选项才起作用:
PARAMETERS: m TYPE mara-matnr MEMORY ID mat.
所以选择屏幕中的参数选项 MEMORY ID的作用就等同于对话屏幕中的SET/GET Parameter,它们是作用是相同的,只不过一个用于选择屏幕中,一个用于屏幕计中。
REPORT ztest_sap_memory_1.*PARAMETERS: p_spa TYPE zdele_1 MEMORY ID zpara1.或者是下面这种写法,根本不用参照zdele_1这个Data Element,因为Data Element中的Parameter ID对选择屏幕参数没有任何意义,所以只需要通过MEMORY ID选项动态的创建Parameter ID即可,不需要先选通过SE80或SM30来事先创建好PARAMETERS: p_spa(10) MEMORY ID zpara1.START-OF-SELECTION. LEAVE TO TRANSACTION 'zsap_mem_2'.
zsap_mem_2事务码对应的报表程序为:
DATA: mem(10).GET PARAMETER ID 'ZPARA1' FIELD mem.
19.3.2. GET/SET PARAMETER ID
除了通过上面MEMORY ID 在同一用户下不同窗口之间自动传递选择屏幕参数外,也可以通过以下语句来手动传递屏幕参数以及非屏幕参数:
REPORT ztest_sap_memory_3.PARAMETERS: p_spa TYPE zdele_1.START-OF-SELECTION. SET PARAMETER ID ’ZPARA1’ FIELD p_spa. LEAVE TO TRANSACTION ’ztest_sap_memory_4’.
使用SE91为下面ztest_sap_memory_4程序创建了Tcode为:ztest_sap_memory_4
REPORT ztest_sap_memory_4.PARAMETERS: p_spa TYPE zdele_1.AT SELECTION-SCREEN OUTPUT." GET PARAMETER ID ’ZPARA1’ FIELD p_spa.
GET/SET PARAMETER ID还可以与对话屏幕Parameter ID一起使用,如通过MM02输入物料后,可以在程序中直接读取:
DATA: material TYPE mara-matnr.GET PARAMETER ID 'MAT' FIELD material.WRITE:/ material.
19.4. DATABASE
SHARED BUFFER并不访问(存储)数据库,而要存放在数据库就应该用DATABASE
EXPORT DATABASE与普通数据库操作的不同之处是,它适合大数据量的操作,系统自动将其拆分成多条记录并存储到数据库中,比如图片或文档(甚至是程序中的某个内表,请参考后面的实例),而用IMPORT DATABASE的过程则相反,系统将把这些条相关记录又自动组合起来成为一个整体。
如果要自定义INDX这样的表,需要按以下表结构顺序来定义:
该数据库表结构要求:
①、可以有也可以无 MANDT字段
②、除开第一个字段MANDT(如果有的情况下),下一个字段必须是RELID,类型为CHAR 2,它是用来存储area ID,系统会根据用户在使用EXPORT语句保存数据时指定的area ID来填充它。
③、紧接下一个字段是一个任意长度(根据自己的需要定)的CHAR字段,名字也可以是随便取的,该字段用为主键的一部分来使用,该字段的值也是在使用EXPORT语句保存数据时使用ID选项指定的值。
④、下一个字段的名字必须是SRTF2,类型为INT4,用来存储数据行号(大数据对象——如图片、文件、程序中的内表对象等,要分成多行来存储)。由于某个数据可能很大,需要多行来存储,理念是可能达到2**31行,该字段会自动的由系统填充。
⑤、在SRTF2字段的后面,你可以包括任意数量及类型的数据字段,这些字段是用来管理大对象的相应信息(如文件名、文件类型、创建者等),当你在保存数据时系统不会自动的填充这些字段,所以在保存这些字段时,需要通过一个结构传递需要存储的值(即EXPORT语句中的From选项所带的结构)。
⑥、倒数第二个字段的名必须为CLUSTR,类型为INT2,它存储了最后一个字段CLUSTD所存储数据的长度(字节数),在使用EXPORT语句保存数据时系统会自动填充
⑦、最后一个字段的名必须是CLUSTD,并且数据类型为LRAW,其长度表示能最大存储多少个字节的内容,如果大数据对象很大(一行存储不下时),会分成多行来存储,行号就存储在前面的SRTF2字段中。
19.4.1. 将文件存入表中
注意:上面这个表中的SRTFD实际上没用上,因为Export时,ID选项的值实质上存储到了它前面的ZZKEY中了,所以可以去掉这个字段(一般会留名为SRTFD字段而去掉ZZKEY字段)。
PARAMETERS: p_file TYPE string OBLIGATORY.AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. CALL FUNCTION 'WS_FILENAME_GET' EXPORTING def_filename = '*.*' def_path = 'c:\' mask = ',*' mode = 'O' title = 'File select' IMPORTING filename = p_file.START-OF-SELECTION. DATA: il_data LIKE solix OCCURS 0 WITH HEADER LINE, l_len TYPE i.**Upload file REFRESH: il_data. CLEAR l_len. CALL FUNCTION 'GUI_UPLOAD' EXPORTING filename = p_file filetype = 'BIN' IMPORTING filelength = l_len TABLES data_tab = il_data."ABAP没有二进制类型,X类型代替 EXPORT il_data TO DATABASE indx(YY) ID 'ZZZ' .
上面是直接将读取到的文件的二进制数据内表存储到簇数据库表中,我也也可通过SCMS_BINARY_TO_XSTRING函数将读取的二进制数据内表拼接成只有一行的二进制串,然后再存储这个被转换后的二进制串也可:
PARAMETERS: p_file TYPE string OBLIGATORY, p_id LIKE ybc_file-zzkey OBLIGATORY, p_ftype LIKE ybc_file-mimetype OBLIGATORY, p_fname LIKE ybc_file-filename OBLIGATORY.AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file. CALL FUNCTION 'WS_FILENAME_GET' EXPORTING def_filename = '*.*' def_path = 'c:\' mask = ',*' mode = 'O' title = 'File select' IMPORTING filename = p_file.START-OF-SELECTION. DATA: il_data LIKE solix OCCURS 0 WITH HEADER LINE, l_len TYPE i.**Upload file REFRESH: il_data. CLEAR l_len. CALL FUNCTION 'GUI_UPLOAD' EXPORTING filename = p_file filetype = 'BIN' IMPORTING filelength = l_len TABLES data_tab = il_data."X类型内表 CHECK il_data[] IS NOT INITIAL.**Convert data DATA: l_xstr TYPE xstring. CLEAR l_xstr. "将内表以X类型拼接成XString字符串 CALL FUNCTION 'SCMS_BINARY_TO_XSTRING' EXPORTING input_length = l_len IMPORTING buffer = l_xstr TABLES binary_tab = il_data.**Save data "wl_file用于填充ybc_file表中非规定字段 DATA: wl_file LIKE ybc_file. wl_file-uname = sy-uname. wl_file-aedtm = sy-datum. wl_file-pgmid = sy-cprog. wl_file-mimetype = p_ftype. wl_file-filename = p_fname. DATA: l_answer TYPE c. EXPORT l_xstr = l_xstr TO DATABASE ybc_file(bc) FROM wl_file ID p_id .
19.4.2. 从表中读取文件
DATA: il_data LIKE solix OCCURS 0.IMPORT il_data FROM DATABASE indx(YY) ID 'ZZZ'.CALL METHOD cl_gui_frontend_services=>gui_download EXPORTING " bin_filesize = l_bytes filename = 'c:\1.jpg' filetype = 'BIN' CHANGING data_tab = il_data.
与存储文件一样,如果存储的是拼接好的二进制串,则要使用SCMS_XSTRING_TO_BINARY函数来还原后再下载:PARAMETERS: p_key LIKE ybc_file-zzkey.DATA: l_xstr TYPE xstring.IMPORT l_xstr = l_xstr FROM DATABASE ybc_file(bc) ID p_key.DATA: l_xstring TYPE xstring, l_xcnt TYPE i, l_bytes TYPE i.TYPES: hex512(512) TYPE x.DATA: tab_xstring TYPE TABLE OF hex512 WITH HEADER LINE."将Xstring以X类型视图存储到内表中CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING buffer = l_xstr TABLES binary_tab = tab_xstring.DATA: l_ftype LIKE yhr_attach-mimetype, l_fname LIKE yhr_attach-filename."二进内容需使用IMPORT语句中读取,但其他字段除了可使用 IMPORT语句的TO选项来直接读取外,还可以通过SQL直接查询SELECT SINGLE mimetype filename INTO (l_ftype,l_fname) FROM ybc_file WHERE relid = 'BC' AND zzkey = p_key.DATA: l_file_name TYPE string.CONCATENATE 'C:\' l_fname '.' l_ftype INTO l_file_name.CALL METHOD cl_gui_frontend_services=>gui_download EXPORTING bin_filesize = l_bytes filename = l_file_name filetype = 'BIN' CHANGING data_tab = tab_xstring[].
19.5. JOB间数据传递
有两种方式:
l SHARED MEMORY/SHARED BUFFER
l 通过Cluster Databases