Les Mesh Expressions sont une fonctionnalité puissante en ABAP, disponible depuis la Release 7.40 SP05. Elles permettent la navigation entre tables internes liées via des associations définies - similaire aux relations de clés étrangères dans les bases de données relationnelles.
Qu’est-ce qu’un Mesh ?
Un Mesh est un ensemble typé de tables internes qui sont liées entre elles par des associations. Au lieu d’écrire des LOOPs imbriqués, vous naviguez de manière déclarative d’une table à l’autre.
| Concept | Description |
|---|---|
TYPES MESH | Définit le type Mesh avec des tables et associations |
| Association | Liaison entre deux tables dans le Mesh |
| Mesh Path | Chemin de navigation via associations |
\association | Syntaxe pour navigation forward |
\_association | Syntaxe pour navigation backward |
Cas d’utilisation des Mesh Expressions
- Données hiérarchiques : Relations En-tête-Postes (Commande → Postes)
- Master-Detail : Client → Commandes → Postes
- Structures organisationnelles : Département → Équipes → Employés
- Bill of Materials : Produit → Composants → Sous-composants
- Flux de documents : Document → Documents suivants
Définition TYPES MESH
La structure de base d’un Mesh commence par la définition du type :
CLASS zcl_mesh_basic DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Structures pour En-tête et Postes 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.
" Types de tables 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.
" Définition Mesh avec associations 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. " Créer des 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_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' ) ).
" Créer une instance Mesh DATA(ls_orders) = VALUE ty_m_orders( headers = lt_headers items = lt_items ).
out->write( |Mesh créé avec { lines( ls_orders-headers ) } Headers| ). out->write( |et { lines( ls_orders-items ) } Items| ). ENDMETHOD.
ENDCLASS.Explication de la définition Mesh
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 : Mot-clé pour la définition du type Mesh
- ASSOCIATION name TO node : Définit une association vers une autre node dans le Mesh
- ON field1 = field2 : Condition de jointure (comme les jointures SQL)
Navigation avec des chemins Mesh
La vraie force des Mesh Expressions réside dans la navigation déclarative :
CLASS zcl_mesh_navigation DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_oo_adt_classrun.
" Structures 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.
" Types de tables 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 avec trois niveaux 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. " Construire des données de test DATA(lt_customers) = VALUE ty_t_customers( ( customer_id = 1 name = 'Müller GmbH' city = 'Berlin' ) ( customer_id = 2 name = 'Schmidt AG' city = 'Munich' ) ).
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 = 'Souris' quantity = 2 price = '25.00' ) ( order_id = 101 item_no = 1 product = 'Clavier' quantity = 1 price = '175.00' ) ( order_id = 102 item_no = 1 product = 'Moniteur' quantity = 2 price = '250.00' ) ).
" Créer le Mesh DATA(ls_sales) = VALUE ty_m_sales( customers = lt_customers orders = lt_orders items = lt_items ).
" === Navigation : Customer -> Orders (1:n) === out->write( '=== Navigation Forward : Customer -> Orders ===' ).
LOOP AT ls_sales-customers INTO DATA(ls_customer). out->write( |Client : { ls_customer-name }| ).
" Chemin Mesh : Toutes les commandes du client LOOP AT ls_sales-customers\orders[ ls_customer ] INTO DATA(ls_order). out->write( | Commande { ls_order-order_id } : { ls_order-total } EUR| ).
" Chemin Mesh imbriqué : Postes de la commande LOOP AT ls_sales-orders\items[ ls_order ] INTO DATA(ls_item). out->write( | - { ls_item-product } x{ ls_item-quantity }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" === Navigation Backward : Item -> Order -> Customer === out->write( '' ). out->write( '=== Navigation Backward : Item -> Order -> Customer ===' ).
LOOP AT ls_sales-items INTO DATA(ls_item2). " Du poste vers la commande DATA(ls_parent_order) = ls_sales-items\_order[ ls_item2 ]. " De la commande vers le client DATA(ls_parent_customer) = ls_sales-orders\_customer[ ls_parent_order ].
out->write( |{ ls_item2-product } : Commande { ls_parent_order-order_id } de { ls_parent_customer-name }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Syntaxe des chemins Mesh
| Syntaxe | Description |
|---|---|
mesh-node\assoc[ source ] | Navigation forward : Toutes les cibles de l’association |
mesh-node\_assoc[ source ] | Navigation backward : Retour à la source |
mesh-node\assoc1\assoc2[ source ] | Navigation chaînée sur plusieurs associations |
Avantages par rapport aux LOOPs imbriqués
Le plus grand avantage des Mesh Expressions apparaît dans la comparaison avec le code classique :
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. " Données de test DATA(lt_departments) = VALUE ty_t_departments( ( dept_id = 1 dept_name = 'Développement' ) ( dept_id = 2 dept_name = 'Ventes' ) ).
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-Introduction' budget = '80000.00' ) ).
" Créer le Mesh DATA(ls_org) = VALUE ty_m_organization( departments = lt_departments employees = lt_employees projects = lt_projects ).
" ============================================ " APPROCHE CLASSIQUE : LOOPs imbriqués " ============================================ out->write( '=== Approche classique (LOOPs imbriqués) ===' ).
LOOP AT lt_departments INTO DATA(ls_dept). out->write( |Département : { ls_dept-dept_name }| ).
LOOP AT lt_employees INTO DATA(ls_emp) WHERE dept_id = ls_dept-dept_id. out->write( | Employé : { ls_emp-emp_name }| ).
LOOP AT lt_projects INTO DATA(ls_proj) WHERE emp_id = ls_emp-emp_id. out->write( | Projet : { ls_proj-proj_name }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" ============================================ " APPROCHE MESH : Navigation déclarative " ============================================ out->write( '' ). out->write( '=== Approche Mesh (navigation déclarative) ===' ).
LOOP AT ls_org-departments INTO DATA(ls_dept2). out->write( |Département : { ls_dept2-dept_name }| ).
" Navigation Mesh : Département -> Employés LOOP AT ls_org-departments\employees[ ls_dept2 ] INTO DATA(ls_emp2). out->write( | Employé : { ls_emp2-emp_name }| ).
" Navigation Mesh : Employés -> Projets LOOP AT ls_org-employees\projects[ ls_emp2 ] INTO DATA(ls_proj2). out->write( | Projet : { ls_proj2-proj_name }| ). ENDLOOP. ENDLOOP. ENDLOOP.
" ============================================ " AVANTAGE : Agrégations via chemins Mesh " ============================================ out->write( '' ). out->write( '=== Agrégation : Budget par département ===' ).
LOOP AT ls_org-departments INTO DATA(ls_dept3). " Tous les projets d'un département via employés 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 Budget Projet| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Avantages en résumé
| Aspect | Classique (WHERE) | Mesh |
|---|---|---|
| Lisibilité | LOOPs imbriqués | Chemins déclaratifs |
| Maintenabilité | Conditions dispersées | Relations définies centralement |
| Type-sécurité | Erreurs runtime possibles | Vérification compiletime |
| Performance | Scan linéaire | Utilise les clés de table |
| Réutilisation | Copy-Paste | Définir une fois |
Exemple pratique : Bill of Materials
Un exemple plus complexe montre la force du Mesh pour les structures hiérarchiques :
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 pour résolution de nomenclature 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. " Données articles DATA(lt_materials) = VALUE ty_t_materials( ( material_id = 'BIKE_001' description = 'VTT Complet' unit = 'PC' ) ( material_id = 'FRAME_01' description = 'Cadre Aluminium' unit = 'PC' ) ( material_id = 'WHEEL_01' description = 'Roue 26 pouces' unit = 'PC' ) ( material_id = 'TIRE_001' description = 'Pneu VTT' unit = 'PC' ) ( material_id = 'SPOKE_01' description = 'Rayon Inox' unit = 'PC' ) ( material_id = 'BRAKE_01' description = 'Système freinage Disque' unit = 'PC' ) ).
" Nomenclature (Bill of Materials) DATA(lt_bom) = VALUE ty_t_bom( " Vélo composé de : ( 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 ) " Roue composée de : ( parent_id = 'WHEEL_01' child_id = 'TIRE_001' quantity = 1 position = 10 ) ( parent_id = 'WHEEL_01' child_id = 'SPOKE_01' quantity = 36 position = 20 ) ).
" Stock 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 ) ).
" Créer le Mesh DATA(ls_bom) = VALUE ty_m_bom( materials = lt_materials bom = lt_bom stock = lt_stock ).
" Résolution de nomenclature pour VTT out->write( '=== Résolution nomenclature : VTT ===' ).
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 ).
" Calcul de besoin pour 10 vélos out->write( '' ). out->write( '=== Calcul de besoin pour 10 VTT ===' ). 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 }| ).
" Composants via navigation Mesh LOOP AT is_mesh-materials\components[ is_material ] INTO DATA(ls_bom_item). " Article enfant via chemin Mesh DATA(ls_child) = is_mesh-bom\child[ ls_bom_item ].
" Appel récursif pour sous-ensembles 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.
" Article de départ READ TABLE is_mesh-materials INTO DATA(ls_start) WITH KEY material_id = iv_material.
" Calcul récursif des besoins collect_requirements( is_mesh = is_mesh is_material = ls_start iv_quantity = iv_quantity ct_requirements = lt_requirements ).
" Afficher le résultat et comparer avec stock 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 via navigation Mesh 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 '✗ MANQUE" ).
io_out->write( |{ ls_mat-description } : Besoin { ls_req-quantity } / Stock { lv_stock } { lv_status }| ). ENDLOOP. ENDMETHOD.
METHOD collect_requirements. " Collecter tous les composants 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.
" Collecter le besoin COLLECT VALUE ty_s_requirement( material_id = ls_child-material_id quantity = lv_child_qty ) INTO ct_requirements.
" Récursif pour sous-ensembles collect_requirements( is_mesh = is_mesh is_material = ls_child iv_quantity = lv_child_qty ct_requirements = ct_requirements ). ENDLOOP. ENDMETHOD.
ENDCLASS.Limitations et remarques
Les Mesh Expressions ont quelques limitations à prendre en compte :
| Limitation | Description |
|---|---|
| Uniquement SORTED/HASHED Tables | Les Standard Tables sans clé ne fonctionnent pas de manière optimale |
| Pas d’associations dynamiques | Les relations doivent être connues à la compilation |
| Consommation mémoire | Mesh copie les tables (VALUE) ou les référence (REF) |
| Pas de tables DB | Mesh ne fonctionne qu’avec des tables internes |
Conseils de performance
" 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 sans clé" TYPES ty_t_items TYPE STANDARD TABLE OF ty_s_item" WITH EMPTY KEY.
" BON : Composants Mesh en référence (économise mémoire)DATA(ls_mesh) = VALUE ty_mesh( headers = lt_headers " Copie items = REF #( lt_items ) " Référence (pour grandes tables)).Conclusion
Les Mesh Expressions offrent une alternative élégante aux LOOPs imbriqués :
- Relations déclaratives : Définir une fois, utiliser partout
- Code plus lisible : Chemins Mesh au lieu de conditions WHERE
- Type-sécurité : Vérification Compile-Time des associations
- Performance : Utilise automatiquement les clés de table
- Maintenabilité : Modifications à un seul endroit
Particulièrement pour les structures de données hiérarchiques comme Bill of Materials, structures organisationnelles ou relations En-tête-Postes, l’utilisation des Mesh Expressions est avantageuse.