Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
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:

  • 7.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/
22 Comments
matt
Active Contributor
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!

 

 
Louis-Arnaud
Participant
0 Kudos
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 ?
markteichmann
Product and Topic Expert
Product and Topic Expert
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."

 
Actually this result is not suprising. What you are doing is comparing two O(n log n) algorithms against an O(n^2) algorithm.
hardyp180
Active Contributor
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
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.

Good to know, I wasn't aware of the warning. Notice that in the final program, field-symbols are used, rather than "line_exists".
In the final program, I use field-symbols, and simply check to see if it's assigned. If not, the record doesn't exist.
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.
matt
Active Contributor

I love using eclipse (with darkest dark theme ) ... But it still takes you to sapgui for some tasks.


 

And then my skin burns .  ?
Jelena
Active Contributor
0 Kudos
I was totally expecting someone to point out the prefixes and hashed tables but both in the very first comment? Daaaam... 🙂
shais
Participant
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.

 
custodio_deoliveira
Active Contributor
 

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 😉
hardyp180
Active Contributor
Severfal things:-

I will not be shedding any tears over the death of Web Dynpro. It looked horrible and ran really slow.

I develop using Eclipse. I like it a lot except when it takes you to the SAP GUI, which is pretty much all the time. When we get to the stage where Eclipse stops taking you to the SAP GUI then you can say use Eclipse instead of the SAP GUI. Unitl that point the statement makes no sense.

As I suspected the idea is to make the SAP GUI look so horrible no one will want to use it then OK, but I think for a while (like the next 10 years) a lot of people will have no choice but to use it. Unless S/4 has got to the stage all the transactions end users are likely to use have been replaced by Fiori.

So it could be like saying your car seat was uncomfortable before, now we have added spikes to it to make it even more uncomfortable, but it does not matter that it hurts to drive for now, because in twenty years time all cars will be self-driving and you can sit in the back where there are no spikes.

And whilst I agree that the Fiori design is a million times better than the original SAP GUI (leaving aside horrible new versions) I still don;t think it looks that good, especially on a desktop with all the white space. It's OK but I think the description of it from SAP as "beautiful" is going a bit far.

 
0 Kudos
As always, the software will move faster than the customer base. No doubt that the current UI direction is HTML5. This time, it is moving faster than everyone thinks. With SAP Gateway, I feel that this is the first time that SAP is truly open and RESTFUL and can tap into the Javascript Development universe. No need for ABAP for UI (now Javascript) or Reporting (now HANA, CDS Views)....interesting...learn SAP Gateway, that is where the ABAP will be on the server side. That, and migrations from old ABAP to the new. Personally, I loved Web Dynpro, and think it was one of the most powerful things SAP ever built. I found it to be lightning-fast, too.
0 Kudos
Not comparing anything. Simply rambling on, with the hope that someone will learn something new. For example, how to use a new ABAP feature, or maybe someone has never used SAT. There are an INSANE number of new ABAP features...very exciting!
BaerbelWinkler
Active Contributor
Thanks for this neat and detailed blog post, Jonathan!

This - as well as the links to the additional resources - will help a lot with trying out some "new" (well for me!) stuff when dabbling with some upcoming programming. I have a hard time remembering the various options, syntax constructs and which to best use when. This means that I tend to use what I've been using for many years and what I have readily available in my code-templates. I don't really do a lot of programming anymore but knowing what is possible and how it can get applied will obviously also help with the code-reviews I do every now and then.

So, thanks again for investing the time to put this together!

Cheers

Baerbel
joachimrees1
Active Contributor
0 Kudos
I haven't been able to fully read throug (but I will do so!)
as I was distracted by this:
"Software Versions:

52 of SAP"

What do you mean by 52?

Maybe: NW 7.52 (SAP_BASIS ) ?

best
Joachim
sorry, yes, I've updated it.
philipdavy
Contributor
Hello  matthew.billingham

It is always appreciated to use the correct internal table type . But it is also disappointing that we cant specify the table type while using the inline declartion in a 'Select' statement .  Select * ..... into @data(customers)  is always a customers internal table of standard type ....  :‑(   Here is where the 'New way' contradicts the 'best way'

Regards,

Philip.
matt
Active Contributor
0 Kudos
It is an annoyance I agree. It's also a pain that so many function modules only take standard tables. If I need to select into a HASHED table, then I don't use inline declaration,
matt
Active Contributor
0 Kudos
I remember when the "new" design was introduced some time in 2000. I went to a presentation, and the SAP folk seemed genuinely distressed that we thought they'd wasted their money and it was ugly...
Labels in this area