The FILTER expression creates a filtered copy of an internal table. It is the modern, performant alternative to LOOP AT ... WHERE with APPEND. Requirement: The source table needs a sorted or hashed key.
Syntax
1. Filter with WHERE Condition
FILTER <type>( <source_table> [ USING KEY <key> ] WHERE <condition>)2. Filter with Filter Table (IN)
FILTER <type>( <source_table> [ USING KEY <key> ] IN <filter_table> WHERE <field> = <filter_field>)3. Filter with EXCEPT (NOT IN)
FILTER <type>( <source_table> [ EXCEPT [ IN <filter_table> ] ] WHERE <condition>)Prerequisites
Important: FILTER requires a sorted or hashed key on the filter fields:
" SORTED TABLE with keyDATA: lt_data TYPE SORTED TABLE OF ty_data WITH UNIQUE KEY id.
" STANDARD TABLE with secondary sorted keyDATA: lt_data TYPE STANDARD TABLE OF ty_data WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.Examples
1. Basic Filtering with WHERE
TYPES: BEGIN OF ty_order, order_id TYPE i, status TYPE string, amount TYPE p DECIMALS 2, END OF ty_order.
" Sorted table requiredDATA: lt_orders TYPE SORTED TABLE OF ty_order WITH UNIQUE KEY order_id WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.
lt_orders = VALUE #( ( order_id = 1 status = 'OPEN' amount = '100.00' ) ( order_id = 2 status = 'COMPLETED' amount = '200.00' ) ( order_id = 3 status = 'OPEN' amount = '150.00' ) ( order_id = 4 status = 'CANCELLED' amount = '50.00' ) ( order_id = 5 status = 'OPEN' amount = '300.00' )).
" Filter only open ordersDATA(lt_open_orders) = FILTER #( lt_orders USING KEY by_status WHERE status = 'OPEN').
" Result: order_id 1, 3, 5LOOP AT lt_open_orders INTO DATA(ls_order). WRITE: / ls_order-order_id, ls_order-amount.ENDLOOP.2. Comparison: FILTER vs. LOOP AT WHERE
" CLASSIC: With LOOP and APPENDDATA: lt_result TYPE TABLE OF ty_order.LOOP AT lt_orders INTO DATA(ls_ord) WHERE status = 'OPEN'. APPEND ls_ord TO lt_result.ENDLOOP.
" MODERN: With FILTER (more performant for large tables)DATA(lt_result2) = FILTER #( lt_orders USING KEY by_status WHERE status = 'OPEN').3. Filter with Filter Table (IN)
TYPES: BEGIN OF ty_product, product_id TYPE i, category TYPE string, name TYPE string, END OF ty_product.
DATA: lt_products TYPE SORTED TABLE OF ty_product WITH UNIQUE KEY product_id WITH NON-UNIQUE SORTED KEY by_cat COMPONENTS category.
lt_products = VALUE #( ( product_id = 1 category = 'A' name = 'Product 1' ) ( product_id = 2 category = 'B' name = 'Product 2' ) ( product_id = 3 category = 'A' name = 'Product 3' ) ( product_id = 4 category = 'C' name = 'Product 4' ) ( product_id = 5 category = 'B' name = 'Product 5' )).
" Filter table with desired categoriesTYPES: BEGIN OF ty_filter, category TYPE string, END OF ty_filter.
DATA: lt_filter TYPE SORTED TABLE OF ty_filter WITH UNIQUE KEY category.
lt_filter = VALUE #( ( category = 'A' ) ( category = 'B' )).
" Filter products whose category is in lt_filterDATA(lt_filtered) = FILTER #( lt_products USING KEY by_cat IN lt_filter WHERE category = category).
" Result: Products with category A and B (product_id 1, 2, 3, 5)4. EXCEPT – Exclude Instead of Include
" Products EXCEPT category A and BDATA(lt_except) = FILTER #( lt_products USING KEY by_cat EXCEPT IN lt_filter WHERE category = category).
" Result: Only Product 4 (Category C)5. EXCEPT with WHERE (without Filter Table)
TYPES: BEGIN OF ty_employee, emp_id TYPE i, department TYPE string, active TYPE abap_bool, END OF ty_employee.
DATA: lt_employees TYPE SORTED TABLE OF ty_employee WITH UNIQUE KEY emp_id WITH NON-UNIQUE SORTED KEY by_active COMPONENTS active.
lt_employees = VALUE #( ( emp_id = 1 department = 'IT' active = abap_true ) ( emp_id = 2 department = 'HR' active = abap_false ) ( emp_id = 3 department = 'IT' active = abap_true ) ( emp_id = 4 department = 'Sales' active = abap_false )).
" All NOT active employeesDATA(lt_inactive) = FILTER #( lt_employees USING KEY by_active EXCEPT WHERE active = abap_true).
" Result: emp_id 2, 46. Multiple Conditions
TYPES: BEGIN OF ty_item, id TYPE i, type TYPE string, priority TYPE i, END OF ty_item.
DATA: lt_items TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY id WITH NON-UNIQUE SORTED KEY by_type_prio COMPONENTS type priority.
lt_items = VALUE #( ( id = 1 type = 'A' priority = 1 ) ( id = 2 type = 'A' priority = 2 ) ( id = 3 type = 'B' priority = 1 ) ( id = 4 type = 'A' priority = 1 ) ( id = 5 type = 'C' priority = 3 )).
" Filter by type AND priorityDATA(lt_high_prio_a) = FILTER #( lt_items USING KEY by_type_prio WHERE type = 'A' AND priority = 1).
" Result: id 1, 47. FILTER with Standard Table and Secondary Key
" Standard table with secondary keyDATA: lt_data TYPE STANDARD TABLE OF ty_order WITH NON-UNIQUE SORTED KEY k_status COMPONENTS status.
lt_data = VALUE #( ( order_id = 1 status = 'NEW' amount = 100 ) ( order_id = 2 status = 'DONE' amount = 200 ) ( order_id = 3 status = 'NEW' amount = 150 )).
" FILTER uses the secondary keyDATA(lt_new) = FILTER #( lt_data USING KEY k_status WHERE status = 'NEW').8. FILTER in Method Calls
METHODS: process_orders IMPORTING it_orders TYPE ty_orders.
" Pass filtered data directlyprocess_orders( it_orders = FILTER #( lt_all_orders USING KEY by_status WHERE status = 'PENDING' )).9. FILTER with Range Table
TYPES: BEGIN OF ty_sales, sales_id TYPE i, region TYPE string, revenue TYPE p DECIMALS 2, END OF ty_sales.
DATA: lt_sales TYPE SORTED TABLE OF ty_sales WITH UNIQUE KEY sales_id WITH NON-UNIQUE SORTED KEY by_region COMPONENTS region.
lt_sales = VALUE #( ( sales_id = 1 region = 'NORTH' revenue = 1000 ) ( sales_id = 2 region = 'SOUTH' revenue = 2000 ) ( sales_id = 3 region = 'EAST' revenue = 1500 ) ( sales_id = 4 region = 'NORTH' revenue = 1800 )).
" Filter table with regionsTYPES: BEGIN OF ty_region_filter, region TYPE string, END OF ty_region_filter.
DATA: lt_regions TYPE SORTED TABLE OF ty_region_filter WITH UNIQUE KEY region.
lt_regions = VALUE #( ( region = 'NORTH' ) ( region = 'SOUTH' )).
DATA(lt_filtered_sales) = FILTER #( lt_sales USING KEY by_region IN lt_regions WHERE region = region).10. Combination with Other Expressions
" Combine with VALUE and FORDATA(lt_processed) = VALUE ty_result_tab( FOR ls_item IN FILTER #( lt_items USING KEY by_type_prio WHERE type = 'A' ) ( id = ls_item-id description = |Item { ls_item-id } - Prio { ls_item-priority }| )).
" With REDUCE for aggregationDATA(lv_total) = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR ls_order IN FILTER #( lt_orders USING KEY by_status WHERE status = 'COMPLETED' ) NEXT sum = sum + ls_order-amount).11. Performance Comparison
" === INEFFICIENT: LOOP without index ===DATA: lt_result TYPE TABLE OF ty_order.LOOP AT lt_orders INTO ls_order WHERE status = 'OPEN'. APPEND ls_order TO lt_result.ENDLOOP." → Linear scan O(n)
" === EFFICIENT: FILTER with key ===DATA(lt_result2) = FILTER #( lt_orders USING KEY by_status WHERE status = 'OPEN')." → Uses index for fast access O(log n) to O(1)When to Use FILTER?
| Situation | Recommendation |
|---|---|
| Sorted/Hashed table available | FILTER #() |
| Filter by key fields | FILTER #() |
| Standard table without key | LOOP AT … WHERE |
| Complex conditions (not in key) | LOOP AT … WHERE |
| Large data volumes with index | FILTER #() |
| Filter with IN table | FILTER … IN |
FILTER vs. Alternatives
" 1. FILTER #() - Requires sorted/hashed keyDATA(lt_a) = FILTER #( lt_data USING KEY k WHERE status = 'X' ).
" 2. LOOP AT ... WHERE - More flexible, but potentially slowerLOOP AT lt_data INTO ls_data WHERE status = 'X'. APPEND ls_data TO lt_b.ENDLOOP.
" 3. FOR ... WHERE - Flexible, usable inlineDATA(lt_c) = VALUE ty_tab( FOR ls IN lt_data WHERE ( status = 'X' ) ( ls )).
" 4. REDUCE with COND - For complex filter logicDATA(lt_d) = REDUCE ty_tab( INIT result = VALUE ty_tab( ) FOR ls IN lt_data NEXT result = COND #( WHEN ls-status = 'X' THEN VALUE #( BASE result ( ls ) ) ELSE result )).Important Notes / Best Practice
FILTERrequires a sorted or hashed key on the filter fields.- Without matching key → syntax error.
USING KEYspecifies which key to use.FILTERis more performant thanLOOP AT WHEREfor large tables with index.INenables filtering against a filter table (similar to SQL IN).EXCEPTinverts the filter logic (exclude instead of include).- Combine with
VALUE,FORandREDUCE. - For standard tables without key, use
LOOP AT ... WHERE. - The filter table for
INmust also be sorted or hashed. FILTERcreates a copy – the original table remains unchanged.