ABAP S4新语法

FIELD-SYMBOL

ASSIGN COMPONENT L_I OF STRUCTURE WA1 TO FIELD-SYMBOL(<fs>).
ASSIGN LT_TAB[ 5 ] TO FIELD-SYMBOL(<fs>).
READ TABLE LT_TAB ASSIGNING FIELD-SYMBOL(<fs>) WITH  KEY ZCHAR1 = '3'.

CONV

数据类型的转换可以用 CONV 实现,部分类型不再需要通过中间变量来转换

*---------------------------------------------------------------------*
*    合理的使用 CONV 可以避免因为类型不一致而导致的 dump 问题,
*    例如下例的LV_RESULT,即在调用方法时做参数的类型转换
*    另外,在接口中处理传入参数时,一般情况下也会对数据的类型做对应的转换,
*    但不是所有类型都可以互相转换,例如将含有非数字的 CHAR 类型数据
*    强制转换成 INT 类型时,会抛出异常CX_SY_CONVERSION_ERROR
*---------------------------------------------------------------------*
DATA(LV_STR) = '001001.001'.
DATA(LV_INT) = CONV I( LV_STR ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_INT ).
DATA(LV_FLOAT) = CONV F( LV_STR ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_FLOAT ).
DATA(LV_DIVISION) = 1 / 3.
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_DIVISION ).
DATA(LV_DIV) = CONV DECFLOAT34( 1 / 3 ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_DIV ).

字符拼接

*1. LV_STR1 = 'Hello World'.
DATA(LV_STR) = |Hello World|."这种语法限定了LV_STR的长度就是字符串的长度
*2. LV_STR1 = 'Hello World'.
DATA(LV_STR1) = |{ LV_STR }|.
*3. LV_STR1 = 'TOM,'.
LV_STR1 = |{ 'TOM,' }|.
*4. LV_STR1 = 'TOM,Hello World'.
LV_STR1 = LV_STR1 && LV_STR.
*5. 注意:Good后面的空格会被忽略,但是Morning前面的空格将被保留。LV_STR3 = 'Good Morning!'.
DATA(LV_STR3) = 'Good' && ' Morning!'.
*6. LV_STR4 = 'Hello World TOM,'
CONCATENATE LV_STR LV_STR1 INTO DATA(LV_STR4) SEPARATED BY SPACE.

SWITCH

动态赋值语句,通常根据同一变量的不同数据来动态处理,用法类似于 CASE 语句

*---------------------------------------------------------------------*
*      SWITCH语句的判断条件相对单一,WHEN关键字后只能使用常量,
*      THEN/ELSE后面可以使用表达式进行赋值
*---------------------------------------------------------------------*
DATA(LV_INDICATOR) = 1.
DATA(LV_DAY) = SWITCH CHAR10( LV_INDICATOR
                              WHEN 1 THEN 'MONDAY'
                              WHEN 2 THEN 'TUESDAY'
                              WHEN 3 THEN 'WEDNESDAY'
                              WHEN 4 THEN 'THURSDAY'
                              WHEN 5 THEN 'FRIDAY'
                              WHEN 6 THEN 'SATURDAY'
                              WHEN 7 THEN 'SUNDAY'
                              ELSE '404' && '-ERROR'
                             ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_DAY ).

COND

动态赋值语句,可以根据不同条件来动态处理,用法类似于CASE/IF语句

*---------------------------------------------------------------------*
* COND语句中允许使用较为复杂的判断条件,因此VALUE语句中动态赋值通常会使用COND
*---------------------------------------------------------------------*
DATA(LV_INDICATOR) = 7.
DATA(LV_DAY) = COND CHAR10( WHEN LV_INDICATOR = 1 THEN 'MONDAY'
                            WHEN LV_INDICATOR = 2 THEN 'TUESDAY'
                            WHEN LV_INDICATOR = 3 THEN 'WEDNESDAY'
                            WHEN LV_INDICATOR = 4 THEN 'THURSDAY'
                            WHEN LV_INDICATOR = 5 THEN 'FRIDAY'
                            WHEN LV_INDICATOR = 6 THEN 'SATURDAY'
                            WHEN LV_INDICATOR = 7 AND SY-LANGU EQ 'E' THEN 'SUNDAY'
                            WHEN LV_INDICATOR = 7 AND SY-LANGU EQ 'F' THEN 'DIMANCHE'
                            WHEN LV_INDICATOR = 7 AND SY-LANGU EQ '1' THEN '星期天'
                            ELSE '404' && '-ERROR'
                           ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LV_DAY ).

MOVE-CORRESPONDING(表或者字段之间赋值)

*  MOVE-CORRESPONDING LT_TAB TO LT_TAB1.
* 相同字段名间赋值
  LT_TAB1 = CORRESPONDING #( LT_TAB ).
  CL_DEMO_OUTPUT=>DISPLAY( LT_TAB1 ).
* 不同字段名间赋值,使用关键字 MAPPING
DATA: BEGIN OF struct1, 
        mcomp1 TYPE i VALUE 1, 
        mcomp2 TYPE i VALUE 2, 
        BEGIN OF substruc, 
          subcomp1 TYPE i VALUE 1, 
          subcomp2 TYPE i VALUE 2, 
          subcomp3 TYPE i VALUE 3, 
        END OF substruc, 
      END OF struct1. 

DATA: BEGIN OF struct2, 
        comp2 TYPE i, 
        comp1 TYPE i, 
        BEGIN OF substruc, 
          comp3 TYPE i, 
          comp2 TYPE i, 
          comp1 TYPE i, 
        END OF substruc, 
      END OF struct2. 

