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.
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
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.