Mesh Expressions ermöglichen die Navigation zwischen verknüpften internen Tabellen über definierte Assoziationen - ähnlich wie Joins in SQL, aber für interne Tabellen. Statt verschachtelte LOOPs mit WHERE-Bedingungen zu schreiben, definierst du die Beziehungen einmal zentral und navigierst dann deklarativ.
Was sind Mesh Expressions?
Ein Mesh ist ein typisierter Verbund von internen Tabellen mit definierten Assoziationen. Du definierst die Beziehungen zwischen den Tabellen (1:n, n:1) und kannst dann über Pfad-Ausdrücke navigieren.
| Konzept | Beschreibung |
|---|---|
TYPES MESH | Definiert den Mesh-Typ mit Tabellen und Assoziationen |
ASSOCIATION name TO node | Vorwärts-Assoziation (z.B. Header → Items) |
ON field = field | Join-Bedingung der Assoziation |
mesh-node\assoc[ line ] | Forward-Navigation: Alle zugehörigen Zeilen |
mesh-node\_assoc[ line ] | Backward-Navigation: Zurück zum Parent |
Verfügbarkeit
Mesh Expressions sind seit ABAP 7.40 SP05 verfügbar und vollständig ABAP Cloud-kompatibel. In BTP ABAP Environment und S/4HANA Cloud können sie ohne Einschränkungen verwendet werden.
Praxisbeispiel: Bestellung mit Positionen und Konditionen
Ein typisches Szenario ist die Bestellabwicklung mit drei Ebenen:
- Bestellkopf (Bestellnummer, Kunde, Datum)
- Bestellpositionen (Artikel, Menge, Preis)
- Konditionen (Rabatte, Zuschläge pro Position)
Klassischer Ansatz: Verschachtelte LOOPs
Ohne Mesh Expressions sieht der Code typischerweise so aus:
CLASS zcl_order_classic DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_order_header, order_id TYPE sysuuid_x16, customer TYPE string, order_date TYPE d, END OF ty_s_order_header.
TYPES: BEGIN OF ty_s_order_item, order_id TYPE sysuuid_x16, item_no TYPE i, product TYPE string, quantity TYPE i, net_price TYPE p DECIMALS 2, END OF ty_s_order_item.
TYPES: BEGIN OF ty_s_condition, order_id TYPE sysuuid_x16, item_no TYPE i, condition_type TYPE c LENGTH 4, amount TYPE p DECIMALS 2, END OF ty_s_condition.
TYPES ty_t_headers TYPE STANDARD TABLE OF ty_s_order_header WITH EMPTY KEY. TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_order_item WITH EMPTY KEY. TYPES ty_t_conditions TYPE STANDARD TABLE OF ty_s_condition WITH EMPTY KEY.
ENDCLASS.
CLASS zcl_order_classic IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Testdaten DATA(lv_order1) = cl_system_uuid=>create_uuid_x16_static( ).
DATA(lt_headers) = VALUE ty_t_headers( ( order_id = lv_order1 customer = 'Müller GmbH' order_date = sy-datum ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = lv_order1 item_no = 10 product = 'Laptop' quantity = 2 net_price = '1200.00' ) ( order_id = lv_order1 item_no = 20 product = 'Monitor' quantity = 3 net_price = '350.00' ) ).
DATA(lt_conditions) = VALUE ty_t_conditions( ( order_id = lv_order1 item_no = 10 condition_type = 'RA01' amount = '-120.00' ) ( order_id = lv_order1 item_no = 10 condition_type = 'ZU01' amount = '50.00' ) ( order_id = lv_order1 item_no = 20 condition_type = 'RA01' amount = '-52.50' ) ).
" Verschachtelte LOOPs - unübersichtlich LOOP AT lt_headers INTO DATA(ls_header). out->write( |Bestellung: { ls_header-customer }| ).
LOOP AT lt_items INTO DATA(ls_item) WHERE order_id = ls_header-order_id.
out->write( | Position { ls_item-item_no }: { ls_item-product }| ).
LOOP AT lt_conditions INTO DATA(ls_cond) WHERE order_id = ls_item-order_id AND item_no = ls_item-item_no.
out->write( | Kondition { ls_cond-condition_type }: { ls_cond-amount }| ). ENDLOOP. ENDLOOP. ENDLOOP. ENDMETHOD.
ENDCLASS.Problem mit verschachtelten LOOPs
| Problem | Auswirkung |
|---|---|
| Redundante WHERE-Bedingungen | Fehleranfällig bei Änderungen |
| Schlechte Performance | Bei STANDARD TABLE linearer Scan |
| Unlesbarer Code | Bei mehr Ebenen kaum noch wartbar |
| Keine Wiederverwendung | Bedingungen immer wieder schreiben |
Mesh-Ansatz: Deklarative Navigation
Mit Mesh Expressions definierst du die Beziehungen einmal und navigierst dann elegant:
CLASS zcl_order_mesh DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Strukturen TYPES: BEGIN OF ty_s_order_header, order_id TYPE sysuuid_x16, customer TYPE string, order_date TYPE d, END OF ty_s_order_header.
TYPES: BEGIN OF ty_s_order_item, order_id TYPE sysuuid_x16, item_no TYPE i, product TYPE string, quantity TYPE i, net_price TYPE p DECIMALS 2, END OF ty_s_order_item.
TYPES: BEGIN OF ty_s_condition, order_id TYPE sysuuid_x16, item_no TYPE i, condition_type TYPE c LENGTH 4, amount TYPE p DECIMALS 2, END OF ty_s_condition.
" SORTED Tables für optimale Performance TYPES ty_t_headers TYPE SORTED TABLE OF ty_s_order_header WITH UNIQUE KEY order_id. TYPES ty_t_items TYPE SORTED TABLE OF ty_s_order_item WITH UNIQUE KEY order_id item_no. TYPES ty_t_conditions TYPE SORTED TABLE OF ty_s_condition WITH NON-UNIQUE KEY order_id item_no condition_type.
" Mesh-Definition mit allen Assoziationen TYPES: BEGIN OF MESH ty_m_order, headers TYPE ty_t_headers ASSOCIATION items TO items ON order_id = order_id, items TYPE ty_t_items ASSOCIATION header TO headers ON order_id = order_id ASSOCIATION conditions TO conditions ON order_id = order_id AND item_no = item_no, conditions TYPE ty_t_conditions ASSOCIATION item TO items ON order_id = order_id AND item_no = item_no, END OF MESH ty_m_order.
ENDCLASS.
CLASS zcl_order_mesh IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Testdaten DATA(lv_order1) = cl_system_uuid=>create_uuid_x16_static( ). DATA(lv_order2) = cl_system_uuid=>create_uuid_x16_static( ).
DATA(lt_headers) = VALUE ty_t_headers( ( order_id = lv_order1 customer = 'Müller GmbH' order_date = sy-datum ) ( order_id = lv_order2 customer = 'Schmidt AG' order_date = sy-datum ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = lv_order1 item_no = 10 product = 'Laptop' quantity = 2 net_price = '1200.00' ) ( order_id = lv_order1 item_no = 20 product = 'Monitor' quantity = 3 net_price = '350.00' ) ( order_id = lv_order2 item_no = 10 product = 'Tastatur' quantity = 5 net_price = '75.00' ) ).
DATA(lt_conditions) = VALUE ty_t_conditions( ( order_id = lv_order1 item_no = 10 condition_type = 'RA01' amount = '-120.00' ) ( order_id = lv_order1 item_no = 10 condition_type = 'ZU01' amount = '50.00' ) ( order_id = lv_order1 item_no = 20 condition_type = 'RA01' amount = '-52.50' ) ( order_id = lv_order2 item_no = 10 condition_type = 'RA02' amount = '-15.00' ) ).
" Mesh erstellen DATA(ls_order_mesh) = VALUE ty_m_order( headers = lt_headers items = lt_items conditions = lt_conditions ).
" Navigation mit Mesh-Pfaden LOOP AT ls_order_mesh-headers INTO DATA(ls_header). out->write( |Bestellung: { ls_header-customer }| ).
" Forward-Navigation: Header -> Items LOOP AT ls_order_mesh-headers\items[ ls_header ] INTO DATA(ls_item). out->write( | Position { ls_item-item_no }: { ls_item-product }| ).
" Forward-Navigation: Item -> Conditions LOOP AT ls_order_mesh-items\conditions[ ls_item ] INTO DATA(ls_cond). out->write( | Kondition { ls_cond-condition_type }: { ls_cond-amount }| ). ENDLOOP. ENDLOOP. ENDLOOP. ENDMETHOD.
ENDCLASS.Vergleich: Mesh vs. LOOP AT
Syntax-Vergleich
" KLASSISCH: WHERE-Bedingung bei jedem LOOPLOOP AT lt_items INTO DATA(ls_item) WHERE order_id = ls_header-order_id. " ...ENDLOOP.
" MESH: Beziehung einmal definiert, dann navigierenLOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item). " ...ENDLOOP.Feature-Vergleich
| Aspekt | Klassisch (LOOP + WHERE) | Mesh Expressions |
|---|---|---|
| Beziehungsdefinition | Verstreut im Code | Einmal zentral |
| Typsicherheit | Runtime-Fehler möglich | Compile-Time-Prüfung |
| Performance | Abhängig von Tabellentyp | Nutzt Keys automatisch |
| Lesbarkeit | WHERE-Bedingungen überall | Deklarative Pfade |
| Wartbarkeit | Änderungen an vielen Stellen | Änderungen nur im Mesh |
| Backward-Navigation | Explizite Suche nötig | Mit \_assoc eingebaut |
Backward-Navigation: Vom Detail zum Header
Mit Mesh Expressions kannst du auch rückwärts navigieren:
CLASS zcl_mesh_backward DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_header, order_id TYPE i, customer TYPE string, END OF ty_s_header.
TYPES: BEGIN OF ty_s_item, order_id TYPE i, item_no TYPE i, product TYPE string, END OF ty_s_item.
TYPES: BEGIN OF ty_s_condition, order_id TYPE i, item_no TYPE i, condition_type TYPE string, amount TYPE p DECIMALS 2, END OF ty_s_condition.
TYPES ty_t_headers TYPE SORTED TABLE OF ty_s_header WITH UNIQUE KEY order_id. TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item WITH UNIQUE KEY order_id item_no. TYPES ty_t_conditions TYPE SORTED TABLE OF ty_s_condition WITH NON-UNIQUE KEY order_id item_no.
TYPES: BEGIN OF MESH ty_m_order, headers TYPE ty_t_headers ASSOCIATION items TO items ON order_id = order_id, items TYPE ty_t_items ASSOCIATION header TO headers ON order_id = order_id ASSOCIATION conditions TO conditions ON order_id = order_id AND item_no = item_no, conditions TYPE ty_t_conditions ASSOCIATION item TO items ON order_id = order_id AND item_no = item_no, END OF MESH ty_m_order.
ENDCLASS.
CLASS zcl_mesh_backward IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_headers) = VALUE ty_t_headers( ( order_id = 1 customer = 'Kunde A' ) ( order_id = 2 customer = 'Kunde B' ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = 1 item_no = 10 product = 'Laptop' ) ( order_id = 2 item_no = 10 product = 'Server' ) ).
DATA(lt_conditions) = VALUE ty_t_conditions( ( order_id = 1 item_no = 10 condition_type = 'Rabatt 10%' amount = '-100.00' ) ( order_id = 2 item_no = 10 condition_type = 'Mengenrabatt' amount = '-500.00' ) ).
DATA(ls_mesh) = VALUE ty_m_order( headers = lt_headers items = lt_items conditions = lt_conditions ).
out->write( '=== Backward-Navigation: Kondition -> Item -> Header ===' ).
" Ausgehend von Konditionen zurück zum Kunden navigieren LOOP AT ls_mesh-conditions INTO DATA(ls_cond). " Backward: Kondition -> Item DATA(ls_parent_item) = ls_mesh-conditions\_item[ ls_cond ].
" Backward: Item -> Header DATA(ls_parent_header) = ls_mesh-items\_header[ ls_parent_item ].
out->write( |{ ls_cond-condition_type } ({ ls_cond-amount }) | && |-> { ls_parent_item-product } | && |-> { ls_parent_header-customer }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Aggregationen mit REDUCE und Mesh
Mesh Expressions kombiniert mit REDUCE ermöglichen elegante Berechnungen:
CLASS zcl_mesh_aggregation DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_order, order_id TYPE i, customer TYPE string, END OF ty_s_order.
TYPES: BEGIN OF ty_s_item, order_id TYPE i, item_no TYPE i, quantity TYPE i, net_price TYPE p DECIMALS 2, END OF ty_s_item.
TYPES: BEGIN OF ty_s_condition, order_id TYPE i, item_no TYPE i, condition_type TYPE c LENGTH 4, amount TYPE p DECIMALS 2, END OF ty_s_condition.
TYPES ty_t_orders TYPE SORTED TABLE OF ty_s_order WITH UNIQUE KEY order_id. TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item WITH UNIQUE KEY order_id item_no. TYPES ty_t_conditions TYPE SORTED TABLE OF ty_s_condition WITH NON-UNIQUE KEY order_id item_no.
TYPES: BEGIN OF MESH ty_m_sales, orders TYPE ty_t_orders ASSOCIATION items TO items ON order_id = order_id, items TYPE ty_t_items ASSOCIATION order TO orders ON order_id = order_id ASSOCIATION conditions TO conditions ON order_id = order_id AND item_no = item_no, conditions TYPE ty_t_conditions ASSOCIATION item TO items ON order_id = order_id AND item_no = item_no, END OF MESH ty_m_sales.
ENDCLASS.
CLASS zcl_mesh_aggregation IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. DATA(lt_orders) = VALUE ty_t_orders( ( order_id = 1 customer = 'Kunde A' ) ( order_id = 2 customer = 'Kunde B' ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = 1 item_no = 10 quantity = 2 net_price = '1000.00' ) ( order_id = 1 item_no = 20 quantity = 1 net_price = '500.00' ) ( order_id = 2 item_no = 10 quantity = 5 net_price = '200.00' ) ).
DATA(lt_conditions) = VALUE ty_t_conditions( ( order_id = 1 item_no = 10 condition_type = 'RA01' amount = '-200.00' ) ( order_id = 1 item_no = 20 condition_type = 'RA01' amount = '-50.00' ) ( order_id = 2 item_no = 10 condition_type = 'RA02' amount = '-100.00' ) ).
DATA(ls_mesh) = VALUE ty_m_sales( orders = lt_orders items = lt_items conditions = lt_conditions ).
out->write( '=== Bestellwert pro Kunde (inkl. Konditionen) ===' ).
LOOP AT ls_mesh-orders INTO DATA(ls_order). " Summe aller Positionen DATA(lv_net_total) = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR <item> IN ls_mesh-orders\items[ ls_order ] NEXT sum = sum + ( <item>-quantity * <item>-net_price ) ).
" Summe aller Konditionen DATA(lv_conditions_total) = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR <item> IN ls_mesh-orders\items[ ls_order ] FOR <cond> IN ls_mesh-items\conditions[ <item> ] NEXT sum = sum + <cond>-amount ).
DATA(lv_grand_total) = lv_net_total + lv_conditions_total.
out->write( |{ ls_order-customer }:| ). out->write( | Netto: { lv_net_total } EUR| ). out->write( | Konditionen: { lv_conditions_total } EUR| ). out->write( | Gesamt: { lv_grand_total } EUR| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Performance-Tipps
Tabellentypen wählen
" GUT: SORTED TABLE mit passendem KeyTYPES ty_t_items TYPE SORTED TABLE OF ty_s_item WITH UNIQUE KEY order_id item_no.
" SCHLECHT: STANDARD TABLE - schlechte Performance" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item" WITH EMPTY KEY.Speicher sparen mit REF
Bei großen Tabellen kannst du Referenzen statt Kopien verwenden:
" Mesh mit Referenzen (spart Speicher)DATA(ls_mesh) = VALUE ty_m_order( headers = lt_headers " Kopie items = REF #( lt_items ) " Referenz conditions = REF #( lt_conditions )).ABAP Cloud Kompatibilität
| Aspekt | Status |
|---|---|
| BTP ABAP Environment | ✅ Vollständig unterstützt |
| S/4HANA Cloud | ✅ Vollständig unterstützt |
| S/4HANA On-Premise | ✅ Ab 7.40 SP05 |
| Released API Status | ✅ Kernsprachfeature |
Mesh Expressions sind ein Kernsprachfeature von ABAP und nicht von Released API Restrictions betroffen. Sie können in allen ABAP Cloud-Umgebungen ohne Einschränkungen verwendet werden.
Best Practices für ABAP Cloud
" DO: SORTED/HASHED Tables mit sinnvollen KeysTYPES ty_t_items TYPE SORTED TABLE OF ty_s_item WITH UNIQUE KEY order_id item_no.
" DO: Mesh für komplexe Navigationen verwendenLOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item). " Klarer als WHERE-BedingungenENDLOOP.
" DON'T: STANDARD TABLE ohne Key" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item" WITH EMPTY KEY.
" DON'T: Mesh für einfache 1:1 Lookups" Für einzelne Lookups ist READ TABLE effizienterWann Mesh verwenden?
| Anwendungsfall | Empfehlung |
|---|---|
| Header-Item-Strukturen | ✅ Ideal für Mesh |
| Master-Detail-Beziehungen | ✅ Ideal für Mesh |
| Hierarchische Daten (BOM) | ✅ Ideal für Mesh |
| Einfacher 1:1 Lookup | ❌ READ TABLE ist ausreichend |
| DB-Tabellen direkt | ❌ Mesh nur für interne Tabellen |
| Dynamische Beziehungen | ❌ Assoziationen müssen statisch sein |
Zusammenfassung
Mesh Expressions bieten eine elegante Alternative zu verschachtelten LOOPs:
- Zentrale Definition: Beziehungen einmal im MESH-Typ definieren
- Deklarative Navigation: Pfad-Syntax statt WHERE-Bedingungen
- Typsicherheit: Compile-Time-Prüfung der Assoziationen
- Bidirektionale Navigation: Forward (
\) und Backward (\_) - Performance: Nutzt Tabellenkeys automatisch
- ABAP Cloud-ready: Vollständig unterstützt in allen Cloud-Umgebungen
Besonders bei Bestellstrukturen mit Header, Positionen und Konditionen reduzieren Mesh Expressions die Komplexität erheblich und machen den Code wartbarer.