Optimisation de performance ABAP Cloud : 10 Quick Wins (2025)

Catégorie
Performance
Publié
Auteur
Johannes

Des problemes de performance en ABAP Cloud ? Ces 10 Quick Wins apportent des ameliorations immediates - avec des exemples de code.

Etat d’esprit Performance

Regles d’or :

  1. Mesurer d’abord - Ne pas deviner, mesurer !
  2. DB > ABAP - Pousser le travail dans la base de donnees
  3. Batch > Loop - Regrouper les operations
  4. Cache intelligent - Mais ne pas sur-optimiser

1. Eviter SELECT sans WHERE

Mauvais (charge TOUT)

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

Probleme : 1 million de lignes chargees, 950k rejetees !

Bon (WHERE en DB)

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

Acceleration : ~95% (sur 1 million de lignes)


2. Eviter SELECT dans les boucles

Mauvais (Probleme N+1)

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

Avec 1000 commandes : 1000 instructions SELECT !

Bon (FOR ALL ENTRIES)

" Un SELECT pour tous
SELECT Customer, CustomerName
FROM I_Customer
FOR ALL ENTRIES IN @lt_orders
WHERE Customer = @lt_orders-customer
INTO TABLE @DATA(lt_customers).
" Jointure en memoire
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.

Acceleration : ~90% (1000 → 1 appel DB)


3. CDS Views au lieu de joins ABAP

Mauvais (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.

Bon (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).

Acceleration : ~80% (la DB fait le join plus efficacement)


4. Batching des operations EML

Mauvais (Boucle)

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

Avec 100 livres : 100x acces DB !

Bon (Batch)

" Un MODIFY pour tous
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.

Acceleration : ~85%


5. Projection au lieu de SELECT *

Mauvais (tous les champs)

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

Charge 50 champs, mais n’en utilise que 3 !

Bon (uniquement les champs necessaires)

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

Acceleration : ~60% (moins de trafic reseau)


6. Activer le buffering des tables

Pour les donnees de reference (changent rarement) :

@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
}

Acceleration : ~95% (donnees depuis la memoire au lieu de la DB)

MAIS : Uniquement pour les donnees qui changent rarement !


7. FILTER au lieu de LOOP + IF

Mauvais

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.

Bon (Expression FILTER)

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

Acceleration : ~30% (moins de code, optimise)


8. REDUCE au lieu de LOOP pour les agregations

Mauvais

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

Bon (REDUCE)

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

Acceleration : ~20%


9. Lazy Loading avec les Associations

Mauvais (tout charger)

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
}

Probleme : Meme si les commandes ne sont pas necessaires, toujours un join !

Bon (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 // Exposer seulement, pas de join!
}

Utilisation :

" Seulement Customer
SELECT Customer, CustomerName
FROM Z_Customer
INTO TABLE @DATA(lt_customers).
" Avec Orders (seulement si necessaire!)
SELECT Customer, \_Orders-SalesOrder
FROM Z_Customer
INTO TABLE @DATA(lt_with_orders).

Acceleration : ~70% (quand les commandes sont rarement necessaires)


10. Utiliser SQL Trace

Trouver les goulots d’etranglement :

Dans ADT :

  1. RunRun Configurations
  2. Onglet Trace Requests
  3. Activer SQL Trace
  4. Executer le programme

Analyser :

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

Action : Optimiser les SELECTs lents !


Checklist Performance

Revue de code

  • Pas de SELECT *
  • Clause WHERE utilise l’index
  • Pas de SELECT dans LOOP
  • EML en batch
  • FOR ALL ENTRIES verifie table vide
  • CDS Views pour les joins
  • FILTER/REDUCE au lieu de LOOP
  • Buffering pour les donnees de reference

Mesure

  • SQL Trace effectue
  • Performance < 1s pour les ops standard
  • Pas de timeouts
  • Consommation memoire acceptable

Benchmarks de performance

Exemple : 10 000 enregistrements

OperationAvantApresAcceleration
SELECT *2,5s0,3s (seulement champs)88%
SELECT en boucle15,0s0,5s (FOR ALL ENTRIES)97%
LOOP + IF0,8s0,2s (FILTER)75%
Join ABAP3,0s0,4s (CDS)87%
Boucle EML5,0s0,6s (Batch)88%

Total : De 26,3s a 2,0s = 92% d’acceleration !


Resume

Classement des Quick Wins :

  1. SELECT en boucle → FOR ALL ENTRIES (+90%)
  2. Batching EML (+85%)
  3. Utiliser clause WHERE (+95%)
  4. CDS Views pour les joins (+80%)
  5. Seulement les champs necessaires (+60%)
  6. Lazy Loading (+70%)
  7. FILTER/REDUCE (+20-30%)
  8. Buffering (+95% mais niche)
  9. SQL Trace (trouve les problemes)
  10. Projection Views (+60%)

Etat d’esprit : Utiliser la puissance de la DB, minimiser le travail ABAP !


Voir aussi :

Bonne optimisation !