Skip to Content

New vs. Old ABAP Table Summarizing

 

Introduction

 

In this blog series, the goal is to focus on comparing older ABAP language features with a detailed explanation of the new ABAP syntax.

 

I recently wrote a program to explain older and newer ways to do a summary on an internal table. This blog will dissect that program. We will show the various ways to accomplish the same thing for a table summary, and how the ABAP language has evolved over the years. This blog will be useful for both new ABAPers, as well as experienced ABAPers trying to get familiar with the new ABAP.

 

This Blog is organized as follows:

  1. Program Overview
    1. Radio Button Options
    2. Record Generator
  2. Table Processing Options
    1. Standard Table
    2. Sorted Table
    3. New ABAP
  3. Performance
  4. Complete Program
  5. References

 

Software Versions:

  • 52 of SAP, SAPGUI Version 750.
  • Eclipse Version: Oxygen.1a Release (4.7.1a)

 

Program Overview

 

The program will have the following select options:

The program will perform the following steps:

  1. Take the “Number of Records” field from the selection screen and generate a table of sample records.
  2. Process the table according to the selected Radio Button option.
  3. Display the resulting summary table in ALV.

Newer ABAP supports method chaining, so to run the program, we simply do the following at START-OF-SELECTION:

START-OF-SELECTION.
  lcl_my_class=>main( )->execute( ).

Broken into parts:

  • lcl_my_class=>main( ) – Instantiates a new instance of LCL_MY_CLASS. This is a static factory method, which simply returns an instance of itself.
  • execute( ) – All of the main logic is contained in the EXECUTE method of LCL_MY_CLASS.

 

Radio button Options

  1. Standard Table: With this option, we will show the old school method for performing a summary by sorting and collecting.
  2. Sorted Table: With this option, we will do a read on a sorted table, then update the summary field using a field symbol.
  3. New ABAP: With this option, we will utilize the new ABAP language features to update the records and increment the summary field.

All 3 options, above, will perform the same logic, described below, but with different table reads and updates.

Here is an overview of what the 3 methods will do:

You may have guessed, this simply doubles the value in SUMMARY_FIELD, since the table was cloned from the original.

Example:

IT_TEST_TABLE, passed into the method (unsorted):

Resulting ALV (sorted by keys), after adding the temporary tables. Simply doubles the SumField (SUMMARY_FIELD):

When executing each of the 3 options, the results will be identical to the above.

 

Record Generator

All 3 of the above methods will use the following generated sample table. We will use the new ABAP features to create the random records with the following method lm_build_records:

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_build_records: Build a table with nn records...
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_build_records.
    DO p_recs TIMES.
      APPEND VALUE lty_struct( key1          = 900 + sy-index
                               key2          = sy-index
                               field1        = |A Text Field - { sy-index }|
                               a_date_field  = sy-datum + sy-index
                               summary_field = sy-index * 10 )
        TO et_test_table.
    ENDDO.
  ENDMETHOD. " lm_build_records

The parameter p_recs is the “Number of Records” field on the selection screen. To further explain the above new ABAP code, here is how the random records are generated…

We will loop and create P_RECS number of records.

For each loop pass, it will dynamically generate a new record of type LTY_STRUCT. No intermediate variables needed with the new ABAP.

VALUE lty_struct(…) – This tells the compiler to generate a new record of type LTY_STRUCT. This structure was declared in our private global section for our class:

  PRIVATE SECTION.
    TYPES: BEGIN OF lty_struct,
             key1          TYPE matnr,
             key2          TYPE rsrnumc10,
             field1        TYPE string,
             a_date_field  TYPE sydatum,
             summary_field TYPE i,
           END OF lty_struct,
           lty_tab  TYPE STANDARD TABLE OF lty_struct,
           lty_stab TYPE SORTED TABLE OF lty_struct WITH UNIQUE KEY key1 key2.

This will randomly generate the following values for the fields:

  • KEY1 – Take 900 plus the current loop pass number contained in SY-INDEX (i.e. 901, 902, etc.)
  • KEY2 – The current loop pass contained in SY-INDEX.
  • FIELD1 – The text “A Text Field”, plus the current loop pass number in SY-INDEX. The new string templates in the new ABAP, generates the exact string contained within the Pipe (|) symbols (no more CONCATENATE statement needed!). Also, within your string, you can dynamically specify a variable within your string within curly braces {}. So, |A Text Field – { sy-index }| will be “A Text Field – 1” on the first loop pass.
  • A_DATE_FIELD – Take today’s date and add the current loop pass number (SY-INDEX) in days.
  • SUMMARY_FIELD – Take the current loop pass multiplied by 10.

