Falle beim Left Outer Join

Häufig sind es die Kleinigkeiten, die einem das Leben schwer machen. Die berühmten letzten 20%. Häufig bemerkt man jedoch gar nicht, dass man überhaupt ein Problem hat, weil alles scheinbar so funktioniert, wie man es sich vorstellt.

Aufgabe: SELECT

Die Aufgabe sollte sein: Selektiere aus Tabelle ZZT1 alle Einträge anhand der gegebenen Selektionskriterien (AREA). Lies zusätzlich mit einem LEFT OUTER JOIN alle Einträge aus Tabelle ZZT2, die über das Feld LINK verknüpft sind hinzu. Einträge aus Tabelle ZZT2 mit gesetztem Löschkennzeichen dürfen nicht berücksichtigt werden.

Demodaten

Die folgenden Demodaten stehen zur Verfügung:

  • Tabelle ZZT1
  • Tabelle ZZT2

Selektionskriterium soll sein: AREA = TEST

Tabelle ZZT1 (links)

KEY1 TEXT LINK AREA
1 Eins A TEST
2 Zwei B TEST
3 Drei C TEST
4 Vier TEST
5 Fünf A NEU

Tabelle ZTT2 (rechts)

LINK TEXT LOEVM
A Info A
B Info B X
C Info C

Überlegungen

Wenn aus Tabelle ZZT1 alle Einträge selektiert werden, bei denen das Feld AREA mit “TEST” gefüllt ist, dann erwarte ich, dass die folgenden Einträge selektiert werden: 1, 2, 3 und 4.

Zusätzlich muss bei den Einträgen 1 und 3 der LINK_TEXT gefüllt sein, denn diese Einträge verweisen auf Einträge in Tabelle ZZT2, die keine Löschvormerkung haben.

Erster Versuch

Der erste Wurf sieht folgendermaßen aus:

SELECT z1~key1,
       z1~text,
       z1~link,
       z1~area,
       z2~text AS link_text
  FROM zzt1 AS z1
  LEFT OUTER JOIN zzt2 AS z2 ON z1~link = z2~link
  INTO TABLE @DATA(t_data)
 WHERE z1~area  = 'TEST'
   AND z2~loevm = @space.

Der SELECT ist syntaktisch (!) fehlerfrei, liefert jedoch leider ein falsches Ergebnis:

KEY1 TEXT LINK AREA LINK_TEXT
1 Eins A TEST Info A
3 Drei C TEST Info C

Zweiter Versuch

Ohne die Einschränkung über die WHERE-Bedingung auf der rechten Seite (AND z2~loevm = @space) funktioniert die Selektion wie erwartet.

Das ist nicht ganz das, was ich erwartet habe… Ich habe mich dann damit beschäftigt, warum das Ergebnis so aussieht und warum Ergebniszeilen fehlen. Beziehungsweise habe ich zuerst versucht, das richtige Ergebnis zu bekommen. Der richtige Select lautet so:

SELECT z1~key1,
       z1~text,
       z1~link,
       z1~area,
       z2~text AS link_text
  FROM zzt1 AS z1
  LEFT OUTER JOIN zzt2 AS z2 ON z1~link = z2~link
                            AND z2~loevm = @space
  INTO TABLE @DATA(t_data)
 WHERE z1~area = 'TEST'.

Ergebnis:

KEY1 TEXT LINK AREA LINK_TEXT
1 Eins A TEST Info A
2 Zwei B TEST
3 Drei C TEST Info C
4 Vier TEST

Lessons learned

Bei einem LEFT OUTER JOIN darf die WHERE-Bedingung keine Einschränkung auf die rechte Tabelle haben. In der Hilfe zum JOIN steht:

Eine WHERE-Bedingung für eine SELECT-Anweisung mit Joins wirkt auf die durch die Joins gebildete Ergebnismenge.

Ich deute das so, dass die WHERE-Bedingung sozusagen erst nachträglich angewendet wird. Wobei das für mich ein Zirkelschluss ist, den ich nicht verstehe. Zudem enthält die Ergebnismenge ja gar kein Feld “LOEVM”.

Auf jeden Fall sollte man die Verwendung beziehungsweise die gesammelten Daten eines LEFT OUTER JOINS sehr genau prüfen. Schnell schleicht sich hier ein Fehler ein, mit dem man nicht gerechnet hat.

Enno Wulff

COMMENTS

  • <cite class="fn">abapkadabra</cite>

    Ich hätte das erste Ergebnis auch so erwartet, aber es ist auch eine Sache der Sichtweise. Für mich bedeutet die WHERE-Bedingung immer die Entscheidung, ob ein kompletter Datensatz angezeigt wird oder nicht. Es gibt keine WHERE-Bedingung zur Ermittlung eines “halben” Datensatzes. Und das war – soweit ich es verstanden habe – eigentlich deine Anforderung: Anhand einer Bedingung sollte entschieden werden, ob noch über einen JOIN weitere Daten dazu gelesen werden oder nicht, unabhängig davon, ob grundsätzlich der betreffende Datensatz angezeigt wird. Mit dieser Sichtweise macht es auch Sinn die Bedingung im JOIN-Statement zu platzieren.

Comments are closed.