ABAP FILTER: Filter Internal Tables

Category
ABAP-Statements
Published
Author
Johannes

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 key
DATA: lt_data TYPE SORTED TABLE OF ty_data
WITH UNIQUE KEY id.
" STANDARD TABLE with secondary sorted key
DATA: 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 required
DATA: 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 orders
DATA(lt_open_orders) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN'
).
" Result: order_id 1, 3, 5
LOOP 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 APPEND
DATA: 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 categories
TYPES: 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_filter
DATA(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 B
DATA(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 employees
DATA(lt_inactive) = FILTER #( lt_employees
USING KEY by_active
EXCEPT WHERE active = abap_true
).
" Result: emp_id 2, 4

6. 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 priority
DATA(lt_high_prio_a) = FILTER #( lt_items
USING KEY by_type_prio
WHERE type = 'A' AND priority = 1
).
" Result: id 1, 4

7. FILTER with Standard Table and Secondary Key

" Standard table with secondary key
DATA: 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 key
DATA(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 directly
process_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 regions
TYPES: 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 FOR
DATA(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 aggregation
DATA(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?

SituationRecommendation
Sorted/Hashed table availableFILTER #()
Filter by key fieldsFILTER #()
Standard table without keyLOOP AT … WHERE
Complex conditions (not in key)LOOP AT … WHERE
Large data volumes with indexFILTER #()
Filter with IN tableFILTER … IN

FILTER vs. Alternatives

" 1. FILTER #() - Requires sorted/hashed key
DATA(lt_a) = FILTER #( lt_data USING KEY k WHERE status = 'X' ).
" 2. LOOP AT ... WHERE - More flexible, but potentially slower
LOOP AT lt_data INTO ls_data WHERE status = 'X'.
APPEND ls_data TO lt_b.
ENDLOOP.
" 3. FOR ... WHERE - Flexible, usable inline
DATA(lt_c) = VALUE ty_tab(
FOR ls IN lt_data WHERE ( status = 'X' )
( ls )
).
" 4. REDUCE with COND - For complex filter logic
DATA(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

  • FILTER requires a sorted or hashed key on the filter fields.
  • Without matching key → syntax error.
  • USING KEY specifies which key to use.
  • FILTER is more performant than LOOP AT WHERE for large tables with index.
  • IN enables filtering against a filter table (similar to SQL IN).
  • EXCEPT inverts the filter logic (exclude instead of include).
  • Combine with VALUE, FOR and REDUCE.
  • For standard tables without key, use LOOP AT ... WHERE.
  • The filter table for IN must also be sorted or hashed.
  • FILTER creates a copy – the original table remains unchanged.