Skip to main content

Command Palette

Search for a command to run...

Introduction to New SAP ABAP Syntax

A Beginner’s Guide to ABAP 7.4+ and Beyond

Published
12 min read
Introduction to New SAP ABAP Syntax
D

ABAP Developer | SAP Enthusiast | Writing about modern ABAP, clean code, different concepts in ABAP, real project learnings and challenges faced.

Since SAP introduced ABAP 7.4, a new and modern ABAP syntax was added. Many developers still use traditional ABAP syntax but the new syntax can increase the efficiency of the code, improve the readability and make code shorter and cleaner.

In this blog I will explain some of the most useful new ABAP features which I came across and found useful with simple old vs new examples.


1) Inline Declarations:

It allows us to declare variables exactly where they are used, so no more declaring variables manually. Always helpful when we are not sure about the datatype of the objects to be declared(specially when working with OO objects).

Variable declaration: Assign the value directly with DATA keyword.

*Old Syntax
DATA: lv_vbeln TYPE vbak-vbeln.

lv_vbeln = '1234567890'.

*New Syntax
DATA(lv_vbeln) = '1234567890'.

SELECT: Select into an internal table without declaring the TYPE.

*Old Syntax
TYPES: BEGIN OF ty_mara,
         matnr TYPE mara-matnr,
         erdsa TYPE mara-ersda,
         ernam TYPE mara-ernam,
       END OF ty_mara.

DATA: lt_mara TYPE STANDARD TABLE OF ty_mara.

  SELECT matnr ersda ernam
    FROM mara
    INTO TABLE lt_mara.

*New Syntax
  SELECT matnr, ersda, ernam
    FROM mara
    INTO TABLE @DATA(lt_mara).

SELECT SINGLE: Select into a variable/work area without declaring it.

*Old Syntax
DATA: lv_auart TYPE vbak-auart.

SELECT SINGLE 
       auart
  FROM vbak
  INTO lv_auart
  WHERE vbeln EQ '1234567890'.

*New Syntax
SELECT SINGLE 
       auart
  FROM vbak
  INTO @DATA(lv_auart)
  WHERE vbeln EQ '1234567890'.

LOOP, READ and FIELDSYMBOLS: Use the work area and field-symbol without declaring it.

DATA: lt_vbak TYPE STANDARD TABLE OF vbak,
      lt_vbap TYPE STANDARD TABLE OF vbap,
      lt_makt TYPE STANDARD TABLE OF makt.

*Old Syntax
DATA: ls_vbak TYPE vbak,
      ls_vbap TYPE vbap.
FIELD-SYMBOLS: <ls_makt> TYPE makt.

LOOP AT lt_vbap INTO ls_vbap.

  READ TABLE lt_vbak INTO ls_vbak WITH KEY vbeln = ls_vbap-vbeln.
  IF sy-subrc EQ 0.
    "Logic
  ENDIF.

  READ TABLE lt_makt ASSIGNING <ls_makt> WITH KEY matnr = ls_vbap-matnr.
  IF sy-subrc EQ 0.
    "Logic
  ENDIF.
ENDLOOP.

*New Syntax
LOOP AT lt_vbap INTO DATA(ls_vbap).

  READ TABLE lt_vbak INTO DATA(ls_vbak) WITH KEY vbeln = ls_vbap-vbeln.
  IF sy-subrc EQ 0.
    "Logic
  ENDIF.

  READ TABLE lt_makt ASSIGNING FIELD-SYMBOL(<ls_makt>) WITH KEY matnr = ls_vbap-matnr.
  IF sy-subrc EQ 0.
    "Logic
  ENDIF.
ENDLOOP.

OO objects: Use the objects/variable without declaring.

*Old Syntax
DATA: lo_obj    TYPE REF TO zcl_my_class,
      lv_result TYPE i.

CREATE OBJECT lo_obj.

CALL METHOD lo_obj->calculate
  EXPORTING
    iv_value = 10
  RECEIVING
    rv_result = lv_result.

*New Syntax
DATA(lo_obj) = NEW zcl_my_class( ).