struct2 = 
  CORRESPONDING #( 
    struct1 MAPPING comp1    = mcomp1 
                    comp2    = mcomp2 
                  ( substruc = substruc MAPPING comp1 = subcomp1 
                                                comp2 = subcomp2 
                                                comp3 = subcomp3 ) ) . 

VALUE

可以使用 VALUE 作为赋值语句,主要用来为内表、结构、变量等对象赋值

*---------------------------------------------------------------------*
*      参数类型引用同NEW关键字,在VALUE子句中,字段可以分开赋值,
*      也可以使用结构整体赋值,为内表赋值时,
*      需要用小括号将一行的数据包在一起
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_MARA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_MARA.

DATA: LT_MARA TYPE TABLE OF TY_MARA.
DATA(LW_MARA) = VALUE TY_MARA( MATNR = 'MATERIAL-001'
                               MTART = 'FOOD'
                               MATKL = '1020'
                               TEXT1 = 'FIRST material').
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LW_MARA ).
LT_MARA = VALUE #( ( LW_MARA )
                   ( MATNR = 'MATERIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1020'
                     TEXT1 = 'SECOND material')
                    ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_MARA ).
*---------------------------------------------------------------------*
*    此外,VALUE语句作为结构时,可以直接在特定语句中使用
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  CHECKBOX TYPE C,
  VALUE(4) TYPE C,
  END OF TY_DATA.
DATA LT_DATA TYPE TABLE OF TY_DATA.
APPEND VALUE #( VALUE = 'TEST' ) TO LT_DATA.

MODIFY LT_DATA FROM VALUE #( CHECKBOX = 'X' ) TRANSPORTING CHECKBOX WHERE CHECKBOX IS INITIAL.
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_DATA ).

BASE

在使用 VALUE 作为赋值语句时,默认会覆盖原有的数据,通过BASE子句基于原有数据进行赋值
注意: 使用 BASE 语句时,尽量保持前后结构一致,在使用不同的结构时,可能不报错但数据会错位


*---------------------------------------------------------------------*
*    在结构赋值语句中使用 BASE 时,原有字段的数据会被保留,
*     但是当在VALUE语句中对同一字段再次赋值时,该字段数据会被覆盖
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_MARA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_MARA.

DATA(LW_MARA) = VALUE TY_MARA( MATNR = 'MATERIAL-001'
                               MTART = 'FOOD' ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LW_MARA ).
LW_MARA = VALUE #( BASE LW_MARA
                   MTART = 'WATR'
                   MATKL = '1020'
                   TEXT1 = 'FIRST material').
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LW_MARA ).

*---------------------------------------------------------------------*
*    在内表赋值语句中使用 BASE 时,内表原有的数据会被保留,
*       新增条目会被追加到内表中,效果同 Append Line
*---------------------------------------------------------------------*
DATA LT_MARA TYPE TABLE OF TY_MARA.
LT_MARA = VALUE #( ( LW_MARA ) ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_MARA ).
LT_MARA = VALUE #( BASE LT_MARA
                   ( MATNR = 'MATERIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                  ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_MARA ).

FOR 写法及相关应用

在内表赋值语句中,可以使用FOR语句从其他内表中批量引入数据并处理

*&---------------------------------------------------------------------*
*& Report ZHCXTEST001
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT ZHCXTEST001.

TYPES:
  BEGIN OF LINE,
    COL1 TYPE I,
    COL2 TYPE I,
    COL3 TYPE I,
  END OF LINE,

  ITAB TYPE STANDARD TABLE OF LINE WITH EMPTY KEY.
* 从1-10依次填充到表itab2中
  DATA itab2 TYPE TABLE OF i WITH EMPTY KEY.
    itab2 = VALUE #( FOR j = 1 WHILE j <= 10 ( j ) ).
    cl_demo_output=>write( itab2 ).
	
  DATA(ITAB) = VALUE ITAB(
* THEN J + 1 可缺省
    FOR J = 11 THEN J + 1 UNTIL j > 12
      ( COL1 = J COL2 = J + 1 COL3 = J + 2 )

   ).

*  返回一个值
  DATA(TOTAL) = REDUCE I( INIT RES = 0 FOR WA IN ITAB WHERE ( COL1 < 1000 ) NEXT  RES = RES + WA-COL1 ).

* 返回一个结构
  DATA(RESULT) = REDUCE LINE( INIT RES1 = VALUE LINE( COL2 = 0 COL3 = 9999999 )

                FOR WA IN ITAB WHERE ( COL1 IS NOT INITIAL )
                NEXT RES1-COL1 = RES1-COL1 + 1
* nmax|nmin( val1 = arg1 val2 = arg2 [val3 = arg3] ... [val9 = arg9] )
* 这些函数返回传递的最大或最小参数的值。必须至少传递两个参数 arg1 和 arg2,最多必须传递九个参数。
* 在这里,可选的输入参数 val3 到 val9 必须按升序填充,没有间隙。参数 arg1 到 arg9 是数值表达式位置。
                     RES1-COL2 = NMAX( VAL1 = RES1-COL1 VAL2 = WA-COL2  )
                     RES1-COL3 = NMIN( VAL1 = RES1-COL1 VAL2 = WA-COL3  )
   ).

  CL_DEMO_OUTPUT=>DISPLAY( ITAB ).
  CL_DEMO_OUTPUT=>DISPLAY( TOTAL ).
  CL_DEMO_OUTPUT=>DISPLAY( RESULT ).


*---------------------------------------------------------------------*
*     使用FOR语句时,需要为内表定义临时工作区,如LWA_DATA,
*     仅允许在当前语句中使用,赋值过程中会使用到该工作区,
*     但在WHERE条件里,只能直接使用内表的字段名,需要注意的是,
*WHERE后面接的条件语句必须使用小括号包起来,INDEX INTO 定义的临时变量
*可用来记录当前操作行的序列,作用与LOOP语句中的系统变量 SY-TABIX 类似
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

TYPES: TY_TABLE TYPE TABLE OF TY_DATA WITH DEFAULT KEY.

LT_DATA = VALUE #(
                   ( MATNR = 'MATRIAL-001'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'FIRST material' )
                   ( MATNR = 'MATRIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                   ( MATNR = 'MATRIAL-003'
                     MTART = 'WATR'
                     MATKL = '1040'
                     TEXT1 = 'THIRD material' )
                  ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_DATA ).

