Introduction to New SAP ABAP Syntax
A Beginner’s Guide to ABAP 7.4+ and Beyond

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.
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!
