ABAP Mesh Expressions : Naviguer dans les tables liées sans boucles imbriquées

Catégorie
ABAP
Publié
Auteur
Johannes

Les Mesh Expressions permettent la navigation entre des tables internes liées via des associations définies - similaire aux jointures en SQL, mais pour les tables internes. Au lieu d’écrire des boucles imbriquées avec des conditions WHERE, vous définissez les relations une fois de manière centralisée et naviguez ensuite de façon déclarative.

Que sont les Mesh Expressions ?

Un Mesh est un ensemble typé de tables internes avec des associations définies. Vous définissez les relations entre les tables (1:n, n:1) et pouvez ensuite naviguer via des expressions de chemin.

ConceptDescription
TYPES MESHDéfinit le type Mesh avec tables et associations
ASSOCIATION name TO nodeAssociation vers l’avant (ex. Header → Items)
ON field = fieldCondition de jointure de l’association
mesh-node\assoc[ line ]Navigation vers l’avant : Toutes les lignes associées
mesh-node\_assoc[ line ]Navigation vers l’arrière : Retour au parent

Disponibilité

Les Mesh Expressions sont disponibles depuis ABAP 7.40 SP05 et sont entièrement compatibles ABAP Cloud. Dans BTP ABAP Environment et S/4HANA Cloud, elles peuvent être utilisées sans restrictions.

Exemple pratique : Commande avec positions et conditions

Un scénario typique est le traitement des commandes à trois niveaux :

  • En-tête de commande (numéro de commande, client, date)
  • Positions de commande (article, quantité, prix)
  • Conditions (remises, suppléments par position)

Approche classique : Boucles imbriquées

Sans Mesh Expressions, le code ressemble typiquement à ceci :

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.
" Données de test
DATA(lv_order1) = cl_system_uuid=>create_uuid_x16_static( ).
DATA(lt_headers) = VALUE ty_t_headers(
( order_id = lv_order1 customer = 'Dupont SARL' 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 = 'Moniteur' 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' )
).
" Boucles imbriquées - difficile à lire
LOOP AT lt_headers INTO DATA(ls_header).
out->write( |Commande: { 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( | Condition { ls_cond-condition_type }: { ls_cond-amount }| ).
ENDLOOP.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Problèmes avec les boucles imbriquées

ProblèmeImpact
Conditions WHERE redondantesSusceptible d’erreurs lors de modifications
Mauvaise performanceAvec STANDARD TABLE, scan linéaire
Code illisibleDifficilement maintenable avec plus de niveaux
Pas de réutilisationConditions à réécrire à chaque fois

Approche Mesh : Navigation déclarative

Avec les Mesh Expressions, vous définissez les relations une fois et naviguez élégamment :

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.
" Tables SORTED pour une performance optimale
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.
" Définition Mesh avec toutes les 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.
" Données de test
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 = 'Dupont SARL' order_date = sy-datum )
( order_id = lv_order2 customer = 'Martin SA' 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 = 'Moniteur' quantity = 3 net_price = '350.00' )
( order_id = lv_order2 item_no = 10 product = 'Clavier' 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' )
).
" Créer le Mesh
DATA(ls_order_mesh) = VALUE ty_m_order(
headers = lt_headers
items = lt_items
conditions = lt_conditions
).
" Navigation avec chemins Mesh
LOOP AT ls_order_mesh-headers INTO DATA(ls_header).
out->write( |Commande: { ls_header-customer }| ).
" Navigation vers l'avant : 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 }| ).
" Navigation vers l'avant : 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.

Comparaison : Mesh vs. LOOP AT

Comparaison de syntaxe

" CLASSIQUE : Condition WHERE à chaque LOOP
LOOP AT lt_items INTO DATA(ls_item)
WHERE order_id = ls_header-order_id.
" ...
ENDLOOP.
" MESH : Relation définie une fois, puis naviguer
LOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item).
" ...
ENDLOOP.

Comparaison des fonctionnalités

AspectClassique (LOOP + WHERE)Mesh Expressions
Définition des relationsDispersée dans le codeUne fois de manière centralisée
Sécurité des typesErreurs runtime possiblesVérification à la compilation
PerformanceDépend du type de tableUtilise automatiquement les clés
LisibilitéConditions WHERE partoutChemins déclaratifs
MaintenabilitéModifications à plusieurs endroitsModifications uniquement dans le Mesh
Navigation arrièreRecherche explicite nécessaireIntégrée avec \_assoc

Avec les Mesh Expressions, vous pouvez aussi naviguer vers l’arrière :

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 = 'Client A' )
( order_id = 2 customer = 'Client B' )
).
DATA(lt_items) = VALUE ty_t_items(
( order_id = 1 item_no = 10 product = 'Laptop' )
( order_id = 2 item_no = 10 product = 'Serveur' )
).
DATA(lt_conditions) = VALUE ty_t_conditions(
( order_id = 1 item_no = 10 condition_type = 'Remise 10%' amount = '-100.00' )
( order_id = 2 item_no = 10 condition_type = 'Remise volume' amount = '-500.00' )
).
DATA(ls_mesh) = VALUE ty_m_order(
headers = lt_headers
items = lt_items
conditions = lt_conditions
).
out->write( '=== Navigation arrière : Condition -> Item -> Header ===' ).
" Naviguer des conditions vers le client
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.