DATA(LT_FOR) = VALUE TY_TABLE(
                               FOR LW_DATA IN LT_DATA
                               INDEX INTO LV_INDEX
                               WHERE ( MTART = 'WATR' )
                               ( MATNR = LW_DATA-MATNR
                                 MTART = LW_DATA-MTART
                                 MATKL = CONV #( LV_INDEX )
                                 TEXT1 = LW_DATA-TEXT1 )
                                ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FOR ).
*---------------------------------------------------------------------*
*  在FOR语句中允许将结构作为整体直接进行赋值,但是结构必须与表行兼容,
*  可以用于从内表中获取特定条件的数据
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

TYPES: TY_TABLE TYPE TABLE OF TY_DATA WITH DEFAULT KEY.

LT_DATA = VALUE #(
                   ( MATNR = 'MATRIAL-001'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'FIRST material' )
                   ( MATNR = 'MATRIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                   ( MATNR = 'MATRIAL-003'
                     MTART = 'WATR'
                     MATKL = '1040'
                     TEXT1 = 'THIRD material' )
                  ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_DATA ).

DATA(LT_FOR) = VALUE TY_TABLE(
                               FOR LW_DATA IN LT_DATA
                               WHERE ( MTART = 'WATR' )
                               ( LW_DATA )
                              ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FOR ).
*---------------------------------------------------------------------*
*    如果结构与表行不兼容,可以嵌套使用CORRESPONDING语句进行赋值,
*       如下例( CORRESPONDING具体用法可参考后续示例 )
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

LT_DATA = VALUE #(
                   ( MATNR = 'MATRIAL-001'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'FIRST material' )
                   ( MATNR = 'MATRIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                   ( MATNR = 'MATRIAL-003'
                     MTART = 'WATR'
                     MATKL = '1040'
                     TEXT1 = 'THIRD material' )
                  ).

TYPES: BEGIN OF TY_DATA_NEW,
  MATNR TYPE MARA-MATNR,
  MATKL TYPE MARA-MATKL,
  END OF TY_DATA_NEW.

TYPES: TY_TABLE_NEW TYPE TABLE OF TY_DATA_NEW WITH DEFAULT KEY.

DATA(LT_FOR) = VALUE TY_TABLE_NEW(
                                    FOR LW_DATA IN LT_DATA
                                    WHERE ( MTART = 'WATR' )
                                    ( CORRESPONDING #( LW_DATA ) )
                                    ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FOR ).
*---------------------------------------------------------------------*
* 结构字段过多时,一般建议使用整体赋值,如果少部分字段存在特殊的赋值逻辑
* 可嵌套使用VALUE以及BASE语句进行处理,将例1的逻辑转换如下:
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

TYPES: TY_TABLE TYPE TABLE OF TY_DATA WITH DEFAULT KEY.

LT_DATA = VALUE #(
                   ( MATNR = 'MATRIAL-001'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'FIRST material' )
                   ( MATNR = 'MATRIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                   ( MATNR = 'MATRIAL-003'
                     MTART = 'WATR'
                     MATKL = '1040'
                     TEXT1 = 'THIRD material' )
                  ).

DATA(LT_FOR) = VALUE TY_TABLE(
                                FOR LW_DATA IN LT_DATA
                                INDEX INTO LV_INDEX
                                WHERE ( MTART = 'WATR' )
                                ( VALUE #( BASE LW_DATA
                                           MATKL = CONV #( LV_INDEX ) ) )
                              ).
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FOR ).

GROUP BY(FOR LOOP)

在 LOOP 语句中使用 GROUP BY 实现分组处理数据

*---------------------------------------------------------------------*
* 在 LOOP 中使用 GROUP BY 后,LWA_DATA 中不会存储相应的数据,同样,如果使用 FIELD-SYMBOL,
* 也不会被分配,如果需要修改内表数据,只能通过每个组进行修改,对内表数据进行分组时,
* 可通过 ASCENDING / DESCENDING 按组排序,否则按前后的顺序依次输出;
* GROUP BY 在需要使用多个字段进行分组时:GROUP BY  (  KEY1 = field1  KEY2 = field2  …  )
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
         MATNR TYPE MARA-MATNR,
         MTART TYPE MARA-MTART,
         MATKL TYPE MARA-MATKL,
         TEXT1 TYPE CHAR50,
       END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

DATA LT_TABLE TYPE TABLE OF TY_DATA.


LT_DATA = VALUE #( ( MATNR = 'Material-001'
                     MTART = 'FOOD'
                     MATKL = '1020'
                     TEXT1 = 'FIRST' )
                   ( MATNR = 'Material-002'
                     MTART = 'WATR'
                     MATKL = '1030'
                     TEXT1 = 'SECOND' )
                   ( MATNR = 'Material-003'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'THIRD' ) ).

