ABAP Virtual Sorting: Process Tables Without Physical Sorting

Category
ABAP
Published
Author
Johannes

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
AspectPhysical SORTVirtual Sorting
Original tableModifiedRemains unchanged
OrderPermanentTemporary for operation
Multiple sortsSequentialParallel possible
MemoryIn-placeAdditional 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 product

Comparison: 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

SituationRecommendation
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 sorting
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.
" Iteration in desired order
LOOP AT lt_products INTO DATA(ls_prod) USING KEY by_price.
" Process by price descending
ENDLOOP.

DON’T: Define Too Many Keys

" BAD: Too many secondary keys
" Each key consumes memory and costs performance on 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. " Too much!
" BETTER: Only the actually needed keys
TYPES 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

AspectStatus
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.