Modernes ABAP

Neuigkeiten zu internen Tabellen

(C) Brandeis Consulting.

Übersicht

  • Tabellenausdrücke
  • Tabellenfunktionen
  • Gruppenstufenverarbeitung
(C) Brandeis Consulting.

Tabellenausdrücke

Tabellenausdrücke sollen den lesende und schreibende Zugriff auf einzelne Zeilen und deren Felder vereinfachen. Sie ersetzen somit READ ... INTO und READ ... ASSIGNING Anweisungen und teilweise auch LOOP-Schleifen.

Tabellenausdrücke liefern eine Tabellenzeile*

*In SQL würde man sie deshalb Zeilenausdrücke nennen

(C) Brandeis Consulting.

Struktur von Tabellenausdrücken

<Tabelle>[ <Zeilenspezifikation> ]

Die Tabelle kann eine beliebige interne Tabelle sein. Die Zeilenspezifikation kann auf unterschiedliche Art stattfinden:

  • Angabe der Zeilennummer bei Standardtabellen
  • Angabe eines freien Schlüssel
  • Angabe eines Tabellenschlüssels
(C) Brandeis Consulting.

Beispiele für Tabellenausdrücke

    SELECT *
      FROM zbc_users
      INTO TABLE @DATA(lt_user).

    DATA(ls_first_row) = lt_user[ 1 ].

    DATA(lv_firstname) = lt_user[ 1 ]-firstname.

    DATA(lv_lastname_of_Binky) = lt_user[ firstname = 'Binky' ]-lastname.

    out->write( lv_lastname_of_binky ).

An den Beispielen sieht man schön: Es ist auch möglich auf Komponenten der Struktur zuzugreifen. Und wenn diese Komponenten wieder eine Tabelle sind, dann könnte man auch dort wieder einen Pfadausdruck platzieren...

(C) Brandeis Consulting.

Fehlerhandling bei Tabellenausdrücken

Bei der Zeilenspezifikation sind zwei Fehlersituationen möglich:

  • Es wird mehr als eine Datensatz gefunden. Dann verwendet ABAP den ersten davon.
  • Es wird kein Datensatz gefunden. Dann wird eine Ausnahme vom Typ CX_SY_ITAB_LINE_NOT_FOUND erzeugt.
    TRY.
        DATA(ls_first_row) = lt_user[ 1 ].

        DATA(lv_firstname) = lt_user[ 1 ]-firstname.

        DATA(lv_lastname) = lt_user[ firstname = 'Jörg' ]-lastname.

        out->write( lv_lastname ).

      CATCH cx_sy_itab_line_not_found INTO DATA(lx_not_found).
        out->write( |Nothing found :( | ).
    ENDTRY.
(C) Brandeis Consulting.

Alternatve zur Exception - Verwendung im VALUE Konstruktorausdruck

Falls kein Datensatz durchdie Zeilenspezialisierung gefunden wird, kann man statt einer Exception auch einen leere Zeile erhalten. Oder eine Alternative Zeile. Dazu wird der Tabellenausdruck in einen VALUE Konstruktorausdruck gepackt:

...VALUE #( <Tabelle>[<Zeilenspezifikation>] 
             DEFAULT <Alternativer Tabellenausdruck> | OPTIONAL )

Alternative mit DEFAULT

Falls der ursprüngliche Tabellenausdruck scheitert, dann wird der Wert des alternativen Tabellenausdrucks verwendet. Dieser kann natürlich auch wieder in einem VALUE Operator stecken...

Initiale Zeile mit OPTIONAL

Wenn hinter dem Tabellenausdruck im VALUE das Schlüsselwort OPTIONAL steht, dann wird bei gescheitertem Zeilenzugriff eine initiale Zeile zurückgegeben.

(C) Brandeis Consulting.

Verschachtelung (chaining) von Tabellenausdrücken

Das Ergebnis eines Tabellenausdrucks ist eine Zeile, d.h. Struktur
Auf deren Komponenten kann man wie gewohnt mit - zugreifen.

Falls die Tabelle in einzelnen Feldern wiederum Tabellen hat, so kann man Ketten von Tabellenausdrücken nutzen.

users[ user_id = 3 ]-tasks[ 1 ]-due_date

(C) Brandeis Consulting.

Schreibposition von Tabellenausdrücken

Ein Tabellenausdruck ist zunächst ein Pointer auf die mit der Zeilenspezifikation angegebene Zeile. Das bedeutet, dass diese auch geändert werden kann.

Welche Zeilen werde hier wirklich geändert?

    DATA(lt_data) = get_user_table( ).

    lt_data[ user_id = 1 ]-firstname = 'Olaf'.

    DATA(ls_data) = lt_data[ 2 ].
    ls_data-firstname = 'Teddy'.

    ASSIGN lt_data[ 3 ] TO FIELD-SYMBOL(<ls_data>).
    <ls_data>-firstname = 'Anke'.

    out->write( lt_data ).
(C) Brandeis Consulting.

Limitierungen

Es kann nur auf Gleichheit geprüft werden.

(C) Brandeis Consulting.

Tabellenfunktionen

LINES( <Tabelle> ) liefert die Anzahl der Datensätze in der Tabelle als Ausdruck. D.h. es kann direkt als Operator für alles mögliche verwendet werden.

