Das Verknüpfen von Tabellen in SQL ist einfach – hier erfahren Sie wie es gemacht wird.
von Mike Cravitz
DB2/400 bzw. UDB/400 (DB2 Universal Database for AS/400) unterstützt Join-Operationen durch verschiedene Mechanismen:
- logische Join-Dateien, mit DDS definiert
- die Parameter FILE und JFLD des Befehls OPNQRYF (Open Query File – Abfragedatei eröffnen)
- die FROM- und WHERE-Klauseln von SQL-Subselects
Von allen Möglichkeiten bietet SQL die größte Flexibilität und die einfachste Syntax. SQL ist auch IBMs strategische Datenbank-Oberfläche für UDB/400, und deshalb ist es sicher sinnvoll, wenn man weiß, wie Dateien mit SQL verknüpft werden. Wie Ihnen dieser Artikel zeigen wird, ist der Einstieg gar nicht so schwierig.
Eine klassische Verknüpfung: Kunden und Umsätze
Sehen wir uns ein klassisches Beispiel für eine Verknüpfung an: Jede Zeile einer Umsatz-Tabelle wird mit der zugehörigen Kundenstamm-Zeile verbunden. Abbildung 1 zeigt die Kundenstamm-Tabelle, Abbildung 2 die Umsatz-Tabelle. Zum Verknüpfen dieser beiden Tabellen verwendet man eine SQL-Anweisung wie die folgende:
- Select OrderID, Customer.CustID, Name From Customer Join Sale On Customer.CustID = Sale.CustID
Das Prädikat, das auf das Schlüsselwort On folgt (Customer.CustID = Sale.CustID) wird als Join-Bedingung bezeichnet. Dieses Beispiel verwendet ein sehr einfaches Subselect – so wird der Teil einer SQL-Anweisung bezeichnet, der mit dem Schlüsselwort SELECT beginnt und eine FROM-Klausel zur Angabe der Tabelle(n) oder Sicht(en) enthält, die die Ergebniszeilen liefern. In unserem Beispiel enthält die FROM-Klausel die SQL-Join-Operation, die die Ergebnistabelle in Abbildung 3 produziert. Einfach, nicht wahr? Bevor wir die Join-Syntax diskutieren, wollen wir kurz erklären, wo Sie Subselects einsetzen können, und wo folglich Joins möglich sind. Zunächst einmal können Sie Select-Anweisungen in IBMs interaktiver SQL-Umgebung oder in jedem anderen Ad-hoc-Tool einsetzen, das SQL unterstützt. Das Ergebnis eines Ad-hoc-Selects kann entweder angezeigt, gedruckt oder in eine Ausgabedatei geschrieben werden. Die Select-Anweisung ermöglicht es sogar, mehrere Subselects mit Union-Operationen zu kombinieren und das Ergebnis mit einer Order-By-Klausel zu sortieren. Eine vollständige Beschreibung der Select-Anweisung wollen wir hier nicht liefern, weil sie für das Verständnis von SQL-Joins nicht erforderlich ist. Sie können eine Select-Anweisung (und damit ein Subselect) auch verwenden, wenn Sie einen SQL-Cursor in einem HLL-Programm definieren. Ein Cursor, der in eingebettetem SQL oder in der SQL Stored Procedure Language (SPL) verwendet werden kann, erlaubt es, ausgewählte Zeilen einzeln nacheinander zu holen (und gegebenenfalls zu ändern oder zu löschen). Das einzig wichtige, das Sie sich im Zusammenhang mit unserem Thema merken müssen ist, daß ein Cursor für verknüpfte Tabellen immer als read-only (nur Lesen) definiert ist. Sie können auch mit einem Subselect eine Read-Only-Sicht definieren. Subselects können auch an folgenden Stellen erscheinen:
- in einer Insert-Anweisung, die Werte aus einer oder mehreren Tabellen kopiert und zu einer anderen Tabelle hinzufügt
- in der Set-Klausel einer Update-Anweisung, die einer Spalte einen neuen Wert zuweist
- als Teil einer Suchbedingung in der Where-Klausel einer Datenmanipulations-Anweisung
Grundlegende Join-Syntax
Joins können in mehrere Kategorien eingeteilt werden: Wichtigstes Unterscheidungsmerkmal ist die Einteilung in Equijoins und Non-Equijoins. Equijoins sind der gebräuchlichere Typ; sie kombinieren Zeilen basierend auf gleichen Spalteninhalten. Das oben erwähnte Beispiel mit Kunden und Umsätzen ist ein Equijoin. Im Gegensatz dazu basieren Non-Equijoins auf Ungleichheiten zwischen Spalten. Wir wollen uns zunächst mit Equijoins beschäftigen und untergliedern sie weiter in
- Inner Equijoins
- Left Outer Equijoins
- Exception Equijoins
Diese drei Kategorien unterscheiden sich dadurch, wie gleiche und ungleiche Zeilen behandelt weden (siehe Abbildung 4). Abbildung 5 zeigt ein Beispiel für einen Left Outer Join, der aus allen Zeilen eines einfachen Joins und allen Zeilen der ersten (linken) Tabelle (in unserem Fall der Kundentabelle) besteht, für die keine Entsprechung in der zweiten Tabelle gefunden wurde. Wie Sie in der Abbildung sehen können, setzt SQL in allen Zeilen ohne Entsprechung die Spalten der zweiten Tabelle auf Null. Während der einfache Join in Abbildung 3 Informationen über alle Kunden liefert, die einen Auftrag plaziert haben, liefert der Left Outer Join Informationen über alle Kunden, unabhängig davon, ob sie geordert haben oder nicht. Abbildung 6 zeigt ein Beispiel für einen Exception Join, der nur die Zeilen aus der ersten Tabelle enthält, für die keine Entsprechung in der zweiten Tabelle existiert.
In unserem Beispiel entsteht daraus eine Liste aller Kunden, die keinen Auftrag geschrieben haben. Beachten Sie, daß SQL auch hier die Spalten der zweiten Tabelle auf Null setzt. Obwohl unser Beispiel Spalten für Auftragsnummer und Auftragsdatum enthält, werden normalerweise keine Spalten aus der rechten Tabelle eines Exception-Joins im Ergebnis verwendet, weil diese ohnehin Nullwerte enthalten. Vielleicht fragen Sie sich, warum es keine Right Outer Joins gibt? Sie werden ganz einfach nicht benötigt, weil das gleiche Ergebnis auch dadurch erreicht werden kann, daß man die linke und die rechte Tabelle vertauscht und einen Left Outer Join verwendet. Und wie wäre es mit einem Full Outer Join, der alle Zeilen aus beiden Tabellen mit enthält, für die jeweils keine Entsprechung in der anderen Tabelle existiert? Dazu verwenden Sie einfach den SQL-Operator UNION und kombinieren zwei Ergebnistabellen, wie in Abbildung 7 dargestellt. In unserem Beispiel kombiniert die UNION-Operation alle Kundenstamm-Zeilen (aus dem Left Outer Join) und alle „verwaisten“ Auftragszeilen (aus dem Exception Join) und liefert uns so das gewünschte Ergebnis.
Mehrere Spalten verwenden
Manchmal werden Sie Werte aus zwei oder mehr Spalten aufeinander beziehen müssen. Als Beispiel verwenden wir die Tabellen Item (Artikelstamm) und VendXrItem (Artikel/Lieferanten-Referenz) in Abbildung 8 und 9. In diesem Beispiel benötigen wir die zwei Spalten ItemID (Artikelnummer) und ItemColor (Farbe), um einen Artikel eindeutig zu identifizieren. Die Tabelle VendXrItem stellt dar, welcher Lieferant welche Artikel liefert. Abbildung 10 zeigt eine einfache Verknüpfung dieser beiden Tabellen. Wie Sie sehen, benötigen wir den logischen Operator AND, um die Join-Bedingung vollständig formulieren zu können.
Verknüpfen einer Tabelle mit sich selbst
Es kann auch sinnvoll sein, eine Tabelle mit sich selbst zu verknüpfen. Abbildung 11 zeigt eine Personal-Tabelle, in der die Spalte MgrEmpID die Personalnummer des Vorgesetzten eines Mitarbeiters enthält. Abbildung 12 zeigt, wie wir durch eine Verknüpfung der Personal-Tabelle mit sich selbst eine Ergebnistabelle erstellen können, die den Vornamen jedes Mitarbeiters und den Vornamen seines Vorgesetzten (oder Null, wenn er keinen Vorgesetzten hat) enthält. Beachten Sie in der FROM-Klausel in Abbildung 12 die Verwendung der Korrelationsnamen Emp und Mgr zum Unterscheiden der beiden Vorkommen der Personal-Tabelle in diesem Subselect. Die Join-Bedingung sagt aus, daß die Vorgesetzten-Personalnummer (Emp.MgrEmpID) in einem Mitarbeiter-Stammsatz der Personalnummer im Stammsatz des Vorgesetzten (Mgr.EmpID) entsprechen muß. Diese Bedingung bewirkt, daß jede Personalstamm-Zeile mit der Zeile des jeweiligen Vorgesetzten verknüpft wird. Da ein Left Outer Join verwendet wird, enthält die Ergebnistabelle auch Mitarbeiter ohne Vorgesetzten. Verknüpfen von drei und mehr Tabellen
Als Beispiel für eine Verknüpfung von drei Tabellen wollen wir die Rechnungs-Tabelle in Abbildung 13 mit der Kunden- und der Auftrags-Tabelle aus Abbildung 1 und 2 verknüpfen. Abbildung 14 zeigt die Verknüpfung dieser drei Tabellen. Die Join-Bedingung stellt einfach die natürliche Beziehung zwischen diesen drei Tabellen dar: Die Kundennummer in der Kunden-Tabelle (Customer.CustID) muß mit der Kundennummer in der Auftrags-Tabelle (Sale.CustID) übereinstimmen, und die Auftragsnummer in der Auftrags-Tabelle (Sale.OrderID) muß mit der Auftragsnummer in der Rechnungs-Tabelle (Invoice.OrderID) übereinstimmen. Sehen Sie sich die FROM-Klausel in diesem Beispiel genau an. Die Syntax kann ein wenig verwirrend sein. Die Klammern sind notwendig, um die Join-Bedingung für die ersten beiden Tabellen (Customer und Sale) abzugrenzen. Die Anweisung erstellt eine Tabelle mit einem Zwischenergebnis aus der Verknüpfung der Kunden- und der Auftrags-Tabelle. Mit der zweiten Join-Bedingung wird dieses Zwischenergebnis mit der dritten Tabelle (Invoice) verknüpft.Non-EquijoinsNon-Equijoins verknüpfen Zeilen auf der Basis von Ungleichheiten zwischen Spalteninhalten. Sehen wir uns ein Beispiel an: Abbildung 15 zeigt eine Tabelle für Werbekampagnen, die besondere Werbepreise für einen bestimmten Zeitraum festlegt. In dem abgebildeten Beispiel gilt für alle Aufträge, die zwischen dem 1. und dem 15. Mai 1999 eingehen, ein Rabatt von 5 Prozent und für alle Aufträge, die zwischen dem 1. und dem 10. Juli eingehen, ein Preisnachlaß von 10 Prozent. Abbildung 16 zeigt einen Non-Equijoin (die Join-Bedingung basiert auf Ungleichheiten) der Tabellen Sale und Promotion, der eine Ergebnistabelle mit allen Aufträgen liefert, auf die ein Preisnachlaß gewährt wird.Cross-JoinsZusätzlich zu den bereits besprochenen Join-Typen enthält SQL ein „syntaktisches Zuckerstück“ mit der Bezeichnung Cross-Join. Die folgende FROM-Klausel zeigt ein Beispiel für einen Cross-Join:… From Customer Cross Join SaleDiese Klausel bewirkt exakt dasselbe wie die einfache Auflistung der Tabellen in der FROM-Klausel:… From Customer, SaleBei der Cross-Join-Syntax wird keine Verknüpfungs-Bedingung kodiert, weil das Ergebnis einfach die Kombination aller Zeilen der ersten Tabelle mit allen Zeilen der zweiten Tabelle darstellt. Wenn z.B. die Kunden-Tabelle 20 Zeilen enthielte und die Auftrags-Tabelle 100 Zeilen, so würde die Ergebnistabelle eines Cross-Joins 2000 Zeilen enthalten. Unabhängig davon, ob Sie die Cross-Join-Syntax verwenden oder einfach mehrere Tabellen in der FROM-Klausel aufzählen, können Sie eine WHERE-Klausel angeben, um festzulegen, welche der kombinierten Zeilen die Ergebnistabelle enthalten soll. Neben der Tatsache, daß er keinerlei Funktionalität bereitstellt, trägt der Cross-Join auch einen falschen Namen: Die eigentliche relationale Operation kennt man als kartesisches Produkt.
Viel Vergnügen
Wie Sie sehen, ist das Kodieren von Verknüpfungen in SQL recht einfach. Darüber hinaus können Sie SQL-Joins in vielen verschiedenen Umgebungen verwenden – bei Ad-Hoc-Operationen genauso wie in HLL-Programmen oder beim Erstellen von Sichten. Versuchen Sie es einmal. Viel Vergnügen!


