Mesh Expressions sind ein mächtiges Feature in ABAP, das seit Release 7.40 SP05 verfügbar ist. Sie ermöglichen die Navigation zwischen verknüpften internen Tabellen über definierte Assoziationen - ähnlich wie Fremdschlüssel-Beziehungen in relationalen Datenbanken.
Was ist ein Mesh?
Ein Mesh ist ein typisierter Verbund von internen Tabellen, die über Assoziationen miteinander verknüpft sind. Statt verschachtelte LOOPs zu schreiben, navigierst du deklarativ von einer Tabelle zur anderen.
| Konzept | Beschreibung |
|---|---|
TYPES MESH | Definiert den Mesh-Typ mit Tabellen und Assoziationen |
| Association | Verknüpfung zwischen zwei Tabellen im Mesh |
| Mesh Path | Navigationspfad über Assoziationen |
\association | Syntax für Forward-Navigation |
\_association | Syntax für Backward-Navigation |
Anwendungsfälle für Mesh Expressions
- Hierarchische Daten: Header-Item-Beziehungen (Auftrag → Positionen)
- Master-Detail: Kunde → Aufträge → Positionen
- Organisationsstrukturen: Abteilung → Teams → Mitarbeiter
- Bill of Materials: Produkt → Komponenten → Unterkomponenten
- Dokumentenfluss: Beleg → Folgebelege
TYPES MESH Definition
Die Grundstruktur eines Mesh beginnt mit der Typdefinition:
CLASS zcl_mesh_basic DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Strukturen für Header und Items TYPES: BEGIN OF ty_s_order_header, order_id TYPE sysuuid_x16, customer_id TYPE i, order_date TYPE d, status TYPE c LENGTH 1, END OF ty_s_order_header.
TYPES: BEGIN OF ty_s_order_item, order_id TYPE sysuuid_x16, item_no TYPE i, product_id TYPE c LENGTH 10, quantity TYPE i, price TYPE p DECIMALS 2, END OF ty_s_order_item.
" Tabellentypen 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.
" Mesh-Definition mit Assoziationen TYPES: BEGIN OF MESH ty_m_orders, 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, END OF MESH ty_m_orders.
ENDCLASS.
CLASS zcl_mesh_basic IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Testdaten erstellen 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_id = 100 order_date = sy-datum status = 'N' ) ( order_id = lv_order2 customer_id = 200 order_date = sy-datum status = 'C' ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = lv_order1 item_no = 10 product_id = 'PROD001' quantity = 2 price = '50.00' ) ( order_id = lv_order1 item_no = 20 product_id = 'PROD002' quantity = 1 price = '100.00' ) ( order_id = lv_order2 item_no = 10 product_id = 'PROD003' quantity = 5 price = '25.00' ) ).
" Mesh-Instanz erstellen DATA(ls_orders) = VALUE ty_m_orders( headers = lt_headers items = lt_items ).
out->write( |Mesh erstellt mit { lines( ls_orders-headers ) } Headers| ). out->write( |und { lines( ls_orders-items ) } Items| ). ENDMETHOD.
ENDCLASS.Erklärung der Mesh-Definition
TYPES: BEGIN OF MESH ty_m_orders, 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, END OF MESH ty_m_orders.- MESH: Schlüsselwort für die Mesh-Typdefinition
- ASSOCIATION name TO node: Definiert eine Assoziation zu einer anderen Node im Mesh
- ON field1 = field2: Join-Bedingung (wie SQL-Joins)
Navigation mit Mesh-Pfaden
Die wahre Stärke von Mesh Expressions liegt in der deklarativen Navigation:
CLASS zcl_mesh_navigation DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Strukturen TYPES: BEGIN OF ty_s_customer, customer_id TYPE i, name TYPE string, city TYPE string, END OF ty_s_customer.
TYPES: BEGIN OF ty_s_order, order_id TYPE i, customer_id TYPE i, order_date TYPE d, total TYPE p DECIMALS 2, END OF ty_s_order.
TYPES: BEGIN OF ty_s_item, order_id TYPE i, item_no TYPE i, product TYPE string, quantity TYPE i, price TYPE p DECIMALS 2, END OF ty_s_item.
" Tabellentypen TYPES ty_t_customers TYPE SORTED TABLE OF ty_s_customer WITH UNIQUE KEY customer_id. 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.
" Mesh mit drei Ebenen TYPES: BEGIN OF MESH ty_m_sales, customers TYPE ty_t_customers ASSOCIATION orders TO orders ON customer_id = customer_id, orders TYPE ty_t_orders ASSOCIATION customer TO customers ON customer_id = customer_id ASSOCIATION items TO items ON order_id = order_id, items TYPE ty_t_items ASSOCIATION order TO orders ON order_id = order_id, END OF MESH ty_m_sales.
ENDCLASS.
CLASS zcl_mesh_navigation IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Testdaten aufbauen DATA(lt_customers) = VALUE ty_t_customers( ( customer_id = 1 name = 'Müller GmbH' city = 'Berlin' ) ( customer_id = 2 name = 'Schmidt AG' city = 'München' ) ).
DATA(lt_orders) = VALUE ty_t_orders( ( order_id = 100 customer_id = 1 order_date = '20260101' total = '250.00' ) ( order_id = 101 customer_id = 1 order_date = '20260115' total = '175.00' ) ( order_id = 102 customer_id = 2 order_date = '20260110' total = '500.00' ) ).
DATA(lt_items) = VALUE ty_t_items( ( order_id = 100 item_no = 1 product = 'Laptop' quantity = 1 price = '200.00' ) ( order_id = 100 item_no = 2 product = 'Maus' quantity = 2 price = '25.00' ) ( order_id = 101 item_no = 1 product = 'Tastatur' quantity = 1 price = '175.00' ) ( order_id = 102 item_no = 1 product = 'Monitor' quantity = 2 price = '250.00' ) ).
" Mesh erstellen DATA(ls_sales) = VALUE ty_m_sales( customers = lt_customers orders = lt_orders items = lt_items ).
" === Navigation: Customer -> Orders (1:n) === out->write( '=== Forward Navigation: Customer -> Orders ===' ).
LOOP AT ls_sales-customers INTO DATA(ls_customer). out->write( |Kunde: { ls_customer-name }| ).
" Mesh-Pfad: Alle Aufträge des Kunden LOOP AT ls_sales-customers\orders[ ls_customer ] INTO DATA(ls_order). out->write( | Order { ls_order-order_id }: { ls_order-total } EUR| ).
" Verschachtelter Mesh-Pfad: Items des Auftrags LOOP AT ls_sales-orders\items[ ls_order ] INTO DATA(ls_item). out->write( | - { ls_item-product } x{ ls_item-quantity }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" === Backward Navigation: Item -> Order -> Customer === out->write( '' ). out->write( '=== Backward Navigation: Item -> Order -> Customer ===' ).
LOOP AT ls_sales-items INTO DATA(ls_item2). " Vom Item zurück zum Auftrag DATA(ls_parent_order) = ls_sales-items\_order[ ls_item2 ]. " Vom Auftrag zurück zum Kunden DATA(ls_parent_customer) = ls_sales-orders\_customer[ ls_parent_order ].
out->write( |{ ls_item2-product }: Order { ls_parent_order-order_id } von { ls_parent_customer-name }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Mesh-Pfad Syntax
| Syntax | Beschreibung |
|---|---|
mesh-node\assoc[ source ] | Forward-Navigation: Alle Ziele der Assoziation |
mesh-node\_assoc[ source ] | Backward-Navigation: Zurück zur Quelle |
mesh-node\assoc1\assoc2[ source ] | Verkettete Navigation über mehrere Assoziationen |
Vorteile gegenüber verschachtelten LOOPs
Der größte Vorteil von Mesh Expressions zeigt sich beim Vergleich mit klassischem Code:
CLASS zcl_mesh_comparison DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_department, dept_id TYPE i, dept_name TYPE string, END OF ty_s_department.
TYPES: BEGIN OF ty_s_employee, emp_id TYPE i, dept_id TYPE i, emp_name TYPE string, salary TYPE p DECIMALS 2, END OF ty_s_employee.
TYPES: BEGIN OF ty_s_project, proj_id TYPE i, emp_id TYPE i, proj_name TYPE string, budget TYPE p DECIMALS 2, END OF ty_s_project.
TYPES ty_t_departments TYPE SORTED TABLE OF ty_s_department WITH UNIQUE KEY dept_id. TYPES ty_t_employees TYPE SORTED TABLE OF ty_s_employee WITH UNIQUE KEY emp_id. TYPES ty_t_projects TYPE SORTED TABLE OF ty_s_project WITH UNIQUE KEY proj_id.
TYPES: BEGIN OF MESH ty_m_organization, departments TYPE ty_t_departments ASSOCIATION employees TO employees ON dept_id = dept_id, employees TYPE ty_t_employees ASSOCIATION department TO departments ON dept_id = dept_id ASSOCIATION projects TO projects ON emp_id = emp_id, projects TYPE ty_t_projects ASSOCIATION employee TO employees ON emp_id = emp_id, END OF MESH ty_m_organization.
ENDCLASS.
CLASS zcl_mesh_comparison IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Testdaten DATA(lt_departments) = VALUE ty_t_departments( ( dept_id = 1 dept_name = 'Entwicklung' ) ( dept_id = 2 dept_name = 'Vertrieb' ) ).
DATA(lt_employees) = VALUE ty_t_employees( ( emp_id = 101 dept_id = 1 emp_name = 'Anna' salary = '5000.00' ) ( emp_id = 102 dept_id = 1 emp_name = 'Ben' salary = '4500.00' ) ( emp_id = 103 dept_id = 2 emp_name = 'Clara' salary = '5500.00' ) ).
DATA(lt_projects) = VALUE ty_t_projects( ( proj_id = 1001 emp_id = 101 proj_name = 'App-Redesign' budget = '50000.00' ) ( proj_id = 1002 emp_id = 101 proj_name = 'API-Migration' budget = '30000.00' ) ( proj_id = 1003 emp_id = 102 proj_name = 'Testing' budget = '15000.00' ) ( proj_id = 1004 emp_id = 103 proj_name = 'CRM-Einführung' budget = '80000.00' ) ).
" Mesh erstellen DATA(ls_org) = VALUE ty_m_organization( departments = lt_departments employees = lt_employees projects = lt_projects ).
" ============================================ " KLASSISCHER ANSATZ: Verschachtelte LOOPs " ============================================ out->write( '=== Klassischer Ansatz (verschachtelte LOOPs) ===' ).
LOOP AT lt_departments INTO DATA(ls_dept). out->write( |Abteilung: { ls_dept-dept_name }| ).
LOOP AT lt_employees INTO DATA(ls_emp) WHERE dept_id = ls_dept-dept_id. out->write( | Mitarbeiter: { ls_emp-emp_name }| ).
LOOP AT lt_projects INTO DATA(ls_proj) WHERE emp_id = ls_emp-emp_id. out->write( | Projekt: { ls_proj-proj_name }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" ============================================ " MESH-ANSATZ: Deklarative Navigation " ============================================ out->write( '' ). out->write( '=== Mesh-Ansatz (deklarative Navigation) ===' ).
LOOP AT ls_org-departments INTO DATA(ls_dept2). out->write( |Abteilung: { ls_dept2-dept_name }| ).
" Mesh-Navigation: Abteilung -> Mitarbeiter LOOP AT ls_org-departments\employees[ ls_dept2 ] INTO DATA(ls_emp2). out->write( | Mitarbeiter: { ls_emp2-emp_name }| ).
" Mesh-Navigation: Mitarbeiter -> Projekte LOOP AT ls_org-employees\projects[ ls_emp2 ] INTO DATA(ls_proj2). out->write( | Projekt: { ls_proj2-proj_name }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" ============================================ " VORTEIL: Aggregationen über Mesh-Pfade " ============================================ out->write( '' ). out->write( '=== Aggregation: Budget pro Abteilung ===' ).
LOOP AT ls_org-departments INTO DATA(ls_dept3). " Alle Projekte einer Abteilung über Mitarbeiter DATA(lv_total_budget) = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR <emp> IN ls_org-departments\employees[ ls_dept3 ] FOR <proj> IN ls_org-employees\projects[ <emp> ] NEXT sum = sum + <proj>-budget ).
out->write( |{ ls_dept3-dept_name }: { lv_total_budget } EUR Projektbudget| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Vorteile im Überblick
| Aspekt | Klassisch (WHERE) | Mesh |
|---|---|---|
| Lesbarkeit | Verschachtelte LOOPs | Deklarative Pfade |
| Wartbarkeit | Bedingungen verstreut | Beziehungen zentral definiert |
| Typsicherheit | Runtime-Fehler möglich | Compiletime-Prüfung |
| Performance | Linearer Scan | Nutzt Tabellenkeys |
| Wiederverwendung | Copy-Paste | Einmal definieren |
Praktisches Beispiel: Bill of Materials
Ein komplexeres Beispiel zeigt die Stärke von Mesh bei hierarchischen Strukturen:
CLASS zcl_mesh_bom DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_material, material_id TYPE c LENGTH 10, description TYPE string, unit TYPE c LENGTH 3, END OF ty_s_material.
TYPES: BEGIN OF ty_s_bom_item, parent_id TYPE c LENGTH 10, child_id TYPE c LENGTH 10, quantity TYPE p DECIMALS 3, position TYPE i, END OF ty_s_bom_item.
TYPES: BEGIN OF ty_s_stock, material_id TYPE c LENGTH 10, plant TYPE c LENGTH 4, quantity TYPE p DECIMALS 3, END OF ty_s_stock.
TYPES ty_t_materials TYPE SORTED TABLE OF ty_s_material WITH UNIQUE KEY material_id. TYPES ty_t_bom TYPE SORTED TABLE OF ty_s_bom_item WITH NON-UNIQUE KEY parent_id position. TYPES ty_t_stock TYPE SORTED TABLE OF ty_s_stock WITH UNIQUE KEY material_id plant.
" Mesh für Stücklistenauflösung TYPES: BEGIN OF MESH ty_m_bom, materials TYPE ty_t_materials ASSOCIATION components TO bom ON material_id = parent_id ASSOCIATION stock TO stock ON material_id = material_id, bom TYPE ty_t_bom ASSOCIATION parent TO materials ON parent_id = material_id ASSOCIATION child TO materials ON child_id = material_id, stock TYPE ty_t_stock ASSOCIATION material TO materials ON material_id = material_id, END OF MESH ty_m_bom.
ENDCLASS.
CLASS zcl_mesh_bom IMPLEMENTATION.
METHOD if_oo_adt_classrun~main. " Materialstamm DATA(lt_materials) = VALUE ty_t_materials( ( material_id = 'BIKE_001' description = 'Mountainbike Komplett' unit = 'ST' ) ( material_id = 'FRAME_01' description = 'Rahmen Aluminium' unit = 'ST' ) ( material_id = 'WHEEL_01' description = 'Laufrad 26 Zoll' unit = 'ST' ) ( material_id = 'TIRE_001' description = 'Reifen MTB' unit = 'ST' ) ( material_id = 'SPOKE_01' description = 'Speiche Edelstahl' unit = 'ST' ) ( material_id = 'BRAKE_01' description = 'Bremsanlage Disc' unit = 'ST' ) ).
" Stückliste (Bill of Materials) DATA(lt_bom) = VALUE ty_t_bom( " Bike besteht aus: ( parent_id = 'BIKE_001' child_id = 'FRAME_01' quantity = 1 position = 10 ) ( parent_id = 'BIKE_001' child_id = 'WHEEL_01' quantity = 2 position = 20 ) ( parent_id = 'BIKE_001' child_id = 'BRAKE_01' quantity = 2 position = 30 ) " Laufrad besteht aus: ( parent_id = 'WHEEL_01' child_id = 'TIRE_001' quantity = 1 position = 10 ) ( parent_id = 'WHEEL_01' child_id = 'SPOKE_01' quantity = 36 position = 20 ) ).
" Lagerbestand DATA(lt_stock) = VALUE ty_t_stock( ( material_id = 'FRAME_01' plant = '1000' quantity = 50 ) ( material_id = 'WHEEL_01' plant = '1000' quantity = 100 ) ( material_id = 'TIRE_001' plant = '1000' quantity = 200 ) ( material_id = 'SPOKE_01' plant = '1000' quantity = 5000 ) ( material_id = 'BRAKE_01' plant = '1000' quantity = 150 ) ).
" Mesh erstellen DATA(ls_bom) = VALUE ty_m_bom( materials = lt_materials bom = lt_bom stock = lt_stock ).
" Stücklistenauflösung für Mountainbike out->write( '=== Stücklistenauflösung: Mountainbike ===' ).
READ TABLE ls_bom-materials INTO DATA(ls_bike) WITH KEY material_id = 'BIKE_001'.
explode_bom( is_mesh = ls_bom is_material = ls_bike iv_level = 0 iv_quantity = CONV #( 1 ) io_out = out ).
" Bedarfsermittlung für 10 Bikes out->write( '' ). out->write( '=== Bedarfsermittlung für 10 Mountainbikes ===' ). calculate_requirements( is_mesh = ls_bom iv_material = 'BIKE_001' iv_quantity = 10 io_out = out ). ENDMETHOD.
METHOD explode_bom. DATA(lv_indent) = repeat( val = ` ` occ = iv_level ).
io_out->write( |{ lv_indent }{ is_material-description } ({ is_material-material_id }) x{ iv_quantity }| ).
" Komponenten über Mesh-Navigation LOOP AT is_mesh-materials\components[ is_material ] INTO DATA(ls_bom_item). " Kind-Material über Mesh-Pfad DATA(ls_child) = is_mesh-bom\child[ ls_bom_item ].
" Rekursiver Aufruf für Unterbaugruppen explode_bom( is_mesh = is_mesh is_material = ls_child iv_level = iv_level + 1 iv_quantity = iv_quantity * ls_bom_item-quantity io_out = io_out ). ENDLOOP. ENDMETHOD.
METHOD calculate_requirements. TYPES: BEGIN OF ty_s_requirement, material_id TYPE c LENGTH 10, quantity TYPE p DECIMALS 3, END OF ty_s_requirement.
DATA lt_requirements TYPE SORTED TABLE OF ty_s_requirement WITH UNIQUE KEY material_id.
" Ausgangsmaterial READ TABLE is_mesh-materials INTO DATA(ls_start) WITH KEY material_id = iv_material.
" Rekursive Bedarfsermittlung collect_requirements( is_mesh = is_mesh is_material = ls_start iv_quantity = iv_quantity ct_requirements = lt_requirements ).
" Ergebnis ausgeben und mit Bestand vergleichen LOOP AT lt_requirements INTO DATA(ls_req). READ TABLE is_mesh-materials INTO DATA(ls_mat) WITH KEY material_id = ls_req-material_id.
" Stock über Mesh-Navigation DATA(lv_stock) = VALUE p DECIMALS 3( ). LOOP AT is_mesh-materials\stock[ ls_mat ] INTO DATA(ls_stock). lv_stock = lv_stock + ls_stock-quantity. ENDLOOP.
DATA(lv_status) = COND string( WHEN lv_stock >= ls_req-quantity THEN '✓ OK' ELSE '✗ FEHLMENGE' ).
io_out->write( |{ ls_mat-description }: Bedarf { ls_req-quantity } / Bestand { lv_stock } { lv_status }| ). ENDLOOP. ENDMETHOD.
METHOD collect_requirements. " Alle Komponenten sammeln LOOP AT is_mesh-materials\components[ is_material ] INTO DATA(ls_bom). DATA(ls_child) = is_mesh-bom\child[ ls_bom ]. DATA(lv_child_qty) = iv_quantity * ls_bom-quantity.
" Bedarf sammeln COLLECT VALUE ty_s_requirement( material_id = ls_child-material_id quantity = lv_child_qty ) INTO ct_requirements.
" Rekursiv für Unterbaugruppen collect_requirements( is_mesh = is_mesh is_material = ls_child iv_quantity = lv_child_qty ct_requirements = ct_requirements ). ENDLOOP. ENDMETHOD.
ENDCLASS.Einschränkungen und Hinweise
Mesh Expressions haben einige Einschränkungen, die du beachten solltest:
| Einschränkung | Beschreibung |
|---|---|
| Nur SORTED/HASHED Tables | Standard Tables ohne Key funktionieren nicht optimal |
| Keine dynamischen Assoziationen | Beziehungen müssen zur Compile-Zeit bekannt sein |
| Speicherverbrauch | Mesh kopiert Tabellen (VALUE) oder referenziert sie (REF) |
| Keine DB-Tabellen | Mesh arbeitet nur mit internen Tabellen |
Performance-Tipps
" 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 ohne Key" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item" WITH EMPTY KEY.
" GUT: Mesh-Komponenten als Referenz (spart Speicher)DATA(ls_mesh) = VALUE ty_mesh( headers = lt_headers " Kopie items = REF #( lt_items ) " Referenz (bei großen Tabellen)).Fazit
Mesh Expressions bieten eine elegante Alternative zu verschachtelten LOOPs:
- Deklarative Beziehungen: Einmal definieren, überall nutzen
- Lesbarerer Code: Mesh-Pfade statt WHERE-Bedingungen
- Typsicherheit: Compile-Time-Prüfung der Assoziationen
- Performance: Nutzt Tabellenkeys automatisch
- Wartbarkeit: Änderungen nur an einer Stelle
Besonders bei hierarchischen Datenstrukturen wie Bill of Materials, Organisationsstrukturen oder Header-Item-Beziehungen lohnt sich der Einsatz von Mesh Expressions.