ABAP READ TABLE Anweisung: Einzelne Zeilen in internen Tabellen finden

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Die READ TABLE-Anweisung dient dazu, eine einzelne, spezifische Zeile aus einer internen Tabelle zu lesen. Im Gegensatz zu LOOP AT, das alle oder mehrere Zeilen durchläuft, ist READ TABLE für den gezielten Zugriff auf genau eine Zeile optimiert – entweder über den Tabellenschlüssel, einen Index oder eine freie Suchbedingung.

Syntax

Es gibt verschiedene Varianten, um die gewünschte Zeile zu identifizieren:

1. Suche über Schlüsselfelder (WITH KEY)

READ TABLE <interne_tabelle>
INTO <arbeitsbereich> | ASSIGNING <feldsymbol> | REFERENCE INTO <datenreferenz>
WITH KEY <komponente1> = <wert1> [<komponente2> = <wert2> ...]
[BINARY SEARCH].

2. Suche über Tabellenschlüssel (WITH TABLE KEY)

READ TABLE <interne_tabelle>
INTO <arbeitsbereich> | ASSIGNING <feldsymbol> | REFERENCE INTO <datenreferenz>
WITH TABLE KEY <schlüsselkomponente1> = <wert1> [<schlüsselkomponente2> = <wert2> ...].

3. Zugriff über Index (INDEX)

READ TABLE <interne_tabelle>
INTO <arbeitsbereich> | ASSIGNING <feldsymbol> | REFERENCE INTO <datenreferenz>
INDEX <index>.

4. Suche mit freier Bedingung (WITH KEY ... COMPONENTS)

READ TABLE <interne_tabelle>
INTO <arbeitsbereich> | ASSIGNING <feldsymbol> | REFERENCE INTO <datenreferenz>
WITH KEY <tabellenkomponente> COMPONENTS <komponente1> = <wert1> [...].

5. Nur Existenzprüfung ohne Datentransport

READ TABLE <interne_tabelle>
TRANSPORTING NO FIELDS
WITH KEY <komponente> = <wert>.

Bestandteile

  • <interne_tabelle>: Die Tabelle, in der gesucht werden soll.
  • INTO <arbeitsbereich>: Die gefundene Zeile wird in den Arbeitsbereich kopiert.
  • ASSIGNING <feldsymbol>: Das Feldsymbol zeigt direkt auf die gefundene Zeile.
  • REFERENCE INTO <datenreferenz>: Eine Datenreferenz auf die gefundene Zeile.
  • WITH KEY: Suche über beliebige Komponenten (nicht zwingend der Tabellenschlüssel).
  • WITH TABLE KEY: Suche über den definierten Primärschlüssel der Tabelle (optimiert).
  • INDEX <index>: Direkter Zugriff auf die Zeile an der angegebenen Position.
  • BINARY SEARCH: Binäre Suche (nur bei sortierten Standardtabellen – Tabelle muss sortiert sein!).
  • TRANSPORTING NO FIELDS: Es werden keine Daten transportiert, nur sy-subrc und sy-tabix gesetzt.

Systemfelder

Nach einem READ TABLE werden folgende Systemfelder gesetzt:

  • sy-subrc:

    • 0: Zeile wurde gefunden.
    • 4: Keine passende Zeile gefunden.
    • 8: (Bei BINARY SEARCH) Zeile nicht gefunden, aber sy-tabix zeigt auf die Einfügeposition.
  • sy-tabix: Enthält den Index der gefundenen Zeile (bei Index-Tabellen). Bei Hash-Tabellen ist sy-tabix nicht definiert.

Suchverhalten nach Tabellentyp

Das Suchverhalten und die Performance hängen vom Tabellentyp ab:

TabellentypWITH KEYWITH TABLE KEYINDEXPerformance
STANDARD TABLELineare Suche O(n)Lineare Suche O(n)O(1)Langsam bei großen Tabellen
SORTED TABLEBinäre Suche O(log n)Binäre Suche O(log n)O(1)Schnell
HASHED TABLEHash-Lookup O(1)Hash-Lookup O(1)Nicht möglichSehr schnell

Beispiele

1. READ TABLE mit INTO (Kopie)

