Einführung SQL

Um uns die Grundlagen von SQL anzueignen, verwenden wir folgende kleine Beispieldatenbank. Die Idee hinter dieser ist, dass wir uns vorstellen, einen kleinen Frühstücksservice einzurichten. Dieser beschränkt sich auf Auslieferungen in Düsseldorf, so dass die Stadt in der unsere Kunden wohnen nicht gespeichert werden muss.
Die Datenbank ist absichtlich klein, damit man die Beispiele „per Hand“ nachvollziehen kann.

Folgendes Datenbankschema verwenden wir:

Kunde(KundenNr, Name, Vorname, Anschrift, Stadtteil)
Lieferant(ID, Name, Vorname)
beliefert(↑KundenNr, ↑ID)
Ware(Typ, Bezeichnung, Preis)
bestellt(↑KundenNr, ↑Typ, Menge)

Dies sind unsere Beispieleinträge:

Kunde

KundenNr Name Vorname Anschrift Stadtteil
01 Janssen Christian Würfelgasse 6 Niederkassel
02 Witsch Arnold Löricker Straße 3 Lörick
03 Grunewald Egon Hospitalweg 15 Oberkassel
04 Reich  NULL Planetenstraße 56 Lörick
05 Rothe Christian Am langen Holzweg 11 Niederkassel
06 Siegmund Michael Kölner Straße 10 Niederkassel
07 Hochbruck  NULL Nummerngasse 20 Lörick

Lieferant

ID Name Vorname
01 Knight Michael
02 Miles Devon
03 Barstow Bonnie
04 Cornelius Reginald
05 Curtis April

beliefert

KundenNr ID
01 02
02 04
03 01
04 05
05 01
06 02
07 04

Ware

Typ Bezeichnung Preis
01 Brötchen 0,50 €
02 Mehrkornbrötchen 0,60 
03 Croissant 0,80 
04 Rosinenmürbchen 0,60 
05 Puddingteilchen 1,10 
06 Rosinenschnecke 1,10 

bestellt

KundenNr Typ Menge
01 01 02
01 02 04
02 03 02
03 02 03
03 04 01
04 01 03
04 02 03
04 03 01
05 05 01
06 01 02
06 03 02
07 02 02

Um die Beispiele zu testen, empfehle ich den DB Browser for SQLite. Das ist ein sehr schlankes Tool, um SQLite-Datenbanken zu bearbeiten. Er ist für Windows, MacOS und Linux kostenlos erhältlich.

Die Beispieldaten sind in der Datei KundendatenHP.db.zip enthalten. Diese muss nach dem Herunterladen noch entpackt werden. (Weil WordPress keine Dateien mit Endung .db erlaubt und ich nicht an den Sicherheitseinstellungen spielen wollte, musste ich sie als .zip hochladen.) Danach kann sie mit dem DB Browser geöffnet werden.

Im DB Browser muss nun die .db-Datei geöffnet werden. Unter dem Reiter SQL ausführen findest Du das Eingabefenster für SQL-Anweisungen.

DB Browser in Aktion
Hier können wir SQL-Anweisungen ausführen.

Anfragen an eine einzelne Tabelle

Einfache Auflistungen

Als allererstes kann man die Anweisung SELECT * FROM Kunde geben. Diese liefert eine komplette Auflistung der Einträge in der Tabelle Kunde. Das Sternchen steht also stellvertretend für alle Spalten.

Bemerkung am Rande: Schon in Betriebssystem DOS, das vor Windows im Grunde Standard war, wurde das Sternchen als ein solcher Joker verwendet. Wenn man jemanden ärgern wollte, konnte man an seinem Rechner den Befehl delete *.* eingeben. Es ist wohl klar, was dieser bewirkte 🙂

Interessiert man sich aber nur für bestimmte Spalten, so kann man diese wie folgt angeben:

SELECT Kunde.Vorname, Kunde.Name FROM Kunde

So werden nur die beiden Spalten Vorname und Name angezeigt.

Die angezeigten Daten kann man sich auch sortieren lassen. Hier ein Beispiel:

SELECT * FROM Kunde ORDER BY Kunde.Name ASC

Die Tabelle mit den Kundendaten wird hier nach der Spalte Name sortiert angezeigt. Die gespeicherten Daten bleiben dabei übrigens unverändert, d.h., sie werden nicht umsortiert.
ASC steht hier für aufsteigend (ascending) und darf auch weggelassen werden, da dies die Standardoption ist. Um die Daten absteigend (descending)  sortieren zu lassen muss man DESC verwenden.
Es sind auch verschachtelte Sortierungen möglich:

SELECT * FROM Kunde ORDER BY Kunde.Stadtteil, Kunde.Name

Hier werden die Kunden nach Stadtteilen sortiert und innerhalb der Stadtteile wieder nach Namen.

Datensätze herausfiltern

