Performance problems in ABAP Cloud? These 10 Quick Wins bring immediate improvements - with code examples.
Performance Mindset
Golden Rules:
- Measure First - Don’t guess, measure!
- DB > ABAP - Push work to database
- Batch > Loop - Bundle operations
- Cache Smart - But don’t over-optimize
1. Avoid SELECT Without WHERE
Bad (loads EVERYTHING)
SELECT * FROM I_Customer INTO TABLE @DATA(lt_customers).
LOOP AT lt_customers INTO DATA(ls_customer) WHERE Country = 'DE'. " ProcessingENDLOOP.Problem: 1M rows loaded, 950k discarded!
Good (WHERE in DB)
SELECT Customer, CustomerName, Country FROM I_Customer WHERE Country = 'DE' INTO TABLE @DATA(lt_customers).Speedup: ~95% (with 1M rows)
2. Avoid SELECT in Loops
Bad (N+1 Problem)
LOOP AT lt_orders INTO DATA(ls_order). " SELECT per iteration! SELECT SINGLE CustomerName FROM I_Customer WHERE Customer = @ls_order-customer INTO @DATA(lv_name).ENDLOOP.With 1000 orders: 1000 SELECT statements!
Good (FOR ALL ENTRIES)
" One SELECT for allSELECT Customer, CustomerName FROM I_Customer FOR ALL ENTRIES IN @lt_orders WHERE Customer = @lt_orders-customer INTO TABLE @DATA(lt_customers).
" Join in memoryLOOP 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 DB call)
3. CDS Views Instead of ABAP Joins
Bad (Join in 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 in ABAPLOOP AT lt_orders ASSIGNING <order>. READ TABLE lt_customers INTO DATA(ls_cust) WITH KEY customer = <order>-customer. " ...ENDLOOP.Good (Join in 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 does join more efficiently)
4. Batch EML Operations
Bad (Loop)
LOOP AT lt_book_ids INTO DATA(lv_id). " MODIFY per ID MODIFY ENTITIES OF zi_book IN LOCAL MODE ENTITY Book UPDATE FIELDS ( Status ) WITH VALUE #( ( BookId = lv_id Status = 'F' ) ).ENDLOOP.
COMMIT ENTITIES.With 100 books: 100x DB access!
Good (Batch)
" One MODIFY for allMODIFY 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 Instead of SELECT *
Bad (all fields)
SELECT * FROM I_Product INTO TABLE @DATA(lt_products).Loads 50 fields, but only uses 3!
Good (only required fields)
SELECT Product, ProductName, Price FROM I_Product INTO TABLE @DATA(lt_products).Speedup: ~60% (less network traffic)
6. Enable Table Buffering
For master data (rarely changes):
@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% (data from memory instead of DB)
BUT: Only for data that rarely changes!
7. FILTER Instead of LOOP + IF
Bad
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.Good (FILTER Expression)
DATA(lt_active) = FILTER #( lt_customers WHERE status = 'ACTIVE' ).Speedup: ~30% (less code, optimized)
8. REDUCE Instead of LOOP for Aggregations
Bad
DATA lv_total TYPE p.
LOOP AT lt_orders INTO DATA(ls_order). lv_total = lv_total + ls_order-amount.ENDLOOP.Good (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 with Associations
Bad (load everything)
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}Problem: Even when orders aren’t needed, always join!
Good (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 // Only expose, no join!}Usage:
" Only CustomerSELECT Customer, CustomerName FROM Z_Customer INTO TABLE @DATA(lt_customers).
" With Orders (only when needed!)SELECT Customer, \_Orders-SalesOrder FROM Z_Customer INTO TABLE @DATA(lt_with_orders).Speedup: ~70% (when orders are rarely needed)
10. Use SQL Trace
Find bottlenecks:
In ADT:
Run->Run Configurations- Tab
Trace Requests - Enable
SQL Trace - Run program
Analyze:
SELECT * FROM mara Duration: 2.5s Rows: 500,000
SELECT matnr FROM mara WHERE mtart = 'FERT' Duration: 0.01s Rows: 1,000Action: Optimize slow SELECTs!
Performance Checklist
Code Review
- No
SELECT * - WHERE clause uses index
- No SELECT in LOOP
- EML batched
- FOR ALL ENTRIES checks for empty table
- CDS Views for joins
- FILTER/REDUCE instead of LOOP
- Buffering for master data
Measurement
- SQL Trace performed
- Performance < 1s for standard ops
- No timeouts
- Memory consumption acceptable
Performance Benchmarks
Example: 10,000 records
| Operation | Before | After | Speedup |
|---|---|---|---|
| SELECT * | 2.5s | 0.3s (fields only) | 88% |
| SELECT in Loop | 15.0s | 0.5s (FOR ALL ENTRIES) | 97% |
| LOOP + IF | 0.8s | 0.2s (FILTER) | 75% |
| ABAP Join | 3.0s | 0.4s (CDS) | 87% |
| EML Loop | 5.0s | 0.6s (Batch) | 88% |
Total: From 26.3s to 2.0s = 92% Speedup!
Summary
Quick Win Ranking:
- SELECT in Loop -> FOR ALL ENTRIES (+90%)
- EML Batching (+85%)
- Use WHERE clause (+95%)
- CDS Views for Joins (+80%)
- Only required fields (+60%)
- Lazy Loading (+70%)
- FILTER/REDUCE (+20-30%)
- Buffering (+95% but niche)
- SQL Trace (finds problems)
- Projection Views (+60%)
Mindset: Use DB power, minimize ABAP work!
See also:
- RAP Tutorial Part 3: Best Practices
- CDS Views Deep Dive
- ABAP Cloud Debugging
Happy Optimizing!