FILTER en ABAP: Filtrar tablas internas eficientemente

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

La expresion FILTER permite filtrar tablas internas de forma declarativa y eficiente. Es una alternativa moderna a LOOP AT ... WHERE con APPEND y utiliza claves de tabla para mejor rendimiento.

Sintaxis

FILTER <tipo_tabla>( <tabla_origen>
[ USING KEY <clave> ]
[ WHERE <condicion> ]
[ EXCEPT WHERE <condicion> ]
).

Requisitos

  • La tabla de origen debe tener una clave de tabla (sorted o hashed)
  • Los campos de filtro deben ser parte de una clave
  • El resultado tiene el mismo tipo de linea

Ejemplos

1. Filtro basico con WHERE

TYPES: BEGIN OF ty_order,
order_id TYPE i,
status TYPE c LENGTH 1,
amount TYPE p DECIMALS 2,
END OF ty_order,
" Tabla sorted para FILTER
ty_orders TYPE SORTED TABLE OF ty_order
WITH UNIQUE KEY order_id
WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.
DATA: lt_orders TYPE ty_orders.
lt_orders = VALUE #(
( order_id = 1 status = 'O' amount = '100.00' )
( order_id = 2 status = 'C' amount = '200.00' )
( order_id = 3 status = 'O' amount = '150.00' )
( order_id = 4 status = 'X' amount = '300.00' )
( order_id = 5 status = 'O' amount = '250.00' )
).
" Filtrar solo pedidos abiertos (status = 'O')
DATA(lt_open_orders) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'O'
).
" Resultado: ordenes 1, 3, 5

2. EXCEPT WHERE (excluir registros)

" Obtener todos EXCEPTO los cancelados
DATA(lt_active_orders) = FILTER #( lt_orders
USING KEY by_status
EXCEPT WHERE status = 'X'
).
" Resultado: ordenes 1, 2, 3, 5 (todas menos status 'X')

3. Filtro con multiples componentes de clave

TYPES: BEGIN OF ty_item,
plant TYPE c LENGTH 4,
storage TYPE c LENGTH 4,
material TYPE c LENGTH 10,
quantity TYPE i,
END OF ty_item,
ty_items TYPE SORTED TABLE OF ty_item
WITH UNIQUE KEY plant storage material
WITH NON-UNIQUE SORTED KEY by_plant_storage
COMPONENTS plant storage.
DATA: lt_items TYPE ty_items.
lt_items = VALUE #(
( plant = '1000' storage = 'LG01' material = 'MAT001' quantity = 10 )
( plant = '1000' storage = 'LG01' material = 'MAT002' quantity = 20 )
( plant = '1000' storage = 'LG02' material = 'MAT001' quantity = 15 )
( plant = '2000' storage = 'LG01' material = 'MAT001' quantity = 25 )
).
" Filtrar por planta y almacen
DATA(lt_filtered) = FILTER #( lt_items
USING KEY by_plant_storage
WHERE plant = '1000' AND storage = 'LG01'
).
" Resultado: MAT001 y MAT002 de 1000/LG01

4. Comparacion: FILTER vs LOOP WHERE

" === CLASICO CON LOOP ===
DATA: lt_result_loop TYPE ty_orders.
LOOP AT lt_orders INTO DATA(ls_order) WHERE status = 'O'.
APPEND ls_order TO lt_result_loop.
ENDLOOP.
" === MODERNO CON FILTER ===
DATA(lt_result_filter) = FILTER #( lt_orders
USING KEY by_status
WHERE status = 'O'
).
" FILTER es mas corto y potencialmente mas rapido
" (usa la clave para busqueda optimizada)

5. FILTER con tabla como filtro

TYPES: ty_status_filter TYPE SORTED TABLE OF c LENGTH 1
WITH UNIQUE KEY table_line.
DATA: lt_valid_status TYPE ty_status_filter.
" Estados validos a filtrar
lt_valid_status = VALUE #( ( 'O' ) ( 'P' ) ).
" Filtrar usando otra tabla
DATA(lt_valid_orders) = FILTER #( lt_orders
USING KEY by_status
WHERE status IN lt_valid_status
).

6. FILTER con EXCEPT y tabla

TYPES: ty_exclude_status TYPE SORTED TABLE OF c LENGTH 1
WITH UNIQUE KEY table_line.
DATA: lt_exclude TYPE ty_exclude_status.
lt_exclude = VALUE #( ( 'X' ) ( 'R' ) ). " Cancelados y rechazados
" Todos EXCEPTO los de la lista
DATA(lt_active) = FILTER #( lt_orders
USING KEY by_status
EXCEPT WHERE status IN lt_exclude
).

