Zurück zur Artikelliste Artikel
11 Leseminuten

Was ist ein Self Join in SQL? Eine Erläuterung mit sieben Beispielen

Der Self-Join ist ein beliebter Spezialfall des SQL JOIN. Während die meisten JOINs zwei oder mehr Tabellen miteinander verknüpfen, um ihre Daten gemeinsam darzustellen, verknüpft ein Self-Join eine Tabelle mit sich selbst. Normalerweise wird eine Tabelle nur einmal innerhalb einer SQL-Abfrage mit sich selbst verknüpft, aber es ist auch möglich, dies mehrmals innerhalb derselben Abfrage zu tun.

Normalerweise speichert jede Tabelle in einer Datenbank eine bestimmte Art von Informationen. Daher gibt es in einer Datenbank oft Hunderte von Tabellen, die miteinander in Beziehung stehen. Daraus ergibt sich die Notwendigkeit von Joins. Mit dem Schlüsselwort JOIN können Sie verschiedene Tabellen über ihre gemeinsamen Spalten verknüpfen. Es ist auch möglich, eine Tabelle mit sich selbst zu verknüpfen, was als Self-Join bezeichnet wird. In diesem Artikel wird erläutert, was ein Self-Join ist, wie er funktioniert und wann Sie ihn in Ihren SQL-Abfragen benötigen.

Zum Üben von SQL JOIN, einschließlich Self-Joins, empfehle ich unseren interaktiven SQL-JOINs Kurs. Er enthält über 90 Übungen zu verschiedenen Arten von JOIN, darunter auch Abschnitte, die sich ausschließlich mit Self-Joins befassen.

Was ist ein Self-Join in SQL?

Beim Self-Join wird, wie der Name schon sagt, eine Tabelle mit sich selbst verknüpft. Um einen Self-Join zu verwenden, muss die Tabelle eine Spalte (X) enthalten, die als Primärschlüssel fungiert, und eine andere Spalte (Y), in der Werte gespeichert sind, die mit den Werten in Spalte X abgeglichen werden können. Die Werte der Spalten X und Y müssen für eine bestimmte Zeile nicht identisch sein. Der Wert in Spalte Y kann sogar null sein.

Schauen wir uns ein Beispiel an. Betrachten wir die Tabelle Mitarbeiter:

IdVollstaendiger_NameGehaltManager_ID
1John Smith100003
2Jane Anderson120003
3Tom Lanon150004
4Anne Connor20000
5Jeremy York90001

Jeder Mitarbeiter hat seine eigene Id, die unsere "Spalte X" ist. Für einen bestimmten Mitarbeiter (d.h. eine Zeile) enthält die Spalte Manager_ID die Id seines Vorgesetzten. Dies ist unsere "Spalte Y". Verfolgen wir die Mitarbeiter-Manager-Paare in dieser Tabelle anhand dieser Spalten:

  • Der Vorgesetzte des Angestellten John Smith ist der Angestellte mit Id 3, d.h. Tom Lanon.
  • Der Vorgesetzte des Mitarbeiters Jane Anderson ist der Mitarbeiter mit Id 3, d.h. Tom Lanon.
  • Der Vorgesetzte des Angestellten Tom Lanon ist der Angestellte mit Id 4, d.h. Anne Connor.
  • Die Mitarbeiterin Anne Connor hat keinen Vorgesetzten; ihre Manager_ID ist Null.
  • Der Vorgesetzte des Angestellten Jeremy York ist der Angestellte mit Id 1, d.h. John Smith.

Diese Art der Tabellenstruktur ist in Hierarchien sehr verbreitet. Um nun den Namen des Vorgesetzten für jeden Mitarbeiter in derselben Zeile anzuzeigen, können wir die folgende Abfrage ausführen:

SELECT
	Mitarbeiter.Id,
    	Mitarbeiter.Vollstaendiger_Name,
    	Mitarbeiter.Manager_ID,
    	Manager.Vollstaendiger_Name as Name_des_Managers
FROM Mitarbeiter Mitarbeiter
JOIN Mitarbeiter Manager
ON Mitarbeiter.Manager_ID = Manager.Id