DATA(lv_result) = lo_obj->calculate( iv_value = 10 ).

2) Constructor Operators:

These are special expressions that construct values inline(structures, tables) without needing intermediate variables. They make ABAP much more concise and functional-style.

VALUE: It initializes the value for work area or internal tables.

TYPES: BEGIN OF ty_makt,
         matnr TYPE makt-matnr,
         maktx TYPE makt-maktx,
       END OF ty_makt,

       BEGIN OF ty_vbap,
         posnr  TYPE vbap-posnr,
         matnr  TYPE vbap-matnr,
         kwmeng TYPE vbap-kwmeng,
       END OF ty_vbap,

       BEGIN OF ty_vbak,
         vbeln TYPE vbak-vbeln,
         auart TYPE vbak-auart,
         posnr TYPE ty_vbap,
       END OF ty_vbak.

DATA: lt_fieldcat TYPE lvc_t_fcat.
*************************************Structure***********************************************
*Old Syntax
DATA: ls_makt TYPE ty_makt.

ls_makt-matnr = '12345678'.
ls_makt-maktx = 'Material'.

*New Syntax
DATA(ls_makt) = VALUE ty_makt( matnr = '12345678'
                               maktx = 'Material' ).

***********************************Nested Structure*****************************************
*Old Syntax
DATA: ls_vbak TYPE ty_vbak,
      ls_vbap TYPE ty_vbap.

ls_vbak-vbeln  = '1234567890'.
ls_vbak-auart  = 'ABCD'.
ls_vbap-posnr  = '10'.
ls_vbap-matnr  = '12345678'.
ls_vbap-kwmeng = '100'.
APPEND ls_vbap TO ls_vbak-posnr.

*New Syntax
DATA(ls_vbak) = VALUE ty_vbak( vbeln    = '1234567890'
                               auart    = 'ABCD'
                               posnr = VALUE #( posnr  = '10'
                                                matnr  = '12345678'
                                                kwmeng = '100' ) ).

************************************Range table*****************************************
TYPES: ty_r_matnr TYPE RANGE OF matnr.
DATA: lr_matnr TYPE ty_r_matnr.

*Old Syntax
DATA: ls_matnr LIKE LINE OF lr_matnr.

ls_matnr-sign   = 'I'.
ls_matnr-option = 'EQ'.
ls_matnr-low    = '12345667'.
APPEND ls_matnr TO lr_matnr.
CLEAR ls_matnr.

ls_matnr-sign   = 'I'.
ls_matnr-option = 'EQ'.
ls_matnr-low    = '12345668'.
APPEND ls_matnr TO lr_matnr.
CLEAR ls_matnr.

*New Syntax
lr_matnr = VALUE #( ( sign = 'I' option = 'EQ' low = '12345667' )
                    ( sign = 'I' option = 'EQ' low = '12345668' ) ).

************************************Internal Table*****************************************
*Old Syntax
DATA: ls_fieldcat TYPE lvc_s_fcat.

ls_fieldcat-col_pos   = 1.
ls_fieldcat-fieldname = 'VBELN'.
ls_fieldcat-scrtext_l = 'Sales Order'.
APPEND ls_fieldcat TO lt_fieldcat.
CLEAR ls_fieldcat.

ls_fieldcat-col_pos   = 2.
ls_fieldcat-fieldname = 'AUART'.
ls_fieldcat-scrtext_l = 'Order Type'.
APPEND ls_fieldcat TO lt_fieldcat.
CLEAR ls_fieldcat.

ls_fieldcat-col_pos   = 3.
ls_fieldcat-fieldname = 'ERDAT'.
ls_fieldcat-scrtext_l = 'Date'.
APPEND ls_fieldcat TO lt_fieldcat.
CLEAR ls_fieldcat.

*New Syntax
lt_fieldcat = VALUE #( ( col_pos = 1 fieldname = 'VBELN' scrtext_l = 'Sales Order' )
                       ( col_pos = 2 fieldname = 'AUART' scrtext_l = 'Order Type' )
                       ( col_pos = 3 fieldname = 'ERDAT' scrtext_l = 'Date' )

