ABAP FILTER : Filtrer les tables internes

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

L’expression FILTER crée une copie filtrée d’une table interne. C’est l’alternative moderne et performante à LOOP AT ... WHERE avec APPEND. Prérequis : La table source nécessite une clé triée ou hachée.

Syntaxe

1. Filtrer avec condition WHERE

FILTER <type>( <table_source>
[ USING KEY <clé> ]
WHERE <condition>
)

2. Filtrer avec table de filtre (IN)

FILTER <type>( <table_source>
[ USING KEY <clé> ]
IN <table_filtre>
WHERE <champ> = <champ_filtre>
)

3. Filtrer avec EXCEPT (NOT IN)

FILTER <type>( <table_source>
[ EXCEPT [ IN <table_filtre> ] ]
WHERE <condition>
)

Prérequis

Important : FILTER nécessite une clé triée ou hachée sur les champs de filtre :

" SORTED TABLE avec clé
DATA: lt_data TYPE SORTED TABLE OF ty_data
WITH UNIQUE KEY id.
" STANDARD TABLE avec clé triée secondaire
DATA: lt_data TYPE STANDARD TABLE OF ty_data
WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.

Exemples

1. Filtrage de base avec WHERE

TYPES: BEGIN OF ty_order,
order_id TYPE i,
status TYPE string,
amount TYPE p DECIMALS 2,
END OF ty_order.
" Table triée requise
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' )
).
" Filtrer uniquement les commandes ouvertes
DATA(lt_open_orders) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN"
).
" Résultat : order_id 1, 3, 5
LOOP AT lt_open_orders INTO DATA(ls_order).
WRITE: / ls_order-order_id, ls_order-amount.
ENDLOOP.

2. Comparaison : FILTER vs. LOOP AT WHERE

" CLASSIQUE : Avec LOOP et 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.
" MODERNE : Avec FILTER (plus performant sur grandes tables)
DATA(lt_result2) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN"
).

3. Filtrer avec table de filtre (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 = 'Produit 1' )
( product_id = 2 category = 'B' name = 'Produit 2' )
( product_id = 3 category = 'A' name = 'Produit 3' )
( product_id = 4 category = 'C' name = 'Produit 4' )
( product_id = 5 category = 'B' name = 'Produit 5' )
).
" Table de filtre avec catégories souhaitées
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' )
).
" Filtrer les produits dont la catégorie est dans lt_filter
DATA(lt_filtered) = FILTER #( lt_products
USING KEY by_cat
IN lt_filter
WHERE category = category
).
" Résultat : Produits avec catégorie A et B (product_id 1, 2, 3, 5)

4. EXCEPT – Exclure au lieu d’inclure

" Produits SAUF catégorie A et B
DATA(lt_except) = FILTER #( lt_products
USING KEY by_cat
EXCEPT IN lt_filter
WHERE category = category
).
" Résultat : Uniquement produit 4 (Catégorie C)

5. EXCEPT avec WHERE (sans table de filtre)

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 = 'RH' active = abap_false )
( emp_id = 3 department = 'IT' active = abap_true )
( emp_id = 4 department = 'Ventes' active = abap_false )
).
" Tous les employés NON actifs
DATA(lt_inactive) = FILTER #( lt_employees
USING KEY by_active
EXCEPT WHERE active = abap_true
).
" Résultat : emp_id 2, 4

6. Conditions multiples

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 )
).
" Filtrer par type ET priorité
DATA(lt_high_prio_a) = FILTER #( lt_items
USING KEY by_type_prio
WHERE type = 'A' AND priority = 1
).
" Résultat : id 1, 4

7. FILTER avec table standard et clé secondaire

" Table standard avec clé secondaire
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 utilise la clé secondaire
DATA(lt_new) = FILTER #( lt_data
USING KEY k_status
WHERE status = 'NEW"
).

8. FILTER dans des appels de méthode

METHODS: process_orders
IMPORTING it_orders TYPE ty_orders.
" Passer directement des données filtrées
process_orders(
it_orders = FILTER #( lt_all_orders
USING KEY by_status
WHERE status = 'PENDING"
)
).

9. FILTER avec table de ranges

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 = 'NORD' revenue = 1000 )
( sales_id = 2 region = 'SUD' revenue = 2000 )
( sales_id = 3 region = 'EST' revenue = 1500 )
( sales_id = 4 region = 'NORD' revenue = 1800 )
).
" Table de filtre avec régions
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 = 'NORD' )
( region = 'SUD' )
).
DATA(lt_filtered_sales) = FILTER #( lt_sales
USING KEY by_region
IN lt_regions
WHERE region = region
).

10. Combinaison avec d’autres expressions

" Combiner avec VALUE et 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 = |Article { ls_item-id } - Prio { ls_item-priority }|
)
).
" Avec REDUCE pour agrégation
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. Comparaison de performances

" === INEFFICACE: LOOP sans 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.
" → Parcours linéaire O(n)
" === EFFICACE: FILTER avec clé ===
DATA(lt_result2) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'OPEN"
).
" → Utilise l'index pour accès rapide O(log n) à O(1)

Quand utiliser FILTER ?

SituationRecommandation
Table triée/hachée disponibleFILTER #()
Filtrer sur champs de cléFILTER #()
Table standard sans cléLOOP AT … WHERE
Conditions complexes (pas dans la clé)LOOP AT … WHERE
Grandes quantités de données avec indexFILTER #()
Filtrer avec table INFILTER … IN

FILTER vs. Alternatives

" 1. FILTER #() - Nécessite clé triée/hachée
DATA(lt_a) = FILTER #( lt_data USING KEY k WHERE status = 'X' ).
" 2. LOOP AT ... WHERE - Plus flexible, mais potentiellement plus lent
LOOP AT lt_data INTO ls_data WHERE status = 'X'.
APPEND ls_data TO lt_b.
ENDLOOP.
" 3. FOR ... WHERE - Flexible, utilisable inline
DATA(lt_c) = VALUE ty_tab(
FOR ls IN lt_data WHERE ( status = 'X' )
( ls )
).
" 4. REDUCE avec COND - Pour logique de filtre complexe
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
)
).

Remarques importantes / Bonnes pratiques

  • FILTER nécessite une clé triée ou hachée sur les champs de filtre.
  • Sans clé appropriée → Erreur de syntaxe.
  • USING KEY spécifie quelle clé utiliser.
  • FILTER est plus performant que LOOP AT WHERE sur grandes tables avec index.
  • IN permet de filtrer contre une table de filtre (similaire à SQL IN).
  • EXCEPT inverse la logique de filtre (exclure au lieu d’inclure).
  • Combinez avec VALUE, FOR et REDUCE.
  • Pour les tables standard sans clé, utilisez LOOP AT ... WHERE.
  • La table de filtre avec IN doit également être triée ou hachée.
  • FILTER crée une copie – la table originale reste inchangée.