LOOP AT LT_DATA INTO DATA(LWA_DATA) GROUP BY LWA_DATA-MATKL INTO DATA(G1).
  LT_TABLE = VALUE #( FOR LWA_TABLE IN GROUP G1 ( LWA_TABLE ) ).

  CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_TABLE ).
ENDLOOP.
*---------------------------------------------------------------------*
* 如果需要根据自定义条件进行分组,可以使用 COND 语句将特定条件转换成字符或数字再进行分组
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
         MATNR TYPE MARA-MATNR,
         MTART TYPE MARA-MTART,
         MATKL TYPE MARA-MATKL,
         TEXT1 TYPE CHAR50,
       END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.
DATA LT_TABLE TYPE TABLE OF TY_DATA.

LT_DATA = VALUE #( ( MATNR = 'Material-001'
                     MTART = 'FOOD'
                     MATKL = '1020'
                     TEXT1 = 'FIRST' )
                   ( MATNR = 'Material-002'
                     MTART = 'WATR'
                     MATKL = '1030'
                     TEXT1 = 'SECOND' )
                   ( MATNR = 'Material-003'
                     MTART = 'WATR'
                     MATKL = '1010'
                     TEXT1 = 'THIRD' ) ).

LOOP AT LT_DATA INTO DATA(LWA_DATA)
                GROUP BY COND STRING( WHEN LWA_DATA-MATKL = '1020' THEN 'A'
                                      ELSE 'B' ) DESCENDING INTO DATA(G1).

  LT_TABLE = VALUE #( FOR LWA_TABLE IN GROUP G1 ( LWA_TABLE ) ).

  CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_TABLE ).
ENDLOOP.
*---------------------------------------------------------------------*
*    修改内表数据示例如下,第一层 LOOP 遍历的是每个组,
*      第二层遍历的是对应组里的数据,我们需要在第二层做变更
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
         MATNR TYPE MARA-MATNR,
         MTART TYPE MARA-MTART,
         MATKL TYPE MARA-MATKL,
         TEXT1 TYPE CHAR50,
       END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

LT_DATA = VALUE #( ( MATNR = 'Material-001'
                     MATKL = '1020'
                     TEXT1 = 100 )
                   ( MATNR = 'Material-002'
                     MATKL = '1030'
                     TEXT1 = 200 )
                   ( MATNR = 'Material-003'
                     MATKL = '1020'
                     TEXT1 = 300 ) ).

LOOP AT LT_DATA INTO DATA(LWA_DATA) GROUP BY LWA_DATA-MATKL ASCENDING INTO DATA(G1).
  DATA(LV_COUNT) = REDUCE #( INIT LV_INDEX = 0
                             FOR LWA_GROUP IN GROUP G1
                             NEXT LV_INDEX = LV_INDEX + LWA_GROUP-TEXT1 ).
  LOOP AT GROUP G1 ASSIGNING FIELD-SYMBOL(<FS_LINE>).
    <FS_LINE>-TEXT1 = LV_COUNT.
  ENDLOOP.
ENDLOOP.

CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_DATA ).

FILTER

使用 FILTER 根据条件来过滤内表数据

*---------------------------------------------------------------------*
* 使用 FILTER 时,待过滤的内表结构至少需要有一个用于访问的 SORTED KEY 或 HASHED KEY,
* 否则不能通过语法检查,另外,在 WHERE 条件中运算符两边的字段类型需要完全兼容,否则也不能通过语法检查;
* 根据条件进行过滤的功能可以使用 VALUE 嵌套 FOR 语句实现,而且不用考虑内表的键值问题
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
         MATNR TYPE MARA-MATNR,
         MTART TYPE MARA-MTART,
         MATKL TYPE MARA-MATKL,
         TEXT1 TYPE CHAR50,
       END OF TY_DATA.
DATA:LT_DATA TYPE TABLE OF TY_DATA WITH KEY MATNR WITH NON-UNIQUE SORTED KEY MATKL COMPONENTS MATKL.

LT_DATA = VALUE #( ( MATNR = 'Material-001'
                     MATKL = '1020'
                     TEXT1 = 100 )
                   ( MATNR = 'Material-002'
                     MATKL = '1030'
                     TEXT1 = 200 )
                   ( MATNR = 'Material-003'
                     MATKL = '1020'
                     TEXT1 = 300 ) ).

DATA(LT_FILTER) = FILTER #( LT_DATA USING KEY MATKL WHERE MATKL = CONV MATKL( '1020' ) )."CONV对值进行类型转换
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FILTER ).

DATA(LT_FILTER2) = FILTER #( LT_DATA USING KEY MATKL WHERE MATKL > CONV MATKL( '1020' ) )."可以填入比较运算符
CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LT_FILTER2 ).
*---------------------------------------------------------------------*
* 参照内表来过滤数据时,被参照的内表仍然需要有 SORTED KEY 或 HASHED KEY,对于过滤数据的内表没有键值要求;
* 在 FILTER 语句中可以通过 EXCEPT 关键字来指定是需要过滤数据还是保留数据;
* 该语法可以实现 FOR ALL ENTRIES IN,但是需要将数据全部取出,影响性能,
* 不建议使用,且在 ABAP 7.52 后,允许将内表作为数据源,可以用来代替 FOR ALL ENTRIES IN 使用
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
         MATNR TYPE MARA-MATNR,
         MTART TYPE MARA-MTART,
         MATKL TYPE MARA-MATKL,
         TEXT1 TYPE CHAR50,
       END OF TY_DATA.
DATA: LT_DATA TYPE SORTED TABLE OF TY_DATA WITH UNIQUE KEY MATNR,
      LT_TEMP TYPE TABLE OF TY_DATA.