Diese liefert das folgende Ergebnis:

IdVollstaendiger_NameManager_IDName_des_Managers
1John Smith3Tom Lanon
2Jane Anderson3Tom Lanon
3Tom Lanon4Anne Connor
5Jeremy York1John Smith

Die Abfrage wählt die Spalten Id, Vollstaendiger_Name und Manager_ID aus der Tabelle "alias" Mitarbeiter. Sie wählt auch die Spalte Vollstaendiger_Name der Tabelle "alias" Manager und benennt diese Spalte als Name_des_Managers. Als Ergebnis wird jeder Mitarbeiter, der einen Vorgesetzten hat, zusammen mit der ID und dem Namen seines Vorgesetzten ausgegeben.

In dieser Abfrage wird die Mitarbeiter Tabelle mit sich selbst verbunden und hat zwei verschiedene Rollen:

  • Rolle 1: Sie speichert die Mitarbeiterdaten (Alias Mitarbeiter).
  • Rolle 2: Sie speichert die Daten des Managers (Alias Manager).

Auf diese Weise betrachten wir die beiden Kopien der Tabelle Mitarbeiter so, als ob es sich um zwei verschiedene Tabellen handelt, eine für die Mitarbeiter und eine für die Manager.

Weitere Informationen über das Konzept des Self Join finden Sie in unserem Artikel Eine illustrierte Anleitung zum SQL Self Join.

Tabellenalias in Self Join

Wenn in einer SQL-Abfrage mehr als einmal auf dieselbe Tabelle verwiesen wird, brauchen wir eine Möglichkeit, jeden Verweis von den anderen zu unterscheiden. Aus diesem Grund ist es wichtig, Aliasnamen zu verwenden, um jeden Verweis auf dieselbe Tabelle in einer SQL-Abfrage eindeutig zu identifizieren. Als gute Praxis sollten die Aliasnamen die Rolle der Tabelle für jeden spezifischen Verweis in einer Abfrage angeben.

In der folgenden Abfrage sind die Aliase in rot dargestellt. Sie können ihre Deklaration in den Klauseln FROM und JOIN sehen.

SELECT
	Mitarbeiter.Id,
    	Mitarbeiter.Vollstaendiger_Name,
    	Mitarbeiter.Manager_ID,
    	Manager.Vollstaendiger_Name as Name_des_Managers
FROM Mitarbeiter Mitarbeiter
JOIN Mitarbeiter Manager
ON Mitarbeiter.Manager_ID = Manager.Id

Das Schlüsselwort JOIN verbindet zwei Tabellen und wird in der Regel von einer ON oder USING Klausel gefolgt, die die gemeinsamen Spalten angibt, die für die Verknüpfung der beiden Tabellen verwendet werden. Hier sehen wir, dass die beiden Verweise auf die Tabelle Mitarbeiter durch die Bedingung verknüpft werden, dass die Manager_ID des Mitarbeiters mit der Mitarbeiter_ID des Managers übereinstimmt.

Beispiele

Gehen wir einige gängige Szenarien durch, in denen der Self-Join verwendet wird.

Szenario 1: Verarbeitung einer Hierarchie in SQL

Der Self-Join wird häufig bei der Verarbeitung einer Hierarchie verwendet. Wie wir bereits gesehen haben, ordnet eine Hierarchie eine Zeile in einer Tabelle einer anderen Zeile in derselben Tabelle zu. Sie können sich das so vorstellen, dass es Eltern- und Kindzeilen gibt.

Kehren wir zu dem Beispiel mit den Mitarbeitern und ihren Managern zurück. Hier ist wieder die Mitarbeiter Tabelle:

IdVollstaendiger_NameGehaltManager_ID
1John Smith100003
2Jane Anderson120003
3Tom Lanon150004
4Anne Connor20000John Smith
5Jeremy York90001

Und der Code, um jeden Mitarbeiter, der einen Vorgesetzten hat, mit dem Namen seines Vorgesetzten aufzulisten:

SELECT
	Mitarbeiter.Id,
    	Mitarbeiter.Vollstaendiger_Name,
    	Mitarbeiter.Manager_ID,
    	Manager.Vollstaendiger_Name as Name_des_Managers
FROM Mitarbeiter Mitarbeiter
JOIN Mitarbeiter Manager
ON Mitarbeiter.Manager_ID = Manager.Id

Hier ist das Ergebnis, wenn Sie den Code ausführen:

IdVollstaendiger_NameManager_IDName_des_Managers
1John Smith3Tom Lanon
2Jane Anderson3Tom Lanon
3Tom Lanon4Anne Connor
5Jeremy York1John Smith

Diese Abfrage verwendet die Standard-Verknüpfung, auch bekannt als INNER JOIN. Weitere Informationen über INNER JOIN finden Sie in unserem Artikel Ein illustrierter Leitfaden zum SQL INNER JOIN.

Wenn wir alle Mitarbeiter auflisten möchten, unabhängig davon, ob sie Manager haben oder nicht, können wir stattdessen LEFT OUTER JOIN verwenden. Die folgende Abfrage tut dies:

SELECT
	Mitarbeiter.Id,
    	Mitarbeiter.Vollstaendiger_Name,
    	Mitarbeiter.Manager_ID,
    	Manager.Vollstaendiger_Name as Name_des_Managers
FROM Mitarbeiter Mitarbeiter
LEFT OUTER JOIN Mitarbeiter Manager
ON Mitarbeiter.Manager_ID = Manager.Id

Wenn Sie diese Abfrage ausführen, erhalten Sie das folgende Ergebnis:

IdVollstaendiger_NameManager_IDName_des_Managers
1John Smith3Tom Lanon
2Jane Anderson3Tom Lanon
3Tom Lanon4Anne Connor
4Anne Connor  
5Jeremy York1John Smith

Der Unterschied zwischen JOIN und LEFT OUTER JOIN wird deutlich, wenn wir dies mit dem Ergebnis der vorherigen SQL-Abfrage vergleichen. Im Ergebnis der (inneren) Abfrage JOIN sind nur die Mitarbeiter mit Führungskräften enthalten. Im Gegensatz dazu liefert die Abfrage LEFT OUTER JOIN alle Mitarbeiter, mit oder ohne Manager. Um mehr über OUTER JOIN zu erfahren, lesen Sie bitte unseren Artikel Eine illustrierte Anleitung zum SQL OUTER JOIN.

Ein weiteres Beispiel für eine Hierarchie ist die Beziehung zwischen Eltern und ihren Kindern. Betrachten Sie die Mensch Tabelle, die hier gezeigt wird:

IdNameAlterElternId
1Jonathan53
2Alexandra73
3Barbara30

In der folgenden Abfrage werden die Kinder ihren jeweiligen Eltern zugewiesen, indem die Mensch Tabelle mit sich selbst verknüpft:

SELECT
	Kind.Id as Kind_ID,
    	Kind.Vorname as Vorname_des_Kindes,
    	Kind.Alter as Alter_des_Kindes,
    	Kind.ElternId,
    	Elternteil.Vorname as ElternteilVorname,
    	Elternteil.age as ElternteilAlter
FROM Mensch Kind
INNER JOIN Mensch Elternteil
ON Kind.ElternId = Elternteil.Id

Hier ist das Ergebnis dieser Abfrage:

Kind_IDVorname_des_KindesAlter_des_KindesElternIdElternteilVornameElternteilAlter
1Jonathan53Barbara30
2Alexandra73Barbara30

Das Ergebnis der Abfrage enthält nur die Kinder, die Eltern haben. Wie im Beispiel der Mitarbeiter-Manager-Hierarchie könnten wir einen LEFT OUTER JOIN verwenden, um alle Zeilen aus der Tabelle "alias" Kind einzubeziehen.

Hier ist ein weiteres Beispiel für eine Hierarchie. Betrachten Sie die Tabelle Kategorie die unten gezeigt wird:

IdAnzahlKategorieUebergeordnete_KategorieId
160Essen
250Obst1
340Apfel2
420Granny Smith3
5100Milch1
660Sojamilch5
740Kuhmilch5
830Vollmilch7
910Fettfreie Milch7

