ABAP / FIELD NOTES

What Happens to the WHERE Condition When FOR ALL ENTRIES Gets an Empty Table?

When the driver table of FOR ALL ENTRIES is empty, the entire WHERE is ignored, date conditions included. A fictional example covers where the guard goes, what the result table holds when it stops the query, and the duplicate removal that shrinks totals even when the table has rows.

A SELECT with FOR ALL ENTRIES IN @itab drops its entire WHERE condition when the driver table itab is empty. Not only the comparisons with the driver table disappear, but also any date or status conditions written in the same WHERE. After reading this you will be able to find where this gap is open in your code, decide where the guard belongs, and decide what the result table should hold when the guard stops the query. We will also look at a second trap that shrinks the result even when the driver table has rows.

The example uses the flight tables SPFLI and SFLIGHT from SAP's keyword documentation examples, filled with fictional values. The carrier codes ZA and ZB, the input ZZZ, the dates and the amounts are all invented, and the code was not run on a real system. The behaviour comes from SELECT, FOR ALL ENTRIES in the ABAP Keyword Documentation (latest edition, AS ABAP 816) and the same entry in the 7.50 edition.

What is ignored when the table is empty?

Start with the normal path. Suppose the driver table lt_conn holds two rows, ZA/0100 and ZB/0200. FOR ALL ENTRIES evaluates the whole logical expression of the WHERE once per row, and the result set is the union of those evaluations. The fldate >= @p_from written next to the carrid and connid comparisons is applied each time, so only flights on or after the cut-off date for the two routes come back.

Now suppose the user enters the fictional departure city ZZZ and the first SELECT finds no route. lt_conn is empty. The documentation warns that in this case the entire WHERE condition is ignored. No row is skipped, and after duplicates are removed every row of the table goes into the result. The fldate condition has nothing to do with the driver table, but it sits in the same WHERE, so it goes too. The hope that “there is one more condition, so it will be fine” fails right here.

Some things do not disappear. With implicit client handling on, the condition for the current client (or the client named with USING CLIENT) stays, so the read is limited to that client's data. It is different if implicit client handling was switched off with the obsolete addition CLIENT SPECIFIED and a condition on the client column was written into the WHERE. That condition is part of the WHERE as well, so it is ignored with the rest, and data from all clients is read.

Three cards. With two rows ZA/0100 and ZB/0200 in lt_conn, the whole WHERE is evaluated per row and the results are united. With lt_conn empty, the carrid, connid and fldate conditions all vanish and every SFLIGHT row of the current client is read. The implicit client condition and duplicate removal remain; with CLIENT SPECIFIED all clients are read.
With rows in the driver table the condition is evaluated per row; with none, the whole WHERE is dropped and only the client condition remains.

When the result shrinks even though the table has rows

FOR ALL ENTRIES automatically removes rows that occur more than once in the result, comparing the entire content of each row read. The documentation says this has the same effect as specifying DISTINCT in the selection. That is why the result does not double when the driver table contains the same route twice.

The trouble starts when you add things up. Suppose the fictional SFLIGHT holds three rows for route ZA/0100: 500 on September 1, 500 on September 8 and 650 on September 15. If the SELECT list leaves out fldate and reads only carrid, connid, price, the first two rows become identical and only one remains. The result has two rows, and the amounts total 1,150 instead of 1,650. Reading all the key columns that tell rows apart keeps all three.

Where duplicates are removed is not fixed either. If the statement can be passed to the database as a single SQL statement, the database removes them; if it has to be split into several SQL statements, AS ABAP collects the rows and removes them. In the second case, PACKAGE SIZE, UP TO and OFFSET are applied only after all matching rows have been placed in an internal system table, so they do not reduce the number of rows sent from the database. If that system table exceeds its maximum size, a runtime error occurs. Combined with an empty driver table, a whole table can end up taking this path.

