ABAP Performance — S/4HANA / HANA Database
These rules apply to SAP S/4HANA systems or any ABAP system running on HANA DB.
Before writing, optimizing, or reviewing ABAP code, read the abap-code-review-helper skill in full unless its complete contents are already available in your current context. Preserve its correctness safeguards when applying performance recommendations; an optimization must not change result semantics. Keep passed checks internal.
Before using this skill: Call the SAP system info tool. If the system is ECC on a traditional DB, use the abap-performance-ecc skill instead.
Core philosophy on HANA: Push data-intensive operations to the database. HANA is a columnar in-memory DB optimized for set-based operations, aggregations, and complex SQL. Let it do the heavy lifting. Keep ABAP for business logic, authorization, and exception handling.
Code Pushdown — The #1 Rule
Move data-intensive operations to the database layer.
Push down to HANA:
- Aggregations (SUM, COUNT, AVG, MIN, MAX)
- Filtering (WHERE clauses — the more selective, the better)
- Sorting (ORDER BY)
- JOINs (HANA handles complex multi-table JOINs efficiently)
- String operations and arithmetic in SQL
- CASE expressions / conditional logic on data
- Grouping and HAVING
- Window functions (OVER/PARTITION BY)
- UNION / INTERSECT / EXCEPT
Keep in ABAP:
- Complex business logic with many branches
- Authority checks, messages, exceptions
- Small dataset processing where pushdown overhead exceeds benefit
- Operations requiring ABAP runtime features (RFC calls, file I/O, etc.)
Avoid:
- Reading all rows to ABAP and filtering/aggregating in loops
- Using internal tables as intermediate storage for what SQL can do in one statement
- Multiple sequential SELECTs that could be a single JOIN
Database Access
SELECT Patterns
Select only the fields you need. Never SELECT * in production.
SELECT matnr, maktx FROM mara INTO TABLE @DATA(itab).
Always use a WHERE clause. Always use @ escaped host variables.
Use JOINs aggressively — HANA handles complex JOINs very well, even 5+ tables.
SELECT m~matnr, t~maktx, p~werks, p~ekgrp
FROM mara AS m
INNER JOIN makt AS t ON t~matnr = m~matnr AND t~spras = @sy-langu
INNER JOIN marc AS p ON p~matnr = m~matnr
LEFT OUTER JOIN mvke AS s ON s~matnr = m~matnr
WHERE m~mtart = @material_type
INTO TABLE @DATA(materials).
Use aggregate functions and GROUP BY — let HANA calculate:
SELECT werks, SUM( labst ) AS total_stock, COUNT(*) AS item_count
FROM mard
WHERE matnr = @matnr
GROUP BY werks
INTO TABLE @DATA(stock_by_plant).
Use CASE expressions to push conditional logic to the DB:
SELECT matnr,
CASE mtart
WHEN 'FERT' THEN 'Finished'
WHEN 'ROH' THEN 'Raw Material'
ELSE 'Other'
END AS type_text
FROM mara
INTO TABLE @DATA(materials).
Use string functions in SQL:
SELECT matnr, CONCAT( matnr, CONCAT( ' - ', maktx ) ) AS display_text
FROM mara
INNER JOIN makt ON makt~matnr = mara~matnr AND makt~spras = @sy-langu
INTO TABLE @DATA(display_data).
Use FOR ALL ENTRIES when JOINs aren't possible. Always check driver table is not empty.
Use UP TO n ROWS when you only need limited results.
Use subqueries where they simplify logic:
SELECT matnr, maktx FROM mara
WHERE matnr IN ( SELECT matnr FROM marc WHERE werks = @plant )
INTO TABLE @DATA(plant_materials).
CDS Views
- Prefer CDS views for complex data models. They are the primary code pushdown mechanism on HANA.
- CDS views are reusable, testable, and automatically optimized by HANA.
- Use CDS for: complex joins, calculated fields, aggregations, associations, access control.
- Consume CDS views in ABAP via
SELECT FROM zcds_view.
AMDP (ABAP-Managed Database Procedures)
- Use AMDP for very complex calculations that must run entirely on HANA.
- AMDP gives you access to full SQLScript (HANA's procedural SQL language).
- Use when: complex multi-step transformations, heavy string processing, graph operations, or when CDS is insufficient.
- AMDP is NOT portable to other DBs — use only when you're certain the system stays on HANA.
Avoiding Redundant DB Access
Never SELECT the same data twice. Read once, reuse.
Use READ TABLE on internal table buffer instead of SELECT SINGLE in a loop:
SELECT matnr, maktx FROM makt WHERE spras = @sy-langu INTO TABLE @DATA(texts).
" later:
READ TABLE texts WITH KEY matnr = current_matnr INTO DATA(text_line).
On HANA, even redundant DB access is faster than on traditional DBs — but it's still wasteful and adds network overhead.
Table Buffering
Buffering matters less on HANA than ECC because HANA is in-memory. But it still helps for:
Reducing network round-trips between app server and DB server
Avoiding query parsing overhead for tiny lookups
Use SELECT SINGLE on buffered tables — reads from buffer. UP TO 1 ROWS bypasses buffer.
" good — uses buffer
SELECT SINGLE * FROM t001 WHERE bukrs = @bukrs INTO @DATA(company).
" bad — bypasses buffer
SELECT * FROM t001 UP TO 1 ROWS WHERE bukrs = @bukrs INTO @DATA(company).
JOINs, aggregates, GROUP BY, ORDER BY, subqueries bypass the buffer.
Internal Tables
Table Type Selection
Same rules as any ABAP system — this is ABAP runtime, not DB:
- HASHED: O(1) lookup. Large tables, unique key, read-heavy, filled once.
- SORTED: O(log n) lookup. Non-unique key, range access, incremental fill.
- STANDARD: O(n) unless sorted + binary search. Small tables or sequential.
" good — O(1) lookup
DATA materials TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnr.
READ TABLE materials WITH TABLE KEY matnr = input INTO DATA(mat).
Loop Optimization
Bulk Operations
INSERT lines_of for bulk inserts.
VALUE #( FOR ... ) and REDUCE for functional transformations.
CORRESPONDING #( ) for structure mapping.
HANA-Specific: Consider Pushing to SQL
Before writing a complex ABAP loop with aggregation, filtering, or transformation — ask: can this be a SQL statement instead?
" ABAP way (acceptable for small data)
LOOP AT sales ASSIGNING FIELD-SYMBOL(<s>).
AT NEW kunnr.
total = 0.
ENDAT.
total += <s>-netwr.
AT END OF kunnr.
APPEND VALUE #( kunnr = <s>-kunnr total = total ) TO totals.
ENDAT.
ENDLOOP.
" HANA way (better for large data)
SELECT kunnr, SUM( netwr ) AS total
FROM vbak
WHERE erdat >= @from_date
GROUP BY kunnr
INTO TABLE @DATA(totals).
String Operations
Use string templates | | instead of CONCATENATE.
Avoid repeated string concatenation in loops — build a string table:
DATA lines TYPE string_table.
LOOP AT data INTO DATA(d).
APPEND |{ d-field1 };{ d-field2 }| TO lines.
ENDLOOP.
DATA(csv) = concat_lines_of( table = lines sep = cl_abap_char_utilities=>cr_lf ).
HANA-specific: For heavy string assembly from DB data, consider doing it in SQL with CONCAT or STRING_AGG (via CDS/AMDP).
Authorization Checks
Check authority before expensive data retrieval:
AUTHORITY-CHECK OBJECT 'M_MATE_WRK' ID 'WERKS' FIELD plant.
IF sy-subrc <> 0. RAISE EXCEPTION NEW zcx_no_auth( ). ENDIF.
SELECT ... " now fetch
On S/4HANA, consider CDS access control (DCL) for row-level authorization built into the data model.
ALV / UI Performance
- Pass data by reference.
- Use
CL_SALV_TABLE for read-only display.
- For very large result sets, consider pagination.
- On S/4HANA: consider Fiori/RAP for UI instead of classical ALV.
Parallel Processing
aRFC for independent parallel tasks.
SPTA framework for parallelized mass processing.
- Background jobs for very long tasks.
- HANA-specific: Before parallelizing in ABAP, check if the work can be pushed to HANA — a single efficient SQL may outperform parallel ABAP tasks.
HANA Anti-Patterns
| Anti-Pattern |
Fix |
SELECT * |
Select only needed fields |
| SELECT in a LOOP |
JOINs (HANA handles complex JOINs well) |
| Aggregating in ABAP loops |
SUM/COUNT/AVG in SQL with GROUP BY |
| Filtering in ABAP what SQL can filter |
Push WHERE to SQL |
| Multiple SELECTs that could be one JOIN |
Combine into single JOIN statement |
| Nested LOOPs on STANDARD tables |
HASHED lookup for inner data |
| Complex ABAP transformations on large data |
CDS view or AMDP |
String concat in loops with && |
Build string table, or push to SQL |
| Ignoring CDS views |
Use CDS for reusable data models |
UP TO 1 ROWS on buffered table |
SELECT SINGLE to use buffer |
| Authority check after data retrieval |
Check before SELECT, or use CDS DCL |
| Writing ABAP for what SQL can express |
Push to database — always ask "can SQL do this?" |
1---2name: abap-performance-hana3description: ABAP Performance — S/4HANA / HANA Database4---56# ABAP Performance — S/4HANA / HANA Database78These rules apply to SAP S/4HANA systems or any ABAP system running on HANA DB.910Before writing, optimizing, or reviewing ABAP code, read the `abap-code-review-helper` skill in full unless its complete contents are already available in your current context. Preserve its correctness safeguards when applying performance recommendations; an optimization must not change result semantics. Keep passed checks internal.1112**Before using this skill:** Call the SAP system info tool. If the system is ECC on a traditional DB, use the `abap-performance-ecc` skill instead.1314**Core philosophy on HANA:** Push data-intensive operations to the database. HANA is a columnar in-memory DB optimized for set-based operations, aggregations, and complex SQL. Let it do the heavy lifting. Keep ABAP for business logic, authorization, and exception handling.1516---1718## Code Pushdown — The #1 Rule1920**Move data-intensive operations to the database layer.**2122### Push down to HANA:23- Aggregations (SUM, COUNT, AVG, MIN, MAX)24- Filtering (WHERE clauses — the more selective, the better)25- Sorting (ORDER BY)26- JOINs (HANA handles complex multi-table JOINs efficiently)27- String operations and arithmetic in SQL28- CASE expressions / conditional logic on data29- Grouping and HAVING30- Window functions (OVER/PARTITION BY)31- UNION / INTERSECT / EXCEPT3233### Keep in ABAP:34- Complex business logic with many branches35- Authority checks, messages, exceptions36- Small dataset processing where pushdown overhead exceeds benefit37- Operations requiring ABAP runtime features (RFC calls, file I/O, etc.)3839### Avoid:40- Reading all rows to ABAP and filtering/aggregating in loops41- Using internal tables as intermediate storage for what SQL can do in one statement42- Multiple sequential SELECTs that could be a single JOIN4344---4546## Database Access4748### SELECT Patterns4950- Select only the fields you need. Never `SELECT *` in production.51 ```abap52 SELECT matnr, maktx FROM mara INTO TABLE @DATA(itab).53 ```5455- Always use a WHERE clause. Always use `@` escaped host variables.5657- Use JOINs aggressively — HANA handles complex JOINs very well, even 5+ tables.58 ```abap59 SELECT m~matnr, t~maktx, p~werks, p~ekgrp60 FROM mara AS m61 INNER JOIN makt AS t ON t~matnr = m~matnr AND t~spras = @sy-langu62 INNER JOIN marc AS p ON p~matnr = m~matnr63 LEFT OUTER JOIN mvke AS s ON s~matnr = m~matnr64 WHERE m~mtart = @material_type65 INTO TABLE @DATA(materials).66 ```6768- Use aggregate functions and GROUP BY — let HANA calculate:69 ```abap70 SELECT werks, SUM( labst ) AS total_stock, COUNT(*) AS item_count71 FROM mard72 WHERE matnr = @matnr73 GROUP BY werks74 INTO TABLE @DATA(stock_by_plant).75 ```7677- Use CASE expressions to push conditional logic to the DB:78 ```abap79 SELECT matnr,80 CASE mtart81 WHEN 'FERT' THEN 'Finished'82 WHEN 'ROH' THEN 'Raw Material'83 ELSE 'Other'84 END AS type_text85 FROM mara86 INTO TABLE @DATA(materials).87 ```8889- Use string functions in SQL:90 ```abap91 SELECT matnr, CONCAT( matnr, CONCAT( ' - ', maktx ) ) AS display_text92 FROM mara93 INNER JOIN makt ON makt~matnr = mara~matnr AND makt~spras = @sy-langu94 INTO TABLE @DATA(display_data).95 ```9697- Use `FOR ALL ENTRIES` when JOINs aren't possible. **Always check driver table is not empty.**9899- Use `UP TO n ROWS` when you only need limited results.100101- Use subqueries where they simplify logic:102 ```abap103 SELECT matnr, maktx FROM mara104 WHERE matnr IN ( SELECT matnr FROM marc WHERE werks = @plant )105 INTO TABLE @DATA(plant_materials).106 ```107108### CDS Views109110- **Prefer CDS views** for complex data models. They are the primary code pushdown mechanism on HANA.111- CDS views are reusable, testable, and automatically optimized by HANA.112- Use CDS for: complex joins, calculated fields, aggregations, associations, access control.113- Consume CDS views in ABAP via `SELECT FROM zcds_view`.114115### AMDP (ABAP-Managed Database Procedures)116117- Use AMDP for very complex calculations that must run entirely on HANA.118- AMDP gives you access to full SQLScript (HANA's procedural SQL language).119- Use when: complex multi-step transformations, heavy string processing, graph operations, or when CDS is insufficient.120- AMDP is NOT portable to other DBs — use only when you're certain the system stays on HANA.121122### Avoiding Redundant DB Access123124- Never SELECT the same data twice. Read once, reuse.125- Use `READ TABLE` on internal table buffer instead of `SELECT SINGLE` in a loop:126 ```abap127 SELECT matnr, maktx FROM makt WHERE spras = @sy-langu INTO TABLE @DATA(texts).128 " later:129 READ TABLE texts WITH KEY matnr = current_matnr INTO DATA(text_line).130 ```131132- On HANA, even redundant DB access is faster than on traditional DBs — but it's still wasteful and adds network overhead.133134### Table Buffering135136Buffering matters **less** on HANA than ECC because HANA is in-memory. But it still helps for:137- Reducing network round-trips between app server and DB server138- Avoiding query parsing overhead for tiny lookups139140- Use `SELECT SINGLE` on buffered tables — reads from buffer. `UP TO 1 ROWS` bypasses buffer.141 ```abap142 " good — uses buffer143 SELECT SINGLE * FROM t001 WHERE bukrs = @bukrs INTO @DATA(company).144 " bad — bypasses buffer145 SELECT * FROM t001 UP TO 1 ROWS WHERE bukrs = @bukrs INTO @DATA(company).146 ```147148- JOINs, aggregates, GROUP BY, ORDER BY, subqueries **bypass the buffer**.149150---151152## Internal Tables153154### Table Type Selection155156Same rules as any ABAP system — this is ABAP runtime, not DB:157- **HASHED**: O(1) lookup. Large tables, unique key, read-heavy, filled once.158- **SORTED**: O(log n) lookup. Non-unique key, range access, incremental fill.159- **STANDARD**: O(n) unless sorted + binary search. Small tables or sequential.160161```abap162" good — O(1) lookup163DATA materials TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnr.164READ TABLE materials WITH TABLE KEY matnr = input INTO DATA(mat).165```166167### Loop Optimization168169- Use `ASSIGNING FIELD-SYMBOL(<fs>)` for fastest loop processing.170- Use `WHERE` on LOOP — especially on SORTED tables.171- Avoid nested loops O(n*m). Use HASHED lookup for inner data.172- Use `FILTER` for extracting subsets from SORTED/HASHED tables:173 ```abap174 DATA(subset) = FILTER #( sorted_table WHERE status = 'A' ).175 ```176177### Bulk Operations178179- `INSERT lines_of` for bulk inserts.180- `VALUE #( FOR ... )` and `REDUCE` for functional transformations.181- `CORRESPONDING #( )` for structure mapping.182183### HANA-Specific: Consider Pushing to SQL184185Before writing a complex ABAP loop with aggregation, filtering, or transformation — ask: **can this be a SQL statement instead?**186187```abap188" ABAP way (acceptable for small data)189LOOP AT sales ASSIGNING FIELD-SYMBOL(<s>).190 AT NEW kunnr.191 total = 0.192 ENDAT.193 total += <s>-netwr.194 AT END OF kunnr.195 APPEND VALUE #( kunnr = <s>-kunnr total = total ) TO totals.196 ENDAT.197ENDLOOP.198199" HANA way (better for large data)200SELECT kunnr, SUM( netwr ) AS total201 FROM vbak202 WHERE erdat >= @from_date203 GROUP BY kunnr204 INTO TABLE @DATA(totals).205```206207---208209## String Operations210211- Use string templates `| |` instead of CONCATENATE.212- Avoid repeated string concatenation in loops — build a string table:213 ```abap214 DATA lines TYPE string_table.215 LOOP AT data INTO DATA(d).216 APPEND |{ d-field1 };{ d-field2 }| TO lines.217 ENDLOOP.218 DATA(csv) = concat_lines_of( table = lines sep = cl_abap_char_utilities=>cr_lf ).219 ```220221- **HANA-specific:** For heavy string assembly from DB data, consider doing it in SQL with `CONCAT` or `STRING_AGG` (via CDS/AMDP).222223---224225## Authorization Checks226227- Check authority **before** expensive data retrieval:228 ```abap229 AUTHORITY-CHECK OBJECT 'M_MATE_WRK' ID 'WERKS' FIELD plant.230 IF sy-subrc <> 0. RAISE EXCEPTION NEW zcx_no_auth( ). ENDIF.231 SELECT ... " now fetch232 ```233234- On S/4HANA, consider CDS access control (DCL) for row-level authorization built into the data model.235236---237238## ALV / UI Performance239240- Pass data by reference.241- Use `CL_SALV_TABLE` for read-only display.242- For very large result sets, consider pagination.243- On S/4HANA: consider Fiori/RAP for UI instead of classical ALV.244245---246247## Parallel Processing248249- `aRFC` for independent parallel tasks.250- `SPTA` framework for parallelized mass processing.251- Background jobs for very long tasks.252- **HANA-specific:** Before parallelizing in ABAP, check if the work can be pushed to HANA — a single efficient SQL may outperform parallel ABAP tasks.253254---255256## HANA Anti-Patterns257258| Anti-Pattern | Fix |259|---|---|260| `SELECT *` | Select only needed fields |261| SELECT in a LOOP | JOINs (HANA handles complex JOINs well) |262| Aggregating in ABAP loops | SUM/COUNT/AVG in SQL with GROUP BY |263| Filtering in ABAP what SQL can filter | Push WHERE to SQL |264| Multiple SELECTs that could be one JOIN | Combine into single JOIN statement |265| Nested LOOPs on STANDARD tables | HASHED lookup for inner data |266| Complex ABAP transformations on large data | CDS view or AMDP |267| String concat in loops with `&&` | Build string table, or push to SQL |268| Ignoring CDS views | Use CDS for reusable data models |269| `UP TO 1 ROWS` on buffered table | `SELECT SINGLE` to use buffer |270| Authority check after data retrieval | Check before SELECT, or use CDS DCL |271| Writing ABAP for what SQL can express | Push to database — always ask "can SQL do this?" |