Weisen wir jeder Kategorie eine übergeordnete Kategorie zu, wo immer dies möglich ist. Hier ist eine Abfrage, die das tut:

SELECT
	Kategorie.Id,
	Kategorie.Anzahl,
    	Kategorie.Kategorie,
    	Kategorie.Uebergeordnete_KategorieId,
    	ElternKategorie.Kategorie as Uebergeordnete_Kategorie
FROM Kategorie Kategorie
JOIN Kategorie ElternKategorie
ON Kategorie.Uebergeordnete_KategorieId = ElternKategorie.Id

Und hier ist das Ergebnis:

IdAnzahlKategorieUebergeordnete_KategorieIdUebergeordnete_Kategorie
250Obst1Essen
340Apfel2Obst
420Granny Smith3Apfel
5100Milch1Essen
660Sojamilch5Milch
740Kuhmilch5Milch
830Vollmilch7Kuhmilch
910Fettfreie Milch7Kuhmilch

Die ersten vier Spalten des obigen Ergebnisses stammen aus dem Verweis auf die Tabelle "alias" Kategorie. Die letzte Spalte stammt aus der Tabelle "alias" ElternKategorie und enthält den Namen der übergeordneten Kategorie, die mit der entsprechenden Id übereinstimmt.

Die Tabelle Kategorie hat zwei verschiedene Rollen, die durch die beiden separaten Verweise gekennzeichnet sind. Die Spalte Uebergeordnete_KategorieId aus der Tabelle "alias" Kategorie wird mit Id aus der Tabelle "alias" ElternKategorie abgeglichen. Die Klausel ON legt fest, dass Uebergeordnete_KategorieId aus Kategorie gleich sein muss wie Id von ElternKategorie, um die entsprechenden Zeilen zu verbinden.

Szenario 2: Auflistung von Paaren innerhalb einer Tabelle

Sie können einen Self-Join verwenden, um Zeilenpaare auf der Grundlage der Bedingung in der ON-Klausel zu erzeugen. Beginnen wir mit einem einfachen Beispiel, das alle möglichen Paare unter den Kollegen erzeugt. Betrachten Sie die folgende Tabelle Kollegen:

IdVollstaendiger_NameAlter
1Bart Thompson43
2Catherine Anderson44
3John Burkin35
4Nicole McGregor29

Angenommen, wir müssen alle möglichen Paare unter den Kollegen generieren, damit jeder eine Chance hat, mit jedem anderen am Einführungsabend der Firma zu sprechen. Hier ist der SQL-Code:

SELECT
	Teammitglied1.Vollstaendiger_Name as Teammitglied1Vollstaendiger_Name,
	Teammitglied1.Alter as Teammitglied1Alter,
	Teammitglied2.Vollstaendiger_Name as Teammitglied2Vollstaendiger_Name,
	Teammitglied2.Alter as Teammitglied2Alter
FROM Kollegen Teammitglied1
CROSS JOIN Kollegen Teammitglied2
ON Teammitglied1.Vollstaendiger_Name <> Teammitglied2.Vollstaendiger_Name

Und hier ist das Ergebnis:

Teammitglied1Vollstaendiger_NameTeammitglied1AlterTeammitglied2Vollstaendiger_NameTeammitglied2Alter
Catherine Anderson44Bart Thompson43
John Burkin35Bart Thompson43
Nicole McGregor29Bart Thompson43
Bart Thompson43Catherine Anderson44
John Burkin35Catherine Anderson44
Nicole McGregor29Catherine Anderson44
Bart Thompson43John Burkin35
Catherine Anderson44John Burkin35
Nicole McGregor29John Burkin35
Bart Thompson43Nicole McGregor29
Catherine Anderson44Nicole McGregor29
John Burkin35Nicole McGregor29

