ABAP Performance — ECC / Traditional Database
These rules apply to SAP ECC systems running on traditional databases (Oracle, DB2, MSSQL, MaxDB).
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 S/4HANA or HANA DB, use the abap-performance-hana skill instead.
Core philosophy on ECC: Minimize database round-trips. Keep SQL simple — traditional DBs don't optimize complex expressions well. Buffer aggressively. Move complex logic to ABAP.
Database Access
SELECT Patterns
Select only the fields you need. Never use SELECT * in production code.
" good
SELECT matnr maktx FROM mara INTO TABLE itab.
" bad
SELECT * FROM mara INTO TABLE itab.
Always use a WHERE clause. Never read an entire table without filtering.
Prefer JOINs over nested SELECTs. One round-trip is always better than N.
" good — single round-trip
SELECT m~matnr t~maktx
FROM mara AS m
INNER JOIN makt AS t ON t~matnr = m~matnr
WHERE m~mtart = material_type
AND t~spras = sy-langu
INTO TABLE materials.
BUT keep JOINs simple on ECC. Avoid more than 3-4 table JOINs — traditional DBs may generate poor execution plans. Split into two SELECTs if needed.
Use FOR ALL ENTRIES when JOINs aren't possible. Always check driver table is not empty.
IF itab[] IS NOT INITIAL.
SELECT matnr werks FROM marc
FOR ALL ENTRIES IN itab
WHERE matnr = itab-matnr
INTO TABLE plant_data.
ENDIF.
FOR ALL ENTRIES removes duplicates from results. Add extra key fields if you need duplicates.
Use UP TO n ROWS when you only need a limited result set.
ECC-specific: Avoid subqueries, CASE expressions, and complex SQL functions in SELECT — traditional DBs often create poor plans for these. Fetch data and process in ABAP.
ECC-specific: Be careful with ORDER BY on large result sets — it can be expensive. If you need sorted data, consider fetching into a SORTED table type or sorting in ABAP.
Avoiding Redundant DB Access
Never SELECT the same data twice. Read once, store in internal table, reuse.
Use READ TABLE with key on a buffered internal table instead of SELECT SINGLE in a loop.
" good — read from buffer
SELECT matnr maktx FROM makt WHERE spras = sy-langu INTO TABLE texts.
SORT texts BY matnr.
" later in a loop:
READ TABLE texts WITH KEY matnr = current_matnr BINARY SEARCH INTO text_line.
" bad — SELECT in every loop iteration
LOOP AT items INTO item.
SELECT SINGLE maktx FROM makt WHERE matnr = item-matnr AND spras = sy-langu INTO desc.
ENDLOOP.
When using FOR ALL ENTRIES, populate a HASHED or SORTED table as lookup buffer.
Table Buffering (Critical on ECC)
Buffering is more important on ECC than on HANA because traditional DBs are slower for small lookups.
Know the buffering types:
- Full buffering: entire table on first access. Good for small config tables (T001, T005, etc.).
- Generic buffering: by key prefix. Good for language-dependent tables (T002T, etc.).
- Single-record buffering: individual rows. Good for large tables with single-record access.
Use SELECT SINGLE on buffered tables — it reads from buffer. SELECT ... UP TO 1 ROWS bypasses the buffer.
" good — uses buffer
SELECT SINGLE * FROM t001 WHERE bukrs = bukrs INTO company.
" bad — bypasses buffer
SELECT * FROM t001 UP TO 1 ROWS WHERE bukrs = bukrs INTO company.
Avoid BYPASSING BUFFER unless you need absolute latest DB state.
JOINs, aggregates, DISTINCT, GROUP BY, ORDER BY, subqueries all bypass the buffer. On buffered tables, use simple SELECTs.
ECC tip: For frequently accessed config data, consider reading the full small table into an internal table once (application-level cache) rather than hitting the DB buffer repeatedly.
Indexes
- ECC-specific: Be aware of secondary indexes on tables you SELECT from. Design WHERE clauses to match index fields in order.
- If your SELECT is slow, check if a secondary index exists and whether your WHERE clause uses it.
- On ECC, the optimizer depends more on correct index usage than on HANA where columnar storage helps.
Internal Tables
Table Type Selection
Choose the right table type — this is the single biggest performance lever:
- HASHED: O(1) lookup. Large tables with unique key, read-heavy, filled once.
- SORTED: O(log n) lookup. Large tables, non-unique key, range access, incremental fill.
- STANDARD: O(n) unless sorted + binary search. Small tables or sequential-only.
For lookup tables, always use HASHED or SORTED:
" good — O(1) lookup
DATA materials TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnr.
READ TABLE materials WITH TABLE KEY matnr = input INTO mat.
" bad — O(n) scan
DATA materials TYPE STANDARD TABLE OF mara.
READ TABLE materials WITH KEY matnr = input INTO mat.
Loop Optimization
Use ASSIGNING <fs> for fastest loop processing (no data copy).
Use WHERE on LOOP — especially on SORTED tables (binary search).
Avoid nested loops O(n*m). Use HASHED lookup for inner data:
" good — O(n) with O(1) lookups
DATA texts TYPE HASHED TABLE OF makt WITH UNIQUE KEY matnr spras.
LOOP AT materials ASSIGNING <mat>.
READ TABLE texts WITH TABLE KEY matnr = <mat>-matnr spras = sy-langu INTO text.
ENDLOOP.
Use DELETE ADJACENT DUPLICATES on SORTED tables.
Bulk Operations
- Use
INSERT lines_of itab INTO TABLE target for bulk inserts.
- Use
APPEND LINES OF for STANDARD tables.
- Use
CORRESPONDING #( ) for structure mapping instead of field-by-field loops.
String Operations
Authorization Checks
ALV / UI Performance
- Pass data by reference to ALV to avoid copying large tables.
- Use
CL_SALV_TABLE for read-only display.
- For large result sets (>100k rows), consider pagination.
Parallel Processing
- Use
aRFC for independent long-running parallel tasks.
- Use
SPTA framework for parallelized mass processing.
- Background jobs for very long tasks.
- Each unit must be self-contained — no shared state.
ECC Anti-Patterns
| Anti-Pattern |
Fix |
SELECT * |
Select only needed fields |
| SELECT in a LOOP |
JOINs or FOR ALL ENTRIES |
| Nested LOOPs on STANDARD tables |
HASHED lookup for inner data |
LOOP AT ... WHERE on STANDARD table |
SORTED/HASHED with proper keys |
String concat in loops with && |
Build string table, concat at end |
| Complex SQL (many JOINs, subqueries) |
Simplify SQL, move logic to ABAP |
UP TO 1 ROWS on buffered table |
SELECT SINGLE to use buffer |
| WHERE clause not matching index |
Design WHERE to use secondary indexes |
| Authority check after data retrieval |
Check authorization before SELECT |
| Aggregating in ABAP loops |
Acceptable on ECC if data volume is small; for large volumes, use simple GROUP BY |
1---2name: abap-performance-ecc3description: ABAP Performance — ECC / Traditional Database4---56# ABAP Performance — ECC / Traditional Database78These rules apply to SAP ECC systems running on traditional databases (Oracle, DB2, MSSQL, MaxDB).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 S/4HANA or HANA DB, use the `abap-performance-hana` skill instead.1314**Core philosophy on ECC:** Minimize database round-trips. Keep SQL simple — traditional DBs don't optimize complex expressions well. Buffer aggressively. Move complex logic to ABAP.1516---1718## Database Access1920### SELECT Patterns2122- Select only the fields you need. Never use `SELECT *` in production code.23 ```abap24 " good25 SELECT matnr maktx FROM mara INTO TABLE itab.26 " bad27 SELECT * FROM mara INTO TABLE itab.28 ```2930- Always use a WHERE clause. Never read an entire table without filtering.3132- Prefer JOINs over nested SELECTs. One round-trip is always better than N.33 ```abap34 " good — single round-trip35 SELECT m~matnr t~maktx36 FROM mara AS m37 INNER JOIN makt AS t ON t~matnr = m~matnr38 WHERE m~mtart = material_type39 AND t~spras = sy-langu40 INTO TABLE materials.41 ```4243- BUT keep JOINs simple on ECC. Avoid more than 3-4 table JOINs — traditional DBs may generate poor execution plans. Split into two SELECTs if needed.4445- Use `FOR ALL ENTRIES` when JOINs aren't possible. **Always check driver table is not empty.**46 ```abap47 IF itab[] IS NOT INITIAL.48 SELECT matnr werks FROM marc49 FOR ALL ENTRIES IN itab50 WHERE matnr = itab-matnr51 INTO TABLE plant_data.52 ENDIF.53 ```5455- `FOR ALL ENTRIES` removes duplicates from results. Add extra key fields if you need duplicates.5657- Use `UP TO n ROWS` when you only need a limited result set.5859- **ECC-specific:** Avoid subqueries, CASE expressions, and complex SQL functions in SELECT — traditional DBs often create poor plans for these. Fetch data and process in ABAP.6061- **ECC-specific:** Be careful with `ORDER BY` on large result sets — it can be expensive. If you need sorted data, consider fetching into a SORTED table type or sorting in ABAP.6263### Avoiding Redundant DB Access6465- Never SELECT the same data twice. Read once, store in internal table, reuse.6667- Use `READ TABLE` with key on a buffered internal table instead of `SELECT SINGLE` in a loop.68 ```abap69 " good — read from buffer70 SELECT matnr maktx FROM makt WHERE spras = sy-langu INTO TABLE texts.71 SORT texts BY matnr.72 " later in a loop:73 READ TABLE texts WITH KEY matnr = current_matnr BINARY SEARCH INTO text_line.7475 " bad — SELECT in every loop iteration76 LOOP AT items INTO item.77 SELECT SINGLE maktx FROM makt WHERE matnr = item-matnr AND spras = sy-langu INTO desc.78 ENDLOOP.79 ```8081- When using `FOR ALL ENTRIES`, populate a HASHED or SORTED table as lookup buffer.8283### Table Buffering (Critical on ECC)8485Buffering is **more important on ECC** than on HANA because traditional DBs are slower for small lookups.8687- Know the buffering types:88 - **Full buffering**: entire table on first access. Good for small config tables (T001, T005, etc.).89 - **Generic buffering**: by key prefix. Good for language-dependent tables (T002T, etc.).90 - **Single-record buffering**: individual rows. Good for large tables with single-record access.9192- Use `SELECT SINGLE` on buffered tables — it reads from buffer. `SELECT ... UP TO 1 ROWS` **bypasses the buffer**.93 ```abap94 " good — uses buffer95 SELECT SINGLE * FROM t001 WHERE bukrs = bukrs INTO company.9697 " bad — bypasses buffer98 SELECT * FROM t001 UP TO 1 ROWS WHERE bukrs = bukrs INTO company.99 ```100101- Avoid `BYPASSING BUFFER` unless you need absolute latest DB state.102103- JOINs, aggregates, `DISTINCT`, `GROUP BY`, `ORDER BY`, subqueries **all bypass the buffer**. On buffered tables, use simple SELECTs.104105- **ECC tip:** For frequently accessed config data, consider reading the full small table into an internal table once (application-level cache) rather than hitting the DB buffer repeatedly.106107### Indexes108109- **ECC-specific:** Be aware of secondary indexes on tables you SELECT from. Design WHERE clauses to match index fields in order.110- If your SELECT is slow, check if a secondary index exists and whether your WHERE clause uses it.111- On ECC, the optimizer depends more on correct index usage than on HANA where columnar storage helps.112113---114115## Internal Tables116117### Table Type Selection118119Choose the right table type — this is the single biggest performance lever:120- **HASHED**: O(1) lookup. Large tables with unique key, read-heavy, filled once.121- **SORTED**: O(log n) lookup. Large tables, non-unique key, range access, incremental fill.122- **STANDARD**: O(n) unless sorted + binary search. Small tables or sequential-only.123124For lookup tables, **always use HASHED or SORTED**:125```abap126" good — O(1) lookup127DATA materials TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnr.128READ TABLE materials WITH TABLE KEY matnr = input INTO mat.129130" bad — O(n) scan131DATA materials TYPE STANDARD TABLE OF mara.132READ TABLE materials WITH KEY matnr = input INTO mat.133```134135### Loop Optimization136137- Use `ASSIGNING <fs>` for fastest loop processing (no data copy).138- Use `WHERE` on LOOP — especially on SORTED tables (binary search).139- Avoid nested loops O(n*m). Use HASHED lookup for inner data:140 ```abap141 " good — O(n) with O(1) lookups142 DATA texts TYPE HASHED TABLE OF makt WITH UNIQUE KEY matnr spras.143 LOOP AT materials ASSIGNING <mat>.144 READ TABLE texts WITH TABLE KEY matnr = <mat>-matnr spras = sy-langu INTO text.145 ENDLOOP.146 ```147148- Use `DELETE ADJACENT DUPLICATES` on SORTED tables.149150### Bulk Operations151152- Use `INSERT lines_of itab INTO TABLE target` for bulk inserts.153- Use `APPEND LINES OF` for STANDARD tables.154- Use `CORRESPONDING #( )` for structure mapping instead of field-by-field loops.155156---157158## String Operations159160- Avoid repeated string concatenation in loops — quadratic reallocation.161 ```abap162 " good — build table, concat at end163 DATA lines TYPE string_table.164 LOOP AT data INTO d.165 APPEND |{ d-field1 };{ d-field2 }| TO lines.166 ENDLOOP.167 DATA(csv) = concat_lines_of( table = lines sep = cl_abap_char_utilities=>cr_lf ).168 ```169170---171172## Authorization Checks173174- Check authority **before** expensive data retrieval, not after.175 ```abap176 AUTHORITY-CHECK OBJECT 'M_MATE_WRK' ID 'WERKS' FIELD plant.177 IF sy-subrc <> 0. RAISE EXCEPTION NEW zcx_no_auth( ). ENDIF.178 SELECT ... " now fetch data179 ```180181---182183## ALV / UI Performance184185- Pass data by reference to ALV to avoid copying large tables.186- Use `CL_SALV_TABLE` for read-only display.187- For large result sets (>100k rows), consider pagination.188189---190191## Parallel Processing192193- Use `aRFC` for independent long-running parallel tasks.194- Use `SPTA` framework for parallelized mass processing.195- Background jobs for very long tasks.196- Each unit must be self-contained — no shared state.197198---199200## ECC Anti-Patterns201202| Anti-Pattern | Fix |203|---|---|204| `SELECT *` | Select only needed fields |205| SELECT in a LOOP | JOINs or FOR ALL ENTRIES |206| Nested LOOPs on STANDARD tables | HASHED lookup for inner data |207| `LOOP AT ... WHERE` on STANDARD table | SORTED/HASHED with proper keys |208| String concat in loops with `&&` | Build string table, concat at end |209| Complex SQL (many JOINs, subqueries) | Simplify SQL, move logic to ABAP |210| `UP TO 1 ROWS` on buffered table | `SELECT SINGLE` to use buffer |211| WHERE clause not matching index | Design WHERE to use secondary indexes |212| Authority check after data retrieval | Check authorization before SELECT |213| Aggregating in ABAP loops | Acceptable on ECC if data volume is small; for large volumes, use simple GROUP BY |