NEW: It is used to create an instance of a class.

*Old Syntax
DATA: lo_abap TYPE REF TO zcl_demo_class.
CREATE OBJECT lo_abap.

*New Syntax
DATA(lo_abap) = NEW zcl_demo_class( ).

CORRESPONDING: It is used to move data between two internal tables, with same or different structures. It can also be used for structures. The additional features like MAPPING, EXCEPT was not possible in old ABAP syntax.

TYPES: BEGIN OF ty_tab1,
         ebeln TYPE ekko-ebeln,
         bukrs TYPE ekko-bukrs,
         bsart TYPE ekko-bsart,
         lifnr TYPE ekko-lifnr,
       END OF ty_tab1,

       BEGIN OF ty_tab2,
         ebeln     TYPE ekko-ebeln,
         comp_code TYPE ekko-bukrs,
         plant     TYPE ekpo-werks,
         bsart     TYPE ekko-bsart,
         vendor    TYPE ekko-lifnr,
       END OF ty_tab2.

DATA: lt_tab1  TYPE TABLE OF ty_tab1,
      lt_tab2  TYPE SORTED TABLE OF ty_tab2 WITH UNIQUE KEY ebeln,
      lt_tab3  TYPE SORTED TABLE OF ty_tab2 WITH UNIQUE KEY ebeln,
      lt_tab4  TYPE TABLE OF ty_tab2.

SELECT ebeln,
       bukrs,
       bsart,
       lifnr
  FROM ekko
  INTO TABLE @lt_tab1
  UP TO 5 ROWS.

*Old Syntax
MOVE-CORRESPONDING lt_tab1 TO lt_tab2.

*New Syntax
lt_tab2 = CORRESPONDING #( lt_tab1 ).

"Mapping fields: The data will be moved even if the fieldnames don't match
lt_tab3 = CORRESPONDING #( lt_tab1 MAPPING comp_code = bukrs
                                           plant     = bukrs
                                           vendor    = lifnr  ).

"leaving one column blank
lt_tab4 = CORRESPONDING #( lt_tab1 MAPPING comp_code = bukrs
                                           plant     = bukrs
                                           vendor    = lifnr
                                    EXCEPT bsart ).   " bsart will be blank

CONV: It converts the value to the type specified. Here, there is no need of using helper variable.

PARAMETERS: p_amount TYPE dmbtr.
DATA: lv_words TYPE char255.

*Old Syntax
DATA: lv_amount TYPE maxbt.

lv_amount = p_amount.
CALL FUNCTION 'HR_IN_CHG_INR_WRDS'
  EXPORTING
    amt_in_num         = lv_amount        " Helper Variable       
  IMPORTING
    amt_in_words       = lv_words
  EXCEPTIONS
    data_type_mismatch = 1
    OTHERS             = 2.
IF sy-subrc NE 0.
* Implement suitable error handling here
ENDIF.

*New Syntax
"CONV OPERATOR(No need to use helper variable)
CALL FUNCTION 'HR_IN_CHG_INR_WRDS'
  EXPORTING
    amt_in_num         = CONV maxbt( p_amount )       "Write data element name after CONV
  IMPORTING
    amt_in_words       = lv_words
  EXCEPTIONS
    data_type_mismatch = 1
    OTHERS             = 2.
IF sy-subrc NE 0.
* Implement suitable error handling here
ENDIF.

FILTER: It filters the data and retrieves the subset of the data into a new internal table. In the old ABAP syntax, to achieve this we would need to LOOP the internal table and delete the rows.