After this record is generated, it is appended to ET_TEST_TABLE, an export table parameter for the method.

 

Now, on to the table code…

 

Table Processing Options

 

Standard Table

The COLLECT statement was one original ABAP statement for adding numeric fields together in an internal table. If summarizing a standard table with no keys, it will compare all non-numeric fields, and will sum the numeric field. In our case, we have the following structure:

    TYPES: BEGIN OF lty_struct,
             key1          TYPE matnr,
             key2          TYPE rsrnumc10,
             field1        TYPE string,
             a_date_field  TYPE sydatum,
             summary_field TYPE i,
           END OF lty_struct,

Because SUMMARY_FIELD is our only numeric field, it will sum those values.

Example:

Table 1:

Table 2:

If adding the above 2 identical tables together with the COLLECT statement, it will give the following:

Summary Table:

Logic for the above results:

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_standard_table: Pre-740 Method to process a
* standard table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_standard_table.
    DATA: lt_temp_table TYPE lty_tab,
          lt_sum_table  TYPE lty_tab,
          lw_rec        TYPE lty_struct.
    lt_temp_table[] = it_test_table[].
    lt_sum_table[] = it_test_table[].
    SORT lt_temp_table BY key1 key2.
    SORT lt_sum_table BY key1 key2.

    "This will double the summary_field entries...
    LOOP AT lt_temp_table INTO lw_rec.
      COLLECT lw_rec INTO lt_sum_table.
    ENDLOOP.
    lm_display( CHANGING ct_report = lt_sum_table ).
  ENDMETHOD. " lm_process_standard_table

The above code for a Standard Table was a classical way of performing a summary, and should work in most older SAP versions. Of course, what if you have 2 numeric fields, but you only want to add up one of them? Let’s say we add an additional numeric field at the end, that we do not want to summarize. For example, let’s add a field named DO_NOT_ADD, which is also an integer:

Table 1:

Table 2:

Summary Table:

If we only wanted to add together the field SUMMARY_FIELD, then this would require a read and update of that record, and the COLLECT statement would not work. In this case, a Sorted Table or Hashed Table would be a good candidate to handle a fast read and update of existing records in an internal table.

 

Sorted Table

In this example, we will update a specific field, and summarize it by reading the record from a Sorted Table.

 

The code for a Sorted Table summary, to achieve the same results as the Standard Table example:

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_sorted_table: Pre-740 Logic to process a
* sorted table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_sorted_table.
    DATA: lt_temp_table TYPE lty_stab,
          lt_sum_table  TYPE lty_stab,
          lt_pass_table TYPE lty_tab,
          lw_rec        TYPE lty_struct.
    FIELD-SYMBOLS: <lw_rec> TYPE lty_struct.

    LOOP AT it_test_table INTO lw_rec.
      INSERT lw_rec INTO TABLE lt_temp_table.
      INSERT lw_rec INTO TABLE lt_sum_table.
    ENDLOOP.

    "This will double the summary_field entries...
    CLEAR lw_rec.
    LOOP AT lt_temp_table INTO lw_rec.
      UNASSIGN <lw_rec>.
      READ TABLE lt_sum_table ASSIGNING <lw_rec>
                              WITH TABLE KEY key1 = lw_rec-key1
                                             key2 = lw_rec-key2.
      IF <lw_rec> IS ASSIGNED.
        "Add the summary_field...
        <lw_rec>-summary_field = <lw_rec>-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.
    lt_pass_table[] = lt_sum_table[].
    lm_display( CHANGING ct_report = lt_pass_table ).
  ENDMETHOD. " lm_process_sorted_table

The above code uses a read on the sorted table using the keys and into a field symbol to update the SUMMARY_FIELD.

New ABAP

Finally, let’s look at the new ABAP way of achieving the same results. With 740, table indexes have been added with the bracket notation []. For example, to retrieve record #5 into a work area, you could specify:

data(lw_record5) = lt_sum_table[ 5 ].

For this table:

For Record 5, it would retrieve the following:

You can also specify values for your key fields to lookup a record.

