Internal Tables Part 8: LOOP Patterns — WHERE, Control Levels and Totals
Process tables with the right loop pattern: LOOP WHERE, control-level processing with AT FIRST/AT NEW/AT END OF/SUM, FROM/TO ranges, and modern GROUP BY, VALUE FOR and REDUCE.
Explanation
📌 KEY POINTS
✅ LOOP AT … WHERE filters inside the loop statement — cleaner (and on sorted tables faster) than IF inside.
✅ Control levels AT NEW / AT END OF / SUM need a table SORTED by the control fields, from left to right.
✅ Totals and filtered subsets are built with a normal LOOP (WHERE) plus APPEND or addition.
💡 Side note: 7.40+ offers GROUP BY, VALUE FOR and REDUCE for the same tasks — useful to recognise in newer code.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Parts 1–7 covered table operations. Loops are where most business logic happens; this part shows the patterns you will meet in classic code and the modern equivalents.
━━━━━━━━━━━━━━━━━━━━
▶ 1. LOOP WHERE AND RANGES
LOOP AT gt_flight INTO gs_flight WHERE carrid = 'LH' AND seatsocc < seatsmax.
LOOP AT gt_flight INTO gs_flight FROM 11 TO 20. " index range (paging)
On sorted tables, WHERE on leading key fields is optimized.
━━━━━━━━━━━━━━━━━━━━
▶ 2. CONTROL-LEVEL PROCESSING (CLASSIC)
Classic reports produce subtotals with:
AT FIRST — before the first row (headers)
AT NEW carrid — when carrid (or any field left of it) changes → group header
AT END OF carrid — last row of the group → subtotal with SUM
AT LAST — after the last row → grand total
Rules:
- SORT by the control fields first, and the control field order must match the structure's field order (fields to the left count).
- Inside AT … ENDAT, fields to the right of the control field contain '*' or are initial — copy what you need before AT.
- SUM adds all numeric fields of the group into the work area.
⚠️ WATCH OUT: using LOOP … WHERE together with AT NEW gives unexpected groups — filter first, then loop.
━━━━━━━━━━━━━━━━━━━━
▶ 3. SIDE NOTE: GROUP BY (7.40+)
LOOP AT gt_flight INTO DATA(ls) GROUP BY ls-carrid INTO DATA(lv_carr).
LOOP AT GROUP lv_carr INTO DATA(ls_member). … ENDLOOP.
ENDLOOP.
No sorting required, explicit group key, members available per group, group keys can be structures (GROUP BY ( carrid = ls-carrid month = ls-fldate(6) )).
━━━━━━━━━━━━━━━━━━━━
▶ 4. SIDE NOTE: VALUE FOR AND REDUCE (7.40+)
- Build: DATA(lt_lh) = VALUE ty_flights( FOR f IN gt_flight WHERE ( carrid = 'LH' ) ( f ) ).
- Total: DATA(lv_occ) = REDUCE i( INIT s = 0 FOR f IN gt_flight NEXT s = s + f-seatsocc ).
- Transform: CORRESPONDING #( gt_flight MAPPING … ) for structure mapping.
━━━━━━━━━━━━━━━━━━━━
▶ 5. WHICH PATTERN?
- Existing classic report → keep control levels consistent, but sort correctly.
- Systems below 7.40 or teams with classic standards → control levels and LOOP WHERE; on 7.40+ the newer constructs are an option if the team guideline allows them.
- Complex logic per row → a normal LOOP calling a method.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
Run the code example: occupancy subtotals per airline with AT NEW/AT END OF + SUM, a filtered subset with LOOP WHERE, and a total with a simple loop.
✅ CHECKPOINT: you can read and write classic control-level loops and their modern equivalents.
Code example
REPORT zitab_part8. TYPES: BEGIN OF ty_flight, carrid TYPE s_carr_id, " control field 1 (leftmost) connid TYPE s_conn_id, fldate TYPE s_date, seatsmax TYPE s_seatsmax, seatsocc TYPE s_seatsocc, END OF ty_flight. DATA: gt_flight TYPE STANDARD TABLE OF ty_flight, gs_flight TYPE ty_flight, gt_lh TYPE STANDARD TABLE OF ty_flight, gv_carrid TYPE s_carr_id, gv_total TYPE i, gv_lines TYPE i. START-OF-SELECTION. SELECT carrid connid fldate seatsmax seatsocc FROM sflight INTO TABLE gt_flight UP TO 60 ROWS. * ===== 1. Control levels: SORT first! ===== SORT gt_flight BY carrid connid fldate. LOOP AT gt_flight INTO gs_flight. gv_carrid = gs_flight-carrid. " copy before AT (fields may be masked) AT FIRST. WRITE: / 'Occupancy per airline'. ULINE. ENDAT. AT NEW carrid. WRITE: / 'Airline', gv_carrid. ENDAT. WRITE: /5 gs_flight-connid, gs_flight-fldate, gs_flight-seatsocc, '/', gs_flight-seatsmax. AT END OF carrid. SUM. " sums numeric fields of the group WRITE: /5 'Subtotal:', gs_flight-seatsocc, '/', gs_flight-seatsmax. ENDAT. AT LAST. SUM. ULINE. WRITE: / 'Grand total:', gs_flight-seatsocc, '/', gs_flight-seatsmax. ENDAT. ENDLOOP. * ===== 2. LOOP WHERE: filter into a new table ===== LOOP AT gt_flight INTO gs_flight WHERE carrid = 'LH'. APPEND gs_flight TO gt_lh. ENDLOOP. DESCRIBE TABLE gt_lh LINES gv_lines. WRITE: / 'LH flights:', gv_lines. * ===== 3. Total over the whole table ===== LOOP AT gt_flight INTO gs_flight. gv_total = gv_total + gs_flight-seatsocc. ENDLOOP. WRITE: / 'Total occupied seats:', gv_total. * 💡 Modern equivalents (7.40+), for reading newer code:* LOOP AT gt_flight INTO DATA(ls) GROUP BY ls-carrid INTO DATA(lv_grp). ... ENDLOOP.* lt_lh = VALUE #( FOR f IN gt_flight WHERE ( carrid = 'LH' ) ( f ) ).* lv_total = REDUCE i( INIT t = 0 FOR f IN gt_flight NEXT t = t + f-seatsocc ).Real project scenario
A classic sales report printed subtotals per customer with AT END OF kunnr, but after a change the table was sorted by date first. Subtotals appeared after every date change, and totals per customer were split into dozens of fragments. Users exported to Excel and summed manually.
The team restored the correct SORT order matching the control fields and, in the next rewrite, replaced control levels with LOOP GROUP BY on the customer, which does not depend on the sort order and made the grouping explicit.
Common mistakes
- Control levels without sorting by the control fields.
- Control field not the leftmost relevant field in the structure.
- Reading masked fields inside AT … ENDAT.
- Combining LOOP WHERE with AT NEW.
- IF inside loops instead of WHERE.
Best practices
- SORT by control fields before AT NEW/AT END OF.
- Copy needed values before AT blocks.
- Use GROUP BY for new grouping logic.
- Use REDUCE for totals and VALUE FOR for filtering/transforming.
- Keep loop bodies small; call methods for complex logic.
Interview angle
Questions: "How do AT NEW and AT END OF work?", "What are the prerequisites for control-level processing?", "What does LOOP GROUP BY offer?"
Strong answer: control levels trigger on changes of the control field (and fields to its left) in a table sorted by those fields; AT NEW for headers, AT END OF with SUM for subtotals, AT FIRST/AT LAST for totals; fields to the right are masked inside AT; GROUP BY groups without sorting, with explicit group keys and member loops; REDUCE and VALUE FOR compute and build without explicit loops.
Practice this lesson
Quick check
1. What must be true before using AT NEW carrid in a LOOP?
2. To use the FILTER operator on an internal table with a condition like FILTER itab WHERE field = value, what must be true about the source table's key?
3. When using LOOP AT itab GROUP BY ( key = itab-field ) INTO DATA(group), what happens to the original internal table itab during the loop?
Interview questions
1. An upload changes a work area inside LOOP INTO but the table is not updated. Why, and what is the fix?
Answer it in your own words first, then compare.
2. A performance-critical program uses table expressions heavily. What pitfalls do you check in a review?
Answer it in your own words first, then compare.
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
- Internal Tables Part 1: Standard, Sorted and Hashed Tables
- Internal Tables Part 10: Memory Management and Large Internal Tables
- Internal Tables Part 2: Choosing the Right Table Type
- Internal Tables Part 3: Work Area vs Field Symbol vs Data Reference
- Internal Tables Part 4: READ TABLE, sy-subrc and Table Expressions
- Internal Tables Part 5: SORT, BINARY SEARCH and Secondary Keys
- Internal Tables Part 6: APPEND, INSERT, MODIFY, DELETE and COLLECT
- Internal Tables Part 7: Removing Duplicates and Building Unique Datasets