Mesh Expressions permiten navegar entre tablas internas relacionadas de forma declarativa. Son especialmente útiles cuando trabajas con estructuras de datos jerárquicas como cabecera-posiciones.
Concepto básico
Un Mesh define relaciones entre tablas internas, similar a las asociaciones en CDS Views. Esto permite navegar de una entrada a las entradas relacionadas en otra tabla.
Definición de Mesh
Sintaxis básica
TYPES: BEGIN OF MESH ty_mesh, header TYPE STANDARD TABLE OF ty_header WITH EMPTY KEY, items TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY parent_id item_id,
" Asociación: header -> items _items TYPE ASSOCIATION ty_mesh-header TO ty_mesh-items ON parent_id = header_id, END OF MESH ty_mesh.Ejemplo completo de definición
TYPES: BEGIN OF ty_order, order_id TYPE i, customer_id TYPE i, order_date TYPE d, END OF ty_order.
TYPES: BEGIN OF ty_item, order_id TYPE i, item_id TYPE i, product TYPE string, quantity TYPE i, price TYPE p DECIMALS 2, END OF ty_item.
TYPES: BEGIN OF ty_customer, customer_id TYPE i, name TYPE string, END OF ty_customer.
" Definición del Mesh con asociacionesTYPES: BEGIN OF MESH ty_order_mesh, orders TYPE SORTED TABLE OF ty_order WITH UNIQUE KEY order_id, items TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY order_id item_id, customers TYPE SORTED TABLE OF ty_customer WITH UNIQUE KEY customer_id,
" Orden -> Posiciones _items FOR orders TYPE ASSOCIATION ty_order_mesh-orders TO ty_order_mesh-items ON order_id = order_id,
" Orden -> Cliente _customer FOR orders TYPE ASSOCIATION ty_order_mesh-orders TO ty_order_mesh-customers ON customer_id = customer_id,
" Posición -> Orden _order FOR items TYPE ASSOCIATION ty_order_mesh-items TO ty_order_mesh-orders ON order_id = order_id, END OF MESH ty_order_mesh.Uso del Mesh
Llenar el Mesh
DATA: ls_mesh TYPE ty_order_mesh.
" Llenar pedidosls_mesh-orders = VALUE #( ( order_id = 1 customer_id = 100 order_date = '20241101' ) ( order_id = 2 customer_id = 200 order_date = '20241102' ) ( order_id = 3 customer_id = 100 order_date = '20241103' )).
" Llenar posicionesls_mesh-items = VALUE #( ( order_id = 1 item_id = 10 product = 'Producto A' quantity = 5 price = '100.00' ) ( order_id = 1 item_id = 20 product = 'Producto B' quantity = 3 price = '50.00' ) ( order_id = 2 item_id = 10 product = 'Producto C' quantity = 1 price = '200.00' ) ( order_id = 3 item_id = 10 product = 'Producto A' quantity = 2 price = '100.00' )).
" Llenar clientesls_mesh-customers = VALUE #( ( customer_id = 100 name = 'Cliente A' ) ( customer_id = 200 name = 'Cliente B' )).Navegación simple
" Obtener el primer pedidoDATA(ls_order) = ls_mesh-orders[ 1 ].
" Navegar a las posiciones de este pedidoDATA(lt_items) = ls_mesh-_items[ ls_order ].
" MostrarLOOP AT lt_items INTO DATA(ls_item). WRITE: / ls_item-product, ls_item-quantity.ENDLOOP.Navegación encadenada
" Desde pedido al clienteDATA(ls_order) = ls_mesh-orders[ order_id = 1 ].DATA(lt_customers) = ls_mesh-_customer[ ls_order ].DATA(ls_customer) = lt_customers[ 1 ].
WRITE: / 'Cliente:', ls_customer-name.
" O en una sola expresiónDATA(lv_customer_name) = ls_mesh-_customer[ ls_mesh-orders[ order_id = 1 ] ][ 1 ]-name.Navegación inversa
" Desde posición al pedidoDATA(ls_item) = ls_mesh-items[ order_id = 1 item_id = 10 ].DATA(lt_orders) = ls_mesh-_order[ ls_item ].DATA(ls_parent_order) = lt_orders[ 1 ].
WRITE: / 'Pedido padre:', ls_parent_order-order_id, ls_parent_order-order_date.Casos de uso avanzados
Calcular totales por pedido
LOOP AT ls_mesh-orders INTO DATA(ls_order). DATA(lt_order_items) = ls_mesh-_items[ ls_order ].
DATA(lv_total) = REDUCE decfloat34( INIT sum = CONV decfloat34( 0 ) FOR item IN lt_order_items NEXT sum = sum + ( item-quantity * item-price ) ).
WRITE: / 'Pedido:', ls_order-order_id, 'Total:', lv_total.ENDLOOP.Filtrar con condiciones
" Todos los pedidos del cliente 100DATA(lt_customer_orders) = VALUE ty_order_mesh-orders( FOR order IN ls_mesh-orders WHERE ( customer_id = 100 ) ( order )).
" Para cada uno, obtener las posicionesLOOP AT lt_customer_orders INTO DATA(ls_order). DATA(lt_items) = ls_mesh-_items[ ls_order ]. " Procesar posiciones...ENDLOOP.Comprobar existencia
" ¿Tiene posiciones el pedido?DATA(ls_order) = ls_mesh-orders[ order_id = 1 ].DATA(lt_items) = ls_mesh-_items[ ls_order ].
IF lt_items IS NOT INITIAL. WRITE: / 'Pedido tiene', lines( lt_items ), 'posiciones'.ELSE. WRITE: / 'Pedido sin posiciones'.ENDIF.Comparación con alternativas
Sin Mesh (clásico)
" Clásico: Buscar posiciones manualmenteLOOP AT lt_orders INTO DATA(ls_order). LOOP AT lt_items INTO DATA(ls_item) WHERE order_id = ls_order-order_id. " Procesar... ENDLOOP.ENDLOOP.Con Mesh
" Con Mesh: Navegación declarativaLOOP AT ls_mesh-orders INTO DATA(ls_order). DATA(lt_order_items) = ls_mesh-_items[ ls_order ]. LOOP AT lt_order_items INTO DATA(ls_item). " Procesar... ENDLOOP.ENDLOOP.Requisitos de clave
Las asociaciones solo funcionan correctamente si las tablas tienen las claves apropiadas:
" Tabla ordenada con clave única - mejor rendimientoitems TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY order_id item_id,
" Tabla estándar con clave vacía - funciona, pero menos eficienteitems TYPE STANDARD TABLE OF ty_item WITH EMPTY KEY,Manejo de errores
" Comprobar si la navegación devuelve resultadosTRY. DATA(lt_items) = ls_mesh-_items[ ls_mesh-orders[ order_id = 999 ] ]. CATCH cx_sy_itab_line_not_found. WRITE: / 'Pedido no encontrado'.ENDTRY.
" Alternativa con OPTIONALDATA(ls_order) = VALUE #( ls_mesh-orders[ order_id = 999 ] OPTIONAL ).IF ls_order IS NOT INITIAL. DATA(lt_items) = ls_mesh-_items[ ls_order ].ENDIF.Notas importantes / Mejores prácticas
- Usa SORTED TABLE con claves para mejor rendimiento.
- Los Meshes son ideales para estructuras cabecera-posiciones.
- La navegación es solo lectura - no puedes modificar a través del Mesh.
- Las asociaciones deben definirse para ambas direcciones si necesitas navegación bidireccional.
- Considera usar CDS Views para datos de base de datos con relaciones.
- Los Meshes funcionan bien con VALUE y FOR.