[ABAP] Vergleich von LOOPs mit Objektreferenzen und Feldsymbolen

* http://zevolving.com/2014/03/use-of-reference-variable-vs-workarea-vs-field-symbols/
DATA(it_p) = VALUE stringtab( ( |Udo| ) ( |Ede| ) ( |Ida| ) ).

* Code mit Referenz -> langsamer als ASSIGNING FIELD-SYMBOL
LOOP AT it_p REFERENCE INTO DATA(o_p).
* o_p dereferenzieren und Wert ausgeben
  WRITE: / o_p->*.
ENDLOOP.

* Code mit Feldsymbol -> schneller als REFERENCE INTO
LOOP AT it_p ASSIGNING FIELD-SYMBOL(<p>).
  WRITE: / <p>.
ENDLOOP.

[ABAP] Arbeit mit Referenzen

einfache Wertänderung über Referenz

* int-Variable anlegen, Wert 1
DATA(lv_int) = 1.
* Referenz auf int
DATA(o_int) = REF #( lv_int ).

* Änderung auf Wert 2
lv_int = 2.

* Ausgabe Wert 2
WRITE: / o_int->*.

* Änderung auf 3
o_int->* = 3.

* Ausgabe Wert 3
WRITE: / lv_int.

mehrfache Wertänderung über Referenz

* int-Variable anlegen, Wert 1
DATA(lv_int) = 1.
* generische Referenz auf die int-Variable
DATA(o_int) = REF data( lv_int ).
* Variable auf Wert 2 ändern
lv_int = 2.

* Zwei Feldsymbole (<i1> und <i2>) mit der Referenz verknüpfen
ASSIGN o_int->* TO FIELD-SYMBOL(<i1>).
ASSIGN o_int->* TO FIELD-SYMBOL(<i2>).

* den Wert eines der Feldsymbole ändern
<i1> = 4.

* es ändern sich durch den Bezug sogleich alle anderen Feldsymbole und Variablen mit :)
WRITE: / lv_int.
WRITE: / <i1>.
WRITE: / <i2>.

generische Referenz auf interne Tabelle

* Stringtable aus DDIC (gefüllt)
DATA(it_stringtab) = VALUE stringtab( ( |Udo| )
                                      ( |Heinz| )
                                      ( |Klaus| ) ).

* generische Referenz auf die Stringtable
DATA(o_tab) = REF data( it_stringtab ).

* Feldsymbol explizit als generische Table definieren
FIELD-SYMBOLS: <tab> TYPE ANY TABLE.
* Feldsymbol auf die interne Tabelle mit der generischen Referenz verknüpfen
ASSIGN o_tab->* TO <tab>.

* Tabelleninhalt darstellen
LOOP AT <tab> ASSIGNING FIELD-SYMBOL(<l>).
  WRITE: / <l>.
ENDLOOP.

referentieller Zugriff auf eine interne Tabelle

* Typdeklaration
TYPES: ty_it_sflight TYPE STANDARD TABLE OF sflight WITH DEFAULT KEY.

* Tabelle anlegen
DATA(it_sflight) = VALUE ty_it_sflight( ( carrid = 'AA' connid = '0123' )
                                        ( carrid = 'LH' connid = '3210' ) ).

* generische Referenz auf die Table
DATA(o_tab) = REF data( it_sflight ).

* Feldsymbol explizit als generische Table definieren
FIELD-SYMBOLS: <tab> TYPE ty_it_sflight. " hier auch STANDARD TABLE möglich
* Feldsymbol auf die interne Tabelle mit der generischen Referenz verknüpfen
ASSIGN o_tab->* TO <tab>.

* wenn Zeilen in der Tabelle vorhanden
IF lines( <tab> ) > 0.
* erste Zeile holen und mit Feldsymbol verknüpfen
  ASSIGN <tab>[ 1 ] TO FIELD-SYMBOL(<row>).
