Optimización de rendimiento ABAP Cloud: 10 Quick Wins (2025)

Kategorie
Performance
Veröffentlicht
Autor
Johannes

¿Problemas de rendimiento en ABAP Cloud? Estos 10 Quick Wins traen mejoras inmediatas - con ejemplos de código.

Mentalidad de rendimiento

Reglas de oro:

  1. Medir primero - ¡No adivinar, medir!
  2. DB > ABAP - Empujar trabajo a la base de datos
  3. Batch > Loop - Agrupar operaciones
  4. Cache inteligente - Pero no sobreoptimizar

1. Evitar SELECT sin WHERE

❌ Malo (carga TODO)

SELECT * FROM I_Customer
INTO TABLE @DATA(lt_customers).
LOOP AT lt_customers INTO DATA(ls_customer)
WHERE Country = 'DE'.
" Procesamiento
ENDLOOP.

Problema: ¡1 millón de filas cargadas, 950k descartadas!

✅ Bueno (WHERE en DB)

SELECT Customer, CustomerName, Country
FROM I_Customer
WHERE Country = 'DE'
INTO TABLE @DATA(lt_customers).

Speedup: ~95% (con 1 millón de filas)


2. Evitar SELECT en Loops

❌ Malo (Problema N+1)

LOOP AT lt_orders INTO DATA(ls_order).
" ❌ SELECT por iteración!
SELECT SINGLE CustomerName
FROM I_Customer
WHERE Customer = @ls_order-customer
INTO @DATA(lv_name).
ENDLOOP.

¡Con 1000 pedidos: 1000 sentencias SELECT!

✅ Bueno (FOR ALL ENTRIES)

" Un SELECT para todos
SELECT Customer, CustomerName
FROM I_Customer
FOR ALL ENTRIES IN @lt_orders
WHERE Customer = @lt_orders-customer
INTO TABLE @DATA(lt_customers).
" Join en memoria
LOOP AT lt_orders ASSIGNING FIELD-SYMBOL(<order>).
READ TABLE lt_customers INTO DATA(ls_cust)
WITH KEY customer = <order>-customer.
<order>-customer_name = ls_cust-customername.
ENDLOOP.

Speedup: ~90% (1000 → 1 llamada DB)


3. CDS Views en lugar de Joins ABAP

❌ Malo (Join en ABAP)

SELECT Customer, CustomerName FROM I_Customer
INTO TABLE @DATA(lt_customers).
SELECT SalesOrder, Customer, NetValue FROM I_SalesOrder
INTO TABLE @DATA(lt_orders).
" Join en ABAP ❌
LOOP AT lt_orders ASSIGNING <order>.
READ TABLE lt_customers INTO DATA(ls_cust)
WITH KEY customer = <order>-customer.
" ...
ENDLOOP.

✅ Bueno (Join en CDS)

define view Z_CustomerOrders
as select from I_Customer
inner join I_SalesOrder
on I_Customer.Customer = I_SalesOrder.Customer
{
key I_Customer.Customer,
I_Customer.CustomerName,
I_SalesOrder.SalesOrder,
I_SalesOrder.NetValue
}
SELECT * FROM Z_CustomerOrders
INTO TABLE @DATA(lt_result).

Speedup: ~80% (DB hace join más eficientemente)


4. Agrupar operaciones EML

❌ Malo (Loop)

LOOP AT lt_book_ids INTO DATA(lv_id).
" ❌ MODIFY por ID
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( ( BookId = lv_id Status = 'F' ) ).
ENDLOOP.
COMMIT ENTITIES.

¡Con 100 libros: 100× accesos DB!

✅ Bueno (Batch)

" Un MODIFY para todos
MODIFY ENTITIES OF zi_book IN LOCAL MODE
ENTITY Book
UPDATE FIELDS ( Status )
WITH VALUE #( FOR lv_id IN lt_book_ids
( BookId = lv_id Status = 'F' ) )
FAILED DATA(failed).
COMMIT ENTITIES.

Speedup: ~85%


5. Projection en lugar de SELECT *

❌ Malo (todos los campos)

SELECT * FROM I_Product
INTO TABLE @DATA(lt_products).

¡Carga 50 campos, pero solo usa 3!

✅ Bueno (solo campos necesarios)

SELECT Product, ProductName, Price
FROM I_Product
INTO TABLE @DATA(lt_products).

Speedup: ~60% (menos tráfico de red)


6. Activar Table Buffering

Para datos maestros (rara vez cambia):

