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 memory

10.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.