* Struktur (Felder) der ersten Zeile ermitteln
  DATA(o_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( <row> ) ).
* Anzahl Felder in der Struktur ermitteln
  DATA(lv_cnt) = lines( o_struct->get_components( ) ).

* Tabelleninhalt darstellen
  LOOP AT <tab> ASSIGNING <row>.

    DATA(lv_row) = ||.

* Felder durchgehen
    DO lv_cnt TIMES.
* Zellen einer zeile holen
      ASSIGN COMPONENT sy-index OF STRUCTURE <row> TO FIELD-SYMBOL(<cell>).
* Zellinhalt ausgeben
      IF sy-index = 0.
        lv_row = |{ <cell> }|.
      ELSE.
        lv_row = |{ lv_row } \| { <cell> }|.
      ENDIF.
    ENDDO.

    WRITE: / lv_row.
  ENDLOOP.

ENDIF.

[ABAP] Schleifenperformance durch Feldsymbole und binäre Suche verbessern

Variante 1 (langsam: keine Optimierung)

* Verkaufsbeleg: Kopfdaten
SELECT * FROM vbak INTO TABLE @DATA(it_vbak).
* Verkaufsbeleg: Positionsdaten
SELECT * FROM vbap INTO TABLE @DATA(it_vbap).

DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).

LOOP AT it_vbak ASSIGNING FIELD-SYMBOL(<k>).
  LOOP AT it_vbap ASSIGNING FIELD-SYMBOL(<p>) WHERE vbeln EQ <k>-vbeln.
    WRITE: / <p>-vbeln, <p>-matnr.
  ENDLOOP.
ENDLOOP.

DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.

WRITE: / 'Laufzeit:', sec, 's'.

Variante 2 (schnell: FIELD-SYMBOL, BINARY SEARCH)

* Verkaufsbeleg: Kopfdaten
SELECT * FROM vbak INTO TABLE @DATA(it_vbak).
* Verkaufsbeleg: Positionsdaten
SELECT * FROM vbap INTO TABLE @DATA(it_vbap).

* Tabelle muss aufsteigend sortiert sein, sonst stimmt der Rückgabeindex bei der binären Suche nicht
SORT: it_vbak BY vbeln.
SORT: it_vbap BY vbeln.

DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).

LOOP AT it_vbak ASSIGNING FIELD-SYMBOL(<k>).
* binäre Suche mit READ TABLE
* -> sollen stattdessen Table-Expressions verwendet werden, muss ein Sorted Key benutzt werden
  READ TABLE it_vbap TRANSPORTING NO FIELDS WITH KEY vbeln = <k>-vbeln BINARY SEARCH.
* gefundenen Index merken
  DATA(lv_index) = sy-tabix.

  IF sy-subrc = 0.
* Suche in der sortierten Tabelle ab Index lv_index
    LOOP AT it_vbap ASSIGNING FIELD-SYMBOL(<p>) FROM lv_index WHERE vbeln = <k>-vbeln.
      WRITE: / <p>-vbeln, <p>-matnr.
    ENDLOOP.
  ELSE.
    WRITE: / 'Fehler.'.
  ENDIF.
ENDLOOP.

DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.

WRITE: / 'Laufzeit:', sec, 's'.

Variante 3 (am schnellsten: FIELD-SYMBOLS, HASHED TABLES, Secondary Keys)

DATA: it_vbak TYPE HASHED TABLE OF vbak WITH UNIQUE KEY vbeln.
DATA: it_vbap TYPE HASHED TABLE OF vbap WITH UNIQUE KEY vbeln posnr                             " Primary Key
                                        WITH NON-UNIQUE SORTED KEY vbeln_sort COMPONENTS vbeln. " Secondary Key

* Verkaufsbeleg: Kopfdaten
SELECT * FROM vbak INTO TABLE it_vbak.
* Verkaufsbeleg: Positionsdaten
SELECT * FROM vbap INTO TABLE it_vbap.

DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).

LOOP AT it_vbak ASSIGNING FIELD-SYMBOL(<k>).
* Nested Loop über Secondary Key vbeln_sort
* BINARY SEARCH wird durch die Verwendung des Secondary Keys implizit ausgeführt
  LOOP AT it_vbap ASSIGNING FIELD-SYMBOL(<p>) USING KEY vbeln_sort WHERE vbeln = <k>-vbeln.
    WRITE: / <p>-vbeln, <p>-matnr.
  ENDLOOP.
ENDLOOP.

DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.

WRITE: / 'Laufzeit:', sec, 's'.

Links

[ABAP] Generische Verarbeitung von internen Tabellen / Verwendung von Referenzen

* irgendeine interne Tablelle anlegen
DATA: it_tab TYPE STANDARD TABLE OF ...
* generische Tabellenreferenz anlegen
DATA: o_ref_table TYPE REF TO data.
 
...
 
* Tabellenreferenz übergeben
o_ref_table = REF #( it_tab ).
 
* Referenz auf generisches Feldsymbol mappen
FIELD-SYMBOLS: <tab> TYPE ANY TABLE.
ASSIGN o_ref_table->* TO <tab>.
 
* Tabellen-Zeile erzeugen
* muss hier erfolgen, damit man ein "greifbares" Tabellen-Zeilen-Objekt
* für die Strukturermittlung (describe_by_data) hat
DATA: o_row TYPE REF TO data.
CREATE DATA o_row LIKE LINE OF <tab>.
FIELD-SYMBOLS: <row> TYPE any.
ASSIGN o_row->* TO <row>.
 
* Komponenten (Spalten) einer Tabellenzeile ermitteln
DATA(o_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( <row> ) ).
DATA(it_comp_tab) = o_struct->get_components( ).
 
* Anzahl Spalten der Tabellen-Zeile holen
DATA(lv_colcnt) = lines( it_comp_tab ).
 
* Tabelle durchloopen
LOOP AT <tab> ASSIGNING <row>.

* Spalten der akt. Zeile durchgehen
  DO lv_colcnt TIMES.
* Zelle: n-tes Element der akt. Zeile holen
    ASSIGN COMPONENT sy-index OF STRUCTURE <row> TO FIELD-SYMBOL(<cell>).
    
* Trennzeichen vor die Spalte einfügen, wenn nicht 1. Spalte
    IF sy-index > 1.
      WRITE: '|'.
    ENDIF.

* Achtung: Zell-Typ beachten! Es können hier nur flache Typen (Keine Strukturen, Tabellen) ausgegeben werden, der Rest muss gesondert behandelt werden
    IF CAST cl_abap_elemdescr( it_comp_tab[ sy-index ]-type )->kind = cl_abap_elemdescr=>kind_elem.