Let’s try this:

data(lw_record5) = lt_sum_table[ key1 = '905' key2 = '5' ].

Ooops, that gives us a short dump saying “This line is not contained in the table”:

Because we are attempting to use a record that could not be found and assign it to the work area lw_record5, we get a short dump. To handle this, SAP added the “line_exists” function in order to verify that the line actually exists, before trying to use it. So, we must always do this:

    if line_exists( lt_sum_table[ key1 = '905' key2 = '5' ] ).
      data(lw_record5) = lt_sum_table[ key1 = '905' key2 = '5' ].
    endif.

 

We can now avoid a short dump.

But wait, why doesn’t it exist? As you guessed, if we look at the data type for key1 and key2, it is of type MATNR and RSRNUMC10:

If I copy the variable exactly as it appears in the Eclipse Debugger, I get:

KEY1 (MATNR) as:

“                                    905”

KEY2 is “0000000005”.

So, if we try the following, we can find the record:

    if line_exists( lt_sum_table[ key1 = '                                    905' key2 = '0000000005' ] ).
      data(lw_record5) = lt_sum_table[ key1 = '                                    905' key2 = '0000000005' ].
    endif.

So, the keys must match exactly, with leading zeros, spaces, etc.

Alternatively, you could do the following, and compare it to record 5 in our duplicate table:

data(lw_record5) = lt_sum_table[ key1 = lt_temp_table[ 5 ]-key1 key2 = lt_temp_table[ 5 ]-key2 ].

 

…enough on that, here is the method for new ABAP:

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_new_abap: Use New ABAP Features to process a table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_new_abap.
    DATA: lt_temp_table TYPE lty_stab,
          lt_sum_table  TYPE lty_stab,
          lt_pass_table TYPE lty_tab.

    LOOP AT it_test_table INTO DATA(lw_rec).
      INSERT lw_rec INTO TABLE lt_temp_table.
      INSERT lw_rec INTO TABLE lt_sum_table.
    ENDLOOP.

    "This will double the summary_field entries...
    "The bracket notation [] finds the record with that key, then we can update it...
    CLEAR lw_rec.
    LOOP AT lt_temp_table INTO lw_rec.
      IF line_exists( lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ] ).
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field =
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.
    lt_pass_table[] = lt_sum_table[].
    lm_display( CHANGING ct_report = lt_pass_table ).
  ENDMETHOD. " lm_process_new_abap

Performance

As I was looking at the following code, I became curious about performance:

      IF line_exists( lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ] ).
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field =
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field + lw_rec-summary_field.
      ENDIF.

Is the bracket notation, for the above, doing 3 reads on the same record, 3 different times? Perhaps we could eliminate these by assigning it to a field-symbol, then updating the summary_field. I don’t know the answer to this, does it matter? To answer that question, let’s just do a very un-scientific runtime analysis and see if there is a difference in runtimes. Also, I would like to see which of the 3 methods – Standard, Sorted and the new ABAP technique has a performance difference, if any.

 

Since we have the “Number of Records” parameter on the selection screen, let’s crank it up to a number that is large, but acceptable enough to run multiple times. 40,000 records seems like a bearable number to test.

 

Execute the SAP transaction “SAT” and enter the program name:

Click on the “Execute” button, and it will take you to the selection screen:

Enter 40,000 for number of records (or more or less, depending on your system).

Execute the transaction, to get the results:

Click on the Exit button twice, to get back to the SAT measurement screen:

Note the call to our “LM_PROCESS_STANDARD_TABLE” method took 24,927,504 microseconds:

Perform these same steps with the other 2 options for Sorted Table and New ABAP. We get the following runtimes:

Sorted Table:

New ABAP:

So, overall, we have:

Standard Table: 24,927,504

Sorted Table: 1,137,987

New ABAP: 1,200,210

Because the New ABAP technique and the Sorted Table option both use sorted tables, this just tells us that the New ABAP is about the same speed as the other sorted technique. A standard table is slow, simply because it’s doing a sequential read. Now, let’s change the code, and compare the “3 reads” we brought up earlier, to see if using a field-symbol makes a difference.

Here is our current code for the New ABAP option, which got us the above results:

    LOOP AT lt_temp_table INTO lw_rec.
      IF line_exists( lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ] ).
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field =
        lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ]-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.