Agrégations avec REDUCE et Mesh

Les Mesh Expressions combinées avec REDUCE permettent des calculs élégants :

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 = 'Client A' )
( order_id = 2 customer = 'Client 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( '=== Valeur de commande par client (conditions incluses) ===' ).
LOOP AT ls_mesh-orders INTO DATA(ls_order).
" Somme de toutes les positions
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 )
).
" Somme de toutes les 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.

Conseils de performance

Choisir les types de tables

" BON : SORTED TABLE avec clé appropriée
TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item
WITH UNIQUE KEY order_id item_no.
" MAUVAIS : STANDARD TABLE - mauvaise performance
" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item
" WITH EMPTY KEY.

Économiser la mémoire avec REF

Pour les grandes tables, vous pouvez utiliser des références au lieu de copies :

" Mesh avec références (économise la mémoire)
DATA(ls_mesh) = VALUE ty_m_order(
headers = lt_headers " Copie
items = REF #( lt_items ) " Référence
conditions = REF #( lt_conditions )
).

Compatibilité ABAP Cloud

AspectStatut
BTP ABAP EnvironmentEntièrement supporté
S/4HANA CloudEntièrement supporté
S/4HANA On-PremiseÀ partir de 7.40 SP05
Statut API ReleasedFonctionnalité du langage

Les Mesh Expressions sont une fonctionnalité du langage ABAP et ne sont pas affectées par les restrictions d’API Released. Elles peuvent être utilisées sans restrictions dans tous les environnements ABAP Cloud.

Bonnes pratiques pour ABAP Cloud

" FAIRE : Tables SORTED/HASHED avec clés appropriées
TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item
WITH UNIQUE KEY order_id item_no.
" FAIRE : Utiliser Mesh pour les navigations complexes
LOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item).
" Plus clair que les conditions WHERE
ENDLOOP.
" NE PAS FAIRE : STANDARD TABLE sans clé
" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item
" WITH EMPTY KEY.
" NE PAS FAIRE : Mesh pour les lookups simples 1:1
" Pour les lookups individuels, READ TABLE est plus efficace

Quand utiliser Mesh ?

Cas d’utilisationRecommandation
Structures Header-ItemIdéal pour Mesh
Relations Master-DetailIdéal pour Mesh
Données hiérarchiques (BOM)Idéal pour Mesh
Lookup simple 1:1READ TABLE suffit
Tables DB directesMesh uniquement pour tables internes
Relations dynamiquesLes associations doivent être statiques

Résumé

Les Mesh Expressions offrent une alternative élégante aux boucles imbriquées :

  • Définition centralisée : Relations définies une fois dans le type MESH
  • Navigation déclarative : Syntaxe de chemin au lieu de conditions WHERE
  • Sécurité des types : Vérification des associations à la compilation
  • Navigation bidirectionnelle : Forward (\) et Backward (\_)
  • Performance : Utilise automatiquement les clés des tables
  • Prêt pour ABAP Cloud : Entièrement supporté dans tous les environnements Cloud

En particulier pour les structures de commande avec en-tête, positions et conditions, les Mesh Expressions réduisent considérablement la complexité et rendent le code plus maintenable.

Articles connexes