@AbapCatalog.buffering.status: #ACTIVE
@AbapCatalog.buffering.type: #FULL
@AbapCatalog.buffering.numberOfKeyFields: 1
define view Z_Country
as select from T005
{
key land1 as Country,
landx as CountryName
}

Speedup: ~95% (datos de memoria en lugar de DB)

PERO: ¡Solo para datos que cambian raramente!


7. FILTER en lugar de LOOP + IF

❌ Malo

DATA lt_active_customers TYPE TABLE OF ty_customer.
LOOP AT lt_customers INTO DATA(ls_customer)
WHERE status = 'ACTIVE'.
APPEND ls_customer TO lt_active_customers.
ENDLOOP.

✅ Bueno (FILTER Expression)

DATA(lt_active) = FILTER #( lt_customers
WHERE status = 'ACTIVE' ).

Speedup: ~30% (menos código, optimizado)


8. REDUCE en lugar de LOOP para agregaciones

❌ Malo

DATA lv_total TYPE p.
LOOP AT lt_orders INTO DATA(ls_order).
lv_total = lv_total + ls_order-amount.
ENDLOOP.

✅ Bueno (REDUCE)

DATA(lv_total) = REDUCE p( INIT sum = 0
FOR ls_order IN lt_orders
NEXT sum = sum + ls_order-amount ).

Speedup: ~20%


9. Lazy Loading con Associations

❌ Malo (cargar todo)

define view Z_CustomerWithOrders
as select from I_Customer
left outer join I_SalesOrder
on I_Customer.Customer = I_SalesOrder.Customer
{
I_Customer.Customer,
I_Customer.CustomerName,
I_SalesOrder.SalesOrder,
I_SalesOrder.NetValue
}

Problema: ¡Incluso cuando no se necesitan pedidos, siempre Join!

✅ Bueno (Association)

define view Z_Customer
as select from I_Customer
association [0..*] to I_SalesOrder as _Orders
on $projection.Customer = _Orders.Customer
{
key Customer,
CustomerName,
_Orders // Solo exponer, ¡sin Join!
}

Uso:

" Solo Customer
SELECT Customer, CustomerName
FROM Z_Customer
INTO TABLE @DATA(lt_customers).
" Con Orders (¡solo cuando sea necesario!)
SELECT Customer, \_Orders-SalesOrder
FROM Z_Customer
INTO TABLE @DATA(lt_with_orders).

Speedup: ~70% (cuando Orders raramente se necesita)


10. Usar SQL Trace

Encontrar cuellos de botella:

En ADT:

  1. RunRun Configurations
  2. Tab Trace Requests
  3. Activar SQL Trace
  4. Ejecutar programa

Analizar:

❌ SELECT * FROM mara
Duration: 2.5s
Rows: 500,000
✅ SELECT matnr FROM mara
WHERE mtart = 'FERT'
Duration: 0.01s
Rows: 1,000

Acción: ¡Optimizar SELECTs lentos!


Checklist de rendimiento

Code Review

  • Sin SELECT *
  • WHERE-Clause usa índice
  • Sin SELECT en LOOP
  • EML agrupado
  • FOR ALL ENTRIES verifica tabla vacía
  • CDS Views para Joins
  • FILTER/REDUCE en lugar de LOOP
  • Buffering para datos maestros

Medición

  • SQL Trace realizado
  • Rendimiento < 1s para operaciones estándar
  • Sin timeouts
  • Consumo de memoria aceptable

Benchmarks de rendimiento

Ejemplo: 10.000 registros

OperaciónAntesDespuésSpeedup
SELECT *2.5s0.3s (solo campos)88%
SELECT en Loop15.0s0.5s (FOR ALL ENTRIES)97%
LOOP + IF0.8s0.2s (FILTER)75%
ABAP Join3.0s0.4s (CDS)87%
EML Loop5.0s0.6s (Batch)88%

Total: De 26.3s a 2.0s = ¡92% Speedup!


Resumen

Ranking de Quick Wins:

  1. ⭐⭐⭐ SELECT en Loop → FOR ALL ENTRIES (+90%)
  2. ⭐⭐⭐ EML Batching (+85%)
  3. ⭐⭐⭐ Usar WHERE-Clause (+95%)
  4. ⭐⭐ CDS Views para Joins (+80%)
  5. ⭐⭐ Solo campos necesarios (+60%)
  6. ⭐⭐ Lazy Loading (+70%)
  7. FILTER/REDUCE (+20-30%)
  8. Buffering (+95% pero nicho)
  9. SQL Trace (encuentra problemas)
  10. Projection Views (+60%)

Mentalidad: ¡Usar poder de la DB, minimizar trabajo ABAP!


Ver también:

¡Happy Optimizing!