💡
Note: The filter table which is used should either be a sorted or hashed table or it should be used with USING KEY. If there are more than two fields in the WHERE condition, and if that field/fields is not a key field, filter can give incorrect results.
TYPES: BEGIN OF ty_tab1,
         ebeln TYPE ekko-ebeln,
         bukrs TYPE ekko-bukrs,
         bsart TYPE ekko-bsart,
         lifnr TYPE ekko-lifnr,
       END OF ty_tab1,

       BEGIN OF ty_tab2,
         ebeln     TYPE ekko-ebeln,
         comp_code TYPE ekko-bukrs,
         plant     TYPE ekpo-werks,
         bsart     TYPE ekko-bsart,
         vendor    TYPE ekko-lifnr,
       END OF ty_tab2,

       BEGIN OF ty_vendor,
         lifnr TYPE lifnr,
         bukrs TYPE bukrs,
       END OF ty_vendor.

DATA: lt_tab1  TYPE TABLE OF ty_tab1,
      lt_tab2  TYPE SORTED TABLE OF ty_tab2 WITH UNIQUE KEY ebeln,
      lt_tab3  TYPE SORTED TABLE OF ty_tab2 WITH UNIQUE KEY ebeln,
      lt_tab4  TYPE TABLE OF ty_tab2.

DATA: lt_vendors TYPE SORTED TABLE OF ty_vendor WITH UNIQUE KEY lifnr
                 WITH NON-UNIQUE SORTED KEY bukrs COMPONENTS bukrs.

lt_vendors = VALUE #( ( lifnr = 'ABCD' bukrs = '1112' )
                      ( lifnr = 'WXYZ' bukrs = '1111' )
                      ( lifnr = 'LMNO' bukrs = '1112' ) ).
"Filter vendors belonging to bukrs 1112 
DATA(lt_filter_tab) = FILTER #( lt_vendors USING KEY bukrs WHERE bukrs = '1112' ).

SELECT ebeln, bukrs, bsart, lifnr
  FROM ekko
  INTO TABLE @lt_tab1
  UP TO 5 ROWS.

lt_tab2 = CORRESPONDING #( lt_tab1 ).
lt_tab3 = CORRESPONDING #( lt_tab1 MAPPING comp_code = bukrs
                                           plant     = bukrs
                                           vendor    = lifnr  ).

"Retain the rows which are present in LT_TAB2
lt_tab3 = FILTER #( lt_tab3 IN lt_tab2 WHERE ebeln EQ ebeln ).
"Retain the rows which are different from LT_TAB2
lt_tab1 = FILTER #( lt_tab1 EXCEPT IN lt_tab2 WHERE ebeln EQ ebeln ).

REDUCE: It creates a result of a specified data type using the type of one or more conditional expressions. It is also possible to do a mathematical operation grouping the items of a table. For Eg: We can get the sum of the columns of an internal table directly into the result column/variable. It can avoid loop inside loop between two tables.

  • INIT→ Initializes the variable i.

  • FOR→ Loops over the other table.

  • NEXT→ Updates the value of i in each iteration.

At the end of the code, all the amounts from BSID table are aggregated for every client(KUNNR) and stored in the corresponding rows of table LT_KNA1. This way, every client in table LT_KNA1 holds the total amount calculated from LT_BSID. Thus it eliminates the use of nested loop which would be the case in old ABAP syntax.

TYPES: ty_r_kunnr TYPE RANGE OF bsid-kunnr.

DATA: lr_kunnr TYPE ty_r_kunnr.

lr_kunnr = VALUE #( ( sign = 'I' option = 'BT' low = '12345667' high = '12345680' ) ).

SELECT bukrs,
       kunnr,
       CASE shkzg WHEN 'H' THEN ( dmbtr * -1 ) ELSE dmbtr END AS amount
  FROM bsid
  INTO TABLE @DATA(lt_bsid)
  WHERE kunnr IN @lr_kunnr.

  IF lt_bsid[] IS NOT INITIAL.
    SELECT kunnr, CAST( 0 AS DEC ) AS amount
      FROM kna1
      INTO TABLE @DATA(lt_kna1)
      WHERE kunnr IN @lr_kunnr.
    IF sy-subrc EQ 0.
      LOOP AT lt_kna1 ASSIGNING FIELD-SYMBOL(<ls_kna1>).
        <ls_kna1>-amount = REDUCE i( INIT i TYPE dmbtr 
                                     FOR ls_bsid IN lt_bsid WHERE ( kunnr = <ls_kna1>-kunnr )
                                     NEXT i = i + ls_bsid-amount ).
      ENDLOOP.
    ENDIF.
  ENDIF.