LT_DATA = VALUE #( ( MATNR = 'MATERIAL-001' MATKL = '1020' )
                   ( MATNR = 'MATERIAL-002' MATKL = '1030' )
                   ( MATNR = 'MATERIAL-003' MATKL = '1020' ) ).

LT_TEMP = VALUE #( ( MATNR = 'MATERIAL-002' MATKL = '1000' )
                   ( MATNR = 'MATERIAL-002' MATKL = '1040' )
                   ( MATNR = 'MATERIAL-004' MATKL = '1020' ) ).

DATA(LT_FILTER) = FILTER #( LT_TEMP IN LT_DATA WHERE MATNR = MATNR ).
DATA(LT_EXCEPT) = FILTER #( LT_TEMP EXCEPT IN LT_DATA WHERE MATNR = MATNR ).

EXACT

关键字 EXACT 可以用来检查操作语句返回值是否存在丢失,如果存在丢失则会抛出异常

*---------------------------------------------------------------------*
* 抛出异常的范围比CONV更广,例如将CHAR10的数据赋值到CHAR1时,
* 因此在使用时需要注意异常的捕获,如果没有特殊的处理或属性需求,
* 可以直接使用父类异常 CX_SY_CONVERSION_ERROR 进行捕获.
* 判断数值语句是否被精确计算,如下实际抛出的异常是CX_SY_CONVERSION_ROUNDING,
* 获取到该异常类中的属性字段VALUE的数据
*---------------------------------------------------------------------*

TRY .
    DATA(LV_DATA) = EXACT #( 3 * ( 1 / 3 ) ).
  CATCH CX_SY_CONVERSION_ROUNDING INTO DATA(LO_EXCEPT).
    DATA(LV_RESULT) = LO_EXCEPT->VALUE.
ENDTRY.
*---------------------------------------------------------------------*
* 字段类型不兼容时,如下实际抛出的异常是 CX_SY_CONVERSION_NO_NUMBER,
* 这里获取的是父类CX_SY_CONVERSION_ERROR中的返回信息
*---------------------------------------------------------------------*
TYPES LTY_NUM TYPE N LENGTH 10.
TRY .
    DATA(LV_NUM) = EXACT LTY_NUM( '4 Apples' ).
  CATCH CX_SY_CONVERSION_ERROR INTO DATA(LO_CONVERT).
    DATA(LV_ERROR) = LO_CONVERT->GET_TEXT( ).
ENDTRY.

LET

使用 LET 引入短生命周期变量,可以用来简化部分冗余代码

*---------------------------------------------------------------------*
*    LET关键字可以使用在VALUE,SWITCH,COND等语句中;
*   与 FOR 语句类似,LET 语句中定义的临时变量同样只能在当前语句中使用,
*   在其他语句中使用时会检查出语法错误
*---------------------------------------------------------------------*
TYPES: BEGIN OF TY_DATA,
  MATNR TYPE MARA-MATNR,
  MTART TYPE MARA-MTART,
  MATKL TYPE MARA-MATKL,
  TEXT1 TYPE CHAR50,
  END OF TY_DATA.

DATA LT_DATA TYPE TABLE OF TY_DATA.

LT_DATA = VALUE #(
                   ( MATNR = 'MATRIAL-001'
                     MTART = 'WATR'
                     MATKL = '1020'
                     TEXT1 = 'FIRST material' )
                   ( MATNR = 'MATRIAL-002'
                     MTART = 'FOOD'
                     MATKL = '1030'
                     TEXT1 = 'SECOND material' )
                   ( MATNR = 'MATRIAL-003'
                     MTART = 'WATR'
                     MATKL = '1040'
                     TEXT1 = 'THIRD material' )
                  ).

DATA(LW_LINE) = VALUE TY_DATA( LET
                               LW_DATA = VALUE #( LT_DATA[ MATNR = 'MATRIAL-001' ] OPTIONAL )
                               LV_STR  = 'Date:' && SY-DATUM
                               IN
                               MATNR = LW_DATA-MATNR
                               MTART = LW_DATA-MTART
                               MATKL = LW_DATA-MATKL
                               TEXT1 = LV_STR
                              ).

CALL METHOD CL_DEMO_OUTPUT=>DISPLAY( LW_LINE ).

去前导零 加前导零

旧语法:
去字符串前导0:CONVERSION_EXIT_ALPHA_OUTPUT
加字符串前导0:CONVERSION_EXIT_ALPHA_INPUT

S4新语法:
去X的前导零:X = |{ X ALPHA = OUT }|
加X的前导零:X = |{ X ALPHA = IN }|

内表itab

IF line_exists( lt_auth_attach[ 1 ] ).
****
ENDIF.

内表追加数据

内表操作指定行赋值(不会改变系统变量SY-TABIX的值)

DATA(WA1) = LT_TAB[ 5 ].
CL_DEMO_OUTPUT=>DISPLAY( WA1 ).

变量类型转换

DATA(lv_task_id) = CONV ze_process_task_id( ls_data-task_id ).
DATA(LV_INT) = CONV i( '1' )

内表是否为空

IF NOT line_exists( para[ 1 ] ).  
  RETURN.  
ENDIF.

枚举ENUM

1.

TYPES:  
  BEGIN OF ENUM planet,  
    mercury,  
    venus,  
    earth,  
    mars,  
    jupiter,  
    saturn,  
    uranus,  
    neptune,  
  END OF ENUM planet.  
  
DATA planet type planet.  
  
planet = earth.  
  
