# FOR ALL ENTRIES with an empty table: the trap and the fix

> In ABAP SQL, FOR ALL ENTRIES with an empty internal table ignores the entire WHERE condition and reads every row. Why it happens, the guard to add, the duplicate-row trap, and safer alternatives.

By ABAP for Agents · Updated 2026-10-22 · https://abapforagents.com/guides/for-all-entries-empty-table/

## TL;DR

- If the internal table after FOR ALL ENTRIES is empty, the entire WHERE condition is ignored, including conditions that don't use the table. Every row is read.
- Always wrap the SELECT in IF itab IS NOT INITIAL. ... ENDIF.
- FOR ALL ENTRIES removes duplicate result rows. Select all key fields, or rows that happen to be identical are lost.
- An empty ranges table has the same effect: WHERE col IN @range is always true when the range is initial.
- AI assistants often forget the guard, so make it a rule in your Cursor, Copilot or AGENTS.md instructions.

`FOR ALL ENTRIES` is one of the most common patterns in classic ABAP: read some keys into an internal table, then fetch the matching rows from the database in one statement. It is also behind a whole class of production incidents, because of one documented rule that is easy to forget.

## The trap

Here is code an AI assistant (or a tired developer) writes every day:

```abap
SELECT vbeln, posnr, matnr, netwr
  FROM vbap
  FOR ALL ENTRIES IN @orders
  WHERE vbeln = @orders-vbeln
    AND erdat >= @date_from
  INTO TABLE @DATA(items).
```

It activates, the syntax check is happy, and in a test with a few orders it returns exactly what you expect. Now imagine `orders` is empty because an earlier selection found nothing.

SAP's ABAP Keyword Documentation is explicit: **if the internal table is empty, the entire WHERE condition is ignored**. No row of the database table is skipped. That includes `erdat >= @date_from`, which has nothing to do with the internal table. The statement reads every item in VBAP for the current client.

On a development system with a few hundred rows, nobody notices. In production it can mean a very long runtime, a memory dump, or worse, a program that silently processes every document in the system.

## The fix: guard the driver table

The fix is one `IF`:

```abap
IF orders IS NOT INITIAL.
  SELECT vbeln, posnr, matnr, netwr
    FROM vbap
    FOR ALL ENTRIES IN @orders
    WHERE vbeln = @orders-vbeln
      AND erdat >= @date_from
    INTO TABLE @DATA(items).
ENDIF.
```

A few practical points:

- **Put the guard directly around the SELECT.** A check twenty lines earlier stops protecting you as soon as someone adds a `DELETE` in between.
- **Decide what "empty" means for the caller.** If the SELECT is skipped, the target table stays as it was. That's usually what you want, but if the code runs in a loop, clear the target first so old rows don't leak into the next pass.
- **Remove duplicate keys from the driver.** Sorting the driver and deleting adjacent duplicates keeps the generated database statements smaller:

```abap
SORT orders BY vbeln.
DELETE ADJACENT DUPLICATES FROM orders COMPARING vbeln.
```

## The second trap: duplicate rows disappear

The same documentation page says that FOR ALL ENTRIES removes rows that occur more than once in the result set, considering the entire content of the row. It behaves like `SELECT DISTINCT`.

That's harmless when you select the full primary key. It's a silent data bug when you don't:

```abap
" Wrong: no POSNR, so two items with the same net value collapse into one
SELECT vbeln, netwr
  FROM vbap
  FOR ALL ENTRIES IN @orders
  WHERE vbeln = @orders-vbeln
  INTO TABLE @DATA(values).
```

If an order has two items worth 100.00 each, `values` contains one row, and any total you compute from it is wrong. Add the key field and the problem goes away:

```abap
SELECT vbeln, posnr, netwr
  FROM vbap
  FOR ALL ENTRIES IN @orders
  WHERE vbeln = @orders-vbeln
  INTO TABLE @DATA(values).
```

**Rule of thumb:** with FOR ALL ENTRIES, always select all key fields of the table you read.

## Other restrictions worth knowing

FOR ALL ENTRIES doesn't combine freely with the rest of ABAP SQL. According to the keyword documentation:

- It can't be used with `SELECT SINGLE`.
- Aggregate expressions other than `COUNT( * )` aren't allowed in the SELECT list, and `GROUP BY` has no effect.
- `ORDER BY` is only possible as `ORDER BY PRIMARY KEY`, for a single table or view.
- It can't be combined with `UNION`, `INTERSECT` or `EXCEPT`.

These are exactly the details an AI assistant tends to get wrong, because they only show up in the documentation, not in typical example code.

## A related trap: empty ranges tables

Ranges tables have a similar rule. The documentation for `IN range_tab` says that if the ranges table is initial, the expression is always true. With no other conditions, every row is selected:

```abap
" If material_range is initial, this reads all of MARA
SELECT matnr, mtart
  FROM mara
  WHERE matnr IN @material_range
  INTO TABLE @DATA(materials).
```

That is often intended for selection-screen input (an empty select-option means "no restriction"). It's a bug when the range is built in code from a list that turned out to be empty. Guard it the same way, and keep in mind that very large ranges tables can exceed the database's limit on input values, which raises `CX_SY_OPEN_SQL_DB`.

## Alternatives to FOR ALL ENTRIES

FOR ALL ENTRIES isn't wrong: the documentation notes that it can use table buffering and can be more efficient than a join in some cases. But it's not your only option:

- **A join or subquery**, when the keys come from another database table anyway. One statement, no driver table, no empty-table trap.
- **An internal table as a data source** (`SELECT ... FROM @itab`), available from 7.52. Check the keyword documentation for your release and database before relying on it in a join.
- **A CDS view**, on S/4HANA, when the same combination of tables is read in several places.

Whichever you choose, avoid the worst option: a `SELECT SINGLE` inside a `LOOP`.

## Why AI assistants get this wrong

Language models learn from public code, and a lot of public ABAP uses FOR ALL ENTRIES without a guard because the guard sits somewhere else, or because the example was never run on an empty table. The model reproduces the pattern without the context.

The reliable fix is to state the rule where the assistant reads it on every request: "Always guard FOR ALL ENTRIES with `IF itab IS NOT INITIAL`, and select all key fields." Our free lite rules include it in the ABAP SQL rule, together with strict-mode `@` escaping and the `SELECT SINGLE` full-key rule. For more of these patterns, see [ABAP mistakes AI coding assistants make](https://abapforagents.com/guides/ai-abap-mistakes/).

As always, rules reduce repeat mistakes but don't replace review. An ATC run and a unit test with an empty driver table will catch this bug before it reaches production.

## Get the rules

Start with the free rules: ABAP for Agents Lite (MIT), for Cursor, Claude Code, GitHub Copilot, Windsurf and AGENTS.md: https://github.com/abapforagents/abap-for-agents-lite (ZIP: https://github.com/abapforagents/abap-for-agents-lite/releases/latest/download/abap-for-agents-lite.zip).

The full Rules Pack (v1.1) has 300+ rules across 20 topics (adding performance, security, RAP, CDS, OData and Fiori, ABAP Cloud and clean core, LUW and locks, BAPIs, ABAP Unit and S/4HANA migration) plus 8 agent skills, with free updates: 19 € at launch (29 € after), https://abapforagents.com/#pricing