Three fictional SFLIGHT rows (ZA/0100 on 09-01 at 500, 09-08 at 500, 09-15 at 650). Reading only carrid, connid and price makes the first two identical, so one is removed. The result has two rows and totals 1,150 instead of 1,650.
Rows that are identical in every selected column collapse into one. If you total a column, also read the key columns that tell rows apart.

Where does the guard go, and what happens when it stops the query?

The documentation's advice is short: before using an internal table after FOR ALL ENTRIES, check that it is not initial. When a guard exists and things still go wrong, the cause is usually its position. If a DELETE or a filter sits between the guard and the SELECT, the table can become empty after passing the guard. Put the guard directly before the SELECT, after the last change to the driver table.

When the guard skips the SELECT, INTO TABLE never runs, so the result table keeps its previous contents. In code that reuses the same result table inside a loop, rows from the previous pass remain. Decide explicitly in the ELSE branch whether to clear it. The numbers on the example screen below follow this order.

SAP GUI ABAP Editor showing a SELECT on SPFLI, then an IF lt_conn IS NOT INITIAL guard around a FOR ALL ENTRIES SELECT on SFLIGHT, and an ELSE branch that clears the result table, with four numbered markers.
An example editor view with the guard against an empty driver table and its ELSE branch. SPFLI and SFLIGHT are the flight tables used in SAP's documentation examples; the code was not run.

The documentation mentions two more behaviours worth knowing. The driver table is evaluated once per query, so changing its content inside a SELECT loop does not affect the condition. The same internal table may also appear after both FOR ALL ENTRIES and INTO; it is evaluated as the condition first and then overwritten by the result. The documentation example adds that the same selection could be done with a single join in the FROM clause. Where the column types allow it, adding DISTINCT makes the duplicate removal visible in the code.

What differs by release

The sentence that an empty table means the entire WHERE is ignored appears in both the 7.50 documentation and the latest edition. So do the restrictions that it cannot be combined with SINGLE and that ORDER BY is allowed only with PRIMARY KEY on a single table or view.

Some things did change. The 7.50 edition says FOR ALL ENTRIES cannot be combined with SQL expressions, and that STRING, RAWSTRING, LCHR and LRAW columns in the SELECT list are a syntax error in strict mode from 7.40 SP05. The latest edition (816) makes individually specified columns and a lone COUNT( * ) exceptions to the SQL expression rule, treats those long-type columns as something to avoid with a syntax check warning, and gives 7.68 as the release from which this combination is checked in strict mode. INTERSECT and EXCEPT have joined UNION in the list of additions it cannot be combined with. The safe course is to read the edition that matches your system's release.

Three cards. In both versions an empty table means the whole WHERE is ignored, SINGLE and UNION cannot be combined, and ORDER BY allows only PRIMARY KEY. The 7.50 documentation forbids SQL expressions and makes STRING/RAWSTRING columns a strict-mode syntax error from 7.40 SP05. The latest 816 documentation allows single columns and a lone COUNT(*), warns on STRING and similar types with a strict-mode rule from 7.68, and also forbids INTERSECT and EXCEPT.
The empty-table behaviour matches in both versions; the limits on SQL expressions and long string columns differ.

Four things to check in your code

When reviewing an existing program, look at four things wherever FOR ALL ENTRIES appears. First, is there a guard that checks whether the driver table is empty directly before the SELECT? Second, does the code say whether the result table is cleared or kept when the guard stops the query? Third, if a column will be totalled or counted, are the key columns that tell rows apart in the SELECT list too? Fourth, is CLIENT SPECIFIED used? If the fourth answer is yes, an empty driver table reads not one client but all of them.

If you remember only one thing, make it this: the WHERE of a FOR ALL ENTRIES exists only while the driver table has rows. When the table is empty, however many conditions you wrote, it is as if there were none.

END OF NOTEBack to the library
COMMENTS BOX

Add your perspective.

Share a question, another approach, or something you have tried.

Newest first

Checking sign-in…

Loading comments…