* Ausgabe Zellinhalt mit vordefinierter Spaltenbreite
      WRITE: |{ <cell> WIDTH = 20 |.
    ENDIF.
  ENDDO.

  NEW-LINE.
ENDLOOP.

[ABAP] Dynamische Tabelle anhand eines Tabellennamens erstellen, Daten ausgeben

DATA: table_name TYPE string VALUE 'spfli'. " Tabellenname
DATA: o_it TYPE REF TO data.                " Referenz auf Tabelle
DATA: o_row TYPE REF TO data.               " Referenz auf Tabellenzeile

FIELD-SYMBOLS: <fs_table> TYPE ANY TABLE.   " Feldsymbol für Arbeit mit Tabelle
FIELD-SYMBOLS: <fs_row> TYPE any.           " Feldsymbol für Arbeit mit Zeile

* dynamische Tabelle vom Typ table_name erzeugen
CREATE DATA o_it TYPE TABLE OF (table_name).
* Feldsymbol auf die dynamische Tabelle anlegen
ASSIGN o_it->* TO <fs_table>.

IF <fs_table> IS ASSIGNED.
* dynamische Workarea vom Typ der Tabellenzeile erzeugen
  CREATE DATA o_row LIKE LINE OF <fs_table>.
* Feldsymbol auf die Workarea anlegen
  ASSIGN o_row->* TO <fs_row>.

  IF <fs_row> IS ASSIGNED.
* Daten holen
    SELECT * FROM (table_name) INTO TABLE <fs_table>.

* Daten ausgeben
    DATA: go_struct TYPE REF TO cl_abap_structdescr.

* herausfinden, wieviele Spalten die Tabelle hat
    go_struct ?= cl_abap_typedescr=>describe_by_data( <fs_row> ).
    DATA(gt_comp) = go_struct->get_components( ).

* alle Datensätze der Tabelle durchgehen
    LOOP AT <fs_table> INTO <fs_row>.

* spaltenweise jeden Datensatz durchgehen
      LOOP AT gt_comp ASSIGNING FIELD-SYMBOL(<fs_col>).
* den Inhalt der Strukturkomponente (Zeile) ausgeben
        ASSIGN COMPONENT <fs_col>-name OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fs_cell>).

        IF <fs_cell> IS ASSIGNED.
          WRITE: <fs_cell>.
        ENDIF.
      ENDLOOP.

      NEW-LINE.

    ENDLOOP.
  ENDIF.
ENDIF.

[ABAP] Daten einer internen Tabelle verändern (Index, Key, Feldsymbol)

* Tabelleninhalt über Index modifizieren
MODIFY itab FROM struct INDEX 1.

* Eintrag in der Tabelle über ein Feldsymbol direkt ändern
FIELD-SYMBOLS: <z2> LIKE LINE OF itab.
READ TABLE itab WITH TABLE KEY keyname = 'Wert' ASSIGNING <z2>.
<z2>-Element = 'Wert'.

* Änderung einer Tabellenspalte (struct-element) über Bedingung
MODIFY itab FROM struct TRANSPORTING element WHERE keyname = 'Wert'.

[ABAP] Daten von interner Tabelle lesen (Index, Key, Feldsymbol)

Typen & Daten

* Zeilentyp der internen Tabelle
TYPES : BEGIN OF ty_s_persons,
          pernr TYPE i,
          name  TYPE string,
          age   TYPE i,
        END OF ty_s_persons.

* Typ: Interne Tabelle mit Primärschlüssel pernr und Sekundärschlüssel persons
TYPES: ty_it_persons TYPE STANDARD TABLE OF ty_s_persons WITH KEY pernr
                                                         WITH NON-UNIQUE SORTED KEY persons COMPONENTS name.

* Daten
DATA(it_persons) = VALUE ty_it_persons(
                                        ( pernr = 1 name = |Udo|    age = 20 )
                                        ( pernr = 2 name = |Heinz|  age = 30 )
                                        ( pernr = 3 name = |Jochen| age = 40 )
                                        ( pernr = 4 name = |Ede|    age = 50 )
                                        ( pernr = 5 name = |Ina|    age = 60 )
                                        ( pernr = 6 name = |Erika|  age = 70 )
                                        ( pernr = 7 name = |Erna|   age = 80 )
                                      ).

Datensatz mit Index 1 auslesen

* table expressions
IF line_exists( it_persons[ 1 ] ).
* Datenstruktur
  DATA(lv_per1) = it_persons[ 1 ].
  WRITE: / lv_per1-pernr, lv_per1-name, lv_per1-age.

* Feldsymbol
  ASSIGN it_persons[ 1 ] TO FIELD-SYMBOL(<p1>).
  IF <p1> IS ASSIGNED.
    WRITE: / <p1>-pernr, <p1>-name, <p1>-age.
  ENDIF.
ENDIF.