We could change it to a single read, as follows:

    FIELD-SYMBOLS: <lfs_sum> TYPE lty_struct.
    LOOP AT lt_temp_table INTO lw_rec.
      UNASSIGN <lfs_sum>.
      ASSIGN lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ] TO <lfs_sum>.
      IF <lfs_sum> IS ASSIGNED.
        <lfs_sum>-summary_field = <lfs_sum>-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.

When doing the same runtime analysis in SAT for 40K records, we get runtimes of:

Run1:

Run2:

Run3:

So, when running 3 times, each one was slightly faster by using the field symbol.

 

Complete Program

Here is the complete program:

REPORT zjctest_tables.

SELECTION-SCREEN BEGIN OF BLOCK b1.
PARAMETERS: rb_std  RADIOBUTTON GROUP g1,
            rb_sort RADIOBUTTON GROUP g1,
            rb_new  RADIOBUTTON GROUP g1,
            p_recs  TYPE syindex DEFAULT '20000'.
SELECTION-SCREEN END OF BLOCK b1.

CLASS lcl_my_class DEFINITION CREATE PRIVATE FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
      main
        RETURNING VALUE(r_obj) TYPE REF TO lcl_my_class.
    METHODS:
      execute.

  PRIVATE SECTION.
    TYPES: BEGIN OF lty_struct,
             key1          TYPE matnr,
             key2          TYPE rsrnumc10,
             field1        TYPE string,
             a_date_field  TYPE sydatum,
             summary_field TYPE i,
           END OF lty_struct,
           lty_tab  TYPE STANDARD TABLE OF lty_struct,
           lty_stab TYPE SORTED TABLE OF lty_struct WITH UNIQUE KEY key1 key2.
    METHODS:
      constructor,
      lm_process_standard_table
        IMPORTING it_test_table TYPE lty_tab,
      lm_process_sorted_table
        IMPORTING it_test_table TYPE lty_tab,
      lm_process_new_abap
        IMPORTING it_test_table TYPE lty_tab,
      lm_display
        CHANGING ct_report TYPE table,
      lm_build_alv_table
        CHANGING  ct_report     TYPE table
        RETURNING VALUE(rt_alv) TYPE REF TO cl_salv_table,
      lm_build_records
        EXPORTING et_test_table TYPE lty_tab.
ENDCLASS.

