Now accepting guest editorial from SAP, Oracle, Workday and ServiceNow practitioners.Pitch a guide
The Sign-Off Table

Lesson 4 of 6

Table expressions and internal table operations

11 min

Listen to this lesson

0:00 / 10:03

Classic ABAP reads internal tables with READ TABLE, then checks sy-subrc. Modern ABAP reads a row inside an expression: agreements[ id = agr_id ]. It is shorter and can be used anywhere an operand is expected. It also behaves differently when the row is missing, and that difference is the most common regression in syntax refactoring. This lesson covers table expressions, the built-in functions around them, and iteration with FOR and LOOP AT ... GROUP BY.

Reading a row

NeedClassicModern
Row by key, must existREAD TABLE lt_agr INTO ls_agr WITH KEY id = lv_id.
IF sy-subrc <> 0. RAISE ... ENDIF.
DATA(agr) = agreements[ id = agr_id ].
Row may be missingREAD TABLE ... then IF sy-subrc = 0DATA(agr) = VALUE #( agreements[ id = agr_id ] OPTIONAL ).
Default if missingREAD TABLE plus IF ... ELSEVALUE #( rates[ product = p ]-rate DEFAULT 45 )
Only check existenceREAD TABLE ... TRANSPORTING NO FIELDS.IF line_exists( agreements[ id = agr_id ] ).
Row numberREAD TABLE ...then sy-tabix DATA(idx) = line_index( agreements[ id = agr_id ] ).
Change a field in placeREAD TABLE ... ASSIGNING <fs>.
<fs>-status = ...
agreements[ id = agr_id ]-status = 'CLOSED'.

Classic READ TABLE patterns and their table expression equivalents.

The key difference: if the row does not exist, a table expression raises the exception CX_SY_ITAB_LINE_NOT_FOUND. READ TABLE only set sy-subrc to 4. Classic code that ignored sy-subrc carried on with an empty work area; refactored code dumps, unless it uses OPTIONAL, DEFAULT, line_exists or catches the exception. Which one is right depends on the business rule, and that is a question for the process owner, not the compiler.

A missing row is where table expressions differ from READ TABLE. Choose the behavior the business rule needs.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-206

Keys and performance

A table expression uses the table key when the condition matches it, and otherwise searches linearly, exactly like READ TABLE ... WITH KEY on a standard table. For a hashed or sorted table, or a secondary key named with KEY, lookups are fast. Contoso's legacy report read 3,000 customers per agreement row with a linear search; giving the customer table a hashed key fixed a runtime problem that predated the conversion. Two other built-ins replace classic statements: lines( itab ) instead of DESCRIBE TABLE, and line_exists( ) instead of TRANSPORTING NO FIELDS.

Secondary keys deserve a sentence of their own. A table can have one primary key and several secondary keys, declared in the type with WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status or a hashed variant. Table expressions, FILTER and LOOP AT ... USING KEY can then name the key, and the runtime keeps the key's index up to date. For Contoso's report, a standard table of agreements with a sorted secondary key on status lets the code read by agreement ID in order and filter by status quickly, without sorting the table twice.

Iteration expressions

FOR inside VALUE builds one table from another: DATA(open_ids) = VALUE string_table( FOR a IN agreements WHERE ( status = 'OPEN' ) ( CONV string( a-id ) ) ). LOOP AT ... GROUP BY groups rows by a key, for example agreements by customer, and lets you loop over the members of each group. Use them to express a transformation, not to cram complex logic into one statement; a LOOP with clear steps is still fine when the body does real work.

ReadTable expressions itab[ ... ] with OPTIONAL or DEFAULT
Checkline_exists( ), line_index( ), lines( )
Select rowsFILTER on a key; VALUE #( FOR ... WHERE ... ) otherwise
TransformVALUE #( FOR ... ) and CORRESPONDING for tables
AggregateREDUCE, or LOOP AT ... GROUP BY for groups
The internal table toolkit from read to transform.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-206

Walkthrough: refactoring a lookup

  1. Find the legacy block in ZCMC_CORE_RET_LIST: READ TABLE lt_rates INTO ls_rate WITH KEY product = ls_item-product. followed by ls_out-rate = ls_rate-rate. with no sy-subrc check.
  2. Ask what happens today when the rate is missing: the work area keeps the previous row's rate, a silent defect. Maria confirms the business rule: a missing rate means 45.00, the standard deposit.
  3. Write a characterization test for the current behavior and a test for the agreed rule (missing rate gives 45.00).
  4. Refactor to out-rate = VALUE #( rates[ product = item-product ]-rate DEFAULT 45 ).
  5. Give rates a sorted or hashed key on product if the table is large.
  6. Run the tests; the characterization test now fails on purpose because the old defect is fixed. Record that as an intended change.
Refactoring surfaces hidden behavior; an intended change is agreed with the key user, not slipped in.
The Sign-Off Table · thesignofftable.com/academy/sap-abp-206

What goes wrong in testing

  • New dumps. A table expression without OPTIONAL in a path where the row can be missing turns an old silent defect into a short dump in production.
  • Stale work areas fixed by accident. Refactoring changes output; without a characterization test nobody can say whether that was intended.
  • Hidden performance. Repeated table expressions on a standard table in a loop are linear searches each time.
  • Changing in place. agreements[ ... ]-status = 'CLOSED' changes the table row directly, like a field symbol; a test that only checks the return value misses it.

What would you do?

After Kenji refactors a READ TABLE into a table expression, the nightly test run in CMQ shows a short dump CX_SY_ITAB_LINE_NOT_FOUND in the deposit report for one customer. What should Kenji do?

Key points
  • A table expression raises CX_SY_ITAB_LINE_NOT_FOUND when the row is missing; READ TABLE only set sy-subrc.
  • Use OPTIONAL, DEFAULT, line_exists( ) or a TRY block, depending on the business rule.
  • Keys decide speed: hashed, sorted or secondary keys avoid linear searches.
  • Characterization tests show when a refactoring changes output; agree intended changes with the key user.

Check your understanding · not graded

Lesson check

  1. 1. What happens when agreements[ id = 99 ] finds no row?

  2. 2. Which built-in replaces READ TABLE ... TRANSPORTING NO FIELDS for an existence check?

  3. 3. How do you get 45.00 when the rate row is missing?

  4. 4. Why can repeated table expressions in a loop be slow?

  5. 5. A refactoring fixes a stale-work-area defect. What should happen?