Wednesday, May 5, 2021

ABAP: Less Looping & Better Performance with the REDUCE Statement

 The  REDUCE statement in ABAP is a newer, more performant way of grouping numbers, strings and of course tables without nested looping and declaring too many additional helping variables.

In this post I am going to give some simple theorethical examples with numbers and strings and one more business related example.


  • Simple Sum
This is a rather theorethical example, that will help us understand the syntax. Here we want to sum the numbers from 1 to 100.
The result is the variable lv_i of type i.
The sum is in the variable s and is only valid in the reduce statement. 
j is our counter and so we go.Execute and you will get 5050 .


 data(lv_i=  reduce iinit s 0
  for until j > 100
    next s + j ).

  • String operation
This is another theorethical example. It demonstrates that we can use the reduce statement for non-arithmetic operations.


DATA(lv_textREDUCE stringINIT text | |
                              FOR |AB| THEN t && |0|
                                          UNTIL strlen10
                              NEXT text text && |{ t }| && |,).
The result is:

  • Business-related example
Let us take an example from the accounting and assume that we want to sum the amounts per trading partner. For simplicity, we will do this in one company code.

This is what my code would look like by using the old LOOP AT NEW statement:

TYPESBEGIN OF ty_sum,
*        bukrs TYPE bukrs,
         vbund TYPE rassc,
         dmbtr TYPE dmbtr,
       END OF ty_sum.


DATAlt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY vbund,
      ls_sum TYPE ty_sum.

TYPESBEGIN OF ty_bsis,
         vbund TYPE bsis-vbund,
         bukrs TYPE bsis-bukrs,
         gjahr TYPE bsis-grant_nbr,
         belnr TYPE bsis-belnr,
         shkzg TYPE bsis-shkzg,
         dmbtr TYPE bsis-dmbtr,
       END OF ty_bsis.

DATA lt_bsis TYPE SORTED TABLE OF ty_bsis WITH NON-UNIQUE KEY vbund.



SELECT vbundbukrsgjahrbelnrshkzgdmbtr FROM bsis INTO CORRESPONDING FIELDS OF TABLE @lt_bsis
  WHERE bukrs EQ 'XXXX' AND gjahr '????' AND vbund NE @space.

CHECK lineslt_bsis 0.

GET RUN TIME FIELD tstart.
LOOP AT lt_bsis INTO DATA(ls_bsis).
  AT NEW vbund.
    ls_sum-vbund ls_bsis-vbund.
    LOOP AT lt_bsis ASSIGNING FIELD-SYMBOL(<fs_bsis>WHERE vbund ls_bsis-vbund.
      ls_sum-dmbtr ls_sum-dmbtr + COND dmbtrWHEN <fs_bsis>-shkzg EQ 'S' THEN <fs_bsis>-dmbtr ELSE <fs_bsis>-dmbtr * -).
    ENDLOOP.
    APPEND ls_sum TO lt_sum.
    CLEAR ls_sum.
  ENDAT.
ENDLOOP.

* Record end time
GET RUN TIME FIELD tstop.

trun tstop tstart ).

The same with REDUCE:

The filter statement generates an internal with the desired records only, so that we can avoid the LOOP AT ...WHERE situation above.

LOOP AT lt_sum ASSIGNING FIELD-SYMBOL(<fs_sum_>).
  <fs_sum_>-dmbtr REDUCE dmbtrINIT val TYPE dmbtr
                                FOR ls_bsis_r IN
                                FILTER #lt_bsis
                                          WHERE vbund EQ  <fs_sum_>-vbund )
                                NEXT val val + COND dmbtrWHEN ls_bsis_r-shkzg EQ 'S' THEN ls_bsis_r-dmbtr
                                                             ELSE ls_bsis_r-dmbtr * -).
  WRITE<fs_sum_>-vbund<fs_sum_>-dmbtr NEW-LINE.
ENDLOOP.

The result is cleaner and more performant code!