cl_demo_output=>display( planet ).
  1. TYPES:  
      BEGIN OF ENUM number,  
        n0, n1 , n2 , n3, n4, n5, n6, n7, n8, n9,  
      END OF ENUM number.  
      
    DATA doma_values TYPE TABLE OF dd07v.  
    CALL FUNCTION 'DD_DOMVALUES_GET'  
      EXPORTING  
        domname   = 'S_NUMBERS'  
      TABLES  
        dd07v_tab = doma_values.  
      
    TRY.  
        LOOP AT doma_values INTO DATA(doma_value).  
          DATA(number) =  CONV number( CONV i( doma_value-domvalue_l ) ).  
          WRITE: number.  
        ENDLOOP.  
      CATCH cx_sy_conversion_no_enum_value.  
        cl_demo_output=>display(  
          |{ doma_value-domvalue_l } does not match| ).  
    ENDTRY.
  2. TYPES:  
      BEGIN OF ENUM planet STRUCTURE p,  
        mercury,  
        venus,  
        earth,  
        mars,  
        jupiter,  
        saturn,  
        uranus,  
        neptune,  
      END OF ENUM planet STRUCTURE p.  
      
    DATA planet type planet.  
      
    planet = p-earth.  
      
    cl_demo_output=>write( planet ).  
      
    cl_demo_output=>display( p ).

MESH

  
TYPES:  
  tt_root TYPE HASHED TABLE OF zrre_s_bo_caseh_c WITH UNIQUE KEY key,  
  tt_cljg TYPE HASHED TABLE OF zrre_d_bo_casecl WITH UNIQUE KEY db_key  
    WITH NON-UNIQUE SORTED KEY key_par COMPONENTS parent_key,  
  tt_fcp  TYPE HASHED TABLE OF zrre_d_bo_casefc WITH UNIQUE KEY db_key  
    WITH NON-UNIQUE SORTED KEY key_par COMPONENTS parent_key,  
  tt_seci TYPE HASHED TABLE OF zrre_d_bo_casesi WITH UNIQUE KEY db_key  
    WITH NON-UNIQUE SORTED KEY key_par COMPONENTS parent_key,  
  tt_retr TYPE HASHED TABLE OF zrre_d_bo_casert WITH UNIQUE KEY db_key  
    WITH NON-UNIQUE SORTED KEY key_par COMPONENTS parent_key,  
  
  BEGIN OF MESH ts_mesh,  
    root TYPE tt_root  
           ASSOCIATION _cljg TO cljg ON parent_key = key USING KEY key_par  
           ASSOCIATION _fcp TO fcp ON parent_key = key USING KEY key_par  
           ASSOCIATION _seci TO seci ON parent_key = key USING KEY key_par  
           ASSOCIATION _retr TO retr ON parent_key = key USING KEY key_par,  
    cljg TYPE tt_cljg,  
    fcp  TYPE tt_fcp,  
    seci TYPE tt_seci,  
    retr TYPE tt_retr,  
  END OF MESH ts_mesh.  
DATA ls_mesh TYPE ts_mesh.  
  
  
  
SELECT FROM zrre_d_bo_caseh  
  FIELDS db_key AS key, ajlx, wfsf, ref_anyoui_ayno, pzrq, ajjb, bdje  
       WHERE ajzt NE @zrre_cl_ac_case_controller=>cs_ajzt-draft  
      AND   appstatus = @/rer/if_ccm_constants=>gc_appstatus-approved  
      AND   ajly NE @zrre_cl_ac_case_controller=>cs_ajly-from_sfsj  
      INTO CORRESPONDING FIELDS OF TABLE @ls_mesh-root.  
CHECK sy-subrc EQ 0.  
"FCP  
SELECT FROM zrre_d_bo_casefc AS a INNER JOIN @ls_mesh-root AS b  
  ON a~parent_key = b~key  
  FIELDS a~*  
  INTO CORRESPONDING FIELDS OF TABLE @ls_mesh-fcp.  
"CLJG  
SELECT FROM zrre_d_bo_casecl AS a INNER JOIN @ls_mesh-root AS b  
  ON a~parent_key = b~key  
  FIELDS a~db_key, a~parent_key, a~ssfy, a~sfja, a~whss, a~jarq, a~jafs  
  INTO CORRESPONDING FIELDS OF TABLE @ls_mesh-cljg.  
"SECI  
SELECT FROM zrre_d_bo_casesi AS a INNER JOIN @ls_mesh-root AS b  
  ON a~parent_key = b~key  
  FIELDS a~db_key, a~parent_key, a~ssfy, a~sfja, a~whss, a~jarq, a~jafs  
  INTO CORRESPONDING FIELDS OF TABLE @ls_mesh-seci.  
"RETR  
SELECT FROM zrre_d_bo_casert AS a INNER JOIN @ls_mesh-root AS b  
  ON a~parent_key = b~key  
  FIELDS a~db_key, a~parent_key, a~ssfy, a~sfja, a~whss, a~jarq, a~jafs  
  INTO CORRESPONDING FIELDS OF TABLE @ls_mesh-retr.  
  
  
LOOP AT ls_mesh-root ASSIGNING FIELD-SYMBOL(<fs_root>).  
  IF VALUE #( ls_mesh-root\_cljg[ <fs_root> ]-sfja OPTIONAL ) EQ abap_true  
    OR VALUE #( ls_mesh-root\_seci[ <fs_root> ]-sfja OPTIONAL ) EQ abap_true  
    OR VALUE #( ls_mesh-root\_retr[ <fs_root> ]-sfja OPTIONAL ) EQ abap_true.  
  
  ENDIF.  
  
  LOOP AT ls_mesh-root\_retr[ <fs_root> ] INTO DATA(ls_cljg).  
  
  ENDLOOP.  