TYPES: BEGIN OF ty_customer,
id TYPE i,
name TYPE string,
city TYPE string,
END OF ty_customer.
DATA: lt_customers TYPE STANDARD TABLE OF ty_customer WITH EMPTY KEY,
ls_customer TYPE ty_customer.
" Tabelle füllen
lt_customers = VALUE #(
( id = 1 name = 'Müller GmbH' city = 'Berlin' )
( id = 2 name = 'Schmidt AG' city = 'München' )
( id = 3 name = 'Weber KG' city = 'Hamburg' )
).
" Kunde mit ID 2 suchen
READ TABLE lt_customers INTO ls_customer WITH KEY id = 2.
IF sy-subrc = 0.
WRITE: / 'Gefunden:', ls_customer-name, 'in', ls_customer-city.
WRITE: / 'Index:', sy-tabix.
ELSE.
WRITE: / 'Kunde nicht gefunden.'.
ENDIF.

2. READ TABLE mit ASSIGNING (Direktzugriff)

FIELD-SYMBOLS: <fs_customer> TYPE ty_customer.
" Kunde suchen und direkt ändern
READ TABLE lt_customers ASSIGNING <fs_customer> WITH KEY id = 1.
IF sy-subrc = 0.
" Änderung wirkt direkt in der Tabelle
<fs_customer>-city = 'Frankfurt'.
WRITE: / 'Stadt geändert zu:', <fs_customer>-city.
ELSE.
WRITE: / 'Kunde nicht gefunden.'.
ENDIF.

3. READ TABLE mit INDEX

" Erste Zeile lesen
READ TABLE lt_customers INTO ls_customer INDEX 1.
IF sy-subrc = 0.
WRITE: / 'Erste Zeile:', ls_customer-name.
ENDIF.
" Letzte Zeile lesen
READ TABLE lt_customers INTO ls_customer INDEX lines( lt_customers ).
IF sy-subrc = 0.
WRITE: / 'Letzte Zeile:', ls_customer-name.
ENDIF.
" WICHTIG: Tabelle muss nach dem Suchfeld sortiert sein!
" Siehe: /sort-statement/
SORT lt_customers BY id.
READ TABLE lt_customers INTO ls_customer
WITH KEY id = 2
BINARY SEARCH.
IF sy-subrc = 0.
WRITE: / 'Gefunden (binär):', ls_customer-name.
ENDIF.

Achtung: BINARY SEARCH bei einer nicht sortierten Tabelle liefert falsche Ergebnisse ohne Fehlermeldung!

5. READ TABLE mit TRANSPORTING NO FIELDS

" Nur prüfen, ob ein Kunde existiert
READ TABLE lt_customers TRANSPORTING NO FIELDS
WITH KEY city = 'Berlin'.
IF sy-subrc = 0.
WRITE: / 'Mindestens ein Kunde in Berlin (Index:', sy-tabix, ').'.
ELSE.
WRITE: / 'Kein Kunde in Berlin.'.
ENDIF.

6. READ TABLE mit mehreren Schlüsselfeldern

TYPES: BEGIN OF ty_order,
customer_id TYPE i,
order_id TYPE i,
amount TYPE p DECIMALS 2,
END OF ty_order.
DATA: lt_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY,
ls_order TYPE ty_order.
lt_orders = VALUE #(
( customer_id = 1 order_id = 100 amount = '1500.00' )
( customer_id = 1 order_id = 101 amount = '2300.50' )
( customer_id = 2 order_id = 100 amount = '800.00' )
).
" Bestellung für Kunde 1, Order 101 suchen
READ TABLE lt_orders INTO ls_order
WITH KEY customer_id = 1
order_id = 101.
IF sy-subrc = 0.
WRITE: / 'Betrag:', ls_order-amount.
ENDIF.

7. READ TABLE mit REFERENCE INTO

DATA: lr_customer TYPE REF TO ty_customer.
READ TABLE lt_customers REFERENCE INTO lr_customer WITH KEY id = 3.
IF sy-subrc = 0.
" Zugriff über Dereferenzierung
WRITE: / 'Name:', lr_customer->name.
lr_customer->city = 'Köln'. " Ändert die Tabelle direkt
ENDIF.

Moderne Alternative: Tabellenausdrücke (ab ABAP 7.40)

Ab ABAP 7.40 können Tabellenausdrücke als kompakte Alternative verwendet werden:

Inline-Zugriff mit Tabellenausdruck

" Direkter Zugriff (löst Ausnahme aus, wenn nicht gefunden)
TRY.
DATA(ls_found) = lt_customers[ id = 2 ].
WRITE: / 'Gefunden:', ls_found-name.
CATCH cx_sy_itab_line_not_found.
WRITE: / 'Nicht gefunden.'.
ENDTRY.
" Zugriff per Index
DATA(ls_first) = lt_customers[ 1 ].
" Mit OPTIONAL (gibt initiale Zeile zurück, wenn nicht gefunden)
DATA(ls_safe) = VALUE #( lt_customers[ id = 99 ] OPTIONAL ).
" Mit DEFAULT (gibt Standardwert zurück, wenn nicht gefunden)
DATA(ls_default) = VALUE #( lt_customers[ id = 99 ]
DEFAULT VALUE #( id = 0 name = 'Unbekannt' ) ).

