Internal Tables Fundamentals
Internal tables are dynamic in-memory tables — ABAP's main collection type and the workhorse of every program.
Internal tables are dynamic in-memory tables — ABAP's main collection type and the workhorse of every program.
10.1 Table categories
Index or key (linear search, or binary with BINARY SEARCH on sorted data) Non-unique (or empty)
Small tables, appending, sequential processing Index or key (binary search, O(log n))
Unique or non-unique; always kept sorted Range/partial key access, ordered output Key only (hash, O(1)) Must be unique
Large lookup tables accessed by full key
10.2 Declaring tables
Obsolete: tables with header lines (DATA itab TYPE … WITH HEADER LINE, OCCURS 0) — still found in legacy code and forbidden in classes. itab[] addresses the table body when a header line exists.
TYPES: BEGIN OF ty_order,
order_id TYPE n LENGTH 10,
customer TYPE kunnr,
amount TYPE p LENGTH 15 DECIMALS 2,
status TYPE c LENGTH 1,
END OF ty_order.
DATA lt_std TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY.
DATA lt_std2 TYPE STANDARD TABLE OF ty_order WITH NON-UNIQUE KEY customer.
DATA lt_sorted TYPE SORTED TABLE OF ty_order WITH UNIQUE KEY order_id.
DATA lt_hashed TYPE HASHED TABLE OF ty_order WITH UNIQUE KEY order_id.
DATA lt_dflt TYPE STANDARD TABLE OF ty_order WITH DEFAULT KEY. " all char-like fields: avoid
" Table type for reuse
TYPES tt_orders TYPE SORTED TABLE OF ty_order WITH UNIQUE KEY order_id.10.3 Secondary keys (7.02+)
Secondary keys are updated lazily (sorted) or on change (hashed); use them when one table must be searched by several different keys.
DATA lt_orders TYPE SORTED TABLE OF ty_order
WITH UNIQUE KEY order_id
WITH NON-UNIQUE SORTED KEY by_customer COMPONENTS customer
WITH UNIQUE HASHED KEY by_ref COMPONENTS customer order_id.
LOOP AT lt_orders INTO DATA(ls_o) USING KEY by_customer WHERE customer = '0000001000'.
ENDLOOP.
READ TABLE lt_orders INTO ls_o WITH KEY by_customer COMPONENTS customer = '0000001000'.10.4 Filling tables
DATA ls_order TYPE ty_order.
ls_order = VALUE #( order_id = '1' customer = 'C1' amount = '100' status = 'O' ).
APPEND ls_order TO lt_std. " standard only: adds at end
INSERT ls_order INTO TABLE lt_sorted. " generic: by key (sy-subrc = 4 if duplicate)
INSERT ls_order INTO lt_std INDEX 1. " at position
APPEND VALUE #( order_id = '2' customer = 'C2' amount = '50' ) TO lt_std.
APPEND INITIAL LINE TO lt_std ASSIGNING FIELD-SYMBOL(<ls_new>).
<ls_new>-order_id = '3'.
" COLLECT: sums numeric fields for rows with the same key (non-numeric fields)
COLLECT ls_order INTO lt_totals.
APPEND LINES OF lt_other TO lt_std.
INSERT LINES OF lt_other INTO TABLE lt_sorted.10.5 Reading single rows
" Classic READ TABLE
READ TABLE lt_std INTO ls_order INDEX 1.
READ TABLE lt_hashed INTO ls_order WITH TABLE KEY order_id = '0000000002'.
READ TABLE lt_std INTO ls_order WITH KEY customer = 'C1'.
IF sy-subrc = 0.
" found: sy-tabix holds the index
ENDIF.
READ TABLE lt_std TRANSPORTING NO FIELDS WITH KEY status = 'X'. " existence check
" Binary search on a sorted standard table
SORT lt_std BY customer.
READ TABLE lt_std INTO ls_order WITH KEY customer = 'C1' BINARY SEARCH.
" Modern table expressions (7.40+) — raise CX_SY_ITAB_LINE_NOT_FOUND if missing
DATA(ls_first) = lt_std[ 1 ].
DATA(ls_c1) = lt_std[ customer = 'C1' ].
DATA(lv_amt) = lt_hashed[ order_id = '0000000002' ]-amount.
DATA(ls_safe) = VALUE #( lt_std[ customer = 'ZZ' ] OPTIONAL ). " initial if missing
DATA(ls_dflt) = VALUE #( lt_std[ customer = 'ZZ' ] DEFAULT ls_order ).
IF line_exists( lt_std[ customer = 'C1' ] ).
DATA(lv_idx) = line_index( lt_std[ customer = 'C1' ] ).
ENDIF.10.6 Looping
LOOP AT lt_std INTO DATA(ls_row). " copy of each row
ENDLOOP.
LOOP AT lt_std ASSIGNING FIELD-SYMBOL(<ls_row>). " direct access, no copy (faster)
<ls_row>-amount = <ls_row>-amount * '1.1'. " modifies the table in place
ENDLOOP.
LOOP AT lt_std REFERENCE INTO DATA(lr_row). " data reference
lr_row->status = 'C'.
ENDLOOP.
LOOP AT lt_std INTO ls_row WHERE status = 'O' AND amount > 100.
ENDLOOP.
LOOP AT lt_std INTO ls_row FROM 2 TO 5. " index range
ENDLOOP.10.7 Changing and deleting
MODIFY lt_std FROM ls_order INDEX 1.
MODIFY lt_std FROM VALUE #( status = 'C' ) TRANSPORTING status WHERE customer = 'C1'.
MODIFY TABLE lt_hashed FROM ls_order. " by key
lt_std[ 1 ]-status = 'C'. " table expression as target
DELETE lt_std INDEX 2.
DELETE lt_std WHERE status = 'X'.
DELETE TABLE lt_hashed WITH TABLE KEY order_id = '0000000002'.
SORT lt_std BY customer.
DELETE ADJACENT DUPLICATES FROM lt_std COMPARING customer.
CLEAR lt_std. " empties (keeps memory)
FREE lt_std. " empties and releases memory10.8 Useful operations
Number of rows lines( lt_std) or DESCRIBE TABLE lt_std LINES lv_n (old)
SORT lt_std BY customer ASCENDING amount DESCENDING. SORT lt_std STABLE BY customer. IF lt_std IS INITIAL. IF lt_a = lt_b.
lt_b = lt_a. (copy-on-write sharing internally)
10.9 Control-level processing (AT … ENDAT)
Classic group processing on a sorted table:
Caveat: inside AT blocks, fields to the right of the control field are filled with * or initial values; the work area must not be an inline declaration with a different type. Modern alternative: LOOP AT … GROUP BY (§11.7).
SORT lt_std BY customer.
LOOP AT lt_std INTO DATA(ls_line).
AT NEW customer.
WRITE: / 'Customer', ls_line-customer.
ENDAT.
WRITE: / ' Order', ls_line-order_id, ls_line-amount.
AT END OF customer.
SUM. " sums numeric fields of the group
WRITE: / ' Total', ls_line-amount.
ENDAT.
AT LAST.
SUM.
WRITE: / 'Grand total', ls_line-amount.
ENDAT.
ENDLOOP.10.10 Ranges tables
Fields: SIGN (I include / E exclude), OPTION (EQ, NE, BT, NB, CP, NP, GT, GE, LT, LE), LOW, HIGH.
DATA lr_status TYPE RANGE OF zorders-status.
lr_status = VALUE #( sign = 'I' option = 'EQ' ( low = 'O' ) ( low = 'P' ) ).
APPEND VALUE #( sign = 'I' option = 'BT' low = '1000' high = '1999' ) TO lr_amount.
LOOP AT lt_std INTO ls_line WHERE status IN lr_status.
ENDLOOP.