Virtual Sorting enables sorted processing of internal tables without changing the physical order of rows. Using the SORT ... BY syntax in table expressions and iterations, you can traverse data in a specific order while the original table remains unchanged.
What is Virtual Sorting?
Virtual Sorting is a concept where the sort order is defined temporarily for an operation without modifying the table itself. This is particularly useful when:
- Multiple sort orders are needed in parallel
- The original table should not be modified
- Performance with large tables is critical
| Aspect | Physical SORT | Virtual Sorting |
|---|---|---|
| Original table | Modified | Remains unchanged |
| Order | Permanent | Temporary for operation |
| Multiple sorts | Sequential | Parallel possible |
| Memory | In-place | Additional index |
Availability
Virtual Sorting with FOR ... IN ... USING KEY and dynamic keys is available since ABAP 7.40. The extended capabilities with secondary keys are fully ABAP Cloud compatible.
Practical Example: Analyzing Sales Data
A typical scenario is analyzing sales data that should be evaluated in different sort orders:
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 with secondary keys for virtual sorting 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 = 'North' amount = '1200.00' sale_date = '20260110' ) ( sale_id = 2 product = 'Monitor' region = 'South' amount = '450.00' sale_date = '20260115' ) ( sale_id = 3 product = 'Keyboard' region = 'North' amount = '89.00' sale_date = '20260112' ) ( sale_id = 4 product = 'Server' region = 'West' amount = '5500.00' sale_date = '20260108' ) ( sale_id = 5 product = 'Mouse' region = 'South' amount = '35.00' sale_date = '20260120' ) ).
out->write( '=== Original Order (by 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( '=== By Revenue (highest first) ===' ). 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( '=== By Region ===' ). LOOP AT lt_sales INTO ls_sale USING KEY by_region. out->write( |{ ls_sale-region }: { ls_sale-product }| ). ENDLOOP.
out->write( '' ). out->write( '=== By Date (newest first) ===' ). 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.Secondary Keys for Virtual Sorting
The key to Virtual Sorting is secondary table keys. They define alternative sort orders that the ABAP kernel automatically manages.
Key Types
" Primary key (identification)WITH UNIQUE KEY sale_id
" Secondary SORTED key (automatically kept sorted)WITH NON-UNIQUE SORTED KEY by_amount COMPONENTS amount DESCENDING
" Secondary HASHED key (for fast lookups)WITH UNIQUE HASHED KEY by_product COMPONENTS productComparison: Physical vs. Virtual
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' ) ).
" PHYSICAL sorting - modifies the table DATA(lt_sorted_copy) = lt_items. SORT lt_sorted_copy BY value DESCENDING.
out->write( '=== Physically Sorted Copy ===' ). LOOP AT lt_sorted_copy INTO DATA(ls_sorted). out->write( |{ ls_sorted-name }: { ls_sorted-value }| ). ENDLOOP.
" VIRTUAL sorting - original remains unchanged out->write( '' ). out->write( '=== Virtually Sorted (USING KEY) ===' ). LOOP AT lt_items INTO DATA(ls_virtual) USING KEY by_value. out->write( |{ ls_virtual-name }: { ls_virtual-value }| ). ENDLOOP.
" Original is unchanged out->write( '' ). out->write( '=== Original (unchanged) ===' ). LOOP AT lt_items INTO DATA(ls_original). out->write( |{ ls_original-name }: { ls_original-value }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Virtual Sorting with FOR Expressions
Virtual Sorting also works in functional constructs like FOR loops and 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 = 'Mueller' 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 with USING KEY - determine order for constructor 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( '=== Sorted by Priority (VALUE FOR) ===' ). LOOP AT lt_by_priority INTO DATA(ls_output). out->write( |{ ls_output-rank }. { ls_output-info }| ). ENDLOOP.
" Top 2 by amount with 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( |Top 2 orders total: { lv_top2_total } EUR| ).
" Filter combined with virtual sorting 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( '=== High Priority, by Amount ===' ). LOOP AT lt_high_priority INTO DATA(ls_hp). out->write( |{ ls_hp-rank }. { ls_hp-info }| ). ENDLOOP. ENDMETHOD.
ENDCLASS.Performance Considerations
Virtual Sorting with secondary keys offers performance benefits but also has costs:
When to Use Secondary Keys
| Situation | Recommendation |
|---|---|
| Frequently same sorting | ✅ Secondary key is worthwhile |
| One-time sorting | ❌ Physical SORT more efficient |
| Multiple parallel sorts | ✅ Ideal for secondary keys |
| Very large tables (>100,000) | ⚠️ Consider memory costs |
| Frequent INSERT/DELETE | ⚠️ Key maintenance costs performance |
Performance Measurement
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.
" With secondary key 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.
" Without secondary key 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. " Generate test data 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.
" Variant 1: Virtual Sorting with key 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( ).
" Variant 2: Physical SORT + iteration 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( |Physical SORT: { lv_end2 - lv_start2 } ms| ). out->write( |Sum identical: { COND #( WHEN lv_sum1 = lv_sum2 THEN 'Yes' ELSE 'No' ) }| ). ENDMETHOD.
ENDCLASS.Best Practices
DO: Use Secondary Keys Wisely
" GOOD: Key for frequently used sortingTYPES 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.
" Iteration in desired orderLOOP AT lt_products INTO DATA(ls_prod) USING KEY by_price. " Process by price descendingENDLOOP.DON’T: Define Too Many Keys
" BAD: Too many secondary keys" Each key consumes memory and costs performance on INSERT/DELETETYPES 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. " Too much!
" BETTER: Only the actually needed keysTYPES ty_t_balanced TYPE SORTED TABLE OF ty_s_data WITH UNIQUE KEY id WITH NON-UNIQUE SORTED KEY by_priority COMPONENTS priority.ABAP Cloud Compatibility
| Aspect | Status |
|---|---|
| BTP ABAP Environment | ✅ Fully supported |
| S/4HANA Cloud | ✅ Fully supported |
| S/4HANA On-Premise | ✅ From 7.40 |
| Released API Status | ✅ Core language feature |
Virtual Sorting with secondary keys is a core language feature of ABAP and can be used without restrictions in all ABAP Cloud environments.
Summary
Virtual Sorting enables elegant solutions for multiple sortings:
- Secondary Keys define alternative sort orders
- USING KEY activates virtual sorting in LOOP and FOR
- Original remains unchanged - no side effects
- Performance advantage with repeated sortings
- Memory costs for secondary keys should be considered
- Combinable with REDUCE, VALUE, and other functional constructs
Especially in reporting scenarios where data needs to be displayed in different views, Virtual Sorting is an elegant and performant solution.