Anthony Cecchini is the President of Information Technology Partners (ITP), an SAP consulting company headquartered in Pennsylvania. ITP offers comprehensive planning, resource allocation, implementation, upgrade, and training assistance to companies. Anthony has over 20 years of experience in SAP business process analysis and SAP systems integration. His areas of expertise include SAP NetWeaver integration; ALE development; RFC, BAPI, IDoc, Dialog, and Web Dynpro development; and customized Workflow development. You can reach him at [email protected]
Internal tables are a core staple of programming in ABAP. In this blog, you’ll learn about some new ABAP code constructs that relate to internal tables introduced in ABAP 7.2 and ABAP 7.4.
All of us who have been developing in ABAP at one time or another have created a custom index on a database table. Why do we create a custom index or z-index? For performance… we recognize that a table could be queried in more ways then just by the primary key, so we setup customer indexes that we believe will be used by the Database Optimizer when determining the access path and thus make the query performant.
OK, back to internal tables, traditionally, if you wanted to read an internal table in two different ways (e.g.,looking for a material by Material Number or by Reference Number), then you either had to keep sorting the table just before a read, or have two identical tables sorted differently. Well now as of ABAP 7.2 can declare secondary keys for internal tables. The SAP Help states that using the secondary key could increases read access performance significantly. But, on the other hand, secondary keys also incur additional administration costs due to memory consumption and run-time.
DATA: IT_MARA TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnr WITH NON-UNIQUE SORTED KEY sort_key COMPONENTS bismt.
The SAP Help states that statements that previously only accessed the primary key have been enhanced so that access to secondary keys is now possible. Check out the help for a full list, but we will look at the READ TABLE statement here.
The code would look something like the below…
READ TABLE it_mara INTO wa_mara WITH KEY sort_key COMPONENTS bismt = lv_bismt.
Even though IT_MARA is a HASHED table, it is also a SORTED table with the key BISMT, so when we go looking for the record using BISMT a BINARY SEARCH is automatically performed.
In release ABAP 7.4, the syntax for reading into a work area and looping through a table can now leverage INLINE DECLARATIONS, we discussed these in a prior ABAP 7.4 blog.
We learned that from 7.4 onward you no longer need to do a DATA declaration for elementary data types. It is exactly the same for the work areas, which are of course structures. Take a gander at the code below…
READ TABLE lt_mara WITH KEY matnr = lv_matnr INTO DATA(ls_mara). LOOP AT lt_mara INTO DATA(ls_mara).
In the same way that you no longer need DATA declarations for table work areas, you also no longer need FIELD-SYMBOL declarations for the (common) situations in which you want to change the data in the work area while looping through an internal table. In ABAP 7.4, if you want to use field symbols for the work area, then the syntax is shown below…
READ TABLE lt_mara WITH KEY matnr = lv_matnr ASSIGNING FIELD-SYMBOL(). LOOP AT lt_mara ASSIGNING FIELD-SYMBOL().
What if I told you that you would never have to use the statement READ TABLE again to get a line out of an internal table?
That is exactly what happens in ABAP 7.4. Through the use of Table Expressions, a new way for accessing table lines in operand positions. We can view a table expression simply as a short form of a READ TABLE statement. The syntax for using a table expression consists of an internal table, followed by a row specified in square brackets [ ].
lets look at some code…
READ TABLE flight_schedules INTO DATA(flight_schedule) WITH KEY carrid = 'AA' connid = '0017'. lo_mara = zcl_mara_factory( ls_mara-matnr ).
DATA(flight_schedule) = flight_schedules[ carrid = 'AA' connid = '0017' ]. lo_mara = zcl_mara_factory( lt_mara[ matnr = lv_matnr ]-matnr ).
IF line_exists( vendors[ id = '00AED' ] ). vendors[ id = '00AED' ]. ENDIF.
From 7.40, SP08 on, we are able to define default values for avoiding the mentioned exception above. So, if the specified row is not found, then it returns the default value back.
DATA(default_customer) = VALUE customer( id = '00000' name = 'not found' ... ). DATA(lv_customer) = VALUE #( customers[ id = '00024' ] DEFAULT default_customer ).
The new constructor operator CORRESPONDING allows to execute a “MOVE-CORRESPONDING” for structures or internal tables at operand positions. Besides the automatic assigning components of the same name, you can also define your own mapping rules! This is best explained by using an example… We have have 2 internal tables LT_MARA_SOURCE and LT_MARA_TARGET. (1) You do not want to copy the MATKL value from one table to the other, even though both tables have a MATKL column. (2) You want to copy the column named KUNNR from one table into a similar column called CUSTOMER in the second table. We code use the code below, with the mapping rules defined…
lt_mara_source = CORRESPONDING #( lt_mara_target MAPPING customer = kunnr EXCEPT matkl ).
Here is the link to the SAP help.
You can use MOVE-CORRESPONDING not only for structures but also for internal tables in ABAP 7.4. Components of the same name are are assigned row by row. New statement additions EXPANDING NESTED TABLES and KEEPING TARGET LINES allow to resolve tabular components of deep structures and to append lines instead of overwriting existing lines.
MOVE-CORRESPONDING itab_source TO itab_target EXPANDING NESTED TABLES KEEPING TARGET LINES.
I want to once again recommend that you grab a copy of Paul Hardy’s book ABAP to The Future. He has done an excellent job explaining this specific concept in language developers can easily relate. Here is a link to the SAP help. (Not quite as easy on the eyes as Paul’s book ;-))
The filter operator does what its name suggests it filters the content of an internal table. As a prerequisite, the filtered table must have a sorted or a hash key (primary or secondary), that is evaluated behind the WHERE clause.
DATA( lt_materials_fert ) = FILTER #( lt_all_materials USING KEY mtart WHERE mtart = 'FERT' ).
You can get quite fancy by adding some of the additional variants like EXCEPT and (ITAB ….IN …..ITAB) which behaves much like the FOR ALL ENTRIES does on the Database. Take a look at the SAP help for a more complete understanding.
A predicate function is predictive of a result. With ABAP 7.4 we have new built-in functions LINE_EXISTS and LINE_INDEX that fit this description for internal tables. Earlier in this blog I gave you an example of using LINE_EXISTS. Lets take a look at it again. We can think of using LINE_EXISTS as the short form of the READ TABLE … TRANSPORTING NO FIELDS statement. First, we check the existence of the specified row, and if it exists, then we perform the table read.
IF line_exists( vendors[ id = '00AED' ] ). vendors[ id = '00AED' ]. ENDIF.
Here is the SAP Help for LINE_EXISTS. Another predicate function is LINE_INDEX. In ABAP 7.4 release, we have new syntax LINE_INDEX() to identify the index of a row when a condition is met while reading the internal table. The new syntax is similar to READ TABLE with TRANSPORTING NO FIELDS followed by sy-subrc check. if sy-subrc = 0, then sy-tabix will give the index of the row.
READ TABLE it_schedule TRANSPORTING NO FIELDS WITH KEY carrid = 'US' connid = '24'. IF sy-subrc = 0. WRITE: sy-tabix. "index ENDIF.
Here is the SAP Help for LINE_INDEX.
With the new internal table constructs in ABAP 7.2 and ABAP 7.4 we have the ability to create ABAP code with fewer statements for the same functionality, without compromising readability of the code. New built-in functions like LINE_INDEX and LINE_EXISTS, and new constructor operators such as CORRESPONDING allow us more flexability and extensibility when developing and maintaining our ABAP code.