Conditional Operators(COND and SWITCH): We make use of these operators for conditional based logic. They are the replacement of IF-ELSE and CASE-ENDCASE in the old ABAP syntax.

  • COND→ It can be used as a replacement of IF-ELSE.

  • SWITCH→ It can be used as a replacement of CASE-ENDCASE.

DATA: lv_text TYPE char50.
*************************************COND Operator*********************************************
*Old Syntax
IF sy-msgty EQ 'S'.
  lv_text = 'Success'.
ELSEIF sy-msgty EQ 'E'.
  lv_text = 'Error'.
ELSEIF sy-msgty EQ 'W'.
  lv_text = 'Warning'.
ELSE.
  lv_text = 'Not defined'.
ENDIF.

*New Syntax
DATA(lv_text) = COND #( WHEN sy-msgty EQ 'S' THEN 'Success'
                        WHEN sy-msgty EQ 'E' THEN 'Error'
                        WHEN sy-msgty EQ 'W' THEN 'Warning'
                        ELSE 'Not defined' ).

************************************SWITCH Operator********************************************
*Old Syntax
CASE sy-msgty.
  WHEN 'S'.
    lv_text = 'Success'.
  WHEN 'E'.
    lv_text = 'Error'.
  WHEN 'W'.
    lv_text = 'Warning'.
  WHEN OTHERS.
    lv_text = 'Not defined'.
ENDCASE.

*New Syntax
DATA(lv_text) = SWITCH #( sy-msgty WHEN 'S' THEN 'Success'
                                   WHEN 'E' THEN 'Error'
                                   WHEN 'W' THEN 'Warning'
                                   ELSE 'Not defined' ).

3) Other Operators/Operations:

CONCATENATE: For old ABAP syntax, the concatenate statement only allowed operands of type (C, N, D, T, String), and for other data types it used to give syntax error. For new ABAP syntax, it is possible to concatenate data of any type.

DATA: lt_mara TYPE STANDARD TABLE OF mara.

*Old Syntax
DATA: ls_mara TYPE mara,
      lv_string1 TYPE string,
      lv_string2 TYPE string.

LOOP AT lt_mara INTO ls_mara.
  CONCATENATE 'Material:' ls_mara-matnr INTO lv_string1.  
  CONCATENATE 'Material & Article:' ls_mara-matnr 
                                    ls_mara-matkl INTO lv_string2 SEPARATED by space.
ENDLOOP.

*New Syntax
LOOP AT lt_mara INTO DATA(ls_mara).
  DATA(lv_string1)  = | Material:{ ls_mara-matnr } |.
  "Text not required in quote ' '
  DATA(lv_string2) = |Material & Article: | && ls_mara-matnr && ls_mara-matkl && ||. 
ENDLOOP.

ALPHA formatting: In old ABAP syntax to add/remove the leading zeros of a variable, we used to use two function modules.

CONVERSION_EXIT_ALPHA_INPUT → To add the leading zeros.
CONVERSION_EXIT_ALPHA_OUTPUT → To remove the leading zeros.

In new ABAP syntax, it has been replaced by ALPHA = OUT and ALPHA = IN.

DATA: lv_vbeln TYPE vbak-vbeln.
***********************************Add the Leading zeros*************************************
*Old Syntax
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
  EXPORTING
    input         = lv_vbeln
 IMPORTING
   OUTPUT         = lv_vbeln.

*New Syntax
lv_vbeln = |{ lv_vbeln ALPHA = IN }|.

*********************************Remove the Leading zeros*************************************
*Old Syntax
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
  EXPORTING
    input         = lv_vbeln
 IMPORTING
   OUTPUT         = lv_vbeln.

*New Syntax
lv_vbeln = |{ lv_vbeln ALPHA = OUT }|.

READ Statement: READ statement has been modernized to use table expressions and inline declarations, making code shorter and more readable.

