Internal Tables
ABAP Developmentadvanced

Internal Tables Part 9: Nested LOOP Optimization and Parallel Cursor

Key takeaway

Fix the most common ABAP performance problem — nested loops over two large tables — with sorted tables and LOOP WHERE, hashed lookups, and the classic parallel cursor technique.

Explanation

📌 KEY POINTS
✅ LOOP inside LOOP over two standard tables = rows1 × rows2 comparisons — minutes become hours.

✅ Fix 1: inner table SORTED by the join key → LOOP … WHERE is optimized.

✅ Fix 2: one matching row per key → HASHED inner table + READ TABLE.

✅ Fix 3 (classic): parallel cursor — find the start with BINARY SEARCH, loop FROM that index, EXIT when the key changes.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Part 8 covered loop patterns. When two tables are combined in memory (headers and items, flights and bookings), the nested loop is where performance is won or lost.

━━━━━━━━━━━━━━━━━━━━
▶ 1. THE PROBLEM

LOOP AT gt_flight INTO gs_flight. " 10,000 rows

LOOP AT gt_book INTO gs_book WHERE carrid = gs_flight-carrid AND connid = gs_flight-connid AND fldate = gs_flight-fldate. " 500,000 rows

ENDLOOP.

ENDLOOP.

With standard tables, the inner WHERE scans all 500,000 rows for each of the 10,000 flights: 5 billion comparisons.

━━━━━━━━━━━━━━━━━━━━
▶ 2. FIX 1 — SORTED INNER TABLE

Declare gt_book as SORTED TABLE … WITH NON-UNIQUE KEY carrid connid fldate. The inner LOOP WHERE on the leading key fields now uses binary search to find the group — typically the simplest, cleanest fix.

━━━━━━━━━━━━━━━━━━━━
▶ 3. FIX 2 — HASHED LOOKUP

If the inner table has at most one match per key (e.g. flight → airline name), use a HASHED table with UNIQUE KEY and READ TABLE … WITH TABLE KEY instead of an inner loop.

━━━━━━━━━━━━━━━━━━━━
▶ 4. FIX 3 — PARALLEL CURSOR (CLASSIC STANDARD TABLES)

When you must keep standard tables (existing code):

  • SORT both tables by the join key.
  • For each outer row: READ TABLE inner WITH KEY … BINARY SEARCH TRANSPORTING NO FIELDS → sy-tabix = first matching row.
  • LOOP AT inner FROM lv_index; EXIT when the key differs.

Each inner row is visited only for its own group.

━━━━━━━━━━━━━━━━━━━━
▶ 5. BETTER STILL: THE DATABASE

If both tables come from the database, a JOIN or CDS view often avoids the in-memory join entirely (Open SQL topic).

━━━━━━━━━━━━━━━━━━━━
▶ 6. MEASURE

Use SAT (runtime analysis) or GET RUN TIME with realistic volumes. Small test data hides nested-loop problems completely.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

Run the code example with larger UP TO values: compare the nested standard loop, the sorted-table loop and the parallel cursor. The runtime difference grows quadratically.

✅ CHECKPOINT: you never leave a nested loop over two large standard tables in your code.

Code example

