P类型的数据,可用于精确运算(这里的精确指的是存储中所存储的数据与定义时字面上所看到的大小相同,而不存在精度丢失问题——看到的就是内存中实实在在的大小)。在使用P类型时,要先选择程序属性中的选项 Fixed point arithmetic(即定点算法,一般默认选中),否则系统将P类型看用整型。其效率低于I或F类型。
DATA: p(16) TYPE p DECIMALS 14 VALUE '12345678901234567.89012345678901'.
"正负符号与小数点固定要占用半个字节,一个字面上位置,并包括在这16个字节里面。 "16 * 2 = 32位包括了小数点与在正负号在内 "在定义时字面上允许最长可以达到32位,除去小数点与符号需占半个字节以后 "有效数字位可允许31位,这31位中包括了整数位与小数位,再除去定义时小 "数位为14位外,整数位最多还可达到17位,所以下面最多只能是17个9 DATA: p1(16) TYPE p DECIMALS 14 VALUE '-99999999999999999'.
"P类型是以字符串来表示一个数的,与字符串不一样的是,P类型中的每个数字位只会占用4Bit位,所以两个数字位才会占用一个字节。另外,如果定义时没有指定小数位,表示是整型,但小数点固定要占用半个字节,所以不带小数位与符号的最大与最小整数如下(最多允许31个9,而不是32个) DATA: p1(16) TYPE p VALUE'+9999999999999999999999999999999'. DATA: p2(16) TYPE p VALUE'-9999999999999999999999999999999'.
DATA: p(9) TYPE p DECIMALS 2 VALUE '-123456789012345.12'. WRITE: / p."123456789012345.12-
DATA: f1 TYPE f VALUE '2.0', f2 TYPE f VALUE '1.1', f3 TYPE f. f3 = f1 - f2."不能精确计算 "2.0000000000000000E+00 1.1000000000000001E+00 8.9999999999999991E-01 WRITE: / f1 , f2 , f3.
DATA: p1 TYPE p DECIMALS 1 VALUE '2.0', p2 TYPE p DECIMALS 1 VALUE '1.1', p3 TYPE p DECIMALS 1. p3 = p1 - p2."能精确计算 WRITE: / p1 , p2 , p3. "2.0 1.1 0.9
TYPES type6 TYPE mara-matnr. TYPES type7 LIKE mara-matnr. DATA obj6 TYPE mara-matnr. DATA obj7 LIKE mara-matnr.
"SFLIGHT为表类型 DATA plane LIKE sflight-planetype. DATA plane2 TYPE sflight-planetype. DATA plane3 LIKE sflight. DATA plane4 TYPE sflight. "syst为结构类型 DATA sy1 TYPE syst. DATA sy2 LIKE syst. DATA sy3 TYPE syst-index. DATA sy4 LIKE syst-index.
FIND ALL OCCURRENCES OF REGEX regex IN dobj [MATCHCOUNT mcnt] 成功匹配的次数 {{[MATCH OFFSETmoff][MATCH LENGTH mlen]}最后一次整体匹配到的串(整体串,最外层分组,而不是指正则式最内最后一个分组)起始位置与长度 | [RESULTS result_tab|result_wa] } result_tab接收所有匹配结果,result_wa只能接收最后一次匹配结果 [SUBMATCHES s1 s2 ...].通常与前面的MATCH OFFSET/ LENGTH一起使用。只会接收使用括号进行分组的子组。如果变量s1 s2 ...比分组的数量多,则多余的变量被initial;如果变量s1 s2 ...比分组的数量少,则多余的分组将被忽略;且只存储第一次或最后一次匹配到的结果
replace( val = TEXT REGEX = REGEX WITH = NEW)使用new替换指定的子符串,返回String类型
REPLACE ALL OCCURRENCES OF REGEX regex IN dobj WITH new
1.7.1. count、match结合
DATA: text TYPE string VALUE `Cathy's cat with the hat sat on Matt's mat.`, regx TYPE string VALUE `\<.at\>`."\< 单词开头,\> 单词结尾 DATA: counts TYPE i, index TYPE i, substr TYPE string. WRITE / text. NEW-LINE. counts = count( val = text regex = regx )."返回匹配次数 DO counts TIMES. index = find( val = text regex = regx occ = sy-index )."返回匹配到的的起始位置索引 substr = match( val = text regex = regx occ = sy-index )."返回匹配到的串 index = index + 1. WRITE AT index substr. ENDDO.
1.7.2.FIND …SUBMATCHES
DATA: moff TYPE i, mlen TYPE i, s1 TYPE string, s2 TYPE string, s3 TYPE string, s4 TYPE string. FIND ALL OCCURRENCES OF REGEX`((\w+)\W+\2\W+(\w+)\W+\3)`"\2 \3 表示反向引用前面匹配到的第二与第三个子串 IN `Hey hey, my my, Rock and roll can never die Hey hey, my my`"会匹配二次,但只会返回第二次匹配到的结果,第一次匹配到的子串不会存储到s1、s2、s3中去 IGNORINGCASE MATCH OFFSET moff MATCH LENGTH mlen SUBMATCHES s1 s2 s3 s4."根据从外到内,从左到右的括号顺序依次存储到s1 s2…中,注:只取出使用括号括起来的子串,如想取整体子串则也要括起来,这与Java不同 WRITE: / s1, / s2,/ s3 ,/ s4,/ moff ,/ mlen."s4会被忽略
1.7.3. FIND …RESULTS itab
DATA: result TYPE STANDARD TABLE OF string WITH HEADER LINE . "与Java不同,只要是括号括起来的都称为子匹配(即使用整体也用括号括起来了), "不管括号嵌套多少层,统称为子匹配,且匹配到的所有子串都会存储到, "MATCH_RESULT-SUBMATCHES中,即使最外层的括号匹配到的子串也会存储到SUBMATCHES "内表中。括号解析的顺序为:从外到内,从左到右的优先级顺序来解析匹配结构。 "Java中的group(0)存储的是整体匹配串,即使整体未(或使用)使用括号括起来 PERFORM get_match TABLES result USING '2011092131221032' '(((\d{2})(\d{2}))(\d{2})(\d{2}))'. LOOP AT result . WRITE: / result. ENDLOOP. FORM get_match TABLES p_result"返回所有分组匹配(括号括起来的表达式) USING p_str p_reg. DATA: result_tab TYPE match_result_tab WITH HEADER LINE. DATA: subresult_tab TYPE submatch_result_tab WITH HEADER LINE. "注意:带表头时 result_tab 后面一定要带上中括号,否则激活时出现奇怪的问题 FIND ALL OCCURRENCES OF REGEX p_reg IN p_str RESULTS result_tab[]. "result_tab中存储了匹配到的子串本身(与Regex整体匹配的串,存储在 "result_tab-offset、result_tab-length中)以及所子分组(括号部分,存储在 "result_tab-submatches中) LOOP AT result_tab . "如需取整体匹配到的子串(与Regex整体匹配的串),则使用括号将整体Regex括起来 "来即可,括起来后也会自动存储到result_tab-submatches,而不需要在这里像这样读取 * p_result = p_str+result_tab-offset(result_tab-length). * APPEND p_result. subresult_tab[] = result_tab-submatches. LOOP AT subresult_tab. p_result = p_str+subresult_tab-offset(subresult_tab-length). APPEND p_result. ENDLOOP. ENDLOOP. ENDFORM.
DATA: matcher TYPE REF TO cl_abap_matcher, match TYPE match_result, match_line TYPE submatch_result. "^$可以省略,因为matches方法本身就是完全匹配整个Regex IF cl_abap_matcher=>matches( pattern = '^(db(ai).*)$' text = 'dbaiabd' ) = 'X'. matcher = cl_abap_matcher=>get_object( )."获取最后一次匹配到的 Matcher 实例 match = matcher->get_match( ). "获取最近一次匹配的结果(注:是整体匹配的结果) WRITE / matcher->text+match-offset(match-length). LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分) WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length). ENDLOOP. ENDIF.
DATA: matcher TYPE REF TO cl_abap_matcher, match TYPE match_result, match_line TYPE submatch_result. "^$可以省略,因为matche方法本身就是完全匹配整个Regex matcher = cl_abap_matcher=>create( pattern = '^(db(ai).*)$' text = 'dbaiabd' ). IF matcher->match( ) = 'X'. match = matcher->get_match( ). "获取最近一次匹配的结果 WRITE / matcher->text+match-offset(match-length). LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分) WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length). ENDLOOP. ENDIF.
DATA: matcher TYPE REF TO cl_abap_matcher, match TYPE match_result, match_line TYPE submatch_result. IF cl_abap_matcher=>contains( pattern = '(db(ai).{2}b)' text = 'dbaiabddbaiabb' ) = 'X'. matcher = cl_abap_matcher=>get_object( ). "获取最后一次匹配到的 Matcher 实例 match = matcher->get_match( ). "获取最近一次匹配的结果 WRITE / matcher->text+match-offset(match-length). LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分) WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length). ENDLOOP. ENDIF.
1.7.4.3.find_all
一次性找出所有匹配的子串,包括子分组(括号括起的部分)
DATA: matcher TYPE REF TO cl_abap_matcher, match_line TYPE submatch_result, itab TYPE match_result_tab WITH HEADER LINE. matcher = cl_abap_matcher=>create( pattern = '<[^<>]*(ml)>' text = 'hello' )."创建 matcher 实例 "注:子分组存储在itab-submatches字段里 itab[] = matcher->find_all( ). LOOP AT itab . WRITE: / matcher->text, itab-offset, itab-length,matcher->text+itab-offset(itab-length). LOOP AT itab-submatches INTO match_line. "提取子分组(括号括起来的部分) WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length). ENDLOOP. ENDLOOP.
1.7.4.4.find_next
逐个找出匹配的子串,包括子分组(括号括起的部分)
DATA: matcher TYPE REF TO cl_abap_matcher, match TYPE match_result, match_line TYPE submatch_result, itab TYPE match_result_tab WITH HEADER LINE. matcher = cl_abap_matcher=>create( pattern = '<[^<>]*(ml)>' text = 'hello' ). WHILE matcher->find_next( ) = 'X'. match = matcher->get_match( )."获取最近一次匹配的结果 WRITE: / matcher->text, match-offset, match-length,matcher->text+match-offset(match-length). LOOP AT match-submatches INTO match_line. "提取子分组(括号括起来的部分) WRITE: /20 match_line-offset, match_line-length,matcher->text+match_line-offset(match_line-length). ENDLOOP. ENDWHILE.
1.7.4.5.get_length、get_offset、get_submatch
DATA: matcher TYPE REF TO cl_abap_matcher, length TYPE i,offset TYPE i, submatch TYPE string. matcher = cl_abap_matcher=>create( pattern = '(<[^<>]*(ml)>)' text = 'hello' ). WHILE matcher->find_next( ) = 'X'. "循环2次 "为0时,表示取整个Regex匹配到的子串,这与Java一样,但如果整个Regex使用括号括起来后, "则分组索引为1,这又与Java不一样(Java不管是否使用括号将整个Regex括起来,分组索引号都为0) "上面Regex中共有两个子分组,再加上整个Regex为隐含分组,所以一共为3组 DO 3 TIMES. "在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串长度 length = matcher->get_length( sy-index - 1 ). "在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串起始位置 offset = matcher->get_offset( sy-index - 1 ). "在当前匹配到的串(整个Regex相匹配的串)中返回指定子分组的匹配到的字符串 submatch = matcher->get_submatch( sy-index - 1 ). WRITE:/ length , offset,matcher->text+offset(length),submatch. ENDDO. SKIP. ENDWHILE.
1.7.4.6.replace_all
DATA: matcher TYPE REF TO cl_abap_matcher, count TYPE i, repstr TYPE string. matcher = cl_abap_matcher=>create( pattern = '<[^<>]*>' text = 'hello' ). count= matcher->replace_all( ``)."返回替换的次数 repstr = matcher->text. "获取被替换后的新串 WRITE: / count, repstr.
function fuc_ref . *"------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" REFERENCE(I_I1) TYPE IREFERENCE(别名)为参数的默认传递类型 *" VALUE(I_I2) TYPE I 定义时勾选了Pass Value选项才会是 VALUE类型 *" REFERENCE(I_I3) TYPEREF TO I *" VALUE(I_I4) TYPE REF TO I *" EXPORTING *" REFERENCE(E_I1) TYPE I *" VALUE(E_I2) TYPE I *" REFERENCE(E_I3) TYPE REF TO I *" VALUE(E_I4) TYPE REF TO I *" TABLES *" T_1 TYPE ZJZJ_ITAB *" CHANGING *" REFERENCE(C_I1) TYPE I *" VALUE(C_I2) TYPE I *" REFERENCE(C_I3) TYPE REF TO I *" VALUE(C_I4) TYPE REF TO I *"------------------------------------------------------------------- write: / i_i1."1 "由于i_i1为输入类型参数,且又是引用类型,实参不能被修改。这里i_i1是以C++中的引用(别名)参数方式传递参数,所以如果修改了i_i1就会修改实际参数,所以函数中不能修改REFERENCE 的 IMPORTING类型的参数,如果去掉下面注释则编译出错 "i_i1 = 10.
TYPE REF TO 后面可接的通用类型只能是data(数据引用)或者是object(对象引用)通用类型,其他通用类型不行
1.12.1.数据引用Data References
DATA: dref TYPE REF TO i ."dref即为数据引用,即数据指针,指向某个变量或常量,存储变量地址 CREATE DATAdref. dref->* = 2147483647."可直接解引用使用,不需要先通过分配给字段符号后再使用
DATA: BEGIN OF strct, c, END OF strct. DATA: dref LIKE REF TO strct . CREATE DATA dref . dref->*-c = 'A'.
TYPES: tpy TYPE c. DATA: c1 TYPE REF TO tpy. DATA: c2 LIKE REF TO c1."二级指针 GET REFERENCE OF 'a' INTO c1. GET REFERENCE OF c1 INTO c2. WRITE: c2->*->*."a
1.12.2.对象引用Object references
CLASScl DEFINITION. PUBLIC SECTION. DATA: i VALUE 1. ENDCLASS. CLASScl IMPLEMENTATION. ENDCLASS.
DATA: objTYPE REF TO cl. CREATE OBJECT obj. "创建对象
DATA: oref LIKE REF TO obj. "oref即为对象引用,即对象指针,指向某个对象,存储对象地址 GET REFERENCE OFobj INTO oref. "获取对象地址 WRITE: oref->*->i."1
1.12.3.GET REFERENCE OF获取变量/对象/常量地址
DATA: e_i3 TYPE REF TO i . GET REFERENCE OF 33 INTO e_i3. WRITE: e_i3->*."33 "但不能修改常量的值 "e_i3->* = 44.
DATA: i TYPE i VALUE 33, dref LIKE REF TO i."存储普通变量的地址 GET REFERENCE OF i INTO dref. dref->* = 44. WRITE: i. "44
DATA:str(20) TYPE c VALUE 'Output String', name(20) TYPE c VALUE 'STR'.
"静态分配:编译时就知道要分配的对象名 ASSIGN name TO."结果是与name变量等同
"通过变量名动态访问变量 ASSIGN (name) TO ."结果是是的值为str变量值
DATA: BEGIN OF line, col1 TYPE i VALUE '11', col2 TYPE i VALUE '22', col3 TYPE i VALUE '33', END OF line. DATA comp(5) VALUE 'COL3'. FIELD-SYMBOLS: , , . ASSIGN line TO . ASSIGN comp TO .
"还可以直接使用以下的语法访问其他程序中的变量 ASSIGN ('(ZJDEMO)SBOOK-FLDATE') TO .
"如果定义的内表没有组件名时,可以使用索引为0的组件来访问这个无名字段(注:不是1) ASSIGN COMPONENT0OF STRUCTUREitab TO .
1.13.5.1.动态访问类的属性成员
ASSIGN oref->('attr') TO . ASSIGN oref->('static_attr') TO . ASSIGN ('C1')=>('static_attr') TO . ASSIGN c1=>('static_attr') TO . ASSIGN ('C1')=>static_attr TO .
DATA: oref1 TYPE REF TO object. DATA: descr_ref1 TYPE REF TO cl_abap_typedescr. CREATE OBJECT oref1 TYPE ('C1'). "C1为类名 descr_ref1 = cl_abap_typedescr=>describe_by_object_ref( oref1 ).
DATA: dref TYPE REF TO data, c10type TYPE REF TO cl_abap_elemdescr. c10type = cl_abap_elemdescr=>get_c( 10 ). CREATE DATA dref TYPE HANDLE c10type.
DATA: x20type TYPE REF TO cl_abap_elemdescr. x20type = cl_abap_elemdescr=>get_x( 20 ). FIELD-SYMBOLS: TYPE any. ASSIGN dref->* TO CASTING TYPE HANDLE x20type.
1.14.2.动态创建数据Data或对象Object
TYPES: ty_i TYPE i. DATA: dref TYPE REF TO ty_i . CREATE DATA dref TYPE ('I')."根据基本类型名动态创建数据 dref->* = 1. WRITE: / dref->*." 1
CREATE OBJECT oref TYPE ('C1')."根据类名动态创建实例对象
1.14.3.动态创建基本类型变量、结构、内表
DATA: dref_str TYPE REF TO data, dref_tab TYPE REF TO data, dref_i TYPE REF TO data, itab_type TYPE REF TO cl_abap_tabledescr, struct_type TYPE REF TO cl_abap_structdescr, elem_type TYPE REF TO cl_abap_elemdescr, table_type TYPE REF TO cl_abap_tabledescr, comp_tab TYPE cl_abap_structdescr=>component_table WITH HEADER LINE. FIELD-SYMBOLS : TYPE ANY TABLE.
**=========动态创建基本类型 elem_type ?= cl_abap_elemdescr=>get_i( ). CREATE DATA dref_i TYPE HANDLE elem_type ."动态的创建基本类型数据对象
**=========动态创建结构类型 struct_type ?= cl_abap_typedescr=>describe_by_name( 'SFLIGHT' )."结构类型 comp_tab[] = struct_type->get_components( )."组成结构体的各个字段组件 * 向结构中动态的新增一个成员 comp_tab-name = 'L_COUNT'."为结构新增一个成员 comp_tab-type = elem_type."新增成员的类型对象 INSERT comp_tab INTO comp_tab INDEX 1. * 动态创建结构类型对象 struct_type = cl_abap_structdescr=>create( comp_tab[] ). CREATE DATA dref_str TYPE HANDLE struct_type."使用结构类型对象来创建结构对象
**=========动态创建内表 * 基于结构类型对象创建内表类型对象 itab_type = cl_abap_tabledescr=>create( struct_type ). CREATE DATA dref_tab TYPE HANDLE itab_type."使用内表类型对象来创建内表类型 ASSIGN dref_tab->* TO ."将字段符号指向新创建出来的内表对象
CLASS c1 DEFINITION. PUBLIC SECTION. DATA: c VALUE 'C'. METHODS: test. ENDCLASS.
CLASS c1 IMPLEMENTATION. METHOD:test. WRITE:/ 'test'. ENDMETHOD. ENDCLASS. START-OF-SELECTION. TYPES: ty_c. DATA: oref TYPE REF TO object . DATA: oref_classdescr TYPE REF TO cl_abap_classdescr . CREATE OBJECT oref TYPE ('C1')."根据类名动态创建实例对象 "相当于Java中的Class类对象 oref_classdescr ?= cl_abap_classdescr=>describe_by_object_ref( oref ).
DATA: t_attrdescr_tab TYPE abap_attrdescr_tab WITH HEADER LINE,"类中的属性列表 t_methdescr_tab TYPE abap_methdescr_tab WITH HEADER LINE."类中的方法列表 FIELD-SYMBOLS TYPE any. t_attrdescr_tab[] = oref_classdescr->attributes. t_methdescr_tab[] = oref_classdescr->methods. LOOP AT t_attrdescr_tab."动态访问类中的属性 ASSIGN oref->(t_attrdescr_tab-name)TO . WRITE: / . ENDLOOP. LOOP AT t_methdescr_tab."动态访问类中的方法 CALL METHOD oref->(t_methdescr_tab-name). ENDLOOP.
2.面向对象
2.1. 类与接口定义
CLASS class DEFINITION [ABSTRACT][FINAL]. [PUBLIC SECTION. [components]] [PROTECTED SECTION. [components]] [PRIVATE SECTION. [components]] ENDCLASS.
INTERFACE intf. [components] ENDINTERFACE.
2.1.1. components
²TYPES, DATA, CLASS-DATA, CONSTANTS for data types and data objects
²METHODS, CLASS-METHODS, EVENTS, CLASS-EVENTSfor methods and events
² INTERFACES(如果在类中,表示需要实现哪个接口;如果是在接口中,表示继承哪个接口) for implementing interfaces
² ALIASESfor alias names for interface components给接口组件取别名
2.2.类定义、实现
CLASS math DEFINITION. PUBLIC SECTION. METHODS divide_1_by IMPORTING operand TYPE i EXPORTING result TYPE f RAISING cx_sy_arithmetic_error. ENDCLASS. CLASS math IMPLEMENTATION. METHOD divide_1_by. result = 1 / operand. ENDMETHOD. ENDCLASS.
此种方式仅适用于只有一个必选输入参数(IMPORTING)(如果还有其他输入参数,则其他都为可选,或者不是可选时但有默认值也可),或者是有多个可选输入参数(IMPORTING)(此时没有必选输入参数情况下)的情况下但方法声明时通过使用PREFERRED PARAMETER选项指定了其中某个可选参数为首选参数(首选参数即在使用meth( a )方式传递一个参数进行调用时,通过实参a传递给设置为首选的参数)
CLASS c1 DEFINITION. PUBLIC SECTION. EVENTS e1 EXPORTING value(p1) TYPE string value(p2) TYPE i OPTIONAL. "定义 METHODS m1. ENDCLASS. CLASS c1 IMPLEMENTATION. METHOD m1. RAISE EVENT e1 EXPORTING p1 = '...'."触发 ENDMETHOD. ENDCLASS.
2.8.3.事件处理器Event Handler
静态或非静态事件处理方法都可以处理静态或非静态事件,与事件的静态与否没有直接的关系
INTERFACE window. "窗口事件接口 EVENTS: minimize EXPORTINGVALUE(status) TYPE i."最小化事件 ENDINTERFACE.
CLASS dialog_window DEFINITION. "窗口事件实现 PUBLIC SECTION. INTERFACES window. ENDCLASS.
INTERFACE window_handler. "窗口事件处理器接口 METHODS: minimize_window FOR EVENT window~minimize OF dialog_window IMPORTING status sender. "事件处理器方法参数要与事件接口定义中的一致 ENDINTERFACE.
2.8.4. 注册事件处理器
实例事件处理器(方法)注册(注:被注册的方法只能是用来处理非静态事件的方法):
SET HANDLER handler1 handler2 ... FOR oref|{ALL INSTANCES}[ACTIVATION act].
静态事件处理器(方法)注册(注:被注册的方法只能是用来处理静态事件的方法):
SET HANDLER handler1 handler2 ... [ACTIVATION act].
oref:只将事件处理方法handler1 handler2注册到 oref 这一个事件源对象
ALL INSTANCES:将事件处理方法注册到所有的事件源实例中
ACTIVATION act:表示是注册还是注销
2.8.5. 示例
CLASS c1 DEFINITION."事件源 PUBLIC SECTION. EVENTS: e1 EXPORTING value(p1) TYPE c,e2. CLASS-EVENTS ce1 EXPORTING value(p2) TYPE i. METHODS:trigger."事件触发方法 ENDCLASS.
可以在循环内表时增加与删除当前行:If you insert or delete lines in the statement block of a LOOP , this will have the following effects:
If you insert lines behind(后面) the current line, these new lines will be processed in the subsequent loop(新行会在下一次循环时被处理) passes. An endless loop(可能会引起死循环)can result
If you delete lines behind the current line, the deleted lines will no longer be processed in the subsequent loop passes
If you insert lines in front(前面) of the current line, the internal loop counter is increased by one with each inserted line. This affects sy-tabix in the subsequent loop pass(这会影响在随后的循环过程SY-TABIX)
If you delete lines in front of the current line, the internal loop counter is decreased by one with each deleted line. This affects sy-tabix in the subsequent loop pass
3.1.1.循环中删除行
DATA : BEGIN OF gt_table OCCURS 0,
c,
END OF gt_table.
APPEND 'a' TO gt_table.
APPEND 'b' TO gt_table.
APPEND 'c' TO gt_table.
APPEND 'd' TO gt_table.
APPEND 'e' TO gt_table.
APPEND 'f' TO gt_table.
APPEND 'g' TO gt_table.
APPEND 'h' TO gt_table.
LOOP AT gt_table .
IF gt_table-c = 'b' OR gt_table-c = 'c' OR gt_table-c = 'e'.
WRITE : / sy-tabix COLOR = 6 INTENSIFIED ON INVERSE OFF ,
gt_table COLOR = 6 INTENSIFIED ON INVERSE OFF .
ELSE.
WRITE : / sy-tabix, gt_table.
ENDIF.
ENDLOOP.
SKIP 2.
DATA count TYPE i .
LOOP AT gt_table .
count = count + 1.
"当循环到第三次时删除,即循环到 C 时进行删除
IF count = 3.
DELETE gt_table WHERE c = 'b' OR c = 'c' OR c = 'e'.
ENDIF.
"删除之后sy-tabix会重新开始对内表现有的行进行编号
WRITE :/ sy-tabix, gt_table.
ENDLOOP.
SKIP 2.
LOOP AT gt_table .
WRITE : / sy-tabix, gt_table.
ENDLOOP.
3.1.2.SUM
如果在 AT - ENDAT 块中使用 SUM,则系统计算当前行组中所有行的数字字段之和并将其写入工作区域中相应的字段中
3.1.3. AT...ENDAT
含义
FIRST
内表的第一行时触发
LAST
内表的最后一行时触发
NEW
相邻数据行中相同字段构成一组,在循环到该组的开头时触发
END Of
相邻数据行中相同字段构成一组,在循环到该组的最末时触发
在使用AT...... ENDAT之前,一这要先按照这些语句中的组件名进行排序,且排序的顺序要与在AT...... ENDAT语句中使用顺序一致,排序与声明的顺序决定了先按哪个分组,接着再按哪个进行分组,最后再按哪个进行分组,这与SQL中的Group By 相似
用在AT...... ENDAT语句中的中的组件名不一定要是结构中的关键字段,但这些字段一定要按照出现在AT关键字后面的使用顺序在结构最前面进行声明,且这些组件字段的声明之间不能插入其他组件的声明。如现在需要按照, , ....多个字段的顺序来使用在AT...... ENDAT语句中,则首先需要在结构中按照, , ....,多字段的顺序在结构最前面都声明,然后按照, , ....,多字段来排序的,最后在循环中按如下的顺序块书写程序(请注意书写AT END OF的顺序与AT NEW 是相反的,像下面这样):
LOOP AT .
AT FIRST. ... ENDAT.
AT NEW . ...... ENDAT.
AT NEW 2>. ...... ENDAT.
.......
.......
AT END OF .... ENDAT.
AT END OF . ... ENDAT.
AT LAST. .... ENDAT.
ENDLOOP.
一旦进入到 AT......ENDAT 块中时,当前工作区(或表头)中的从往后,但不包括(按照在结构中声明的次序)所有字段的字符类型字段会以星号(*)号来填充,而数字字设置为初始值(注:在测试过程中发现String类型不会使用*来填充,而是设置成empty String,所以只有固定长度类型的非数字基本类型才设置为*)。如果在 AT 块中使用了SUM,则会将所有数字类型字段统计出来将存入当前工作区(或表头);但一旦离开AT....ENDAT块后,又会将当前遍历的行恢复到工作区(或表头)中
DATA: BEGINOF th_mseg OCCURS10, matnr TYPE mard-matnr, werks TYPE mard-werks, lgort TYPE mard-lgort, shkzgTYPE mseg-shkzg, menge TYPE mseg-menge, budat TYPE mkpf-budat, LOOPAT th_mseg. ATENDOF shkzg."会根据shkzg及前面所有字段来进行分组 sum. WRITE: / th_mseg-matnr, th_mseg-werks,th_mseg-lgort, th_mseg-shkzg,th_mseg-menge,th_mseg-budat. ENDAT. ENDLOOP.
AS-101 2300 0001 S 10.000 ****.**.**
AS-100 2300 0002 S 10.000 ****.**.**
AS-100 2300 0001 S 20.000 ****.**.**
上面由于没有根据matnr + werks + lgort + shkzg 进行排序,所以结果中的第三行其实应该与第一行合并。其实这个统计与SQL里的分组(Group By)统计原理是一样的,Group By 后面需要明确指定分组的字段,如上面程序使用SQL分组写法应该为 Group Bymatnr werks lgort shkzg,但在ABAP里你只需要按照 matnr werks lgort shkzg按照先后顺序在结构定义的最前面进行声明就可表达了Group By那种意义,而且不一定要将matnr werks lgort shkzg这四个字段全部用在AT语句块中AT NEW、AT END OF shkzg 才正确,其实像上面程序一样,只写AT END OF shkzg这一个语句,前面三个字段matnr werks lgort都可以不用在AT语句中出现,因为ABAP默认会按照结构中声明的顺序将shkzg前面的字段也全都用在了分组中了
DATA: BEGIN OF line, "C2、C3组件名声明的顺序一定要与在AT...... ENDAT块中使用的次序一致,即这里不能将C3声明在C2之前,且不能在C2与C3之间插入其他字段的声明 c2(5) TYPE c, c3(5) TYPE c, c4(5) TYPE c, i1 TYPE i, i2 TYPE i, c1(5) TYPE c, END OF line.
"使用在AT...... ENDAT语句中的字段不一定要是关键字段 DATA: itab LIKE TABLE OF line WITH HEADER LINE WITH NON-UNIQUE KEY i1. PERFORM append USING 2 'b' 'bb' 'bbb' '2222' 22.PERFORM append USING 3 'c' 'aa' 'aaa' '3333' 33. PERFORM append USING 4 'd' 'aa' 'bbb' '4444' 44.PERFORM append USING 5 'e' 'bb' 'aaa' '5555' 55. PERFORM append USING 6 'f' 'bb' 'bbb' '6666' 66.PERFORM append USING 7 'g' 'aa' 'aaa' '7777' 77. PERFORM append USING 8 'h' 'aa' 'bbb' '8888' 88. SORTitab ASCENDING BYc2 c3. LOOP AT itab. WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2. ENDLOOP. SKIP. LOOP AT itab. AT FIRST. WRITE:/ '>>>> AT FIRST'. ENDAT. AT NEWc2. WRITE: / ' >>>> Start of' , itab-c2. ENDAT. AT NEW c3. WRITE: / ' >>>> Start of' , itab-c2, itab-c3. ENDAT. "只要一出 AT 块,则表头的数据又会恢复成当前被遍历行的内容 WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2. AT END OF c3. SUM. WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2. WRITE: / ' <<<< End of' , itab-c2, itab-c3. ENDAT. AT END OF c2. SUM. WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2. WRITE: / ' <<<< End of' , itab-c2. ENDAT. AT LAST. SUM. WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2. WRITE:/ '<<<< AT LAST'. ENDAT. ENDLOOP. TYPES: c5(5) TYPE c. FORM append USING value(p_i1) TYPE Ivalue(p_c1) TYPE c5 value(p_c2) TYPE c5 value(p_c3) TYPE c5 value(p_c4) TYPE c5 value(p_i2) TYPE i. itab-i1 = p_i1. itab-c1 = p_c1. itab-c2 = p_c2. itab-c3 = p_c3. itab-c4 = p_c4. itab-i2 = p_i2. APPEND itab. ENDFORM.
aa aaa c 3333 3 33
aa aaa g 7777 7 77
aa bbb d 4444 4 44
aa bbb h 8888 8 88
bb aaa a 1111 1 11
bb aaa e 5555 5 55
bb bbb b 2222 2 22
bb bbb f 6666 6 66
>>>> AT FIRST
>>>> Start of aa
>>>> Start of aa aaa
aa aaa c 3333 3 33
aa aaa g 7777 7 77
aa aaa ***** ***** 10 110
<<<< End of aa aaa
>>>> Start of aa bbb
aa bbb d 4444 4 44
aa bbb h 8888 8 88
aa bbb ***** ***** 12 132
<<<< End of aa bbb
aa ***** ***** ***** 22 242
<<<< End of aa
>>>> Start of bb
>>>> Start of bb aaa
bb aaa a 1111 1 11
bb aaa e 5555 5 55
bb aaa ***** ***** 6 66
<<<< End of bb aaa
>>>> Start of bb bbb
bb bbb b 2222 2 22
bb bbb f 6666 6 66
bb bbb ***** ***** 8 88
<<<< End of bb bbb
bb ***** ***** ***** 14 154
<<<< End of bb
***** ***** ***** ***** 36 396
<<<< AT LAST
3.1.4.自已实现AT...ENDAT
如果循环的内表不是自己定义的,有时无法将分组的字段按顺序声明在一起,所以需要自己实现这些功能,下面是自己实现AT NEW与AT END OF(另一好处是在循环内表时可以使用Where条件语句)(注:使用这种只需要按照分组的顺序排序即可,如要分成bukrs与bukrs anlkl两组时,需要按照BY bukrs anlkl排序,而不能是BYanlkl bukrs):
DATA: lp_bukrs TYPE bukrs, "上一行bukrs字段的值 lp_anlkl TYPE anlkl. "上一行anlkl字段的值
"下面假设按bukrs,bukrs anlkl分成两组 SORT itab_data BY bukrsanlkl. DATA: i_indx TYPE i .
DATA: lwa_data Like itab_data LOOP AT itab_data where flg = 'X'.
i_indx = sy-tabix. "**********AT NEW 对当前分组首行进行处理
IF itab_data-bukrs <> lp_bukrs. "Bukrs组 "......... ENDIF. IF itab_data-bukrs <> lp_bukrs OR itab_data-anlkl <> lp_anlkl. "bukrs anlkl 分组 "......... ENDIF. IF itab_data-bukrs <> lp_bukrs OR itab_data-anlkl <> lp_anlkl OR itab_data-.. <> lp_.. . "bukrs anlkl .. 分组 "......... ENDIF.
TYPES: BEGIN OF line, key, val TYPE i , END OF line. DATA: itab1 TYPE line OCCURS 0 WITH HEADER LINE . DATA: itab2 TYPE line OCCURS 0 WITH HEADER LINE . itab1-key= 1. itab1-val = 1. APPEND itab1. itab2 = itab1. APPEND itab2. itab1-key= 2. itab1-val = 2. APPEND itab1. itab2 = itab1. APPEND itab2.
LOOP AT itab1. WRITE: / 'itab1 index: ' , sy-tabix. READ TABLE itab2 INDEX 1 TRANSPORTING NO FIELDS."试着读取其他内表 "READ TABLE itab1 INDEX 1 TRANSPORTING NO FIELDS."读取本身也不会影响后面的 MODIFY 语句 WRITE: / 'itab2 index: ', sy-tabix. itab1-val = itab1-val + 1. "在循环中可以使用下面简洁方法来修改内表,修改的内表行为当前正被循环的行,即使循环中使用了 "READ TABLE语句读取了其他内表(读取本身也没有关系)而导致了sy-tabix 发生了改变,因为以下 "语句不是根据sy-tabix来修改的(如果在前面读取内表导致sy-tabix 发生了改变发生改变后,再使用
"MODIFY itab1 INDEX sy-tabix语句进行修改时,反而不正确。而且该语句还适用于Hash内表,需在 "MODIFY后面加上TABLE关键字后再适用于Hash表——请参见后面章节示例)
MODIFY itab1. ENDLOOP. LOOP AT itab1. WRITE: / itab1-key,itab1-val. ENDLOOP.
3.2.2. 循环中修改HASH表
TYPES: BEGIN OF line, key, val TYPE i, END OF line. DATA: itab1 TYPE HASHED TABLE OF line WITH HEADER LINE WITH UNIQUE KEY key. DATA: itab2 TYPE line OCCURS 0 WITH HEADER LINE . itab1-key= 1. itab1-val = 1. INSERT itab1 INTO TABLE itab1. itab2 = itab1. APPEND itab2. itab1-key= 2. itab1-val = 2. INSERT itab1 INTO TABLE itab1. itab2 = itab1. APPEND itab2.
"不管是Sort,还是Hash 表,只要是 Unique Key,且从数据库查出来的数据存在重复,则会Dump掉 *SELECT mandt bukrs INTO TABLE lt_hash FROM t001 CLIENT SPECIFIED WHERE mandt BETWEEN '100' AND'999'. *SELECT mandt bukrs INTO TABLE lt_sort FROM t001 CLIENT SPECIFIED WHERE mandt BETWEEN '100' AND'999'.
SELECT vbeln posnr pstyv werks matnr arktx lgort waerk kwmeng FROM vbap INTO TABLE gt_so FOR ALL ENTRIES IN lt_matnr WHERE matnr = lt_matnr-matnr AND vbeln IN s_vbeln AND posnr IN s_posnr.
如果上面的lt_matnr为空,则“AND vbeln IN s_vbeln AND posnr IN s_posnr”条件也会忽略掉,即整个Where都会被忽略掉。
SELECT matnr FROM mara INTO CORRESPONDING FIELDS OF TABLE strc FOR ALL ENTRIES IN strc WHERE matnr = strc-matnr .
生成的SQL语句:SELECT "MATNR" FROM "MARA" WHERE "MANDT" = '210' AND "MATNR" IN ( '000000000000000101' , '000000000000000103' , '000000000000000104' )
注:这里看上去FOR ALL ENTRIES使用 IN 表达式来代替了,这是只有使用到内表中一个条件是这样的,如果使用多个条件时,不会使用In表达式,而是使用OR连接,像这样:
另外,在使用FOR ALL ENTRIES时,不管使用了条件内表中的一个还是多个条件字段,都会以5个值为单位进行SQL发送
TYPES: line_type TYPE c LENGTH 72. DATA: column_syntax TYPE TABLE OF line_type . APPEND 'CARRID' TO column_syntax. APPEND 'CITYFROM CITYTO' TO column_syntax.
SELECT ... FROM (dbtab_syntax)...
PARAMETERS: p_cityfr TYPE spfli-cityfrom, p_cityto TYPE spfli-cityto. DATA: BEGIN OF wa, fldate TYPE sflight-fldate, carrname TYPE scarr-carrname, connid TYPE spfli-connid, END OF wa. DATA itab LIKE SORTED TABLE OF wa WITH UNIQUE KEY fldate carrname connid. DATA: column_syntax TYPE string, dbtab_syntax TYPE string. column_syntax = `c~carrname p~connid f~fldate`. dbtab_syntax = `( ( scarr AS c ` & ` INNER JOIN spfli AS p ON p~carrid = c~carrid` & ` AND p~cityfrom = p_cityfr` & ` AND p~cityto = p_cityto )` & ` INNER JOIN sflight AS f ON f~carrid = p~carrid ` & ` AND f~connid = p~connid )`. SELECT (column_syntax) FROM (dbtab_syntax) INTO CORRESPONDING FIELDS OF TABLE itab.
SELECT ... WHERE (cond_syntax) ... SELECT ... WHERE AND/OR (cond_syntax) ...
DATA: cond(72) TYPE c, itab LIKE TABLE OF cond. APPEND 'cityfrom = ''NEW YORK''' TO itab. APPEND 'or cityfrom = ''SAN FRANCISCO''' TO itab. SELECT * INTO TABLE itab_spfli FROM spfli WHERE (itab).
DATA:cond1(72) TYPE c VALUE 'cityfrom = ''NEW YORK''', cond2(72) TYPE c VALUE 'cityfrom = ''SAN FRANCISCO'''. SELECT * INTO TABLE itab_spfli FROM spfli WHERE (cond1) OR (cond2).
DATA: cond(72) TYPE c, cond1(72) TYPE c VALUE 'cityfrom = ''NEW YORK''', itab LIKE TABLE OF cond. APPEND 'cityfrom = ''SAN FRANCISCO''' TO itab. SELECT * INTO TABLE itab_spfli FROM spfli WHERE(itab)OR (cond1).
DATA: cond(72) TYPE c, itab LIKE TABLE OF cond. APPEND 'cityfrom = ''SAN FRANCISCO''' TO itab. SELECT * INTO TABLE itab_spfli FROM spfli WHERE(itab)OR cityfrom ='NEW YORK'
SELECT * FROM sflight INTO wa_sflight WHERE seatsocc = ( SELECT MAX( seatsocc ) FROM sflight ). ENDSELECT.
操作符可以是:=、<>、<、<=、>、>=
4.7.1.1.ALL、ANY、SOME
如果子查询返回的是多条,则使用ALL、ANY、SOME来修饰
SELECT customid COUNT( * ) FROM sbook INTO (id, cnt) GROUP BY customid HAVING COUNT( * ) >= ALL ( SELECT COUNT( * ) FROM sbook GROUP BY customid ). ENDSELECT.
² ALL:主查询数据大于所有子查询返回的行数据时,才为真
² ANY|SOME:主查询数据只要大于任何一条子查询返回的行数据时,才为真
² = ANY|SOME:等效IN子查询
4.7.2.[NOT] IN子查询
此类子查询SELECT中也只有单独的一列选择列,但查询出的结果可能有多条
SELECTSINGLE city latitude longitude INTO (city, lati, longi) FROM sgeocity WHEREcity IN( SELECT cityfrom FROM spfli WHERE carrid = carr_id AND connid = conn_id ).
SELECT MIN( price ) AS mINTO price FROM sflight GROUP BY carrid HAVINGMAX(price)>10. Having从句中比较统计结果时,需要将统计函数重写一遍,而不能使用Select中定义的别名 ENDSELECT.
4.10.游标
DATA: cTYPEcursor.[ˈkɜ:sə] DATA: wa TYPE spfli. "1、打开游标 OPEN CURSOR: cFORSELECT carrid connid FROM spfli WHERE carrid = 'LH'. DO. "2、读取数据 FETCH NEXT CURSORcINTOCORRESPONDINGFIELDSOF wa. IF sy-subrc <> 0. "3、关闭游标 CLOSE CURSORc. EXIT. ELSE. WRITE: / wa-carrid, wa-connid. ENDIF. ENDDO.
4.11.三种缓存
l 单记录缓存:从数据库中仅读取一条数据并存储到table buffer 中。此缓存只对SELECT SINGLE…语句起作用
l 部分缓存:需要在指定generic key(即关键字段组合,根据哪些关键字段来缓存,可以是部分或全部关键字段)。如果主键是由一个字段构成,则不能选择此类型缓存。当你使用generic key进行数据访问时,则属于此条件范围的整片数据都会被加载到table buffer中
DB LUW(Logic Unit Work)是确保数据库更新一致性的机制,是数据库级别的,和底层DBMS有关,和SAP系统无关。如下图,从一致性状态A到B,中间有一系列的数据库操作,一个BD luw以数据库提交commit结束,这些操作要么全都执行,要么全都不执行。当全部执行成功,则数据库进入一致性状态B,如果在此DB luw中发生错误,则将从DB luw开始的所有操作进行回滚,数据库还是在A状态。
这是在数据库级别实现的,和SAP系统无关。
在SAP系统中,DB luw commit and rollback 可以被显式或隐式的触发。
不管是commit还是rollback,结束了一个DB luw,也即是开始了一个新的DB luw。
5.1.1.显式提交
·Native SQL提交语句
·Calling the function module DB_COMMIT.
·COMMIT WORK语句
5.1.2.隐式提交
·对话屏幕结束时(跳转另一屏幕)
·同步或异步方式远程调用RFC时:
CALL FUNCTION func DESTINATION dest
CALL FUNCTION func STARTING NEW TASKDESTINATION dest taskname
但RFC事务调用不会触发隐式提交:CALL FUNCTION func IN BACKGROUND TASK DESTINATION dest
·取RFC异步执行结果回调Form中RECEIVE 语句:CALL FUNCTION rfm_name STARTING NEW TASK DESTINATION dest tasknamePERFORMING return_form ON END OF TASK中的RECEIVERESULTSFROM FUNCTION rfm_name语句会触发
·WAIT UNTILlog_exp [UP TOsec SECONDS]语句
·Sending error messages(E), information messages(I), and warnings(W).
5.1.3. 显示回滚
·使用Native-SQL进行回滚
·使用ROLLBACK WORK.进行回滚
5.1.4. 隐式回滚
·runtime error:在程序执行过程中未捕获的异常以及message type X的消息,因为runtime error会导致程序的termination
SAP LUW可以跨多个DB LUW,是一个业务逻辑上的概念,可人为的去定义开始与结束,而DB LUW是不能人为控制的
SAP LUW是DB LUW的一个增强,受体系结构限制,SAP程序每次屏幕切换时(控制权从后台DIALOG进程转移到前台GUI的Session),都会触发一个隐式的数据库提交,一个程序在运行是会产生多个DB 的LUW,这样无法做到全部提交或全部回滚,在某些业务场景下,这种事务的提交机制不足以保证数据的一致性,为此有了SAP LUW机制。
SAP LUW是一种延迟执行的技术,它将本来需要执行的程序块,记录下来(通过特定的方式来调用来实现SAP LUW的绑定:perform XXX on commit、update Funciton module),待系统在执行COMMIT WORK的时候会查询记录,真正执行需要运行的代码。COMMIT WORK一般在最后一个屏幕执行,这样就实现了将跨屏幕的数据更新逻辑绑定到一个DBLUW中(在后台会自动将这一系列的数据操作绑定在同一个数据事务DBLUW里,这样当提交出问题后,数据库事务DBLUW会自动回滚,这样就保证了数据的一致性),实现复杂情况数据更新的一致性
5.2.1.SAP LUW的绑定方式
5.2.1.1.Function
可以使用CALLFUNCTION update_function IN UPDATE TASK将多个数据更新绑定到一个database LUW中
CALL FUNCTION ... IN UPDATE TASK的Function并不会立即执行,这只是先将它记录到内存(本地方式)或数据库表中(非本地方式),等到COMMIT WORK语句执行时才真正执行。如果在程序执行过程中,没有在Update Function 后面执行COMMIT WORK,则function module不会执行
增加一个单值输入条件框(PARAMETERS语句一般在LDB中只用于除节点表外的非表字段屏幕参数),在PARAMETERS语句中必须使用选项FOR NODE XXX 或者 FOR TABLE XXX 来指定这些扩展参数属性哪个节点的:PARAMETERS CITYTO LIKE SPFLI-CITYTO FOR NODE SPFLI.
注:SELECT-OPTIONS没有FOR NODE这样的用法
具体请参数后面的LDB选择屏幕章节
6.3.2.SELECTION-SCREEN格式化屏幕
使用SELECTION-SCREEN语句来格式化屏幕
具体请参数后面的LDB选择屏幕章节
6.3.3.DYNAMIC SELECTIONS动态选择条件
SELECTION-SCREEN DYNAMIC SELECTIONS FOR NODE|TABLE .用来开启节点的LDB dynamic selections功能,即可以在WHERE从句中使用动态选择条件(形如:…WHERE field1 = value1 AND (条件内表) …只有开启了动态选择条件功能的表,才可以在LDB数据库程序中对表进行动态选择条件处理。下面是数据库程序中如何使用动态选择条件示例:
TYPE-POOL RSDS . TYPES: RSDS_WHERE_TAB LIKE RSDSWHERE OCCURS 5."RSDSWHERE 类型为C(72) TYPES: BEGIN OF RSDS_WHERE, TABLENAME LIKE RSDSTABS-PRIM_TAB, WHERE_TAB TYPE RSDS_WHERE_TAB, END OF RSDS_WHERE. TYPES:RSDS_TWHERE TYPE RSDS_WHERE OCCURS 5.
TYPES: BEGIN OF RSDS_TYPE, CLAUSESTYPE RSDS_TWHERE, TEXPR TYPE RSDS_TEXPR, TRANGETYPE RSDS_TRANGE, END OF RSDS_TYPE.
FORM PUT_SCARR. STATICS: DYNAMIC_RANGES TYPE RSDS_RANGE, "存储某个表的所有屏幕字段的Ranges DYNAMIC_RANGE1 TYPERSDS_FRANGE,"存储某个屏幕字段的Ranges DYNAMIC_RANGE2 TYPE RSDS_FRANGE, FLAG_READ."确保DYN_SEL只读取一次 IF FLAG_READ = SPACE. DYNAMIC_RANGES-TABLENAME = 'SCARR'. "先取出 SCARR 表的所有屏幕字段的Ranges READ TABLE DYN_SEL-TRANGEWITH KEY DYNAMIC_RANGES-TABLENAME INTO DYNAMIC_RANGES. "再读取出属于某个字段的Ranges DYNAMIC_RANGE1-FIELDNAME = 'CARRNAME'. READ TABLE DYNAMIC_RANGES-FRANGE_T WITH KEY DYNAMIC_RANGE1-FIELDNAME INTO DYNAMIC_RANGE1. DYNAMIC_RANGE2-FIELDNAME = 'CURRCODE'. READ TABLE DYNAMIC_RANGES-FRANGE_T WITH KEY DYNAMIC_RANGE2-FIELDNAME INTO DYNAMIC_RANGE2. FLAG_READ = 'X'. ENDIF. SELECT * FROM SCARR WHERE CARRID IN S_CARRID AND CARRNAME IN DYNAMIC_RANGE1-SELOPT_T"使用IN 关键字使用Ranges内表 AND CURRCODE IN DYNAMIC_RANGE2-SELOPT_T."(与select-options屏幕参数是一样的用法) PUT SCARR. ENDSELECT. ENDFORM.
6.3.4.FIELD SELECTION动态选择字段
SELECTION-SCREEN FIELD SELECTION FOR NODE|TABLE .语句的作用是开启节点的动态字段选择的功能(形如:SELECT (选择字段内表) FROM…,而不是SELECT * FROM …,即选择了哪些字段,就只查询哪些字段,而不是将所有字段查询出来,进而可以提高性能)。
在可执行报表程序里,可以通过GET node [FIELDS f1 f2 ...] 语句中的 FIELDS选项来指定要读取字段;
FORM PUT_SPFLI. STATICS: FIELDLISTS TYPE RSFS_TAB_FIELDS, FLAG_READ."确保SELECT_FIELDS只读取一次 IF FLAG_READ = SPACE. FIELDLISTS-TABLENAME = 'SPFLI'. "读取相应表的动态选择字段 READ TABLESELECT_FIELDSWITH KEY FIELDLISTS-TABLENAME INTO FIELDLISTS. FLAG_READ = 'X'. ENDIF. SELECT(FIELDLISTS-FIELDS)"动态选择字段 INTO CORRESPONDING FIELDS OF SPFLI FROM SPFLI WHERE CARRID = SCARR-CARRID AND CONNID IN S_CONNID. PUT SPFLI. ENDSELECT. ENDFORM.
在相应的可执行报表程序里,相应的代码可能会是这样的:
TABLES SPFLI. GET SPFLI FIELDS CITYFROM CITYTO. ...
DATA: ITAB LIKE SELECT_FIELDS, ITAB_L LIKE LINE OF ITAB, JTAB LIKE ITAB_L-FIELDS, JTAB_L LIKE LINE OF JTAB. START-OF-SELECTION. ITAB = SELECT_FIELDS. "在报表程序中也可以直接使用LDB程序中的全局变量! LOOP AT ITAB INTO ITAB_L. IF ITAB_L-TABLENAME = 'SPFLI'. JTAB = ITAB_L-FIELDS. LOOP AT JTAB INTO JTAB_L. WRITE / JTAB_L. ENDLOOP. ENDIF. ENDLOOP.
zebra(1) type c, " striped pattern斑马线显示,颜色隔行交替显示 edit(1) type c, " for grid only ALV是否可编辑,注意只对Grid模式有效,对List模式无效 f2code like sy-ucomm, "gs_layout-f2code='&ETA'.双击时触发的Funcode,这里为弹出详情窗口
colwidth_optimize(1) type c, ALV网格(单元格)宽度设置为自动最优化,按输出内容宽度自动调整[ˈɔptəˌmaɪz]
lights_fieldname type slis_fieldname," fieldname for exception列显示为红绿灯 box_fieldname type slis_fieldname, " fieldname for checkbox指定数据内表中哪列以选择按钮形式显示(首列前可按下或弹上来的按钮),ALV最左上角会出现全选按钮
key_hotspot(1) type c, " keys as hotspot " K_KEYHOT设置关键字段是否是热点,可单击
key(1) type c, " column with key-color指定字段是否是关键字段,如果是则单元格显示的颜色会不同,并会靠前显示
col_pos like sy-cucol, " position of the column列的输出位置字段在表中第几列
fieldnametype slis_fieldname,"针对输出内表哪列进行设置,只有设置了的列才会显示,如果没有设置,则不会显示在ALV中。如果此字段是CURR金额(currency field) ,QUAN数量(Quantity field) 需要指定所参照的CUKY货币单位、UNIT字段名,需设置Cfieldname Ctabname 和Qfieldname Qtabname cfieldname type slis_fieldname, "field with currency unit金额字段所参照的货币单位字段名
ctabname type slis_tabname, " and table qfieldname type slis_fieldname, " field with quantity unit数量字段所参照的数量单位字段名 qtabname type slis_tabname, " and table
just(1) type c, " (R)ight (L)eft (C)ent.单元格中内容显示时对齐方式。不设置时按钮数据类型默认对齐方式来对齐 lzero(1) type c, " leading zero 为X时输出前导零 no_sign(1) type c, " write no-sign 不显示数字符号 no_zero(1) type c, " write no-zero 只输出有意义的值,空值不输出。为X时全为零(如:00000)时不输出,所以不输出零时应该最好同时设置lzero = sapce与no_zero = X,相反如果要输出,则应同时设置lzero = X 与no_zero = space fix_column(1) type c, " Spalte fixieren列固定不滚动,与Key属性相似,但颜色不会发生变化 do_sum(1) type c, " sum up该列是否进行小计,需与gt_sort-subtot一起使用(即需要参考排序),否则只对整列进行一个合计
seltext_llike dd03p-scrtext_l, " long key word标题字段显示的名称(长) seltext_mlike dd03p-scrtext_m, " middle key word标题字段显示的名称(中) seltext_slike dd03p-scrtext_s, " short key word标题字段显示的名称(短) ddictxt(1) type c, " (S)hort (M)iddle (L)ong设置以长、中还是短名称来显示,取值分别为 S、M、L。直接指定文本显示为长文本、中、还是短文本, 指定这个字段后则会固定下来,不会随着用户的宽度调整变化.
i_grid_settings-edt_cll_cb= 'X'. CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING i_grid_settings= i_grid_settings 法二:通过函数参数I_CALLBACK_USER_COMMAND指定的回调Form的参数slis_selfield进行设置:
FORM user_command USING ucomm LIKE sy-ucommselfield selfield TYPE slis_selfield. selfield-refresh= 'X'. CASE ucomm. WHEN 'UPDATE'. PERFORM frm_update. ENDCASE. ENDFORM.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING it_events = t_events[]
"注:如果没有设置REUSE_ALV_GRID_DISPLAY 函数的参数i_grid_settings-edt_cll_cb = 'X',在单元格数据被修改后,此Form不会自动调用(即不触发data_changed事件),直到点击了保存或刷新按钮后才会被调用,另外 cl_gui_alv_grid 的CHECK_CHANGED_DATA方法也会触发data_changed事件;另外,如果是通过OO实现的ALV,要让DATA_CHANGE事件触发,则还需要注册回车或焦点失去动作,具体参看后面 FORM alv_data_changed USING pel_data TYPE REF TO cl_alv_changed_data_protocol. DATA: l_name(20),ls_cells TYPE lvc_s_modi. FIELD-SYMBOLS . LOOP AT pel_data->mt_mod_cells INTO ls_cells."读取被修改了的单元格 CLEAR gt_data. READ TABLE gt_data INDEX ls_cells-row_id."被修改了的单元格所对应输出内表行数据 CONCATENATE 'GT_DATA-' ls_cells-fieldnameINTO l_name. "读取被单元格所对应的输出内表中的相应列数据,注:读取出来是的单元格修改之前的数据 ASSIGN (l_name) TO ."即为修改前的值 = ls_cells-value. "ls_cells-value单元格中修改后的新值? "实际上不需要此句来修改输出内表中的数据,因为只要在该Form中不弹出 E MSG,则该Form执行完后会也会自动更新输出内表 "MODIFY gt_data INDEX ls_cells-row_id. ENDLOOP.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTINGit_events = t_events[]
"页眉触发时所回调Form FORM alv_top_of_page. DATA:lr_rows TYPE REF TO cl_salv_form_layout_grid, lr_grid_rows LIKE lr_rows, lr_row TYPE REF TO cl_salv_form_layout_flow, lr_logo TYPE REF TO cl_salv_form_layout_logo.
DATA: l_row TYPE i VALUE '1'. CREATE OBJECT lr_rows. CREATE OBJECT lr_logo. ... ENDFORM.
FORM handle_before_user_command USING i_ucomm TYPE syucomm . CASE e_ucomm . WHEN '&INFO' . CALL FUNCTION 'ZSFLIGHT_PROG_INFO'. CALL METHOD gr_alvgrid->set_user_commandEXPORTING i_ucomm = space. ENDCASE . ENDFORM .
3、Field not outputting more than 255 characters in a loop. This is happening because when you send a string to smartform with length >255 characters then it takes only first 255 characters. I overcomed this problem by splitting the string which was of around 500 char into two and then sending it to smartform as individual vairables and displaying the two variables one after the other in the smartform.
DELETE ADJACENT DUPLICATES FROM [COMPARING ... | ALL FIELDS]
注,在未使用COMPARING 选项时,要删除重复数据之前,一定要按照内表关键字声明的顺序来进行排序,才能删除重复数据,否则不会删除掉;如果指定了COMPARING 选项,则需要根据指定的比较字段顺序进行排序(如COMPARING 时,则需要sort by ,而不能是sort by ),才能删除所有重复数据
SELECT SINGLE vbeln posnn txz01 menge INTO (it_result-ebeln, it_result-ebelp, it_result-txz01 , it_result-menge_2) FROM vbfa AS v INNERJOIN ekpo AS e ON v~vbeln = e~ebeln AND v~posnn= e~ebelpAND e~knttp = 'E'"Where条件是根据销售单查找前置单据——采购单,V为采购单凭证类型 WHERE vbelv = it_result-vbeln AND posnv = it_result-posnr AND vbtyp_n = 'V'.
HNTTP:采购凭证中的帐户设置类型,E——生产/销售所需物料的采购
分成两个可以正常查询:
SELECT SINGLE vbeln posnn INTO (it_result-ebeln, it_result-ebelp) FROM vbfa AS v"Where条件是:先根据销售单查找到前置采购单的单号与行项目号 WHERE vbelv = it_result-vbeln AND posnv = it_result-posnr AND vbtyp_n = 'V'.
SELECT SINGLE txz01 menge INTO (it_result-txz01, it_result-menge_2) FROM ekpo"Where条件是:再根据前面查出来的采购单号与行项目号,查出EKPO其他详细信息 WHERE ebeln = it_result-ebeln AND ebelp = it_result-ebelp AND knttp = 'E'.
10.技巧
10.1.让READ TABLE...WITH KEY可使用OR条件或其他非“=”操作符
READ TABLE...WITH KEY... 后面不能接OR条件操作符,也不能使用其他非等于的比较操作符,因原是该语句即使在查询出多条时也只取第一条,所以限制了 WITH KEY 后面条件使用。下面是错误的语法:
"江 <--> 6C5F "正 <--> 6B63 *DATA: x(4) TYPE x VALUE '6C5F'. DATA: x(2) TYPE x VALUE '6C5F'. FIELD-SYMBOLS: TYPE c. "有时将X类型分配给C类型时会出错(长度需要是4的倍数,所以定义成4的倍数 "即可解决这个问题,但有时定义的长度只能是某个特定数,所以此时只能使用后面这种方式) "编译时报错误:The length of "X" in bytes must be a multiple of the size of "a Unicode character, regardless of the size of the Unicode character. *ASSIGN x to CASTING.
"只能先定义一个C类型变量,再将这个C类型变量分配给X类型字段符号,这样就可 "以随便在x类型之间捣腾了,但此时C变量不是X变量的真正视图了(经过了拷贝) DATA: c(1) . FIELD-SYMBOLS: TYPE x. ASSIGN c to CASTING. = x. "江 6C5F注:如果输出的是乱码,则是字节序的问题,需写成5F6C(如Windows操作系统中) WRITE: /(2) c, . "正 6B63注:如果输出的是乱码,则是字节序的问题,需写成636B(如Windows操作系统中) x = '6B63'. = x. WRITE: /(2) c, .
DATA: i TYPE i VALUE '10'. DATA: str TYPE string VALUE 'string'. DATA: tmp TYPE string. str = i && str. WRITE: / str. tmp = i. CONCATENATEtmp str INTO str. WRITE: / str.
12. 要根据主键或索引字段查找数据,且WHERE从句中的条件字段需按INDEX字段顺序书写,且将索引字段条件靠前(左边),如:在VBFA表中查找SO所对应的交货单DN,因为如果直接到LIPS中找时,SO的订单中号与行号在LIPS中非主键,但在VBFA是部分主键(VBFA中根据部分主键查找 SO -> DN; 根据索引查找 SO -> PO,VBFA-VBELN+VBFA-POSNN组合字段上创建了索引,即根据SO找PO时,不要从EKKN关联表中查找,而是通过VBFA中查找。后来查看EKKN,发现在VBELN+VBELP字段上创建了索引,所以从VBFA与EKKO查找应该差不多,主要看哪个表数据量少的问题了)
检查条件组合字段是否是主键,或者是上在上面创建了索引,避免条件组合字段即不是主键又没有索引
13. SELECT语句WHERE条件,应该先将主键相关条件放在前面 然后按照比较符 = 、< 、>、 <> 、LIKE IN 的顺序排列WHERE条件
((Select Single Values OR…) OR(Select Intervals OR…))( AND NOT Exclude Single Values) … (ANDNOT Exclude Intervals) …
("MATNR" = '1' OR "MATNR" >= '2' OR "MATNR" <= '3' OR "MATNR" > '4' OR "MATNR" < '5' OR "MATNR" <> '6' OR "MATNR" <> '7' OR "MATNR" LIKE '23%' OR NOT ( "MATNR" LIKE '24_' ) OR "MATNR" BETWEEN '8' AND '9' OR NOT ( "MATNR" BETWEEN '10' AND '11' )) AND "MATNR" <> '12' AND "MATNR" < '13' AND "MATNR" > '14' AND "MATNR" <= '15' AND "MATNR" >= '16' AND "MATNR" = '17' AND "MATNR" = '18' AND NOT ( "MATNR" LIKE '25%' ) AND "MATNR" LIKE '26_'AND NOT ("MATNR" BETWEEN '19' AND '20' ) AND "MATNR" BETWEEN '21' AND '22'
TABLES: marc. SELECT-OPTIONS: s_werks FOR marc-werks NO INTERVALS NO-EXTENSION.
12.5.各种屏幕元素演示
TABLES: mara,marc. DATA: g_pg(24).
SELECTION-SCREEN BEGIN OF BLOCK bk1 WITH FRAME TITLE text-001. PARAMETERS: p_bukrs LIKE t001-bukrs OBLIGATORY."Company code SELECT-OPTIONS: s_werks FOR marc-werks OBLIGATORYNO INTERVALS, s_matnr FOR mara-matnr NO-EXTENSION , s_segme FOR g_pg."参照普通变量 PARAMETERS: p_line(6). SELECTION-SCREEN SKIP 1. PARAMETERS: p_x1 RADIOBUTTON GROUP gp1 DEFAULT 'X', p_x2 RADIOBUTTON GROUP gp1. PARAMETERS: p_old TYPE c AS CHECKBOX. PARAMETERS: p_oldhir LIKE grpdynp-name_coall MODIF ID m1 DEFAULT 'ABB_CHINA.XXXX'.
SELECTION-SCREEN:SKIP 1. SELECTION-SCREEN BEGIN OF LINE. PARAMETERS: p_dwload AS CHECKBOX. SELECTION-SCREEN COMMENT 5(29) text-001. PARAMETERS: p_file TYPE string. SELECTION-SCREEN END OF LINE. SELECTION-SCREEN END OF BLOCK bk1.
SELECTION-SCREEN:PUSHBUTTON2(12) but1 USER-COMMANDcli1. INITIALIZATION. but1 = 'Button 1'."可直接设置按钮上的标签文本 AT SELECTION-SCREEN. CASE sy-ucomm. WHEN 'CLI1'. ENDCASE.
12.6.2.选择屏幕中的单选/复选按钮:点击时显示、隐藏其他屏幕元素
更多请参考动态修改屏幕章节
PARAMETERS show_allAS CHECKBOX USER-COMMANDflag. PARAMETERS hideRADIOBUTTON GROUPrd USER-COMMANDflag2 DEFAULT 'X'. PARAMETERS show RADIOBUTTON GROUPrd . SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME . PARAMETERS: p1 TYPE c LENGTH 10 , p2 TYPE c LENGTH 10. SELECTION-SCREEN END OF BLOCK b1. SELECTION-SCREEN BEGIN OF BLOCK b2 WITH FRAME TITLE t. PARAMETERS: p3 TYPE c LENGTH 10 MODIF ID bl2, p4 TYPE c LENGTH 10 MODIF ID bl2. SELECTION-SCREEN END OF BLOCK b2. SELECTION-SCREEN BEGIN OF BLOCK b3 WITH FRAME . PARAMETERS: p5 TYPE c LENGTH 10 MODIF ID bl3, p6 TYPE c LENGTH 10 MODIF ID bl3. SELECTION-SCREEN END OF BLOCK b3.
INITIALIZATION. t = '----ALL----'. "单先与复选框、下拉列表项点击触发PAI后,接下来还会触发屏幕的PBO(回车也是这样),但如果点击的是执行按钮,则不会接着触发屏幕的PBO,除非没有输出或在Basic List列表页面上点击返回按钮时,才会触发PBO AT SELECTION-SCREEN OUTPUT. LOOP AT SCREEN. IF show_all = 'X' AND screen-group1 = 'BL2'. screen-active= '1'."显示 MODIFY SCREEN. ELSEIF screen-group1 = 'BL2'. screen-active= '0'."隐藏 MODIFY SCREEN. ENDIF. IF show = 'X' AND screen-group1 = 'BL3'. screen-active = '1'. MODIFY SCREEN. ELSEIF screen-group1 = 'BL3'. screen-active = '0'. MODIFY SCREEN. ENDIF. ENDLOOP.
FIELD MODULE ON INPUT|REQUEST|*-INPUT. 相当于选择屏幕的 AT SELECTION-SCREEN ON field
CHAIN. FIELD: , ,. MODULE ON CHAIN-INPUT|CHAIN-REQUEST. FIELD: , ,. MODULE ON CHAIN-INPUT|CHAIN-REQUEST. ... ENDCHAIN.
只要中某个字段满足条件(后面的CHAIN-INPUT与CHAIN-REQUEST条件),就会被调用,而只要或中的某个字段满足条件,则就会被调用。如果在module中检测不通过(如MESSAGE… E类消息时),则CHAIN…ENDCHAIN之外的所有其他屏幕字段将会被锁定且置灰,这与选择屏幕的AT SELECTION-SCREEN ON BLOCK校验是一样的
CHAIN. FIELD: , ,. FIELD MODULE ON INPUT|REQUEST|*-INPUT|CHAIN-INPUT|CHAIN-REQUEST. MODULE ON CHAIN-INPUT|CHAIN-REQUEST. ENDCHAIN.
PARAMETERS c TYPE sflight-carrid VALUE CHECK."应该是这样,参照的是从表字段(主表为值表) PARAMETERS c TYPE s_conn_id VALUE CHECK."而不应该直接参照DataElement,不会出现F4帮助,也不会进行Value Check
FUNCTION zfvbeln_find_exit. *"---------------------------------------------------------------------- *"*"Local Interface: *" TABLES *" SHLP_TAB TYPE SHLP_DESCT *" RECORD_TAB STRUCTURE SEAHLPRES *" CHANGING *" VALUE(SHLP) TYPE SHLP_DESCR *" VALUE(CALLCONTROL) LIKE DDSHF4CTRL STRUCTURE DDSHF4CTRL *"---------------------------------------------------------------------- "此内表用于存储命中清单数据.注:字段的名称一定要与搜索参数名一样,但顺序可以不同, DATA: BEGIN OF lt_tab OCCURS 0, wbstk TYPE wbstk, lfdat TYPE lfdat_v, vbeln TYPE vbeln_vl, END OF lt_tab. "用于存储从选择屏幕上传进的屏幕字段的选择条件值 DATA: r_vbeln TYPE RANGEOF vbeln_vl WITH HEADER LINE, r_lfdat TYPE RANGEOF lfdat_v WITH HEADER LINE, r_wbstk TYPE RANGEOF wbstk WITH HEADER LINE, wa_selopt LIKE LINE OF shlp-selopt." "callcontrol-step该字段的值是由系统设置,并且你可以在程序中进行修改它。出口函数会在处理的每一步(时间点)都会调用一次 IF callcontrol-step = 'SELECT'."如果有弹出限制对话框,则会在弹出限制对话框中点击确认按钮后step值才为SELECT "shlp-selopt存储的是经过映射转换后选择屏幕上字段的值,而不是直接为 "选择屏幕字段名,而是转映射为Help参数名后再存储到 selopt 内表中, "屏幕字段到Help参数映射是通过 shlp-interface 来映射的 LOOP AT shlp-selopt INTO wa_selopt. CASE wa_selopt-shlpfield. WHEN 'VBELN'."由于屏幕字段已映射为了Help相应参数,所以这里不是S_VBELN MOVE-CORRESPONDING wa_selopt TO r_vbeln. APPEND r_vbeln. WHEN 'LFDAT'. MOVE-CORRESPONDING wa_selopt TO r_lfdat. APPEND r_lfdat. WHEN 'WBSTK'. MOVE-CORRESPONDING wa_selopt TO r_wbstk. APPEND r_wbstk. ENDCASE. ENDLOOP. "根据屏幕上传进的条件查询数据 SELECT likp~vbeln likp~lfdat vbuk~wbstk INTO CORRESPONDINGFIELDS OF TABLE lt_tab FROM likp INNER JOIN vbuk ON likp~vbeln = vbuk~vbeln WHERE likp~vbeln IN r_vbeln AND likp~lfdat IN r_lfdat AND vbuk~wbstk IN r_wbstk. "该函数的作用是将内表 lt_tab 中的数据转换成record_tab,即将某内表中的数据显示在命中清单中 CALL FUNCTION 'F4UT_RESULTS_MAP' TABLES shlp_tab = shlp_tab record_tab = record_tab source_tab = lt_tab CHANGING shlp = shlp callcontrol = callcontrol. "注:下一个时间点一定要直接设置为 DISP,否则命中清单不会有值,也不显示出来 "从表面上看,SELECT时间点下一个就是DISP时间点,按理是不需要设置为DISP, "但如果不设置为DISP,出口函数在执行完后,系统会转入DISP时间点执行(即再次调用此出口函数) ",但再次进入此出口函数时,record_tab内表已经被清空了(是否可以通过判断callcontrol-step的值来决定走什么新的逻辑代码来解决此问题?)。如果这里直接设置为DISP,就好比欺骗了系统一样,告诉系统当前执行的正是DISP时间点,而不是SELECT,系统就不会再转到DISP时间点了而是直接显示 callcontrol-step ='DISP'."DISP:在命中清单显示之前调用,表示数据已经查出,下一步就该显示了。该时间用于控制搜索帮助的输出结果。例如,在输出搜索结果时对用户检查权限,删除未授权的数据 ENDIF. ENDFUNCTION.
12.12.2.删除重复
FUNCTION zeh_lxsecond. IF callcontrol-step = 'DISP'. SORT RECORD_TAB. DELETE ADJACENT DUPLICATES FROM RECORD_TAB COMPARING ALL FIELDS."zsecond. EXIT. ENDIF. ENDFUNCTION.
12.13.搜索帮助优先级
先PROCESS ON VALUE-REQUEST,AT SELECTION-SCREEN ON VALUE-REQUEST
REQUIRED选项的应用:该选项可以解决这个问题:在点击某个单选框(p_rd1)后显示某个必输字段(p_lclfil),但当这个必输框显示出来后,如果点击p_rd2想隐藏它时,此时输入框中必须有值,否则系统会自动检验要求重新输入。现要求输入框没有输入值的情况下,也可在点击p_rd2时隐藏它,则解决的办法是:将输入框的这个属性设置为2(显示必须的钩,但系统不会自动进行必输验证),去掉OBLIGATORY选项(不去掉也会被忽略),并在AT SELECTION-SCREEN ON field事件里时手动进行为空验证
"一定要设置 USER-COMMAND ,否则点击之后,不会触发屏幕PAI事件,PAI事件不触发则会导致 "屏幕的AT SELECTION-SCREEN OUTPUT也就不会被触发(非执行按钮的FunCode触发时都会刷新 "屏幕,所以再次显示屏幕时再次执行PBO) PARAMETERS p_rd1 RADIOBUTTON GROUP gp1 USER-COMMAND mxx."用来隐藏 p_lclfil PARAMETERS p_rd2 RADIOBUTTON GROUP gp1 DEFAULT 'X'."用来显示 p_lclfil "当通过程序动态修改屏幕元素属性 required 后,会忽略掉这里的 OBLIGATORY 选项 *PARAMETERS p_lclfil(128) AS LISTBOX VISIBLE LENGTH 20 MODIF ID mxy OBLIGATORY . PARAMETERS p_lclfil(128) MODIF ID mxy OBLIGATORY . PARAMETERS: c AS CHECKBOX."没什么作用,用来测试 CHECKBOX 的可输入性 "当 C2 被钩选时,屏幕上的其他输入元素均不可输入 PARAMETERS: c2 AS CHECKBOX USER-COMMAND ddd DEFAULT 'X'.
AT SELECTION-SCREEN OUTPUT. LOOP AT SCREEN . "当 C2 没有钩选时,其他元素都设置为可输入 IF screen-name <> 'C2' AND c2 IS INITIAL . screen-input = 1. MODIFY SCREEN. ELSEIF screen-name <> 'C2' AND c2 IS NOT INITIAL . screen-input = 0."C2钩选时,所以屏幕输入元素禁止输入 MODIFY SCREEN. ENDIF. "控制下拉列表(文本框也是一样)的必输性:外观上打钩,但不自动校验 IF p_rd2 = 'X' AND screen-group1 = 'MXY'. "显示 screen-active= '1'. * screen-input = '1'."显示前设为可输入 screen-required= '2'."外观上打钩,但不自动校验 MODIFY SCREEN. ELSEIF screen-group1 = 'MXY'. " "隐藏 screen-active = '0'. screen-required = '2'. MODIFY SCREEN. ENDIF. ENDLOOP.
AT SELECTION-SCREEN ON p_lclfil. IF p_rd2 IS NOT INITIAL"手动检验:但当点击单选按钮与复选框 C2 时,不校验 AND sy-ucomm <> 'MXX' AND sy-ucomm <> 'DDD' AND p_lclfil IS INITIAL. MESSAGE e055(00). ENDIF.
12.17.子屏幕
除通过屏幕的属性将某个屏幕设置为子屏幕外,还可以通过程序创建一个子屏幕:
SELECTION-SCREEN BEGIN OF SCREENdynnr ASSUBSCREEN. ... SELECTION-SCREEN END OF SCREEN dynnr.
l子屏幕可以嵌入到Tabstrip中使用:
SELECTION-SCREENBEGIN OF TABBED BLOCKtblock FOR n LINES. SELECTION-SCREEN TAB(len) tab USER-COMMAND fcode DEFAULTSCREENdynnr.
... tab:Tab的标题;len:Tab标题显示的宽度;Tab页签内容的行数由n来决定;DEFAULT SCREEN:给Tab静态分配子屏幕 SELECTION-SCREEN END OF BLOCK tblock.
l通过CALL SUBSCREEN语句将子屏幕嵌入到对话屏幕中:
PROCESS BEFOREOUTPUT. CALL SUBSCREENINCLUDING [] . ...子屏幕都需要放在主屏幕中的某个指定的区域元素中
为了调用子屏幕的PAI事件,需要在主屏幕的PAI flow logic里如下调用: PROCESS AFTER INPUT. CALL SUBSCREEN. ...
普通选择屏幕,可以使用CALL SELECTION-SCREEN来单独调用
12.18.屏幕跳转
LEAVE SCREEN.
or
LEAVE TO SCREEN .
LEAVE SCREEN语句会结束当前屏幕并调用下一屏幕,next scree可以是static next screen,或者是dynamic next screen,如果是动态的,你必须在使用LEAVE SCREEN语句前使用SET SCREEN语句来重写static next screen
SET SCREEN XXXX设置调用栈当前层次的Next Screen为XXXX,它并不影响调用栈的层数(即不会重新开启一个新的屏幕序列,只做屏幕之间的切换,而不是屏幕序列之间的切换),除非XXXX为0,那将导致调用栈退掉一层(出栈)。要注意的是,PAI中SET SCREEN XXXX后的语句,系统将照样执行,只有执行完毕该PAI整个逻辑后,才考虑Next Screen的PBO和PAI。
LEAVE TO SCREEN XXX与SET SCREEN XXX比较类似(也不会重新开启一个新的屏幕序列,只做屏幕之间的切换,而不是屏幕序列之间的切换),所不同的是,LEAVE TO SCREEN XXXX将强行中断当前SCREEN的PAI,直接执行XXXX的PBO和PAI。换言之,PAI中LEAVE TO SCREEN XXXX后面的语句,系统将不会执行到。
LEAVE SCREEN.后面的语句也不会执行
注:上面语句的XXX也可以是选择屏幕的屏幕号,而不只是对话屏幕号
CALL SCREEN是将正在运行的画面挂起,进入所调用的画面,当使用LEAVE TO SCREEN 0时,能够返回原主调画面,可理解为嵌套调用;而LEAVE TO SCREEN是立即结束本画面的执行,调用所指定的画面,在调用画面中,无法再返回原主调画面。
AT SELECTION-SCREEN { OUTPUT} | { ON{para|selcrit} } | { ON END OF selcrit } | { ON BLOCK block} | { ON RADIOBUTTON GROUP radi } | {ON {HELP-REQUEST|VALUE-REQUEST} { FOR {para|selcrit-low|selcrit-high} } | { ON EXIT-COMMAND } | { }.
12.20.3.逻辑数据库事件
GETnode [LATE] [FIELDS f1 f2 ...].
12.20.4.列表事件
TOP-OF-PAGE. END-OF-PAGE. AT LINE-SELECTION. AT USER-COMMAND. AT pf.
当用户在基础列表屏幕上双击一行或按功能键“F2”时,将会触发ABAP事件AT LINE-SELECTION,如果还想进一步显示该行数据的详细信息,则可以继续使用WRITE语句输出要显示的内容,这次生成另外一个详细列表屏幕(Details List Screen)。此详细列表屏幕将覆盖其上一层的基础列表屏幕,若在其界面的工具条上点“返回”或按功能键F3,将返回到基础列表屏幕。
如果在报表程序中没有设置(Basic List与Detail List都未设置时)GUI status,则系统会将list screen的Status设置为系统内置的default list status;在其他类型的程序中(如当你在Screen中创建一个list时),你可以明确的调用下面语句来设置成系统内置的default list status:
SET PF-STATUSspace.
该语句定义了Standard List所拥有的默认functions,系统所提供的内置default list status如下(这些功能都已实现,可直接使用):
该语句可以使用在PBO 或者PAI event中,它的作用是在当前屏幕的PAI processing(一般在PBO块里使用SUPPRESS DIALOG.或LEAVE SCREEN.语句后不会显示这个屏幕,此时在PBO事件块结束后立即显示Basic List)结束后开始list processor并显示Basic List。调用该语句所在屏幕的PBO and PAI modules中的list output都会被输出到Basic List中缓存起来,待该屏幕处理完后显示(如果没有在PBO中使用SUPPRESS DIALOG.或LEAVE SCREEN.语句,则在PAI结束后显示;如果使用了这两个语句,则会在PBO块结束后就会显示)
可以使用以下两种方式来离开list processing:
• 在basiclist中点击Back,Exit,orCancel
• 在list processing程序中使用:LEAVE LIST-PROCESSING.
以上两种方式都会使控制权从list processor转交到dialog processor。
在默认的情况下,不带AND RETURN TO SCREEN选项的LEAVE TO LIST-PROCESSING语句在当list processor 处理结束后(如关闭list列表输出窗体时),dialog processor将会又会返回到LEAVE TO LIST-PROCESSING语句调用所在屏幕的PBO事件块,并重新执行PBO事件块(所以这样使用会出现死循环:关不掉List列表输出窗口);选项AND RETURN TO SCREEN允许你指定当前屏幕序列(即LEAVE TO LIST-PROCESSING语句调用所在屏幕所在的屏幕序列)中的某个屏幕,当list processor 处理结束后,dialog processor将会回到指定的屏幕并执行其相应的PBO事件块
13.8.LIST 打印输出
打印参数设置:
SET_PRINT_PARAMETERS
GET_PRINT_PARAMETERS
从程序中启动打印:NEW-PAGEPRINTON
14.Messages
14.1.00消息ID中的通用消息
00消息ID中的001消息本身未设置任何消息串,这条消息可以传递8个参数,在用于拼接消息时很有用
MESSAGE e001(00) WITH 'No local currecny maintained for company:' p_bukrs.
14.2.消息常量
直接显示消息常量,不引用消息ID与消息号
MESSAGE 'aaaa' TYPE 'S'.
14.3.静态指定
MESSAGE () [with... ][raising ].
MESSAGE s002(00).
14.4.动态指定
MESSAGE IDTYPENUMBER [with...] [raising].
DATA: t(1) VALUE 'S', id(2) VALUE '00', num(3) VALUE '002'. MESSAGE IDidTYPEt NUMBERnum.
14.5.消息拼接MESSAGE …INTO
DATA mtext TYPE string. CALL FUNCTION ... EXCEPTIONS error_message = 4. IF sy-subrc = 4. MESSAGEIDsy-msgid TYPEsy-msgty NUMBERsy-msgno INTOmsgtext WITHsy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF.
2、如果调用时加上了exception 选项对exc 异常进行了捕获,则不会再显示消息(但如果即使加上了exception选项,但没有捕获到exc异常,则此时会忽略RAISING选项),并设置sy-subrc。只要异常被捕获,相关消息内容将会入存入到SY-MSGID,SY-MSGTY, SY-MSGNO, and SY-MSGV1 to SY-MSGV4有关系统变量中。
CLASS c1 DEFINITION. PUBLIC SECTION. CLASS-METHODS m1 EXCEPTIONSexc1. ENDCLASS. CLASS c1 IMPLEMENTATION. METHOD m1. MESSAGE 'Message in a Method' TYPE 'I' RAISINGexc1. ENDMETHOD. ENDCLASS.
CALL FUNCTION 'ZJZJ_FUNC1' EXCEPTIONS error_message = 5 "被e捕获,如果注释掉下面E类型异常捕获列表,则会被error_message捕获 e = 4 d = 6.
14.8.1.error_message = n_error捕获消息
可以在Message语句没有使用RAISING选项的情况下(或使用exc1...exci或others但未捕获到),在主调程序中的CALL FUNCTION ...Exception参数列表中使用隐式异常error_message选项来捕获Message,但error_message是否能捕获到Message(实为是否设置sy-subrc = n_error),与消息类型有关:
1、对于W、I、S类型的消息,将不显示消息(本来是要显示的),也不会去设置 sy-subrc = n_error,但此时还是会将消息的相关信息存储到SY-MSGID, SYMSGTY,SY-MSGNO, and SY-MSGV1 to SY-MSGV4这些系统变量中
2、对于A、E类型消息,也将不显示提示消息,但会抛出ERROR_MESSAGE异常,即这两类型的消息会自动被转换为error_message异常抛出,并终最被CALL FUNCTION 中Exception异常列表中的error_message异常所捕获,并设置sy-subrc = n_error。此时还会将消息的相关信息存储到SY-MSGID, SYMSGTY,SY-MSGNO, and SY-MSGV1 to SY-MSGV4这些系统变量中
此时,对于A类型消息而言,ROLLBACK WORK语句将会隐式执行
3、对于X类型消息将会抛出runtime error,并且程序会dump
14.9.各种消息的显示及处理
消息类型
非屏幕PAI事件
PBO、
AT SELECTION-SCREEN OUTPUT、INITIALIZATION、START-OF-SELECTION、
一旦主调程序捕获了异常,以上两种触发异常的方式都会返回到主调程序,并且不会返回值(Function Module参数输出)。MESSAGE ..... RAISING语句也不会再显示消息,而是将相关的信息填充到SY-MSGID, SY-MSGTY,SY-MSGNO, and SY-MSGV1 to SY-MSGV4这些系统变量中(即使是I,S,W三种消息类型也会设置这些系统变量)
DATA result TYPE p DECIMALS 2. DATA oref TYPE REF TO cx_root. DATA text TYPE string. DATA i TYPE i. TRY . i = 1 / 0. CATCH cx_root INTO oref. text = oref->get_text( ). WRITE: '---' , text. RAISE EXCEPTION oref. ENDTRY.
DATA: exc TYPE REF TO cx_sy_dynamic_osql_semantics, text TYPE string. TRY. RAISE EXCEPTION TYPEcx_sy_dynamic_osql_semantics EXPORTINGtextid= cx_sy_dynamic_osql_semantics=>unknown_table_name token= 'Test'. CATCH cx_sy_dynamic_osql_semantics INTO exc. text= exc->get_text( ). MESSAGE text TYPE 'I'. ENDTRY.
DATA oref TYPE REF TO cx_root. DATA text TYPE string. DATA i TYPE i. TRY . RAISE RESUMABLE EXCEPTION TYPE cx_demo_constructor EXPORTING my_text = sy-repid. i = i + 1. WRITE: / i. CATCH BEFORE UNWINDcx_demo_constructor INTO oref . text = oref->get_text( ). IF i < 1. RESUME. ENDIF. WRITE:/ '--'. ENDTRY.
结果只输出 1
14.10.2.捕获异常
14.10.2.1.类异常捕获TRY…CATCH
DATA myref TYPEREFTOcx_sy_arithmetic_error. DATA err_text TYPE string. DATA result TYPEi. TRY. result = 1 / 0. CATCH cx_sy_arithmetic_error INTO myref. err_text = myref->get_text( ). ENDTRY.
DATA:i_meins LIKE ekpo-meins, i_meins2 TYPE c LENGTH 3. START-OF-SELECTION. SELECT meins meins FROM ekpo INTO (i_meins,i_meins2) WHERE ebeln = '4500012164'. "输出时, i_meins会自动发生转换,但 i_meins2 不会 WRITE: i_meins,i_meins2. ENDSELECT. SKIP.
输出时会发生自动转换,那么,在输入时,如从选择屏幕上录入的数据是参照带有规则转换的Domain的数据元素创建的选择屏幕字段时,从界面录入到ABAP程序中时,会自动按照转换规则进行转换,如下面从界面上输入的是 PC (外部格式的单位),但录入到ABAP程序中时,自动转换为ST(内部格式的部位),但再次Write输出时,又将 ST转换为PC输出(从内部转换为外部格式):
DATA: netpr LIKE vbap-netpr,"实际的类型为p(6.2) waers LIKE vbap-waerk, jpy_netpr TYPE i, netpr1(6) TYPE p DECIMALS 3. "通过SQL从数据库查询出来的是真实存储在表里的数据,没有经过其他转换,日元存到数据库中时缩小了100倍,所以要还原操作界面上输入的日元金额,则需要使用后面的格式化输出 SELECT SINGLE netpr waerk INTO (netpr,waers) FROM vbap WHERE waerk = 'JPY' AND vbeln = '0500001326'. WRITE: waers,netpr."数据库中的值,被缩小了100倍
FUNCTION currency_converting_factor. *"---------------------------------------------------------------------- *"*"Lokale Schnittstelle: * IMPORTING *" VALUE(CURRENCY) LIKE TCURR-TCURR *" EXPORTING *" VALUE(FACTOR) TYPE ISOC_FACTOR *" EXCEPTIONS *" TOO_MANY_DECIMALS *"---------------------------------------------------------------------- DATA: cur_factor TYPE isoc_factor. *- determine Decimal place in currency from TCURX CLEAR tcurx. "首先根据币种到db表tcurx中读取相应的小数位数currdec SELECT SINGLE * FROM tcurx WHERE currkey EQ currency. "如果没有维护相应币别信息则默认currdec = 2 IF sy-subrc NE 0. tcurx-currdec = 2. ENDIF. "如果currdec 大于了 5就报错 IF tcurx-
openjdk上关于hotspot将移除永久带的描述非常详细,http://openjdk.java.net/jeps/122
JEP 122: Remove the Permanent Generation
Author Jon Masamitsu
Organization Oracle
Created 2010/8/15
Updated 2011/
PL/SQL也是一门高级语言,所以流程控制是必须要有的,oracle数据库的pl/sql比sqlserver数据库要难,很多pl/sql中有的sqlserver里面没有
流程控制;
分支语句 if 条件 then 结果 else 结果 end if ;
条件语句 case when 条件 then 结果;
循环语句 loop
/*
*开发子程序
*/
--子程序目是指被命名的PL/SQL块,这种块可以带有参数,可以在不同应用程序中多次调用
--PL/SQL有两种类型的子程序:过程和函数
--开发过程
--建立过程:不带任何参数
CREATE OR REPLACE PROCEDURE out_time
IS
BEGIN
DBMS_OUTPUT.put_line(systimestamp);
E
long 长的
show 给...看,出示
mouth 口,嘴
write 写
use 用,使用
take 拿,带来
hand 手
clever 聪明的
often 经常
wash 洗
slow 慢的
house 房子
water 水
clean 清洁的
supper 晚餐
out 在外
face 脸,