CLASS lcl_my_class IMPLEMENTATION.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD constructor: Initialize my object.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD constructor.

  ENDMETHOD. " constructor

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD main: Instantiate the object
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD main.
    CREATE OBJECT r_obj.
  ENDMETHOD.                    "MAIN

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_standard_table: Pre-740 Method to process a
* standard table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_standard_table.
    DATA: lt_temp_table TYPE lty_tab,
          lt_sum_table  TYPE lty_tab,
          lw_rec        TYPE lty_struct.
    lt_temp_table[] = it_test_table[].
    lt_sum_table[] = it_test_table[].
    SORT lt_temp_table BY key1 key2.
    SORT lt_sum_table BY key1 key2.

    "This will double the summary_field entries...
    LOOP AT lt_temp_table INTO lw_rec.
      COLLECT lw_rec INTO lt_sum_table.
    ENDLOOP.
    lm_display( CHANGING ct_report = lt_sum_table ).
  ENDMETHOD. " lm_process_standard_table

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_sorted_table: Pre-740 Logic to process a
* sorted table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_sorted_table.
    DATA: lt_temp_table TYPE lty_stab,
          lt_sum_table  TYPE lty_stab,
          lt_pass_table TYPE lty_tab,
          lw_rec        TYPE lty_struct.
    FIELD-SYMBOLS: <lw_rec> TYPE lty_struct.

    LOOP AT it_test_table INTO lw_rec.
      INSERT lw_rec INTO TABLE lt_temp_table.
      INSERT lw_rec INTO TABLE lt_sum_table.
    ENDLOOP.

    "This will double the summary_field entries...
    CLEAR lw_rec.
    LOOP AT lt_temp_table INTO lw_rec.
      UNASSIGN <lw_rec>.
      READ TABLE lt_sum_table ASSIGNING <lw_rec>
                              WITH TABLE KEY key1 = lw_rec-key1
                                             key2 = lw_rec-key2.
      IF <lw_rec> IS ASSIGNED.
        "Add the summary_field...
        <lw_rec>-summary_field = <lw_rec>-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.
    lt_pass_table[] = lt_sum_table[].
    lm_display( CHANGING ct_report = lt_pass_table ).
  ENDMETHOD. " lm_process_sorted_table

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_process_new_abap: Use New ABAP Features to process a table.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_process_new_abap.
    DATA: lt_temp_table TYPE lty_stab,
          lt_sum_table  TYPE lty_stab,
          lt_pass_table TYPE lty_tab.
    FIELD-SYMBOLS: <lfs_sum> TYPE lty_struct.

    LOOP AT it_test_table INTO DATA(lw_rec).
      INSERT lw_rec INTO TABLE lt_temp_table.
      INSERT lw_rec INTO TABLE lt_sum_table.
    ENDLOOP.

    "This will double the summary_field entries...
    "The bracket notation [] finds the record with that key, then we can update it...
    CLEAR lw_rec.
    LOOP AT lt_temp_table INTO lw_rec.
      UNASSIGN <lfs_sum>.
      ASSIGN lt_sum_table[ key1 = lw_rec-key1 key2 = lw_rec-key2 ] TO <lfs_sum>.
      IF <lfs_sum> IS ASSIGNED.
        <lfs_sum>-summary_field = <lfs_sum>-summary_field + lw_rec-summary_field.
      ENDIF.
    ENDLOOP.
    lt_pass_table[] = lt_sum_table[].

    lm_display( CHANGING ct_report = lt_pass_table ).
  ENDMETHOD. " lm_process_new_abap

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_build_records: Build a table with nn records...
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_build_records.
    DO p_recs TIMES.
      APPEND VALUE lty_struct( key1          = 900 + sy-index
                               key2          = sy-index
                               field1        = |A Text Field - { sy-index }|
                               a_date_field  = sy-datum + sy-index
                               summary_field = sy-index * 10 )
        TO et_test_table.
    ENDDO.
  ENDMETHOD. " lm_build_records

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD execute: Execute the work for my object.
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD execute.
    DATA: lt_report TYPE lty_tab.
    "Create records...
    lm_build_records( IMPORTING et_test_table = lt_report ).
    "Mess up the sorting, so that we can process and test, below...
    SORT lt_report DESCENDING.

    IF rb_std = abap_true.
      lm_process_standard_table( lt_report ).
    ELSEIF rb_sort = abap_true.
      lm_process_sorted_table( lt_report ).
    ELSEIF rb_new = abap_true.
      lm_process_new_abap( lt_report ).
    ENDIF.
  ENDMETHOD.                    "execute

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_display: Show the report...
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_display.
    lm_build_alv_table( CHANGING ct_report = ct_report )->display( ).
  ENDMETHOD.                    "display

