ABAP Virtual Sorting : Traiter les tables sans tri physique

Catégorie
ABAP
Publié
Auteur
Johannes

Le Virtual Sorting permet le traitement trié des tables internes sans modifier l’ordre physique des lignes. Avec la syntaxe SORT ... BY dans les expressions de table et les itérations, vous pouvez parcourir les données dans un ordre spécifique tandis que la table originale reste inchangée.

Qu’est-ce que le Virtual Sorting ?

Le Virtual Sorting est un concept où l’ordre de tri est défini temporairement pour une opération, sans modifier la table elle-même. C’est particulièrement utile quand :

  • Plusieurs ordres de tri sont nécessaires en parallèle
  • La table originale ne doit pas être modifiée
  • La performance est critique sur de grandes tables
AspectSORT physiqueVirtual Sorting
Table originaleEst modifiéeReste inchangée
OrdrePermanentTemporaire pour l’opération
Plusieurs trisSuccessivementPossible en parallèle
MémoireIn-PlaceIndex supplémentaire

Disponibilité

Le Virtual Sorting avec FOR ... IN ... USING KEY et les clés dynamiques est disponible depuis ABAP 7.40. Les possibilités étendues avec les clés secondaires sont entièrement compatibles ABAP Cloud.

Exemple pratique : Analyser les données de ventes

Un scénario typique est l’analyse des données de ventes qui doivent être évaluées dans différents ordres :

