ABAP FILTER: Interne Tabellen filtern

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Der FILTER-Ausdruck erstellt eine gefilterte Kopie einer internen Tabelle. Er ist die moderne, performante Alternative zu LOOP AT ... WHERE mit APPEND. Voraussetzung: Die Quelltabelle benötigt einen sortierten oder gehashten Schlüssel.

Syntax

1. Filtern mit WHERE-Bedingung

FILTER <typ>( <quelltabelle>
[ USING KEY <schlüssel> ]
WHERE <bedingung>
)

2. Filtern mit Filtertabelle (IN)

FILTER <typ>( <quelltabelle>
[ USING KEY <schlüssel> ]
IN <filtertabelle>
WHERE <feld> = <filterfeld>
)

3. Filtern mit EXCEPT (NOT IN)

FILTER <typ>( <quelltabelle>
[ EXCEPT [ IN <filtertabelle> ] ]
WHERE <bedingung>
)

Voraussetzungen

Wichtig: FILTER erfordert einen sortierten oder gehashten Schlüssel auf den Filterfeldern:

" SORTED TABLE mit Schlüssel
DATA: lt_data TYPE SORTED TABLE OF ty_data
WITH UNIQUE KEY id.
" STANDARD TABLE mit sekundärem sortierten Schlüssel
DATA: lt_data TYPE STANDARD TABLE OF ty_data
WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.

Beispiele

1. Grundlegendes Filtern mit WHERE

TYPES: BEGIN OF ty_order,
order_id TYPE i,
status TYPE string,
amount TYPE p DECIMALS 2,
END OF ty_order.
" Sortierte Tabelle erforderlich
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' )
).
" Nur offene Bestellungen filtern
DATA(lt_open_orders) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN'
).
" Ergebnis: order_id 1, 3, 5
LOOP AT lt_open_orders INTO DATA(ls_order).
WRITE: / ls_order-order_id, ls_order-amount.
ENDLOOP.

2. Vergleich: FILTER vs. LOOP AT WHERE

" KLASSISCH: Mit LOOP und 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: Mit FILTER (performanter bei großen Tabellen)
DATA(lt_result2) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN'
).

3. Filtern mit Filtertabelle (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 = 'Produkt 1' )
( product_id = 2 category = 'B' name = 'Produkt 2' )
( product_id = 3 category = 'A' name = 'Produkt 3' )
( product_id = 4 category = 'C' name = 'Produkt 4' )
( product_id = 5 category = 'B' name = 'Produkt 5' )
).
" Filtertabelle mit gewünschten Kategorien
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' )
).
" Produkte filtern, deren Kategorie in lt_filter vorkommt
DATA(lt_filtered) = FILTER #( lt_products
USING KEY by_cat
IN lt_filter
WHERE category = category
).
" Ergebnis: Produkte mit Kategorie A und B (product_id 1, 2, 3, 5)

4. EXCEPT – Ausschließen statt Einschließen

" Produkte AUSSER Kategorie A und B
DATA(lt_except) = FILTER #( lt_products
USING KEY by_cat
EXCEPT IN lt_filter
WHERE category = category
).
" Ergebnis: Nur Produkt 4 (Kategorie C)

5. EXCEPT mit WHERE (ohne Filtertabelle)

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 )
).
" Alle NICHT aktiven Mitarbeiter
DATA(lt_inactive) = FILTER #( lt_employees
USING KEY by_active
EXCEPT WHERE active = abap_true
).
" Ergebnis: emp_id 2, 4

6. Mehrere Bedingungen

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 )
).
" Filtern nach Typ UND Priorität
DATA(lt_high_prio_a) = FILTER #( lt_items
USING KEY by_type_prio
WHERE type = 'A' AND priority = 1
).
" Ergebnis: id 1, 4

7. FILTER mit Standard-Tabelle und Sekundärschlüssel

" Standard-Tabelle mit sekundärem Schlüssel
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 nutzt den sekundären Schlüssel
DATA(lt_new) = FILTER #( lt_data
USING KEY k_status
WHERE status = 'NEW'
).

8. FILTER in Methodenaufrufen

METHODS: process_orders
IMPORTING it_orders TYPE ty_orders.
" Direkt gefilterte Daten übergeben
process_orders(
it_orders = FILTER #( lt_all_orders
USING KEY by_status
WHERE status = 'PENDING'
)
).

9. FILTER mit Range-Tabelle

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 )
).
" Filtertabelle mit Regionen
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. Kombination mit anderen Ausdrücken

" Mit VALUE und FOR kombinieren
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 }|
)
).
" Mit REDUCE für 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-Vergleich

" === INEFFIZIENT: LOOP ohne 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.
" → Linearer Durchlauf O(n)
" === EFFIZIENT: FILTER mit Schlüssel ===
DATA(lt_result2) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN'
).
" → Nutzt Index für schnellen Zugriff O(log n) bis O(1)

Wann FILTER verwenden?

SituationEmpfehlung
Sortierte/Gehashte Tabelle vorhandenFILTER #()
Filtern nach SchlüsselfeldernFILTER #()
Standard-Tabelle ohne SchlüsselLOOP AT … WHERE
Komplexe Bedingungen (nicht im Schlüssel)LOOP AT … WHERE
Große Datenmengen mit IndexFILTER #()
Filtern mit IN-TabelleFILTER … IN

FILTER vs. Alternativen

" 1. FILTER #() - Benötigt sortierten/gehashten Schlüssel
DATA(lt_a) = FILTER #( lt_data USING KEY k WHERE status = 'X' ).
" 2. LOOP AT ... WHERE - Flexibler, aber potenziell langsamer
LOOP AT lt_data INTO ls_data WHERE status = 'X'.
APPEND ls_data TO lt_b.
ENDLOOP.
" 3. FOR ... WHERE - Flexibel, inline nutzbar
DATA(lt_c) = VALUE ty_tab(
FOR ls IN lt_data WHERE ( status = 'X' )
( ls )
).
" 4. REDUCE mit COND - Für komplexe Filterlogik
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
)
).

Wichtige Hinweise / Best Practice

  • FILTER erfordert einen sortierten oder gehashten Schlüssel auf den Filterfeldern.
  • Ohne passenden Schlüssel → Syntaxfehler.
  • USING KEY gibt an, welcher Schlüssel verwendet wird.
  • FILTER ist performanter als LOOP AT WHERE bei großen Tabellen mit Index.
  • IN ermöglicht Filtern gegen eine Filtertabelle (ähnlich SQL IN).
  • EXCEPT invertiert die Filterlogik (ausschließen statt einschließen).
  • Kombinieren Sie mit VALUE, FOR und REDUCE.
  • Bei Standard-Tabellen ohne Schlüssel nutzen Sie LOOP AT ... WHERE.
  • Die Filtertabelle bei IN muss ebenfalls sortiert oder gehasht sein.
  • FILTER erstellt eine Kopie – die Originaltabelle bleibt unverändert.