ABAP Code
REPORT zitab_part9. TYPES ty_book_sorted TYPE SORTED TABLE OF sbook        WITH NON-UNIQUE KEY carrid connid fldate. DATA: gt_flight  TYPE STANDARD TABLE OF sflight,      gs_flight  TYPE sflight,      gt_book    TYPE STANDARD TABLE OF sbook,      gt_book_s  TYPE ty_book_sorted,      gs_book    TYPE sbook,      gv_index   TYPE sy-tabix,      gv_count   TYPE i,      gv_t0      TYPE i,      gv_t1      TYPE i. START-OF-SELECTION.  SELECT * FROM sflight INTO TABLE gt_flight UP TO 500 ROWS.  SELECT * FROM sbook   INTO TABLE gt_book   UP TO 50000 ROWS. * ===== BAD: nested loop over two standard tables =====  GET RUN TIME FIELD gv_t0.  LOOP AT gt_flight INTO gs_flight.    LOOP AT gt_book INTO gs_book WHERE carrid = gs_flight-carrid                                   AND connid = gs_flight-connid                                   AND fldate = gs_flight-fldate.      gv_count = gv_count + 1.    ENDLOOP.  ENDLOOP.  GET RUN TIME FIELD gv_t1.  gv_t1 = gv_t1 - gv_t0.                          " elapsed microseconds  WRITE: / 'nested standard :', gv_t1, 'microsec,', gv_count, 'bookings'. * ===== FIX 1: sorted inner table, LOOP WHERE on key =====  gt_book_s = gt_book. CLEAR gv_count.  GET RUN TIME FIELD gv_t0.  LOOP AT gt_flight INTO gs_flight.    LOOP AT gt_book_s INTO gs_book WHERE carrid = gs_flight-carrid                                     AND connid = gs_flight-connid                                     AND fldate = gs_flight-fldate.      gv_count = gv_count + 1.    ENDLOOP.  ENDLOOP.  GET RUN TIME FIELD gv_t1.  gv_t1 = gv_t1 - gv_t0.                          " elapsed microseconds  WRITE: / 'sorted inner    :', gv_t1, 'microsec,', gv_count, 'bookings'. * ===== FIX 3: parallel cursor on standard tables =====  SORT gt_flight BY carrid connid fldate.  SORT gt_book   BY carrid connid fldate.  CLEAR gv_count.  GET RUN TIME FIELD gv_t0.  LOOP AT gt_flight INTO gs_flight.    READ TABLE gt_book TRANSPORTING NO FIELDS         WITH KEY carrid = gs_flight-carrid connid = gs_flight-connid                  fldate = gs_flight-fldate BINARY SEARCH.    CHECK sy-subrc = 0.    gv_index = sy-tabix.                            " first booking of this flight    LOOP AT gt_book INTO gs_book FROM gv_index.      IF gs_book-carrid <> gs_flight-carrid OR gs_book-connid <> gs_flight-connid         OR gs_book-fldate <> gs_flight-fldate.        EXIT.                                       " group finished      ENDIF.      gv_count = gv_count + 1.    ENDLOOP.  ENDLOOP.  GET RUN TIME FIELD gv_t1.  gv_t1 = gv_t1 - gv_t0.                          " elapsed microseconds  WRITE: / 'parallel cursor :', gv_t1, 'microsec,', gv_count, 'bookings'.

Real project scenario

A freight settlement job matched 40,000 shipments against 1.2 million cost lines with a nested loop over standard tables. It ran for nine hours and was often cancelled by the batch window.
Changing the cost table to a sorted table keyed by shipment number (inner LOOP WHERE) brought runtime down to under five minutes. In another older program that could not be restructured, the parallel cursor technique achieved a similar improvement.

Common mistakes

  • Nested loops over two large standard tables.
  • Parallel cursor without sorting both tables by the same key.
  • Forgetting EXIT when the key changes (loops to the end).
  • Testing only with small data.
  • Doing in-memory joins that the database could do.

Best practices

  • Sorted inner tables with LOOP WHERE on leading key fields.
  • Hashed tables for one-to-one lookups.
  • Parallel cursor for existing standard-table code.
  • Push joins to the database where possible.
  • Measure with SAT and realistic volumes.

Interview angle

Questions: "How do you optimize nested loops?", "Explain the parallel cursor technique.", "Why does a sorted inner table help?"
Strong answer: nested loops over standard tables cost rows × rows; use a sorted inner table so LOOP WHERE on leading key fields uses binary search, hashed tables for 1:1 lookups, or the parallel cursor: sort both tables, find the first match with READ … BINARY SEARCH, loop FROM sy-tabix and EXIT when the key changes; better, join in the database.

Practice this lesson

Quick check

1. A nested LOOP over 10,000 flights and 500,000 bookings (both standard tables) is slow. What is the best simple fix?

2. In the parallel cursor technique, how is the first matching inner row found?

3. Why does a sorted inner table speed up LOOP ... WHERE on its key fields?

Interview questions

1. A report scans 200,000 items for every sales order header. How do you fix the lookup design?

Answer it in your own words first, then compare.

2. A pricing report uses several nested loops over large standard tables. How do you optimize it?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics