ABAP Mesh Expressions: Navigating Linked Tables Without Nested LOOPs

Category
ABAP
Published
Author
Johannes

Mesh Expressions enable navigation between linked internal tables through defined associations - similar to SQL joins, but for internal tables. Instead of writing nested LOOPs with WHERE conditions, you define the relationships centrally once and then navigate declaratively.

What are Mesh Expressions?

A mesh is a typed compound of internal tables with defined associations. You define the relationships between tables (1:n, n:1) and can then navigate using path expressions.

ConceptDescription
TYPES MESHDefines the mesh type with tables and associations
ASSOCIATION name TO nodeForward association (e.g., Header → Items)
ON field = fieldJoin condition of the association
mesh-node\assoc[ line ]Forward navigation: All related rows
mesh-node\_assoc[ line ]Backward navigation: Back to parent

Availability

Mesh Expressions are available since ABAP 7.40 SP05 and fully ABAP Cloud compatible. They can be used without restrictions in BTP ABAP Environment and S/4HANA Cloud.

Practical Example: Order with Items and Conditions

A typical scenario is order processing with three levels:

  • Order Header (Order number, customer, date)
  • Order Items (Product, quantity, price)
  • Conditions (Discounts, surcharges per item)

Classic Approach: Nested LOOPs

Without Mesh Expressions, the code typically looks like this:

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.
" Test data
DATA(lv_order1) = cl_system_uuid=>create_uuid_x16_static( ).
DATA(lt_headers) = VALUE ty_t_headers(
( order_id = lv_order1 customer = 'Mueller 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' )
).
" Nested LOOPs - hard to read
LOOP AT lt_headers INTO DATA(ls_header).
out->write( |Order: { ls_header-customer }| ).
LOOP AT lt_items INTO DATA(ls_item)
WHERE order_id = ls_header-order_id.
out->write( | Item { 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( | Condition { ls_cond-condition_type }: { ls_cond-amount }| ).
ENDLOOP.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Problems with Nested LOOPs

ProblemImpact
Redundant WHERE conditionsError-prone when making changes
Poor performanceLinear scan with STANDARD TABLE
Unreadable codeBarely maintainable with more levels
No reusabilityConditions must be rewritten every time

Mesh Approach: Declarative Navigation

With Mesh Expressions, you define the relationships once and navigate elegantly:

CLASS zcl_order_mesh DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
" Structures
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 for optimal 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 with all associations
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.
" Test data
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 = 'Mueller 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 = 'Keyboard' 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' )
).
" Create mesh
DATA(ls_order_mesh) = VALUE ty_m_order(
headers = lt_headers
items = lt_items
conditions = lt_conditions
).
" Navigation with mesh paths
LOOP AT ls_order_mesh-headers INTO DATA(ls_header).
out->write( |Order: { ls_header-customer }| ).
" Forward navigation: Header -> Items
LOOP AT ls_order_mesh-headers\items[ ls_header ] INTO DATA(ls_item).
out->write( | Item { 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( | Condition { ls_cond-condition_type }: { ls_cond-amount }| ).
ENDLOOP.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Comparison: Mesh vs. LOOP AT

Syntax Comparison

" CLASSIC: WHERE condition in every LOOP
LOOP AT lt_items INTO DATA(ls_item)
WHERE order_id = ls_header-order_id.
" ...
ENDLOOP.
" MESH: Relationship defined once, then navigate
LOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item).
" ...
ENDLOOP.

Feature Comparison

AspectClassic (LOOP + WHERE)Mesh Expressions
Relationship definitionScattered in codeOnce centrally
Type safetyRuntime errors possibleCompile-time checking
PerformanceDepends on table typeUses keys automatically
ReadabilityWHERE conditions everywhereDeclarative paths
MaintainabilityChanges in many placesChanges only in mesh
Backward navigationExplicit search requiredBuilt-in with \_assoc

Backward Navigation: From Detail to Header

With Mesh Expressions, you can also navigate backwards:

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 = 'Customer A' )
( order_id = 2 customer = 'Customer 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 = 'Discount 10%' amount = '-100.00' )
( order_id = 2 item_no = 10 condition_type = 'Volume discount' amount = '-500.00' )
).
DATA(ls_mesh) = VALUE ty_m_order(
headers = lt_headers
items = lt_items
conditions = lt_conditions
).
out->write( '=== Backward Navigation: Condition -> Item -> Header ===' ).
" Starting from conditions, navigate back to customer
LOOP AT ls_mesh-conditions INTO DATA(ls_cond).
" Backward: Condition -> 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.

Aggregations with REDUCE and Mesh

Mesh Expressions combined with REDUCE enable elegant calculations:

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 = 'Customer A' )
( order_id = 2 customer = 'Customer 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( '=== Order value per customer (incl. conditions) ===' ).
LOOP AT ls_mesh-orders INTO DATA(ls_order).
" Sum of all items
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 )
).
" Sum of all conditions
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( | Net: { lv_net_total } EUR| ).
out->write( | Conditions: { lv_conditions_total } EUR| ).
out->write( | Total: { lv_grand_total } EUR| ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Performance Tips

Choose Table Types

" GOOD: SORTED TABLE with matching key
TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item
WITH UNIQUE KEY order_id item_no.
" BAD: STANDARD TABLE - poor performance
" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item
" WITH EMPTY KEY.

Save Memory with REF

For large tables, you can use references instead of copies:

" Mesh with references (saves memory)
DATA(ls_mesh) = VALUE ty_m_order(
headers = lt_headers " Copy
items = REF #( lt_items ) " Reference
conditions = REF #( lt_conditions )
).

ABAP Cloud Compatibility

AspectStatus
BTP ABAP Environment✅ Fully supported
S/4HANA Cloud✅ Fully supported
S/4HANA On-Premise✅ From 7.40 SP05
Released API Status✅ Core language feature

Mesh Expressions are a core language feature of ABAP and not affected by Released API restrictions. They can be used without limitations in all ABAP Cloud environments.

Best Practices for ABAP Cloud

" DO: SORTED/HASHED tables with meaningful keys
TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item
WITH UNIQUE KEY order_id item_no.
" DO: Use mesh for complex navigations
LOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item).
" Clearer than WHERE conditions
ENDLOOP.
" DON'T: STANDARD TABLE without key
" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item
" WITH EMPTY KEY.
" DON'T: Mesh for simple 1:1 lookups
" For single lookups, READ TABLE is more efficient

When to Use Mesh?

Use CaseRecommendation
Header-Item structures✅ Ideal for Mesh
Master-Detail relationships✅ Ideal for Mesh
Hierarchical data (BOM)✅ Ideal for Mesh
Simple 1:1 lookup❌ READ TABLE is sufficient
Database tables directly❌ Mesh only for internal tables
Dynamic relationships❌ Associations must be static

Summary

Mesh Expressions offer an elegant alternative to nested LOOPs:

  • Central Definition: Define relationships once in the MESH type
  • Declarative Navigation: Path syntax instead of WHERE conditions
  • Type Safety: Compile-time checking of associations
  • Bidirectional Navigation: Forward (\) and Backward (\_)
  • Performance: Uses table keys automatically
  • ABAP Cloud-ready: Fully supported in all cloud environments

Especially for order structures with headers, items, and conditions, Mesh Expressions significantly reduce complexity and make code more maintainable.