Das Ergebnis bringt jede Person mit jeder einzelnen Person in der Tabelle zusammen. Da wir nicht wollen, dass jemand mit sich selbst gepaart wird, haben wir die ON Klausel Bedingung Teammitglied1.Vollstaendiger_Name <> Teammitglied2.Vollstaendiger_Name. Dies bedeutet, dass jede Person mit drei anderen Kollegen gepaart wird, da es bei dieser Veranstaltung vier Kollegen gibt.

Schauen wir uns nun ein etwas komplizierteres Beispiel an. Betrachten Sie die unten gezeigte Tabelle Mensch. Wir wollen jeder Person alle Vorfahren zuordnen, soweit die Daten dies zulassen. Wobei eine Person ein Vorfahre ist, wenn sie einen höheren Id Wert hat.

Unten ist die Mensch Tabelle, die in diesem Beispiel verwendet wird.

IdVornameAlterElternId
1Jonathan53
2Alexandra73
3Barbara304
4Tom506
5George556
6Amy807
7Josephine9935

Lassen Sie uns alle Nachkommen-Ahnen-Paare in der obigen Tabelle finden. Hier ist der SQL-Code:

SELECT
	Nachkomme.Id,
    	Nachkomme.Vorname,
    	Nachkomme.Alter,
    	Nachkomme.ElternId,
    	Vorfahre.Id as Vorfahre_Id,
    	Vorfahre.Vorname as Vorname_des_Vorfahren,
    	Vorfahre.Alter as VorfahreAlter
FROM Mensch Nachkomme
LEFT JOIN Mensch Vorfahre
ON Nachkomme.ElternId <= Vorfahre.Id

Und das Ergebnis:

IdVornameAlterElternIdVorfahre_IdVorname_des_VorfahrenVorfahreAlter
1Jonathan533Barbara30
1Jonathan534Tom50
1Jonathan535George55
1Jonathan536Amy80
1Jonathan537Josephine99
2Alexandra733Barbara30
2Alexandra734Tom50
2Alexandra735George55
2Alexandra736Amy80
2Alexandra737Josephine99
3Barbara3044Tom50
3Barbara3045George55
3Barbara3046Amy80
3Barbara3047Josephine99
4Tom5066Amy80
4Tom5067Josephine99
5George5566Amy80
5George5567Josephine99
6Amy8077Josephine99
7Josephine99

Durch Angabe der ON-Klauselbedingung Nachkomme.ElternId <= Vorfahre.Id finden wir alle Vorfahren jeder Person in der Tabelle, sofern sie vorhanden sind. Andernfalls gibt die Abfrage null für die Vorfahreninformationen zurück.

Die ersten vier Spalten werden aus der Tabelle "alias" Nachkomme entnommen, die Informationen über die Person enthält, für die die Vorfahren gesucht werden. Die letzten drei Spalten stammen aus der Tabelle "alias" Vorfahre und enthalten Details über jeden Vorfahren.

Szenario 3: Self Join in Kombination mit einer anderen Tabelle

In SQL ist es möglich, einen Self-Join in Kombination mit einer oder mehreren anderen Tabellen durchzuführen. Dies ist zwar kein reiner Self-Join, kommt aber in der Praxis sehr häufig vor.

Ein Beispiel aus der Praxis sind die Fluginformationen auf Flughäfen, die stündlich eine enorme Datenmenge enthalten. Nehmen wir an, wir wollen nach einer Flugidentifikationsnummer zusammen mit den Details zu den Abflug- und Zielflughäfen suchen. Betrachten Sie die folgenden Tabellen:

Tabelle Flughafen:

FlughafenIdLandStadt
1USANew York
2KanadaToronto
3DeutschlandFrankfurt
4FrankreichParis
5ItalienRom

Tabelle Flug:

FlugIdFlugzeugIdStart_ZeitstempelEnde_ZeitstempelStartFlughafenIdZielFlughafenId
25558772020-01-14 13:00:002020-01-14 15:00:0034
32225362020-02-04 01:00:002020-02-04 16:00:0015
41117452020-02-15 09:00:002020-02-15 12:00:0054
57775242020-02-24 03:00:002020-02-24 19:00:0042
68885212020-03-25 10:00:002020-03-25 12:00:0021
74449372020-04-01 00:00:002020-04-01 17:00:0031
2431116542020-01-01 02:00:002020-01-01 04:00:0012