Möchte man nur bestimme Zeilen – also bestimmte Datensätze – muss man mit dem Schlüsselwort where Einschränkungen angeben. Zum Beispiel liefert uns SELECT * FROM Kunde WHERE Kunde.KundenNr=4 den Kunden mit der Kundennummer 4.

Wir sind ein paar weitere Beispiele:

SELECT * FROM Kunde WHERE Kunde.Name='Grunewald' liefert Kunden mit Namen Grunewald. Beachte, dass bei Texten als Suchkriterium Anführungszeichen verwendet werden müssen.

SELECT * FROM Kunde WHERE Kunde.KundenNr > 3 liefert alle Kunden mit einer Kundennummer größer als 3. Statt > kann man auch >=, <, <= und <> (für ungleich) verwenden.

SELECT * FROM Kunde WHERE Kunde.KundenNr BETWEEN 2 AND 5 liefert alle Kunden mit einer Kundennummer zwischen 2 und 5 (inklusive).

SELECT * FROM Kunde WHERE Kunde.KundenNr IN (2,3,5,7) liefert alle Kunden mit einer der genannten Kundennummern.

SELECT * FROM Kunde WHERE NOT Kunde.KundenNr=4 liefert alle Kunden mit einer Kundennummer ungleich 4. Mit NOT kann man allgemein Bedingungen umkehren.

SELECT * FROM Kunde WHERE Kunde.KundenNr=4 OR Kunde.Vorname='Michael' liefert alle Kunden mit Vornamen Michael oder auch Kundennummer 4. Neben OR kann auch auch AND und NOT (s.o.) als logische Verknüpfungen verwenden.

SELECT Kunde.Name FROM Kunde WHERE Kunde.Vorname IS NULL liefert alle Kunden bei deren Vorname kein Eintrag vorhanden ist.

SELECT Kunde.Name FROM Kunde WHERE Kunde.Vorname IS NOT NULL liefert alle Kunden deren Vornamen eingetragen sind.

SELECT * FROM Kunde WHERE Kunde.Stadtteil LIKE '%kassel' liefert alle Kunden deren Stadtteil auf „kassel“ endet.

SELECT * FROM Kunde WHERE Kunde.Anschrift LIKE '%Straße%' liefert alle Kunden deren Anschrift „Straße“ enthält.

Das Symbol % ist ein Joker, der für beliebig viele beliebige Zeichen steht. Neben diesem gibt es auch noch den Joker _, der für genau ein beliebiges Zeichen steht.

Einfache arithmetische Funktionen

Auf den Daten kann man auch einfache Rechnungen durchführen. Sehen wir uns das anhand einiger Beispiele an.

SELECT COUNT(*) FROM Ware liefert die Anzahl der eingetragenen Kunden.

SELECT COUNT(Kunde.Vorname) FROM Kunde liefert die Anzahl der eingetragenen Kundenvornamen, d.h., die beiden leeren Zellen werden nicht mitgezählt.

SELECT COUNT(*) FROM (SELECT DISTINCT Kunde.Vorname FROM Kunde) liefert die Anzahl der verschiedenen Vornamen, d.h., Einträge (auch NULL) die mehrfach vorkommen werden nur einmal gezählt.

SELECT MAX(Ware.Preis) FROM Ware liefert liefert den höchsten Preis unserer Ware.

Gruppieren von Datensätzen

Oft ist es nützlich, einige Datensätze zu einer Gruppe zusammenzufassen. An folgendem Beispiel wird dies deutlich.

SELECT MAX (bestellt.Menge) FROM bestellt liefert die größte Einzelnmenge der bestellen Artikel. Hier findet noch keine Gruppierung statt.

SELECT MAX (bestellt.Menge) FROM bestellt GROUP BY bestellt.KundenNr liefert für jeden Kunden die größte Einzelnmenge der seiner Artikel.

SELECT bestellt.KundenNr , MAX (bestellt.Menge) FROM bestellt GROUP BY bestellt.KundenNr liefert ebenfalls für jeden Kunden die größte Einzelnmenge der seiner Artikel und gibt dazu auch zu zugehörige Kundennummer aus.

Anfragen an zwei Tabellen

Aus einer einzelnen Tabelle können wir nun schon gezielt Informationen abfragen. Da unsere Daten aber auf mehrere Tabellen verteilt wurden, müssen wir auch in der Lage sein, Informationen abzufragen, die sich erst bei Betrachtung mehrerer Tabellen erschließen. Für den Anfang schauen wir uns erst einmal an, wie man zwei Tabellen miteinander verknüpfen kann. Dabei betrachten wir weiter unsere Beispieldaten des Frühstücksservice.

Mengenoperatoren

Eine Mengenoüeration kann man immer dann durchführen, wenn zwei Tabellen gleiche Attribute — oder zumindest Attribute vom selben Typ, wie etwa Text — besitzen. Zum Beispiel haben sowohl die Tabelle Kunden also auch die Tabelle Lieferant das Attribut Name.