LINE_INDEX( <Tabellenausdruck> ) liefert die Zeilennummer der durch den Tabellenausdruck festgelegten Zeile.

TODO line_exists( table_exp ) ...

        SELECT *
      FROM zbc_users
      INTO TABLE @DATA(lt_user).

      out->write( |Binky steht in Zeile { line_index( lt_user[ firstname = 'Binky' ]  ) }| ).
  
(C) Brandeis Consulting.

ITAB - LOOP AT ... GROUP BY [ABAP 7.53]

Schleife über eine Interne Tabelle, gruppiert nach Feldern. Die Schleife erfolgt in 2 Schritten:

  • LOOP über die Gruppen
    • LOOP über die Datensätze der jeweiligen Gruppe

Ersatz für die Gruppenstufenverarbeitung mit AT:

  • AT NEW <Component>
  • AT END OF <Component>
(C) Brandeis Consulting.

ITAB - LOOP AT ... GROUP BY - Syntax

LOOP AT <Table>
        <Line> 
        [<Condition>]
        GROUP BY <GroupingKey>
        [<Sorting>]
        [WITHOUT MEMBERS]
        [<GroupingResult>]
   ...
   LOOP AT GROUP <GroupingResult> 
        <Result>
        [WHERE <LogExpr>]
        [GROUP BY ... ] 

    ENDLOOP     
   ...
ENDLOOP.      
  • Tabelle - Die Interne Tabelle
  • Line - INTO/ASSIGNING/REF TO/TNF - Zeile des äusseren LOOP in der Struktur von <ITAB>, nur die Daten im GroupingKey sind gefüllt.
  • Condition - Filter mit WHERE-Klausl oder Schlüssel
  • GroupingKey - Gruppierungsschlüssel, entweder einzelwert oder in Klammern eine Liste
  • Sorting - Sortierung der Gruppen nach Gruppierunsschlüssel ASCENDING oder DESCENDING
  • GroupingResult - Gruppenstruktur
(C) Brandeis Consulting.

ITAB - LOOP AT ... GROUP BY - Vorteile

  • Funktioniert immer korrekt, auch unabhängig von der Sortierung der Daten
  • Hängt nicht an der Reihenfolge der Felder in der Struktur
  • es kann nach beliebigen Komponenten gruppieren
  • Ist intuitiver von der Syntax.
(C) Brandeis Consulting.

Beispiel nur Gruppierung

Im einfachsten Falle wird nur gruppiert, ohne dann über die Gruppe zu Loopen. Daran sieht man den Charakter des äußeren Loops

    loop at lt_user into data(ls_user) 
                    group by ls_user-gender.
        out->write( ls_user ).
    endloop.

Ergebnis:

Structure
CLIENT  USER_ID     FIRSTNAME  LASTNAME  EMAIL               GENDER  DATE_OF_BIRTH  
100     0000000001  Emmye      Alywen    ealywen0@alexa.com  F       1976-05-16     
Structure  
CLIENT  USER_ID     FIRSTNAME  LASTNAME      EMAIL                       GENDER  DATE_OF_BIRTH  
100     0000000003  Stavro     Antonopoulos  santonopoulos2@histats.com  M       2000-09-16     

In LS_USER steht immer eine ganze Zeile der Tabelle. Da es in den Daten genau zwei Ausprägungen des Feldes GENDER gibt, wurden hier 2 Gruppen gebildet und wir sehen jeweils eine Zeile davon in der Ausgabe.

(C) Brandeis Consulting.

LOOP AT ...INTO ... GROUP BY ... INTO

Das 2. INTO füllt die Gruppenstruktur. Diese besteht aus allen Feldern des Gruppierungsschlüssels.

    DATA(lt_user) = get_user_table( ).

    LOOP AT lt_user INTO DATA(ls_user)
                    GROUP BY ls_user-gender
                    INTO DATA(ls_group).
      out->write( ls_group ).
    ENDLOOP.

Ergebnis

F
M
(C) Brandeis Consulting.

LOOP über die Gruppenstruktur

Im LOOP über die Daten der Gruppe repräsentiert die Gruppenstruktur die entsprechende Tabelle.

LOOP

    LOOP AT lt_user INTO DATA(ls_user)
                    GROUP BY ls_user-gender
                    INTO DATA(ls_grouping).

      out->write( |At begin of gender { ls_grouping } | ).

      LOOP AT GROUP ls_grouping INTO DATA(ls_group).
        out->write( ls_group-firstname ).
      ENDLOOP.

      out->write( |At end of gender { ls_grouping } | ).
    ENDLOOP.

Ergebnis

At begin of gender F 
   Emmye
   Yolande
   Heidi
   Constantina
   Martica
At end of gender F 
At begin of gender M 
   Stavro
   Peyter
   Gonzalo
   Thibaut
At end of gender M 
(C) Brandeis Consulting.

LOOP .. STEP [ABAP 7.57]

Schrittweite beim LOOP. Kann positiv oder Negativ sein.

    DATA(lt_user) =  get_user_table( ) .
    LOOP AT lt_user INTO DATA(ls_user) STEP 10.
      out->write( |Benutzer: { ls_user-user_id }| ).
    ENDLOOP.
(C) Brandeis Consulting.