* READ TABLE
* Datenstruktur
READ TABLE it_persons INDEX 1 INTO DATA(lv_per2).
IF sy-subrc = 0.
  WRITE: / lv_per2-pernr, lv_per2-name, lv_per2-age.
ENDIF.
* Feldsymbol
READ TABLE it_persons INDEX 1 ASSIGNING FIELD-SYMBOL(<p2>).
IF sy-subrc = 0 AND <p2> IS ASSIGNED.
  WRITE: / <p2>-pernr, <p2>-name, <p2>-age.
ENDIF.

Datensatz mit Primärschlüssel auslesen

* table expressions
IF line_exists( it_persons[ pernr = 2 ] ).
* Datenstruktur
  DATA(lv_per3) = it_persons[ pernr = 2 ].
  WRITE: / lv_per3-pernr, lv_per3-name, lv_per3-age.

* Feldsymbol
  ASSIGN it_persons[ pernr = 2 ] TO FIELD-SYMBOL(<p3>).
  IF <p3> IS ASSIGNED.
    WRITE: / <p3>-pernr, <p3>-name, <p3>-age.
  ENDIF.
ENDIF.

* READ TABLE
* Datenstruktur
READ TABLE it_persons WITH TABLE KEY pernr = 2 INTO DATA(lv_per4).
IF sy-subrc = 0.
  WRITE: / lv_per4-pernr, lv_per4-name, lv_per4-age.
ENDIF.
* Feldsymbol
READ TABLE it_persons WITH TABLE KEY pernr = 2 ASSIGNING FIELD-SYMBOL(<p4>).
IF sy-subrc = 0 AND <p4> IS ASSIGNED.
  WRITE: / <p4>-pernr, <p4>-name, <p4>-age.
ENDIF.

Datensatz mit Sekundärschlüssel auslesen

* table expressions
IF line_exists( it_persons[ KEY persons COMPONENTS name = 'Ina' ] ).
* Datenstruktur
  DATA(lv_per5) = it_persons[ KEY persons COMPONENTS name = 'Ina' ].
  WRITE: / lv_per5-pernr, lv_per5-name, lv_per5-age.

* Feldsymbol
  ASSIGN it_persons[ KEY persons COMPONENTS name = 'Ina' ] TO FIELD-SYMBOL(<p5>).
  IF <p5> IS ASSIGNED.
    WRITE: / <p5>-pernr, <p5>-name, <p5>-age.
  ENDIF.
ENDIF.

* READ TABLE
* Datenstruktur
READ TABLE it_persons WITH KEY persons COMPONENTS name = 'Ina' INTO DATA(lv_per6).
IF sy-subrc = 0.
  WRITE: / lv_per6-pernr, lv_per6-name, lv_per6-age.
ENDIF.
* Feldsymbol
READ TABLE it_persons WITH KEY persons COMPONENTS name = 'Ina' ASSIGNING FIELD-SYMBOL(<p6>).
IF sy-subrc = 0 AND <p6> IS ASSIGNED.
  WRITE: / <p6>-pernr, <p6>-name, <p6>-age.
ENDIF.

[ABAP] Verwendung von Feldsymbolen (Zeiger)

* Zeilentyp
TYPES: BEGIN OF ty_data,
         name TYPE string,
         ort TYPE string,
       END   OF ty_data.

* Tabellentyp
TYPES: ty_it_itab TYPE STANDARD TABLE OF ty_data WITH DEFAULT KEY.

* Tabelle
DATA: it_itab TYPE ty_it_itab.

* Feldsymbol definieren
FIELD-SYMBOLS: <fs_line> TYPE ty_data.

* Beispielausgabe 1 mit Feldsymbol
LOOP AT itab ASSIGNING <fs_line>.
  WRITE: / <fs_line>-name, <fs_line>-ort.
ENDLOOP.

* Beispielausgabe 2 mit Inline-Feldsymbol
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs_inline>).
  WRITE: / <fs_inline>-name, <fs_inline>-ort.
ENDLOOP.