Vereinigung

Wir können uns daher z.B. alle Namen ausgeben lassen, die in wenigstens einer der beiden Tabellen auftauchen:

SELECT Lieferant.Name FROM Lieferant
UNION
SELECT Kunde.Name FROM Kunde

Schnitt

Wir könnten uns auch alle Vornamen ausgeben lassen, die in beiden Tabellen auftauchen:

SELECT Lieferant.Vorname FROM Lieferant
INTERSECT
SELECT Kunde.Vorname FROM Kunde

Differenz

Hier lassen wir uns alle Vornamen von Kunden anzeigen, die nicht als Vorname eines Lieferanten auftauchen:

SELECT Kunde.Vorname FROM Kunde
   WHERE Kunde.Vorname NOT IN
      (SELECT Lieferant.Vorname FROM Lieferant)

Kartesisches Produkt

Das Kartesische Produkt gehört streng genommen auch zu den Mengenoperatoren. Es wird hier gesondert aufgeführt, weil es in Anwendungen noch besonders interessant sein wird.

Mit dieser Anweisung bilden wir das Kartesische Produkt der beiden Tabellen Kunde und Ware:

SELECT * FROM Kunde , Ware

Führen wir diese Anweisung aus, erhalten wir eine sehr große Ergebnistabelle. In dieser wird jeder Kunde mit jeder einzelnen Ware kombiniert. In unserer Beispieldatenbank haben wir 7 Kunden und 6 Typen von Ware. Entsprechend hat das Kartesische Produkt damit 42 Zeilen!

Sinnvoll wird das Bilden eines solchen Produkts erst, wenn noch eine Selektion folgt. Das führt und zum nächsten Punkt.

Join

Verbindet man das kartesische Produkt mit einer Selektion, so spricht man von einem Join. Diese erweisen sich als äußerst nützlich, wie wir nun sehen werden.

SELECT * FROM Kunde, beliefert
  WHERE Kunde.KundenNr = beliefert.KundenNr

Hier werden zunächst die Zeilen der Tabelle Kunde kombiniert mit denen der Tabelle beliefert. Schließlich werden aus diesen Kombination aber nur die Zeilen angezeigt, die hier im Sachzusammenhang sinnvoll sind – nämlich die, bei denen in der Tabelle Kunde und in beliefert dieselbe Kundennummer eingetragen ist. Wir können damit also die kompletten Kundendaten zusammen mit der zuständigen Lieferanten-ID ausgeben lassen.

Im folgenden Beispiel möchten wir uns etwas Überblick über die Bestellungen der Kunden verschaffen:

SELECT Kunde.Name, Typ, bestellt.Menge FROM Kunde, bestellt 
  WHERE Kunde.KundenNr = bestellt.KundenNr
Tabelle
Ausschnitt des Ergebnisses

Wir erhalten eine Tabelle mit drei Spalten: Kundenname, Warentyp und bestellte Menge. Ein Ausschnitt des Ergebnisses ist in der Abbildung zu sehen.

Zur Erstellung eines Joins kann man auch Anweisungen der folgenden Gestalt verwenden:

SELECT bestellt.KundenNr, Ware.Bezeichnung, bestellt.Menge FROM bestellt JOIN Ware 
  ON bestellt.Typ = Ware.Typ

Das Ergebnis ist dasselbe wie bei dieser Abfrage:

SELECT bestellt.KundenNr, Ware.Bezeichnung, bestellt.Menge FROM bestellt, Ware 
  WHERE bestellt.Typ = Ware.Typ

Beide Varianten liefern also dasselbe Ergebnis. Bei sehr großen Datenmengen kann es aber sein, dass eine Verwendung der zweiten Variante Vorteile hat, weil diese eventuell effizienter und damit schneller ausgeführt wird – obgleich dies in einschlägigen Foren heiß diskutiert wird.

Left/Right Join

Um den Unterschied zwischen einem gewöhnlichen Join und einem left oder auch right Join zu erkennen, sollte man auf Details in der Ausgabe achten.

SELECT Lieferant.Vorname, beliefert.KundenNr FROM beliefert LEFT JOIN Lieferant
  ON Lieferant.ID = beliefert.ID

Beim left Join wird jede Zeile der linken Tabelle angezeigt, auch wenn sie keine passende Zeile in der rechten Tabelle haben sollte – die entsprechenden Spalten bleiben dann leer. Hat eine Zeile der rechten Tabelle kein passendes Gegenstück, wird sie ausgelassen.
Beim Right Join ist es genau umgekehrt.

Hinweis: Momentan wird ein right Join vom DB Browser nicht unterstützt. Ersatzsweise kann man einfach die Reihenfolge der Tabellen vertauschen.

Weiterführender Link

Eine sehr gute Erklärung der verschiedenen SQL-Anweisungen gibt es auf W3Schools.com. Dort finden sich auch noch Details, die nicht für das Abitur vorausgesetzt werden.