Self Joins

PostgreSQL bietet die Möglichkeit, Tabellen mit sich selbst zu joinen. Self-Joins bieten unzählige Möglichkeiten und sind für viele praktische Anwendungen von essentieller Bedeutung.

Um Self-Joins zu erläutern, legen wir zwei Tabellen an und fügen Daten ein:

test=# CREATE TABLE t_produkt (id int4, name text, price numeric);
CREATE TABLE
test=# CREATE TABLE t_produkt_gruppe (produkt_id int4, gruppe text);
CREATE TABLE
test=# INSERT INTO t_produkt VALUES (1, 'Semmel', '1');
INSERT 4900026 1
test=# INSERT INTO t_produkt VALUES (2, 'Käsestangerl', '2');
INSERT 4900027 1
test=# INSERT INTO t_produkt VALUES (3, 'Kasten', '216');
INSERT 4900028 1
test=# INSERT INTO t_produkt VALUES (4, 'Rose', '2.16');
INSERT 4900029 1
test=# INSERT INTO t_produkt VALUES (5, 'Apfel', '0.45');
INSERT 4900030 1

Neben der Produkttabelle wollen wir noch die Tabelle befüllen, die speichert, in welcher Produktgruppe sich ein Produkt befindet:

test=# INSERT INTO t_produkt_gruppe VALUES (1, 'Lebensmittel');
INSERT 4900031 1
test=# INSERT INTO t_produkt_gruppe VALUES (2, 'Lebensmittel');
INSERT 4900032 1
test=# INSERT INTO t_produkt_gruppe VALUES (5, 'Lebensmittel');
INSERT 4900033 1
test=# INSERT INTO t_produkt_gruppe VALUES (3, 'Möbel');
INSERT 4900034 1
test=# INSERT INTO t_produkt_gruppe VALUES (5, 'Botanik');
INSERT 4900035 1
test=# INSERT INTO t_produkt_gruppe VALUES (4, 'Botanik');
INSERT 4900036 1

Die Fragestellung lautet nun: Welches Produkt befindet sich gleichzeitig in der Produktgruppe 'Botanik' und in der Produktgruppe 'Lebensmittel'. Oft versuchen Programmierer, das Problem mit einer einfachen WHERE-Clause zu lösen:

test=# SELECT produkt_id 
	FROM t_produkt_gruppe 
	WHERE gruppe = 'Lebensmittel'
		AND gruppe = 'Botanik';
 produkt_id
------------
(0 rows)

Das funktioniert in dieser Form nicht, da die erste WHERE-Bedingung bereits alle Datensätze entfernt, die die zweite WHERE-Bedingung noch brauchen könnte.

Nach diesem Rückschlag versuchen Viele dann den folgenden Ansatz:

test=# SELECT produkt_id 
	FROM t_produkt_gruppe 
	WHERE gruppe IN ('Lebensmittel', 'Botanik');
 produkt_id
------------
          1
          2
          5
          5
          4
(5 rows)

Diese Abfrage liefert alle Produkte, die in einer der beiden Gruppen sind. Klarerweise kann es auch zufällig passieren, dass ein Produkt in beiden Gruppen ist wie etwa die '5' in unserem Beispiel - das Beispiel ist aber immer noch nicht korrekt, da eben auch alle gelistet werden, die nur in einer der beiden Gruppen sind.

Korrekt und effizient kann das Problem sinnvollerweise nur mit einem Self-Join gelöst werden:

test=# SELECT t_produkt.name 
	FROM t_produkt, t_produkt_gruppe AS a, t_produkt_gruppe AS b 
	WHERE a.produkt_id = b.produkt_id 
		AND a.gruppe = 'Botanik' 
		AND b.gruppe = 'Lebensmittel' 
		AND a.produkt_id = t_produkt.id;
 name
-------
 Apfel
(1 row)

In der FROM-Clause schreiben wir die Tabelle t_produkt_gruppe doppelt an. Das bedeutet, dass wir so tun, als gäbe es die Tabelle doppelt. Auf der 'linken' Tabelle (also aus 'a') holen wir uns alle Datensätze der Gruppe 'Botanik'. Aus der 'rechten' Tabelle holen wir uns alle Datensätze der Gruppe 'Lebensmittel'. Die beiden Tabellen können über die Spalte 'id' gejoint werden, da es ja darum geht, Produkte zu finden, die in beiden Gruppen (also in beiden Tabellen) zu finden sind.

Um die Ausgabe ein wenig zu verfeinern, schauen wir abschließend noch in der Tabelle t_produkt nach, welchen Namen das Produkt mit der Nummer 5 trägt. Wie Sie sehen können, haben wir einen Apfel gefunden.

Self-Joins sind in der Regel sehr effizient und führen bei sinnvoller Anwendung zu keinen Performance Problemen. Auch in Oracle ist es möglich, Tabellen mit sich selbst zu joinen. Dabei müssen Sie jedoch das Schlüsselwort 'AS' vermeiden, da es in Oracle aus verschiedenen Gründen nicht benötigt wird.


Cybertec Schönig & Schönig GmbH
PostgreSQL support, training, consulting
www.postgresql-support.de