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.
| Concept | Description |
|---|---|
TYPES MESH | Définit le type Mesh avec tables et associations |
ASSOCIATION name TO node | Association vers l’avant (ex. Header → Items) |
ON field = field | Condition 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ème | Impact |
|---|---|
| Conditions WHERE redondantes | Susceptible d’erreurs lors de modifications |
| Mauvaise performance | Avec STANDARD TABLE, scan linéaire |
| Code illisible | Difficilement maintenable avec plus de niveaux |
| Pas de réutilisation | Conditions à 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 LOOPLOOP AT lt_items INTO DATA(ls_item) WHERE order_id = ls_header-order_id. " ...ENDLOOP.
" MESH : Relation définie une fois, puis naviguerLOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item). " ...ENDLOOP.Comparaison des fonctionnalités
| Aspect | Classique (LOOP + WHERE) | Mesh Expressions |
|---|---|---|
| Définition des relations | Dispersée dans le code | Une fois de manière centralisée |
| Sécurité des types | Erreurs runtime possibles | Vérification à la compilation |
| Performance | Dépend du type de table | Utilise automatiquement les clés |
| Lisibilité | Conditions WHERE partout | Chemins déclaratifs |
| Maintenabilité | Modifications à plusieurs endroits | Modifications uniquement dans le Mesh |
| Navigation arrière | Recherche explicite nécessaire | Intégrée avec \_assoc |
Navigation arrière : Du détail vers l’en-tête
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éeTYPES 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
| Aspect | Statut |
|---|---|
| BTP ABAP Environment | Entièrement supporté |
| S/4HANA Cloud | Entièrement supporté |
| S/4HANA On-Premise | À partir de 7.40 SP05 |
| Statut API Released | Fonctionnalité 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éesTYPES ty_t_items TYPE SORTED TABLE OF ty_s_item WITH UNIQUE KEY order_id item_no.
" FAIRE : Utiliser Mesh pour les navigations complexesLOOP AT ls_mesh-headers\items[ ls_header ] INTO DATA(ls_item). " Plus clair que les conditions WHEREENDLOOP.
" 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 efficaceQuand utiliser Mesh ?
| Cas d’utilisation | Recommandation |
|---|---|
| Structures Header-Item | Idéal pour Mesh |
| Relations Master-Detail | Idéal pour Mesh |
| Données hiérarchiques (BOM) | Idéal pour Mesh |
| Lookup simple 1:1 | READ TABLE suffit |
| Tables DB directes | Mesh uniquement pour tables internes |
| Relations dynamiques | Les 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.