CLASS zcl_virtual_sorting DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_sale,
sale_id TYPE i,
product TYPE string,
region TYPE string,
amount TYPE p DECIMALS 2,
sale_date TYPE d,
END OF ty_s_sale.
" Table avec clés secondaires pour le tri virtuel
TYPES ty_t_sales TYPE SORTED TABLE OF ty_s_sale
WITH UNIQUE KEY sale_id
WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING
WITH NON-UNIQUE SORTED KEY by_region COMPONENTS region
WITH NON-UNIQUE SORTED KEY by_date COMPONENTS sale_date DESCENDING.
ENDCLASS.
CLASS zcl_virtual_sorting IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_sales) = VALUE ty_t_sales(
( sale_id = 1 product = 'Laptop' region = 'Nord' amount = '1200.00' sale_date = '20260110' )
( sale_id = 2 product = 'Écran' region = 'Sud' amount = '450.00' sale_date = '20260115' )
( sale_id = 3 product = 'Clavier' region = 'Nord' amount = '89.00' sale_date = '20260112' )
( sale_id = 4 product = 'Serveur' region = 'Ouest' amount = '5500.00' sale_date = '20260108' )
( sale_id = 5 product = 'Souris' region = 'Sud' amount = '35.00' sale_date = '20260120' )
).
out->write( '=== Ordre original (par sale_id) ===' ).
LOOP AT lt_sales INTO DATA(ls_sale).
out->write( |{ ls_sale-sale_id }: { ls_sale-product } - { ls_sale-amount }| ).
ENDLOOP.
out->write( '' ).
out->write( '=== Par chiffre d''affaires (le plus élevé en premier) ===' ).
LOOP AT lt_sales INTO ls_sale USING KEY by_amount.
out->write( |{ ls_sale-product }: { ls_sale-amount } EUR| ).
ENDLOOP.
out->write( '' ).
out->write( '=== Par région ===' ).
LOOP AT lt_sales INTO ls_sale USING KEY by_region.
out->write( |{ ls_sale-region }: { ls_sale-product }| ).
ENDLOOP.
out->write( '' ).
out->write( '=== Par date (les plus récentes en premier) ===' ).
LOOP AT lt_sales INTO ls_sale USING KEY by_date.
out->write( |{ ls_sale-sale_date DATE = USER }: { ls_sale-product }| ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Clés secondaires pour le Virtual Sorting

La clé du Virtual Sorting réside dans les clés de table secondaires. Elles définissent des ordres de tri alternatifs que le noyau ABAP gère automatiquement.

Types de clés

" Clé primaire (identification)
WITH UNIQUE KEY sale_id
" Clé SORTED secondaire (maintenue automatiquement triée)
WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING
" Clé HASHED secondaire (pour les recherches rapides)
WITH UNIQUE HASHED KEY by_product COMPONENTS product

Comparaison : Physique vs. Virtuel

CLASS zcl_sort_comparison DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_item,
item_id TYPE i,
name TYPE string,
value TYPE p DECIMALS 2,
END OF ty_s_item.
TYPES ty_t_items TYPE SORTED TABLE OF ty_s_item
WITH UNIQUE KEY item_id
WITH NON-UNIQUE SORTED KEY by_value COMPONENTS value DESCENDING.
ENDCLASS.
CLASS zcl_sort_comparison IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_items) = VALUE ty_t_items(
( item_id = 1 name = 'A' value = '100.00' )
( item_id = 2 name = 'B' value = '300.00' )
( item_id = 3 name = 'C' value = '200.00' )
).
" Tri PHYSIQUE - modifie la table
DATA(lt_sorted_copy) = lt_items.
SORT lt_sorted_copy BY value DESCENDING.
out->write( '=== Copie triée physiquement ===' ).
LOOP AT lt_sorted_copy INTO DATA(ls_sorted).
out->write( |{ ls_sorted-name }: { ls_sorted-value }| ).
ENDLOOP.
" Tri VIRTUEL - l'original reste inchangé
out->write( '' ).
out->write( '=== Trié virtuellement (USING KEY) ===' ).
LOOP AT lt_items INTO DATA(ls_virtual) USING KEY by_value.
out->write( |{ ls_virtual-name }: { ls_virtual-value }| ).
ENDLOOP.
" L'original est inchangé
out->write( '' ).
out->write( '=== Original (inchangé) ===' ).
LOOP AT lt_items INTO DATA(ls_original).
out->write( |{ ls_original-name }: { ls_original-value }| ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Virtual Sorting avec les expressions FOR

Le Virtual Sorting fonctionne également dans les constructions fonctionnelles comme les boucles FOR et REDUCE :

CLASS zcl_virtual_sort_for 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,
amount TYPE p DECIMALS 2,
priority TYPE i,
END OF ty_s_order.
TYPES ty_t_orders TYPE SORTED TABLE OF ty_s_order
WITH UNIQUE KEY order_id
WITH NON-UNIQUE SORTED KEY by_priority COMPONENTS priority
WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING.
TYPES: BEGIN OF ty_s_output,
rank TYPE i,
info TYPE string,
END OF ty_s_output.
TYPES ty_t_output TYPE STANDARD TABLE OF ty_s_output WITH EMPTY KEY.
ENDCLASS.
CLASS zcl_virtual_sort_for IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_orders) = VALUE ty_t_orders(
( order_id = 101 customer = 'Müller' amount = '500.00' priority = 2 )
( order_id = 102 customer = 'Schmidt' amount = '1500.00' priority = 1 )
( order_id = 103 customer = 'Weber' amount = '300.00' priority = 3 )
( order_id = 104 customer = 'Fischer' amount = '2000.00' priority = 1 )
).
" FOR avec USING KEY - détermine l'ordre pour le constructeur
DATA(lt_by_priority) = VALUE ty_t_output(
FOR ls_order IN lt_orders USING KEY by_priority
INDEX INTO lv_idx
( rank = lv_idx
info = |{ ls_order-customer } (Prio { ls_order-priority }): { ls_order-amount } EUR| )
).
out->write( '=== Trié par priorité (VALUE FOR) ===' ).
LOOP AT lt_by_priority INTO DATA(ls_output).
out->write( |{ ls_output-rank }. { ls_output-info }| ).
ENDLOOP.
" Top 2 par montant avec REDUCE
DATA(lv_top2_total) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
count = 0
FOR ls_ord IN lt_orders USING KEY by_amount
WHILE count < 2
NEXT sum = sum + ls_ord-amount
count = count + 1
).
out->write( '' ).
out->write( |Somme des 2 premières commandes : { lv_top2_total } EUR| ).
" Filtre combiné avec tri virtuel
DATA(lt_high_priority) = VALUE ty_t_output(
FOR ls_o IN lt_orders USING KEY by_amount
WHERE ( priority <= 2 )
INDEX INTO lv_i
( rank = lv_i
info = |{ ls_o-customer }: { ls_o-amount }| )
).
out->write( '' ).
out->write( '=== Haute priorité, par montant ===' ).
LOOP AT lt_high_priority INTO DATA(ls_hp).
out->write( |{ ls_hp-rank }. { ls_hp-info }| ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Considérations de performance

Le Virtual Sorting avec des clés secondaires offre des avantages de performance, mais a aussi des coûts :

Quand utiliser les clés secondaires

SituationRecommandation
Même tri fréquentClé secondaire rentable
Tri uniqueSORT physique plus efficace
Plusieurs tris parallèlesIdéal pour les clés secondaires
Très grandes tables (>100 000)Peser les coûts mémoire
INSERT/DELETE fréquentsLa maintenance des clés coûte en performance

Mesure de performance

CLASS zcl_virtual_sort_perf DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES: BEGIN OF ty_s_data,
id TYPE i,
value TYPE p DECIMALS 2,
END OF ty_s_data.
" Avec clé secondaire
TYPES ty_t_with_key TYPE SORTED TABLE OF ty_s_data
WITH UNIQUE KEY id
WITH NON-UNIQUE SORTED KEY by_value COMPONENTS value.
" Sans clé secondaire
TYPES ty_t_no_key TYPE SORTED TABLE OF ty_s_data
WITH UNIQUE KEY id.
ENDCLASS.
CLASS zcl_virtual_sort_perf IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
" Générer des données de test
DATA lt_with_key TYPE ty_t_with_key.
DATA lt_no_key TYPE ty_t_no_key.
DO 10000 TIMES.
DATA(lv_value) = CONV p DECIMALS 2( sy-index MOD 1000 ).
INSERT VALUE #( id = sy-index value = lv_value ) INTO TABLE lt_with_key.
INSERT VALUE #( id = sy-index value = lv_value ) INTO TABLE lt_no_key.
ENDDO.
" Variante 1 : Virtual Sorting avec clé
DATA(lv_start1) = cl_abap_context_info=>get_system_time( ).
DATA(lv_sum1) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_data IN lt_with_key USING KEY by_value
NEXT sum = sum + ls_data-value
).
DATA(lv_end1) = cl_abap_context_info=>get_system_time( ).
" Variante 2 : SORT physique + itération
DATA(lt_copy) = CORRESPONDING ty_t_no_key( lt_no_key ).
DATA(lv_start2) = cl_abap_context_info=>get_system_time( ).
SORT lt_copy BY value.
DATA(lv_sum2) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_data IN lt_copy
NEXT sum = sum + ls_data-value
).
DATA(lv_end2) = cl_abap_context_info=>get_system_time( ).
out->write( |Virtual Sorting : { lv_end1 - lv_start1 } ms| ).
out->write( |SORT physique : { lv_end2 - lv_start2 } ms| ).
out->write( |Somme identique : { COND #( WHEN lv_sum1 = lv_sum2 THEN 'Oui' ELSE 'Non' ) }| ).
ENDMETHOD.
ENDCLASS.

Bonnes pratiques

À FAIRE : Utiliser judicieusement les clés secondaires

" BON : Clé pour un tri fréquemment utilisé
TYPES ty_t_products TYPE SORTED TABLE OF ty_s_product
WITH UNIQUE KEY product_id
WITH NON-UNIQUE SORTED KEY by_price COMPONENTS price DESCENDING.
" Itération dans l'ordre souhaité
LOOP AT lt_products INTO DATA(ls_prod) USING KEY by_price.
" Traitement par prix décroissant
ENDLOOP.

À NE PAS FAIRE : Définir trop de clés

" MAUVAIS : Trop de clés secondaires
" Chaque clé occupe de la mémoire et coûte lors des INSERT/DELETE
TYPES ty_t_overkill TYPE SORTED TABLE OF ty_s_data
WITH UNIQUE KEY id
WITH NON-UNIQUE SORTED KEY k1 COMPONENTS field1
WITH NON-UNIQUE SORTED KEY k2 COMPONENTS field2
WITH NON-UNIQUE SORTED KEY k3 COMPONENTS field3
WITH NON-UNIQUE SORTED KEY k4 COMPONENTS field4
WITH NON-UNIQUE SORTED KEY k5 COMPONENTS field5. " Trop !
" MIEUX : Uniquement les clés vraiment nécessaires
TYPES ty_t_balanced TYPE SORTED TABLE OF ty_s_data
WITH UNIQUE KEY id
WITH NON-UNIQUE SORTED KEY by_priority COMPONENTS priority.

Compatibilité ABAP Cloud

AspectStatut
BTP ABAP EnvironmentEntièrement supporté
S/4HANA CloudEntièrement supporté
S/4HANA On-PremiseÀ partir de 7.40
Statut Released APIFonctionnalité du langage de base

Le Virtual Sorting avec des clés secondaires est une fonctionnalité de base d’ABAP et peut être utilisé dans tous les environnements ABAP Cloud sans restrictions.

Résumé

Le Virtual Sorting permet des solutions élégantes pour les tris multiples :

  • Les clés secondaires définissent des ordres de tri alternatifs
  • USING KEY active le tri virtuel dans LOOP et FOR
  • L’original reste inchangé - pas d’effets de bord
  • Avantage de performance pour les tris répétés
  • Coûts mémoire des clés secondaires à considérer
  • Combinable avec REDUCE, VALUE, et autres constructions fonctionnelles

Particulièrement dans les scénarios de reporting, où les données doivent être présentées dans différentes vues, le Virtual Sorting est une solution élégante et performante.

Articles connexes