Hier ist zu beachten, dass die Spalte FlughafenId der Tabelle Flughafen Tabelle der Fremdschlüssel zu den Spalten StartFlughafenId und ZielFlughafenId der Tabelle Flug Tabelle ist. Wir verbinden die Flughafen Tabelle mit der Flug Tabelle zwei separate Male wie folgt:

  • Im ersten JOIN übernimmt Flughafen die Rolle der Tabelle mit den Startflughäfen.
  • Im zweiten JOIN übernimmt Flughafen die Rolle der Tabelle mit den Zielflughäfen.

Die Abfrage sieht wie folgt aus:

SELECT
	Flug.FlugId,
    	Flug.FlugzeugId,
    	Flug.StartFlughafenId,
    	startFlughafen.Land as StartFlughafenLand,
    	startFlughafen.Stadt as StartFlughafenStadt,
    	Flug.ZielFlughafenId,
    	endFlughafen.Land as EndFlughafenLand,
    	endFlughafen.Stadt as EndFlughafenStadt
FROM Flug Flug
JOIN Flughafen startFlughafen
ON Flug.StartFlughafenId = startFlughafen.FlughafenId
JOIN Flughafen endFlughafen
ON Flug.ZielFlughafenId = endFlughafen.FlughafenId

Und das Ergebnis der Abfrage sieht wie folgt aus:

FlugIdFlugzeugIdStartFlughafenIdStartFlughafenLandStartFlughafenStadtZielFlughafenIdEndFlughafenLandEndFlughafenStadt
11116541USANew York2KanadaToronto
25558773DeutschlandFrankfurt4FrankreichParis
32225361USANew York5ItalienRom
41117455ItalienRom4FrankreichParis
57775244FrankreichParis2KanadaToronto
68885212KanadaToronto1USANew York
74449373DeutschlandFrankfurt1USANew York

Lassen Sie uns das Ergebnis analysieren. Die ersten drei Spalten stammen aus einem einfachen SELECT der Flug-Tabelle. Die nächsten beiden Spalten stammen aus Flughafen in der Rolle der Startflughafentabelle. Die Zeilen werden auf der Grundlage von FlughafenId und StartFlughafenId aus den Tabellen Flughafen und Flug Tabellen abgeglichen. Es folgt eine Spalte aus der Tabelle Flug. Die letzten beiden Spalten stammen aus Flughafen, diesmal in der Rolle der Tabelle des Zielflughafens. Die Zeilen werden auf der Grundlage von FlughafenId und ZielFlughafenId aus den Tabellen Flughafen und Flug abgeglichen.

Sind Sie immer noch etwas verwirrt über all die JOINs? Es gibt noch viele weitere Artikel, in denen Sie sich Hilfe holen können. Ich empfehle insbesondere den Artikel Wie man SQL-JOINs lernt. Und wenn Sie anfangen möchten zu üben, besuchen Sie bitte unseren Artikel Wie man SQL-JOINs übt.

Self Join: Ein Spezialfall des Join

Wie wir gesehen haben, ist Self-Join ein wichtiger Spezialfall des Joins. Wir haben Beispiele für verschiedene Anwendungen des Self-Joins gesehen, darunter die Verarbeitung einer Hierarchie in einer Tabelle und die Paarung von Zeilen innerhalb einer Tabelle. Wir können dieselbe Tabelle mehrfach verknüpfen, aber es ist wichtig, jedem Verweis einen Alias zu geben, der seine Rolle angibt. Diese Tabellenaliase werden verwendet, um Spalten aus dieser einzelnen Tabelle auf der Grundlage der Rolle, für die sie referenziert werden, abzurufen.

Joins sind ein wesentlicher Bestandteil von SQL und eine sehr hilfreiche, häufig verwendete Funktion zur Kombination verschiedener Tabellen. Sie sind allgegenwärtig - sehen Sie sich unbedingt unseren Kurs über SQL-JOINs um dieses mächtige Werkzeug zu beherrschen!