7. Combinacion con VALUE

" Crear tabla filtrada y transformada en un paso
DATA(lt_order_ids) = VALUE ty_order_ids(
FOR ls_ord IN FILTER #( lt_orders
USING KEY by_status
WHERE status = 'O'
)
( ls_ord-order_id )
).

8. FILTER encadenado

TYPES: ty_orders_std TYPE STANDARD TABLE OF ty_order WITH DEFAULT KEY.
" Primero filtrar, luego usar resultado
DATA(lt_high_value_open) = VALUE ty_orders_std(
FOR ls_ord IN FILTER #( lt_orders
USING KEY by_status
WHERE status = 'O'
)
WHERE ( amount > 150 )
( ls_ord )
).

9. FILTER con REDUCE

" Sumar montos de pedidos abiertos
DATA(lv_total_open) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_ord IN FILTER #( lt_orders
USING KEY by_status
WHERE status = 'O'
)
NEXT sum = sum + ls_ord-amount
).
WRITE: / 'Total abiertos:', lv_total_open. " 500.00

10. Rendimiento: FILTER vs alternativas

" Para tablas grandes, FILTER con clave es muy eficiente
" CASO 1: Tabla sorted con clave secundaria
" FILTER usa busqueda binaria -> O(log n) + iteracion
DATA(lt_result1) = FILTER #( lt_orders_sorted
USING KEY by_status
WHERE status = 'O'
).
" CASO 2: Tabla hashed
" Acceso directo por hash -> O(1) + iteracion
TYPES: ty_orders_hash TYPE HASHED TABLE OF ty_order
WITH UNIQUE KEY order_id
WITH NON-UNIQUE SORTED KEY by_status COMPONENTS status.
DATA(lt_result2) = FILTER #( lt_orders_hashed
USING KEY by_status
WHERE status = 'O'
).
" CASO 3: Sin clave adecuada -> error de sintaxis
" La tabla DEBE tener una clave que incluya los campos WHERE

11. Crear clave secundaria para FILTER

" Si la tabla no tiene clave adecuada, crear una nueva
TYPES: BEGIN OF ty_product,
prod_id TYPE i,
category TYPE c LENGTH 2,
name TYPE string,
price TYPE p DECIMALS 2,
END OF ty_product,
" Tabla con clave secundaria para filtrar por categoria
ty_products TYPE SORTED TABLE OF ty_product
WITH UNIQUE KEY prod_id
WITH NON-UNIQUE SORTED KEY by_cat COMPONENTS category.
DATA: lt_products TYPE ty_products.
" Ahora FILTER puede usar la clave by_cat
DATA(lt_electronics) = FILTER #( lt_products
USING KEY by_cat
WHERE category = 'EL'
).

12. FILTER sin USING KEY

" Si la clave primaria incluye el campo de filtro
TYPES: ty_by_status TYPE SORTED TABLE OF ty_order
WITH UNIQUE KEY status order_id.
DATA: lt_by_status TYPE ty_by_status.
" USING KEY no es necesario si usa la clave primaria
DATA(lt_open) = FILTER #( lt_by_status
WHERE status = 'O'
).

13. Error comun: Campo no en clave

" ESTO CAUSA ERROR DE SINTAXIS:
" FILTER #( lt_orders
" WHERE amount > 100 " amount no esta en ninguna clave!
" )
" SOLUCION: Usar FOR con WHERE para campos sin clave
DATA(lt_high_amount) = VALUE ty_orders(
FOR ls_ord IN lt_orders
WHERE ( amount > 100 )
( ls_ord )
).

Requisitos de clave

Tipo de tablaRequisito para FILTER
SORTEDCampos WHERE deben estar en clave sorted
HASHEDCampos WHERE deben estar en clave (parcial OK)
STANDARDNo soportado directamente

FILTER vs Alternativas

MetodoCuando usar
FILTERCampos en clave, rendimiento critico
FOR ... WHERECampos no en clave, transformacion
LOOP WHERELogica compleja, modificaciones

Notas importantes / Mejores practicas

  • La tabla de origen debe tener una clave sorted o hashed.
  • Los campos en WHERE deben ser parte de una clave.
  • USING KEY especifica cual clave usar (importante si hay varias).
  • EXCEPT WHERE invierte la logica de filtro.
  • Para campos sin clave, usar FOR ... WHERE en lugar de FILTER.
  • FILTER es mas eficiente que LOOP para grandes volumenes de datos.
  • El resultado tiene el mismo tipo de linea que la tabla origen.
  • Considerar agregar claves secundarias para filtros frecuentes.