ENDLOOP.

WITH

Common Table Expressions (CTE)
SQL Enhancements:

  1. Higher expresiveness in Open SQL ABAP SQL statements, ABAP 7.53,>Open SQL已经被更名为ABAP SQL。这个重命名反映出ABAP SQL的某些部分目前只支持特定的数据库平台(SAP HANA数据库),已经不再是全平台独立的了。游标cursor和存储过程属于native SQL;
  2. Code pushdown through new language features
  3. Reduction of existing limitations
  4. Flexible consumption of CDS modeling entities
    在from子句使用子查询;
" CTE, coalensce, max join 50, max sub_query 50;
  DATA: lv_empty(8) TYPE c.
  WITH  +mara( matnr, ersda, nul ) AS 
          ( SELECT matnr, ersda, lvorm  FROM mara  ), "UP TO 3 ROWS
        +m2( matnr, coalesce ) AS 
          ( SELECT matnr, @lv_empty AS coalesce FROM +mara )

  SELECT FROM +mara AS mara LEFT OUTER JOIN  +m2 AS m2 ON mara~matnr = m2~matnr
  FIELDS  mara~matnr, 
          coalesce( '截胡', “ 如果不为空则获取此变量并跳出coalesce,否则看下一个参数;
              CASE m2~coalesce WHEN  @lv_empty THEN mara~nul END, “如果为null则看下一个
              m2~coalesce, “如果为空则看下一个
              'coalesce' ) AS col2
  INTO TABLE @DATA(lt_data6)
  UP TO 8 ROWS
  .
  cl_demo_output=>display( lt_data6 ).

WITH  
      +cte_simple_tree_source AS  
        ( SELECT FROM @__mt_tree_source AS tree_source  
                 FIELDS id,  
                        parent_id AS parent,  
                        name )  
           WITH ASSOCIATIONS (  
             JOIN TO MANY +cte_simple_tree_source AS _tree  
               ON +cte_simple_tree_source~parent = _tree~id ),  
      +tree AS  
        ( SELECT FROM HIERARCHY(  
            SOURCE +cte_simple_tree_source  
            CHILD TO PARENT ASSOCIATION _tree  
            START WHERE id = '0'  
            SIBLINGS ORDER BY id  
            MULTIPLE PARENTS NOT ALLOWED ) AS cte_hierarchy  
            FIELDS *  )  
            WITH HIERARCHY cte_hierarchy  
      SELECT FROM +tree "hierarchy  
             FIELDS +tree~*,  
                    hierarchy_rank,  
                    hierarchy_tree_size,  
                    hierarchy_parent_rank,  
                    hierarchy_level,  
                    hierarchy_is_cycle,  
                    hierarchy_is_orphan,  
                    node_id,  
                    parent_id  
             INTO TABLE @DATA(lt_hierarchy).

ABAP SQL

Host Variable/Expression

在查询语句中使用 @ 作为转义符
通常在查询语句中,程序声明(非数据库层级)的变量前需要使用转义符 @ 进行标识,这些 Host Variable 通常被用作 Open SQL 语句中的操作数
在查询语句内部声明结构/内表时,应该在 DATA 前使用转义符
此外,在使用 Host Expression(在 Open SQL 中作为操作数使用的一些表达式) 时,也需要添加转义字符,如下例所示
(注:表达式内部的变量不需要再使用转义符,且不能使用表达式外部的数据库对象)