DATA: lt_vbak TYPE STANDARD TABLE OF vbak.

*Old Syntax
DATA: ls_vbak TYPE vbak.
READ TABLE lt_vbak INTO ls_vbak WITH KEY vbeln = '1234567890'.
IF sy-subrc EQ 0.
  "Logic
ENDIF.

*New Syntax
DATA(ls_vbak) = lt_vbak[ vbeln = '1234567890' ].
"If the row does not exist for the above value, it will result in dump.
"It needs to be handled with OPTIONAL keyword. 
"This way it avoids dump even if the value is not present.
DATA(ls_vbak) = VALUE #( lt_vbak[ vbeln = '1234567890']-vbeln OPTIONAL ).

"Reads the Index 1
DATA(ls_vbak) = lt_vbak[ 1 ].

"READ only one field.
TRY.
    DATA(lv_vbeln) = lt_vbak[ 1 ]-vbeln.
  CATCH cx_root.
ENDTRY.

Union/Union ALL: Union is used to combine the results of separate select queries into one table. Instead of separate select queries, here in new ABAP syntax we can use one query.

  • Union → Does not select duplicate records.

  • Union ALL → Fetches duplicate records as well.

TABLES: bsid.

SELECT-OPTIONS: s_bukrs FOR bsid-bukrs,
                s_gjahr FOR bsid-gjahr.

IF s_bukrs[] IS NOT INITIAL AND
   s_gjahr[] IS NOT INITIAL.

  "Union and Union All
  SELECT bukrs,
         belnr,
         gjahr,
         shkzg,
         dmbtr,
         'BSID' AS table,   " Adds a new field in the table with column name TABLE
         CASE shkzg         " Adds a calculated value in the table column name AMOUNT
         WHEN 'H' THEN ( dmbtr * -1 ) ELSE dmbtr END AS amount
    FROM bsid
    WHERE bukrs IN @s_bukrs
      AND gjahr IN @s_gjahr

    UNION   " Union - Does not fetch duplicate records
            " Union ALL - Fetches duplicates as well
    SELECT bukrs,
           belnr,
           gjahr,
           shkzg,
           dmbtr,
           'BSAD' AS table, " Adds a new field in the table with column name TABLE
           CASE shkzg       " Adds a calculated value in the table column name AMOUNT
           WHEN 'H' THEN ( dmbtr * -1 ) ELSE dmbtr END AS amount
      FROM bsad
      WHERE bukrs IN @s_bukrs
        AND gjahr IN @s_gjahr
      INTO TABLE @DATA(lt_bsid_bsad).
  IF sy-subrc NE 0.
    REFRESH lt_bsid_bsad.
  ENDIF.
ENDIF.

LOOP AT GROUP BY: It allows us to group rows of an internal table by a field and then process each group separately. In the below example, the table will be grouped by VBELN’s(Sales Orders), and then each sales order will be processed for its line items.

SELECT a~vbeln,
       b~posnr,
       b~matnr
  FROM vbak AS a
  INNER JOIN vbap AS b
  ON a~vbeln EQ b~vbeln
  INTO TABLE @DATA(lt_vbak_vbap)
  WHERE a~auart EQ 'ABCD'
    AND a~erdat GE '20250505'.
IF sy-subrc NE 0.
  REFRESH lt_vbak_vbap.
ENDIF.

LOOP AT lt_vbak_vbap INTO DATA(ls_vbak_vbap) GROUP BY ls_vbak_vbap-vbeln ASCENDING.

  WRITE: / ls_vbak_vbap-vbeln COLOR 1.

  LOOP AT GROUP ls_vbak_vbap INTO DATA(ls_vbap).
    WRITE: / ls_vbap-posnr , ls_vbap-matnr.
  ENDLOOP.

ENDLOOP.

These new ABAP syntax improvements make the code cleaner, shorter and more efficient. Try using them in your next project!

Thank you for reading. Stay tuned for more!

P

Thanks for this detailed guide!

N
Nishant7mo ago

Really helpful piece of information, will be surely implement from now on...