Showing posts with label ABAP. Show all posts
Showing posts with label ABAP. Show all posts

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!









Wednesday, June 24, 2020

SAP ABAP: Object Oriented Design for Reports

In this post I would like to demonstrate the possibilty to use Object Oriented Code in a classic report.

I often face pseudo object orieted code in ABAP reports (classes containing only statis methods that serve as wrappers for procedural code, performs wrapped in methods).

That is why I would like demonstrate a simple and flexible OO solution, using local classes.


REPORT zxyz.
TABLESestvhestvaestrh.


SELECTION-SCREEN BEGIN OF SCREEN 900.
SELECT-OPTIONSs_estcat FOR estvh-estcat DEFAULT 'SAP_EHS_XYZ'.
SELECT-OPTIONSs_subid FOR estrh-subid.
SELECTION-SCREEN END OF SCREEN 900.


*--------------------------------
*CLASS lcl_main DEFINITION
*--------------------------------
CLASS lcl_main DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS :start_report.

  PRIVATE SECTION.
    METHODS fetch,
              check_auth,
              display.
    CLASS-DATA lr_main TYPE REF TO lcl_main.
    DATA it_table TYPE TABLE OF zasi_cds_count_inst.
ENDCLASS.                   
*------------------------------------
*       CLASS lcl_main IMPLEMENTATION
*-------------------------------------
CLASS lcl_main IMPLEMENTATION.
  METHOD start_report.
BREAK-POINT.
    CALL SELECTION-SCREEN 900.
    IF sy-subrc IS INITIAL.

      CREATE OBJECT lr_main.
      lr_main->fetch).
      lr_main->display).
    ENDIF.

  ENDMETHOD.                   
  METHOD fetch.
SELECT INTO TABLE @it_table FROM zasi_cds_count_inst WHERE subid IN @s_subid AND estcat IN @s_estcat.
  ENDMETHOD.                    
  METHOD check_auth.
-->>perform authorization checks here
  ENDMETHOD.
  METHOD display.
    DATA lr_table TYPE REF TO cl_salv_table.
    cl_salv_table=>factory(  IMPORTING    r_salv_table   lr_table
    CHANGING     t_table        =   me->it_table  )    .
    lr_table->display).
  ENDMETHOD.                    
ENDCLASS.  



START-OF-SELECTION.

  lcl_main=>start_report).


This solution provides more control and more flexibility compared to the procedural approach over events.

The selection screen is defined with a dedicated number and called later at start_report.
This allows flexibilty. We can define more selection screens and call the one we need based on authorization checks, for example. 

This approach might seem strange for those of us who are used to the START-OF-SELECTION, PERFORM xyz, END-OF-SELECTION type of programming but it is worth trying.