SQL Grundlagen – Teil 1

Da ich nie studiert habe, fehlen mir manchmal noch die Grundlagen um manchmal die Theorie dahinter zu verstehen. Ich hab allerdings vor vielen Jahren mal eine Ausbildung zum FISI (Fachinformatiker für Systemintegration) abgeschlossen. Da gab es auch mal Datenbanken mit den 3 Normalformen. Letztendlich um Primärschlüssel und Fremdschlüssel zu verwenden und die Tabellen konsistent zu halten und um Daten nicht mehrfach abzuspeichern. Manchmal ist die Normalisierung nervig, wenn man beispielsweise eine Tabelle Auftrag hat und dort nur ein Verweis auf die Adresse besteht. Um beispielsweise dann die Auftragskopfdaten und die Firmenbezeichnung anzuzeigen müssen Auftrag und Adresse verknüpft werden. Andererseits hat dies auch wieder den Vorteil, dass man später die neuen Firmendaten nur an einer Stelle (nämlich in der Adresse) ändern muss.

In meinen Beispielen beziehe ich mich nun ausschließlich auf den MS SQL Dialekt. Bei anderen Datenbanksystemen gibt es definitiv Unterschiede.

Bisher verband ich mit SQL immer nur SELECT * FROM ADRESSE. Damit gebe ich beispielsweise alle Datensätze der Tabelle mit dem Namen ADRESSE aus. Das kann für einen Überblick schon mal sinnvoll sein. Ist es allerdings eine sehr große Tabelle mit mehreren Tausend oder gar Millionen Datensätze ist das eher unpraktisch, da damit sehr viel Last erzeugt wird. Deshalb ist hier schon mal folgendes besser: SELECT TOP(100) * FROM ADRESSE Damit werden die obersten 100 Datensätze ausgegeben. So sieht man immer noch alle Spalten und kann sich ein Bild von der Tabelle machen ohne gleich eine enorme Last zu erzeugen.

Kennt man die Tabelle schon etwas genauer und möchte nur einen bestimmten Datensatz aufrufen kommt der WHERE Befehle ins Spiel. Beispielsweise um den Datensatz mit der Id 42 aufzurufen könnte ich folgenden Befehl abfeuern:
SELECT *
FROM ADRESSE
WHERE Id = 42

Trotzdem bekomme ich hiermit vermutlich wieder zu viele Informationen zurück. Ich habe mit dem WHERE Id = 42 zwar den Datensatz auf die Id 42 eingegrenzt, aber bekomme trotzdem weiterhin alle Spalten angezeigt. Möchte ich beispielsweise nur den Namen, die Straße, die PLZ und den Ort bekommen, könnte folgendes Beispiel sinnvoll sein.
SELECT Name, Straße, PLZ, Ort
FROM ADRESSE
WHERE Id = 42

Das ist dann quasi schon ziemlich der einfache Standard, wenn es nur um Spalten innerhalb einer Tabelle geht.

Möchte man aber nun Daten ausgeben, die in verschiedenen Tabellen sind, ist eine Verknüpfung möglich. Dabei verknüpft man immer mit einer Spalte, die in den beiden Tabellen vorhanden ist und nach Möglichkeit auch eindeutig. Dabei gibt es INNER JOIN, LEFT JOIN und RIGHT JOIN. Der INNER JOIN verknüpft die Datensätze, die in beiden Tabellen vorhanden sind. LEFT JOIN dagegen gibt alle Datensätze von der linken Tabelle aus und ergänzt mit den Datensätze, die in der rechten vorhanden sind. RIGHT JOIN macht es entsprechend umgekehrt. So, dass bei LEFT und RIGHT eine Tabelle führend ist und nicht nur die gemeinsamen Datensätze aus beiden Tabellen ausgegeben werden.

Nehmen wir beispielsweise an, wir möchten nun einen Auftrag und seine Positionen ausgeben. Dann wird rein logisch betrachtet der Auftrag mit seinen Kopfdaten und die Auftragspositionen mit den Positionen jeweils eigene Positionen sein. Sicherlich gibt es dazu aber in beiden Tabellen eindeutige Schlüsselspalten. Beispielsweise die Auftragsnummer. Da hier aller Wahrscheinlichkeit in beiden Tabellen immer mindestens ein Auftragskopf und mindestens eine Position vorhanden ist, wird ein INNER JOIN ausreichen. Ein LEFT JOIN wäre dann interessant, wenn man alle Aufträge haben will und dabei auch Aufträge OHNE Positionen erwartet.
SELECT a.Auftragsnr, a.Kdnr, ap.PosNr, ap.Artikelnr, ap.Menge, ap.Preis
FROM AUFTRAG a (NOLOCK)
INNER JOIN AUFTRAGPOSITION ap (NOLOCK) ON ap.Auftragsnr = a.Auftragsnr
WHERE a.KdNr = 23

Ich habe im obigen Beispiel jeder Tabelle auch noch einen Alias verpasst. Für Auftrag hier die a und für Auftragsposition ap. Der Grund dafür ist relativ einfach. Da wir nun mehrere Tabellen verknüpft haben, sind die einzelnen Spaltennamen nicht mehr eindeutig. Beispielsweise kommt Auftragsnr in beiden Tabellen vor. Um uns und dem SQL Server die Angabe der Tabellen leichter zu machen, verwende ich hier kleine Aliase. Mit a.Auftragsnr drücke ich also aus, dass ich die Auftragsnummer aus der Tabelle AUFTRAG haben möchte.
Außerdem hab ich mit INNER JOIN AUFTRAGPOSITION ap (NOLOCK) ON ap.Auftragsnr = a.Auftragsnr klar gemacht, dass ich eine Verknüpfung der Auftragstabelle mit der Auftragspositionentabelle wünsche und nur die gleichen Datensätze verknüpft werden, wo jeweils die Auftragsnummer gleich ist.
Außerdem hab ich die Ausgabe auf die Datenzahlen vom Kunden mit der Kundennummer 23 eingegrenzt.

Das (NOLOCK) Statement macht klar, dass der Befehl ohne Schreibzugriff erfolgen soll. Das ist besonders interessant, wenn in der Datenbank immer wieder Abfragen erfolgen, die auch mal zeitintensiv sind. Ein gutes Beispiel zu den Auswirkungen von (NOLOCK) findet man hier. Generell macht ein (NOLOCK) also nur Sinn, wenn im Hintergrund noch viele Daten geändert werden und man trotzdem schon mal Daten erhalten möchte.

Im nächsten Teil geht es dann weiter mit GROUP BY und ORDER BY.