*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* METHOD lm_build_alv_table: Build the ALV Table...
*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  METHOD lm_build_alv_table.
    DATA: lv_functions TYPE REF TO cl_salv_functions_list,
          lv_columns   TYPE REF TO cl_salv_columns_table,
          lv_display   TYPE REF TO cl_salv_display_settings,
          lv_col_tab   TYPE salv_t_column_ref,
          lv_key       TYPE salv_s_layout_key,
          lv_layout    TYPE REF TO cl_salv_layout,
          lv_oref      TYPE REF TO cx_root,
          lv_text      TYPE bapi_msg,
          lv_ltext     TYPE scrtext_l,
          lv_mtext     TYPE scrtext_m,
          lv_stext     TYPE scrtext_s,
          lv_select    TYPE REF TO cl_salv_selections.
    FIELD-SYMBOLS: <lfs_w_col> TYPE salv_s_column_ref.

    CLEAR lv_text.
    TRY.
        cl_salv_table=>factory(
          IMPORTING
            r_salv_table = rt_alv
          CHANGING
            t_table      = ct_report ).
      CATCH cx_salv_msg INTO lv_oref.
        lv_text = lv_oref->get_text( ).
        RETURN.
    ENDTRY.

    lv_functions = rt_alv->get_functions( ).
    lv_functions->set_all( ).
    lv_columns = rt_alv->get_columns( ).
    lv_columns->set_optimize( if_salv_c_bool_sap=>true ).
    lv_col_tab = lv_columns->get( ).

    LOOP AT lv_col_tab ASSIGNING <lfs_w_col>.
      CASE <lfs_w_col>-columnname.
        WHEN 'KEY1'.
          <lfs_w_col>-r_column->set_short_text( 'Key1' ).
          <lfs_w_col>-r_column->set_medium_text( 'Key Field #1' ).
          <lfs_w_col>-r_column->set_long_text( 'Key Field #1' ).
        WHEN 'KEY2'.
          <lfs_w_col>-r_column->set_short_text( 'Key2' ).
          <lfs_w_col>-r_column->set_medium_text( 'Key Field #2' ).
          <lfs_w_col>-r_column->set_long_text( 'Key Field #2' ).
        WHEN 'FIELD1'.
          <lfs_w_col>-r_column->set_short_text( 'Field1' ).
          <lfs_w_col>-r_column->set_medium_text( 'Text Field1' ).
          <lfs_w_col>-r_column->set_long_text( 'Text Field1' ).
        WHEN 'A_DATE_FIELD'.
          <lfs_w_col>-r_column->set_short_text( 'DateField' ).
          <lfs_w_col>-r_column->set_medium_text( 'A Date Field' ).
          <lfs_w_col>-r_column->set_long_text( 'The First Date Field' ).
        WHEN 'SUMMARY_FIELD'.
          <lfs_w_col>-r_column->set_short_text( 'SumField' ).
          <lfs_w_col>-r_column->set_medium_text( 'Summary Field' ).
          <lfs_w_col>-r_column->set_long_text( 'Summary Field' ).
      ENDCASE.
    ENDLOOP.
    lv_display = rt_alv->get_display_settings( ).
    lv_display->set_striped_pattern( if_salv_c_bool_sap=>true ).
    lv_layout = rt_alv->get_layout( ).
    lv_key-report = sy-repid.
    lv_layout->set_key( lv_key ).
    lv_layout->set_default( abap_true ).
    lv_layout->set_save_restriction( if_salv_c_layout=>restrict_none ).
    lv_select = rt_alv->get_selections( ).
    lv_select->set_selection_mode( if_salv_c_selection_mode=>multiple ).
  ENDMETHOD.                    "lm_build_alv_table
ENDCLASS.

*---------------------------------------------------------------------*
*                  I N I T I A L I Z A T I O N                        *
*---------------------------------------------------------------------*
INITIALIZATION.
  %_rb_std_%_app_%-text  = 'Standard Table'.
  %_rb_sort_%_app_%-text = 'Sorted Table'.
  %_rb_new_%_app_%-text  = 'New ABAP'.
  %_p_recs_%_app_%-text  = 'Number of Records'.

*---------------------------------------------------------------------*
*               S T A R T   O F   S E L E C T I O N
*---------------------------------------------------------------------*
START-OF-SELECTION.
  lcl_my_class=>main( )->execute( ).

*---------------------------------------------------------------------*
*               E N D   O F   S E L E C T I O N
*---------------------------------------------------------------------*
END-OF-SELECTION.

 

References

 

Some excellent blogs for new ABAP Language features:

https://blogs.sap.com/2015/10/25/abap-740-quick-reference/

https://blogs.sap.com/2016/03/02/old-and-new-abap-syntax-overview-sheet/

 

What if you wanted to sort your cloned tables internally, without affecting the sort order of the original table? Checkout this blog…

Blog on Virtual Sorting of Internal Tables:

https://blogs.sap.com/2017/09/20/abap-news-for-release-7.52-virtual-sorting-of-internal-tables/

To report this post you need to login first.

13 Comments