Existenzprüfung mit line_exists()

IF line_exists( lt_customers[ id = 2 ] ).
WRITE: / 'Kunde 2 existiert.'.
ENDIF.
IF NOT line_exists( lt_customers[ city = 'Stuttgart' ] ).
WRITE: / 'Kein Kunde in Stuttgart.'.
ENDIF.

Index ermitteln mit line_index()

DATA(lv_index) = line_index( lt_customers[ id = 2 ] ).
IF lv_index > 0.
WRITE: / 'Kunde 2 ist an Position:', lv_index.
ELSE.
WRITE: / 'Nicht gefunden.'.
ENDIF.

READ TABLE vs. Tabellenausdruck

AspektREAD TABLETabellenausdruck
SyntaxAusführlicherKompakt
Nicht gefundensy-subrc = 4Exception oder OPTIONAL
Index verfügbarsy-tabixline_index()
PerformanceIdentischIdentisch
ASSIGNINGJaJa (mit FIELD-SYMBOL)
" Tabellenausdruck mit Feldsymbol
ASSIGN lt_customers[ id = 2 ] TO FIELD-SYMBOL(<fs_cust>).
IF sy-subrc = 0.
<fs_cust>-city = 'Dresden'.
ENDIF.

Abgrenzung zu LOOP AT

  • READ TABLE: Liest eine einzelne Zeile. Ideal für gezielte Zugriffe.
  • LOOP AT: Durchläuft mehrere/alle Zeilen sequenziell.
" Eine bestimmte Zeile lesen → READ TABLE
READ TABLE lt_customers INTO ls_customer WITH KEY id = 2.
" Alle Zeilen mit bestimmtem Kriterium verarbeiten → LOOP AT
LOOP AT lt_customers INTO ls_customer WHERE city = 'Berlin'.
" Mehrere Zeilen verarbeiten
ENDLOOP.

Performance-Tipps

  1. Richtigen Tabellentyp wählen:

    • Häufige Schlüsselzugriffe → SORTED TABLE oder HASHED TABLE
    • Sequenzieller Zugriff → STANDARD TABLE
  2. BINARY SEARCH nur bei sortierten Tabellen: Verwenden Sie SORT vor der binären Suche:

    " Falsch: BINARY SEARCH ohne Sortierung
    READ TABLE lt_unsorted INTO ls_wa WITH KEY field = value BINARY SEARCH. " Gefährlich!
    " Richtig: Erst sortieren
    SORT lt_data BY field.
    READ TABLE lt_data INTO ls_wa WITH KEY field = value BINARY SEARCH.
  3. Sekundärschlüssel nutzen:

    DATA: lt_customers TYPE SORTED TABLE OF ty_customer
    WITH UNIQUE KEY id
    WITH NON-UNIQUE SORTED KEY by_city COMPONENTS city.
    " Schneller Zugriff über Sekundärschlüssel
    READ TABLE lt_customers INTO ls_customer
    WITH KEY by_city COMPONENTS city = 'Berlin'.
  4. TRANSPORTING NO FIELDS für Existenzprüfungen: Wenn nur geprüft werden soll, ob eine Zeile existiert, ist TRANSPORTING NO FIELDS effizienter.

  5. Tabellenausdrücke mit line_exists(): Für reine Existenzprüfungen ist line_exists() elegant und performant.

Wichtige Hinweise / Best Practice

  • Prüfen Sie immer sy-subrc nach einem READ TABLE, bevor Sie auf die Ergebnisdaten zugreifen.
  • Verwenden Sie ASSIGNING für bessere Performance und wenn Sie die Zeile ändern möchten. Alternativ können Sie MODIFY verwenden.
  • BINARY SEARCH nur verwenden, wenn die Tabelle garantiert sortiert ist (siehe SORT) – andernfalls sind die Ergebnisse unzuverlässig.
  • Für häufige Schlüsselzugriffe sollten Sie sortierte oder Hash-Tabellen in Betracht ziehen.
  • Ab ABAP 7.40 sind Tabellenausdrücke oft die elegantere und kürzere Alternative.
  • Bei Hash-Tabellen ist sy-tabix nach READ TABLE nicht definiert – verlassen Sie sich nicht darauf.
  • READ TABLE ist für interne Tabellen. Für Datenbanktabellen verwenden Sie SELECT SINGLE.