SELECT matnr,
       @( COND #( WHEN sy-langu = 'E' THEN 'Material'
                  WHEN sy-langu = '1' THEN '物料' ) ) AS desc
  FROM mara
  INTO TABLE @DATA(lt_data)
  UP TO @( CONV #( '005' ) ) ROWS. 

SELECT matnr, maktx
  FROM makt
  WHERE spras = @sy-langu
  AND matnr = @( VALUE #( lt_data[ 5 ]-matnr OPTIONAL ) )
  INTO TABLE @DATA(lt_desc). 

Typed Literals

(refer F1) 可以使用INTO table @DATA(lv_exist)新建变量接收数据,可以用类型文字语法在sql中新建all built-in ABAP Dictionary types常量;

SELECT *
    FROM dbtab
    WHERE char_col = char`hallo`
    AND int_col = int8`123`

Built-In Functions

在 SELECT LIST 使用内嵌表达式来处理数值,使用时需注意传入参数的类型

DATA(lv_dec) = CONV zdec_3_demo( '-123.456' ).

SELECT SINGLE
       @lv_dec AS original,
       abs( @lv_dec ) AS abs,
       ceil( @lv_dec ) AS ceil,
       floor( @lv_dec ) AS floor,
       div( -4 , -3 ) AS div,
       division( -4 , -3 , 2 ) AS division,
       mod( -4 , -3 ) AS mod,
       round( @lv_dec , 2 ) AS round_po,
       round( @lv_dec , -2 ) AS round_ne
  FROM sflight
  INTO @DATA(lwa_data). 

SELECT concat( carrid , currcode ) AS concat,
       carrid && currcode AS concat_sign,
       concat_with_space( carrid , currcode , 1 ) AS with_space,
       instr( carrid , 'BA' ) AS instr,
       left( carrname , 4 ) AS left,
       right( carrname , 4 ) AS right,
       length( carrname ) AS length
  FROM scarr
  INTO TABLE @DATA(lt_data)
    UP TO 5 ROWS. 

DATA(lv_date) = CONV datum( '20181022' ). 

SELECT fldate AS original_date,
       dats_is_valid( fldate ) AS valid_date,
       tims_is_valid( @sy-uzeit ) AS valid_time, 
       dats_days_between( fldate , @lv_date ) AS between,
       dats_add_days( fldate , 10 ) AS add_days,
       dats_add_months( fldate , 3 ) AS add_months
  FROM sflight
  INTO TABLE @DATA(lt_data)
    UP TO 3 ROWS.

DATA(lv_stamp_now) = CONV timestamp( '20190603133559' ).
DATA(lv_stamp_past) = CONV timestamp( '20190602161408' ).

SELECT tstmp_is_valid( @lv_stamp_now ) AS valid_stamp,
       tstmp_current_utctimestamp( ) AS current_stamp,
       tstmp_seconds_between( tstmp1 = @lv_stamp_past,
                              tstmp2 = @lv_stamp_now,
                              on_error = @sql_tstmp_seconds_between=>set_to_null ) AS between,
       tstmp_add_seconds( tstmp = @lv_stamp_now,
                          seconds = @( CONV timestamp( 999 ) ),
                          on_error = @sql_tstmp_add_seconds=>set_to_null ) AS add_second
  FROM sflight
  INTO TABLE @DATA(lt_data)
    UP TO 1 ROWS.

SELECT abap_user_timezone( user = @( CONV uname( 'JIANGRE' ) ),
                           client = '130',
                           on_error = @sql_abap_user_timezone=>set_to_null ) AS user_zone,
       abap_system_timezone( client = '130',
                             on_error = @sql_abap_system_timezone=>set_to_null ) AS sys_zone
  FROM sflight
  INTO TABLE @DATA(lt_data)
    UP TO 1 ROWS. 

DATA(lv_stamp) = CONV timestamp( '20190603133559' ).

SELECT tstmp_to_dats( tstmp = @lv_stamp,
                      tzone = @( CONV tznzone( 'CET' ) ) ) AS dats,
       tstmp_to_tims( tstmp = @lv_stamp,
                      tzone = @( CONV tznzone( 'CET' ) ) ) AS tims,
       tstmp_to_dst( tstmp = @lv_stamp,
                     tzone = @( CONV tznzone( 'CET' ) ) ) AS dst,
       dats_tims_to_tstmp( date = @sy-datum,
                           time = @sy-uzeit,
                           tzone = @( CONV tznzone( 'CET' ) ) ) AS tstmp
  FROM sflight
  INTO TABLE @DATA(lt_data)
    UP TO 1 ROWS. 

WHERE Condition

WHERE与HAVING的区别:WHERE 子句的搜索条件在进行分组操作之前应用;而 HAVING 的搜索条件则在进行分组操作之后应用

常用的条件语句,整理如下:
[ NOT ] IN:除了 SELECT-OPTION,也支持多个自定义或者通过子查询获取的值
ANY/SOME/ALL:允许将子查询获取到的结果集作为限制条件使用
[ NOT ] BETWEEN … AND …:使用范围条件
[ NOT ] LIKE … [ ESCAPE ]:使用模糊查询
IS [ NOT ] NULL:判断是否有关联到相应的记录
IS [ NOT ] INITIAL:判断是否为空值
[ NOT ] EXISTS:根据指定条件到数据库表查询数据,判断查询结果是否存在
like_regexpr:匹配正则表达式

SELECT i~*
  FROM scarr AS r
  LEFT OUTER JOIN spfli AS i ON i~carrid = r~carrid
 WHERE i~carrid IN ( 'AA' , 'CO' )
   AND i~carrid IN ( SELECT DISTINCT carrid FROM sflight ) 
   AND i~carrid = ANY ( SELECT DISTINCT carrid FROM sflight )
   AND i~carrid NOT BETWEEN 'BA' AND 'CA'
   AND ( i~carrid LIKE 'A%' OR i~carrid LIKE 'C%' )
   AND r~carrid IS NOT NULL
   AND i~carrid IS NOT INITIAL
   AND EXISTS ( SELECT carrid FROM sflight WHERE carrid = i~carrid )
  INTO TABLE @DATA(lt_data). 
************
where like_regexpr( pcre = '^[Aa]\s*\w+', value = mara~matnr ) > 0
更多精彩尽在se26: CL_ABAP_MATCHER, CL_ABAP_REGEX; 

JOIN Expression

在特定的应用场景中,需要使用字符长度不一致的两个字段进行关联时,可以使用相应的表达式处理,但要注意表达式的位置,一般需要放在等式左边,如下例

"例:(NAST-OBJKY类型为CHAR30,EKKO-EBELN类型为CHAR10)
SELECT k~ebeln,
       t~kschl
  FROM nast AS t
 INNER JOIN ekko AS k ON left( t~objky, 10 ) = k~ebeln
  INTO TABLE @DATA(lt_data).

Aggregate Expressions

聚合表达式用于对一组值执行计算并返回单一的值,可以使用在SELECT或HAVING子句中,不能用在WHERE子句

常见的聚合表达式如下,表达式内部可选用DISTINCT对数据去重后再进行处理:
AVG:返回结果集的平均值,返回类型默认为浮点型,可通过AS语句返回指定类型,如DEC,CURR,QUAN或FLTP
MAX:返回结果集的最大值
MIN:返回结果集的最小值
SUM:返回结果集的汇总值
COUNT:返回结果集的条目数,通常情况下使用COUNT( * ) / COUNT(*),需要使用DISTINCT时则要指定字段名

更多聚合函数,尽在F1:
… AVG, MEDIAN, MAX, MIN, SUM, PRODUCT, STDDEV, VAR, CORR, CORR_SPEARMAN,
STRING_AGG, COUNT, GROUPING( col ), ALLOW_PRECISION_LOSS( … )