You must be Logged on to comment or reply to a post.

  1. Matthew Billingham

    We need more blogs like these, encouraging people to use new ABAP features. I find it quite sad that so many people continue to use only standard tables when hashed and sorted tables (and secondary indexes on tables) have been available for many years.

    A few questions.

    1. Do you think that rather than using selection screen parameters in your local class they should be passed into the class as importing parameters e.g. to the constructor?
    2. Why do you have an empty constructor?
    3. What is that rather strange looking code in the INITIALIZATION event?
    4. Have you considered that you could make this even more modern by following SAP’s own ABAP guidelines, and DSAG’s guidelines (Appendices A2 and A3), and not using prefixes to indicate local variables, nor variable types (as opposed to use). (I find <lfs_w_col> particularly odd – first it’s already surround by angle brackets so you already know it’s a field symbol, and you can’t have a non-local field symbol in an OO context, so the entire lfs is completely redundant.)

    And a few comments

    1. You could further modernise and simplify the code by using inline declaration for helper variables. E,g, LOOP AT lv_col_tab ASSIGNING FIELD-SYMBOL(<lfs_w_col>).
    2. Hashed tables are exceptionally effective when used with COLLECT. It would be nice to see an example for that, with timings.

    But a good blog nonetheless – thank-you!

     

     

    (3) 
    1. Louis-Arnaud BOUQUIN

      I agree, we really need more blog like this, thank you.

       

      Something that I don’t like in the new ABAP is the new way to do a READ TABLE :

          if line_exists( lt_sum_table[ key1 = '905' key2 = '5' ] ).
            data(lw_record5) = lt_sum_table[ key1 = '905' key2 = '5' ].
          endif.

      When I see this, firstly it is less convenient than testing sy-subrc and it leads to reading 2 times the table, which can be performance issue.

      I now that you can also catch the exception, but sometimes a missing entry in a table is something normal in the program, why raising an exception for that ?

      But maybe I missed something ?

      (0) 
      1. Custodio de Oliveira

         

        or you can try:

         data(lw_record5) = value #( lt_sum_table[ key1 = ‘905’ key2 = ‘5’ ] optional ).

        This reads the table only once, and if the record does not exist, no short dump is given, just an empty lw_record5 😉

        (0) 
    2. Jonathan Capps Post author

      Thanks for the excellent feedback.

      1. Yes, I agree passing in the selections into the Constructor is a best practice for OO design.
      2. Empty constructor, simply because I copied this program from elsewhere. But that is a good point. I could comment out the declaration and implementation for the constructor, and it still compiles and works the same.
      3. That sets the descriptions for the selection screen, so that you can simply copy this program and run it (i.e. no need to go thru the menu path in the ABAP editor to set the texts). Of course, I believe you lose the multi-language capabilities.
      4. I will read thru that guideline document, very cool.
      (1) 
  2. Mark Teichmann

    In the ABAP Help it is stated that you should not use line_exists if you want to read the line afterwards:

    “…line_exists must be used carefully and no duplicate selections made. For example, line_exists should not be used to first check the existence of row and then read it. Instead, the table expression can be assigned to a field symbol and then sy-subrc checked. If the row in question usually exists, the table expression can be specified in the required operand position and the exception CX_SY_ITAB_LINE_NOT_FOUND caught.”

     

    (2) 
    1. Jonathan Capps Post author

      Good to know, I wasn’t aware of the warning. Notice that in the final program, field-symbols are used, rather than “line_exists”.

      (1) 
  3. Paul Hardy

    One off the topic question.

    Some of the screen shots above look like an even uglier version of the SAP GUI, if such a thing is possible.

    To be more clear I am referring to the images with the hige SAP logo, lots of white space, and the drug induced “Blue Crystal” icons.

    Is that an S/4 HANA screen or the SAP GUI with the “belize” theme?

    In either case, how ca it be possible for people to complain about the SAP GUI for 30 years, get a worse looking display with WEB DYNPRO, and then in 2018 that even worse looking monstrosity above?

    I know you did not design any of the above user interfaces, I am just wondering if the whole thing is not just some elaborate  practical joke being played on ABAP developers for some sort of game show.

    Cheersy Cheers

    Paul

    (1) 
    1. Jonathan Capps Post author

      You are correct, it is the new SAPGUI for S4. And yes, it is shocking and instead of buttons with icons in the ABAP Editor, there are large buttons with descriptions. What you see is all default.

      Here is why it doesn’t matter – ABAP development in the newer releases should be done in Eclipse. You should embrace Eclipse as the future and just start using it. Some things like creating ABAP CDS Views, you can only do in Eclipse.

      Finally, everything UI will be Fiori, so embrace that, too. On my current project, the only UI the business will see is a Fiori App. Goodbye classic SAP GUI and Web Dynpro.

      (1) 
  4. Shai Sinai

    I’m little confused.

    What exactly do you want to compare?
    Performance aspects or programming syntax/technique?

    As far as I know, from performance perspective, option 2 (“Sorted Table”) and 3 (“New ABAP”) should have exactly the same results.

    P.S.
    If you are already exploring new ABAP syntax, you should cover also the LOOP AT … GROUP BY command.

     

    (1) 

Leave a Reply