SQL: Nur wer fragt, bekommt Antworten. Wer mit Datenbanken arbeitet, kommt um die Abfragesprache Structured Query Language (SQL) nicht umher. Sie dient aber nicht nur dem Zweck der Informationsgewinnung, sondern ist auch beim Aufbau der Datenbankstruktur, deren Pflege und bei Verwaltung der eigentlichen Daten in der Datenbank hilfreich. SQL ist für nahezu jedes relationale Datenbanksystem verfügbar. Wir geben einen Überblick über die wichtigsten Befehle.
Im ersten Teil haben wir uns mit den Grundlagen der Datenbanktheorie beschäftigt. Im Mittelpunkt standen Fragen rund um die Phasen der Datenbankentwicklung, das Entity Relationship Model und einige Bemerkungen zum Erstellen der Datenbankstruktur, wie z. B. das grundsätzliche Vorgehen bei der Normalisierung. Ein wichtiges Arbeitsmittel für den Umgang mit Datenbanken ist die Kenntnis – oder besser die Beherrschung – der wichtigsten Befehle der Structured Query Language (SQL). Für die am häufigsten benötigten Befehle wird deren Syntax vorgestellt und anhand von Beispielen erläutert. Hilfreich ist es, wenn man sich den frei verfügbaren SQL-Teacher von [1] herunterlädt. In einer komfortablen Lernumgebung (Abb. 2.1) kann man die vorgestellten Befehle gut nachvollziehen. Alternativ kann man sich auch eine eigene Beispieldatenbank aufbauen. Kostenfrei ist beispielsweise Base von OpenOffice.org [2].
Abbildung 2.1 Erfassung und Abfrageergebnis mit SQL-Teacher [1]
Eine kompakte Übersicht zu Structured Query Language (SQL)
SQL zählt zu dem Bereich der Datenbanksprachen. Diese sind eine Untermenge der Computersprachen, die zur strukturierten, meist mengenorientierten Abfrage, Erstellung und Manipulation von Daten, die in einem relationalen Datenbanksystem vorliegen, benutzt werden. Die Anweisungen operieren mit den Tabellen des Datenbanksystems. Die Geschichte von SQL begann im Jahr 1970, als Dr. E. F. Codd vom IBM-Forschungslabor ein Paper mit dem Titel „A Relational Model of Data for Large Shared Data Bank“ („Ein relationales Datenmodell für große, verteilte Datenbanken“) vorlegte. In diesem Paper wurde befürwortet, die Menge von Daten in Tabellen darzustellen. Gemeinsam mit einer Definition dieses relationalen Modells stellte Codd die Sprache DSL/Alpha vor, um die Daten in relationalen Tabellen zu verarbeiten. Es wurde eine Arbeitsgruppe für den Bau eines Prototyps zusammengestellt, die Codds Ideen umsetzen sollte. Diese Gruppe erschuf eine vereinfachte Version von DSL/Alpha namens SQUARE. SQUARE wurde weiter verfeinert und es entstand eine Sprache namens SEQUEL, die dann schließlich SQL getauft wurde. Die Sprache ist heute ein von der ISO verabschiedeter Standard. SQL wird von einer Vielzahl von Herstellern in ihre Software implementiert und ist damit Bestandteil des Datenbank-Management-Systems (DBMS). Die aktuelle Version ist SQL 2008 (ISO/IEC 9075:2008). SQL kann strukturell in vier Kategorien unterteilt werden (Abb. 2.2):
Abbildung 2.2 Zusammensetzung der Sprache SQL
SQL ist mengenorientiert und nichtprozedural. Wenn Sie in der Vergangenheit bereits prozedural programmiert haben, sind Sie im Umgang mit Variablen, Entscheidungsstrukturen (z. B. if – then – else) und Schleifen (z. B. while) vertraut. Dieses Wissen hilft bei SQL recht wenig, denn SQL ist eine mengenorientierte Sprache. Dazu gleich mehr. Des Weiteren kann man mit SQL keine ausführbaren Anwendungen schreiben. SQL wird in Form von Skripten an die Datenbank, genauer an das Datenbank-Management-System (DBMS) übermittelt. Alternativ wird SQL in die Programme einer anderen Programmiersprache eingebettet. Oft wird die Sprache auch dazu verwendet, ad hoc bestimmte Informationen aus der Datenbank zu gewinnen. Wenn eine Standardisierung dieser Fragestellungen nicht sinnvoll erscheint, z. B. weil es sich um eine einmalige Abfrage handelt, kann eine SQL-Befehlssyntax direkt an das DBMS übermittelt werden. Dazu stellen die meisten Datenbankhersteller ein Konsolenwerkzeug zur Verfügung.
Die Grundlagen schaffen: Datenbankdefinition
Bevor man mit den Daten in der Datenbank arbeiten kann, muss sie eingerichtet werden. SQL stellt dafür einige Befehle zur Verfügung. Wir gehen davon aus, dass man sich über den grundsätzlichen Aufbau der neuen Datenbank abschließend Gedanken gemacht hat und die Konzeption für die künftige Tabellenstruktur feststeht [6]. Eine Tabelle wird auch als Relation bezeichnet. Die Spalten repräsentieren die Attribute mit ihren spezifischen Datentypen. Eine Zeile umfasst einen einzelnen Datensatz, ein so genanntes Tupel. Zum Anlegen einer Tabelle benutzt man den Befehl CREATE TABLE. In der einfachsten Version, d. h. ohne Berücksichtigung optionaler Argumente, lautet die Syntax wie folgt:
CREATE TABLE <Tabellenname> <Spaltenbeschreibung>;
Spaltenbeschreibung :=(<Spalte> <Wertebereich>
<ZUSATZATTRIBUTE> [' Spaltenbeschreibung])
Die Syntax ist nahezu selbsterklärend, daher nur einige kurze Hinweise: Nach dem Schlüsselwort CREATE TABLE muss der Name der entsprechenden Tabelle stehen. Danach folgen für jede Spalte der Tabelle deren Name, der Wertebereich (also die Spezifikation des Datentyps, wie float, int usw.) und ggf. weitere optionale Zusatzattribute. Mehrere Beschreibungen für Spalten werden durch Kommata voneinander getrennt. Die letzte Spalte wird mit einem Semikolon abgeschlossen. Listing 2.1 zeigt ein Beispiel für die Einrichtung einer Tabelle Kunde mit den Attributen Kundennummer, Name und Vorname.
CREATE TABLE 'Kunde' (
'Kundennummer' BIGINT,
'Name' VARCHAR,
'Vorname' VARCHAR
);
Listing 2.1
Zusätzlich können noch CONSTRAINTs (Zwangsbedingungen) und CHECKs eingebaut werden, um die Richtigkeit der Daten zu gewährleisten. Zunächst zu den CONSTRAINTs. Es sind folgende Optionen möglich:
Gegenüber diesen vordefinierten Bedingungen sind CHECKs zusätzliche Bedingungen, die selbst zu definieren sind. Sie stellen eine Sonderform der CONSTRAINTs dar. Die Syntax lautet wie folgt: CONSTRAINT <Spaltenname> CHECK. Hinter dem Schlüsselwort CHECK ist die Prüfbedingung zu formulieren. Dazu ein konkretes Beispiel: Für das Attribut Kundennummer ist nur ein numerischer Wert größer als Null zulässig:
CREATE TABLE 'Kunde' (
'Kundennummer' BIGINT,
CHECK (Kundennummer >0)
);
Zum Ende dieses Abschnitts lohnt es sich, einen Blick auf Listing 2.2 zu werfen. Es zeigt die Formulierung eines CREATE TABLE-Befehls zur vollständigen Erzeugung einer Tabelle mit den Attributen Kundennummer, Name und Vorname. Gegenüber dem o. g. Beispiel wurde jedoch angegeben, dass die Kundennummer als Primärschlüssel der Tabelle fungiert, ein Wert zwingend zu erfassen ist (NOT NULL) und dieser Wert automatisch mit dem Einfügen eines Datensatzes vergeben wird. Für Vor- und Nachname können Zeichenketten mit maximal 100 Zeichen erfasst werden. Auch dabei ist die Eingabe des Nachnamens zwingend. Mit einem CHECK wird zusätzlich verfügt, dass der numerische Wert der Kundennummer > 0 sein muss. An dieser Stelle ist also der Unterschied zwischen NOT NULL (zwingende Erfassung eines Werts, ggf. auch die Zahl „0“) und der Bedingung >0 (in Bezug auf den numerischen Wert) zu beachten.
CREATE TABLE 'Kunde' (
'Kundennummer' BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
'Name' VARCHAR( 100 ) NOT NULL,
'Vorname' VARCHAR( 100 ),
CHECK (Kundennummer >0)
);
Listing 2.2
Die Struktur modifizieren: Tabellen ändern und löschen
Selbstverständlich können Tabellen geändert oder gelöscht werden. Um später die Struktur einer Tabelle zu ändern, dient der Befehl ALTER TABLE. Attribute können verändert, gelöscht oder neu hinzugefügt werden. Beispielsweise fügt man wie in Listing 2.3 eine neue Spalte hinzu.
ALTER TABLE <Tabelle>
ADD <Spaltenbeschreibung>
Spaltenbeschreibung :=
<Spalte> <Wertebereich> <ZUSATZATTRIBUTE>
[ADD Spaltenbeschreibung]
Listing 2.3
Das Ändern einer Spalte ist wie in Listing 2.4 möglich.
ALTER TABLE <Tabelle>
CHANGE <Spaltenbeschreibung>
Spaltenbeschreibung :=
<SPALTE_ALT> <SPALTE_NEU> <WERTEBEREICH_NEU>
<ZUSATZATTRIBUTE_NEU>
[CHANGE Spaltenbeschreibung]
Listing 2.4
Zum Löschen von Tabellen steht der Befehl DROP TABLE zur Verfügung. Diesem Schlüsselwort folgt der Name der betreffenden Tabelle. Also: DROP TABLE <Tabelle>;. Je nach Datenbank erfolgt ein unmittelbares Löschen oder zunächst ein vorläufiges Entsorgen über den Datenbankpapierkorb. Ein Nachlesen im DB-Handbuch kann an dieser Stelle hilfreich und notwendig sein.
Das Einfügen von Datensätzen
Nachdem die Struktur der Datenbank feststeht und diese aufgebaut ist, können Datensätze in ihr abgelegt werden. Natürlich ist es auch möglich, vorhandene Datensätze anderer Datenbanken und Tabellen zu importieren. Ein problemloser Import setzt einen identischen Aufbau der Tabellen und Kompatibilität der Datentypen voraus. Der Import von Daten wird in diesem Kapitel jedoch nicht behandelt. Der Befehl zum Einfügen eines Datensatzes lautet INSERT. Zunächst die Syntax des Befehls:
INSERT INTO Tabellenname
(Spalten-Name1, Spalten-Name2, …)
VALUES (Wert-Spalten-Name1, Wert-Spalten-Name2…);
An einem einfachen Beispiel wird es deutlich:
INSERT INTO Lieferadresse
(Ort, PLZ, Anschrift, Name)
VALUES ('Berlin', 98590, Hamburger Str. 7',
'Oliver');
Zur Erläuterung: Dem Schlüsselwort INSERT folgen der Name der betreffenden Tabelle und die Angabe zu den angesprochenen Feldern. Hinter Values werden die eigentlichen Daten angegeben. Zu beachten ist, dass die Reihenfolge wie bei der Benennung der Felder eingehalten wird.
Die richtigen Fragen stellen: Daten abfragen
Was nützt es uns, wenn nun alle relevanten Informationen in unserer Datenbank in Form von einzelnen Datensätzen vorhanden sind? Wir kommen daher zur Königsdisziplin: die Datenabfrage. Es handelt sich wohl um die am meisten genutzte Funktion der Sprache SQL. Zentraler Befehl ist die Anweisung SELECT. Deren allgemeiner Aufbau lautet folgendermaßen:
SELECT Spaltenname1, Spaltenname2,…
FROM Tabellenname
WHERE Bedingung
ORDER BY Sortierung;
Der SELECT-Operator besteht aus mehreren Teilen, die als Klauseln bezeichnet werden (Tabelle 2.1).
Klausel |
Beschreibung |
SELECT |
Legt fest, welche Spalten der Tabelle(n) in die Ergebnismenge zu übernehmen sind. |
FROM |
Spezifiziert die Tabellen, aus denen die Daten stammen. Werden Daten aus mehreren Tabellen entnommen, so wird angegeben, wie sie miteinander verbunden werden. |
WHERE |
Dient der Angabe von Bedingungen. Daten/Datensätze, die diesen Bedingungen genügen, werden aus den Tabellen ausgewählt. |
GROUP BY |
Dient dem Zusammenfassen von Zeilen mit gemeinsamen Werten. |
HAVING |
Mit HAVING können weitere Bedingungen auf Gruppenebene formuliert werden. |
ORDER BY |
Es kann angegeben werden, dass die Anordnungen der Zeilen der Ergebnismenge nach einem bestimmten Kriterium (Spalte) sortiert ausgegeben werden. |
Tabelle 2.1: Klauseln ds SELECT-Operators
Die Anwendung der einzelnen Klauseln wird klarer, wenn man sich deren Syntax und dann konkrete Beispiele ansieht oder noch besser selbst ausprobiert. Bei der Formulierung von Datenbankabfragen spielt der Mengenbegriff eine große Rolle (Kasten: Begriff der Menge). Ein einfaches Beispiel für eine Abfrage lautet:
SELECT b.Bestellnummer, k.Vorname, p.Anzahl, p.Preis,
b.Datum
FROM posten p, bestellung b, kunde k
WHERE b.Bestellnummer = p.Bestellnummer
Begriff der Menge
Die Menge ist eines der wichtigsten und grundlegenden Konzepte der Mathematik. Man fasst im Rahmen der Mengenlehre einzelne Elemente, beispielsweise Zahlen, zu einer übergeordneten Einheit zusammen. Eine Menge muss kein Element enthalten. In diesem Fall handelt es sich um die leere Menge. Bei der Beschreibung einer Menge geht es ausschließlich um die Frage, welche Elemente in ihr enthalten sind. Ein mehrfaches Vorkommen von Elementen gleichen Werts bzw. die Reihenfolge der Elemente in der Menge spielt keine Rolle. Mittels Mengenoperationen wird das Verhältnis von Elementen zweier Mengen beschrieben. Man unterscheidet:
Ausgewählt werden die Attribute Bestellnummer, Vorname, Anzahl, Preis und Datum. Diese Angaben erfolgen nach dem Schlüsselwort SELECT. Woher stammen diese Informationen? Hinter FROM werden die Tabellen angegeben und ein Kürzel für die obige SELECT-Anweisung vergeben. Das Wichtigste folgt nach der WHERE-Klausel: die Formulierung der Bedingung. Lässt man sie weg, werden alle Datensätze mit den angegebenen Attributen ausgegeben.
Betrachten wir an einem einfachen Beispiel die GROUP BY-Klausel. Listing 2.5 zeigt die Ausgangssituation. Aus den Tabellen Posten, Kunde und Bestellung wählen wir die Attribute Name, Artikelnummer, Preis und Anzahl. Zusätzlich definieren wir die neu berechnete Größe Umsatz als Produkt von Preis und Anzahl: p.Preis * p.Anzahl AS Umsatz. Es werden sowohl Verknüpfungen zwischen den Tabellen Kunde und Bestellung (k.Kundennummer = b.Kundennummer), als auch zwischen Bestellung und Posten (b.Bestellnummer = p.Bestellnummer) hergestellt. Datensätze mit gleichem Wert bezüglich des Attributs Name werden mehrfach ausgegeben. Oftmals ist jedoch eine Zusammenfassung sinnvoll.
SELECT k.Name, p.Artikelnummer, p.Preis, p.Anzahl, p.Preis
* p.Anzahl AS Umsatz
FROM Posten p, Kunde k, Bestellung b
WHERE k.Kundennummer = b.Kundennummer
AND b.Bestellnummer = p.Bestellnummer
Listing 2.5
Eine Verdichtung der Informationen kann man erreichen, indem man je betreffender Person (Attribut Name) eine Zusammenfassung vornimmt. Das Schlüsselwort lautet GROUP BY. Die modifizierte Abfrage ist in Listing 2.6 zu sehen.
SELECT k.Name, SUM( p.Preis * p.Anzahl ) AS Umsatz
FROM Posten p, Kunde k, Bestellung b
WHERE k.Kundennummer = b.Kundennummer
AND b.Bestellnummer = p.Bestellnummer
GROUP BY k.Name;
Listing 2.6
Das Verdichten von Daten ist eine zeitaufwändige Operation, insbesondere dann, wenn alle Tupel einer Relation zu berücksichtigen sind. Solche Operationen können die gleichzeitige Benutzung eines relationalen Datenbank-Management-Systems (RDBMS) für andere Benutzer stark einschränken. Bei der heutigen Menge an Daten ist es jedoch oft zwingend erforderlich, die Einzeldaten zu wenigen, aussagekräftigen Kennzahlen zusammenzufassen. Hierfür wurde der so genannte CUBE-Operator als Option zu GROUP-BY-Klausel eingefügt. Dieser Operator bewirkt, dass alle möglichen Kombinationen zur Verdichtung der Daten geliefert werden. Dabei existieren verschiedene Schreibweisen:
Die erste Notation entspricht dem Vorschlag auf der Basis von SQL-99. Die zweite Schreibweise ist eine ältere Form. Neben der einfacheren Formulierung erlaubt der CUBE-Operator dem DBMS einen Ansatz zur Optimierung, indem stärker verdichtete Aggregate auf weniger starken aufbauen und sehr große Relationen nur einmal eingelesen werden müssen [3]. Diese Begriffe stammen aus dem Themenbereich des Data Warehouse (Kasten: Data Warehouse). Die Hauptaufgabe ist das Verdichten von Daten.
Data Warehouse
Ein Data Warehouse ist eine Systemlösung, die der Unterstützung des Managements durch die Bereitstellung von selektierten, verdichteten und fachlich aufbereiteten Informationen dient [4]. Ein solches Data Warehouse wird in der Regel getrennt von den operativen Datenbanksystemen aufgebaut und betrieben. Charakteristisch sind eine thematische Ausrichtung, Vereinheitlichung, Langfristigkeit und eine Zeitorientierung.
Für die Analyse der vorliegenden Informationen kommen oft so genannte OLAP-Werkzeuge (Online Analytical Processing) zur Anwendung. Derartige Werkzeuge sind speziell auf die Analyse mehrdimensionaler Datenbestände ausgelegt. Beispielhaft ist die dreidimensionale Sichtweise auf einen solchen Datacube (in Form eines Würfels) in Abbildung 2.3 dargestellt.
Abbildung 2.3 Beispiel für einen dreidimensionalen Datenwürfel [5]
Erläutern wir noch beispielhaft die ORDER-BY-Klausel: ORDER BY stellt eine zusätzliche Sortierfunktion dar, die auf eine oder mehrere Spalten angewendet werden kann. Generell gibt es zwei Arten der Sortierung: aufsteigend (ASC) und absteigend (DESC). Ein Beispiel sagt bekanntermaßen mehr als tausend Worte:
SELECT k.Vorname
FROM Kunden k
ORDER BY k.Vorname ASC;
Was Sie als Ergebnis bekommen, sind die Vornamen aller Einträge der Tabelle Kunden. Die Ausgabe erfolgt aufsteigend sortiert.
Ändern von Datensätzen
Der Befehl UPDATE ist für das Ändern von Daten zuständig:
UPDATE Tabellenname
SET Spaltenname = Wert
WHERE Bedingung;
Auch hier darf die WHERE-Klausel nicht vergessen werden, da man sonst alle Datensätze einer Tabelle im betreffenden Feld auf den neuen Wert anpasst. Zum Beispiel:
UPDATE mitarbeiter
SET gehalt = gehalt*1.2;
Diese Aktion dürfte Sie mit Sicherheit bei allen Kollegen beliebt machen. Aber mit der Kündigung durch den Chef ist zu rechnen, da ohne WHERE-Klausel das Gehalt aller Mitarbeiter pauschal um 20 % erhöht wird. Korrekterweise ist die eingrenzende Bedingung hinzuzufügen. Jetzt kommt nur der Mitarbeiter mit der Personalnummer 5364 in den Genuss einer Gehaltserhöhung:
UPDATE mitarbeiter
SET gehalt = gehalt*1.2;
WHERE personalNR = 5364;
Auch das Schachteln von Anweisungen ist in SQL möglich. Listing 2.7 zeigt die Änderung von Daten, wobei für die Auswahl (WHERE-Klausel) eine untergeordnete Abfrage in Form einer SELECT-Abfrage durchgeführt wird.
UPDATE Tabellenname
SET Spaltenname = Wert
WHERE Spaltenname IN (
SELECT Spaltenname FROM Tabellenname);
Listing 2.7
Niederreißen: Löschen von Datensätzen
Alle Informationen, die erfasst wurden, können auch wieder gelöscht werden. An dieser Stelle ist ein warnender Hinweis notwendig: Oft wurden schon zu viele oder falsche Daten gelöscht. Wie kann das passieren? Eigentlich recht einfach: Die zu löschenden Daten müssen mittels Bedingung (WHERE-Klausel) spezifiziert werden. Fehlt diese Klausel oder wird sie nicht richtig formuliert, können irrtümlicherweise die falschen Daten aus der Datenbank entfernt werden. Zum Löschen von Daten steht unter SQL der Befehl DELETE zur Verfügung. Durch das Wissen aus den vorherigen Abschnitten überrascht uns die allgemeine Syntax des Befehls wenig:
DELETE FROM Tabellenname
WHERE Spaltenname = Wert;
Arbeitet man allerdings mit einem komplexen Datenbankschema (mehrere Tabellen, die untereinander in Beziehung stehen), so ist darauf zu achten, dass sich die Datenbank nach dem Löschvorgang noch in einem konsistenten Zustand befindet. Das gilt umso mehr, wenn man beispielsweise die Datenbankoperationen unmittelbar in den Quellcode eines Programms einbindet und damit ggf. Sicherungsmechanismen der Datenbankverwaltung umgeht. Ein Beispiel dafür: Es existieren die Tabellen Kunde und Bestellungen. Löscht man einen Kunden aus der erstgenannten Tabelle, so muss man sicherstellen, dass die zugehörigen Datensätze aus der Tabelle Bestellungen auch entfernt werden. Zusammenfassend: Abhängigkeiten sind zwingend zu beachten.
Wie geht es weiter?
Die wichtigsten SQL-Befehle haben wir kennen gelernt. SQL kann jedoch noch mehr. Oder mit anderen Worten: Mittels SQL kann man auch komplexere Funktionalitäten eines Datenbank-Management-Systems abrufen. Gemeint sind Themen wie Datensichten und Transaktionen. Dazu jedoch im nächsten Kapitel mehr.
Links & Literatur
[2] http://www.openoffice.org/
[5] http://www.controlligent.com/de/index.htm
[6] Dr. Krypczyk, Veikko: „Daten, Daten, Daten. Teil 1: Grundlagen des Datenbankentwurfs, ER-Modell, Normalisierung“ in Entwickler Magazin 2.2011, S. 68–74.