Einführung in SQL: PDF-Version
Diese Seite ist nicht zum Lesen oder Ausdrucken gedacht.
- Es wird auf ein Inhaltsverzeichnis verzichtet.
- Es gibt keine Verweise zwischen den Kapiteln.
- Verschiedene Teile werden nicht sinnvoll angezeigt oder fehlen.
Dies ist nur die Grundlage dafür, dass mit LaTeX eine vollständige PDF-Version erstellt wird.
Nehmen wir einmal an, dass ein Versicherungsunternehmen einen neuen Geschäftsführer bekommt. Dieser sammelt zunächst Informationen, um sich mit dem Unternehmen vertraut zu machen.
Dieses Buch richtet sich an: | Schüler, Studenten und Andere, die sich mit relationalen Datenbanken beschäftigen wollen bzw. müssen |
Was dieses Buch erreichen will: |
Die Schwerpunkte liegen hierbei auf folgenden Themen:
|
Was dieses Buch nicht sein soll: |
|
Ein Einstieg |
Datenbanken enthalten Informationen
BearbeitenDer Geschäftsführer findet in seinem Büro keine Akten, sondern nur einen PC. Nach dem Einschalten und Anmelden bekommt er folgende Begrüßung:
Wikibooks-Beispieldatenbank (C) Wiki-SQL Bitte geben Sie einen SQL-Befehl ein: sql >
Damit wird der Geschäftsführer auf mehrere Punkte der Organisation in seinem Unternehmen hingewiesen.
- Die Daten sind in einer Datenbank zusammengefasst mit dem Namen Beispieldatenbank.
- Diese Datenbank (DB) stammt von der Firma Wikibooks.
- Um etwas mit dieser DB zu machen, soll er hinter "sql >" einen SQL-Befehl eingeben.
Die Inhalte dieser Datenbank sind in Beispieldatenbank beschrieben, Einzelheiten in Tabellenstruktur der Beispieldatenbank erläutert.
Das Datenbanksystem (DBMS) der Firma Wikibooks wird als Wiki-SQL bezeichnet. Dies bedeutet, dass die Beispieldatenbank zu Wiki-SQL passen muss – in Bezug auf das Dateiformat, die interne Struktur und den Aufbau der Befehle.
SQL ist die Abkürzung für Structured Query Language, also strukturierte Abfragesprache. Der Geschäftsführer hat schon einmal davon gehört, dass dies eine standardisierte Form ist, um mit Datenbanken zu arbeiten, und probiert es aus.
Abfrage nach den Mitarbeitern
BearbeitenAls erstes möchte er eine Liste seiner Mitarbeiter haben. Er überlegt, wie die Abfrage lauten könnte:
Hole Name und Vorname von den Mitarbeitern
Da die Abfrage lediglich eine Fehlermeldung erzeugt, probiert er, seine Abfrage in unterschiedlicher Form auf Englisch durchzuführen (leider auch ohne Erfolg); diese könnten etwa so aussehen:
Get Name, Vorname From Mitarbeiter Fetch Name, Vorname From Mitarbeiter Find Name, Vorname From Mitarbeiter Search Name, Vorname From Mitarbeiter
Und schließlich bekommt er mit der folgenden Eingabe keine Fehlermeldung, sondern ein Ergebnis:
Select Name, Vorname From Mitarbeiter
Die Liste ist ihm zu lang und unübersichtlich. Er will sie zunächst einmal sortieren und probiert Sortierung, Reihenfolge aus, bis es passt:
Select Name, Vorname From Mitarbeiter order by Name
Dann möchte er die Auswahl einschränken, nämlich auf die Mitarbeiter mit Anfangsbuchstaben 'A'. Wieder nach ein paar Versuchen weiß er, dass nicht WITH, sondern WHERE die Lösung liefert.
Select Name, Vorname From Mitarbeiter where Name < 'B'
Jetzt möchte er beide Abfragen verbinden:
Das kann doch nicht sein?! WHERE ist doch das richtige Verfahren für eine solche Einschränkung?! Kommt es etwa auf die Reihenfolge der Zusätze an?
Welche Informationen sind denn sonst gespeichert? Er weiß auch (z. B. vom DIR-Befehl des Betriebssystems), dass ein Sternchen anstelle von alles gesetzt werden kann. Und siehe da, es klappt:
Prima, damit ist klar, wie Informationen aus der Datenbank geholt werden:
SELECT <Liste von Teilinformationen> FROM <Teil der Datenbank> WHERE <Bedingung> ORDER BY <Sortierung>
Neuaufnahme bei den Mitarbeitern
BearbeitenAls nächstes möchte der Geschäftsführer sich selbst als Mitarbeiter speichern. Schnell kommt er auf das „Grundgerüst“ des Befehls:
INSERT INTO Mitarbeiter VALUES
Wenn er danach seinen Namen schreibt, bekommt er wieder eine Fehlermeldung mit "token unknown". Er hat aber schon von der Benutzung von Klammern in der EDV gehört.
Na gut, dann wird eben ausdrücklich angegeben, dass erstmal nur Name und Vorname einzutragen sind.
Ach so, die Personalnummer muss angegeben werden, und vermutlich alles andere auch. Aber die ID ist doch gar nicht bekannt? Nun, immerhin sind wir auf diese Grundstruktur des Befehls gekommen:
INSERT INTO <Teil der Datenbank> [ ( <Liste von Teilinformationen> ) ] VALUES ( <Liste von Werten> )
SQL und natürliche Sprache
BearbeitenOffensichtlich sind die Befehle von SQL der natürlichen englischen Sprache nachempfunden. (Die englische Sprache hat wegen der klaren Satzstruktur und Grammatik insofern natürlich Vorteile gegenüber der komplizierten deutschen Syntax.)
SELECT für Abfragen
BearbeitenUm Daten abzufragen, gibt es den SELECT-Befehl mit folgenden Details:
SELECT wähle aus [ DISTINCT | ALL ] verschiedene | alle <Liste von Teilinformationen> FROM <Teil der Datenbank> aus [ WHERE <Bedingungen> ] wobei [ GROUP BY <Liste von Spalten> ] gruppiert durch [ HAVING <Bedingungen> ] wobei [ ORDER BY <Sortierung> ] sortiert durch
INSERT für Neuaufnahmen
BearbeitenUm Daten neu zu speichern, gibt es den INSERT-Befehl mit folgenden Details:
INSERT INTO <Teil der Datenbank> einfügen in [ <Liste von Teilinformationen> ] VALUES ( <Liste von Werten> ) Werte /* oder */ INSERT INTO <Teil der Datenbank> einfügen in [ <Liste von Teilinformationen> ] SELECT <Ergebnis einer Abfrage> durch eine Auswahl
UPDATE für Änderungen
BearbeitenUm Daten zu ändern, gibt es den UPDATE-Befehl mit folgenden Details:
UPDATE <Teil der Datenbank> aktualisiere SET <spalte1> = <wert1> [ , setze fest <spalte2> = <wert2> , usw. <spalten> = <wertn> ] [ WHERE <bedingungsliste> ]; wobei
DELETE für Löschungen
BearbeitenUm Daten zu löschen, gibt es den DELETE-Befehl mit folgenden Details:
DELETE FROM <Teil der Datenbank> lösche aus [ WHERE <bedingungsliste> ]; wobei
CREATE TABLE bei der Struktur einer Tabelle
BearbeitenUm eine neue Tabelle zu erstellen, gibt es den CREATE TABLE-Befehl mit folgenden Einzelheiten:
CREATE TABLE <Teil der Datenbank> erzeuge Tabelle ( <Spaltenliste>, <weitere Angaben> )
So einfach kann es gehen? Dann kann man doch auch eigene Daten erzeugen, speichern, abfragen und auswerten.
Zusammenfassung
BearbeitenDie einzelnen Teile der SQL-Befehle sind leicht verständlich; und es scheint nur wenige Befehle zu geben, die man als „Anfänger“ wirklich lernen muss. Natürlich kann man nicht sofort alle Möglichkeiten erfassen. Aber angesichts des begrenzten Umfangs und der klaren Struktur lohnt es sich, sich näher damit zu befassen. Dies will dieses Buch erleichtern.
Siehe auch
BearbeitenWikipedia hat einen Artikel zum Thema SQL.
Weitere Informationen gibt es in folgenden Kapiteln:
Einleitung |
In diesem Kapitel erhalten Sie Informationen über Inhalt und Aufbau des Buches.
Einführung
BearbeitenDie Abfragesprache SQL ist die etablierte Sprache für die Arbeit mit relationalen Datenbankmanagementsystemen (DBMS). Es existieren verschiedene Standards, und jeder Hersteller von DBMS hat seine eigenen Erweiterungen und Besonderheiten zu den Standards.
Das Buch soll eine Einführung in die Sprache SQL bieten. Ziel ist es, dass der Leser nach dem Durcharbeiten folgende Aufgaben selbständig lösen kann:
- Eigene Abfragen für relationale Datenbanken erstellen
- Manipulieren von Daten (Einfügen, Ändern und Löschen)
- Eigene einfache relationale Datenbanken aufbauen
- Bestehende Datenbankstrukturen erweitern
Um die Ziele zu erreichen, wird SQL anhand praxisnaher Beispiele erläutert.
Die Beispiele im Buch wurden unter mindestens einem der folgenden DBMS getestet:
- Firebird
- MS-SQL Server 2005 oder 2008
- MySQL 4.1 oder 5
- Oracle 9, 10 oder 11
Vorzugsweise werden allgemeingültige Schreibweisen nach dem SQL-Standard (siehe unten) benutzt. Deshalb sollten die Befehle in aller Regel auf allen gängigen DBMS funktionieren und höchstens kleinere Änderungen benötigen. Dort, wo eine spezielle Schreibweise wesentlich abweicht, wird das ausdrücklich erwähnt.
Achtung: Wegen der vielen unterschiedlichen Schreibweisen der SQL-Befehle ist eine vollständige Prüfung leider nicht möglich. Sie müssen in allen Zweifelsfällen in der Dokumentation Ihres DBMS die passende Schreibweise nachlesen. |
Geschichte von SQL
BearbeitenSQL ist aus IBM's SEQUEL in den siebziger Jahren entstanden. Der Erfolg der Sprache SQL ist sicherlich auch darin begründet, dass die Sprache einfach aufgebaut ist und sich an der englischen Umgangssprache orientiert. Von SQL gibt es verschiedene Standards. Diese sind:
- Erster SQL-Standard (1986 ANSI)
- SQL2 bzw. SQL-92 (1992)
- SQL3 bzw. SQL:1999 (1999 ISO)
- SQL:2003 (2003)
- SQL:2008 (2008)
Hierbei ist zu beachten, dass die meisten Datenbankmanagementsysteme SQL2 unterstützen. Die neueren Versionen sind in der Regel nur teilweise oder gar nicht in den einzelnen Datenbankmanagementsystemen umgesetzt.
Alles, was in diesem Buch als SQL-Standard, also als „offizielle SQL-Feststellung“ angegeben wird, bezieht sich auf die SQL-Dokumente von 2003
.Übersicht über Datenbankmanagementsysteme
BearbeitenAllgemein
BearbeitenDatenbanken sind Systeme (Daten und Programme) zur Verwaltung von Daten. Es gibt verschiedene Konzepte:
- Relationale DBMS
- Objektrelationale DBMS
- Objektorientierte DBMS
Bei Wikipedia gibt es eine Liste der Datenbankmanagementsysteme.
Da SQL die Abfragesprache für relationale Datenbanken ist, bezieht sich das Buch nur auf diese Art von Datenbanken. Das Konzept hinter den relationalen Datenbanken wird im nächsten Kapitel erläutert.
Kommerzielle Datenbankmanagementsysteme
Bearbeiten- DB2
- Informix
- Interbase
- Microsoft SQL Server
- Oracle
- Sybase
Microsoft und Oracle bieten auch kostenlose Express-Versionen mit eingeschränkten Möglichkeiten oder Nutzungsrechten an.
Freie Datenbankmanagementsysteme
Bearbeiten- Firebird
- MariaDB
- MySQL
- PostgreSQL
- SQLite
Bei MySQL ist das duale Lizenzsystem zu beachten: je nach Nutzungsbedingungen frei oder kostenpflichtig.
MariaDB ist 2009 als Abspaltung von MySQL entstanden. Seit MySQL 2010 von Oracle übernommen wurde, wurde in vielen Systemen MySQL durch MariaDB ersetzt. Soweit dieses Buch MySQL erwähnt, können Beispiele und Erläuterungen genauso gut auf MariaDB bezogen werden.
- Die Unterscheidung zwischen „frei“ und „kommerziell“ ist nicht korrekt, sondern wird nur der Einfachheit halber gemacht. Bei den „freien“ DBMS steht die freie Verfügbarkeit im Vordergrund, auch wenn Kosten anfallen können oder das DBMS nicht als Open Source-Projekt entwickelt wird. Bei den „kommerziellen“ DBMS steht das gewerbliche Interesse des Anbieters im Vordergrund, auch wenn es kostenlose Lizenzen gibt.
Weitere Systeme zur Datenverwaltung
BearbeitenDie folgenden Dateisysteme enthalten keine Datenbanken im eigentlichen Sinne, sondern Dateien für strukturierte Daten. Auch diese können (je nach verwendetem Programm) in eingeschränktem Umfang mit SQL-Befehlen umgehen.
- dBASE und seine Varianten
- MS-Access
- das Datenbankmodul Base von LibreOffice
- Paradox
Auf diese Systeme gehen wir nicht ein. Sie müssen in der jeweiligen Programm-Dokumentation nachlesen, welche Befehle und Optionen möglich sind.
Regeln für die Beispiele
BearbeitenDas Buch ist grundsätzlich schrittweise aufgebaut. Aber nicht immer können in einem Beispiel nur bereits bekannte Begriffe verwendet werden. Wenn in seltenen Fällen Bestandteile erst in einem späteren Kapitel erläutert werden, dann gibt es ausdrückliche Hinweise, beispielsweise hier:
- Der INSERT-Befehl in DML (2) – Daten speichern muss korrekt mit Datentypen umgehen und benutzt dazu auch Funktionen.
- Auch für das Erstellen einer Tabelle in DDL – Struktur der Datenbank muss genau auf die Datentypen geachtet werden.
Wenn Sie die SQL-Begriffe aus dem Englischen ins Deutsche übersetzen, sollten Sie den Zusammenhang auch ohne Hin- und Herblättern verstehen.
Die Beispiele für SQL-Befehle werden nach den folgenden Regeln geschrieben.
- Alle SQL-Befehle und Schlüsselwörter, wie z. B. SELECT, INSERT, DELETE, WHERE, ORDER BY, werden vorzugsweise groß geschrieben. SQL selbst verlangt das nicht, sondern arbeitet ohne Berücksichtigung von Groß- und Kleinschreibung (case insensitive); dort werden SELECT, select und sogar sElEcT gleich behandelt.[1]
- Eigentlich sollten Tabellen- und Spaltennamen vorzugsweise ebenfalls groß geschrieben werden, und zwar ohne Anführungszeichen. Aber in der Praxis werden solche Namen meistens „gemischt“ geschrieben.
- String-Literale werden mit einfachen Anführungszeichen gekennzeichnet.
- SQL-Befehle werden mit einem Semikolon abgeschlossen.
- Optionale Argumente (d. h. solche, die nicht unbedingt erforderlich sind) werden in [ ] eingeschlossen.
- Variable Argumente (d. h. solche, die mit unterschiedlichem Inhalt vorkommen) werden in < > eingeschlossen.
- Wahlmöglichkeiten werden durch das Pipe-Zeichen | (den senkrechten Strich) getrennt.
- Listen werden gekennzeichnet durch <inhaltliste>, wobei dies eine Kurzform ist für <inhalt1, inhalt2, ... inhaltn>.
- Sofern das Ergebnis einer Abfrage im Ausgabefenster aufgeführt wird, handelt es sich überwiegend nur um einen Teil des Ergebnisses, gleichgültig ob darauf hingewiesen wird oder nicht.
Die Struktur eines Befehls steht in einem Rahmen mit Courier-Schrift:
SELECT <spaltenliste> FROM <tabellenname> [ WHERE <bedingungsliste> ] ;
So wird eine Aufgabenstellung angezeigt, die mit dem danach folgenden Beispiel erledigt werden soll.
Ein konkretes Beispiel wird mit einem komplexen Rahmen und unterschiedlichen Inhalten (farblicher Hinweis bei Fehlern, mit oder ohne Kopf- und Fußzeile, mit oder ohne Ausgabefenster) dargestellt:
Siehe auch
BearbeitenUnter Weblinks stehen viele zusätzliche Hinweise.
XAMPP bietet eine relativ einfache Installation für eine Arbeitsumgebung mit Webserver Apache, Datenbank MariaDB und PHP-Skriptsprache. MariaDB ist sehr ähnlich zu MySQL und eignet sich gut, SQL zu lernen und zu testen.
- ↑ In der PDF-Version verhindern zurzeit technische Einschränkungen, dass alle diese Begriffe automatisch großgeschrieben werden.
Relationale Datenbanken |
Um mit SQL auf relationalen Datenbanken arbeiten zu können, muss der Anwender ein Grundverständnis für relationale Datenbanken haben. Dieses soll in diesem Kapitel vermittelt werden.
Grundstruktur von relationalen Datenbanken
BearbeitenBevor man mit der Sprache SQL beginnt, muss das Grundprinzip von relationalen Datenbanken geklärt werden. Relationale Datenbanken versuchen einen Bestandteil der Realität in einem Datenmodell abzubilden.
Für diese Datenmodelle gibt es verschiedene Abstraktionsebenen. In der Regel unterscheidet man zwischen Entitätenmodell und Tabellenmodell. Da es sich hier um eine Einführung handelt, beschränken wir uns auf das Tabellenmodell, das weniger Theorie voraussetzt.
Grundsätzlich sollen dabei Objekte der Realität betrachtet werden, welche zueinander in Beziehung stehen. Zum einen werden die Objekte mit ihren Eigenschaften untersucht: Objekte mit gleichen Eigenschaften werden zusammengefasst; Objekte mit verschiedenen Eigenschaften werden getrennt. Zum anderen werden die Beziehungen zwischen unterschiedlichen Objekten behandelt. Außerdem geht es darum, möglichst keine Informationen unnötigerweise doppelt zu speichern.
Beispielhafte Struktur
BearbeitenIn unserer Beispieldatenbank simulieren wir dazu eine Versicherungsgesellschaft. Unter anderem werden die Verträge mit den dazugehörigen Kunden betrachtet:
- Ein Versicherungsvertrag ist durch die Vertragsnummer, das Datum des Abschlusses, den Versicherungsnehmer, die Art und die Höhe der Police gekennzeichnet.
- Ein Versicherungsnehmer kann bei der Versicherung einen oder mehrere Verträge haben. Es kann Kunden geben, die aktuell keinen Vertrag haben; aber es kann keinen Vertrag ohne zugehörigen Kunden geben.
- Ein Versicherungsnehmer ist gekennzeichnet durch seinen Namen und Anschrift und bei Personen einen Vornamen und ein Geburtsdatum. Außerdem verfügt er „üblicherweise“ über eine Kundennummer, die ihn eindeutig kennzeichnet.
Nun könnte man alle Verträge wie folgt in einer einzigen Datei, z. B. einem Arbeitsblatt einer Tabellenkalkulation, speichern:
NUMMER ABSCHLUSSDATUM ART NAME ANSCHRIFT BETREUER TELEFON DG-01 03.05.1974 TK Heckel Obsthandel GmbH 46282 Dorsten Pohl, Helmut 0201/4014186 Mobil (0171) 4123456 DG-02 11.06.1975 TK Heckel Obsthandel GmbH 46282 Dorsten Pohl, Helmut 0201/4014186 Mobil (0171) 4123456 DG-03 25.02.1977 TK Heckel Obsthandel GmbH 46282 Dorsten Pohl, Helmut 0201/4014186 Mobil (0171) 4123456 XC-01 07.08.1974 HP Antonius, Bernhard 45892 Gelsenkirchen Braun, Christian 0201/4014726 Mobil (0170) 8351647 RH-01 11.12.1976 VK Cornelsen, Dorothea 44577 Castrop-Rauxel Braun, Christian 0201/4014726 Mobil (0170) 8351647
Dadurch wird die Darstellung viel zu breit und damit unübersichtlich. Offensichtlich werden auch die persönlichen Daten eines Versicherungsnehmers und seines Betreuers „zu oft“ gespeichert. Es ist also sinnvoll, dies zu trennen – zum einen die Verträge:
NUMMER ABSCHLUSSDATUM ART KUNDE BETREUER DG-01 03.05.1974 TK 1 9 DG-02 11.06.1975 TK 1 9 DG-03 25.02.1977 TK 1 9 XC-01 07.08.1974 HP 2 10 RH-01 11.12.1976 VK 3 10
Zum anderen die Kunden:
NUMMER NAME ANSCHRIFT 1 Heckel Obsthandel GmbH 46282 Dorsten 2 Antonius, Bernhard 45892 Gelsenkirchen 3 Cornelsen, Dorothea 44577 Castrop-Rauxel
Und schließlich die zuständigen Sachbearbeiter (Betreuer):
NUMMER NAME TELEFON MOBIL 9 Pohl, Helmut 0201/4014186 (0171) 4123456 10 Braun, Christian 0201/4014726 (0170) 8351647
Durch die Angabe der Nummer (Kunde bzw. Betreuer) in den Aufstellungen ist eine klare Verbindung hergestellt. Außerdem zeigt die Wiederholung des Wortes „Mobil“ an, dass dieser Wert in einer eigenen Spalte eingetragen werden sollte.
Diese Trennung von Informationen sind Schritte bei der Normalisierung einer Datenbank .
Eigenschaften der Objekte
BearbeitenVor allem müssen wir uns Gedanken über die Eigenschaften der verschiedene Objekte machen. Dabei gibt es solche, die ein Objekt eindeutig kennzeichnen, andere, die immer anzugeben sind, und weitere, die nur unter manchen Umständen von Bedeutung sind.
Für einen Versicherungsnehmer gibt es u. a. folgende Eigenschaften:
- NUMMER ist eindeutig und eine Pflichtangabe.
- NAME, PLZ, ORT sind Pflichtangaben, ihre Inhalte können aber bei mehreren Versicherungsnehmern vorkommen.
- VORNAME und GEBURTSDATUM sind bei natürlichen Personen Pflicht, aber bei juristischen Personen (Firmen) irrelevant.
Für einen Versicherungsvertrag gibt es u. a. folgende Eigenschaften:
- NUMMER ist eindeutig und eine Pflichtangabe.
- Auch die anderen bisher genannten Eigenschaften sind Pflicht, aber sie sind nicht eindeutig.
Die verschiedenen Objekte stehen über die Kundennummer miteinander in Beziehung. Im Beispiel geht es um die Verknüpfung: „Ein Kunde kann einen oder mehrere Verträge oder auch keinen haben.“ Der letzte Fall „keinen Vertrag“ kommt erst am Schluss des Buches vor, wenn wir weitere Testdaten erzeugen.
In den relationalen Datenbanksystemen (DBMS) werden die Objekte als Tabellen dargestellt. Die Eigenschaften werden über die Spalten der Tabelle abgebildet. Eine Zeile (wahlweise als Datensatz bezeichnet) in der Tabelle entspricht genau einem Objekt in der Realität. Die Beziehungen zwischen Tabellen werden über Fremdschlüssel abgebildet.
Tabellen
BearbeitenTabellen sind zweidimensional gegliederte Informationen. Anzahl, Bezeichnung und Typ der Spalten (auch Felder oder Attribute genannt) werden durch die Definition der Tabelle festgelegt. Die Zeilen (Anzahl und Inhalte) sind variabel und entsprechen jeweils einem wirklichen Objekt des Typs, der in der Tabelle gesammelt wird.
So sieht ein Ausschnitt aus der Tabelle Abteilung der Beispieldatenbank aus:
Spaltenname ID KURZBEZEICHNUNG BEZEICHNUNG ORT Datentyp integer varchar(10) varchar(30) varchar(30) Zeilen 1 Fibu Finanzbuchhaltung Dortmund 2 Albu Anlagenbuchhaltung Dortmund 5 Vert Vertrieb Essen 6 Lagh Lagerhaltung Bochum
Diese Tabelle enthält also 4 Spalten und 12 Zeilen, von denen hier 4 angezeigt werden.
Dabei handelt es sich um eine Basistabelle (TABLE), die tatsächlich Informationen speichert. Daneben gibt es „virtuelle“ Arten von Tabellen, nämlich die VIEW als Sichttabelle und die Ergebnismenge (Resultset) als Ergebnis einer SELECT-Abfrage.
Eine View enthält eine fest vordefinierte Abfrage, die sich auf eine oder mehrere Tabellen bezieht. Aus Sicht des Anwenders sieht sie wie eine Basistabelle aus, ist aber nur eine Abbildung realer Tabellen. Ein Beispiel wäre ein Ausschnitt aus einer View Mitarbeiter_Bochum, nämlich der Mitarbeiter, die zu einer der Abteilungen in Bochum gehören:
PERSNR NAME VORNAME BEZEICHNUNG 60001 Aagenau Karolin Lagerhaltung 60002 Pinkart Petra Lagerhaltung 70001 Olschewski Pjotr Produktion 70002 Nordmann Jörg Produktion 120001 Carlsen Zacharias Forschung und Entwicklung 120002 Baber Yvonne Forschung und Entwicklung
Näheres zu Sichttabellen steht im Kapitel Erstellen von Views.
Jede Ergebnismenge hat zwangsläufig die Struktur einer Tabelle.
Ergänzend sei darauf hingewiesen, dass auch das DBMS selbst sämtliche Schemata in Systemtabellen speichert. Beispielsweise stehen bei Interbase und Firebird die Definition von TABLEs und VIEWs in der Tabelle RDB$RELATIONS und die dazugehörigen Felder (Spalten) in RDB$RELATION_FIELDS.
Spalten
BearbeitenSpalten bezeichnen die Elemente einer Tabellenstruktur. Sie werden eindeutig gekennzeichnet durch ihren Namen; diese Eindeutigkeit gilt innerhalb einer Tabelle, verschiedene Tabellen dürfen Spalten mit gleichem Namen (z. B. ID) haben. Außerdem gehört zur Definition einer Spalte der Datentyp; dies wird im Kapitel Datentypen behandelt.
Die Spalten (innerhalb einer Tabelle) werden intern nach Position geordnet. Spalten an verschiedenen Positionen können denselben Datentyp haben, aber niemals denselben Namen. Auf eine bestimmte Spalte wird fast immer über den Namen zugegriffen, nur äußerst selten über die Position.
Eine Spalte hat also einen Namen und einen Datentyp. Jede Zeile in einer Tabelle hat genau einen Wert für jede Spalte; wenn mehrere gleichartige Werte eingetragen werden sollen, werden mehrere Spalten benötigt. Jeder Wert in einer Zeile entspricht dem Datentyp der Spalte.
- Hinweis: In dieser Hinsicht unterscheiden sich Datenbank-Tabellen ganz wesentlich von denjenigen einer Tabellenkalkulation, bei der der Datentyp einzelner Zellen abweichen kann von der Spaltendefinition und einzelne Zellen zusammengezogen werden können.
Die Eigenschaft NULL für einen Wert ist eine Besonderheit, die vor allem Einsteiger gerne verwirrt. Dies bedeutet, dass einer Zelle (noch) kein Wert zugeordnet worden ist. Eine bessere Bezeichnung wäre etwas wie UNKNOWN; aber es heißt nun leider NULL. Bitte beachten Sie deshalb:
- Für ein Textfeld werden folgende Werte unterschieden:
- Der Wert ’’ ist ein leerer Text.
- Der Wert ’ ’ ist ein Text, der genau ein Leerzeichen enthält.
- Der Wert NULL enthält nichts.
- Für ein logisches Feld (Datentyp boolean) wird dies unterschieden:
- Der Wert TRUE bedeutet „wahr“.
- Der Wert FALSE bedeutet „falsch“.
- Der Wert NULL bedeutet „unbekannt“.
- Für ein Zahlenfeld wird dies unterschieden:
- Der Wert 0 ist eine bestimmte Zahl, genauso gut wie jede andere.
- Der Wert NULL bedeutet „unbekannt“.
Merke Der Wert NULL steht nicht für einen bestimmten Wert, sondern kann immer als „unbekannt“ interpretiert werden. |
Dies kann bei jeder Spalte allgemein festgelegt werden: Die Eigenschaft „NOT NULL“ bestimmt, dass in dieser Spalte der NULL-Wert nicht zulässig ist; wenn Daten gespeichert werden, muss immer ein Wert eingetragen werden (und sei es ein leerer Text). Wenn dies nicht festgelegt wurde, muss kein Wert eingetragen werden; der Feldinhalt ist dann NULL.
Bei SELECT-Abfragen (vor allem auch bei Verknüpfungen mehrerer Tabellen) gibt es unterschiedliche Ergebnisse je nachdem, ob NULL-Werte vorhanden sind und ob sie berücksichtigt oder ausgeschlossen werden sollen.
Verknüpfungen und Schlüssel
BearbeitenMit diesen Verfahren werden die Tabellen in Beziehung zueinander gebracht. Auch dies folgt der Vorstellung, dass die Wirklichkeit abgebildet werden soll.
Verknüpfungen
BearbeitenDiese, nämlich die Beziehungen zwischen den Tabellen, sind ein Kern eines relationalen Datenbanksystems. In der Beispieldatenbank bestehen unter anderem folgende Beziehungen:
- Die Tabelle Mitarbeiter verweist auf folgende Tabelle:
- Jeder Mitarbeiter gehört zu einem Eintrag der Tabelle Abteilung.
- Die Tabelle Zuordnung_SF_FZ verbindet Schadensfälle und Fahrzeuge und verweist auf folgende Tabellen:
- Jedes beteiligte Fahrzeug gehört zu einem Eintrag der Tabelle Fahrzeug.
- Jeder Schadensfall muss in der Tabelle Schadensfall registriert sein.
- Die Tabelle Versicherungsvertrag verweist auf folgende Tabellen:
- Jeder Vertrag wird von einer Person aus der Tabelle Mitarbeiter bearbeitet.
- Zu jedem Vertrag gehört ein Eintrag der Tabelle Fahrzeug.
- Zu jedem Vertrag gehört ein Eintrag der Tabelle Versicherungsnehmer.
Durch diese Verknüpfungen werden mehrere Vorteile erreicht:
- Informationen werden nur einmal gespeichert.
Beispiel: Der Name und Sitz einer Abteilung muss nicht bei jedem Mitarbeiter notiert werden. - Änderungen werden nur einmal vorgenommen.
Beispiel: Wenn die Abteilung umzieht, muss nur der Eintrag in der Tabelle Abteilung geändert werden und nicht die einzelnen Angaben bei jedem Mitarbeiter. - Der Zusammenhang der Daten wird gewährleistet.
Beispiel: Ein Versicherungsnehmer kann nicht gelöscht werden, solange er noch mit einem Vertrag registriert ist.
Damit dies verwirklicht werden kann, werden geeignete Maßnahmen benötigt:
- Jeder Datensatz muss durch einen Schlüssel eindeutig identifiziert werden können.
- Die Schlüssel der verschiedenen miteinander verknüpften Datensätze müssen sich zuordnen lassen.
Schlüssel
BearbeitenPrimaryKey (PK): Der Primärschlüssel ist eine Spalte in der Tabelle, durch die eindeutig jede Zeile identifiziert wird (gerne mit dem Namen ID). Es kann auch eine Kombination von Spalten als eindeutig festgelegt werden; das ist aber selten sinnvoll. In der Regel sollte diese Spalte auch keine andere „inhaltliche“ Bedeutung haben als die ID.
- Beispiele: Die Kombination Name/Vorname kann bei kleinen Datenmengen zwar praktisch eindeutig sein, aber niemals theoretisch; irgendwann kommt ein zweiter „Hans Müller“, und dann? Bei einem Mehrbenutzersystem werden häufig mehrere Einträge „gleichzeitig“ gespeichert; es ist besser, wenn das DBMS die Vergabe der ID selbst steuert, als dass die Benutzer sich absprechen müssen. In der Beispieldatenbank wird deshalb in der Tabelle Mitarbeiter zwischen der automatisch vergebenen ID und der ebenfalls eindeutigen Personalnummer unterschieden.
ForeignKey (FK): Über Fremdschlüssel werden die Tabellen miteinander verknüpft. Einem Feld in der einen Tabelle wird ein Datensatz in einer anderen Tabelle zugeordnet; dieser wird über den Primärschlüssel bereitgestellt. Es kann auch eine Kombination von Spalten verwendet werden; da sich der Fremdschlüssel aber auf einen Primärschlüssel der anderen Tabelle beziehen muss, ist dies ebenso selten sinnvoll. Die „Datenbank-Theorie“ geht sogar soweit, dass die Schlüsselfelder dem Anwender gar nicht bekannt sein müssen.
- Beispiele stehen in der obigen Aufstellung. Nähere Erläuterungen sind im Kapitel Fremdschlüssel-Beziehungen zu finden.
Index: Ein Suchbegriff dient zum schnellen Zugriff auf Datensätze innerhalb einer Tabelle. Die Mehrzahl lautet nach Duden ‚Indizes‘, auch ‚Indexe‘ ist möglich; in der EDV wird oft auch der englische Plural 'Indexes' verwendet. Dies gehört zwar nicht zum „Kernbereich“ eines relationalen DBMS, passt aber (auch wegen der umgangssprachlichen Bedeutung des Wortes „Schlüssel“) durchaus hierher.
- Der Primärschlüssel ist ein Suchbegriff, mit dem eindeutig ein Datensatz gefunden werden kann.
- Mit einem Index kann die Suche nach einem bestimmten Datensatz oder einer Datenmenge beschleunigt werden.
Beispiel: die Suche nach PLZ - Die Werte einer Spalte oder einer Kombination von Spalten sollen eindeutig sein.
Beispiel: die Personalnummer
Nähere Erläuterungen sind in den Kapiteln im Teil Erweiterungen ab DDL – Einzelheiten zu finden.
Siehe auch
BearbeitenÜber Wikipedia sind weitere Informationen zu finden:
- Relationale Datenbank
- Entitätenmodell und Entity-Relationship-Modell
- Normalisierung
- Tabellenkalkulation im Gegensatz zu Datenbank-Tabellen
- Nullwert
Normalisierung |
In diesem Kapitel werden einige Überlegungen angestellt, wie eine Datenbank konzipiert werden soll.
Dieses Kapitel geht über die Anforderungen einer Einführung hinaus; denn es richtet sich weniger an „einfache“ Anwender, die eine vorhandene Datenbank benutzen wollen, sondern an (künftige) Programmentwickler, die eine Datenbank entwerfen und erstellen. Aber wann soll man darüber sprechen, wenn nicht am Anfang?
Grundgedanken
BearbeitenSchon das einfache Muster einer unzureichenden Tabelle wie im vorigen Kapitel weist auf ein paar Forderungen hin, die offensichtlich an eine sinnvolle Struktur gestellt werden sollten:
- Redundanz vermeiden
- Eine Information, die an mehreren Stellen benötigt wird, soll nur einmal gespeichert werden. Dadurch müssen auch sämtliche Änderungen, die solche Informationen betreffen, nur an einer Stelle erledigt werden.
- Im Beispiel waren das u. a. die Angaben zum Fahrzeughalter und zum Sachbearbeiter (mit seinen Telefonnummern), die bei jedem Vertrag angegeben werden.
- Wiederholungen trennen
- Die Zusammenfassung gleichartiger Informationen ist nicht sehr übersichtlich. Im vorigen Kapitel waren zunächst Festnetz- und Mobil-Rufnummer in einer Spalte untergebracht. Das ist praktisch, wenn man sich keine Gedanken darüber machen will, welche und wie viele Kontaktnummern es gibt. Es ist ziemlich unpraktisch, wenn man gezielt einzelne Nummern suchen will oder von einer Nummer auf den Sachbearbeiter schließen will. Sinnvoller ist es, Festnetz- und Mobilnummer in getrennten Spalten zu speichern.
- Primärschlüssel verwenden
- Jeder Datensatz muss eindeutig identifiziert werden, damit die Informationen verwendet werden können.
- In der ungenügenden Tabelle wurde wegen der Zeilennummern darauf verzichtet. Aber selbst wenn man die Suche in zigtausend Zeilen für machbar hielte, spätestens bei der Aufteilung in mehrere Tabellen braucht man Werte, um die Zusammenhänge eindeutig darzustellen.
Bei diesen Überlegungen handelt es sich nur um ein paar Gedanken, die sich einem aufdrängen. Das wollen wir nun besser strukturieren.
Tabellenkalkulation als Ausgangspunkt
BearbeitenÜberlegen wir uns zunächst, welche Informationen benötigt werden.
Für die Verträge müssen die folgenden Angaben gespeichert werden.
- die Vertragsdaten selbst, also Nummer, Abschlussdatum und Art des Vertrags (HP = Haftpflicht, TK = Teilkasko, VK = Vollkasko) und der Prämienberechnung
- der Vertragspartner mit Name, Anschrift, Telefonnummern, dazu bei natürlichen Personen Geschlecht und Geburtsdatum sowie bei juristischen Personen der Eintrag im Handelsregister o. ä.
- das versicherte Fahrzeug mit Kennzeichen, Hersteller und Typ (die Farbe ist eine Zusatzinformation, die bei manchen Suchvorgängen hilfreich sein kann)
- der zuständige Sachbearbeiter mit Name, Abteilung und Telefonnummern
Ein Teil der Angaben muss immer vorhanden sein, ein anderer Teil je nach Situation (Geschlecht bei Personen) und ein weiterer Teil nur bei Bedarf (Mobiltelefon).
Schnell stellen wir fest, dass ein Versicherungsnehmer auch mehrere Fahrzeuge versichern kann. Andererseits kann ein Fahrzeug zu mehreren Verträgen gehören, wenn Haftpflicht und Vollkasko getrennt abgeschlossen werden oder wenn zur Haftpflicht vorübergehend Vollkasko fürs Ausland hinzukommt. Jeder Vertrag ist ein eigener Datensatz; also stehen bei jedem Datensatz die Angaben des Vertragspartners und des Fahrzeugs. Um alle Verträge eines Kunden gemeinsam anzuzeigen, brauchen wir folglich ein paar Angaben zur Organisation:
- Kundennummer und laufende Nummer seiner Verträge als eindeutiger Suchbegriff (vielleicht als Primärschlüssel)
- Vertragsnummer als eindeutiger Wert
- Fahrzeug-Kennzeichen als Wert, der meistens (aber nicht immer) nur einmal vorkommt
Nun sollen zu einem Vertrag auch die Schadensfälle gespeichert werden. Wir benötigen also eine oder mehrere Spalten mit den erforderlichen Angaben (Datum und Art des Schadens, Sachbearbeiter der Schadensabwicklung, andere beteiligte Fahrzeuge); aber wie wird das am besten gespeichert?
- eine gemeinsame Spalte für alle diese Angaben für alle Schadensfälle (denn die Mehrzahl der Fahrzeuge fährt schadensfrei)
- je eine Spalte für alle Angaben eines Schadensfalls (werden dann zwei oder drei Spalten benötigt, oder muss man für „Mehrfach-Sünder“ gleich zehn Spalten vorsehen?)
- oder einzelne Datensätze für jeden Schaden eines Fahrzeugs (sodass sich alle Fahrzeug- und Vertragsdaten in diesen Datensätzen wiederholen und nur die Schadensangaben unterscheiden)
Ungeklärt bleibt dabei noch dieses Problem: Wie viele versicherte Schadensgegner gibt es denn – keine (wenn ein Reh angefahren wird), einen (z. B. beim Parken) oder viele (bei einem Auffahrunfall auf der Autobahn)?
Entscheiden wir uns deshalb provisorisch für ein eigenes Arbeitsblatt Schadensfälle mit folgender Struktur:
- je eine Spalte für die Angaben, die direkt zum Schadensfall gehören
- vorläufig 5 Gruppen für maximal 5 beteiligte Fahrzeuge
- jede dieser Gruppen enthält in einzelnen Spalten Fahrzeug-Kennzeichen und die Halter-Angaben (Name, Anschrift)
Damit stehen die Fahrzeugdaten in beiden Arbeitsblättern. Als praktisches Problem kommt hinzu: Für die Schadenshäufigkeit eines bestimmten Fahrzeugs muss man es in fünf Spalten heraussuchen. Die Beschränkung auf fünf beteiligte Fahrzeuge wird im nächsten Schritt aufgelöst, machen wir uns dazu keine weiteren Gedanken.
Auf diesem Weg kann jedenfalls keine sinnvolle Struktur erreicht werden.
Mit diesem Begriff werden die folgenden Unklarheiten bezeichnet. Dabei handelt es sich um weitere Probleme bei einer ungenügenden Struktur.
- Einfügen-Anomalie
- In der ersten Tabelle sind u. a. die Sachbearbeiter enthalten. Es wäre sinnvoll, auch alle anderen Mitarbeiter der Gesellschaft hier einzutragen. Aber bei einem Vertrag werden zwingend Vertragsnummer und Abschlussdatum benötigt; dieser Zwang verhindert das Speichern der Teilinformation Sachbearbeiter ohne Bezug auf einen Vertrag.
- Löschen-Anomalie
- Wenn ein Vertrag gekündigt und abgelaufen ist und deshalb gelöscht wird, dann sind in der ersten Tabelle auch alle Angaben zum Vertragspartner verloren. Wenn er drei Tage später einen neuen Vertrag abschließt, müssen alle seine Angaben neu aufgenommen werden. Und was soll mit den Schadensfällen geschehen, die zu diesem Vertrag gespeichert sind?
- Ändern-Anomalie
- Wenn der Sachbearbeiter wechselt, muss sein Name bei allen von ihm betreuten Verträgen geändert werden, obwohl eine einzige Änderung ausreichend sein sollte. (Dies ist auf die Datenredundanz zurückzuführen, dass nämlich dieser Hinweis vielfach gespeichert ist statt einmalig.)
All diesen Problemen wollen wir nun durch eine deutlich verbesserte Datenstruktur begegnen. Nehmen wir dazu zunächst an, dass alle benötigten Informationen in den beiden Arbeitsblättern Verträge und Schadensfälle der Tabellenkalkulation stehen, und beginnen wir, dies sinnvoll zu strukturieren.
Die 1. Normalform
BearbeitenAm „grausamsten“ für den Aufbau der Tabelle und die praktische Arbeit ist die vielfache Wiederholung gleicher Informationen.
Sowohl beim Namen des Fahrzeughalters als auch bei den Mitarbeitern steht der Name bisher in einer Spalte in der Form „Nachname, Vorname“, was als Suchbegriff geeignet ist. Nun benötigen wir aber auch eine persönliche Anrede (Name mit Titel) und eine Briefanschrift (Vorname, Name). Soll dies in weiteren Spalten gespeichert werden? Nein, denn all dies kann aus den Einzelangaben „Name“ und „Vorname“ zusammengesetzt werden.
Ein Schadensfall ist bisher auf fünf beteiligte Fahrzeuge beschränkt, auch wenn selten mehr als zwei Beteiligungen benötigt werden. Wenn nun ein sechstes, zehntes oder zwanzigstes Fahrzeug beteiligt ist, muss dann jedesmal die Tabellenstruktur (und jedes Makro, das auf diese Spalten zugreift) geändert werden?!
Damit haben wir bereits zwei Regeln, die als Definition der ersten Normalform gelten. Hinzu kommt eine dritte Regel, die zwar formal nicht erforderlich ist; aber aus praktischen Gründen gibt es keine sinnvolle Lösung ohne diese Regel.
Die 1. Normalform 1. Jede Spalte enthält nur unteilbare (atomare, atomische) Werte. 2. Spalten, die gleiche oder gleichartige Informationen enthalten, sind in eigene Tabellen (Relationen) auszulagern. 3. Jede Tabelle enthält einen Primärschlüssel. |
Unsere Ausgangstabelle verstößt an vielen Stellen gegen diese Regeln:
- Zusammengesetzte Werte befinden sich z. B. an folgenden Stellen:
- Fahrzeughalter: Name und Vorname, PLZ und Ort, ggf. Straße und Hausnummer
- Sachbearbeiter: Name und Vorname, Festnetz- und Mobilnummer
- Wiederholungen finden sich vor allem hier:
- mehrere Fahrzeuge bei einem Schadensfall
Es werden also zwei Schritte benötigt:
- Zusammengesetzte Werte werden in Einzelinformationen aufgeteilt: je eine Spalte für jeden unteilbaren Wert.
- Wiederholungen werden in getrennte Tabellen ausgelagert; welche Daten zusammengehören, wird durch eine laufende Nummer angegeben.
An jeder Stelle, die Namen oder Anschrift enthält, werden getrennte Spalten verwendet, beispielsweise im Arbeitsblatt Verträge:
Kundennummer Vertrag Abschluss Halter_Name Halter_PLZ Halter_Ort Sachbearbeiter_N Sachbearbeiter_V Telefon 1 DG-01 03.05.1974 Heckel Obsthandel GmbH 46282 Dorsten Pohl Helmut 0201/4014186 1 DG-02 04.07.1974 Heckel Obsthandel GmbH 46282 Dorsten Pohl Helmut 0201/4014186
Die Tabelle Schadensfälle wird auf die eigentlichen Daten beschränkt; die beteiligten Fahrzeuge stehen in einer eigenen Tabelle Zuordnungen.
Nummer Datum Schadensort Beschreibung Sachbearbeiter_N Sachbearbeiter_V Telefon 1 02.03.2007 Recklinghausen, Bergknappenstr. 144 Gartenzaun gestreift Schindler Christina 0201/4012151 2 11.07.2007 Haltern, Hauptstr. 46 beim Ausparken hat ... Aliman Zafer 0201/4012161
Die Anzahl der beteiligten Fahrzeuge an einem Schadensfall wird durch eine eigene Tabelle Zuordnungen berücksichtigt:
Nummer Fahrzeug Hersteller Typ Halter_Name Halter_Vorname Halter_PLZ Halter_Ort Halter_Straße 1 RE-LM 902 Opel Corsa Heckel Obsthandel GmbH 46282 Dorsten Gahlener Str. 40 2 BO-GH 102 Volvo C30 Geissler Helga 44809 Bochum Steinbankstr. 15 2 RE-CD 456 Renault Twingo Cornelsen Dorothea 44577 Castrop-Rauxel Kiefernweg 9
Die Zusatzbedingung eines Primärschlüssels wurde gleichzeitig erfüllt; die betreffenden Spalten wurden unterstrichen.
Die 2. Normalform
BearbeitenEine weitere Wiederholung in den „Monster-Tabellen“ sind die Angaben zum Halter bei den Verträgen und den Zuordnungen oder die Fahrzeugdaten sowohl bei den Verträgen als auch bei den Zuordnungen. Auch diese werden in eigene Tabellen ausgelagert; das ergibt sich als Definition aus der folgenden Regel:
Die 2. Normalform 1. Die Tabelle erfüllt die 1. Normalform. 2. Alle Informationen in den Spalten, die nicht Teil des Primärschlüssels sind, müssen sich auf den gesamten Primärschlüssel beziehen. |
Man sagt dazu auch, dass die Informationen funktional abhängig sind von der Gesamtheit der Schlüsselwerte. Umgekehrt formuliert bedeutet es: Wenn eine Spalte nur zu einem einzelnen Schlüsselfeld gehört, ist die 2. Normalform nicht erfüllt.
Während sich die 1. Normalform auf die einzelnen Spalten und Wiederholungen innerhalb eines Datensatzes bezieht, befasst sich die 2. Normalform mit Wiederholungen bei verschiedenen Datensätzen.
Unsere derzeitigen Tabellen verstoßen mehrfach gegen diese Regel:
- Bei den Verträgen beziehen sich Name und Anschrift des Vertragspartners nur auf die Kundennummer, aber nicht auf die Vertragsnummer.
- Bei den Verträgen stehen auch die Fahrzeugdaten. (Dies wurde bei der Beschreibung der grundlegenden Daten erwähnt, fehlt aber in den bisherigen Beispielen.) Diese beziehen sich auf die Vertragsnummer, haben aber nur indirekt etwas mit Name und Anschrift des Vertragspartners zu tun.
- Bei den Zuordnungen der Schadensfälle gehören sowohl die Fahrzeug- als auch die Halterdaten nur zu einem Fahrzeug (also dem Kennzeichen), aber nicht zu einem Schadensfall.
Es müssen alle „unpassenden“ Informationen in eigene Tabellen ausgelagert werden. Der Primärschlüssel wird vereinfacht, sodass er sich nur auf die „eigentlichen“ Informationen einer Tabelle bezieht.
- Aus den Verträgen werden alle Angaben des Vertragspartners entfernt und in eine Tabelle Versicherungsnehmer übertragen. Der Primärschlüssel besteht nur noch aus der Spalte Vertrag. Die Spalte Kundennummer ist nur noch ein Fremdschlüssel als Verweis auf die neue Tabelle Versicherungsnehmer.
- Aus den Verträgen werden alle Angaben des Fahrzeugs entfernt und in eine neue Tabelle Fahrzeuge übertragen. Das Fahrzeug-Kennzeichen ist hier nur noch ein Fremdschlüssel als Verweis auf die Tabelle Fahrzeuge.
- Aus den Zuordnungen werden alle Angaben der Fahrzeuge entfernt und in eine Tabelle Fahrzeuge übertragen. Die Tabelle Zuordnungen besteht nur noch aus den Spalten des Primärschlüssels.
Damit stehen die Fahrzeugdaten nur noch in einer Tabelle – sowohl für die Verträge als auch für die Schadensfälle (genauer: die Zuordnungen).
Die ursprüngliche Tabelle Verträge beschränkt sich jetzt auf diese Angaben:
Vertrag Abschluss Typ Kundennummer Fahrzeug Sachbearbeiter_N Sachbearbeiter_V Telefon DG-01 03.05.1974 HP 1 RE-LM 901 Pohl Helmut 0201/4014186 DG-02 04.07.1974 HP 1 RE-LM 902 Pohl Helmut 0201/4014186
Die neue Tabelle Versicherungsnehmer umfasst die Daten, die sich auf den Vertragspartner beziehen:
Kundennummer Name Vorname Geburtsdatum PLZ Ort Straße Hausnummer 1 Heckel Obsthandel GmbH 46282 Dorsten Gahlener Str. 40 5 Geissler Helga 13.01.1953 44809 Bochum Steinbankstr. 15
Die neue Tabelle Fahrzeuge umfasst nur noch die Daten, die sich auf das Fahrzeug selbst beziehen. Name und Anschrift des Fahrzeughalters werden durch die Kundennummer, also den Verweis auf die Tabelle Versicherungsnehmer ersetzt.
Fahrzeug Hersteller Typ Farbe Halter RE-LM 902 Opel Corsa ocker 1 BO-GH 102 Volvo C30 rot 5 RE-CD 456 Renault Twingo ocker 3
Die Tabelle Schadensfälle muss nicht angepasst werden. Die Tabelle Zuordnungen vereinfacht sich radikal:
Nummer Fahrzeug 1 RE-LM 902 2 BO-GH 102 2 RE-CD 456
Die 2. Normalform kann ganz einfach dadurch gewährleistet werden, dass sich der Primärschlüssel nur auf eine Spalte bezieht.
Die 3. Normalform
BearbeitenBeseitigen wir noch die übrigen Wiederholungen, nämlich die Sachbearbeiter bei den Verträgen und den Schadensfällen sowie die Hersteller bei den Fahrzeugen. Diese werden ebenfalls in eigene Tabellen ausgelagert gemäß der Definition nach der folgenden Regel:
Die 3. Normalform 1. Die Tabelle erfüllt die 2. Normalform. 2. Informationen in den Spalten, die nicht Teil des Primärschlüssels sind, dürfen funktional nicht voneinander abhängen. |
Die 3. Normalform befasst sich also mit Wiederholungen bei verschiedenen Datensätzen, die nur zusätzliche Informationen bereitstellen.
Unsere derzeitigen Tabellen verstoßen in folgender Hinsicht gegen diese Regel:
- Name, Vorname und Telefonnummer eines Sachbearbeiters hängen voneinander ab. Sie haben aber nur insgesamt etwas mit dem Vertrag bzw. dem Schadensfall zu tun, nicht als einzelne Information.
- Hersteller und Typ eines Fahrzeugs hängen voneinander ab. Sie haben aber nur insgesamt etwas mit dem Fahrzeug zu tun, nicht als einzelne Information.
Eine andere Erklärung dafür ist, dass die Zusatzinformation auch ohne Bezug zum eigentlichen Datensatz gültig bleibt. Der Sachbearbeiter gehört zum Unternehmen unabhängig von einem bestimmten Vertrag. Der Fahrzeughersteller existiert unabhängig davon, ob ein bestimmtes Fahrzeug noch fährt oder inzwischen verschrottet worden ist.
Alle „unpassenden“ Informationen kommen wieder in eigene Tabellen. Ihre Spalten werden ersetzt durch einen Fremdschlüssel als Verweis auf die neue Tabelle.
- Aus den Verträgen und den Schadensfällen werden alle Angaben zum Sachbearbeiter entfernt und in eine Tabelle Mitarbeiter übertragen. Die Spalte Sachbearbeiter verweist nur noch als Fremdschlüssel auf die neue Tabelle Mitarbeiter.
- Dies löst automatisch auch das oben erwähnte Problem: Wir können nun alle Mitarbeiter in einer gemeinsamen Tabelle speichern.
- Aus den Fahrzeugen werden alle Angaben zu Hersteller und Typ entfernt und in eine neue Tabelle Fahrzeugtypen übertragen. Die Spalten Hersteller und Typ werden ersetzt durch einen Fremdschlüssel als Verweis auf die Tabelle Fahrzeugtypen.
- Um es korrekt zu machen, gehört der Hersteller in eine weitere Tabelle Fahrzeughersteller; in der Tabelle Fahrzeugtypen verweist er als Fremdschlüssel auf diese weitere Tabelle.
Die Tabellen Verträge und Schadensfälle werden also nochmals vereinfacht:
Vertrag Abschluss Typ Kundennummer Fahrzeug Sachbearbeiter DG-01 03.05.1974 HP 1 RE-LM 901 9 DG-02 04.07.1974 HP 1 RE-LM 902 9
Nummer Datum Schadensort Beschreibung Sachbearb 1 02.03.2007 Recklinghausen, Bergknappe... Gartenzaun gestreift 14 2 11.07.2007 Haltern, Hauptstr. 46 beim Ausparken hat ... 15
Hinzu kommt die neue Tabelle Mitarbeiter:
Nummer Nachname Vorname Telefon 9 Pohl Helmut 0201/4014186 14 Schindler Christina 0201/4012151 15 Aliman Zafer 0201/4012161
In gleicher Weise wird die Tabelle Fahrzeuge gekürzt; die Angaben werden in die neuen Tabellen Fahrzeugtypen und Fahrzeughersteller ausgelagert.
Zusätzliche Maßnahmen
BearbeitenIn der Theorie gibt es noch eine 4. und eine 5. Normalform (und eine Alternative zur 3. Normalform). Dazu sei auf den Wikipedia-Artikel und die dortigen Hinweise (Quellen, Literatur, Weblinks) verwiesen. In der Praxis ist aber eine Datenbank, die den Bedingungen der 3. Normalform entspricht, sehr gut konzipiert. Weitere Normalisierungen bringen kaum noch Vorteile; stattdessen können sie die Übersichtlichkeit und die Geschwindigkeit beim Datenzugriff beeinträchtigen.
Verzicht auf Zusatztabellen
BearbeitenBeispielsweise wiederholen sich in der Tabelle Versicherungsnehmer die Ortsangaben. Dabei gehören die Kombinationen PLZ/Ort immer zusammen; auch wenn der Kunde umzieht, ist eine solche Kombination unverändert gültig. Also könnte man in der Tabelle die Adresse wie folgt speichern:
Kd-Nr Name Vorname Geburtsdatum PLZ Alort Straße Hausnummer 1 Heckel Obsthandel GmbH 46282 10884500 Gahlener Str. 40 5 Geissler Helga 13.01.1953 44809 05902500 Steinbankstr. 15
Der Ortsname ist dann zu finden über die PL-Datei der Deutschen Post AG (mit PLZ/Alort als Primärschlüssel):
Dateiversion Geltung PLZ Alort PLZ-Arten Ortsname PL 0509 244 20010101 44809 05902500 06 2 2 Bochum PL 0509 244 20050329 46282 10884500 06 2 2 Dorsten
Das gleiche Verfahren ist möglich für die Straßennamen. Es sorgt dafür, dass nur gültige Anschriften gespeichert sind und auch bei Eingemeindungen die neuen Angaben eindeutig übernommen werden. Aber selbst wenn man wegen der Datensicherheit mit Alort arbeiten will, wird man für die praktische Arbeit den Ortsnamen in der Adressendatei behalten wollen.
Primärschlüssel nur zur Identifizierung
BearbeitenIm vorigen Kapitel hatten wir kurz darauf hingewiesen, dass der Primärschlüssel nur die Bedeutung als ID haben sollte.
Eine Begründung liefert die obige Tabelle Fahrzeuge, bei der das Kennzeichen zur Identifizierung benutzt wurde. Wenn der Fahrzeughalter in einen anderen Kreis umzieht, bekommt er ein neues Kennzeichen. Dann müsste es an allen Stellen geändert werden, an denen es gespeichert ist. Nun darf die Vertragsabteilung nur die Verträge ändern, die Schadensabwicklung die Schadensfälle (und wer weiß, wo noch darauf Bezug genommen wird). Jede Ummeldung verursacht also erheblichen Mehraufwand.
Sorgen wir also für mehr Datensicherheit und Arbeitsvereinfachung:
- Der Primärschlüssel ist eine automatisch zugewiesene ID. Diese ID wird niemals geändert.
- Die Identifizierung, die der Benutzer kennt, ist eine einzelne Spalte in einer einzigen Tabelle.
- Beispiele: Vertragsnummer, Fahrzeug-Kennzeichen, Personalnummer
- Die Verknüpfungen mit anderen Tabellen regelt die Datenbank mit Hilfe der ID selbständig.
Änderungsdaten
BearbeitenIn vielen Fällen ist es sinnvoll, wenn in einer Tabelle nicht nur der aktuelle Zustand gespeichert ist, sondern der Verlauf.
- Beispiel mit Änderung des Kennzeichens: Wenn die Polizei nach einem Unfallverursacher sucht, der inzwischen umgezogen ist, wird für das alte (nicht mehr gültige) Kennzeichen kein Fahrzeug gefunden.
- Adressenänderungen auf Termin legen: Es wäre viel zu aufwändig, wenn alle Änderungen gleichzeitig an dem Stichtag, an dem sie gelten sollen, eingegeben werden müssten. Besser ist es, sie sofort zu speichern mit Angabe des Geltungsdatums; die Datenbank holt abhängig vom aktuellen und dem Geltungsdatum immer die richtige Version.
- Aktueller Steuersatz: Die Abrechnung einer Versandfirma muss immer mit dem richtigen Steuersatz rechnen, auch wenn er mitten im Jahr geändert wird. Als Steuersatz 1 für die Mehrwertsteuer wird dann bei einer „älteren“ Rechnung mit 16 % und bei einer Rechnung neueren Datums mit 19 % gerechnet.
Für alle solche Fälle erhalten Tabellen gerne zusätzliche Spalten gültig von und gültig bis. Anstelle einer Änderung werden Datensätze verdoppelt; die neuen Informationen ersetzen dabei die bisher gültigen.
Selbstverständlich erfordert eine solche Datenbankstruktur höheren Aufwand sowohl beim Entwickler der Datenbank als auch beim Programmierer der Anwendung. Der zusätzliche Nutzen rechtfertigt diesen Aufwand.
Reihenfolge ohne Bedeutung
BearbeitenBeim Aufbau einer Datenbank darf es nicht relevant sein, in welcher Reihenfolge die Zeilen und Spalten vorkommen: Die Funktionalität der Datenbank darf nicht davon abhängen, dass zwei Spalten in einer bestimmten Folge nacheinander kommen oder dass nach einem Datensatz ein bestimmter zweiter folgt.
- Ein Datensatz (also eine Zeile) steht nur über den Primärschlüssel bereit.
- Eine Spalte steht nur über den Spaltennamen zur Verfügung.
Nur ausnahmsweise wird mit der Nummer einer Zeile oder Spalte gearbeitet, z. B. wenn ein „Programmierer-Werkzeug“ nur indizierte Spalten kennt.
Zusammenfassung
BearbeitenFassen wir diese Erkenntnisse zusammen zu einigen Regeln, die bei der Entwicklung einer Datenbank-Struktur zu beachten sind.
Allgemeine Regeln
Bearbeiten- Von der Realität ausgehen
- Erstellen Sie Tabellen danach, mit welchen Dingen (Objekten) Sie arbeiten. Benutzen Sie aussagekräftige Namen für Tabellen und Spalten.
- Einzelne Informationen klar trennen
- Alle Informationen werden in einzelne Spalten mit unteilbaren Werten aufgeteilt.
- Vollständigkeit
- Alle möglichen Informationen sollten von vornherein berücksichtigt werden. Nachträgliche Änderungen sind zu vermeiden.
- Keine berechneten Werte speichern
- Eine Information, die aus vorhandenen Angaben zusammengestellt werden kann, wird nicht als eigene Spalte gespeichert.
- Kleine Tabellen bevorzugen
- Wenn eine Tabelle sehr viele Informationen umfasst, ist es besser, sie zu unterteilen und über einen einheitlichen Primärschlüssel zu verbinden.
- Keine doppelten Speicherungen
- Es darf weder ganze doppelte Datensätze geben noch wiederholte Speicherungen gleicher Werte (Redundanz von Daten). Doppelte Datensätze werden durch Primärschlüssel und Regeln zur Eindeutigkeit in Felder vermieden; Datenredundanz wird durch getrennte Tabellen ersetzt, sodass es nur die Fremdschlüssel mehrfach gibt.
Wichtig ist auch, dass der Aufbau der Datenbank beschrieben und ggf. begründet wird. Hilfreich sind dabei graphische Darstellungen, wie sie von vielen Datenbank-Programmen angeboten werden.
Abweichungen
BearbeitenDie vorstehenden Überlegungen werden nicht immer beachtet. Für fast jede Regel gibt es begründete Ausnahmen
Beispielsweise enthält eine Datenbank mit Adressen in der Regel die Adressen im Klartext und nicht nur als Verweise auf die Nummern von Orten, Ortsteilen, Straßen und Straßenabschnitten der Deutschen Post.
Entscheiden Sie erst nach bewusster Abwägung von Vor- und Nachteilen, wenn Sie von einer der Regeln abweichen wollen.
Siehe auch
BearbeitenÜber Wikipedia sind weitere Informationen zu finden:
- Normalisierung
- Relationale Datenbank
- Edgar F. Codd und seine 12 Regeln für relationale Datenbanken
- Update-Anomalien
- Redundanz von Daten
Für eine sorgfältige Planung einer Adressen-Datenbank hilft die Datenstruktur der Deutschen Post:
- Datafactory als Überblick (PLZ, Orte / Ortsteile und Straßen)
- Datafactory Streetcode Handbuch mit Aufbau und Inhalt (PDF, 600 kB)
Beispieldatenbank |
Dieses Kapitel bespricht die Grundlagen der Beispieldatenbank. Alle Einzelheiten der Tabellen stehen im Anhang unter Tabellenstruktur der Beispieldatenbank.
Hinweis Alle Beispiele, Erläuterungen und Aufgaben beziehen sich auf diese Datenstruktur. Bitte schlagen Sie (soweit erforderlich) immer dort nach. |
Sachverhalt
BearbeitenDie Beispieldatenbank in diesem Buch versucht, eine Versicherungsgesellschaft für Kfz-Versicherungen abzubilden. Das Beispiel ist eine starke Vereinfachung der Realität; zum Beispiel fehlen alle Teile der laufenden Abrechnung der Prämien und Schadensfälle.
Die relevanten Begriffe sind für eine bessere Übersichtlichkeit fett gekennzeichnet. Folgender Sachverhalt wird in der Datenbank abgebildet:
Die Versicherungsgesellschaft UnsereFirma verwaltet mit dieser Datenbank ihre Kundendaten und die Schadensfälle. Wegen der Schadensfälle müssen auch Daten „fremder“ Kunden und Versicherungen gespeichert werden.
Jeder Versicherungsvertrag wird mit einem Versicherungsnehmer über genau ein Fahrzeug abgeschlossen. Der Versicherungsvertrag wird durch folgende Eigenschaften gekennzeichnet; zu jeder Eigenschaft gehört eine Spalte:
- Vertragsnummer (Pflicht, eindeutig)
- Datum des Abschlusses, Art des Vertrages (Pflicht); dabei gibt es die Arten Haftpflicht (HP), Haftpflicht mit Teilkasko (TK), Vollkasko (VK).
- Verweis auf den Versicherungsnehmer (Pflicht)
- Verweis auf das Fahrzeug (Pflicht, eindeutig)
- Verweis auf den Mitarbeiter, der den Vertrag bearbeitet (Pflicht)
Der Versicherungsnehmer ist gekennzeichnet durch diese Eigenschaften:
- Kundennummer (Pflicht, eindeutig)
- Name und Anschrift: PLZ, Ort, Straße, Hausnummer (Pflicht)
- bei natürlichen Personen zusätzlich durch Vorname, Geburtsdatum, Datum des Führerscheins (optional)
- Verweis auf eine „Fremdversicherung“, wenn ein (fremdes) Fahrzeug an einem Unfall beteiligt ist (optional)
Das Fahrzeug ist gekennzeichnet durch diese Eigenschaften:
- polizeiliches Kennzeichen (Pflicht, eindeutig)
- Farbe (optional)
- Fahrzeugtyp und damit indirekt auch den Fahrzeughersteller (Pflicht)
Ein Mitarbeiter ist gekennzeichnet durch diese Eigenschaften:
- Name, Vorname, Geburtsdatum (Pflicht)
- Personalnummer (Pflicht, eindeutig)
- Verweis auf Abteilung, Vermerk, ob es sich um den Leiter der Abteilung handelt (Pflicht)
- Kontaktdaten wie Telefon, Mobiltelefon, Email, Raum (optional)
Die Abteilung ist gekennzeichnet durch diese Eigenschaften:
- Nummer (Pflicht, eindeutig)
- Kurzbezeichnung, Bezeichnung (Pflicht, eindeutig)
- Ort (optional)
Zusätzlich gibt es Dienstwagen. Dabei handelt es sich um eine Tabelle mit den gleichen Eigenschaften wie bei Fahrzeug und zusätzlich:
- Verweis auf den Mitarbeiter, zu dem ein Dienstwagen gehört (optional)
- denn es gibt auch Firmenwagen, die keinem Mitarbeiter persönlich zugeordnet sind
Ein Schadensfall ist gekennzeichnet durch diese Eigenschaften:
- Datum, Ort und Umstände des Unfalls (Pflicht)
- Vermerk, ob es Verletzte gab, sowie Höhe des Gesamtschadens (optional, denn die Angaben könnten erst später bekannt werden)
- Verweis auf den Mitarbeiter, der den Schadensfall bearbeitet (Pflicht)
An einem Schadensfall können mehrere Fahrzeuge unterschiedlicher Versicherungen beteiligt sein. (Unfälle mit Radfahrern und Fußgängern werden nicht betrachtet.) Deshalb gibt es eine weitere Tabelle Zuordnung_SF_FZ:
- Liste aller Schadensfälle und aller beteiligten Fahrzeuge (Pflicht)
- anteilige Schadenshöhe eines Fahrzeugs an dem betreffenden Unfall (optional)
Über die Verbindung Schadensfall → Fahrzeug → Versicherungsvertrag → Versicherungsnehmer → Versicherungsgesellschaft können alle beteiligten Gesellschaften festgestellt und in die Schadensabwicklung eingebunden werden.
Bitte beachten Sie auch die unten stehenden Hinweise über Fehlende Spalten und Einschränkungen.
Schematische Darstellung
BearbeitenDie vorstehende Struktur kann in folgendem Diagramm dargestellt werden. Die Verweise, nämlich die Verknüpfungen zwischen den Tabellen sind daraus so abzulesen:
- Von jeder Tabelle gibt es einen Verweis auf einen Eintrag in der direkt darunter stehenden Tabelle.
- Zu jedem Vertrag gehört ein Sachbearbeiter; das wird durch den Pfeil nach links außen angedeutet, der in der Tabelle Mitarbeiter von rechts hereinkommt.
- Zu jedem Vertrag gehört genau ein Fahrzeug; zu jedem Fahrzeug gehört ein Vertrag.
- Jeder Eintrag in der Liste der Zuordnungen Schadensfall, Fahrzeug bezieht sich auf einen Schadensfall und ein Fahrzeug; dabei gilt:
- Zu jedem Schadensfall gehören ein oder mehrere Fahrzeuge.
- Nicht jedes Fahrzeug hat einen Schadensfall.
- Ein Dienstwagen kann einem Mitarbeiter zugeordnet sein, muss es aber nicht.
Die Kürzel bei den Tabellennamen werden künftig immer wieder als „Alias“ anstelle des „Langnamens“ verwendet. In dieser Übersicht wurden sie wegen der Deutlichkeit großgeschrieben; künftig werden sie kleingeschrieben.
Tabellenstruktur und Datenbank
BearbeitenIm Anhang ist die Tabellenstruktur der Beispieldatenbank beschrieben, nämlich die Liste der Tabellen und aller ihrer Spalten. Innerhalb des Buches wird die Datenbankstruktur erweitert werden; dabei werden einige der nachstehenden Anmerkungen beachtet.
Im Anhang stehen auch Wege, wie Sie die Beispieldatenbank erhalten.
- Entweder Sie holen sich eine fertige Version der Datenbank:
- Laden Sie die komprimierte Datei herunter.
- Benennen Sie die Datei um, soweit erforderlich.
- Extrahieren Sie die Daten und speichern Sie das Ergebnis an einer passenden Stelle.
- Oder Sie erzeugen die Datenbank mit einem Hilfsprogramm des von Ihnen verwendeten DBMS:
- Erstellen Sie zunächst eine leere Datenbank, soweit erforderlich.
- Laden bzw. kopieren Sie das passende Skript zum Erzeugen der Datenbank-Tabellen und Daten.
- Führen Sie das Skript mit einem Hilfsprogramm des von Ihnen verwendeten DBMS aus.
Eine kurze Beschreibung für die Installation steht bei den Hinweisen zu den Downloads. Für Details nutzen Sie bitte die Dokumentation des jeweiligen Datenbankmanagementsystems; Links dazu stehen in der Einleitung sowie unter Weblinks.
Zum Abschluss werden auch ein paar Verfahren behandelt, schnell viele Datensätze als Testdaten zu erzeugen. Für das Verständnis der Inhalte des Buchs sind diese Verfahren nicht wichtig. Sie sind aber äußerst nützlich, wenn Sie weitere Möglichkeiten der SQL-Befehle ausprobieren wollen.
Anmerkungen
BearbeitenDie Beispieldatenbank wurde erstellt und eingefügt, als sich das Buch noch ziemlich am Anfang befand. Im Laufe der Zeit wurden bei der Konzeption und der Umsetzung in Beispielen Unstimmigkeiten und Mängel festgestellt. Auf diese soll hier hingewiesen werden; teilweise wurden sie beseitigt, teilweise werden wir die Datenstruktur in späteren Kapiteln ändern, teilweise belassen wir es dabei.
Es ist üblicher, eine Tabelle im Singular zu bezeichnen.
- Es ist einfacher zu sagen: Ein Versicherungsvertrag hat diese Eigenschaften… Umständlicher wäre: Ein Datensatz der Tabelle Versicherungsverträge hat diese Eigenschaften…
- Vor allem beim Programmieren werden die Namen mit anderen Begriffen verknüpft; es käme dann etwas wie VertraegePositionenRow zustande – also die Positionen von Verträgen –, wo nur eine einzelne Zeile, nämlich eine Position eines Vertrags gemeint ist.
➤ Aus diesem Grund wurden die o. g. SQL-Skripte nochmals vollständig überarbeitet; alle Tabellennamen benutzen jetzt den Singular.
- In einem Programmiererforum gab es einmal eine Diskussion über „Singular oder Plural“. Dort wurde das als uraltes Streitthema bezeichnet, das zu heftigsten Auseinandersetzungen führen kann. Im Ergebnis sollte es eher als Geschmackssache angesehen werden. Also hat der Autor in diesem Buch seinen Geschmack durchgesetzt. Wer mag, kann für sich alle Tabellennamen (im Skript für die Beispieldatenbank und in allen Beispielen und Übungen) ändern.
Die Spalten von Tabellen sollen den Inhalt klar angeben; das ist durchgehend berücksichtigt worden. Es ist nicht üblich, ein Tabellenkürzel als Teil des Spaltennamens zu verwenden; denn ein Spaltenname wird immer in Zusammenhang mit einer bestimmten Tabelle benutzt.
➤ Aus einer früheren Version der Beispieldatenbank wurden diese Kürzel entfernt.
Es ist häufig praktischer, englische Begriffe für Tabellen und Spalten zu verwenden. Programmierer müssen die Namen weiterverarbeiten; weil sowohl Datenbanken als auch Programmiersprachen mit Englisch arbeiten, entstehen ständig komplexe Namen. Dabei ist ein Begriff wie CustomerNumberChanged (also reines Englisch) immer noch besser als etwas wie KundeNummerChanged, also denglischer Mischmasch.
➤ Auf diese Änderung wird verzichtet, weil es ziemlich umständlich gewesen wäre. Außerdem spielt die Weiterverwendung durch Programmierer in diesem Buch eigentlich keine Rolle.
Die Aufgaben der Versicherungsgesellschaft können nur eingeschränkt behandelt werden. Es fehlen Tabellen für Abrechnung und Zahlung der Prämien sowie Abrechnung der Kosten für Schadensfälle.
➤ Auf diese Aufgaben wird hier verzichtet.
Die Tarife und damit die Höhe der Basisprämie gehören in eine eigene Tabelle; ein Versicherungsvertrag müsste darauf verweisen.
➤ Auf diese Aufteilung wird wegen der Vereinfachung verzichtet.
Sowohl Mitarbeiter als auch Versicherungsnehmer sind Personen: Auch zu einem Versicherungsnehmer passen Kontaktdaten; auch bei einem Mitarbeiter ist die Privatanschrift von Bedeutung. Es wäre deshalb vernünftig, eine gemeinsame Tabelle Person einzurichten; die Tabelle Mitarbeiter würde Verweise auf diese Tabelle erhalten.
➤ Auf diese Änderung wird wegen der Vereinfachung verzichtet.
Man kann darüber streiten, ob der optionale Verweis auf eine fremde Versicherungsgesellschaft besser zum Versicherungsvertrag oder zum Versicherungsnehmer gehört.
➤ Wegen dieser Unklarheit wird auf eine Änderung verzichtet.
Die Verknüpfungen zwischen den Tabellen fehlten in der Ursprungsversion der Beispieldatenbank vollständig. Sie werden in den Kapiteln Fremdschlüssel-Beziehungen besprochen und in Änderung der Datenbankstruktur eingebaut.
Neben den bereits genannten Punkten sind weitere Spalten sinnvoll bzw. notwendig:
- Tabelle Versicherungsvertrag: Basisprämie, Prämiensatz, letzte Änderung des Prämiensatzes
- Tabelle Zuordnung_SC_FZ: Anteil am Verschulden
- Tabellen Versicherungsnehmer und Mitarbeiter: Geschlecht m/w wegen der Anrede in Briefen
Viele Einschränkungen fehlen in der ursprünglichen Version:
- Spalten, deren Werte eindeutig sein müssen
- Spalten, deren Werte nur bestimmte Werte annehmen dürfen
- Tabellen, bei denen Spaltenwerte in gewisser Beziehung stehen müssen
Diese Änderungen werden in DDL - Einzelheiten besprochen und in Änderung der Datenbankstruktur eingebaut.
SQL-Befehle |
Manchen Einsteigern mag SQL sehr sperrig erscheinen. SQL ist weniger für improvisierte Einmalabfragen gedacht als vielmehr zur Entwicklung von stabilen, dauerhaft nutzbaren Abfragen. Wenn die Abfrage einmal entwickelt ist, wird sie meistens in eine GUI- oder HTML-Umgebung eingebunden, sodass der Benutzer mit dem SQL-Code gar nicht mehr in Berührung kommt.
Es gibt zwar einen SQL-Standard (siehe das Kapitel Einleitung), der möglichst von allen Datenbanken übernommen werden soll, aber leider hält sich kein angebotenes DBMS (Datenbank-Management-System) vollständig daran. Es kommt also je nach DBMS zu leichten bis großen Abweichungen, und für Sie führt kein Weg daran vorbei, immer wieder in der Dokumentation ihrer persönlichen Datenbank nachzulesen. Das gilt natürlich besonders für Verfahren, die im SQL-Standard gar nicht enthalten sind, von Ihrem DBMS trotzdem angeboten werden.
Die Abweichungen eines bestimmten DBMS vom SQL-Standard werden sehr oft als SQL-Dialekt bezeichnet. |
Allgemeine Hinweise
BearbeitenDie gesamte Menge an Befehlen ist recht überschaubar; Schwierigkeiten machen die vielen Parameter mit zahlreichen Varianten.
Der Übersicht halber wurde SQL in Teilbereiche gegliedert; allerdings gibt es auch für diese Aufteilung Unterschiede bei den DBMS, den Dokumentationen und in Fachbüchern. Diese Aufteilung (siehe Inhaltsverzeichnis dieses Kapitels) dient aber nicht nur der Übersicht, sondern hat auch praktische Gründe:
- Befehle der Data Manipulation Language (DML) werden vor allem von „einfachen“ Anwendern benutzt.
- Befehle der Data Definition Language (DDL) und der Transaction Control Language (TCL) dienen Programmierern.
- Befehle der Data Control Language (DCL) gehören zum Aufgabenbereich von Systemadministratoren.
Dies sind die Bestandteile eines einzelnen Befehls:
- der Name des Befehls
- der Name des Objekts (Datenbank, Tabelle, Spalte usw.)
- ein Hinweis zur Maßnahme, soweit diese nicht durch den Befehl klar ist
- weitere Einzelheiten
- das Semikolon als Zeichen für den Abschluss eines SQL-Befehls
Damit dies alles eindeutig ist, gibt es eine Reihe von Schlüsselwörtern (key words, reserved words, non-reserved words), anhand derer das DBMS die Informationen innerhalb eines Befehls erkennt.
Die Schreibweise eines Befehls ist flexibel.
- Groß- und Kleinschreibung der Schlüsselwörter werden nicht unterschieden.
- Ein Befehl kann beliebig auf eine oder mehrere Zeilen verteilt werden; der wichtigste Gesichtspunkt dabei ist die Lesbarkeit auch für Sie selbst beim Schreiben.
- Das Semikolon ist nicht immer erforderlich, wird aber empfohlen. Bei manchen DBMS wird der Befehl erst nach einem folgenden GO o. ä. ausgeführt.
Für eigene Bezeichner, d. h. die Namen von Tabellen, Spalten oder eigenen Funktionen gilt:
- Vermeiden Sie unbedingt, Schlüsselwörter dafür zu verwenden; dies führt schnell zu Problemen auch dort, wo es möglich wäre.
- Das Wort muss in der Regel mit einem Buchstaben oder dem Unterstrich a…z A…Z _ beginnen. Danach folgen beliebig Ziffern und Buchstaben.
- Inwieweit andere Zeichen und länderspezifische Buchstaben (Umlaute) möglich sind, hängt vom DBMS ab.
- Der Autor dieser Zeilen rät davon ab, aber das mag Geschmackssache sein. Bei den Erläuterungen zur Beispieldatenbank (Namen von Tabellen und Spalten) wird sowieso für englische Bezeichner plädiert.
Dieses Buch geht davon aus, dass Schlüsselwörter nicht als Bezeichner dienen und auch Umlaute nicht benutzt werden.
Kommentare können in SQL-Befehle fast beliebig eingefügt werden (nur die Schlüsselwörter dürfen natürlich nicht „zerrissen“ werden). Es gibt zwei Arten von Kommentaren:
-- (doppelter Bindestrich, am besten mit Leerzeichen dahinter)
Alles von den beiden Strichen an (einschließlich) bis zum Ende dieser Zeile gilt als Kommentar und nicht als Bestandteil des Befehls.
/* (längerer Text, gerne auch über mehrere Zeilen) */
Alles, was zwischen '/*'
und '*/'
steht (einschließlich dieser Begrenzungszeichen), gilt als Kommentar und nicht als Bestandteil des Befehls.
DML – Data Manipulation Language
BearbeitenDML beschäftigt sich mit dem Inhalt des Datenbestandes. „Manipulation“ ist dabei nicht nur im Sinne von „Manipulieren“ zu verstehen, sondern allgemeiner im Sinne von „in die Hand nehmen“ (lat. manus = Hand).
Das Kapitel DML (1) - Daten abfragen befasst sich mit dem SELECT-Befehl.
- Gelegentlich finden Sie dafür auch den Begriff Data Query Language (DQL). Diese Einführung fasst ihn als Teilgebiet der DML auf; nur aus Gründen der Übersicht gibt es getrennte Kapitel.
Die Befehle INSERT, UPDATE, DELETE dienen der Speicherung von Daten und werden in DML (2) - Daten speichern behandelt.
Bei diesen Befehlen ist immer genau eine Tabelle – nur bei SELECT auch mehrere – anzugeben, dazu Art und Umfang der Arbeiten sowie in aller Regel mit WHERE eine Liste von Bedingungen, welche Datensätze bearbeitet werden sollen.
Die folgenden Erläuterungen sind einheitlich für alle DML-Befehle zu beachten.
Datenmengen, nicht einzelne Datensätze
BearbeitenBitte beachten Sie, dass SQL grundsätzlich mengenorientiert arbeitet. DML-Befehle wirken sich meistens nicht nur auf einen Datensatz aus, sondern auf eine ganze Menge, die aus 0, einem oder mehreren Datensätzen bestehen kann. Auch die WHERE-Bedingungen sorgen „nur“ für den Umfang der Datenmenge; aber das Ergebnis ist immer eine Datenmenge.
Im Einzelfall wissen Sie als Anwender oder Programmierer natürlich häufig, ob die Datenmenge 0, 1 oder n Datensätze enthalten kann oder soll. Aber Sie müssen selbst darauf achten, denn SQL oder das DBMS können das nicht wissen.
Die Struktur als Datenmenge führt auch dazu, dass es bei einem SELECT-Befehl keine „natürliche“ Reihenfolge gibt, in der die Daten angezeigt werden. Manchmal kommen sie in der Reihenfolge, in der sie gespeichert wurden; aber bei umfangreicheren Tabellen mit vielen Änderungen sieht es eher nach einem großen Durcheinander aus – es sei denn, Sie verwenden die ORDER BY-Klausel.
SQL-Ausdrücke
BearbeitenIn vielen Fällen wird der Begriff Ausdruck verwendet. Dies ist ein allgemeiner Begriff für verschiedene Situationen:
- An Stellen, an denen ein einzelner Wert angegeben werden muss, kann auch ein Werte-Ausdruck verwendet werden: ein konstanter Wert (Zahl, Text, boolescher Wert), das Ergebnis einer Funktion, die einen solchen Wert zurückgibt, oder eine Abfrage, die als Ergebnis einen einzigen Wert liefert.
- An Stellen, an denen eine oder mehrere Zeilen einer Datenmenge angegeben werden müssen, kann auch ein SQL-Ausdruck (im SQL-Standard als query expression bezeichnet) verwendet werden. Dabei handelt es sich um einen SQL-Befehl (in der Regel einen SELECT-Befehl), der als Ergebnis eine Menge von Datensätzen liefert.
- An Stellen, an denen eine Liste einzelner Werte angegeben werden muss, kann ebenfalls ein SQL-Ausdruck verwendet werden; dieser muss dann als Ergebnis eine Menge passender Werte liefern.
Datenintegrität
BearbeitenBei allen Datenmanipulationen ist zu beachten, dass die Bedingungen für Querverweise erhalten bleiben, siehe das Kapitel Fremdschlüssel-Beziehungen. Beispielsweise darf ein Datensatz in der Tabelle Abteilung erst dann gelöscht werden, wenn alle zugeordneten Mitarbeiter gelöscht oder versetzt wurden.
Hinweis für Programmierer: Parameter benutzen!
BearbeitenEin SQL-Befehl in einem Programm sollte niemals als „langer String“ mit festen Texten erstellt werden, sondern mit Parametern. Das erleichtert die Wiederverwendung, die Einbindung von Werten, vermeidet Probleme mit der Formatierung von Zahlen und Datumsangaben und verhindert SQL-Injection.
Im Kapitel DML (2) - Daten speichern steht unter UPDATE so ein Beispiel:
UPDATE Mitarbeiter
SET Name = 'Mayer'
WHERE Personalnummer = 20001;
Dieses Beispiel sieht besser so aus:
UPDATE Mitarbeiter
SET Name = @Name
WHERE Personalnummer = @PersNr;
In einem Programm mit ADO.NET und Visual Basic für MS-SQL wird dieser Befehl wie folgt verwendet.
' zuerst der vollständige SQL-Befehl in einer oder mehreren Zeilen
Dim sel As String = "UPDATE ... @PersNr;"
' danach die Festlegungen zu den Parametern
Dim cmd As SqlCommand = new SqlCommand(sel)
cmd.Parameters.AddWithValue("@Name", "Mayer")
cmd.Parameters.AddWithValue("@PersNr", 20001)
Zwar unterscheidet sich die Art, wie die Parameter bezeichnet werden, nach Programmiersprache und DBMS. Auch sieht es nach mehr Schreibarbeit aus. Aber diese Unterschiede sind viel geringer als die Unterschiede nach den Datentypen; insgesamt wird die Arbeit viel sicherer.
Parameter gibt es nur für DML-Befehle, nicht für DDL oder DCL.
DDL – Data Definition Language
BearbeitenDDL definiert die Struktur einer Datenbank.
Hierzu gibt es die Befehle CREATE, ALTER, DROP; diese werden für Datenbank-Objekte DATABASE, TABLE, VIEW usw. verwendet.
Einzelheiten werden in DDL - Struktur der Datenbank behandelt.
TCL – Transaction Control Language
BearbeitenDamit die Daten dauerhaft zusammenpassen, also die Integrität der Daten gewahrt bleibt, sollen Änderungen, die zusammengehören, auch „am Stück“ übertragen und gespeichert werden. Falls eine einzelne dieser Änderungen nicht funktioniert, muss der gesamte Vorgang rückgängig gemacht werden.
Dies wird durch Transaktionen gesteuert, die mit COMMIT oder ROLLBACK abgeschlossen werden.
Einzelheiten werden in TCL - Ablaufsteuerung behandelt.
DCL – Data Control Language
BearbeitenEine „vollwertige“ SQL-Datenbank regelt umfassend die Rechte für den Zugriff auf Objekte (Tabellen, einzelne Felder, interne Funktionen usw.). Hierzu kommen die Befehle GRANT und REVOKE zum Einsatz.
Mehr darüber steht in DCL - Zugriffsrechte.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir grundlegende Informationen zu SQL-Befehlen kennen:
- Die Befehle der DML (Data Manipulation Language) bearbeiten die Daten und gehören zum Aufgabenbereich eines jeden Benutzers.
- Mit SELECT werden Daten abgerufen, mit INSERT und UPDATE gespeichert und mit DELETE gelöscht.
- Die DML-Befehle arbeiten mengenorientiert; anstelle konkreter Werte werden häufig Ausdrücke verwendet.
- Die Befehle der DDL (Data Definition Language) steuern die interne Struktur der Datenbank und gehören zum Aufgabenbereich von Programmentwicklern.
- Die Befehle der TCL (Transaction Control Language) sorgen für die Integrität der Daten beim Speichern und gehören ebenfalls zum Aufgabenbereich von Programmentwicklern.
- Die Befehle der DCL (Data Control Language) sorgen für die Zugriffssicherheit und gehören zum Aufgabenbereich von Systemadministratoren.
Die Bestandteile der SQL-Befehle werden anhand von Schlüsselwörtern erkannt und ausgewertet.
Übungen
Bearbeiten
Übung 1 | Begriffsklärung | Zur Lösung |
Was versteht man unter „SQL“?
- Süß – Quadratisch – Lecker
- Server's Quick Library
- Structured Query Language
- Standard Quarterly Lectures
Übung 2 | Begriffsklärung | Zur Lösung |
Was versteht man unter „DBMS“?
- Datenbankmanagementsprache
- Datenbankmanagementsystem
- Data-Base Manipulation Standard
- Deutsche Bahn Mobilstation
Übung 3 | Begriffsklärung | Zur Lösung |
Was versteht man unter „SQL-Dialekt“?
- die Sprache des Computers
- die Sprache des Benutzers
- die Sprache des Programmierers eines DBMS
- die Abweichungen der SQL-Befehle vom SQL-Standard
- die jeweilige Version eines DBMS
Übung 4 | Teilbereiche von SQL | Zur Lösung |
Zu welchem der SQL-Teilbereiche DML (Data Manipulation Language), DDL (Data Definition Language), TCL (Transaction Control Language), DCL (Data Control Language) gehören die folgenden Befehle?
- wähle Daten aus
- erzeuge eine Tabelle in der Datenbank
- erzeuge eine Prozedur
- ändere die Informationen, die zu einem Mitarbeiter gespeichert sind
- ändere die Definition einer Spalte
- bestätige eine Gruppe von zusammengehörenden Anweisungen
- gewähre einem Benutzer Zugriffsrechte auf eine Tabelle
- SELECT ID FROM Mitarbeiter
- ROLLBACK
- UPDATE Versicherungsvertrag SET ...
- lösche einen Datensatz in einer Tabelle
- lösche eine Tabelle insgesamt
Übung 5 | SQL-Kommentare | Zur Lösung |
Welche der folgenden Zeilen enthalten korrekte Kommentare? Mit … werden weitere Teile angedeutet, die für die Frage unwichtig sind.
- SELECT * FROM Mitarbeiter;
- UPDATE Mitarbeiter SET Name = 'neu'; -- ändert den Namen aller Zeilen
- DEL/*löschen*/ETE FROM Mitarbeiter WHERE ...
- -- DELETE FROM Mitarbeiter WHERE ...
- UPDATE Mitarbeiter /* ändern */ SET Name = ...; /* usw. */
Lösung zu Übung 1 | Begriffsklärung | Zur Übung |
Antwort 3 ist richtig.
Lösung zu Übung 2 | Begriffsklärung | Zur Übung |
Antwort 2 ist richtig.
Lösung zu Übung 3 | Begriffsklärung | Zur Übung |
Antwort 4 ist richtig.
Lösung zu Übung 4 | Teilbereiche von SQL | Zur Übung |
1. DML – 2. DDL – 3. DDL – 4. DML
5. DDL – 6. TCL – 7. DCL – 8. DML
9. TCL – 10. DML – 11. DML – 12. DDL
Lösung zu Übung 5 | SQL-Kommentare | Zur Übung |
- Diese Zeile enthält keinen Kommentar.
- Der letzte Teil nach dem Semikolon ist ein Kommentar.
- Dieser Versuch eines Kommentars zerstört das Befehlswort DELETE und ist deshalb unzulässig.
- Die gesamte Zeile zählt als Kommentar.
- Diese Zeile enthält zwei Kommentare: zum einen zwischen dem Tabellennamen und dem Schlüsselwort SET sowie den Rest der Zeile hinter dem Semikolon.
Siehe auch
BearbeitenBei Wikipedia gibt es Erläuterungen zu Fachbegriffen:
- Grafische Benutzeroberfläche (GUI)
- HTML
- Parameter in der Informatik
- SQL-Injection
- .NET und ADO.NET
- Datenintegrität
Zur Arbeit mit SQL-Parametern unter C# und ADO.NET gibt es eine ausführliche Darstellung:
DML (1) – Daten abfragen |
Eine Datenbank enthält eine Vielzahl von verschiedenen Daten. Abfragen dienen dazu, bestimmte Daten aus der Datenbank auszugeben. Dabei kann die Ergebnismenge entsprechend den Anforderungen eingegrenzt und genauer gesteuert werden.
Dieser Teilbereich der Data Manipulation Language (DML) behandelt den SQL-Befehl SELECT, mit dem Abfragen durchgeführt werden.
Zunächst geht es um einfache Abfragen. Vertieft wird der SELECT-Befehl unter „Abfragen für Fortgeschrittene“ behandelt, beginnend mit Ausführliche SELECT-Struktur; unten im Abschnitt Ausblick auf komplexe Abfragen gibt es Hinweise auf diese weiteren Möglichkeiten.
SELECT – Allgemeine Hinweise
BearbeitenSELECT ist in der Regel der erste und wichtigste Befehl, den der SQL-Neuling kennenlernt, und das aus gutem Grund: Man kann damit keinen Schaden anrichten. Ein Fehler im Befehl führt höchstens zu einer Fehlermeldung oder dem Ausbleiben des Abfrageergebnisses, aber nicht zu Schäden am Datenbestand. Trotzdem erlaubt der Befehl das Herantasten an die wichtigsten Konzepte von DML, und die anderen Befehle müssen nicht mehr so intensiv erläutert werden.
Dieser Befehl enthält die folgenden Bestandteile („Klauseln“ genannt).
SELECT [DISTINCT | ALL] <spaltenliste> | * FROM <tabellenliste> [WHERE <bedingungsliste>] [GROUP BY <spaltenliste> ] [HAVING <bedingungsliste>] [UNION <select-ausdruck>] [ORDER BY <spaltenliste> ] ;
Die Reihenfolge der Klauseln ist fest im SQL-Standard vorgegeben. Klauseln, die in [ ] stehen, sind nicht nötig, sondern können entfallen; der Name des Befehls und die FROM-Angaben sind unbedingt erforderlich, das Semikolon als Standard empfohlen.
Die wichtigsten Teile werden in den folgenden Abschnitten erläutert.
Die folgenden Punkte verlangen dagegen vertiefte Beschäftigung mit SQL:
- GROUP BY – Daten gruppieren
- HAVING – weitere Einschränkungen
- UNION – mehrere Abfragen verbinden
Diese Punkte sowie weitere Einzelheiten zu den wichtigsten Bestandteilen werden in Ausführliche SELECT-Struktur und anderen „fortgeschrittenen“ Kapiteln behandelt.
Die Beispiele beziehen sich auf den Anfangsbestand der Beispieldatenbank; auf die Ausgabe der selektierten Datensätze wird in der Regel verzichtet. Bitte probieren Sie alle Beispiele aus und nehmen Sie verschiedene Änderungen vor, um die Auswirkungen zu erkennen.
Die einfachste Abfrage
BearbeitenGesucht wird der Inhalt der Tabelle der Fahrzeughersteller mit all ihren Spalten und Datensätzen (Zeilen).
SELECT * FROM Fahrzeughersteller;
Schauen wir uns das Beispiel etwas genauer an:
- Die beiden Begriffe SELECT und FROM sind SQL-spezifische Bezeichner.
- Fahrzeughersteller ist der Name der Tabelle, aus der die Daten selektiert und ausgegeben werden sollen.
- Das Sternchen, Asterisk genannt, ist eine Kurzfassung für „alle Spalten“.
Eingrenzen der Spalten
BearbeitenNun wollen wir nur bestimmte Spalten ausgeben, nämlich eine Liste aller Fahrzeughersteller; das Land interessiert uns dabei nicht. Dazu müssen zwischen den SQL-Bezeichnern SELECT und FROM die auszugebenden Spalten angegeben werden. Sind es mehrere, dann werden diese durch jeweils ein Komma getrennt.
So erhält man die Namensliste aller Fahrzeughersteller:
SELECT Name FROM Fahrzeughersteller;
Folgendes Beispiel gibt die beiden Spalten für Name und Land des Herstellers aus. Die Spalten werden durch Komma getrennt.
SELECT Name, Land FROM Fahrzeughersteller;
Für die Ausgabe kann eine (abweichende) Spaltenüberschrift festgelegt werden. Diese wird als Spalten-Alias bezeichnet. Der Alias kann dem Spaltennamen direkt folgen oder mit dem Bindewort AS angegeben werden. Das vorherige Beispiel kann also wie folgt mit dem Alias Hersteller für Name (ohne AS) und dem Alias Staat für Land (mit AS) versehen werden:
SELECT Name Hersteller, Land AS Staat
FROM Fahrzeughersteller;
DISTINCT – Keine doppelten Zeilen
BearbeitenGesucht wird die Liste der Herstellerländer:
SELECT Land
FROM Fahrzeughersteller;
Dabei stellen wir fest, dass je Hersteller eine Zeile ausgegeben wird. Somit erscheint beispielweise 'Deutschland' mehrmals. Damit keine doppelten Zeilen ausgegeben werden, wird DISTINCT vor den Spaltennamen in das SQL-Statement eingefügt:
SELECT DISTINCT Land
FROM Fahrzeughersteller;
Damit erscheint jedes Herstellerland nur einmal in der Liste.
Die Alternative zu DISTINCT ist übrigens das in der Syntax genannte ALL: alle Zeilen werden gewünscht, ggf. auch doppelte. Dies ist aber der Standardwert, ALL kann weggelassen werden.
Vertiefte Erläuterungen sind unter Nützliche Erweiterungen zu finden.
WHERE – Eingrenzen der Ergebnismenge
BearbeitenFast immer soll nicht der komplette Inhalt einer Tabelle ausgegeben werden. Dazu wird die Ergebnismenge mittels Bedingungen in der WHERE-Klausel eingegrenzt, welche nach dem Tabellennamen im SELECT-Befehl steht.
Eine Bedingung ist ein logischer Ausdruck, dessen Ergebnis WAHR oder FALSCH ist. In diesen logischen Ausdrücken werden die Inhalte der Spalten (vorwiegend) mit konstanten Werten verglichen. Hierbei stehen verschiedene Operatoren zur Verfügung, vor allem:
= gleich <> ungleich; seltener auch: != < kleiner als <= kleiner als oder gleich > größer als >= größer als oder gleich
Bedingungen können durch die logischen Operatoren OR und AND und die Klammern () verknüpft werden. Je komplizierter solche Verknüpfungen werden, desto sicherer ist es, die Bedingungen durch Klammern zu gliedern. Mit diesen Mitteln lässt sich die Abfrage entsprechend eingrenzen.
Beispielsweise sollen alle Hersteller angezeigt werden, die ihren Sitz in Schweden oder Frankreich haben:
SELECT * FROM Fahrzeughersteller
WHERE ( Land = 'Schweden' ) OR ( Land = 'Frankreich' );
Hinter der WHERE-Klausel kann man also eine oder mehrere (mit einem booleschen Operator verknüpft) Bedingungen einfügen. Jede einzelne besteht aus dem Namen der Spalte, deren Inhalt überprüft werden soll, und einem Wert, wobei beide mit einem Vergleichsoperator verknüpft sind.
In einer anderen Abfrage sollen alle Fahrzeughersteller angezeigt werden, die außerhalb Deutschlands sitzen. Jetzt könnte man alle anderen Fälle einzeln in der WHERE-Klausel auflisten, oder man dreht einfach den Vergleichsoperator um.
SELECT * FROM Fahrzeughersteller
WHERE Land <> 'Deutschland';
Das Gleichheitszeichen aus der oberen Abfrage wurde durch das Ungleichheitszeichen ersetzt. Dadurch werden jetzt alle Hersteller ausgegeben, deren Sitz ungleich Deutschland ist.
Vertiefte Erläuterungen sind unter WHERE-Klausel im Detail zu finden.
ORDER BY – Sortieren
BearbeitenNachdem wir nun die Zeilen und Spalten der Ergebnismenge eingrenzen können, wollen wir die Ausgabe der Zeilen sortieren. Hierfür wird die ORDER BY-Klausel genutzt. Diese ist die letzte im SQL-Befehl vor dem abschließenden Semikolon und enthält die Spalten, nach denen sortiert werden soll.
So lassen wir uns die Liste der Hersteller nach dem Namen sortiert ausgeben:
SELECT * FROM Fahrzeughersteller
ORDER BY Name;
Anstatt des Spaltennamens kann auch die Nummer der Spalte genutzt werden. Mit dem folgenden Statement erreichen wir also das gleiche Ergebnis, da Name die zweite Spalte in unserer Ausgabe ist:
SELECT * FROM Fahrzeughersteller
ORDER BY 2;
Die Angabe nach Spaltennummer ist unüblich; sie wird eigentlich höchstens dann verwendet, wenn die Spalten genau aufgeführt werden und komplizierte Angaben – z. B. Berechnete Spalten – enthalten.
Die Sortierung erfolgt standardmäßig aufsteigend; das kann auch durch ASC ausdrücklich angegeben werden. Die Sortierreihenfolge kann mit dem DESC-Bezeichner in absteigend verändert werden.
SELECT * FROM Fahrzeughersteller
ORDER BY Name DESC;
In SQL kann nicht nur nach einer Spalte sortiert werden. Es können mehrere Spalten zur Sortierung herangezogen werden. Hierbei kann für jede Spalte eine eigene Regel verwendet werden. Dabei gilt, dass die Regel zu einer folgend angegebenen Spalte der Regel zu der vorig angegebenen Spalte untergeordnet ist. Bei der Sortierung nach Land und Name wird also zuerst nach dem Land und dann je Land nach Name sortiert. Eine Neusortierung nach Name, die jene Sortierung nach Land wieder verwirft, findet also nicht statt.
Der folgende Befehl liefert die Hersteller – zuerst absteigend nach Land und dann aufsteigend sortiert nach dem Namen – zurück.
SELECT * FROM Fahrzeughersteller
ORDER BY Land DESC, Name ASC;
FROM – Mehrere Tabellen verknüpfen
BearbeitenIn fast allen Abfragen werden Informationen aus mehreren Tabellen zusammengefasst. Die sinnvolle Speicherung von Daten in getrennten Tabellen ist eines der Merkmale eines relationalen DBMS; deshalb müssen die Daten bei einer Abfrage nach praktischen Gesichtspunkten zusammengeführt werden.
Traditionell mit FROM und WHERE
BearbeitenBeim „traditonellen“ Weg werden dazu einfach alle Tabellen in der FROM-Klausel aufgeführt und durch jeweils eine Bedingung in der WHERE-Klausel verknüpft.
Ermittle die Angaben der Mitarbeiter, deren Abteilung ihren Sitz in Dortmund oder Bochum hat.
SELECT mi.Name,
mi.Vorname,
mi.Raum,
ab.Ort
FROM Mitarbeiter mi, Abteilung ab
WHERE mi.Abteilung_ID = ab.ID
AND ab.Ort in ('Dortmund', 'Bochum')
ORDER BY mi.Name, mi.Vorname;
Es werden also Informationen aus den Tabellen Mitarbeiter (Name und Raum) sowie Abteilung (Ort) gesucht. Für die Verknüpfung der Tabellen werden folgende Bestandteile benötigt:
- In der FROM-Klausel stehen die benötigten Tabellen.
- Zur Vereinfachung wird jeder Tabelle ein Kürzel als Tabellen-Alias zugewiesen.
- In der Spaltenliste wird jede einzelne Spalte mit dem Namen der betreffenden Tabelle bzw. dem Alias verbunden. (Der Tabellenname bzw. Alias kann sehr oft weggelassen werden; aber schon wegen der Übersichtlichkeit sollte er immer benutzt werden.)
- Die WHERE-Klausel enthält die Verknüpfungsbedingung „mi.Abteilung_ID = ab.ID“ – zusätzlich zur Einschränkung nach dem Sitz der Abteilung.
Jede Tabelle in einer solchen Abfrage benötigt mindestens eine direkte Verknüpfung zu einer anderen Tabelle. Alle Tabellen müssen zumindest indirekt miteinander verknüpft sein. Falsche Verknüpfungen sind eine häufige Fehlerquelle.
Vertiefte Erläuterungen sind unter Einfache Tabellenverknüpfung zu finden.
Modern mit JOIN...ON
BearbeitenBeim „modernen“ Weg wird eine Tabelle in der FROM-Klausel aufgeführt, nämlich diejenige, die als wichtigste oder „Haupttabelle“ der Abfrage angesehen wird. Eine weitere Tabelle wird durch JOIN und eine Bedingung in der ON-Klausel verknüpft.
Das obige Beispiel sieht dann so aus:
SELECT mi.Name,
mi.Vorname,
mi.Raum,
ab.Ort
FROM Mitarbeiter mi
JOIN Abteilung ab
ON mi.Abteilung_ID = ab.ID
WHERE ab.Ort in ('Dortmund', 'Bochum')
ORDER BY mi.Name, mi.Vorname;
Für die Verknüpfung der Tabellen werden folgende Bestandteile benötigt:
- In der FROM-Klausel steht eine der benötigten Tabellen.
- In der JOIN-Klausel steht jeweils eine weitere Tabelle.
- Die ON-Klausel enthält die Verknüpfungsbedingung „mi.Abteilung_ID = ab.ID“.
- Die WHERE-Klausel beschränkt sich auf die wirklich gewünschten Einschränkungen für die Ergebnismenge.
Ein Tabellen-Alias ist wiederum für alle Tabellen sinnvoll. In der Spaltenliste und auch zur Sortierung können alle Spalten aller Tabellen benutzt werden.
Vertiefte Erläuterungen sind unter Arbeiten mit JOIN zu finden.
Ausblick auf komplexe Abfragen
BearbeitenDas folgende Beispiel ist erheblich umfangreicher und geht über „Anfängerbedürfnisse“ weit hinaus. Es zeigt aber sehr schön, was alles mit SQL möglich ist:
Gesucht werden die Daten der Versicherungsnehmer im Jahr 2008, und zwar die Adresse, die Höhe des Gesamtschadens und die Anzahl der Schadensfälle.
select vn.Name,
vn.Vorname,
vn.Strasse,
vn.Hausnummer as HNR,
vn.PLZ,
vn.Ort,
SUM(sf.Schadenshoehe) as Gesamtschaden,
COUNT(sf.ID) as Anzahl
from Versicherungsnehmer vn
join Versicherungsvertrag vv
on vv.Versicherungsnehmer_ID = vn.ID
join Fahrzeug fz
on fz.ID = vv.Fahrzeug_ID
join Zuordnung_SF_FZ zu
on zu.Fahrzeug_ID = fz.ID
join Schadensfall sf
on sf.ID = zu.Schadensfall_ID
where EXTRACT(YEAR from sf.Datum) = 2008
group by vn.Name, vn.Vorname, vn.Strasse, vn.Hausnummer, vn.PLZ, vn.Ort
order by Gesamtschaden, Anzahl;
NAME VORNAME STRASSE HNR PLZ ORT GESAMTSCHADEN ANZAHL
---------------------- -------- --------------- --- ----- ------------- ------------- ------
Heckel Obsthandel GmbH Gahlener Str. 40 46282 Dorsten 1.438,75 1
Antonius Bernhard Coesfelder Str. 23 45892 Gelsenkirchen 1.983,00 1
Hierbei kommen die Funktionen SUM (Summe) und COUNT (Anzahl) zum Einsatz. Diese können nur eingesetzt werden, wenn die Datenmenge richtig gruppiert wurde. Deshalb wird mit GROUP BY das Datenmaterial nach allen verbliebenen, zur Ausgabe vorgesehenen, Datenfeldern gruppiert.
Vertiefte Erläuterungen sind zu finden unter Funktionen sowie Gruppierungen.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir die Grundlagen eines SELECT-Befehls kennen:
- SELECT-Befehle werden zur Abfrage von Daten aus Datenbanken genutzt.
- Die auszugebenden Spalten können festgelegt werden, indem die Liste der Spalten zwischen den Bezeichnern SELECT und FROM angegeben wird.
- Mit DISTINCT werden identische Zeilen in der Ergebnismenge nur einmal ausgegeben.
- Die Ergebnismenge wird mittels der WHERE-Klausel eingegrenzt.
- Die WHERE-Klausel enthält logische Ausdrücke. Diese können mit AND und OR verknüpft werden.
- Mittels der ORDER BY-Klausel kann die Ergebnismenge sortiert werden.
Die Reihenfolge innerhalb eines SELECT-Befehls ist zu beachten. SELECT und FROM sind hierbei Pflicht, das abschließende Semikolon als Standard empfohlen. Alle anderen Klauseln sind optional.
Übungen
BearbeitenHinweis: Der direkte Sprung zur jeweiligen Lösung funktioniert erst, wenn die Lösung sichtbar ist.
Bei den Übungen 2 ff. ist jeweils eine Abfrage zur Tabelle Abteilung zu erstellen.
Übung 1 | Pflichtangaben | Zur Lösung |
Welche Bestandteile eines SELECT-Befehls sind unbedingt erforderlich und können nicht weggelassen werden?
Übung 2 | Alle Angaben | Zur Lösung |
Geben Sie alle Informationen zu allen Abteilungen aus.
Übung 3 | Angaben mit Einschränkung | Zur Lösung |
Geben Sie alle Abteilungen aus, deren Standort Bochum ist.
Übung 4 | Angaben mit Einschränkungen | Zur Lösung |
Geben Sie alle Abteilungen aus, deren Standort Bochum oder Essen ist. Hierbei soll nur der Name der Abteilung ausgegeben werden.
Übung 5 | Abfrage mit Sortierung | Zur Lösung |
Geben Sie nur die Kurzbezeichnungen aller Abteilungen aus. Hierbei sollen die Abteilungen nach den Standorten sortiert werden.
Lösung zu Übung 1 | Pflichtangaben | Zur Übung |
SELECT, Spaltenliste oder '*', FROM, Tabellenname.
Lösung zu Übung 2 | Alle Angaben | Zur Übung |
select * from Abteilung;
Lösung zu Übung 3 | Angaben mit Einschränkung | Zur Übung |
select * from Abteilung
where Ort = 'Bochum';
Lösung zu Übung 4 | Angaben mit Einschränkungen | Zur Übung |
select Bezeichnung from Abteilung
where Ort = 'Bochum' or Ort = 'Essen';
Alternativ ist es auch so möglich:
select Bezeichnung from Abteilung
where Ort in ('Bochum', 'Essen');
Lösung zu Übung 5 | Abfrage mit Sortierung | Zur Übung |
select Kuerzel from Abteilung
order by Ort;
DML (2) – Daten speichern |
Dieser Teilbereich der Data Manipulation Language (DML) behandelt die Befehle, mit denen die Inhalte der Datenbank geändert werden: Neuaufnahme, Änderung, Löschung.
Bitte beachten Sie, dass mit den Befehlen INSERT, UPDATE, DELETE (fast) immer nur Daten genau einer Tabelle bearbeitet werden können – anders als beim SELECT-Befehl, der Daten mehrerer Tabellen zusammenfassen kann.
INSERT – Daten einfügen
BearbeitenDer INSERT-Befehl dient dem Erstellen von neuen Datensätzen. Es gibt ihn in zwei Versionen – zum einen durch die Angabe einzelner Werte, zum anderen mit Hilfe eines SELECT-Befehls.
In beiden Versionen müssen die Datentypen der Werte zu den Datentypen der Spalten passen. Man sollte nicht versuchen, einer Spalte, die eine Zahl erwartet, eine Zeichenkette zuzuweisen. Man wird nur selten das Ergebnis erhalten, welches man erwartet. Das Kapitel Funktionen erläutert im Abschnitt „Konvertierungen“ Möglichkeiten, wie Werte implizit (also automatisch) oder explizit durch CAST oder CONVERT angepasst werden können.
Einzeln mit VALUES
BearbeitenWenn ein einzelner Datensatz durch die Angabe seiner Werte gespeichert werden soll, gilt folgende Syntax:
INSERT INTO <tabellenname> [ ( <spaltenliste> ) ] VALUES ( <werteliste> ) ;
Zu diesem Befehl gehören folgende Angaben:
- INSERT als Name des Befehls, INTO als feststehender Begriff
- <Tabellenname> als Name der Tabelle, die diesen Datensatz erhalten soll
- in Klammern ( ) gesetzt eine Liste von Spalten (Feldnamen), denen Werte zugewiesen werden
- der Begriff VALUES als Hinweis darauf, dass einzelne Werte angegeben werden
- in Klammern ( ) gesetzt eine Liste von Werten, die in den entsprechenden Spalten gespeichert werden sollen
Wenn eine Liste von Spalten fehlt, bedeutet das, dass alle Spalten dieser Tabelle in der Reihenfolge der Struktur mit Werten versehen werden müssen.
So wird (wie im Skript der Beispieldatenbank) ein Eintrag in der Tabelle Mitarbeiter gespeichert:
INSERT INTO Mitarbeiter
( Personalnummer, Name, Vorname,
Telefon, Email, Raum, Ist_Leiter, Abteilung_ID, Geburtsdatum )
VALUES ( '20002', 'Schmitz', 'Michael',
'0231/5556187', 'michael.schmitz@unserefirma.de', '212', 'N', 2, '1959-08-25' );
Wenn Sie diesen Befehl mit der Tabellenstruktur vergleichen, werden Sie feststellen:
- Die Spalte ID fehlt. Dieser Wert wird von der Datenbank automatisch vergeben.
- Die Spalte Mobil fehlt. In dieser Spalte wird folglich ein NULL-Wert gespeichert.
- Die Reihenfolge der Spalten weicht von der Tabellendefinition ab; das ist also durchaus möglich.
In der Beschreibung der Beispieldatenbank werden sehr viele Spalten als „Pflicht“ festgelegt. Folgender Befehl wird deshalb zurückgewiesen:
Die Spalte Geburtsdatum darf laut Definition nicht NULL sein. Eine Angabe fehlt in diesem Befehl: das wird als NULL interpretiert, also mit einer Fehlermeldung quittiert.
Mengen mit SELECT
BearbeitenWenn eine Menge von Datensätzen mit Hilfe eines SELECT-Befehls gespeichert werden soll, gilt folgende Syntax:
INSERT INTO <tabellenname> [ ( <spaltenliste> ) ] SELECT <select-Ausdruck> ;
Zu diesem Befehl gehören die folgenden Angaben:
- INSERT INTO <Tabellenname> (wie oben)
- in Klammern ( ) gesetzt eine Liste von Spalten (Feldnamen), sofern vorgesehen
- dazu ein vollständiger SELECT-Befehl, mit dem die passenden Inhalte geliefert werden
Da ein SELECT-Befehl auch ohne Bezug auf eine Tabelle nur mit konstanten Werten möglich ist, kann das obige Beispiel auch so formuliert werden:
INSERT INTO Mitarbeiter
( Personalnummer, Name, Vorname,
Telefon, Email, Raum, Ist_Leiter, Abteilung_ID, Geburtsdatum )
select '20002', 'Schmitz', 'Michael',
'0231/5556187', 'michael.schmitz@unserefirma.de', '212', 'N', 2, '1959-08-25'
/* from rdb%database (bei Firebird) */ ;
/* from dual (bei Oracle) */ ;
Hinweis: Firebird und Oracle kennen diese Kurzform des SELECT-Befehls nicht; dort ist die als Kommentar jeweils eingefügte FROM-Klausel erforderlich.
Wichtig ist diese Art des INSERT-Befehls, wenn neue Datensätze aus vorhandenen anderen Daten abgeleitet werden wie im Skript der Beispieldatenbank:
Für jeden Abteilungsleiter aus der Tabelle Mitarbeiter wird ein Eintrag in der Tabelle Dienstwagen gespeichert:
INSERT INTO Dienstwagen
( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )
SELECT 'DO-WB 42' || Abteilung_ID, 'elfenbein', 14, ID
FROM Mitarbeiter
WHERE Ist_Leiter = 'J';
Die Spalte ID wird automatisch zugewiesen. Alle anderen Spalten erhalten ausdrücklich Werte:
- Farbe und Fahrzeugtyp als Konstante
- dazu natürlich die ID des Mitarbeiters, dem der Dienstwagen zugeordnet wird
- und ein Kfz-Kennzeichen, das aus einem konstanten Teil mit der ID der Abteilung zusammengesetzt wird
Manche Datenbanken erlauben auch die Erstellung von Tabellen aus einem SELECT-Ausdruck wie bei MS-SQL (nächstes Beispiel) oder Teradata (anschließend):
SELECT [ ( <spaltenliste> ) ] INTO <tabellenname> FROM <tabellenname>
CREATE TABLE <tabellennameA> AS SELECT [ ( <spaltenliste> ) ] FROM <tabellenname>
UPDATE – Daten ändern
BearbeitenDer UPDATE-Befehl dient zum Ändern einer Menge von Datensätzen in einer Tabelle:
UPDATE <Tabellenname> SET <Feldänderungen> [WHERE <Bedingungsliste>];
Jede Änderung eines Feldes ist so einzutragen:
<Feldname> = <Wert>,
Zu diesem Befehl gehören die folgenden Angaben:
- UPDATE als Name des Befehls
- <Tabellenname> als Name der Tabelle, in der die Daten zu ändern sind
- SET als Anfang der Liste von Änderungen
- <Feldname> als Name der Spalte, die einen neuen Inhalt erhalten soll, dazu das Gleichheitszeichen und der <Wert> als neuer Inhalt
- ein Komma als Hinweis, dass ein weiteres Feld zu ändern ist; vor der WHERE-Klausel oder dem abschließenden Semikolon muss das Komma entfallen
- die WHERE-Klausel mit Bedingungen, welche Datensätze zu ändern sind: einer oder eine bestimmte Menge
Die Struktur der WHERE-Klausel ist identisch mit derjenigen beim SELECT-Befehl. Wenn alle Datensätze geändert werden sollen, kann die WHERE-Bedingung entfallen; aber beachten Sie unbedingt:
- Ohne WHERE-Bedingung wird wirklich alles sofort geändert.
An den Beispielen ist zu sehen, dass die Änderung aller Datensätze nur selten sinnvoll ist und meistens mit WHERE-Bedingung gearbeitet wird.
Wie beim INSERT-Befehl muss der Datentyp eines Wertes zum Datentyp der Spalte passen. Beispiele:
Korrigiere die Schreibweise des Namens bei einem Mitarbeiter.
UPDATE Mitarbeiter
SET Name = 'Mayer'
WHERE Personalnummer = 20001;
Ändere nach einer Eingemeindung PLZ und Ortsname für alle betroffenen Adressen.
UPDATE Versicherungsnehmer
SET Ort = 'Leipzig',
PLZ = '04178'
WHERE PLZ = '04430';
Erhöhe bei allen Schadensfällen die Schadenshöhe um 10 % (das ist natürlich keine sinnvolle Maßnahme):
UPDATE Schadensfall
SET Schadenshoehe = Schadenshoehe * 1.1;
Berichtige das Geburtsdatum für einen Versicherungsnehmer:
Nanu, keine Zeilen wurden geändert? Bei diesem Befehl wurde zur Kontrolle, welcher Datensatz geändert werden sollte, nicht nur der Nachname, sondern auch das bisher notierte Geburtsdatum angegeben – und dieses war falsch.
Daran ist zu sehen, dass der UPDATE-Befehl tatsächlich eine Menge von Datensätzen ändert: je nach WHERE-Klausel null, einen, mehrere oder alle Zeilen der Tabelle.
DELETE – Daten löschen
BearbeitenDer DELETE-Befehl löscht eine Menge von Datensätzen in einer Tabelle:
DELETE FROM <Tabellenname> [ WHERE <Bedingungsliste> ] ;
Zu diesem Befehl gehören folgende Angaben:
- DELETE als Name des Befehls, FROM als feststehender Begriff
- <Tabellenname> als Name der Tabelle, aus der diese Datenmenge entfernt werden soll
- die WHERE-Klausel mit Bedingungen, welche Datensätze zu löschen sind: einer oder eine bestimmte Menge
Die Struktur der WHERE-Klausel ist identisch mit derjenigen beim SELECT-Befehl. Wenn alle Datensätze gelöscht werden sollen, kann die WHERE-Bedingung entfallen; aber beachten Sie unbedingt:
- Ohne WHERE-Bedingung wird wirklich alles sofort gelöscht.
Beispiele:
Der Mitarbeiter mit der Personalnummer 20001 ist ausgeschieden.
DELETE FROM Mitarbeiter
WHERE Personalnummer = 20001;
Die Abteilung 1 wurde ausgelagert, alle Mitarbeiter gehören nicht mehr zum Unternehmen.
DELETE FROM Mitarbeiter
WHERE Abteilung_ID = 1;
Dies leert den gesamten Inhalt der Tabelle, aber die Tabelle selbst bleibt mit ihrer Struktur erhalten.
DELETE FROM Schadensfall;
Achtung: Dies löscht ohne weitere Rückfrage alle Schadensfälle. Ein solcher Befehl sollte unbedingt nur nach einer vorherigen Datensicherung ausgeführt werden. Auch der Versuch ist „strafbar“ und führt zum sofortigen Datenverlust.
TRUNCATE – Tabelle leeren
BearbeitenWenn Sie entgegen den oben genannten Hinweisen wirklich alle Datensätze einer Tabelle löschen wollen, können Sie (soweit vorhanden) anstelle von DELETE den TRUNCATE-Befehl benutzen. Damit werden (ohne Verbindung mit WHERE) immer alle Datensätze gelöscht; dies geschieht schneller und einfacher, weil auf das interne Änderungsprotokoll der Datenbank verzichtet wird. – Wegen solcher technischen Gründe könnte TRUNCATE auch als DDL-Befehl angesehen werden. Aus der Sicht des Anwenders gehört es zu DML, siehe auch die SQL-Dokumente 20nn. Weitere Gesichtspunkte stehen auf der Wikibooks-Diskussionsseite.
TRUNCATE TABLE Schadensfall;
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir die SQL-Befehle kennen, mit denen der Datenbestand geändert wird:
- Mit INSERT + VALUES wird ein einzelner Datensatz eingefügt.
- Mit INSERT + SELECT wird eine Menge von Datensätzen mit Hilfe einer Abfrage eingefügt.
- Mit UPDATE wird eine Menge von Datensätzen geändert; die Menge wird durch WHERE festgelegt.
- Mit DELETE wird eine Menge von Datensätzen gelöscht; die Menge wird durch WHERE festgelegt.
- Mit TRUNCATE werden alle Datensätze einer Tabelle gelöscht.
Die WHERE-Bedingungen sind hier besonders wichtig, damit keine falschen Speicherungen erfolgen.
Übungen
Bearbeiten
Übung 1 | Daten einzeln einfügen | Zur Lösung |
Welche Angaben werden benötigt, wenn ein einzelner Datensatz in der Datenbank gespeichert werden soll?
Übung 2 | Daten einzeln einfügen | Zur Lösung |
Speichern Sie in der Tabelle Mitarbeiter einen neuen Datensatz und lassen Sie alle Spalten und Werte weg, die nicht benötigt werden.
Übung 3 | Daten einfügen | Zur Lösung |
Begründen Sie, warum der Spalte ID beim Einfügen in der Regel kein Wert zugewiesen wird.
Übung 4 | Daten einfügen | Zur Lösung |
Begründen Sie, warum die folgenden Befehle nicht ausgeführt werden können.
/* Mitarbeiter */
INSERT INTO Mitarbeiter
( ID, Personalnummer, Name, Vorname, Geburtsdatum, Ist_Leiter, Abteilung_ID )
values ( 4, 'PD-348', 'Çiçek', 'Yasemin', '23.08.1984', 'J', 9 );
/* Dienstwagen 1 */
INSERT INTO Dienstwagen
( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )
values ( 'DO-UF 1234', null, null, null );
/* Dienstwagen 2 */
INSERT INTO Dienstwagen
( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )
values ( 'HAM-AB 1234', 'rot', 7, null );
Übung 5 | Daten ändern und löschen | Zur Lösung |
Warum gibt es selten UPDATE- oder DELETE-Befehle ohne eine WHERE-Klausel?
Übung 6 | Daten ändern | Zur Lösung |
Schreiben Sie einen SQL-Befehl für folgende Änderung: Alle Mitarbeiter, die bisher noch keinen Mobil-Anschluss hatten, sollen unter einer einheitlichen Nummer erreichbar sein.
Lösung zu Übung 1 | Daten einzeln einfügen | Zur Übung |
- der INSERT-Befehl selbst
- INTO mit Angabe der Tabelle
- bei Bedarf in Klammern die Liste der Spalten, die mit Werten versehen werden
- VALUES zusammen mit (in Klammern) der Liste der zugeordneten Werte
Lösung zu Übung 2 | Daten einzeln einfügen | Zur Übung |
Beispielsweise so:
insert into Mitarbeiter
( Personalnummer, Name, Vorname, Geburtsdatum, Ist_Leiter, Abteilung_ID )
values ( 'PD-348', 'Çiçek', 'Yasemin', '23.08.1984', 'J', 9 );
Lösung zu Übung 3 | Daten einfügen | Zur Übung |
Dieser soll von der Datenbank automatisch zugewiesen werden; er muss deshalb weggelassen oder mit NULL vorgegeben werden.
Lösung zu Übung 4 | Daten einfügen | Zur Übung |
- Mitarbeiter: Diese ID ist schon vergeben; sie muss aber eindeutig sein.
- Dienstwagen 1: Der Fahrzeugtyp ist eine Pflicht-Angabe; die Fahrzeugtyp_ID darf nicht null sein.
- Dienstwagen 2: Das Kennzeichen ist zu lang; es darf maximal eine Länge von 10 Zeichen haben.
Lösung zu Übung 5 | Daten ändern und löschen | Zur Übung |
Dies würde die gleiche Änderung bzw. die Löschung für alle Datensätze ausführen; das ist selten sinnvoll bzw. gewünscht.
Lösung zu Übung 6 | Daten ändern | Zur Übung |
update Mitarbeiter
set Mobil = '(0177) 44 55 66 77'
where Mobil is null or Mobil = '';
DDL – Struktur der Datenbank |
Mit den Befehlen der Data Definition Language (DDL) wird die Struktur der Datenbank gesteuert. Diese Aufgaben gehen über eine Einführung in SQL hinaus. Hier werden deshalb nur einige grundlegende Informationen und Beispiele behandelt, für welche Objekte einer Datenbank diese Befehle verwendet werden.
Am Ende des Buches werden einige Erweiterungen behandelt:
Bitte beachten Sie, dass ein Benutzer vor allem bei DDL-Befehlen über die entsprechenden Rechte verfügen muss, siehe DCL – Zugriffsrechte.
Allgemeine Syntax
BearbeitenDie DDL-Befehle sind grundsätzlich so aufgebaut:
BEFEHL OBJEKTTYP <Objektname> [<weitere Angaben>]
CREATE
BearbeitenCREATE erzeugt ein Datenobjekt, zum Beispiel eine Datentabelle oder gar eine Datenbank.
ALTER
BearbeitenMit ALTER kann das Objekt, z. B. die Tabelle, auch wieder geändert werden:
In neueren Versionen gibt es auch den gemeinsamen Aufruf (unterschiedlich je nach DBMS):
- CREATE OR ALTER
- CREATE OR REPLACE
- RECREATE
Das DBMS entscheidet dann selbst: Wenn das Objekt schon existiert, wird es geändert, andernfalls erzeugt.
DROP
BearbeitenMit DROP kann das Objekt, z. B. eine Tabelle wieder gelöscht werden:
Hauptteile der Datenbank
BearbeitenDATABASE – die Datenbank selbst
BearbeitenDer Befehl zum Erstellen einer Datenbank lautet:
CREATE DATABASE <Dateiname> [ <Optionen> ] ;
Der <Dateiname> ist meistens ein vollständiger Name einschließlich Pfad; in einer solchen Datei werden alle Teile der Datenbank zusammengefasst. Zu den <Optionen> gehören z. B. der Benutzername des Eigentümers der Datenbank mit seinem Passwort, der Zeichensatz mit Angaben zur Standardsortierung, die Aufteilung in eine oder mehrere Dateien usw.
Jedes DBMS bietet sehr verschiedene Optionen; wir können hier keine Gemeinsamkeiten vorstellen und müssen deshalb ganz auf Beispiele verzichten.
Wegen der vielen Möglichkeiten ist zu empfehlen, dass eine Datenbank nicht per SQL-Befehl, sondern innerhalb einer Benutzeroberfläche erstellt wird.
Mit ALTER DATABASE werden die Optionen geändert, mit DROP DATABASE wird die Datenbank gelöscht. Diese Befehle kennt nicht jedes DBMS.
TABLE – eine einzelne Tabelle
BearbeitenCREATE TABLE
BearbeitenUm eine Tabelle zu erzeugen, sind wesentlich konkretere umfangreiche Angaben nötig.
CREATE TABLE <Tabellenname> ( <Spaltenliste> [ , <Zusatzangaben> ] );
Zum Erzeugen einer Tabelle werden folgende Angaben benutzt:
- der Name der Tabelle, mit dem die Daten über die DML-Befehle gespeichert und abgerufen werden
- die Liste der Spalten (Felder), und zwar vor allem mit dem jeweiligen Datentyp
- Angaben wie der Primärschlüssel (PRIMARY KEY, PK) oder weitere Indizes
Jede Spalte und Zusatzangabe wird mit einem Komma abgeschlossen; dieses entfällt vor der schließenden Klammer. Die Zusatzangaben werden häufig nicht sofort festgelegt, sondern durch anschließende ALTER TABLE-Befehle; sie werden deshalb weiter unten besprochen.
In der Beispieldatenbank wird eine Tabelle so erzeugt:
create table Dienstwagen
( ID integer not null auto_increment primary key,
Kennzeichen varchar(30) not null,
Farbe varchar(30),
Fahrzeugtyp_ID integer not null,
Mitarbeiter_ID integer
);
Die einzelnen Spalten berücksichtigen mit ihren Festlegungen unterschiedliche Anforderungen:
- ID ist eine ganze Zahl, darf nicht NULL sein, wird automatisch hochgezählt und dient dadurch gleichzeitig als Primärschlüssel.
- Das Kennzeichen ist eine Zeichenkette von variabler Länge (maximal 30 Zeichen), die unbedingt erforderlich ist.
- Die Farbe ist ebenfalls eine Zeichenkette, deren Angabe entfallen kann.
- Für den Fahrzeugtyp wird dessen ID benötigt, wie er in der Tabelle Fahrzeugtyp gespeichert ist; diese Angabe muss sein – ein „unbekannter“ Fahrzeugtyp macht bei einem Dienstwagen keinen Sinn.
- Für den Mitarbeiter, dem ein Dienstwagen zugeordnet ist, wird dessen ID aus der Tabelle Mitarbeiter benötigt. Dieser Wert kann entfallen, wenn es sich nicht um einen „persönlichen“ Dienstwagen handelt.
ALTER TABLE
BearbeitenDie Struktur einer Tabelle wird wie folgt geändert:
ALTER TABLE <Aufgabe> <Zusatzangaben>
Mit der Aufgabe ADD CONSTRAINT wird eine interne Einschränkung – Constraint genannt – hinzugefügt:
Ein Primärschlüssel kann auch nachträglich festgelegt werden, z. B. wie folgt:
ALTER TABLE Dienstwagen
ADD CONSTRAINT Dienstwagen_PK PRIMARY KEY (ID);
Die Einschränkung bekommt den Namen Dienstwagen_PK und legt fest, dass es sich dabei um den PRIMARY KEY unter Verwendung der Spalte ID handelt.
In der Tabelle Mitarbeiter muss auch die Personalnummer eindeutig sein (zusätzlich zur ID, die als PK sowieso eindeutig ist):
ALTER TABLE Mitarbeiter
ADD CONSTRAINT Mitarbeiter_PersNr UNIQUE (Personalnummer);
In der Tabelle Zuordnung_SF_FZ – Verknüpfung zwischen den Schadensfällen und den Fahrzeugen – wird ein Feld für sehr lange Texte eingefügt:
alter Table Zuordnung_SF_FZ
add Beschreibung blob;
Mit ALTER ... DROP Beschreibung kann dieses Feld auch wieder gelöscht werden.
DROP TABLE
BearbeitenDamit wird eine Tabelle mit allen Daten gelöscht (diese Tabelle gab es in einer früheren Version der Beispieldatenbank):
DROP TABLE ZUORD_VNE_SCF;
Warnung: Dies löscht die Tabelle einschließlich aller Daten unwiderruflich!
USER – Benutzer
BearbeitenAuf diese Weise wird ein neuer Benutzer für die Arbeit mit der aktuellen Datenbank registriert.
CREATE USER Hans_Dampf IDENTIFIED BY 'cH4y37X1P';
Dieser Befehl richtet einen neuen Benutzer mit dem Namen Hans_Dampf ein, der sich mit dem Passwort 'cH4y37X1P' anmelden muss. – Jedes DBMS kennt eigene Regeln und weitere Optionen für die Zuordnung des Passworts und die Verwendung von Anführungszeichen.
Ergänzungen zu Tabellen
BearbeitenWeitere Objekte in der Datenbank erleichtern die Arbeit mit Tabellen.
VIEW – besondere Ansichten
BearbeitenEine VIEW ist eine spezielle Sicht auf eine oder mehrere Tabellen. Für den Anwender sieht es wie eine eigene Tabelle aus; es handelt sich aber „nur“ um eine fest gespeicherte Abfrage, die immer wieder in der gleichen Form benutzt und ausgeführt wird. Bitte beachten Sie: Nur die Abfrage wird fest gespeichert, nicht das Ergebnis; dieses muss bei jedem neuen Aufruf nach den aktuellen Daten neu erstellt werden.
Einzelheiten werden unter Erstellen von Views behandelt.
INDEX – Datenzugriff beschleunigen
BearbeitenEin Index beschleunigt die Suche nach Datensätzen. Um beispielsweise in der Tabelle Versicherungsnehmer nach dem Namen „Schulze“ zu suchen, würde es zu lange dauern, wenn das DBMS alle Zeilen durchgehen müsste, bis es auf diesen Namen träfe. Stattdessen wird ein Index angelegt (ähnlich wie in einem Telefon- oder Wörterbuch), sodass schnell alle passenden Datensätze gefunden werden.
So wird ein Index mit der Bezeichnung Versicherungsnehmer_Name für die Kombination „Name, Vorname“ angelegt:
create index Versicherungsnehmer_Name
on Versicherungsnehmer (Name, Vorname);
Es ist dringend zu empfehlen, dass Indizes für alle Spalten bzw. Kombinationen von Spalten angelegt werden, die immer wieder zum Suchen benutzt werden.
Weitere Einzelheiten werden unter DDL – Einzelheiten behandelt.
IDENTITY – auch ein automatischer Zähler
BearbeitenAnstelle von AUTO_INCREMENT verwendet MS-SQL diese Erweiterung für die automatische Nummerierung neuer Datensätze:
CREATE TABLE Fahrzeug
(ID INTEGER NOT NULL IDENTITY(1,1),
Kennzeichen VARCHAR(10) NOT NULL,
Farbe VARCHAR(30),
Fahrzeugtyp_ID INTEGER NOT NULL,
CONSTRAINT Fahrzeug_PK PRIMARY KEY (ID)
);
Der erste Parameter bezeichnet den Startwert, der zweite Parameter die Schrittweite zum nächsten ID-Wert.
SEQUENCE – Ersatz für automatischen Zähler
BearbeitenWenn das DBMS für Spalten keine automatische Zählung kennt (Firebird, Oracle), steht dies als Ersatz zur Verfügung.
/* zuerst die Folge definieren */
CREATE SEQUENCE Versicherungsnehmer_ID;
/* dann den Startwert festlegen */
ALTER SEQUENCE Versicherungsnehmer_ID RESTART WITH 1;
/* und im Trigger (s.u.) ähnlich wie eine Funktion benutzen */
NEXT VALUE FOR Versicherungsnehmer_ID
Während der automatische Zähler, der durch AUTO_INCREMENT eingerichtet wird, genau zu der betreffenden Tabelle gehört, bezieht sich eine „Sequenz“ nicht auf eine einzelne Tabelle, sondern auf die gesamte Datenbank. Es ist ohne weiteres möglich, eine einzige Sequenz AllMyIDs zu definieren und die neue ID einer jeden Tabelle daraus abzuleiten. Dies ist durchaus sinnvoll, weil die ID als Primärschlüssel sowieso keine inhaltliche Bedeutung haben darf, sondern nur ein fortlaufender Zähler ist. In der Beispieldatenbank benutzen wir getrennte Sequenzen, weil sie für die verschiedenen DBMS „ähnlich“ aussehen soll.
Oracle arbeitet (natürlich) mit anderer Syntax (mit mehr Möglichkeiten) und benutzt dann NEXTVAL.
Programmieren mit SQL
BearbeitenDie Funktionalität einer SQL-Datenbank kann erweitert werden, und zwar auch mit Bestandteilen einer „vollwertigen“ Programmiersprache, z. B. Schleifen und IF-Abfragen.
Dies wird unter Programmierung behandelt; dabei gibt es relativ wenig Gemeinsamkeiten zwischen den DBMS.
- FUNCTION – Benutzerdefinierte Funktionen
Eigene Funktionen ergänzen die (internen) Skalarfunktionen des DBMS.
- PROCEDURE – Gespeicherte Prozeduren
Eine Prozedur – gespeicherte Prozedur, engl. StoredProcedure (SP) – ist vorgesehen für Arbeitsabläufe, die „immer wiederkehrende“ Arbeiten ausführen sollen. Es gibt sie mit und ohne Argumente und Rückgabewerte.
- TRIGGER – Ereignisse beim Speichern
Ein Trigger ist ein Arbeitsablauf, der automatisch beim Speichern in einer Tabelle ausgeführt wird. Dies dient Eingabeprüfungen oder zusätzlichen Maßnahmen; beispielsweise holt sich Firebird durch einen Trigger den NEXT VALUE einer SEQUENCE (siehe oben).
TRIGGER werden unterschieden nach INSERT-, UPDATE- oder DELETE-Befehlen und können vor oder nach dem Speichern sowie in einer bestimmten Reihenfolge ausgeführt werden.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir die Grundbegriffe einer Datenbankstruktur kennen:
- Die DATABASE selbst sowie TABLE sind die wichtigsten Objekte, ein USER arbeitet damit.
- Eine VIEW ist eine gespeicherte Abfrage, die wie eine Tabelle abgerufen wird.
- Ein INDEX beschleunigt den Datenzugriff.
Darüber hinaus wurde auf weitere Möglichkeiten wie SEQUENCE, Funktionen, Prozeduren und Trigger hingewiesen.
Übungen
Bearbeiten
Übung 1 | Objekte bearbeiten | Zur Lösung |
Welche der folgenden SQL-Befehle enthalten Fehler?
- CREATE DATABASE C:\DBFILES\employee.gdb DEFAULT CHARACTER SET UTF8;
- DROP DATABASE;
- CREATE TABLE Person ( ID PRIMARY KEY, Name VARCHAR(30), Vorname VARCHAR(30) );
- ALTER TABLE ADD UNIQUE KEY (Name);
Übung 2 | Tabelle erstellen | Zur Lösung |
Auf welchen zwei Wegen kann der Primärschlüssel (Primary Key, PK) einer Tabelle festgelegt werden? Ein weiterer Weg ist ebenfalls üblich, aber noch nicht erwähnt worden.
Übung 3 | Tabelle ändern | Zur Lösung |
Skizzieren Sie einen Befehl, mit dem die Tabelle Mitarbeiter um Felder für die Anschrift erweitert werden kann.
Übung 4 | Tabellen | Zur Lösung |
Worin unterscheiden sich TABLE und VIEW in erster Linie?
Lösung zu Übung 1 | Objekte bearbeiten | Zur Übung |
Bei 2. fehlt der Name der Datenbank.
Bei 3. fehlt der Datentyp zur Spalte ID.
Bei 4. fehlt der Name der Tabelle, die geändert werden soll.
Lösung zu Übung 2 | Tabelle erstellen | Zur Übung |
- Im CREATE TABLE-Befehl zusammen mit der Spalte, die als PK benutzt wird, z. B.:
ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
- Durch einen ALTER TABLE-Befehl, der den PK hinzufügt:
ALTER TABLE Dienstwagen ADD PRIMARY KEY (ID);
Lösung zu Übung 3 | Tabelle ändern | Zur Übung |
alter table Mitarbeiter
add PLZ CHAR(5),
add Ort VARCHAR(24),
add Strasse VARCHAR(24),
add Hausnummer VARCHAR(10);
Lösung zu Übung 4 | Tabellen | Zur Übung |
Eine TABLE (Tabelle) ist real in der Datenbank gespeichert. Eine VIEW (Sichttabelle) ist eine Sicht auf eine oder mehrere tatsächlich vorhandene Tabellen; sie enthält eine Abfrage auf diese Tabellen, die als Abfrage in der Datenbank gespeichert ist, aber für den Anwender wie eine eigene Tabelle aussieht.
Siehe auch
BearbeitenBei diesem Kapitel sind die folgenden Erläuterungen zu beachten:
Manche Themen werden in den folgenden Kapiteln genauer behandelt:
- Eigene Funktionen als Ergänzung zu denen, die in Funktionen und Funktionen (2) behandelt werden.
- Prozeduren
- Trigger
TCL – Ablaufsteuerung |
Dieses Kapitel gibt eine kurze Einführung in die Transaction Control Language (TCL). Deren Befehle sorgen für die Datensicherheit innerhalb einer Datenbank.
MySQL verfolgt eine „offenere“ Philosophie und arbeitet neben Transaktionen auch mit anderen Sicherungsmaßnahmen. Der Ersteller einer Datenbank muss sich für ein Verfahren entscheiden, kann aber auch danach noch variieren.
Beispiele
BearbeitenEine SQL-Datenbank speichert Daten in der Regel in verschiedenen Tabellen. Wenn Daten geändert werden sollen, müssen folglich mehrere Tabellen simultan verändert werden. Dazu wäre es nötig, dass die betreffenden Befehle immer gleichzeitig ausgeführt werden. Da der Computer Befehle nur nacheinander ausführen kann, muss sichergestellt sein, dass nicht der eine Befehl ausgeführt wird, während der andere Befehl scheitert.
- Zu einer Überweisung bei der Bank gehören immer zwei Buchungen: die Gutschrift auf dem einen und die Lastschrift auf dem anderen Konto; häufig gehören die Konten zu verschiedenen Banken. Es wäre völlig unerträglich, wenn die Gutschrift ausgeführt würde und die (externe) Lastschrift nicht, weil in diesem Moment die Datenleitung unterbrochen wird.
- Wenn in dem Versicherungsunternehmen der Beispieldatenbank ein neuer Vertrag abgeschlossen wird, gehören dazu mehrere INSERT-Befehle, und zwar in die Tabellen Fahrzeug, Versicherungsnehmer, Versicherungsvertrag. Zuerst müssen Fahrzeug und Versicherungsnehmer gespeichert werden; aber wenn das Speichern des Vertrags „schiefgeht“, hängen die beiden anderen Datensätze nutzlos in der Datenbank herum.
- Wenn dort eine Abteilung ausgelagert wird, werden alle ihre Mitarbeiter gestrichen, weil sie nicht mehr zum Unternehmen gehören. Wie soll verfahren werden, wenn nur ein Teil der DELETE-Befehle erfolgreich war?
- Eine Menge einzelner Befehle (z. B. 1000 INSERTs innerhalb einer Schleife) dauert „ewig lange“.
Solche Probleme können nicht nur durch die Hardware entstehen, sondern auch dadurch, dass parallel andere Nutzer denselben Datenbestand ändern wollen.
Transaktionen
BearbeitenAlle solche Ungereimtheiten werden vermieden, indem SQL-Befehle in Transaktionen zusammengefasst und ausgeführt werden:
- Entweder alle Befehle können ausgeführt werden. Dann wird die Transaktion bestätigt und erfolgreich abgeschlossen.
- Oder (mindestens) ein Befehl kann nicht ausgeführt werden. Dann wird die Transaktion für ungültig erklärt; alle Befehle werden rückgängig gemacht.
- Auch das Problem mit der langen Arbeitszeit von 1000 INSERTs wird vermieden, wenn das DBMS nicht jeden Befehl einzeln prüft und bestätigt, sondern erst alle 1000 am Schluss „am Stück“.
Es gibt verschiedene Arten von Transaktionen. Diese hängen vom DBMS und dessen Version, der Hardware (Einzel- oder Mehrplatzsystem) und dem Datenzugriff (direkt oder über Anwenderprogramme) ab.
- Wenn die Datenbank auf AUTOCOMMIT eingestellt ist, wird jeder SQL-Befehl als einzelne Transaktion behandelt und sofort gültig. (Das wäre die Situation mit der langen Arbeitszeit von 1000 INSERTs.)
- Wenn ein Stapel von Befehlen mit COMMIT bestätigt oder mit ROLLBACK verworfen wird, dann wird mit dem nächsten Befehl implizit eine neue Transaktion begonnen.
- Mit einem ausdrücklichen TRANSACTION-Befehl wird explizit eine neue Transaktion begonnen:
BEGIN TRANSACTION <TName>; /* bei MS-SQL */ SET TRANSACTION <TName>; /* bei Firebird */ START TRANSACTION; /* bei MySQL */ START TRANSACTION; /* PostgreSQL 9.6.3 */
Eine Transaktion kann mit einem Namen versehen werden. Dies ist vor allem dann nützlich, wenn Transaktionen geschachtelt werden. Außerdem gibt es je nach DBMS noch viele weitere Optionen, mit denen eine Transaktion detailliert gesteuert werden kann.
- Eine Transaktion, die implizit oder explizit begonnen wird, ist ausdrücklich abzuschließen durch COMMIT oder ROLLBACK. Wenn dies vergessen wird, wird die Transaktion erst dadurch beendet, dass die Verbindung mit der Datenbank geschlossen wird.
Transaktion erfolgreich beenden
BearbeitenEine Transaktion wird mit einem der folgenden Befehle erfolgreich abgeschlossen und beendet:
COMMIT [ TRANSACTION | WORK ] <TName>;
Die genaue Schreibweise und Varianten müssen in der DBMS-Dokumentation nachgelesen werden.
Dieser Befehl bestätigt alle vorangegangenen Befehle einer Transaktion und sorgt dafür, dass sie „am Stück“ gespeichert werden.
Auszug aus dem Skript zum Erstellen der Beispieldatenbank:
COMMIT;
/* damit wird die vorherige Transaktion abgeschlossen und implizit eine neue Transaktion gestartet */
INSERT INTO Dienstwagen (Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID)
VALUES ('DO-WB 111', 'elfenbein', 16, NULL);
INSERT INTO Dienstwagen (Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID)
SELECT 'DO-WB 3' || Abteilung_ID || SUBSTRING(Personalnummer FROM 5 FOR 1),
'gelb', SUBSTRING(Personalnummer FROM 5 FOR 1), ID
FROM Mitarbeiter
WHERE Abteilung_ID IN (5, 8)
AND Ist_Leiter = 'N';
/* damit wird diese Transaktion abgeschlossen */
COMMIT;
Sicherungspunkte
BearbeitenMit dem folgenden Befehl wird eine Transaktion in „sichere“ Abschnitte geteilt:
SAVEPOINT <SPName>;
Bis zu diesem Sicherungspunkt werden die Befehle auch dann als gültig abgeschlossen, wenn die Transaktion am Ende für ungültig erklärt wird.
Transaktion rückgängig machen
BearbeitenEine Transaktion wird wie folgt für ungültig erklärt:
ROLLBACK [ TRANSACTION | WORK ] <TName> [ TO <SPName> ] ;
Damit werden alle Befehle der Transaktion <TName> für ungültig erklärt und rückgängig gemacht. Sofern ein Sicherungspunkt <SPName> angegeben ist, werden die Befehle bis zu diesem Sicherungspunkt für gültig erklärt und erst alle folgenden für ungültig.
Die genaue Schreibweise und Varianten müssen in der DBMS-Dokumentation nachgelesen werden.
Mit dem folgenden Beispiel werden zu Testzwecken einige Daten geändert und abgerufen; abschließend werden die Änderungen rückgängig gemacht.
update Dienstwagen
set Farbe = 'goldgelb/violett gestreift'
where ID >= 14;
select * from Dienstwagen;
ROLLBACK;
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie die Grundbegriffe von Transaktionen kennen:
- Eine Transaktion wird implizit oder explizit begonnen.
- Eine Transaktion wird mit einem ausdrücklichen Befehl (oder durch Ende der Verbindung) abgeschlossen.
- Mit COMMIT wird eine Transaktion erfolgreich abgeschlossen; die Daten werden abschließend gespeichert.
- Mit ROLLBACK werden die Änderungen verworfen, ggf. ab einem bestimmten SAVEPOINT.
Übungen
Bearbeiten
Übung 1 | Zusammengehörende Befehle | Zur Lösung |
Skizzieren Sie die Befehle, die gemeinsam ausgeführt werden müssen, wenn ein neues Fahrzeug mit einem noch nicht registrierten Fahrzeugtyp und Fahrzeughersteller gespeichert werden soll.
Übung 2 | Zusammengehörende Befehle | Zur Lösung |
Skizzieren Sie die Befehle, die gemeinsam ausgeführt werden müssen, wenn ein neuer Schadensfall ohne weitere beteiligte Fahrzeuge registriert werden soll.
Übung 3 | Zusammengehörende Befehle | Zur Lösung |
Skizzieren Sie die Befehle, die gemeinsam ausgeführt werden müssen, wenn ein neuer Schaden durch einen „Eigenen Kunden“ gemeldet wird; dabei sollen ein zweiter „Eigener Kunde“ sowie ein „Fremdkunde“ einer bisher nicht gespeicherten Versicherungsgesellschaft beteiligt sein. Erwähnen Sie dabei auch den Inhalt des betreffenden Eintrags.
Übung 4 | Transaktionen | Zur Lösung |
Welche der folgenden Maßnahmen starten immer eine neue Transaktion, welche unter Umständen, welche sind unzulässig?
- das Herstellen der Verbindung zur Datenbank
- der Befehl START TRANSACTION;
- der Befehl SET TRANSACTION ACTIVE;
- der Befehl SAVEPOINT <name> wird ausgeführt
- der Befehl ROLLBACK wird ausgeführt
Übung 5 | Transaktionen | Zur Lösung |
Welche der folgenden Maßnahmen beenden immer eine Transaktion, welche unter Umständen, welche sind unzulässig?
- das Schließen der Verbindung zur Datenbank
- der Befehl END TRANSACTION;
- der Befehl SET TRANSACTION INACTIVE;
- der Befehl SAVEPOINT <name> wird ausgeführt
- der Befehl ROLLBACK wird ausgeführt
Lösung zu Übung 1 | Zusammengehörende Befehle | Zur Übung |
- INSERT INTO Fahrzeughersteller
- INSERT INTO Fahrzeugtyp
- INSERT INTO Fahrzeug
Lösung zu Übung 2 | Zusammengehörende Befehle | Zur Übung |
- INSERT INTO Schadensfall
- INSERT INTO Zuordnung_SF_FZ
Lösung zu Übung 3 | Zusammengehörende Befehle | Zur Übung |
- INSERT INTO Schadensfall
- INSERT INTO Versicherungsgesellschaft /* für den zusätzlichen Fremdkunden */
- INSERT INTO Versicherungsnehmer /* für den zusätzlichen Fremdkunden */
- INSERT INTO Fahrzeug /* für den zusätzlichen Fremdkunden */
- INSERT INTO Zuordnung_SF_FZ /* für den zusätzlichen Fremdkunden */
- INSERT INTO Zuordnung_SF_FZ /* für den beteiligten Eigenen Kunden */
- INSERT INTO Zuordnung_SF_FZ /* für den Eigenen Kunden laut Schadensmeldung */
Lösung zu Übung 4 | Transaktionen | Zur Übung |
- ja, sofern AUTOCOMMIT festgelegt ist
- ja, aber nur, wenn das DBMS diese Variante vorsieht (wie MySQL)
- ja, aber nur, wenn das DBMS diese Variante vorsieht (wie Firebird); denn das Wort „ACTIVE“ wird nicht als Schlüsselwort, sondern als Name der Transaction interpretiert
- nein, das ist nur ein Sicherungspunkt innerhalb einer Transaktion
- ja, nämlich implizit für die folgenden Befehle
Lösung zu Übung 5 | Transaktionen | Zur Übung |
- ja, und zwar immer
- nein, diesen Befehl gibt es nicht
- nein, dieser Befehl ist unzulässig; wenn das DBMS diese Variante kennt (wie Firebird), dann ist es der Start einer Transaktion namens „INACTIVE“
- teilweise, das ist ein Sicherungspunkt innerhalb einer Transaktion und bestätigt die bisherigen Befehle, setzen aber dieselbe Transaktion fort
- ja, nämlich explizit durch Widerruf aller Befehle
DCL – Zugriffsrechte |
Eine „vollwertige“ SQL-Datenbank enthält umfassende Regelungen über die Vergabe von Rechten für den Zugriff auf Objekte (Tabellen, einzelne Felder, interne Funktionen usw.). Am Anfang stehen diese Rechte nur dem Ersteller der Datenbank und dem System-Administrator zu. Andere Benutzer müssen ausdrücklich zu einzelnen Handlungen ermächtigt werden.
Da es sich dabei nicht um Maßnahmen für Einsteiger handelt, beschränken wir uns auf ein paar Beispiele.
GRANT – Zugriff gewähren
BearbeitenDieser Befehl gestattet eine bestimmte Aktion mit Zugriff auf ein bestimmtes Objekt.
Der Benutzer Herr_Mueller darf Abfragen auf die Tabelle Abteilungen ausführen.
GRANT SELECT ON Abteilung TO Herr_Mueller
Die Benutzerin Frau_Schulze darf Daten in der Tabelle Abteilungen ändern.
GRANT UPDATE ON Abteilung TO Frau_Schulze
REVOKE – Zugriff verweigern
BearbeitenDieser Befehl verweigert eine bestimmte Aktion mit Zugriff auf ein bestimmtes Objekt.
Herr_Mueller darf künftig keine solche Abfragen mehr ausführen.
REVOKE SELECT ON Abteilung FROM Herr_Mueller
Datentypen |
SQL kennt verschiedene Arten von Datentypen: vordefinierte, konstruierte und benutzerdefinierte. Diese Arten und ihre Verwendungen werden in diesem Abschnitt erklärt.
Vordefinierte Datentypen
BearbeitenLaut SQL-Standard sind die folgenden Datentypen vordefiniert. Die fettgedruckten Begriffe sind die entsprechenden reservierten Schlüsselwörter.
Achtung Bei allen Datentypen weichen die SQL-Dialekte mehr oder weniger vom Standard ab. |
Dies betrifft vor allem die folgenden Punkte:
- bei den möglichen Werten, z. B. dem maximalen Wert einer ganzen Zahl oder der Länge einer Zeichenkette
- bei der Bearbeitung einzelner Typen; z. B. kennt Firebird noch nicht sehr lange den BOOLEAN-Typ und musste mit dem Ersatz „ganze Zahl gleich 1“ leben
- bei der Art der Werte, z. B. ob Datum und Zeit getrennt oder gemeinsam gespeichert sind
Zeichen und Zeichenketten
BearbeitenEs gibt Zeichenketten mit fester, variabler und sehr großer Länge.
- CHARACTER(n), CHAR(n)
Hierbei handelt es sich um Zeichenketten mit fester Länge von genau n Zeichen. Für ein einzelnes Zeichen muss die Länge (1) nicht angegeben werden.
- CHARACTER VARYING(n), VARCHAR(n)
Dies sind Zeichenketten mit variabler Länge bei maximal n Zeichen.
- CHARACTER LARGE OBJECT
Hierbei handelt es sich um beliebig große Zeichenketten. Diese Variante ist relativ umständlich zu nutzen; sie wird vorwiegend für die Speicherung ganzer Textdateien verwendet.
Zu allen diesen Varianten gibt es auch die Festlegung eines nationalen Zeichensatzes durch NATIONAL CHARACTER bzw. NCHAR oder NATIONAL CHARACTER VARYING bzw. NVARCHAR. Erläuterungen dazu siehe unten im Abschnitt über Zeichensätze.
Die maximale Länge von festen und variablen Zeichenketten hängt vom DBMS ab. In früheren Versionen betrug sie oft nur 255, heute sind 32767 verbreitet.
Die maximale Feldlänge bei Zeichenketten ist für Eingabe, Ausgabe und interne Speicherung wichtig. Die DBMS verhalten sich unterschiedlich, ob am Anfang oder Ende stehende Leerzeichen gespeichert oder entfernt werden. Wichtig ist, dass bei fester Feldlänge ein gelesener Wert immer mit genau dieser Anzahl von Zeichen zurückgegeben wird und bei Bedarf rechts mit Leerzeichen aufgefüllt wird.
In der Praxis sind folgende Gesichtspunkte von Bedeutung:
- Für indizierte Felder (also Spalten, denen ein Index zugeordnet wird) sind feste Längen vorzuziehen; beachten Sie aber die nächsten Hinweise.
- Als CHAR(n), also mit fester Länge, sind Felder vorzusehen, deren Länge bei der überwiegenden Zahl der Datensätze konstant ist. Beispiel: die deutschen Postleitzahlen mit CHAR(5).
- Als VARCHAR(n), also mit variabler Länge, sind Felder vorzusehen, deren Länge stark variiert. Beispiel: Namen und Vornamen mit VARCHAR(30).
- In Zweifelsfällen ist pragmatisch vorzugehen. Beispiel: Die internationalen Postleitzahlen (Post-Code, Zip-Code) benötigen bis zu 10 Zeichen. Wenn eine Datenbank überwiegend nur deutsche Adressen enthält, passt VARCHAR(10) besser, bei hohem Anteil von britischen, US-amerikanischen, kanadischen und ähnlichen Adressen ist CHAR(10) zu empfehlen.
Zahlen mit exakter Größe
BearbeitenWerte dieser Datentypen werden mit der genauen Größe gespeichert.
- INTEGER bzw. INT
Ganze Zahl mit Vorzeichen. Der Größenbereich hängt von der Implementierung ab; auf einem 32-bit-System entspricht es meistens ±231-1, genauer von –2 147 483 648 bis +2 147 483 647.
- SMALLINT
Ebenfalls ein Datentyp für ganze Zahlen, aber mit kleinerem Wertebereich als INTEGER, oft von -32 768 bis +32 767.
- BIGINT
Ebenfalls ein Datentyp für ganze Zahlen, aber mit größerem Wertebereich als INTEGER. Auch der SQL-Standard akzeptiert, dass ein DBMS diesen Typ nicht kennt.
- NUMERIC(p,s) sowie DECIMAL(p,s)
Datentypen für Dezimalzahlen mit exakter Speicherung, also „Festkommazahlen“, wobei p die Genauigkeit und s die Anzahl der Nachkommastellen angibt. Dabei muss 0 ≤ s ≤ p sein, und s hat einen Defaultwert von 0. Der Parameter s = 0 kann entfallen; der Vorgabewert für p hängt vom DBMS ab.
Diese Dezimalzahlen sind wegen der genauen Speicherung z. B. für Daten der Buchhaltung geeignet. Bei vielen DBMS gibt es keinen Unterschied zwischen NUMERIC und DECIMAL.
Zahlen mit „näherungsweiser“ Größe
BearbeitenWerte dieser Datentypen werden nicht unbedingt mit der genauen Größe gespeichert, sondern in vielen Fällen nur näherungsweise.
- FLOAT, REAL, DOUBLE PRECISION
Diese Datentypen haben grundsätzlich die gleiche Bedeutung. Je nach DBMS gibt FLOAT(p,s) die Genauigkeit oder die Anzahl der Dezimalstellen an; auch der Wertebereich und die Genauigkeit hängen vom DBMS ab.
Diese „Gleitkommazahlen“ sind für technisch-wissenschaftliche Werte geeignet und umfassen auch die Exponentialdarstellung. Wegen der Speicherung im Binärformat sind sie aber für Geldbeträge nicht geeignet, weil sich beispielsweise der Wert 0,10 € (entspricht 10 Cent) nicht exakt abbilden lässt. Es kommt immer wieder zu Rundungsfehlern.
Zeitpunkte und Zeitintervalle
BearbeitenFür Datum und Uhrzeit gibt es die folgenden Datentypen:
- DATE
Das Datum enthält die Bestandteile YEAR, MONTH, DAY, wobei die Monate innerhalb eines Jahres und die Tage innerhalb eines Monats gemeint sind.
- TIME
Die Uhrzeit enthält die Bestandteile HOUR, MINUTE, SECOND, wobei die Minute innerhalb einer Stunde und die Sekunden innerhalb einer Minute gemeint sind. Sehr oft werden auch Millisekunden als Bruchteile von Sekunden registriert.
- TIMESTAMP
Der „Zeitstempel“ enthält Datum und Uhrzeit zusammen.
Zeitangaben können WITH TIME ZONE oder WITHOUT TIME ZONE deklariert werden. Ohne die Zeitzone ist in der Regel die lokale Zeit gemeint, mit der Zeitzone wird die Koordinierte Weltzeit (UTC) gespeichert.
Bei Datum und Uhrzeit enden die Gemeinsamkeiten der SQL-Dialekte endgültig; sie werden unterschiedlich mit „eigenen“ Datentypen realisiert. Man kann allenfalls annehmen, dass ein Tag intern mit einer ganzen Zahl und ein Zeitwert mit einem Bruchteil einer ganzen Zahl gespeichert wird.
Beispiele:
Datenbanksystem | Datentyp | Geltungsbereich | Genauigkeit |
---|---|---|---|
MS-SQL Server 2005 | datetime | 01.01.1753 bis 31.12.9999 | 3,33 Millisekunden |
smalldatetime | 01.01.1900 bis 06.06.2079 | 1 Minute | |
MS-SQL Server 2008 | date | 01.01.0001 bis 31.12.9999 | 1 Tag |
time | 00:00:00.0000000 bis 23:59:59.9999999 | 100 Nanosekunden | |
datetime | 01.01.0001 bis 31.12.9999 | 3,33 Millisekunden | |
smalldatetime | 01.01.1900 bis 06.06.2079 | 1 Minute | |
Firebird | DATE | 01.01.0100 bis 29.02.32768 | 1 Tag |
TIME | 00:00 bis 23:59.9999 | 6,67 Millisekunden | |
MySQL 5.x | DATETIME | 01.01.1000 00:00:00 bis 31.12.9999 23:59:59 | 1 Sekunde |
DATE | 01.01.1000 bis 31.12.9999 | 1 Tag | |
TIME | –838:59:59 bis 838:59:59 | 1 Sekunde | |
YEAR | 1901 bis 2055 | 1 Jahr |
Bitte wundern Sie sich nicht: bei jedem DBMS gibt es noch weitere Datentypen und Bezeichnungen.
Die Deklaration von TIME bei MySQL zeigt schon: Es muss sich dabei nicht um eine Uhrzeit innerhalb eines Datums handeln, sondern kann auch einen Zeitraum, d. h. ein Intervall darstellen.
- INTERVAL
Ein Intervall setzt sich – je nach betrachteter Zeitdauer – zusammen aus:- YEAR, MONTH für längere Zeiträume (der SQL-Standard kennt auch nur die Bezeichnung "year-month-interval")
- DAY, HOUR, MINUTE, SECOND für Zeiträume innerhalb eines Tages oder über mehrere Tage hinweg
Große Objekte
BearbeitenBLOB (Binary Large Object, binäre große Objekte) ist die allgemeine Bezeichnung für unbestimmt große Objekte.
- BLOB
Allgemein werden binäre Objekte z. B. für Bilder oder Bilddateien verwendet, nämlich dann, wenn der Inhalt nicht näher strukturiert ist und auch Bytes enthalten kann, die keine Zeichen sind.
- CLOB
Speziell werden solche Objekte, die nur „echte“ Zeichen enthalten, zum Speichern von großen Texten oder Textdateien verwendet.
Je nach DBMS werden BLOB-Varianten durch Sub_Type oder spezielle Datentypen für unterschiedliche Maximalgrößen oder Verwendung gekennzeichnet.
Boolean
BearbeitenDer Datentyp BOOLEAN ist für logische Werte vorgesehen. Solche Felder können die Werte TRUE (wahr) und FALSE (falsch) annehmen; auch NULL ist möglich und wird als UNKNOWN (unbekannt) interpretiert.
Wenn ein DBMS diesen Datentyp (noch) nicht kennt – wie MySQL –, dann ist mit einem der numerischen Typen eine einfache Ersatzlösung möglich (wie früher bei Interbase und Firebird); siehe unten im Abschnitt über Domains.
Konstruierte und benutzerdefinierte Datentypen
BearbeitenDiese Datentypen, die aus den vordefinierten Datentypen zusammengesetzt werden, werden hier nur der Vollständigkeit halber erwähnt; sie sind in der Praxis eines Anwenders ziemlich unwichtig.
- ROW
Eine Zeile ist eine Sammlung von Feldern; jedes Feld besteht aus dem Namen und dem Datentyp. Nun ja, eine Zeile in einer Tabelle ist (natürlich) von diesem Typ.
- REF
Referenztypen sind zwar im SQL-Standard vorgesehen, treten aber in der Praxis nicht auf.
- ARRAY, MULTISET
Felder und Mengen sind Typen von Sammlungen ("collection type"), in denen jedes Element vom gleichen Datentyp ist. Ein Array gibt die Elemente durch die Position an, ein Multiset ist eine ungeordnete Menge. Wegen der Notwendigkeit, Tabellen zu normalisieren, sind diese Typen in der Praxis unwichtig.
- Benutzerdefinierte Typen
Dazu gehören nicht nur ein Typ, sondern auch Methoden zu ihrer Verwendung. Auch für solche Typen sind keine sinnvollen Anwendungen zu finden.
Spezialisierte Datentypen
BearbeitenDatentypen können mit Einschränkungen, also CONSTRAINTs versehen werden; auch der Begriff Domain wird verwendet. (Einen vernünftigen deutschen Begriff gibt es dafür nicht.) Der SQL-Standard macht nicht viele Vorgaben.
Der Befehl zum Erstellen einer Domain sieht allgemein so aus:
CREATE DOMAIN <Domain-Name> <zugehöriger Datentyp> [<Vorgabewert>] [<Einschränkungen>]
Unter Interbase/Firebird wurde auf diese Weise ein Ersatz für den BOOLEAN-Datentyp erzeugt:
CREATE DOMAIN BOOLEAN
-- definiere diesen Datentyp
AS INT
-- als Integer
DEFAULT 0 NOT NULL
-- Vorgabewert 0, hier ohne NULL-Werte
CHECK (VALUE BETWEEN 0 AND 1);
-- Werte können nur 0 (= false) und 1 (= true) sein
Bei MySQL können Spalten mit ENUM oder SET auf bestimmte Werte eingeschränkt werden, allerdings nur auf Zeichen.
Nationale und internationale Zeichensätze
BearbeitenAus der Frühzeit der EDV ist das Problem der nationalen Zeichen geblieben: Mit 1 Byte (= 8 Bit) können höchstens 256 Zeichen (abzüglich 32 Steuerzeichen sowie Ziffern und einer Reihe von Satz- und Sonderzeichen) dargestellt werden; und das reicht nicht einmal für die Akzentbuchstaben (Umlaute) aller westeuropäischen Sprachen. Erst mit Unicode gibt es einen Standard, der weltweit alle Zeichen (z. B. auch die chinesischen Zeichen) darstellen und speichern soll.
Da ältere EDV-Systeme (Computer, Programme, Programmiersprachen, Datenbanken) weiterhin benutzt werden, muss die Verwendung nationaler Zeichensätze nach wie vor berücksichtigt werden. Dafür gibt es verschiedene Maßnahmen – jedes DBMS folgt eigenen Regeln für CHARACTER SET (Zeichensatz) und COLLATE (alphabetische Sortierung) und benutzt eigene Bezeichner für die Zeichensätze und die Regeln der Reihenfolge.
Vor allem wegen der DBMS-spezifischen Bezeichnungen kommen Sie nicht um intensive Blicke in die jeweilige Dokumentation herum.
Zeichensatz festlegen mit CHARACTER SET / CHARSET
BearbeitenWenn eine Datenbank erstellt wird, muss ein Zeichensatz festgelegt werden. Ohne eine Festlegung regelt jedes DBMS selbst je nach Version, welcher Zeichensatz als Standard gelten soll. Wenn ein Programm Zugriff auf eine vorhandene Datenbank nimmt, muss ebenso der Zeichensatz angegeben werden; dieser muss mit dem ursprünglich festgelegten übereinstimmen. Wenn Umlaute falsch angezeigt werden, dann stimmen in der Regel diese Angaben nicht.
Eine neue Datenbank sollte, wenn das DBMS und die Programmiersprache dies unterstützen, möglichst mit Unicode (in der Regel als UTF8) angelegt werden.
In neueren Versionen steht die Bezeichnung NCHAR (= NATIONAL CHAR) oft nicht für einen speziellen nationalen Zeichensatz, sondern für den allgemeinen Unicode-Zeichensatz. Bei CHAR bzw. VARCHAR wird ein spezieller Zeichensatz verwendet, abhängig von der Installation oder der Datenbank.
In diesem Abschnitt kann deshalb nur beispielhaft gezeigt werden, wie Zeichensätze behandelt werden.
- Firebird, Interbase; MySQL
CHARACTER SET beschreibt den Zeichensatz einer Datenbank. Dieser gilt als Vorgabewert für alle Zeichenketten: CHAR(n), VARCHAR(n), CLOB. Für eine einzelne Spalte kann ein abweichender Zeichensatz festgelegt werden. Beispiel:
CREATE DATABASE 'europe.fb' DEFAULT CHARACTER SET ISO8859_1;
ALTER TABLE xyz ADD COLUMN lname VARCHAR(30) NOT NULL CHARACTER SET CYRL;
Es kommt auch vor, dass ein Programm mit einem anderen Zeichensatz arbeitet als die Datenbank. Dann können die Zeichensätze angepasst werden:
SET NAMES DOS437;
CONNECT 'europe.fb' USER 'JAMES' PASSWORD 'U4EEAH';
-- die Datenbank selbst arbeitet mit ISO8859_1, das Programm mit DOS-Codepage 437
- MS-SQL
Die Dokumentation geht nur allgemein auf „Nicht-Unicode-Zeichendaten“ ein. Es gibt keinerlei Erläuterung, wie ein solcher Zeichensatz festgelegt wird.
Sortierungen mit COLLATE
BearbeitenCOLLATE legt fest, nach welchen Regeln die Reihenfolge von Zeichenketten (englisch: Collation Order) bestimmt wird. Der Vorgabewert für die Datenbank bzw. Tabelle hängt direkt vom Zeichensatz ab. Abweichende Regeln können getrennt gesteuert werden für Spalten, Vergleiche sowie Festlegungen bei ORDER BY und GROUP BY.
CREATE DATABASE 'europe.fb' DEFAULT CHARACTER SET ISO8859_1;
-- dies legt automatisch die Sortierung nach ISO8859_1 fest
ALTER TABLE xyz ADD COLUMN lname VARCHAR(30) NOT NULL COLLATE FR_CA;
-- dies legt die Sortierung auf kanadisches Französisch fest
SELECT ... WHERE lname COLLATE FR_FR <= :lname_search;
-- dabei soll der Vergleich nach Französisch (Frankreich) durchgeführt werden
SELECT ...
ORDER BY LNAME COLLATE FR_CA, FNAME COLLATE FR_CA
GROUP BY LNAME COLLATE FR_CA, FNAME COLLATE FR_CA;
-- vergleichbare Festlegungen für Reihenfolge und Gruppierung bei SELECT
Diese Beispiele arbeiten mit den Kürzeln für Interbase/Firebird. Andere DBMS nutzen eigene Bezeichnungen; aber die Befehle selbst sind weitgehend identisch.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie die Datentypen unter SQL kennen:
- Bei Zeichenketten ist zwischen fester und variabler Länge zu unterscheiden und der Zeichensatz – UNICODE oder national – zu beachten.
- Für Zahlen ist zwischen exakter und näherungsweiser Speicherung zu unterscheiden und die Genauigkeit zu beachten.
- Für Datums- und Zeitwerte ist vor allem auf den jeweiligen Geltungsbereich und die Genauigkeit zu achten.
- Für spezielle Zwecke gibt es weitere Datentypen wie BLOB oder BOOLEAN.
Übungen
BearbeitenZahlen und Datumsangaben verwenden immer die im deutschsprachigen Raum üblichen Schreibweisen. Zeichen werden mit Hochkommata begrenzt.
Übung 1 | Texte und Zahlen | Zur Lösung |
Geben Sie zu den Werten jeweils an, welche Datentypen passen, welche fraglich sind (also u. U. möglich, aber nicht sinnvoll oder unklar) und welche falsch sind.
- 'A' als Char, Char(20), Varchar, Varchar(20)
- der Ortsname 'Bietigheim-Bissingen' als Char, Char(20), Varchar, Varchar(20)
- das Wort 'Übungen' als Varchar(20), NVarchar(20)
- 123.456 als Integer, Smallint, Float, Numeric, Varchar(20)
- 123,456 als Integer, Smallint, Float, Numeric, Varchar(20)
- 789,12 [€] als Integer, Smallint, Float, Numeric, Varchar(20)
Übung 2 | Datum und Zeit | Zur Lösung |
Geben Sie jeweils an, welche Datentypen passen, welche fraglich sind (also u. U. möglich, aber nicht sinnvoll oder unklar) und welche falsch sind.
- '27.11.2009' als Date, Time, Timestamp, Char(10), Varchar(20)
- '11:42:53' als Date, Time, Timestamp, Char(10), Varchar(20)
- '27.11.2009 11:42:53' als Date, Time, Timestamp, Char(10), Varchar(20)
- 'November 2009' als Date, Time, Timestamp, Char(10), Varchar(20)
Übung 3 | Personen | Zur Lösung |
Bereiten Sie eine Tabelle Person vor: Notieren Sie mit möglichst konsequenter Aufteilung der Bestandteile die möglichen Spaltennamen und deren Datentypen; berücksichtigen Sie dabei auch internationale Adressen, aber keine Kontaktdaten (wie Telefon).
Übung 4 | Buchhaltung | Zur Lösung |
Bereiten Sie eine Tabelle Kassenbuch vor: Notieren Sie die möglichen Spaltennamen und deren Datentypen; berücksichtigen Sie dabei auch, dass Angaben der Buchhaltung geprüft werden müssen.
Lösung zu Übung 1 | Texte und Zahlen | Zur Übung |
Die „richtige“ Festlegung hängt vom Zusammenhang innerhalb einer Tabelle ab.
- Char und Varchar(20) passen, Varchar ist nicht sinnvoll, Char(20) ist falsch.
- Char(20) und Varchar(20) passen, Char und Varchar sind falsch.
- Je nach verwendetem Zeichensatz können beide Varianten richtig, ungeeignet oder falsch sein.
- Integer und Numeric sind richtig, Float ist möglich, Smallint ist falsch, Varchar(20) ist nicht ganz ausgeschlossen.
- Float und Numeric sind richtig, Integer und Smallint sind falsch, Varchar(20) ist nicht ganz ausgeschlossen.
- Numeric ist richtig, Float ist möglich, Integer und Smallint sind falsch, Varchar(20) ist nicht ganz ausgeschlossen.
Lösung zu Übung 2 | Datum und Zeit | Zur Übung |
- Date und Timestamp sind richtig, Char(10) und Varchar(20) sind möglich, Time ist falsch.
- Time und Timestamp sind richtig, Varchar(20) ist möglich, Char(10) und Date sind falsch.
- Timestamp ist richtig, Varchar(20) ist möglich, Date, Time und Char(10) sind falsch.
- Varchar(20) ist richtig, alles andere falsch.
Lösung zu Übung 3 | Personen | Zur Übung |
Bitte wundern Sie sich nicht über unerwartete Unterteilungen: Bei der folgenden Lösung werden auch Erkenntnisse der Datenbank-Theorie und der praktischen Arbeit mit Datenbanken berücksichtigt.
- ID Integer
- Titel Varchar(15)
- Vorname Varchar(30)
- Adelszusatz Varchar(15) – Trennung ist wegen der alphabetischen Sortierung sinnvoll
- Name Varchar(30)
- Adresszusatz Varchar(30)
- Strasse Varchar(24)
- Hausnr Integer (oder Smallint)
- HausnrZusatz Varchar(10) – Trennung ist wegen der numerischen Sortierung sinnvoll
- Länderkennung Char(2) – nach ISO 3166, auch Char(3) möglich, Integer oder Smallint denkbar; der Ländername ist auf jeden Fall unpraktisch
- PLZ Char(10) oder Varchar(10) – international sind bis zu 10 Zeichen möglich
- Geburtsdatum Date
Lösung zu Übung 4 | Buchhaltung | Zur Übung |
- ID Integer
- Buchungsjahr Integer oder Smallint
- Buchungsnummer Integer
- Buchungstermin Timestamp – je nach Arbeitsweise genügt auch Date
- Betrag Numeric oder Decimal
- Vorgang Varchar(50) – als Beschreibung der Buchung
- Bearbeiter Varchar(30) – derjenige, der den Kassenbestand ändert
- Nutzer Varchar(30) – derjenige, der die Buchung registriert
- Buchhaltung Timestamp – Termin, zu dem die Buchung registriert wird
Wenn das Kassenbuch explizit ein Teil der Buchhaltung ist, werden auch Spalten wie Buchungskonto (Haupt- und Gegenkonten) benötigt.
Siehe auch
BearbeitenIn Wikipedia gibt es zusätzliche Hinweise:
- Gleitkommazahlen mit ausführlicher Erläuterung ihrer Ungenauigkeiten
- Koordinierte Weltzeit (UTC)
- Unicode als umfassender Zeichensatz
- ISO 3166 – Postleitzahl – Liste der Postleitsysteme
Funktionen |
Im SQL-Standard werden verschiedene Funktionen festgelegt, die in jedem SQL-Dialekt vorkommen. In aller Regel ergänzt jedes DBMS diese Funktionen durch weitere eigene.
- Skalarfunktionen verarbeiten Werte oder Ausdrücke aus einzelnen Zahlen, Zeichenketten oder Datums- und Zeitwerten. Sofern die Werte aus einer Spalte geholt werden, handelt es sich immer um einen Wert aus einer einzelnen Zeile.
- Spaltenfunktionen verarbeiten alle Werte aus einer Spalte.
- Ergänzend kann man für beide Varianten auch benutzerdefinierte Funktionen erstellen; dies wird unter Programmierung angesprochen.
Dieses Kapitel enthält als Grundlage nur die wichtigsten Skalarfunktionen. Unter Funktionen (2) gibt es viele Ergänzungen.
Allgemeine Hinweise
BearbeitenDie Funktionen können überall dort verwendet werden, wo ein SQL-Ausdruck möglich ist. Wichtig ist, dass das Ergebnis der Funktion zu dem Datentyp passt, der an der betreffenden Stelle erwartet wird. Auch wenn der SQL-Standard nur einige wenige Funktionen vorschreibt, können eine ganze Reihe von Funktionen als Standard angesehen werden, weil sie immer (oder fast immer) vorhanden sind. An vielen Stellen ist aber auf Unterschiede im Namen oder in der Art des Aufrufs hinzuweisen.
Firebird kennt ab Version 2.1 viele Funktionen, die vorher als benutzerdefinierte Funktionen (user defined functions, UDF) erstellt werden mussten.
Schreibweise für Funktionen: Bei Funktionen müssen die Parameter (auch „Argumente“ genannt) immer in Klammern gesetzt werden. Diese Klammern sind auch bei einer Funktion mit konstantem Wert wie PI erforderlich. Eine Ausnahme sind lediglich die Systemfunktionen CURRENT_DATE u. ä.
Schreibweise der Beispiele: Aus Platzgründen werden Beispiele meistens nur einfach in einen Rahmen gesetzt. Für Funktionen ohne Bezug auf Tabellen und Spalten genügt in der Regel ein einfacher SELECT-Befehl; in manchen Fällen muss eine Tabelle oder eine fiktive Quelle angegeben werden:
SELECT 2 * 3; /* Normalfall */ SELECT 2 * 3 FROM rdb$database; /* bei Firebird und Interbase */ SELECT 2 * 3 FROM dual; /* bei Oracle */ SELECT 2 * 3 FROM SYSIBM.SYSDUMMY1; /* bei IBM DB2 */
In diesem Kapitel und auch im zweiten Kapitel zu Funktionen werden diese Verfahren durch eine verkürzte Schreibweise zusammengefasst:
SELECT 2 * 3 [from fiktiv];
Das ist so zu lesen: Die FROM-Klausel ist für Firebird, Interbase und Oracle notwendig und muss die jeweils benötigte Tabelle angeben; bei allen anderen DBMS muss sie entfallen.
Funktionen für Zahlen
BearbeitenBei allen numerischen Funktionen müssen Sie auf den genauen Typ achten. Es ist beispielsweise ein Unterschied, ob das Ergebnis einer Division zweier ganzer Zahlen als ganze Zahl oder als Dezimalzahl behandelt werden soll.
Operatoren
BearbeitenFür Zahlen stehen die üblichen Operatoren zur Verfügung:
+ Addition - Subtraktion, Negation * Multiplikation / Division
Dafür gelten die üblichen mathematischen Regeln (Punkt vor Strich, Klammern zuerst). Bitte beachten Sie auch folgende Besonderheit:
- Bei der Division ganzer Zahlen ist auch das Ergebnis eine ganze Zahl; man nennt das „Integer-Division“:
SELECT 3 / 5 [from fiktiv]; /* Ergebnis: 0 */
- Wenn Sie das Ergebnis als Dezimalzahl haben wollen, müssen Sie (mindestens) eine der beiden Zahlen als Dezimalzahl vorgeben:
SELECT 3.0 / 5 [from fiktiv]; /* Ergebnis: 0.6 */
- Ausnahme: MySQL liefert auch bei "3/5" eine Dezimalzahl. Für die „Integer-Division“ gibt es die DIV-Funktion:
SELECT 3 DIV 5; /* Ergebnis: 0 */
Division durch 0 liefert zunächst eine Fehlermeldung "division by zero has occurred" und danach das Ergebnis NULL.
MOD – der Rest einer Division
BearbeitenDie Modulo-Funktion MOD bestimmt den Rest bei einer Division ganzer Zahlen:
MOD( <dividend>, <divisor> ) /* allgemein */ <dividend> % <divisor> /* bei MS-SQL und MySQL */
Beispiele:
SELECT MOD(7, 3) [from fiktiv]; /* Ergebnis: 1 */ SELECT ID FROM Mitarbeiter WHERE MOD(ID, 10) = 0; /* listet IDs mit '0' am Ende '/
CEILING, FLOOR, ROUND, TRUNCATE – die nächste ganze Zahl
BearbeitenEs gibt mehrere Möglichkeiten, zu einer Dezimalzahl die nächste ganze Zahl zu bestimmen.
CEILING oder CEIL liefert die nächstgrößere ganze Zahl, genauer: die kleinste Zahl, die größer oder gleich der gegebenen Zahl ist.
SELECT CEILING(7.3), CEILING(-7.3) [from fiktiv]; /* Ergebnis: 8, -7 */
FLOOR ist das Gegenstück dazu und liefert die nächstkleinere ganze Zahl, genauer: die größte Zahl, die kleiner oder gleich der gegebenen Zahl ist.
SELECT FLOOR(7.3), FLOOR(-7.3) [from fiktiv]; /* Ergebnis: 7, -8 */
TRUNCATE oder TRUNC schneidet den Dezimalanteil ab.
SELECT TRUNCATE(7.3),TRUNCATE(-7.3) [from fiktiv]; /* Ergebnis: 7, -7 */ SELECT TRUNCATE(Schadenshoehe) FROM Schadensfall; /* Euro-Werte ohne Cent */
ROUND liefert eine mathematische Rundung: ab 5 wird aufgerundet, darunter wird abgerundet.
ROUND( <Ausdruck> [ , <Genauigkeit> ] )
<Ausdruck> ist eine beliebige Zahl oder ein Ausdruck, der eine beliebige Zahl liefert.
- Wenn <Genauigkeit> nicht angegeben ist, wird 0 angenommen.
- Bei einer positiven Zahl für <Genauigkeit> wird auf entsprechend viele Dezimalstellen gerundet.
- Bei einer negativen Zahl für <Genauigkeit> wird links vom Dezimaltrenner auf entsprechend viele Nullen gerundet.
Beispiele:
SELECT ROUND(12.248,-2) [from fiktiv]; /* Ergebnis: 0,000 */ SELECT ROUND(12.248,-1) [from fiktiv]; /* Ergebnis: 10,000 */ SELECT ROUND(12.248, 0) [from fiktiv]; /* Ergebnis: 12,000 */ SELECT ROUND(12.248, 1) [from fiktiv]; /* Ergebnis: 12,200 */ SELECT ROUND(12.248, 2) [from fiktiv]; /* Ergebnis: 12,250 */ SELECT ROUND(12.248, 3) [from fiktiv]; /* Ergebnis: 12,248 */ SELECT ROUND(12.25, 1) [from fiktiv]; /* Ergebnis: 12,300 */ SELECT ROUND(Schadenshoehe) FROM Schadensfall; /* Euro-Werte gerundet */
Funktionen für Zeichenketten
BearbeitenZur Bearbeitung und Prüfung von Zeichenketten (Strings) werden viele Funktionen angeboten.
Verknüpfen von Strings
BearbeitenAls Operatoren, um mehrere Zeichenketten zu verbinden, stehen zur Verfügung:
|| als SQL-Standard + für MS-SQL oder MySQL CONCAT für MySQL oder Oracle
Der senkrechte Strich wird als „Verkettungszeichen“ bezeichnet und oft auch „Pipe“-Zeichen genannt. Es wird auf der deutschen PC-Tastatur unter Windows durch die Tastenkombination Alt Gr + < > erzeugt.
Ein Beispiel in diesen Varianten:
SELECT Name || ', ' || Vorname from Mitarbeiter; SELECT Name + ', ' + Vorname from Mitarbeiter; SELECT CONCAT(Name, ', ', Vorname) from Mitarbeiter;
Alle diese Varianten liefern das gleiche Ergebnis: Für jeden Datensatz der Tabelle Mitarbeiter werden Name und Vorname verbunden und dazwischen ein weiterer String gesetzt, bestehend aus Komma und einem Leerzeichen.
Länge von Strings
BearbeitenUm die Länge einer Zeichenkette zu erfahren, gibt es die folgenden Funktionen:
CHARACTER_LENGTH( <string> ) SQL-Standard CHAR_LENGTH( <string> ) SQL-Standard Kurzfassung LEN( <string> ) nur für MS-SQL
Beispiele:
SELECT CHAR_LENGTH('Hello World') [from fiktiv]; /* Ergebnis: 11 */ SELECT CHAR_LENGTH(’’) [from fiktiv]; /* Ergebnis: 0 */ SELECT CHAR_LENGTH( NULL ) [from fiktiv]; /* Ergebnis: <null> */ SELECT Name FROM Mitarbeiter ORDER BY CHAR_LENGTH(Name) DESC; /* liefert die Namen der Mitarbeiter, absteigend sortiert nach Länge */
UPPER, LOWER – Groß- und Kleinbuchstaben
BearbeitenUPPER konvertiert den gegebenen String zu Großbuchstaben; LOWER gibt einen String zurück, der nur aus Kleinbuchstaben besteht.
SELECT UPPER('Abc Äöü Xyzß ÀÉÇ àéç') [from fiktiv]; /* Ergebnis: 'ABC ÄÖÜ XYZß ÀÉÇ ÀÉÇ' */ SELECT LOWER('Abc Äöü Xyzß ÀÉÇ àéç') [from fiktiv]; /* Ergebnis: 'abc äöü xyzß àéç àéç' */ SELECT UPPER(Kuerzel), Bezeichnung FROM Abteilung; /* Kurzbezeichnungen in Großbuchstaben */
Ob die Konvertierung bei Umlauten richtig funktioniert, hängt vom verwendeten Zeichensatz ab.
SUBSTRING – Teile von Zeichenketten
BearbeitenSUBSTRING ist der SQL-Standard, um aus einem String einen Teil herauszuholen:
SUBSTRING( <text> FROM <start> FOR <anzahl> ) /* SQL-Standard */ SUBSTRING( <text> , <start> , <anzahl> ) /* MS-SQL, MySQL, Oracle */
Diese Funktion heißt unter Oracle SUBSTR und kann auch bei MySQL so bezeichnet werden.
Der Ausgangstext wird von Position 1 an gezählt. Der Teilstring beginnt an der hinter FROM genannten Position und übernimmt so viele Zeichen wie hinter FOR angegeben ist:
SELECT SUBSTRING('Abc Def Ghi' FROM 6 FOR 4) [from fiktiv]; /* Ergebnis: 'ef G' */ SELECT CONCAT(Name, ', ', SUBSTRING(Vorname FROM 1 FOR 1), '.') from Mitarbeiter; /* liefert den Namen und vom Vornamen den Anfangsbuchstaben */
Wenn der <anzahl>-Parameter fehlt, wird alles bis zum Ende von <text> übernommen:
SELECT SUBSTRING('Abc Def Ghi' FROM 6) [from fiktiv]; /* Ergebnis: 'ef Ghi' */
Wenn der <anzahl>-Parameter 0 lautet, werden 0 Zeichen übernommen, man erhält also einen leeren String.
MySQL bietet noch eine Reihe weiterer Varianten.
Hinweis: Nach SQL-Standard liefert das Ergebnis von SUBSTRING seltsamerweise einen Text von gleicher Länge wie der ursprüngliche Text; die jetzt zwangsläufig folgenden Leerzeichen müssen ggf. mit der TRIM-Funktion (im zweiten Kapitel über Funktionen) entfernt werden.
Funktionen für Datums- und Zeitwerte
BearbeitenBei den Datums- und Zeitfunktionen gilt das gleiche wie für Datum und Zeit als Datentyp: Jeder SQL-Dialekt hat sich seinen eigenen „Standard“ ausgedacht. Wir beschränken uns deshalb auf die wichtigsten Funktionen, die es so ähnlich „immer“ gibt, und verweisen auf die DBMS-Dokumentationen.
Vor allem MySQL bietet viele zusätzliche Funktionen an. Teilweise sind es nur verkürzte und spezialisierte Schreibweisen der Standardfunktionen, teilweise liefern sie zusätzliche Möglichkeiten.
Systemdatum und -uhrzeit
BearbeitenNach SQL-Standard werden aktuelle Uhrzeit und Datum abgefragt:
CURRENT_DATE CURRENT_TIME CURRENT_TIMESTAMP
In Klammern kann als <precision> die Anzahl der Dezimalstellen bei den Sekunden angegeben werden.
Beispiele:
SELECT CURRENT_TIMESTAMP [from fiktiv]; /* Ergebnis: '19.09.2009 13:47:49' */ UPDATE Versicherungsvertrag SET Aenderungsdatum = CURRENT_DATE WHERE irgendetwas;
Bei MS-SQL gibt es nur CURRENT_TIMESTAMP als Standardfunktion, dafür aber andere Funktionen mit höherer Genauigkeit.
Teile von Datum oder Uhrzeit bestimmen
BearbeitenFür diesen Zweck gibt es vor allem EXTRACT als Standardfunktion:
EXTRACT ( <part> FROM <value> )
<value> ist der Wert des betreffenden Datums und/oder der Uhrzeit, die aufgeteilt werden soll. Als <part> wird angegeben, welcher Teil des Datums gewünscht wird:
YEAR | MONTH | DAY | HOUR | MINUTE | SECOND | MILLISECOND
Bei einem DATE-Feld ohne Uhrzeit sind HOUR usw. natürlich unzulässig, bei einem TIME-Feld nur mit Uhrzeit die Bestandteile YEAR usw. Beispiele:
SELECT ID, Datum, EXTRACT(YEAR FROM Datum) AS Jahr, EXTRACT(MONTH FROM Datum) AS Monat FROM Schadensfall ORDER BY Jahr, Monat; SELECT 'Stunde = ' || EXTRACT(HOUR FROM CURRENT_TIME) [from fiktiv]; /* Ergebnis: 'Stunde = 14' */
Bei MS-SQL heißt diese Standardfunktion DATEPART; als <part> können viele weitere Varianten genutzt werden.
Sehr oft gibt es weitere Funktionen, die direkt einen Bestandteil abfragen, zum Beispiel:
YEAR( <value> ) liefert das Jahr MONTH( <value> ) liefert den Monat usw. MINUTE( <value> ) liefert die Minute usw. DAYOFWEEK( <value> ) gibt den Wochentag (als Zahl, 1 für Sonntag usw.) an
Wie gesagt: Lesen Sie in der DBMS-Dokumentation nach, was es sonst noch gibt.
Funktionen für logische und NULL-Werte
BearbeitenWenn man es genau nimmt, gehören dazu auch Prüfungen wie „Ist ein Wert NULL?“. Der SQL-Standard hat dazu und zu anderen Prüfungen verschiedene spezielle Ausdrücke vorgesehen. Diese gehören vor allem zur WHERE-Klausel des SELECT-Befehls:
= < > usw. Größenvergleich zweier Werte BETWEEN AND Werte zwischen zwei Grenzen LIKE Ähnlichkeiten (1) CONTAINS u.a. Ähnlichkeiten (2) IS NULL null-Werte prüfen IN genauer Vergleich mit einer Liste EXISTS schneller Vergleich mit einer Liste
Alle diese Ausdrücke liefern einen der logischen Werte TRUE, FALSE – also WAHR oder FALSCH – und ggf. NULL – also <unbekannt> – zurück und können als boolescher Wert weiterverarbeitet oder ausgewertet werden.
Operatoren
BearbeitenZur Verknüpfung logischer Werte gibt es die „booleschen Operatoren“ NOT, AND, OR (nur bei MySQL auch XOR):
NOT als Negation AND als Konjunktion OR als Adjunktion XOR als Kontravalenz
Auch diese Operatoren werden bei der WHERE-Klausel behandelt.
Zur Verknüpfung von NULL-Werten gibt es vielfältige Regeln, je nach dem Zusammenhang, in dem das von Bedeutung ist. Man kann sich aber folgende Regel merken:
- Wenn ein Ausgangswert NULL ist, also <unbekannt>, und dieser „Wert“ mit etwas verknüpft wird (z. B. mit einer Zahl addiert wird), kann das Ergebnis nur <unbekannt> sein, also NULL lauten.
Konvertierungen
BearbeitenIn der EDV – also auch bei SQL-Datenbanken – ist der verwendete Datentyp immer von Bedeutung. Mit Zahlen kann gerechnet werden, mit Zeichenketten nicht. Größenvergleiche von Zahlen gelten immer und überall; bei Zeichenketten hängt die Reihenfolge auch von der Sprache ab. Ein Datum wird in Deutschland durch '11.09.2001' beschrieben, in England durch '11/09/2001' und in den USA durch '09/11/2001'. Wenn wir etwas aufschreiben (z. B. einen SQL-Befehl), dann benutzen wir zwangsläufig immer Zeichen bzw. Zeichenketten, auch wenn wir Zahlen oder ein Datum meinen.
In vielen Fällen „versteht“ das DBMS, was wir mit einer solchen Schreibweise meinen; dann werden durch „implizite Konvertierung“ die Datentypen automatisch ineinander übertragen. In anderen Fällen muss der Anwender dem DBMS durch eine Konvertierungsfunktion erläutern, was wie zu verstehen ist.
Implizite Konvertierung
BearbeitenDatentypen, die problemlos vergleichbar sind, werden „automatisch“ ineinander übergeführt.
- Die Zahl 17 kann je nach Situation ein INTEGER, ein SMALLINT, ein BIGINT, aber auch NUMERIC oder FLOAT sein.
- Die Zahl 17 kann in einem SELECT-Befehl auch als String '17' erkannt und verarbeitet werden.
SELECT ID, Name, Vorname FROM Mitarbeiter WHERE ID = '17';
- Je nach DBMS kann ein String wie '11.09.2001' als Datum erkannt und verarbeitet werden. Vielleicht verlangt es aber auch eine andere Schreibweise wie '11/09/2001'. Die Schreibweise '2009-09-11' nach ISO 8601 sollte dagegen immer richtig verstanden werden (aber auch da gibt es Abweichungen).
Es gibt bereits durch den SQL-Standard ausführliche Regeln, welche Typen immer, unter bestimmten Umständen oder niemals ineinander übergeführt werden können. Jedes DBMS ergänzt diese allgemeinen Regeln durch eigene.
Wenn ein solcher Befehl ausgeführt wird, dürfte es niemals Missverständnisse geben, sondern er wird „mit an Sicherheit grenzender Wahrscheinlichkeit“ korrekt erledigt. Wenn ein Wert nicht eindeutig ist, wird das DBMS eher zu früh als zu spät mit einer Fehlermeldung wie "conversion error from string ..." reagieren. Dann ist es Zeit für eine explizite Konvertierung, meistens durch CAST.
CAST
BearbeitenDie CAST-Funktion ist der SQL-Standard für die Überführung eines Wertes von einem Datentyp in einen anderen.
CAST ( <expression> AS <type> )
Als <expression> ist etwas angegeben, was den „falschen“ Typ hat, nämlich ein Wert oder Ausdruck. Mit <type> wird der Datentyp angegeben, der an der betreffenden Stelle gewünscht oder benötigt wird. Das Ergebnis des CASTings (der Konvertierung) ist dann genau von diesem Typ.
Beispiele für Datum:
SELECT EXTRACT(DAY FROM '07.14.2009') [from fiktiv]; /* expression evaluation not supported */ SELECT EXTRACT(DAY FROM CAST('07.14.2009' as date)) [from fiktiv]; /* conversion error from string "07.14.2009" */ SELECT EXTRACT(DAY FROM CAST('14.07.2009' as date)) [from fiktiv]; /* Ergebnis: '14' */ SELECT Name, Vorname, CAST(Geburtsdatum AS CHAR(10)) FROM Versicherungsnehmer; /* Ergebnis wird in der Form '1953-01-13' angezeigt */
Kürzere Zeichenketten können schnell verlängert werden, wenn es nötig ist:
SELECT ID, CAST(Kuerzel AS CHAR(20)) FROM Abteilung; -- 20 Zeichen Länge statt eigentlich 10 Zeichen
Das Verkürzen funktioniert nicht immer so einfach. Ob bei Überschreitung einer Maximallänge einfach abgeschnitten wird oder ob es zu einer Fehlermeldung "string truncation" kommt, hängt vom DBMS ab; dann müssen Sie ggf. eine SUBSTRING-Variante benutzen.
-- vielleicht funktioniert es so: SELECT CAST(Name AS CHAR(15)) || Vorname from Versicherungsnehmer; -- aber sicherer klappt es so: SELECT SUBSTRING(Name FROM 1 FOR 15) || Vorname from Versicherungsnehmer; -- unter Berücksichtigung des Hinweises bei SUBSTRING: SELECT TRIM( SUBSTRING(Name FROM 1 FOR 15) ) || Vorname from Versicherungsnehmer;
Bitte lesen Sie in Ihrer SQL-Dokumentation nach, zwischen welchen Datentypen implizite Konvertierung möglich ist und wie die explizite Konvertierung mit CAST ausgeführt wird.
CONVERT
BearbeitenNach dem SQL-Standard ist CONVERT vorgesehen zum Konvertieren von Zeichenketten in verschiedenen Zeichensätzen:
CONVERT ( <text> USING <transcoding name> ) SQL-Standard CONVERT ( <text>, <transcoding name> ) alternative Schreibweise
Firebird kennt diese Funktion überhaupt nicht. MS-SQL benutzt eine andere Syntax und bietet vor allem für Datums- und Zeitformate viele weitere Möglichkeiten:
CONVERT ( <type>, <text> [ , <style> ] )
Wegen dieser starken Abweichungen verzichten wir auf weitere Erläuterungen und verweisen auf die jeweilige Dokumentation.
Datum und Zeit
BearbeitenVor allem für die Konvertierung mit Datums- und Zeitangaben bieten die verschiedenen DBMS Erweiterungen. Beispiele:
- MS-SQL hat die Syntax von CONVERT für diesen Zweck erweitert.
- MySQL ermöglicht die Konvertierung mit STR_TO_DATE und DATE_FORMAT.
- Oracle kennt eine Reihe von Funktionen wie TO_DATE usw..
- Wo es so etwas nicht gibt, kann man Day/Month/Year extrahieren, per CAST konvertieren und dann mit || neu zusammensetzen.
Noch ein Grund für das Studium der Dokumentation...
Spaltenfunktionen
BearbeitenDie Spaltenfunktionen werden auch als Aggregatfunktionen bezeichnet, weil sie eine Menge von Werten – nämlich aus allen Zeilen einer bestimmten Spalte – zusammenfassen und einen gemeinsamen Wert bestimmen. In der Regel wird dazu eine Spalte aus einer der beteiligten Tabellen verwendet; es kann aber auch ein sonstiger gültiger SQL-Ausdruck sein, der als Ergebnis einen einzelnen Wert liefert. Das Ergebnis der Funktion ist dann ein Wert, der aus allen passenden Zeilen der Abfrage berechnet wird.
Bei Abfragen kann das Ergebnis einer Spaltenfunktion auch nach den Werten einer oder mehrerer Spalten oder Berechnungen gruppiert werden. Die Aggregatfunktionen liefern dann für jede Gruppe ein Teilergebnis. Näheres siehe unter Gruppierungen.
COUNT – Anzahl
BearbeitenDie Funktion COUNT zählt alle Zeilen, die einen eindeutigen Wert enthalten, also nicht NULL sind. Sie kann auf alle Datentypen angewendet werden, da für jeden Datentyp NULL definiert ist. Beispiel:
SELECT COUNT(Farbe) AS Anzahl_Farbe FROM Fahrzeug;
Die Spalte Farbe ist als VARCHAR(30), also als Text variabler Länge, definiert und optional. Hier werden also alle Zeilen gezählt, die in dieser Spalte einen Eintrag haben. Dasselbe funktioniert auch mit einer Spalte, die numerisch ist:
SELECT COUNT(Schadenshoehe) AS Anzahl_Schadenshoehe FROM Schadensfall;
Hier ist die Spalte numerisch und optional. Die Zahl 0 ist bekanntlich nicht NULL. Wenn in der Spalte eine 0 steht, wird sie mitgezählt.
Ein Spezialfall ist der Asterisk '*
' als Parameter. Dies bezieht sich dann nicht auf eine einzelne Spalte, sondern auf eine ganze Zeile. So wird also die Anzahl der Zeilen in der Tabelle gezählt:
SELECT COUNT(*) AS Anzahl_Zeilen FROM Schadensfall;
Die Funktion COUNT liefert niemals NULL zurück, sondern immer eine Zahl; wenn alle Werte in einer Spalte NULL sind, ist das Ergebnis die Zahl 0 (es gibt 0 Zeilen mit einem Wert ungleich NULL in dieser Spalte).
SUM – Summe
BearbeitenDie Funktion SUM kann (natürlich) nur auf numerische Datentypen angewendet werden. Im Gegensatz zu COUNT liefert SUM nur dann einen Wert zurück, wenn wenigstens ein Eingabewert nicht NULL ist. Als Parameter kann nicht nur eine einzelne numerische Spalte, sondern auch eine Berechnung übergeben werden, die als Ergebnis eine einzelne Zahl liefert. Ein Beispiel für eine einzelne numerische Spalte ist:
SELECT SUM(Schadenshoehe) AS Summe_Schadenshoehe FROM Schadensfall;
Als Ergebnis werden alle Werte in der Spalte Schadenshoehe aufsummiert. Als Parameter kann aber auch eine Berechnung übergeben werden.
Hier werden Euro-Beträge aus Schadenshoehe zuerst in US-Dollar nach einem Tageskurs umgerechnet und danach aufsummiert.
SELECT SUM(Schadenshoehe * 1.5068) AS Summe_Schadenshoehe_Dollar FROM Schadensfall;
Eine Besonderheit ist das Berechnen von Vergleichen. Ein Vergleich wird als WAHR oder FALSCH ausgewertet. Sofern das DBMS (wie bei MySQL oder Access) das Ergebnis als Zahl benutzt, ist das Ergebnis eines Vergleichs daher 1 oder 0 (bei Access –1 oder 0). Um alle Fälle zu zählen, deren Schadenshöhe größer als 1000 ist, müsste der Befehl so aussehen:
SELECT SUM(Schadenshoehe > 1000) AS Anzahl_Schadenshoehe_gt_1000 FROM Schadensfall;
Dabei werden nicht etwa die Schäden aufsummiert, sondern nur das Ergebnis des Vergleichs, also 0 oder 1, im Grunde also gezählt. Die Funktion COUNT kann hier nicht genommen werden, da sie sowohl die 1 als auch die 0 zählen würde.
Einige DBMS (z. B. DB2, Oracle) haben eine strengere Typenkontrolle; Firebird nimmt eine Zwischenstellung ein. Dabei haben Vergleichsausdrücke grundsätzlich ein boolesches Ergebnis, das nicht summiert werden kann. Dann kann man sich mit der CASE-Funktion behelfen, die dem Wahrweitswert TRUE eine 1 zuordnet und dem Wert FALSE eine 0:
SELECT SUM(CASE WHEN Schadenshoehe > 1000 THEN 1
ELSE 0
END) AS Anzahl_Schadenshoehe_gt_1000
FROM Schadensfall;
MAX, MIN – Maximum, Minimum
BearbeitenDiese Funktionen können auf jeden Datentyp angewendet werden, für den ein Vergleich ein gültiges Ergebnis liefert. Dies gilt für numerische Werte, Datumswerte und Textwerte, nicht aber für z. B. BLOBs (binary large objects). Bei Textwerten ist zu bedenken, dass die Sortierreihenfolge je nach verwendetem Betriebssystem, DBMS und Zeichensatzeinstellungen der Tabelle oder Spalte unterschiedlich ist, die Funktion demnach auch unterschiedliche Ergebnisse liefern kann.
Suche den kleinsten, von NULL verschiedenen Schadensfall.
SELECT MIN(Schadenshoehe) AS Minimum_Schadenshoehe FROM Schadensfall;
Kommen nur NULL-Werte vor, wird NULL zurückgegeben. Gibt es mehrere Zeilen, die den kleinsten Wert haben, wird trotzdem nur ein Wert zurückgegeben. Welche Zeile diesen Wert liefert, ist nicht definiert.
Für MAX gilt Entsprechendes wie für MIN.
AVG – Mittelwert
BearbeitenAVG (average = Durchschnitt) kann nur auf numerische Werte angewendet werden. Das für SUM Gesagte gilt analog auch für AVG. Um die mittlere Schadenshöhe zu berechnen, schreibt man:
SELECT AVG(Schadenshoehe) AS Mittlere_Schadenshoehe FROM Schadensfall;
NULL-Werte fließen dabei nicht in die Berechnung ein, Nullen aber sehr wohl.
STDDEV – Standardabweichung
BearbeitenDie Standardabweichung STDDEV oder STDEV kann auch nur für numerische Werte berechnet werden. NULL-Werte fließen nicht mit in die Berechnung ein, Nullen schon. Wie bei SUM können auch Berechnungen als Werte genommen werden. Die Standardabweichung der Schadensfälle wird so berechnet:
SELECT STDDEV(Schadenshoehe) AS StdAbw_Schadenshoehe FROM Schadensfall;
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir die wichtigsten „eingebauten“ Funktionen kennen:
- Für Zahlen gibt es vor allem die Operatoren, dazu die modulo-Funktionen und Möglichkeiten für Rundungen.
- Für Zeichenketten gibt es vor allem das Verknüpfen und Aufteilen, dazu die Längenbestimmung und die Umwandlung in Groß- und Kleinbuchstaben.
- Für Datums- und Zeitwerte gibt es neben Systemfunktionen die Verwendung einzelner Teile.
- Für logische und NULL-Werte gibt es vor allem Vergleiche und Kombinationen durch Operatoren.
- Konvertierungen – implizite, CAST, CONVERT – dienen dazu, dass ein Wert des einen Datentyps anstelle eines anderen Typs verwendet werden kann.
Mit Spaltenfunktionen werden alle Werte einer Spalte gemeinsam ausgewertet.
Übungen
Bearbeiten
Übung 1 | Definitionen | Zur Lösung |
Welche der folgenden Feststellungen sind richtig, welche sind falsch?
- Eine Skalarfunktion bestimmt aus allen Werten eines Feldes einen gemeinsamen Wert.
- Eine Spaltenfunktion bestimmt aus allen Werten eines Feldes einen gemeinsamen Wert.
- Eine Skalarfunktion kann keine Werte aus Spalten einer Tabelle verarbeiten.
- Eine benutzerdefinierte Funktion kann als Skalarfunktion, aber nicht als Spaltenfunktion dienen.
- Wenn einer Funktion keine Argumente übergeben werden, kann auf die Klammern hinter dem Funktionsnamen verzichtet werden.
- Wenn eine Funktion für einen SQL-Ausdruck benutzt wird, muss das Ergebnis der Funktion vom Datentyp her mit dem übereinstimmen, der an der betreffenden Stelle erwartet wird.
Übung 2 | Definitionen | Zur Lösung |
Welche der folgenden Funktionen sind Spaltenfunktionen? Welche sind Skalarfunktionen, welche davon sind Konvertierungen?
- Bestimme den Rest bei einer Division ganzer Zahlen.
- Bestimme die Länge des Namens des Mitarbeiters mit der ID 13.
- Bestimme die maximale Länge aller Mitarbeiter-Namen.
- Bestimme die Gesamtlänge aller Mitarbeiter-Namen.
- Verwandle eine Zeichenkette, die nur Ziffern enthält, in eine ganze Zahl.
- Verwandle eine Zeichenkette, die keine Ziffern enthält, in einen String, der nur Großbuchstaben enthält.
- Bestimme das aktuelle Datum.
Übung 3 | Funktionen mit Zahlen | Zur Lösung |
Benutzen Sie für die folgenden Berechnungen die Spalte Schadenshoehe der Tabelle Schadensfall. Welche Datensätze benutzt werden, also der Inhalt der WHERE-Klausel, soll uns dabei nicht interessieren. Auch geht es nur um die Formeln, nicht um einen SELECT-Befehl.
- Berechnen Sie (ohne AVG-Funktion) die durchschnittliche Schadenshöhe.
- Bestimmen Sie den prozentualen Anteil eines bestimmten Schadensfalls an der gesamten Schadenshöhe.
Übung 4 | Funktionen mit Zeichenketten | Zur Lösung |
Schreiben Sie Name, Vorname und Abteilung der Mitarbeiter in tabellarischer Form (nehmen wir an, dass das Kuerzel der Abteilung in der Tabelle Mitarbeiter stünde); benutzen Sie dazu nacheinander die folgenden Teilaufgaben:
- Bringen Sie die Namen auf eine einheitliche Länge von 20 Zeichen.
- Bringen Sie die Namen auf eine einheitliche Länge von 10 Zeichen.
- Bringen Sie die Vornamen ebenso auf eine Länge von 10 Zeichen.
- Setzen Sie diese Teilergebnisse zusammen und fügen Sie dazwischen je zwei Leerzeichen ein.
Übung 5 | Funktionen mit Datum und Zeit | Zur Lösung |
Gegeben ist ein Timestamp-Wert mit dem Spaltennamen Zeitstempel und dem Inhalt "16. Dezember 2009 um 19:53 Uhr". Zeigen Sie diesen Wert als Zeichenkette im Format "12/2009; 16. Tag; 7 Minuten vor 20 Uhr" an. (Für die String-Verknüpfung benutzen wir jetzt das Plus-Zeichen. Die Leerzeichen zwischen den Bestandteilen können Sie ignorieren. Auch muss es keine allgemeingültige Lösung sein, die alle Eventualitäten beachtet.)
Übung 6 | Funktionen mit Datum und Zeit | Zur Lösung |
Gegeben ist eine Zeichenkette datum mit dem Inhalt "16122009". Sorgen Sie dafür, dass das Datum für jedes DBMS gültig ist. Zusatzfrage: Muss dafür CAST verwendet werden und warum bzw. warum nicht?
Lösung zu Übung 1 | Definitionen | Zur Übung |
Richtig sind 2 und 6, falsch sind 1, 3, 4, 5.
Lösung zu Übung 2 | Definitionen | Zur Übung |
Spaltenfunktionen sind 3 und 4; beide benutzen das Ergebnis von Skalarfunktionen. Alle anderen sind Skalarfunktionen, wobei 5 eine Konvertierung ist und 7 eine Systemfunktion, die ohne Klammern geschrieben wird.
Lösung zu Übung 3 | Funktionen mit Zahlen | Zur Übung |
- SUM(Schadenshoehe) / COUNT(Schadenshoehe)
- ROUND( Schadenshoehe * 100 / SUM(Schadenshoehe) )
Lösung zu Übung 4 | Funktionen mit Zeichenketten | Zur Übung |
1. CAST(Name AS CHAR(20)) 2. SUBSTRING( CAST(Name AS CHAR(20)) FROM 1 FOR 10 ) 3. SUBSTRING( CAST(Vorname AS CHAR(20)) FROM 1 FOR 10 ) 4. SUBSTRING( CAST(Name AS CHAR(20)) FROM 1 FOR 10 ) || ' ' || SUBSTRING( CAST(Vorname AS CHAR(20)) FROM 1 FOR 10 ) || ' ' || Kuerzel
Lösung zu Übung 5 | Funktionen mit Datum und Zeit | Zur Übung |
EXTRACT(MONTH FROM CURRENT_TIMESTAMP) + '/' + EXTRACT(YEAR FROM CURRENT_TIMESTAMP) + ';' + EXTRACT(DAY FROM CURRENT_TIMESTAMP) + '.Tag; ' + CAST((60 - EXTRACT(MINUTE FROM CURRENT_TIMESTAMP)) AS Varchar(2)) + ' Minuten vor ' + CAST( (EXTRACT(HOUR FROM CURRENT_TIMESTAMP) + 1) AS Varchar(2)) + ' Uhr'
Lösung zu Übung 6 | Funktionen mit Datum und Zeit | Zur Übung |
SUBSTRING(datum FROM 5 FOR 4) + '-' + SUBSTRING(datum FROM 3 FOR 2) + '-' + SUBSTRING(datum FROM 1 FOR 2)
Auf CAST kann (fast immer) verzichtet werden, weil mit dieser Substring-Verwendung die Standardschreibweise '2009-12-16' nach ISO 8601 erreicht wird.
Siehe auch
BearbeitenEinige Hinweise sind in den folgenden Kapiteln zu finden:
- SQL-Befehle beschreibt auch den Begriff „SQL-Ausdruck“.
- Datentypen
Weitere Erläuterungen stehen bei Wikipedia:
- Senkrechter Strich oder „Pipe“-Zeichen
- Boolesche Operatoren
- ISO 8601 zur Schreibweise von Datumsangaben
Ausführliche SELECT-Struktur |
Dieses Kapitel erläutert die Syntax des SELECT-Befehls. Anstelle von Beispielen gibt es Verweise auf diejenigen Kapitel, die die betreffenden Klauseln genauer behandeln.
In diesem Kapitel werden vorzugsweise die englischen Begriffe aus der SQL-Dokumentation benutzt.
Bitte beachten Sie: Der SELECT-Befehl bietet so umfangreiche Möglichkeiten, dass auch bei dieser Übersicht nicht alle Einzelheiten vorgestellt werden können.
Allgemeine Syntax
BearbeitenDer SELECT-Befehl wird als <query specification> oder <select expression>, also Abfrage-Ausdruck bezeichnet und setzt sich grundsätzlich aus diesen Bestandteilen zusammen:
SELECT [ DISTINCT | ALL ] <select list> FROM <table reference list> [ <where clause> ] [ <group by clause> ] [ <having clause> ] [ UNION [ALL] <query specification> ] [ <order by clause> ]
Diese Bestandteile setzen sich je nach Situation aus weiteren einfachen oder komplexen Bestandteilen zusammen und werden in den folgenden Abschnitten erläutert.
Unbedingt erforderlich sind:
- das Schlüsselwort SELECT
- eine Liste von Spalten
- das Schlüsselwort FROM mit einem oder mehreren Verweisen auf Tabellen
Alle anderen Bestandteile sind optional, können also auch weggelassen werden. Wenn sie benutzt werden, müssen sie in genau der genannten Reihenfolge verwendet werden.
Set Quantifier – Mengenquantifizierer
BearbeitenAls Mengenquantifizierer stehen DISTINCT und ALL zur Verfügung.
Außerdem ist es sehr oft möglich (wenn auch nicht im SQL-Standard vorgeschrieben), das Ergebnis auf eine bestimmte Anzahl von Zeilen zu beschränken, z. B. FIRST 3 oder LIMIT 7.
Erläuterungen dazu finden sich im Kapitel Nützliche Erweiterungen.
Select List – Auswahlliste
BearbeitenDie Liste der Spalten, die ausgewählt werden sollen, wird angegeben durch den Asterisk oder mit einer <column list>.
Asterisk *
Bearbeiten
Der Asterisk, d. h. das Sternchen *, ist eine Kurzfassung und steht für die Liste aller Spalten (Felder) einer einzelnen Tabelle.
Wenn mehrere Tabellen verknüpft werden, ist der Asterisk zusammen mit dem Namen oder dem Alias einer Tabelle anzugeben.
<Tabellenname>.* /* oder */ <Tabellen-Alias>.*
Erläuterungen dazu finden sich in allen Kapiteln, die sich mit Abfragen befassen.
Column List
BearbeitenDie <Spaltenliste> ist eine Liste von einem oder mehreren Elementen, die in der SQL-Dokumentation als <select sublist> bezeichnet werden:
<select sublist> [ , <select sublist> , <select sublist> , ... ]
Jedes einzelne Element ist eine Spalte einer Tabelle oder eine abgeleitete Spalte <derived column> – siehe den nächsten Abschnitt. Jedem Element kann ein Spalten-Alias zugewiesen, das Wort AS kann dabei auch weggelassen werden:
<element> [ [AS] <column name> ]
Bei Spalten aus Tabellen wird einfach deren Name angegeben. Wenn mehrere Tabellen verknüpft werden und ein Spaltenname nicht eindeutig ist, ist der Spaltenname zusammen mit dem Namen oder dem Alias einer Tabelle anzugeben.
<spaltenname> /* oder */ <Tabellenname>.<spaltenname> /* oder */ <Tabellen-Alias>.<spaltenname>
Hinweis: In manchen DBMS darf der Tabellenname nicht mehr benutzt werden, wenn ein Tabellen-Alias angegeben ist.
Erläuterungen und Beispiele dazu finden sich in allen Kapiteln, die sich mit Abfragen befassen.
Derived Column
BearbeitenEine abgeleitete Spalte bezeichnet den Wert, der von einem <value expression> zurückgeliefert wird. Das kann ein beliebiger Ausdruck sein, der genau einen Wert als Ergebnis hat: vor allem eine Funktion oder eine passende Unterabfrage.
Erläuterungen dazu finden sich vor allem in den Kapiteln Berechnete Spalten und Unterabfragen.
Table Reference List – Tabellen-Verweise
BearbeitenAls Bestandteil der FROM-Klausel werden die beteiligten Tabellen und Verweise darauf aufgeführt:
FROM <reference list> /* nämlich */ FROM <reference1> [ , <reference2> , <reference3> ... ]
In der <Tabellenliste> stehen eine oder (mit Komma getrennt) mehrere Verweise (Referenzen); diese können direkt aus der Datenbank übernommen oder auf verschiedene Arten abgeleitet werden.
Jede dieser Varianten kann erweitert werden:
<reference> [ [ AS ] <Alias-Name> [ ( <derived column list> ) ] ]
Mit AS kann ein Alias-Name dem Verweis hinzugefügt werden; das Schlüsselwort AS kann auch entfallen. Diesem Tabellen-Alias kann (je nach DBMS) in Klammern eine Liste von Spaltennamen hinzugefügt werden, die anstelle der eigentlichen Spaltennamen angezeigt werden sollen.
Die folgenden Varianten sind als Verweise möglich.
Tabellen, Views
BearbeitenPrimär verwendet man die Tabellen sowie die Views (fest definierte Sichten).
Erläuterungen dazu finden sich in allen Kapiteln, die sich mit Abfragen befassen, sowie in Erstellen von Views.
Derived Table – Abgeleitete Tabellen
BearbeitenVor allem können Unterabfragen wie eine Tabelle benutzt werden.
Hinweis: Es gibt eine Reihe weiterer Varianten, um andere Tabellen „vorübergehend“ abzuleiten. Wegen der DBMS-Unterschiede würde die Übersicht zu kompliziert; wir verzichten deshalb auf eine vollständige Darstellung.
Erläuterungen dazu finden sich im Kapitel Unterabfragen.
Joined Table – Verknüpfte Tabelle
BearbeitenWie eine Tabelle kann auch eine Verknüpfung verwendet werden. Dabei handelt es sich um zwei Tabellen, die nach bestimmten Bedingungen verbunden werden.
Für die Verknüpfung zweier (oder mehrerer) Tabellen gibt es zwei Verfahren: Beim (traditionellen) direkten Weg werden die beiden Tabellen einfach nacheinander aufgeführt. Beim (modernen) Weg wird zu einer Tabelle eine weitere mit JOIN genannt, die durch eine Verknüpfungsbedingung über ON verbunden wird.
Erläuterungen dazu finden sich ab dem Kapitel Mehrere Tabellen.
Where Clause – WHERE-Klausel
BearbeitenMit der WHERE-Klausel wird eine Suchbedingung festgelegt, welche Zeilen der Ergebnistabelle zur Auswahl gehören sollen. Dieser Filter wird zuerst erstellt; erst anschließend werden Bedingungen wie GROUP BY und ORDER BY ausgewertet.
WHERE <search condition>
Die Suchbedingung <search condition> ist eine Konstruktion mit einem eindeutigen Prüfergebnis: Entweder die Zeile gehört zur Auswahl, oder sie gehört nicht zur Auswahl. Es handelt sich also um eine logische Verknüpfung von einer oder mehreren booleschen Variablen.
Erläuterungen zu den folgenden Einzelheiten finden sich vor allem in den Kapiteln WHERE-Klausel im Detail und Unterabfragen.
Eine einzelne Suchbedingung
BearbeitenEine Suchbedingung hat eine der folgenden Formen, deren Ergebnis immer WAHR oder FALSCH (TRUE bzw. FALSE) lautet:
<wert1> [ NOT ] <operator> <wert2> <wert1> [ NOT ] BETWEEN <wert2> AND <wert3> <wert1> [ NOT ] LIKE <wert2> [ ESCAPE <wert3> ] <wert1> [ NOT ] CONTAINING <wert2> <wert1> [ NOT ] STARTING [ WITH ] <wert2> <wert1> IS [ NOT ] NULL <wert1> [ NOT ] IN <werteliste> EXISTS <select expression> NOT <search condition>
Anstelle eines Wertes kann auch ein Wertausdruck <value expression> stehen, also eine Unterabfrage, die genau einen Wert als Ergebnis liefert.
Anstelle einer Werteliste kann auch ein Auswahl-Ausdruck <select expression> stehen, also eine Unterabfrage, die eine Liste mehrerer Werte als Ergebnis liefert.
Als <operator> sind folgende Vergleiche möglich, und zwar sowohl für Zahlen als auch für Zeichenketten und Datumsangaben:
= < > <= >= <>
Mehrere Suchbedingungen
BearbeitenMehrere Suchbedingungen können miteinander verbunden werden:
NOT <search condition> <search condition1> AND <search condition2> <search condition1> OR <search condition2>
Bitte beachten Sie: NOT hat die stärkste Verbindung und wird zuerst ausgewertet. Danach hat AND eine stärkere Verbindung und wird als nächstes untersucht. Erst zum Schluss kommen die OR-Verbindungen. Um Unklarheiten zu vermeiden, wird dringend empfohlen, zusammengesetzte Suchbedingungen in Klammern zu setzen, um Prioritäten deutlich zu machen.
Group By Clause – GROUP BY-Klausel
BearbeitenMit der GROUP BY-Klausel werden alle Zeilen, die in einer oder mehreren Spalten den gleichen Wert enthalten, in jeweils einer Gruppe zusammengefasst. Dies macht in der Regel nur dann Sinn, wenn in der Spaltenliste des SELECT-Befehls eine gruppenweise Auswertung, also eine der Spaltenfunktionen enthalten ist.
Die allgemeine Syntax lautet:
GROUP BY <Spaltenliste>
Die Spaltenliste enthält, durch Komma getrennt, die Namen von einer oder mehreren Spalten. Bei jeder Spalte kann eine eigene Sortierung angegeben werden (wie bei den Datentypen erläutert):
<Spaltenname> -- oder <Spaltenname> COLLATE <Collation-Name>
Erläuterungen dazu finden sich vor allem im Kapitel Gruppierungen.
Having Clause – HAVING-Klausel
BearbeitenDie HAVING-Klausel dient dazu, nicht alle ausgewählten Zeilen in die Ausgabe zu übernehmen, sondern nur diejenigen, die den zusätzlichen Bedingungen entsprechen. Sie wird in der Praxis überwiegend als Ergänzung zur GROUP BY-Klausel verwendet und folgt ggf. direkt danach.
HAVING <bedingungsliste>
Erläuterungen dazu finden sich ebenfalls im Kapitel Gruppierungen.
Union Clause – UNION-Klausel
BearbeitenMit der UNION-Klausel werden mehrere eigentlich getrennte Abfragen zusammengefasst, um ein einheitliches Ergebnis zu liefern. Dabei sind die einzelnen Bedingungen zu komplex, um sie zusammenzufassen; oder sie können nicht sinnvoll verbunden werden. Es setzt eine weitgehend identische Spaltenliste voraus.
SELECT <spaltenliste1> FROM <tabellenliste1> WHERE <bedingungsliste1> UNION SELECT <spaltenliste2> FROM <tabellenliste2> WHERE <bedingungsliste2>
Erläuterungen dazu finden sich im Kapitel Nützliche Erweiterungen.
Order By Clause – ORDER BY-Klausel
BearbeitenMit der ORDER BY-Klausel werden die Datensätze der Ergebnismenge in eine bestimmte Reihenfolge gebracht.
Erläuterungen dazu finden sich in allen Kapiteln, die sich mit Abfragen befassen.
Zusammenfassung
BearbeitenIn diesem Kapitel erhielten Sie einen umfangreichen Überblick über die Syntax des SELECT-Befehls:
- Die Listen der gewünschten Spalten und der beteiligten Tabellen sind Pflichtangaben, alle anderen Klauseln sind optional.
- Für die Verknüpfung mehrerer Tabellen gibt es einen (traditionellen) direkten Weg und den (modernen) Weg über JOIN.
- Die WHERE-Klausel ermöglicht komplexe Bedingungen darüber, welche Datensätze abgefragt werden sollen.
- Mit Gruppierung, Sortierung und Zusammenfassung gibt es weitere Möglichkeiten für Abfragen.
Übungen
Bearbeiten
Übung 1 | Allgemeine Syntax | Zur Lösung |
Bringen Sie die folgenden Bestandteile des SELECT-Befehls in die richtige Reihenfolge (es gibt Begriffe, die an zwei bzw. drei Stellen gehören):
<bedingung> – DISTINCT – FROM – GROUP BY – HAVING – ORDER BY – SELECT – <spalten> – <tabelle> – WHERE
Übung 2 | Allgemeine Syntax | Zur Lösung |
Welche der genannten Bestandteile eines SELECT-Befehls sind unbedingt erforderlich?
Übung 3 | Spaltenliste | Zur Lösung |
Welche der folgenden Spaltenlisten aus der Beispieldatenbank sind richtig, welche nicht?
- SELECT * FROM Mitarbeiter;
- SELECT ID, Name FROM Mitarbeiter;
- SELECT ID, Name FROM Mitarbeiter, Abteilung;
- SELECT ID, Name, Kuerzel FROM Mitarbeiter, Abteilung;
- SELECT ab.ID, Name FROM Mitarbeiter, Abteilung ab;
- SELECT ab.ID, Name, Krz Kuerzel FROM Mitarbeiter, Abteilung ab;
- SELECT ab.ID, Name, Kuerzel Krz FROM Mitarbeiter, Abteilung ab;
- SELECT ab.ID, mi.Name, ab.Kuerzel FROM Mitarbeiter mi, Abteilung ab;
Übung 4 | Spaltenliste | Zur Lösung |
Schreiben Sie für folgende Abfragen die Spalten und Tabellen auf.
- Zeige alle Informationen zu den Mitarbeitern.
- Zeige zu jedem Mitarbeiter Name, Vorname und Nummer der Abteilung.
- Zeige zu jedem Mitarbeiter Name, Vorname und Kuerzel der Abteilung.
- Zeige zu jedem Mitarbeiter ID, Name, Vorname sowie das Kennzeichen des Dienstwagens.
Übung 5 | Suchbedingungen | Zur Lösung |
Welche der folgenden Suchbedingungen sind richtig, welche nicht? Welche korrekten Bedingungen liefern immer FALSE als Ergebnis?
- WHERE Name NOT = 'Meyer';
- WHERE 1 = 2;
- WHERE NOT Name LIKE 'M%';
- WHERE Geburtsdatum LIKE '1980';
- WHERE ID BETWEEN 20 AND 10;
- WHERE Mobil IS NULL;
- WHERE Name IS NULL;
- WHERE Name STARTING WITH 'L' AND CONTAINING 'a';
- WHERE ID IN (1, 3, 'A');
- WHERE ID IN (1, 3, '5');
Übung 6 | Suchbedingungen | Zur Lösung |
Formulieren Sie die folgenden Aussagen als Bedingungen der WHERE-Klausel zur Tabelle Mitarbeiter.
- Der Vorname lautet 'Petra'.
- Der Name enthält die Zeichen 'mann'.
- Der Name beginnt mit 'A', es handelt sich um Abteilung 8.
- Es ist keine Mobil-Nummer gespeichert.
Lösung zu Übung 1 | Allgemeine Syntax | Zur Übung |
Richtig ist dies (mit Vervollständigung zu einem Befehl):
SELECT DISTINCT <spalten> FROM <tabelle> WHERE <bedingung> GROUP BY <spalten> HAVING <bedingung> ORDER BY <spalten>;
Lösung zu Übung 2 | Allgemeine Syntax | Zur Übung |
SELECT <spalten> FROM <tabelle>
Lösung zu Übung 3 | Spaltenliste | Zur Übung |
Richtig sind 1, 2, 5, 7, 8.
Falsch sind 3, 4 (ID ist mehrdeutig), 6 (Spaltenname und -Alias in falscher Reihenfolge).
Lösung zu Übung 4 | Spaltenliste | Zur Übung |
- SELECT * FROM Mitarbeiter
- SELECT Name, Vorname, Abteilung_ID FROM Mitarbeiter
- SELECT Name, Vorname, Kuerzel FROM Mitarbeiter, Abteilung
/* oder mit Tabellen-Alias: */
SELECT mi.Name, mi.Vorname, ab.Kuerzel FROM Mitarbeiter mi, Abteilung ab SELECT Name, Vorname, Dienstwagen.ID, Kennzeichen FROM Mitarbeiter, Dienstwagen
/* auch einheitlich mit Tabellen-Namen oder Tabellen-Alias möglich */
Lösung zu Übung 5 | Suchbedingungen | Zur Übung |
- Richtig.
- Richtig, immer FALSE: 1 ist immer ungleich 2.
- Richtig, weil die Teilbedingung "Name LIKE 'M%'" verneint wird. Ebenfalls richtig wäre es, das NOT hinter <wert1>, also hinter Name zu schreiben.
- Richtig, weil das Jahr laut ISO 8601 am Anfang steht.
- Richtig, immer FALSE: es gibt keine Zahl „größer/gleich 20“ und gleichzeitig „kleiner/gleich 10“.
- Richtig.
- Richtig, immer FALSE, weil der Name als Pflichtangabe niemals NULL sein kann.
- Falsch, weil Name in der zweiten Bedingung hinter AND fehlt.
- Falsch, weil 'A' keine Zahl ist, aber zu ID bei IN eine Liste von Zahlen gehört.
- Richtig, weil '5' automatisch als Zahl konvertiert wird.
Lösung zu Übung 6 | Suchbedingungen | Zur Übung |
- WHERE Vorname = 'Petra';
- WHERE Name CONTAINING 'mann';
- WHERE Name STARTING WITH 'A' AND Abteilung_ID = 8;
WHERE Mobil = '' OR Mobil IS NULL;
Funktionen (2) |
Dieses Kapitel behandelt weitere Skalarfunktionen in Ergänzung zu den grundlegenden Funktionen.
Allgemeine Hinweise
BearbeitenAuch hier gelten die dort aufgeführten Hinweise:
- Jedes DBMS bietet eigene Funktionen sowie Varianten.
- Die Klammern werden in den Beschreibungen der Funktionen oft nicht angegeben.
- Die Beispiele werden durch eine verkürzte Schreibweise dargestellt, wobei der Zusatz "from fiktiv" als optional gekennzeichnet ist und für Firebird/Interbase durch "from rdb$database" bzw. für Oracle durch "from dual" zu ersetzen ist.
SELECT 2 * 3 [from fiktiv];
Funktionen für Zahlen
BearbeitenAuch bei diesen weiteren Funktionen müssen Sie auf den Typ achten.
POWER und SQRT – Potenzen und Wurzeln
BearbeitenMit POWER wird eine beliebige Potenz oder Wurzel berechnet:
POWER( <basis>, <exponent> )
Sowohl für <basis> als auch für <exponent> sind nicht nur ganze positive Zahlen, sondern alle Zahlen zulässig. Mit Dezimalzahlen als <exponent> werden (genau nach mathematischen Regeln) beliebige Wurzeln berechnet; in diesem Fall sind als <basis> negative Zahlen unzulässig. Beispiele:
SELECT POWER( 5, 3 ) [from fiktiv]; /* Ergebnis: 125,000 */ SELECT POWER( 5, 2.5) [from fiktiv]; /* Ergebnis: 55,902 */ SELECT POWER( 5, 0.5) [from fiktiv]; /* Ergebnis: 2,236 also Wurzel aus 5 */ SELECT POWER( 0.5, -3 ) [from fiktiv]; /* Ergebnis: 8,000 also 3.Potenz zu 2 */ SELECT POWER( 12.35, 1.5) [from fiktiv]; /* Ergebnis: 43,401 */ SELECT POWER(-12.35, 1.5) [from fiktiv]; /* expression evaluation not supported. */ SELECT POWER( 12.35,-1.5) [from fiktiv]; /* Ergebnis: 0,023 */ SELECT POWER(-12.35,-1.5) [from fiktiv]; /* expression evaluation not supported */
Mit SQRT (= Square Root) gibt es für die Quadratwurzel eine kürzere Schreibweise anstelle von POWER(x,0.5):
SELECT SQRT(12.25) [from fiktiv]; /* Ergebnis: 3,500 */
EXP und LOG – Exponentialfunktion und Logarithmen
BearbeitenMit EXP wird die Exponentialfunktion im engeren Sinne bezeichnet, also mit der Eulerschen Zahl e als Basis.
SELECT EXP(1) [from fiktiv]; /* Ergebnis: 2,71828182845905 */
Mit LOG(<wert>, <basis>) wird umgekehrt ein Logarithmus bestimmt, mit LN der natürliche und mit LOG10 der dekadische Logarithmus.
SELECT LOG(10, EXP(1)) [from fiktiv]; /* Ergebnis: 2,30258509299405 */ SELECT LOG(10, 10) [from fiktiv]; /* Ergebnis: 1,000 */ SELECT LN(10) [from fiktiv]; /* Ergebnis: 2,30258509299405 */
Winkelfunktionen
BearbeitenDie trigonometrischen Funktionen arbeiten mit dem Bogenmaß (nicht mit einer Grad-Angabe).
SIN Sinus COS Cosinus TAN Tangens COT Cotangens ASIN Arcussinus als Umkehrfunktion des Sinus ACOS Arcuscosinus als Umkehrfunktion des Cosinus ATAN Arcustangens als Umkehrfunktion des Tangens
Mit DEGREES wird ein Bogenmaß in Grad umgerechnet, mit RADIANS ein Gradmaß in das Bogenmaß.
PI liefert die entsprechende Zahl und kann auch für die trigonometrischen Funktionen verwendet werden:
SELECT SIN( PI()/6 ) [from fiktiv]; /* sind 30°, also Ergebnis 0,5 */
ABS, RAND, SIGN – verschiedene Funktionen
BearbeitenMit ABS wird der absolute Betrag der gegebenen Zahl zurückgegeben.
SIGN liefert als Hinweis auf das Vorzeichen der gegebenen Zahl einen der Werte 1, 0, -1 – je nachdem, ob die gegebene Zahl positiv, 0 oder negativ ist.
SELECT SIGN(12.34), SIGN(0), SIGN(-5.67) [from fiktiv]; /* Ergebnis: 1 0 -1 */
RAND liefert eine Zufallszahl im Bereich zwischen 0 und 1 (jeweils einschließlich). Bitte beachten Sie, dass dies keine echten Zufallszahlen sind, sondern Pseudozufallszahlen.
Mit RAND(<vorgabewert>) wird innerhalb einer Sitzung immer dieselbe Zufallszahl erzeugt.
Mit einer Kombination von RAND und FLOOR erhält man eine „zufällige“ Folge ganzer Zahlen:
FLOOR( <startwert> + ( RAND() * ( <zielwert> - <startwert> + 1 )) )
Beispielsweise liefert die mehrfache Wiederholung der folgenden Abfrage diese Zahlen zwischen 7 und 12:
SELECT FLOOR(7 + (RAND() * 6)) [from fiktiv]; /* Ergebnisse: 10 9 9 7 8 7 9 9 10 12 usw. */
Diese Funktion ist geeignet, um Datensätze mit SELECT in beliebiger Reihenfolge oder einen zufällig ausgewählten Datensatz abzurufen:
SELECT * FROM <tabelle> ORDER BY RAND(); SELECT FIRST 1 * FROM <tabelle> ORDER BY RAND();
Funktionen für Zeichenketten
BearbeitenAuch zur Bearbeitung und Prüfung von Zeichenketten (Strings) gibt es weitere Funktionen.
Verknüpfen von Strings
BearbeitenZu den Standardverfahren || + CONCAT
gibt es Ergänzungen.
MySQL bietet mit CONCAT_WS eine nützliche Erweiterung, bei der zwischen den Teiltexten ein Trennzeichen gesetzt wird.
SPACE(n) – für MS-SQL und MySQL – erzeugt einen String, der aus n Leerzeichen besteht.
REPEAT( <text>, <n> ) – für MySQL – und REPLICATE( <text>, <n> ) – für MS-SQL – erzeugen eine neue Zeichenkette, in der der <text> n-mal wiederholt wird.
Mit LPAD wird <text1>, sofern erforderlich, auf die gewünschte <länge> gebracht und dabei von links mit <text2> bzw. Leerzeichen aufgefüllt. Mit RPAD wird von rechts aufgefüllt. MS-SQL kennt diese Funktionen nur für Access.
LPAD ( <text1>, <länge> [ , <text2> ] ) RPAD ( <text1>, <länge> [ , <text2> ] )
Wenn der dadurch erzeugte Text zu lang wird, wird zuerst <text2> und notfalls auch <text1> abgeschnitten. Beispiele:
SELECT LPAD( CAST(12345 AS CHAR(8)), 10, '0') [from fiktiv]; -- Ergebnis: '0012345 '
Nanu, das sind doch nur 7 statt 10 Ziffern? Achso, zuerst wird mit CAST ein 8 Zeichen langer String erzeugt; dann ist nur noch Platz für 2 Nullen. Also muss es mit einer dieser Varianten gehen:
SELECT LPAD( CAST(12345 AS CHAR(5)), 10, '0') [from fiktiv]; -- Ergebnis: '0000012345' SELECT LPAD( 12345, 10, '0' ) [from fiktiv]; -- Ergebnis: '0000012345' SELECT LPAD( 'Hilfe', 10, '-_/') [from fiktiv]; -- Ergebnis: '-_/-_Hilfe' SELECT LPAD( 'Ich brauche Hilfe', 10, '-_/') [from fiktiv]; -- Ergebnis: 'Ich brauch' SELECT RPAD( 'Hilfe', 10, '-_/') [from fiktiv]; -- Ergebnis: 'Hilfe-_/-_' SELECT RPAD( 'Ich brauche Hilfe', 10, '-_/') [from fiktiv]; -- Ergebnis: 'Ich brauch'
LEFT, RIGHT – Teile von Zeichenketten
BearbeitenAls Ergänzung zu SUBSTRING wird mit LEFT( <text>, <anzahl> ) der linke Teil, also der Anfang eines Textes mit der gewünschten Länge <anzahl> ausgegeben. Ebenso erhält man mit RIGHT( <text>, <anzahl> ) den rechten Teil, also das Ende eines Textes.
SELECT LEFT ('Abc Def Ghi', 5) [from fiktiv]; /* Ergebnis: 'Abc D' */ SELECT RIGHT('Abc Def Ghi', 5) [from fiktiv]; /* Ergebnis: 'f Ghi' */
TRIM, LTRIM, RTRIM – Leerzeichen u. a. entfernen
BearbeitenMit der TRIM-Funktion werden bestimmte Zeichen – meistens Leerzeichen – am Anfang und/oder am Ende eines Textes entfernt:
TRIM( [ [ LEADING | TRAILING | BOTH ] [ <zeichen> ] FROM ] <text> )
Die Parameter werden wie folgt benutzt:
- Es soll <zeichen> entfernt werden. Es kann sich um ein einzelnes Zeichen, aber auch um einen Text handeln.
- Wenn dieser Parameter fehlt, wird nach führenden bzw. abschließenden Leerzeichen gesucht.
- Wenn LEADING angegeben ist, werden nur führende Zeichen geprüft, bei TRAILING nachfolgende und bei BOTH sowohl als auch. Wenn nichts davon angegeben wird, wird BOTH angenommen.
Beispiele:
SELECT TRIM( ' Dies ist ein Text. ' ) [from fiktiv]; /* Ergebnis: 'Dies ist ein Text.' */ SELECT TRIM( LEADING 'a' FROM 'abcde' ) [from fiktiv]; /* Ergebnis: 'bcde' */ SELECT TRIM( TRAILING 'e' FROM 'abcde' ) [from fiktiv]; /* Ergebnis: 'abcd' */ SELECT TRIM( 'Test' FROM 'Test als Test') [from fiktiv]; /* Ergebnis: ' als ' */
LTRIM (= Left-Trim) und RTRIM (= Right-Trim) sind Kurzfassungen, bei denen Leerzeichen am Anfang bzw. am Ende entfernt werden; MS-SQL kennt nur diese beiden Kurzfassungen.
Suchen und Ersetzen
BearbeitenMit POSITION wird der Anfang eines Textes innerhalb eines anderen gesucht.
POSITION( <text1> IN <text2> ) SQL-Standard POSITION( <text1>, <text2> [, <start>] ) nicht bei MySQL LOCATE ( <text1>, <text2> [, <start>] ) bei MySQL
Bei MS-SQL gibt es diese Funktionen nicht; stattdessen kann (mit abweichender Bedeutung) PATINDEX verwendet werden. Oracle bietet zusätzlich INSTR (= "in string") an.
Die Bedeutung der Parameter dürfte offensichtlich sein:
- <text2> ist der Text, in dem gesucht werden soll.
- <text1> ist ein Teiltext, der in <text2> gesucht wird.
- Sofern <start> angegeben ist, wird erst ab dieser Position innerhalb <text2> gesucht. Wenn <start> fehlt, wird ab Position 1 gesucht.
- Die Funktion gibt die Startposition von <text1> innerhalb von <text2> an. Wenn <text1> nicht gefunden wird, lautet das Ergebnis 0.
Beispiele:
SELECT POSITION( 'ch', 'Ich suche Text' ) [from fiktiv]; /* Ergebnis: 2 */ SELECT POSITION( 'ch', 'Ich suche Text', 3 ) [from fiktiv]; /* Ergebnis: 7 */ SELECT POSITION('sch', 'Ich suche Text' ) [from fiktiv]; /* Ergebnis: 0 */
REPLACE dient zum Ersetzen eines Teiltextes durch einen anderen innerhalb eines Gesamttextes:
REPLACE( <quelltext>, <suche>, <ersetze> )
Die verschiedenen SQL-Dialekte verhalten sich unterschiedlich, ob NULL-Werte oder leere Zeichenketten zulässig sind.
SELECT REPLACE('Ich suche Text', 'ch', 'sch') [from fiktiv]; /* Ergebnis: 'Isch susche Text' */ SELECT REPLACE('Die liebe Seele', 'e', ’’) [from fiktiv]; /* Ergebnis: 'Di lib Sl' */
REVERSE passt zwar nicht zu dem, was man in diesem Zusammenhang erwartet; aber auch diese Funktion ändert einen vorhandenen String, und zwar dadurch, dass die Reihenfolge aller Zeichen umgekehrt wird:
SELECT REVERSE( 'Hilfe' ) [from fiktiv]; /* Ergebnis: 'efliH' */
Funktionen für Datums- und Zeitwerte
BearbeitenBitte beachten Sie wiederum die Besonderheiten der Datentypen je nach DBMS.
Differenzen bei Datum oder Uhrzeit
BearbeitenDafür gibt es vorzugsweise die DATEDIFF-Funktion in unterschiedlicher Version:
DATEDIFF ( <part>, <start>, <end> ) /* bei MS-SQL oder Firebird */ DATEDIFF ( <start>, <end> ) /* bei MySQL nur die Anzahl der Tage */
Das Ergebnis ist vom gleichen Typ wie die gesuchte Differenz (also meistens ein ganzzahliger Wert). Als <part> gibt es die gleichen Varianten wie bei den wichtigsten Funktionen:
YEAR | MONTH | DAY | HOUR | MINUTE | SECOND | MILLISECOND
Beim Vergleich von Start- und Enddatum gilt:
- Das Ergebnis ist positiv, wenn der zweite Wert größer ist als der erste.
- Das Ergebnis ist 0, wenn beide Werte gleich sind.
- Das Ergebnis ist negativ, wenn der zweite Wert kleiner ist als der erste.
Bitte beachten Sie: Die DBMS verhalten sich unterschiedlich, ob die Datumsangaben verglichen werden oder der jeweilige Bestandteil. Beispielsweise kann das Ergebnis für beide der folgenden Prüfungen 1 lauten, obwohl die „echte“ Differenz im einen Fall ein Tag, im anderen fast zwei Jahre sind:
DATEDIFF( YEAR, '31.12.2008', '01.01.2009' ) DATEDIFF( YEAR, '01.01.2008', '31.12.2009' )
Bestimme die Anzahl der Tage seit dem letzten gemeldeten Schadensfall.
SELECT DATEDIFF(DAY, MAX(Datum), CURRENT_DATE) FROM Schadensfall; /* Ergebnis: 49 */
Bestimme die Anzahl der Minuten seit Tagesbeginn.
SELECT DATEDIFF(MINUTE, CAST('00:00' AS TIME), CURRENT_TIME) [from fiktiv]; /* Ergebnis: 967 */
Datumsangaben können grundsätzlich auch per Subtraktion verglichen werden, weil „intern“ häufig ein Tag gleich 1 ist. Darauf kann man sich aber nicht immer verlassen; und es ist schwierig, die Bruchteile eines Tages zu berücksichtigen. Beispiel:
SELECT (CURRENT_DATE - MAX(Datum)) FROM Schadensfall; /* Ergebnis: 49 */
Werte für Datum oder Uhrzeit ändern
BearbeitenSehr häufig muss aus einem vorhandenen Datum oder Uhrzeit ein neuer Wert berechnet werden. Der SQL-Standard sieht dazu die direkte Addition und Subtraktion vor:
<datetime> + <value> <datetime> - <value> <value> + <datetime>
<datetime> steht für den gegebenen Wert, <value> für den Zeitraum, der addiert oder subtrahiert werden soll.
Aus dem aktuellen Zeitwert '19.09.2009 16:10' wird ein neuer Wert bestimmt, der einen halben Tag in der Zukunft liegt:
SELECT CURRENT_TIMESTAMP + 0.5 [from fiktiv]; /* Ergebnis: '20.09.2009 04:10:39' */
MySQL akzeptiert nur ganze Zahlen; deshalb ist explizit die Art des Intervalls anzugeben (siehe Dokumentation).
Da das Umrechnen von Zahlen in Datums- und Zeitwerte und umgekehrt für den Anwender umständlich ist, werden viele zusätzliche Funktionen bereitgestellt. Sehr verbreitet ist DATEADD:
DATEADD( <part>, <value>, <datetime> ) /* Firebird, MS-SQL */ DATE_ADD( <datetime> , INTERVAL <value> <part> ) /* MySQL */
Welche Versicherungsverträge laufen schon mehr als 10 Jahre?
SELECT ID, Vertragsnummer, Abschlussdatum FROM Versicherungsvertrag WHERE DATEADD(YEAR, 10, Abschlussdatum) <= CURRENT_DATE; /* Ergebnis: 18 Datensätze */
Als Ergänzung oder Alternative gibt es weitere Funktionen, beispielsweise DATE_SUB als Subtraktion, ADDDATE oder ADD_MONTHS.
Funktionen für logische und NULL-Werte
BearbeitenNeben den Standardprüfungen vor allem bei der WHERE-Klausel (siehe nächstes Kapitel) und den Operatoren AND, OR, NOT gibt es weitere Prüfungen.
COALESCE – Suche Wert ungleich NULL
BearbeitenDie COALESCE-Funktion sucht in einer Liste von Werten (bzw. Ausdrücken) den ersten, der nicht NULL ist. Wenn alle Werte NULL sind, ist der Rückgabewert (zwangsläufig) NULL.
Nenne zu jedem Mitarbeiter eine Kontaktmöglichkeit: vorzugsweise Mobilnummer, dann Telefonnummer, dann Email-Adresse.
SELECT Name, Vorname, COALESCE(Mobil, Telefon, Email) AS Kontakt FROM Mitarbeiter;
Das Ergebnis überrascht zunächst, denn einige Mitarbeiter hätten danach keine Kontaktmöglichkeit. Bei der Abfrage nach IS NULL zur WHERE-Klausel wird aber erläutert, dass eine leere Zeichenkette ungleich NULL ist; bei diesen Mitarbeitern wird also ein leerer Eintrag, aber nicht "nichts" angezeigt.
- Bitte nehmen Sie diesen Hinweis als Empfehlung, lieber NULL zu speichern als den leeren String ’’.
NULLIF
BearbeitenDie Funktion NULLIF vergleicht zwei Werte und liefert NULL zurück, wenn beide Werte gleich sind; andernfalls liefert der erste Wert das Ergebnis.
Suche alle Versicherungsnehmer, die im Alter von 18 Jahren ihren Führerschein gemacht haben.
Hinweis: Sowohl COALESCE als auch NULLIF sind Kurzfassungen für spezielle Fallunterscheidungen mit CASE WHEN, in denen zusätzlich IS NULL eingebunden wird – siehe dazu Nützliche Erweiterungen.
Verschiedene Funktionen
BearbeitenAuch wenn die Funktionen in diesem Abschnitt beim SQL-Standard vorgesehen sind, sind sie nicht immer vorhanden. Wir verzichten deshalb wiederum auf nähere Erläuterungen und verweisen auf die jeweilige Dokumentation.
ROW_NUMBER – Zeilen nummerieren
BearbeitenMit der ROW_NUMBER-Funktion werden die Zeilen im Ergebnis einer Abfrage nach der betreffenden Sortierung durchnummeriert. MS-SQL verwendet die folgende Syntax:
ROW_NUMBER() OVER ( [ <partition_by_clause> ] <order_by_clause> )
CURRENT_USER – der aktuelle Benutzer
BearbeitenMit CURRENT_USER (in der Regel ohne Klammer) wird der aktuelle Benutzername abgefragt. Dieser kann auch per DEFAULT bei Neuaufnahmen automatisch in einer Spalte einer Tabelle eingetragen werden.
SELECT CURRENT_USER [from fiktiv]; /* Ergebnis: SYSDBA */
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie weitere eingebaute Funktionen kennen:
- Für Zahlen gibt es viele mathematische Funktionen wie Potenzen, Wurzeln, Exponential- oder Winkelfunktionen.
- Zeichenketten können auf vielfache Weise verknüpft oder zum Erstellen neuer Strings bearbeitet werden.
- Datums- und Zeitwerte können im Detail verglichen und verrechnet werden.
Übungen
Bearbeiten
Übung 1 | Funktionen für Zahlen | Zur Lösung |
Geben Sie mit SQL-Funktionen die Formeln für die folgenden Aufgaben an. Es geht nur um die Formeln, nicht um einen passenden SELECT-Befehl. Bei den Aufgaben 1 bis 3 sind jeweils zwei Lösungen möglich.
- Gegeben seien zwei Zahlen a, b. Berechnen Sie a² + 2ab + b².
- Berechnen Sie die Quadratwurzel von 216,09.
- Ein Auftragsverwaltungsprogramm speichert in der Spalte Bestellung die Bestellwerte und in der Spalte Zahlung die Einzahlungen. Bestimmen Sie mit einem einzigen Ausdruck die Prüfung, ob der Kunde Guthaben oder Schulden (Zahlungsverpflichtung) hat.
- Bestimmen Sie den Betrag von Aufgabe 3 (unabhängig davon, ob es sich um Guthaben oder Schulden handelt).
Übung 2 | Zufallszahlen | Zur Lösung |
Bestimmen Sie mit SQL-Funktionen die folgenden Zufallszahlen.
- eine beliebige Zufallszahl zwischen 0 und 1
- eine beliebige Zufallszahl zwischen 0 und 4
- eine beliebige Zufallszahl zwischen 1 und 5
- eine Zufallszahl als ganze Zahl zwischen 1 und 5
- eine Zufallszahl als ganze Zahl zwischen 1 und 26
- einen zufällig ausgewählten Buchstaben aus der Liste letters der Großbuchstaben 'ABC...XYZ'
Übung 3 | Zeichenketten bearbeiten | Zur Lösung |
Aus der Tabelle Mitarbeiter sollen die Werte ID und Abteilung_ID zusammengesetzt werden. Dabei soll die ID immer 4-stellig und die Abteilung_ID immer 2-stellig geschrieben werden, bei Bedarf sollen die Teile mit '0' aufgefüllt werden.
Übung 4 | Zeichenketten bearbeiten | Zur Lösung |
Geben Sie für die Tabelle Mitarbeiter eine der vorhandenen Telefonnummern an – vorrangig die Mobilnummer; berücksichtigen Sie dabei auch, ob überhaupt eine Nummer gespeichert ist.
Übung 5 | Zeichenketten bearbeiten | Zur Lösung |
Zeigen Sie für die Spalte Kennzeichen der Tabelle Fahrzeug den zugehörigen Kreis an.
Übung 6 | Zeichenketten bearbeiten | Zur Lösung |
In der Beschreibung für den Schadensfall mit der ID 6 ist das Kennzeichen 'RE-LM 903' durch 'RE-LM 902' zu berichtigen.
Übung 7 | Datum und Zeit bearbeiten | Zur Lösung |
Die folgenden Teilaufgaben werden benutzt, um im Kapitel Testdaten erzeugen Geburtsdatum, Führerschein-Erwerb oder Abschluss des Versicherungsvertrags zufällig zu bestimmen.
- Bestimmen Sie aus dem Geburtsdatum das Datum des 18. Geburtstags.
- Bestimmen Sie (ausgehend vom 01.01.1950) Datumsangaben bis zum 31.12.1990, bei denen der Monat und das Jahr per Zufallsfunktion bestimmt werden.
- Bestimmen Sie ebenso Datumsangaben, bei denen auch der Tag zufällig festgelegt wird und immer ein gültiges Datum erzeugt wird.
- Prüfen Sie, ob ein neuer Kunde seinen Führerschein bei Vertragsabschluss bereits drei Jahre besitzt. Sie können annehmen, dass sowohl Fuehrerschein als auch Abschlussdatum in derselben Tabelle liegen; Schaltjahre können ignoriert werden.
Lösung zu Übung 1 | Funktionen für Zahlen | Zur Übung |
- a.
POWER(a, 2) + 2*a*b + POWER(b, 2)
b.POWER(a + b, 2)
als einfachste Binomische Formel - a.
SQRT(216.09)
b.POWER(216.09, 0.5)
- a.
IF ( SUM(Bestellung) > SUM(Zahlung) )
b.IF( SIGN( SUM(Bestellung) – SUM(Zahlung) ) = 1)
ABS( SUM(Bestellung) – SUM(Zahlung) )
Lösung zu Übung 2 | Zufallszahlen | Zur Übung |
- RAND()
- RAND() * 4
- 1 + RAND() * 4
- FLOOR( 1 + RAND() * 4 )
- FLOOR( 1 + RAND() * 25 )
- SUBSTRING( letters FROM FLOOR( 1 + RAND() * 25 ) FOR 1 )
Lösung zu Übung 3 | Zeichenketten bearbeiten | Zur Übung |
SELECT LPAD( ID, 4, '0') + LPAD( Abteilung_ID, 2, '0') FROM Mitarbeiter;
Lösung zu Übung 4 | Zeichenketten bearbeiten | Zur Übung |
SELECT COALESCE( NULLIF(Mobil, ’’), Telefon) AS Tel FROM Mitarbeiter;
Erklärung: Wenn Mobil einen Wert enthält, kommt bei NULLIF dieser Wert heraus; andernfalls wird immer NULL geliefert – entweder als Feldinhalt oder als Ergebnis des Vergleichs mit der leeren Zeichenkette.
Lösung zu Übung 5 | Zeichenketten bearbeiten | Zur Übung |
SELECT SUBSTRING( Kennzeichen FROM 1 FOR Position('-', Kennzeichen) - 1) FROM Fahrzeug;
Lösung zu Übung 6 | Zeichenketten bearbeiten | Zur Übung |
UPDATE Schadensfall SET Beschreibung = REPLACE( Beschreibung, 'RE-LM 903', 'RE-LM 902' ) WHERE ID = 6;
Lösung zu Übung 7 | Datum und Zeit bearbeiten | Zur Übung |
Die Teilaufgaben liefern diese Einzellösungen.
DATEADD( YEAR, 18, Geburtsdatum )
DATEADD( MONTH, FLOOR(RAND()*11), DATEADD( YEAR, FLOOR(RAND()*40), '1950-01-01'))
Es handelt sich um eine verschachtelte Funktion: Zuerst wird das Jahr neu bestimmt, es wird maximal ein Wert von 40 addiert. Zu diesem Ergebnis wird der Monat neu bestimmt, es wird maximal 11 addiert. In manchen Fällen sind einzelne Angaben per CAST genauer zu definieren.DATEADD( DAY, FLOOR(RAND()*(41*365+10)), '1950-01-01')
Es handelt sich um 41 Jahre zu je 365 Tagen, dazu 10 Schalttage.SELECT Fuehrerschein, Abschlussdatum FROM (Vertrag/Kunde)
WHERE DATEDIFF( DAY, Fuehrerschein, Abschlussdatum) < 3*365
Hinweis:DATEDIFF(YEAR...)
ist nicht unbedingt geeignet, weil ein DBMS nicht die Termine, sondern die Jahreszahlen vergleichen könnte.
Siehe auch
BearbeitenBei Wikipedia gibt es fachliche Erläuterungen:
- Potenzen
- Exponentialfunktion
- Trigonometrische Funktionen und Bogenmaß
- Betragsfunktion – der absolute Betrag
- Zufallszahl gibt auch Hinweise zu „Pseudozufallszahlen“.
- Binomische Formeln
WHERE-Klausel im Detail |
In diesem Kapitel werden die Einzelheiten der WHERE-Klausel genauer behandelt. Diese Angaben sind vor allem für den SELECT-Befehl, aber auch für UPDATE und DELETE von Bedeutung.
Die Beispiele beziehen sich auf den Anfangsbestand der Beispieldatenbank; auf die Ausgabe der selektierten Datensätze wird verzichtet. Bitte probieren Sie alle Beispiele aus und nehmen Sie verschiedene Änderungen vor, um die Auswirkungen zu erkennen.
Allgemeine Hinweise
BearbeitenDie WHERE-Klausel ist (neben der Verknüpfung mehrerer Tabellen) der wichtigste Bestandteil des SELECT-Befehls: Je sorgfältiger die Auswahlbedingungen formuliert werden, desto genauer ist das Ergebnis der Abfrage.
Neben den hier erläuterten Varianten bietet jedes DBMS noch andere, z. B. STARTING WITH oder SIMILAR.
Anstelle konstanter Werte können auch passende Ausdrücke angegeben werden, z. B. Funktionen oder Unterabfragen.
Verwenden Sie bei den Beispielen möglichst immer auch die Umkehrung der Auswahl mit bzw. ohne NOT. Wie im Kapitel „Ausführliche SELECT-Struktur“ zur WHERE-Klausel angegeben, steht das NOT ggf. unmittelbar vor dem Parameter-Namen.
Eine einzelne Bedingung
BearbeitenGrößenvergleich zweier Werte
BearbeitenDer einfachste Weg ist der direkte Vergleich zweier Werte, nämlich der Inhalt einer Spalte mit einem konstanten Wert. Dies ist möglich mit den folgenden Vergleichsoperatoren, und zwar für alle Datentypen, die verglichen werden können – Zahlen, Zeichenketten, Datumsangaben.
= < > <= >= <>
Beispiele:
Suche einen Datensatz, bei dem der Wert in der Spalte ID gleich ist zu einem vorgegebenen Wert.
select * from Versicherungsnehmer
where ID = 10;
Suche Datensätze, bei denen der Name kleiner als 'B' ist, also mit 'A' anfängt.
select * from Versicherungsnehmer
where Name < 'B';
Suche Führerschein-Neulinge.
select * from Versicherungsnehmer
where Fuehrerschein >= '01.01.2007';
Suche Fahrzeugtypen mit kurzer Bezeichnung.
select * from Fahrzeugtyp
where Char_Length(Bezeichnung) <= 3;
Bei diesen Vergleichen ist NOT zwar ebenfalls möglich; besser verständlich ist aber ein anderer passender Operator.
BETWEEN AND – Werte zwischen zwei Grenzen
BearbeitenMit der Bedingung BETWEEN <wert1> AND <wert2> wird direkt mit einem Bereich verglichen; die Grenzwerte gehören meistens zum Bereich (abhängig vom DBMS). Auch dies ist möglich für Zahlen, Zeichenketten, Datumsangaben.
Suche Datensätze, bei denen die PLZ außerhalb eines Bereichs 45000...45999 liegt.
select * from Versicherungsnehmer
where PLZ NOT BETWEEN '45000' AND '45999';
LIKE – Ähnlichkeiten (1)
BearbeitenDie LIKE-Bedingung vergleicht Zeichenketten „ungenau“: Der gesuchte Text soll als Wert in einer Spalte enthalten sein; dazu werden „Wildcards“ benutzt: Der Unterstrich '_' steht für ein beliebiges einzelnes Zeichen, das an der betreffenden Stelle vorkommen kann. Das Prozentzeichen '%' steht für eine beliebige Zeichenkette mit 0 oder mehr Zeichen.
Diese Bedingung wird vor allem in zwei Situationen gerne benutzt:
- Der Suchbegriff ist sehr lang; dem Anwender soll es genügen, den Anfang einzugeben.
- Der Suchbegriff ist nicht genau bekannt (z. B. nicht richtig lesbar).
Beispiele:
Der Ortsname beginnt nicht mit 'B'; der Inhalt dahinter ist beliebig.
select * from Versicherungsnehmer
where Ort NOT LIKE 'B%';
Der Ortsname enthält irgendwo 'alt' mit beliebigem Inhalt davor und dahinter.
select * from Versicherungsnehmer
where Ort LIKE '%alt%';
Der Anfangsbuchstabe des Namens ist unklar, aber danach folgen die Buchstaben 'ei' und noch etwas mehr.
select * from Versicherungsnehmer
where Name LIKE '_ei%';
Ein Problem haben wir, wenn eines der Wildcard-Zeichen Teil des Suchbegriffs sein soll. Dann muss dem LIKE-Parameter mitgeteilt werden, dass '%' bzw. '_' als „echtes“ Zeichen zu verstehen ist. Das geschieht dadurch, dass ein spezielles Zeichen davor gesetzt wird und dieses Zeichen als „ESCAPE-Zeichen“ angegeben wird:
Innerhalb der Beschreibung kommt die Zeichenfolge '10%' vor.
select * from Schadensfall
where Beschreibung LIKE '%10\%%' ESCAPE '\';
Das erste und das letzte Prozentzeichen stehen dafür, dass vorher und nachher beliebige Inhalte möglich sind. Das mittlere Prozentzeichen wird mit dem Escape-Zeichen '\' verbunden und ist damit Teil der gesuchten Zeichenfolge. Diese Angabe '\%' ist als ein Zeichen zu verstehen.
Vergleichen Sie das Abfrageergebnis, wenn der ESCAPE-Parameter weggelassen wird oder wenn eines oder mehrere der Sonderzeichen im LIKE-Parameter fehlen.
CONTAINS u. a. – Ähnlichkeiten (2)
BearbeitenEin Problem des LIKE-Parameters ist die Verwendung der Wildcard-Zeichen '%' und '_', die man gerne vergisst oder (wie im letzten Beispiel) nicht genau genug beachtet. Deshalb gibt es verschiedene Vereinfachungen.
CONTAINS – in Firebird CONTAINING – prüft, ob eine Zeichenkette im Feldinhalt enthalten ist.
select * from Schadensfall
where Beschreibung CONTAINS '10%';
Bitte prüfen Sie in der Beschreibung Ihres DBMS, welche Möglichkeiten für die Suche nach Ähnlichkeiten außerdem angeboten werden.
IS NULL – null-Werte prüfen
BearbeitenWie schon bei den relationalen Datenbanken besprochen, haben NULL-Werte eine besondere Bedeutung. Mit den folgenden beiden Abfragen werden nicht alle Datensätze gefunden:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil <> '';
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil = '';
Nanu, es gibt doch 28 Mitarbeiter; wo sind die übrigen geblieben? Für diese Fälle gibt es mit IS NULL eine spezielle Abfrage:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil is null;
Der Vollständigkeit halber sei darauf hingewiesen, dass die folgende Abfrage tatsächlich die richtige Gegenprobe liefert.
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil is not null;
Die folgende Abfrage liefert eine leere Ergebnismenge zurück, weil NULL eben kein Wert ist.
select ID, Name, Vorname, Mobil
from Mitarbeiter
where Mobil = null;
Es gibt keine einzelne Bedingung, die alle Datensätze ohne explizite Mobil-Angabe auf einmal angibt. Es gibt nur die Möglichkeit, die beiden Bedingungen "IS NULL" und "ist leer" zu verknüpfen:
select ID, Name, Vorname, Mobil
from Mitarbeiter
where ( Mobil is null ) or ( Mobil = '' );
Beachten Sie auch bei "WHERE ... IS [NOT] NULL" die Bedeutung von NULL:
- Bei Zeichenketten ist zu unterscheiden zwischen dem „leeren“ String und dem NULL-Wert.
- Bei Zahlen ist zu unterscheiden zwischen der Zahl '0' (null) und dem NULL-Wert.
- Bei Datumsangaben ist zu unterscheiden zwischen einem vorhandenen Datum und dem NULL-Wert; ein Datum, das der Zahl 0 entspräche, gibt es nicht. (Man könnte allenfalls das kleinste mögliche Datum wie '01.01.0100' benutzen, aber dies ist bereits ein Datum.)
IN – genauer Vergleich mit einer Liste
BearbeitenDer IN-Parameter vergleicht, ob der Inhalt einer Spalte in der angegebenen Liste enthalten ist. Die Liste kann mit beliebigen Datentypen arbeiten.
Hole die Liste aller Fahrzeuge, deren Typen als „VW-Kleinwagen“ registriert sind.
select * from Fahrzeug
where Fahrzeugtyp_ID in (1, 2);
Suche nach einem Unfall Fahrzeuge mit einer von mehreren möglichen Farben.
select * from Fahrzeug
where Farbe in ('ocker', 'gelb');
Vor allem das erste Beispiel wird sehr oft mit einer Unterabfrage versehen; vergleichen Sie dazu auch den folgenden Abschnitt zu EXISTS.
Hole die Liste aller Fahrzeuge vom Typ „Volkswagen“.
select * from Fahrzeug
where Fahrzeugtyp_ID in
( select ID from Fahrzeugtyp
where Hersteller_ID = 1 );
Dabei wird zuerst mit der Unterabfrage eine Liste aller Fahrzeugtypen-IDs für den Hersteller 1 (= Volkswagen) zusammengestellt; diese wird dann für den Vergleich über den IN-Parameter benutzt.
EXISTS – schneller Vergleich mit einer Liste
BearbeitenIm Gegensatz zu den anderen Parametern der WHERE-Klausel arbeitet der EXISTS-Parameter nicht mit fest vorgegebenen Werten, sondern nur mit dem Ergebnis einer Abfrage, also einer Unterabfrage. Das letzte Beispiel zum IN-Parameter kann auch so formuliert werden:
select * from Fahrzeug fz
where EXISTS
( select * from Fahrzeugtyp ft
where ft.Hersteller_ID = 1
and fz.Fahrzeugtyp_ID = ft.ID );
Zu jedem Datensatz aus der Tabelle Fahrzeug wird zu dieser Fahrzeugtyp_ID eine Unterabfrage aus den Fahrzeugtypen erstellt: Wenn es dort einen Datensatz mit passender ID und Hersteller-ID 1 (= Volkswagen) gibt, gehört der Fahrzeug-Datensatz zur Auswahl, andernfalls nicht.
Da Unterabfragen zuerst ausgeführt werden, wird eine EXISTS-Prüfung in aller Regel schneller erledigt als die entsprechende IN-Prüfung: Bei EXISTS handelt es sich um eine Feststellung „ist überhaupt etwas vorhanden“; bei IN dagegen muss ein exakter Vergleich mit allen Werten einer Liste durchgeführt werden. Bei unserer kleinen Beispieldatenbank spielt das natürlich keine Rolle, aber bei einer „echten“ Datenbank mit Millionen von Einträgen schon.
Mehrere Bedingungen verknüpfen
BearbeitenBei der WHERE-Klausel geht es darum festzustellen, ob ein Datensatz Teil des Abfrageergebnisses ist oder nicht; bei der <search condition> handelt sich also um einen booleschen Ausdruck, d. h. einen Ausdruck, der einen der booleschen Werte WAHR oder FALSCH – TRUE bzw. FALSE – als Ergebnis hat. Nur bei einfachen Abfragen genügt dazu eine einzelne Bedingung; meistens müssen mehrere Bedingungen verknüpft werden (wie beim letzten Beispiel unter IS NULL).
Dazu gibt es die booleschen Operatoren NOT, AND, OR.
NOT als Negation
BearbeitenDieser Operator kehrt das Ergebnis um: aus TRUE wird FALSE, aus FALSE wird TRUE.
SELECT * FROM Versicherungsnehmer
WHERE NOT (Fuehrerschein >= '01.01.2007');
AND als Konjunktion
BearbeitenEine Bedingung, die durch eine AND-Verknüpfung gebildet wird, ist genau dann TRUE, wenn beide (bzw. alle) Bestandteile TRUE sind.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
AND Name < 'K';
OR als Adjunktion
BearbeitenEine Bedingung, die durch eine OR-Verknüpfung gebildet wird, ist genau dann TRUE, wenn mindestens ein Bestandteil TRUE ist; dabei ist es gleichgültig, ob die anderen Bestandteile TRUE oder FALSE sind.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
OR Name < 'K';
Bitte beachten Sie, dass der normale Sprachgebrauch „alle ... und alle ...“ sagt. Gemeint ist nach logischen Begriffen aber, dass <Bedingung 1> erfüllt sein muss ODER <Bedingung 2> ODER BEIDE.
XOR als Kontravalenz
BearbeitenEine Bedingung, die durch eine XOR-Verknüpfung gebildet wird, ist genau dann TRUE, wenn ein Bestandteil TRUE ist, aber der andere Bestandteil FALSE ist – „ausschließendes oder“ bzw. „entweder – oder“. Diese Verknüpfung gibt es selten, z. B. bei MySQL; hier wird es der Vollständigkeit halber erwähnt.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE PLZ BETWEEN '45000' AND '45999'
XOR Name < 'K';
Bitte beachten Sie, dass hier der normale Sprachgebrauch „oder“ sagt und „entweder – oder“ gemeint ist.
Anstelle von XOR kann immer eine Kombination verwendet werden:
( <Bedingung 1> AND ( NOT <Bedingung 2> ) ) OR ( <Bedingung 2> AND ( NOT <Bedingung 1> ) )
Klammern benutzen oder weglassen?
BearbeitenBereits im Kapitel „Ausführliche SELECT-Struktur“ wurde die Hierarchie genannt:
- NOT ist die engste Verbindung und wird vorrangig ausgewertet.
- AND ist die nächststärkere Verbindung und wird danach ausgewertet.
- OR ist die schwächste Verbindung und wird zuletzt ausgewertet.
- Was in Klammern steht, wird vor allem anderen ausgewertet.
Bitte setzen Sie im folgenden Beispiel Klammern an anderen Stellen oder streichen Sie Klammern, und vergleichen Sie die Ergebnisse.
SELECT ID, Name, Vorname, PLZ, Ort
FROM Versicherungsnehmer
WHERE not ( PLZ BETWEEN '45000' AND '45999'
AND ( Name LIKE 'B%'
OR Name LIKE 'K%'
OR NOT Name CONTAINING 'ei'
)
)
order by PLZ, Name;
Sie werden ziemlich unterschiedliche Ergebnisse feststellen. Es empfiehlt sich deshalb, an allen sinnvollen Stellen Klammern zu setzen – auch dort, wo sie nicht erforderlich sind – und das, was zusammengehört, durch Einrückungen sinnvoll zu gliedern.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten wir neben dem Vergleich von Werten viele Möglichkeiten kennen, mit denen Bedingungen für Abfragen festgelegt werden können:
- Mit BETWEEN AND werden Werte innerhalb eines Bereichs geprüft.
- Mit LIKE und CONTAINS werden Werte gesucht, die mit vorgegebenen Werten teilweise übereinstimmen.
- Mit IS NULL werden null-Werte gesucht.
- Mit IN und EXISTS werden Spaltenwerte mit einer Liste verglichen.
Mit AND, OR, NOT werden Bedingungen zusammengefasst.
Übungen
BearbeitenBei den folgenden Aufgaben kommt es nur auf die WHERE-Klausel an; Sie dürfen ein SELECT „mit allen Spalten“ benutzen.
Übung 1 | Auswahl nach Zeichenketten | Zur Lösung |
Suchen Sie alle Versicherungsnehmer, die folgenden Bedingungen entsprechen:
- Der erste Buchstabe des Nachnamens ist nicht bekannt, der zweite ist ein 'r'.
- Der Vorname enthält ein 'a'.
- Die Postleitzahl gehört zum Bereich Essen (PLZ 45...).
Übung 2 | Auswahl nach Datumsbereich | Zur Lösung |
Suchen Sie alle Versicherungsnehmer, die in den Jahren 1967 bis 1970 ihren 18. Geburtstag hatten.
Übung 3 | Auswahl nach Ähnlichkeit | Zur Lösung |
Zeigen Sie alle Schadensfälle an, bei denen in der Beschreibung auf eine prozentuale Angabe hingewiesen wird.
Übung 4 | Auswahl für unbekannte Werte | Zur Lösung |
Zeigen Sie alle Dienstwagen an, die keinem Mitarbeiter persönlich zugeordnet sind.
Hinweis: Die Prüfung „Mitarbeiter ohne Dienstwagen“ ist komplizierter; das dafür erforderliche OUTER JOIN wird erst später behandelt.
Übung 5 | Bedingungen verknüpfen | Zur Lösung |
Zeigen Sie alle Mitarbeiter der Abteilungen „Vertrieb“ (= 'Vert') und „Ausbildung“ (= 'Ausb') an.
Hinweis: Bestimmen Sie zunächst die IDs der gesuchten Abteilungen und benutzen Sie das Ergebnis für die eigentliche Abfrage.
Übung 6 | Bedingungen verknüpfen | Zur Lösung |
Gesucht werden die Versicherungsverträge für Haftpflicht (= 'HP') und Teilkasko (= 'TK'), die mindestens seit dem Ende des Jahres 1980 bestehen und aktuell nicht mit dem minimalen Prämiensatz berechnet werden.
Hinweis: Tragen Sie ausnahmsweise nur die notwendigen Klammern ein, nicht alle sinnvollen.
Lösung zu Übung 1 | Auswahl nach Zeichenketten | Zur Übung |
select * from Versicherungsnehmer
where Name like '_r%' and Vorname like '%a%'
and PLZ STARTING WITH '45' /* oder: */
PLZ like '45%';
Lösung zu Übung 2 | Auswahl nach Datumsbereich | Zur Übung |
select * from Versicherungsnehmer
where DATEADD(YEAR, 18, Geburtsdatum) BETWEEN '01.01.1967' AND '31.12.1970';
Lösung zu Übung 3 | Auswahl nach Ähnlichkeit | Zur Übung |
SELECT * from Schadensfall
where Beschreibung like '%\%%' escape '\';
Lösung zu Übung 4 | Auswahl für unbekannte Werte | Zur Übung |
SELECT * from Dienstwagen
where Mitarbeiter_ID is null;
Lösung zu Übung 5 | Bedingungen verknüpfen | Zur Übung |
SELECT * from Mitarbeiter
where Abteilung_ID in (
select id from Abteilung
where Kuerzel in ('Vert', 'Ausb') );
Lösung zu Übung 6 | Bedingungen verknüpfen | Zur Übung |
SELECT * from Versicherungsvertrag
where (Art = 'HP' or Art = 'TK')
and Abschlussdatum <= '31.12.1980'
and (not Praemiensatz = 30) /* oder */
and Praemiensatz > 30;
Siehe auch
BearbeitenDieses Kapitel verweist auf die folgenden Kapitel:
Bei Wikipedia gibt es weitere fachliche Hinweise:
Mehrere Tabellen |
Ein besonderes Merkmal von relationalen Datenbanken und damit von SQL ist, dass die Informationen fast immer über mehrere Tabellen verteilt sind und bei Abfragen in der Ergebnismenge zusammengeführt werden müssen. Dieses Kapitel gibt einen Überblick über die Möglichkeiten dazu; Einzelheiten stehen in den folgenden Kapiteln.
Schreibweisen bei mehreren Tabellen
BearbeitenBitte beachten Sie bei allen Befehlen, die mehrere Tabellen verwenden (das sind zwangsläufig nur SELECT-Befehle):
- Wenn ein Spaltenname in Bezug auf den gesamten SQL-Befehl eindeutig ist, genügt dieser Name.
- Wenn ein Spaltenname mehrfach vorkommt (wie ID), dann muss der Tabellenname vorangesetzt werden; der Spaltenname wird nach einem Punkt angefügt.
SELECT
Personalnummer AS MitNr,
Name, Vorname,
Dienstwagen.ID, Kennzeichen, Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter, Dienstwagen;
- Wegen der Übersichtlichkeit wird die Tabelle meistens auch dann bei jeder Spalte angegeben, wenn es wegen der ersten Regel nicht erforderlich wäre.
SELECT
Mitarbeiter.Personalnummer AS MitNr,
Mitarbeiter.Name, Mitarbeiter.Vorname,
Dienstwagen.ID AS DIW, Dienstwagen.Kennzeichen, Dienstwagen.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter, Dienstwagen;
- Anstelle des Namens einer Tabelle kann überall auch ein Tabellen-Alias benutzt werden; dieser muss einmal hinter ihrem Namen (in der FROM- oder in der JOIN-Klausel) angegeben werden.
SELECT
mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi, Dienstwagen dw;
Alle diese Befehle für „Liste der Mitarbeiter mit Dienstwagen“ sind gleichwertig. Zu empfehlen ist die vollständige Schreibweise mit Alias wie im vorigen Beispiel.
Ein ähnlicher Befehl unter Verwendung der JOIN-Klausel sieht dann so aus:
SELECT
mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
JOIN Dienstwagen dw on mi.ID = dw.Mitarbeiter_ID;
Der Alias ist nur für den betreffenden SQL-Befehl gültig. Ein und dieselbe Tabelle kann mal als 'a', dann als 'mi' oder auch als 'xyz' bezeichnet werden. Wegen des leichteren Verständnisses sind aussagefähige Kürzel sinnvoll; auch deshalb sind sie im Kapitel Tabellenstruktur der Beispieldatenbank angegeben.
Verknüpfung über WHERE – der traditionelle Weg
BearbeitenBeim einfachsten Verfahren, mehrere Tabellen gleichzeitig abzufragen, stehen alle Tabellen in der FROM-Klausel; die WHERE-Klausel enthält neben den Auswahlbedingungen auch Bedingungen zur Verknüpfung der Tabellen.
Einzelheiten werden in Einfache Tabellenverknüpfung behandelt.
JOINs - der moderne Weg
BearbeitenBeim „modernen“ Weg, mehrere Tabellen in einer gemeinsamen Abfrage zu verknüpfen, wird jede Tabelle in einer JOIN-Klausel aufgeführt; der ON-Parameter enthält die Verknüpfungsbedingung. Die WHERE-Klausel enthält nur die Auswahlbedingungen.
Die Einführung dazu wird in Arbeiten mit JOIN besprochen.
OUTER JOIN - auch null-Werte zurückgeben
BearbeitenBei Abfragen mit einem „einfachen“ JOIN werden nicht alle Datensätze aufgeführt. Zeilen, zu denen es in der einen oder anderen Tabelle keine Verknüpfung gibt, fehlen im Ergebnis. Mit einem OUTER JOIN können auch solche „fehlenden“ Zeilen aufgeführt werden.
Einzelheiten dazu werden in OUTER JOIN behandelt.
Weitere Möglichkeiten von JOIN
BearbeitenAls SELF JOIN wird eine Tabelle mit sich selbst verknüpft.
Oft kommt es vor, dass man die Daten aus einer Tabelle erst bearbeiten möchte, bevor man sie mit einer anderen Tabelle verknüpft. Dazu gibt es die Möglichkeit einer „Inline-View“.
Diese Ergänzungen werden in Mehr zu JOIN besprochen.
Zusammenfassung
BearbeitenIn diesem Kapitel erhielten wir Hinweise darauf, wie mehrere Tabellen verknüpft werden können:
- einfach über die FROM-Klausel und passende WHERE-Bedingungen
- übersichtlich über die JOIN-Klausel mit verschiedenen Varianten
Übungen
Bearbeiten
Übung 1 | Was ist an diesem SELECT-Befehl falsch? | Zur Lösung |
Zeigen Sie zu bestimmten Versicherungsverträgen die Daten der Fahrzeuge an.
select ID, Abschlussdatum, Art,
vv.Kennzeichen, Farbe
from Versicherungsvertrag vv, Fahrzeug
where vv.Fahrzeug_ID = Fahrzeug.ID
and Kennzeichen LIKE 'BO%';
Übung 2 | Was ist an diesem SELECT-Befehl falsch? | Zur Lösung |
Zeigen Sie zu einem Versicherungsvertrag die Daten des Versicherungsnehmers und des Sachbearbeiters an.
select ID, Vorname + ' ' + Name AS Kunde, Ort
Name AS Sachbearbeiter, Telefon
from Versicherungsvertrag, Versicherungsnehmer, Mitarbeiter
where ID = 27
and Versicherungsvertrag.Versicherungsnehmer_ID = Versicherungsnehmer.ID
and Versicherungsvertrag.Mitarbeiter_ID = Mitarbeiter.ID;
Übung 3 | Berichtigen Sie den folgenden SELECT-Befehl. | Zur Lösung |
Zeigen Sie zu jedem Mitarbeiter die Daten seines Dienstwagens (Kennzeichen, Typ, Hersteller) an.
select ID, Name, Vorname,
Kennzeichen, Bezeichnung, Name
from Mitarbeiter mi, Dienstwagen dw,
Fahrzeugtyp ft, Fahrzeughersteller fh
where ID = dw.Mitarbeiter_ID
and ID = dw.Fahrzeugtyp_ID
and ID = ft.Hersteller_ID
order by Name, Vorname;
Lösungen
Bearbeiten
Lösung zu Übung 1 | Was ist an diesem SELECT-Befehl falsch? | Zur Übung |
- Die ID muss mit Tabellennamen oder Alias versehen sein, weil sie in beiden Tabellen enthalten ist.
- Die Spalte Kennzeichen gehört zur Tabelle Fahrzeug, also ist der Alias vv falsch.
Lösung zu Übung 2 | Was ist an diesem SELECT-Befehl falsch? | Zur Übung |
- Die ID muss sowohl in der Spaltenliste als auch in der WHERE-Klausel mit Tabellennamen oder Alias versehen sein, weil sie in allen Tabellen enthalten ist.
- Gleiches gilt für Name und Vorname, weil diese Angaben in mehreren Tabellen enthalten sind.
Wenn (wie in den Anmerkungen zur Beispieldatenbank erwähnt) auch für die Kunden Kontaktdaten gespeichert wären, müsste das auch bei der Spalte Telefon beachtet werden. Für die Spalte Ort gilt das nicht, weil diese nicht zur Tabelle Mitarbeiter gehört, sondern zur Tabelle Abteilung, die hier nicht benutzt wird.
Lösung zu Übung 3 | Berichtigen Sie den folgenden SELECT-Befehl. | Zur Übung |
select mi.ID, mi.Name, mi.Vorname,
dw.Kennzeichen, ft.Bezeichnung, fh.Name
from Mitarbeiter mi, Dienstwagen dw,
Fahrzeugtyp ft, Fahrzeughersteller fh
where mi.ID = dw.Mitarbeiter_ID
and ft.ID = dw.Fahrzeugtyp_ID
and fh.ID = ft.Hersteller_ID
order by mi.Name, mi.Vorname;
Siehe auch
BearbeitenSelfhtml: JOIN – eine andere Beschreibung und Beispielserie zum JOIN-Befehl.
Einfache Tabellenverknüpfung |
Dieses Kapitel behandelt den „traditionellen“ Weg, mehrere Tabellen gleichzeitig abzufragen. Dazu werden in der FROM-Klausel alle Tabellen aufgeführt; die WHERE-Klausel enthält neben den Auswahlbedingungen auch Verknüpfungsbedingungen, wie die Tabellen zueinander gehören.
Alle Kombinationen aller Datensätze
BearbeitenDer einfachste Weg, Tabellen zu verknüpfen, ist ein Befehl wie der folgende, in dem verschiedene Spalten aus zwei Tabellen zusammengefasst werden. Aber das Ergebnis sieht reichlich seltsam aus.
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID as DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
FROM Mitarbeiter mi, Dienstwagen dw;
MITNR NAME VORNAME DIW KENNZEICHEN Typ
----- ------------ --------- --- ----------- ---
10001 Müller Kurt 1 DO-WB 421 14
10002 Schneider Daniela 1 DO-WB 421 14
20001 Meyer Walter 1 DO-WB 421 14
20002 Schmitz Michael 1 DO-WB 421 14
30001 Wagner Gaby 1 DO-WB 421 14
30002 Feyerabend Werner 1 DO-WB 421 14
40001 Langmann Matthias 1 DO-WB 421 14
40002 Peters Michael 1 DO-WB 421 14
/* usw. */
10001 Müller Kurt 2 DO-WB 422 14
10002 Schneider Daniela 2 DO-WB 422 14
20001 Meyer Walter 2 DO-WB 422 14
20002 Schmitz Michael 2 DO-WB 422 14
/* usw. */
Tatsächlich erzeugt dieser Befehl das „kartesische Produkt“ der beiden Tabellen: Jeder Datensatz der einen Tabelle wird (mit den gewünschten Spalten) mit jedem Datensatz der anderen Tabelle verbunden. Das sieht also so aus, als wenn alle Dienstwagen zu jedem Mitarbeiter gehören würden, was natürlich Quatsch ist.
Diese Variante ist also in aller Regel sinnlos (wenn auch syntaktisch korrekt). Nützlich ist sie nur dann, wenn auf einfachem Wege große Mengen von Testdaten erzeugt werden sollen, wie es im Kapitel Testdaten erzeugen benutzt wird.
Zwei Tabellen einfach verbinden
BearbeitenSinnvoll wird die vorstehende Abfrage durch eine kleine Ergänzung. Was will man denn eigentlich wissen?
- Gib mir (einige) Spalten der Tabelle Mitarbeiter zusammen mit (einigen) Spalten der Tabelle Dienstwagen, und zwar bei jedem Mitarbeiter denjenigen Dienstwagen, der zu diesem Mitarbeiter gehört.
Woran erkennt man, zu welchem Mitarbeiter ein Dienstwagen gehört? Nun, in der Tabelle Dienstwagen ist eine Spalte Mitarbeiter_ID enthalten; dieser Wert ist identisch mit der ID eines Eintrags in der Tabelle Mitarbeiter.
Wenn man diese Anfrage und diese Information in „Pseudocode“ übersetzt, dann kommt so etwas heraus:
Hole Spalten der Tabelle Mitarbeiter sowie Spalten der Tabelle Dienstwagen wobei die Mitarbeiter_ID eines Dienstwagens gleich ist der ID eines Mitarbeiters
Das können wir nun in eine vollständige SQL-Abfrage übersetzen; die obige Abfrage muss nur minimal erweitert werden:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi, Dienstwagen dw
WHERE dw.Mitarbeiter_ID = mi.ID
order by MitNr;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
100001 Grosser Horst 10 DO-WB 4210 14
10001 Müller Kurt 1 DO-WB 421 14
110001 Eggert Louis 11 DO-WB 4211 14
120001 Carlsen Zacharias 12 DO-WB 4212 14
20001 Meyer Walter 2 DO-WB 422 14
30001 Wagner Gaby 3 DO-WB 423 14
40001 Langmann Matthias 4 DO-WB 424 14
50001 Pohl Helmut 5 DO-WB 425 14
50002 Braun Christian 14 DO-WB 352 2
50003 Polovic Frantisek 15 DO-WB 353 3
50004 Kalman Aydin 16 DO-WB 354 4
/* usw. */
Wir bekommen also tatsächlich genau diejenigen Mitarbeiter, die über einen (persönlichen) Dienstwagen verfügen.
Hinweis: Wundern Sie sich nicht über die seltsame Reihenfolge. Die Personalnummer wurde als VARCHAR definiert; also kommt das Ergebnis in alphabetischer und nicht in numerischer Reihenfolge.
Mehrere Tabellen verbinden
BearbeitenIn der gleichen Weise können auch mehr als zwei Tabellen verknüpft werden. Im Kapitel Gruppierungen steht ein Beispiel ähnlich wie dieses:
Gesucht wird für jeden Fahrzeughersteller (mit Angabe von ID und Name) und jedes Jahr die Summe der Schadenshöhe aus der Tabelle Schadensfall.
SELECT fh.ID AS Hersteller_ID,
fh.Name AS Hersteller_Name,
EXTRACT(YEAR FROM sf.Datum) AS Jahr,
SUM(sf.Schadenshoehe) AS Schadenssumme
FROM Schadensfall sf, Zuordnung_SF_FZ zu,
Fahrzeug fz, Fahrzeugtyp ft, Fahrzeughersteller fh
where sf.ID = zu.Schadensfall_ID
and fz.ID = zu.Fahrzeug_ID
and ft.ID = fz.Fahrzeugtyp_ID
and fh.ID = ft.Hersteller_ID
GROUP BY Hersteller_ID, Hersteller_Name, Jahr
ORDER BY Jahr, Hersteller_ID;
Wichtig ist, dass es immer eine eindeutige Zuordnung zwischen jeweils einer Spalte einer Tabelle und einer Spalte einer anderen Tabelle gibt. Bitte beachten Sie dabei:
- Statt einer einzigen Spalte kann auch eine Gruppe von Spalten verknüpft werden (z. B. Name + Vorname). Dies macht aber alles umständlicher, unübersichtlicher und unsicherer. Deshalb sollte vorzugsweise über eindeutige IDs o. ä. verknüpft werden.
- Wenn es zwischen einzelnen Tabellen keine „gemeinsamen“ Spalten gibt, dann kommt wieder das kartesische Produkt heraus; das Ergebnis ist dann eher sinnlos.
Verknüpfungs- und Abfragebedingungen
BearbeitenJe mehr Kombinationen benötigt werden, desto unübersichtlicher wird diese Konstruktion. Dabei enthält die WHERE-Klausel bisher nur die Verknüpfungen zwischen den Tabellen, aber noch keine Suchbedingungen wie hier:
select ... from ... where ...
and Jahr in [2006, 2007, 2008]
and fhe.Land in ['Schweden', 'Norwegen', 'Finnland']
order by Jahr, Hersteller_ID;
Das führt außerdem dazu, dass die WHERE-Klausel sachlich gewünschte Suchbedingungen und logisch benötigte Verknüpfungsbedingungen vermischt. Wer soll da noch durchblicken? Besser ist das in den nächsten Kapiteln ausführlich behandelte Verfahren mit JOIN.
Zusammenfassung
BearbeitenDieses Kapitel erläutert, wie mehrere Tabellen einfach durch die FROM-Klausel und passende WHERE-Bedingungen verknüpft werden können:
- In der Spaltenliste sollte immer der jeweilige Tabellenname angegeben werden; es kann auch ein Kürzel als Tabellen-Alias verwendet werden.
- In der FROM-Klausel werden alle Tabellen aufgelistet und in der WHERE-Klausel durch geeignete Bedingungen aufeinander bezogen.
- Durch die Vermischung zwischen Verknüpfungs- und Auswahlbedingungen wird dieses Verfahren schnell unübersichtlich.
Übungen
BearbeitenBei den folgenden Abfragen beziehen wir uns auf den Bestand der Beispieldatenbank im „Anfangszustand“: die Tabellen Versicherungsvertrag, Fahrzeug, Mitarbeiter mit jeweils etwa 28 Einträgen und Versicherungsnehmer mit etwa 26 Einträgen.
Übung 1 | Eine einfache Abfrage | Zur Lösung |
Erstellen Sie eine Abfrage zur Tabelle Versicherungsvertrag, die nur die wichtigsten Informationen (einschließlich der IDs auf andere Tabellen) enthält. Wie viele Einträge zeigt die Ergebnismenge an?
Übung 2 | Das kartesische Produkt | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 1, sodass anstelle der Versicherungsnehmer_ID dessen Name und Vorname angezeigt werden, und verzichten Sie auf eine WHERE-Klausel. Wie viele Einträge zeigt die Ergebnismenge an?
Übung 3 | Das kartesische Produkt | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 2, sodass anstelle der Fahrzeug_ID das Kennzeichen und anstelle der Mitarbeiter_ID dessen Name und Vorname angezeigt werden, und verzichten Sie auf eine WHERE-Klausel. Wie viele Einträge zeigt die Ergebnismenge an?
Übung 4 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 2, sodass Name und Vorname des Versicherungsnehmers genau zu einem jeden Vertrag passen. Wie viele Einträge zeigt die Ergebnismenge an?
Übung 5 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 3, sodass Name und Vorname des Mitarbeiters sowie das Fahrzeug-Kennzeichen genau zu einem jeden Vertrag passen. Wie viele Einträge zeigt die Ergebnismenge an?
Übung 6 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 5, sodass die ausgewählten Zeilen den folgenden Bedingungen entsprechen:
- Es geht ausschließlich um Eigene Kunden.
- Vollkasko-Verträge sollen immer angezeigt werden, ebenso Fahrzeuge aus dem Kreis Recklinghausen 'RE'.
- Teilkasko-Verträge sollen angezeigt werden, wenn sie nach 1990 abgeschlossen wurden.
- Haftpflicht-Verträge sollen angezeigt werden, wenn sie nach 1985 abgeschlossen wurden.
Wie viele Einträge zeigt die Ergebnismenge an?
Lösung zu Übung 1 | Eine einfache Abfrage | Zur Übung |
SELECT Vertragsnummer, Abschlussdatum, Art,
Versicherungsnehmer_ID, Fahrzeug_ID, Mitarbeiter_ID
from Versicherungsvertrag
Es werden 28 Zeilen angezeigt.
Lösung zu Übung 2 | Das kartesische Produkt | Zur Übung |
SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,
vn.Name, vn.Vorname,
Fahrzeug_ID,
Mitarbeiter_ID
from Versicherungsvertrag vv, Versicherungsnehmer vn;
Es werden etwa 728 Zeilen angezeigt.
Lösung zu Übung 3 | Das kartesische Produkt | Zur Übung |
SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,
vn.Name, vn.Vorname,
fz.Kennzeichen,
mi.Name, mi.Vorname
from Versicherungsvertrag vv, Versicherungsnehmer vn,
Fahrzeug fz, Mitarbeiter mi;
Es werden etwa 570 752 Zeilen angezeigt.
Lösung zu Übung 4 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,
vn.Name, vn.Vorname,
Fahrzeug_ID,
Mitarbeiter_ID
from Versicherungsvertrag vv, Versicherungsnehmer vn
where vn.ID = vv.Versicherungsnehmer_ID;
Es werden etwa 28 Zeilen angezeigt.
Lösung zu Übung 5 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,
vn.Name, vn.Vorname,
fz.Kennzeichen,
mi.Name, mi.Vorname
from Versicherungsvertrag vv, Versicherungsnehmer vn,
Fahrzeug fz, Mitarbeiter mi
where vn.ID = vv.Versicherungsnehmer_ID
and fz.ID = vv.Fahrzeug_ID
and mi.ID = vv.Mitarbeiter_ID;
Es werden etwa 28 Zeilen angezeigt.
Lösung zu Übung 6 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,
vn.Name, vn.Vorname,
fz.Kennzeichen,
mi.Name, mi.Vorname
from Versicherungsvertrag vv, Versicherungsnehmer vn,
Fahrzeug fz, Mitarbeiter mi
where vn.ID = vv.Versicherungsnehmer_ID
and fz.ID = vv.Fahrzeug_ID
and mi.ID = vv.Mitarbeiter_ID
and vn.Eigener_kunde = 'J'
and ( ( vv.Art = 'HP' and vv.Abschlussdatum > '31.12.1985' )
or ( vv.Art = 'TK' and vv.Abschlussdatum > '31.12.1990' )
OR ( vv.Art = 'VK' )
or ( fz.Kennzeichen STARTING WITH 'RE-' ) );
Es werden etwa 19 Zeilen angezeigt. Die OR-Verknüpfungen könnten teilweise auch mit CASE geschrieben werden.
Siehe auch
BearbeitenBei Wikipedia stehen weitere Hinweise:
Arbeiten mit JOIN |
Dieses Kapitel enthält die Einführung in die Variante mit JOIN, mit der mehrere Tabellen verknüpft werden können. Dazu wird jede der in Betracht kommenden Tabellen in einer JOIN-Klausel aufgeführt; der ON-Parameter enthält die Verknüpfungsbedingung. Die WHERE-Klausel enthält „nur“ die Auswahlbedingungen.
Die Syntax von JOIN
BearbeitenUm Tabellen sinnvoll miteinander zu verknüpfen (= verbinden, engl. join), wurde die JOIN-Klausel für den SELECT-Befehl mit folgender Syntax eingeführt.
SELECT <spaltenliste> FROM <haupttabelle> [<join-typ>] JOIN <verknüpfte tabelle> ON <bedingung>
Als <join-typ> stehen zur Verfügung:
- [INNER] JOIN, auch Equi-Join genannt, ist eine Verknüpfung innerhalb zweier Tabellen, d. h. ein Teil des kartesischen Produkts, bei dem ein Wert in beiden Tabellen vorhanden ist. INNER JOIN ist der Inhalt dieses Kapitels.
- OUTER JOIN bezeichnet Verknüpfungen, bei denen auch Datensätze geliefert werden, für die eine Vergleichsbedingung nicht erfüllt ist.
- LEFT JOIN, RIGHT JOIN, FULL JOIN bezeichnen Spezialfälle von OUTER JOIN, je nachdem in welcher Tabelle ein gesuchter Wert fehlt.
OUTER JOIN wird im nächsten Kapitel behandelt.
Einige Sonderfälle und Ergänzungen zu JOIN werden im Kapitel Mehr zu JOIN behandelt.
Als <bedingung> wird normalerweise nur eine Übereinstimmung (also eine Gleichheit) zwischen zwei Tabellen geprüft, auch wenn jede Kombination von Bedingungen erlaubt ist. Genauer: es geht um die Gleichheit von Werten je einer Spalte in zwei Tabellen. (Zwei Beispiele für andere Übereinstimmungen lernen Sie in „Mehr zu JOIN“ kennen.)
Auch mehrere Verknüpfungen sind möglich, entweder direkt hintereinander:
SELECT <spaltenliste> FROM <haupttabelle> [<join-typ>] JOIN <zusatztabelle1> ON <bedingung1> [<join-typ>] JOIN <zusatztabelle2> ON <bedingung2> [<join-typ>] JOIN <zusatztabelle3> ON <bedingung3>
oder durch Klammern gegliedert:
SELECT <spaltenliste> FROM <haupttabelle> [<join-typ>] JOIN ( <zusatztabelle1> [<join-typ>] JOIN ( <zusatztabelle2> [<join-typ>] JOIN <zusatztabelle3> ON <bedingung3> ) ON <bedingung2> ) ON <bedingung1>
Bitte beachten Sie dabei genau, wo und wie die Klammern und die dazugehörigen ON-Bedingungen gesetzt werden. Beide Varianten können unterschiedliche Ergebnisse liefern – abhängig vom JOIN-Typ und dem Zusammenhang zwischen den Tabellen.
Alle diese Möglichkeiten werden in den nächsten Abschnitten und Kapiteln genauer erläutert.
INNER JOIN von zwei Tabellen
BearbeitenDas Beispiel „alle Mitarbeiter mit den zugehörigen Dienstwagen“ aus dem vorigen Kapitel benötigt nur geringe Änderungen.
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
from Mitarbeiter mi
join Dienstwagen dw on dw.Mitarbeiter_ID = mi.ID
ORDER BY MitNr;
Das Ergebnis dieser Abfrage ist identisch mit der Liste im vorigen Kapitel; wir verzichten deshalb auf die erneute Ausgabe.
Die zweite Tabelle wird in die JOIN-Klausel verschoben, die Verknüpfungsbedingung in den ON-Parameter – fertig.
WHERE-Klausel bei JOINs
BearbeitenEine solche Abfrage kann wie üblich durch eine WHERE-Klausel eingeschränkt werden. Eine Suchbedingung auf die verknüpfte Tabelle Dienstwagen kann wahlweise in der WHERE-Klausel oder in der JOIN-Klausel stehen. In den beiden folgenden Beispielen geht es nur um die Dienstwagen von Mercedes. Die Information, welche Typen zu Mercedes gehören, kommt über eine Unterabfrage, die ebenfalls einen JOIN verwendet und die in Klammern gesetzt ist.
Suche die Dienstwagen vom Typ Mercedes.
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
from Mitarbeiter mi
join Dienstwagen dw
on mi.ID = dw.Mitarbeiter_ID
and dw.Fahrzeugtyp_ID in ( SELECT ft.ID
from Fahrzeugtyp ft
join Fahrzeughersteller fh
on ft.Hersteller_ID = fh.ID
and fh.Name = 'Mercedes-Benz' );
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
from Mitarbeiter mi
join Dienstwagen dw
on mi.ID = dw.Mitarbeiter_ID
where dw.Fahrzeugtyp_ID in ( SELECT ft.ID
from Fahrzeugtyp ft
join Fahrzeughersteller fh
on ft.Hersteller_ID = fh.ID
where fh.Name = 'Mercedes-Benz');
Natürlich sind Einschränkungen auf beide Tabellen möglich:
Gesucht werden Mitarbeiter mit 'M' und Mercedes als Dienstwagen.
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
from Mitarbeiter mi
join Dienstwagen dw
on mi.ID = dw.Mitarbeiter_ID
where dw.Fahrzeugtyp_ID in ( SELECT ft.ID
from Fahrzeugtyp ft
join Fahrzeughersteller fh
on ft.Hersteller_ID = fh.ID
where fh.Name = 'Mercedes-Benz')
and mi.Name like 'M%';
MITNR NAME VORNAME ID KENNZEICHEN TYP
------ --------- ------- -- ----------- ---
10001 Müller Kurt 1 DO-WB 421 14
20001 Meyer Walter 2 DO-WB 422 14
Bei diesem Beispiel wird sofort deutlich, welche Bedingungen die Verknüpfung und welche Bedingungen die Auswahl bezeichnen. Auf diese Übersichtlichkeit sollten Sie immer achten.
Übrigens gibt es keine allgemeine Regel, was als Haupttabelle und was als verknüpfte Tabelle zu verwenden ist. In den bisherigen Beispielen können die beiden Tabellen ohne weiteres vertauscht werden:
select mi.Personalnummer as MitNr,
mi.Name, mi.Vorname,
dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID as Typ
from Dienstwagen dw
join Mitarbeiter mi
on mi.ID = dw.Mitarbeiter_ID
where dw.Fahrzeugtyp_ID in ( SELECT ft.ID
from Fahrzeugtyp ft
join Fahrzeughersteller fh
on ft.Hersteller_ID = fh.ID
where fh.Name = 'Mercedes-Benz')
and mi.Name like 'M%';
Die Haupttabelle kann nach folgenden Überlegungen gewählt werden:
- Es sollte die Tabelle sein, die die „wichtigste“ bei der Abfrage ist.
- Es sollte diejenige mit den größten Einschränkungen sein; das beschleunigt die Abfrage besonders stark.
INNER JOIN mehrerer Tabellen
BearbeitenDazu nehmen wir wiederum das komplexe Beispiel aus dem vorigen Kapitel, das bei den Gruppierungen genauer besprochen wird. In diesem Fall spielt die Reihenfolge der JOIN-Klauseln eher keine Rolle, weil es sich sowieso um direkte Übereinstimmungen handelt und nur solche Datensätze benutzt werden, die es zu den betreffenden Werten tatsächlich gibt.
Gesucht wird für jeden Fahrzeughersteller (mit Angabe von ID und Name) und jedes Jahr die Summe der Schadenshöhe aus der Tabelle Schadensfall.
SELECT fh.ID AS Hersteller_ID,
fh.Name AS Hersteller_Name,
EXTRACT(YEAR FROM sf.Datum) AS Jahr,
SUM(sf.Schadenshoehe) AS Schadenssumme
FROM Schadensfall sf
JOIN Zuordnung_SF_FZ zu ON sf.ID = zu.Schadensfall_ID
JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID
JOIN Fahrzeugtyp ft ON ft.ID = fz.Fahrzeugtyp_ID
JOIN Fahrzeughersteller fh ON fh.ID = ft.Hersteller_ID
GROUP BY Hersteller_ID, Hersteller_Name, Jahr
ORDER BY Jahr, Hersteller_ID;
Übrigens ist es zulässig, den „traditionellen“ Weg mit mehreren Tabellen in der FROM-Klausel und den „modernen“ Weg über JOIN zu mischen. Wenn Sie in einem Ausnahmefall wirklich so vorgehen wollen, sollten Sie erst recht genau auf die Übersichtlichkeit und den Zusammenhang der Bedingungen achten. Der Autor dieses Hinweises kann sich keine passende Situation vorstellen, aber vielleicht ist es auch einmal sinnvoll.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie die Verknüpfung von Tabellen über die JOIN-Klausel kennen.
- Mit einem INNER JOIN werden Datensätze abgefragt, bei denen ein Wert in je einer Spalte beider Tabellen vorhanden ist.
- In der ON-Klausel steht diese Verknüpfungsbedingung.
- In der WHERE-Klausel stehen die „normalen“ Auswahlbedingungen.
Genauso können mehrere Tabellen verknüpft werden.
Übungen
Bearbeiten
Übung 1 | Definition von JOINs | Zur Lösung |
Welche der folgenden Aussagen sind wahr, welche falsch, welche sinnvoll?
- Der INNER JOIN liefert das kartesische Produkt zwischen den Tabellen.
- LEFT JOIN ist ein Spezialfall von OUTER JOIN.
- Für einen JOIN ist ON Fahrzeug.ID >= Versicherungsvertrag.Fahrzeug_ID eine zulässige Verknüpfungsbedingung.
- Eine Einschränkung auf die mit JOIN verknüpfte Tabelle gehört in die ON-Klausel:
... FROM Zuordnung_SF_FZ zu JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID AND EXTRACT(YEAR from sf.Datum) = 2008;
Übung 2 | Definition von JOINs | Zur Lösung |
Erläutern Sie, was am folgenden Befehl falsch oder äußerst ungünstig ist. Es handelt sich um diese Abfrage:
Gesucht sind die Schadensfälle des Jahres 2008. Zu jedem Schadensfall sind die beteiligten Fahrzeuge, der Schadensanteil sowie die Versicherungsdaten des Fahrzeugs (einschließlich Name des Halters) anzugeben.
SELECT Datum, SUBSTRING(Ort from 1 for 30) as Ort, Schadenshoehe,
zu.Schadenshoehe,
fz.Kennzeichen,
Vertragsnummer as Vertrag, Abschlussdatum, Art,
vn.Name as VN-Name, vn.Vorname as VN-Vorname
from Schadensfall sf
join Zuordnung_SF_FZ zu on ID = zu.Schadensfall_ID
join Fahrzeug fz on ID = zu.Fahrzeug_ID
join Versicherungsnehmer vn on ID = vv.Versicherungsnehmer_ID
join Versicherungsvertrag vv on vv.Fahrzeug_ID = zu.Fahrzeug_ID
where EXTRACT(YEAR from Datum) = 2008
order by Schadensfall_ID, Fahrzeug_ID;
Die folgenden Aufgaben entsprechen teilweise Aufgaben aus dem Kapitel „Einfache Tabellenverknüpfung“. Sie sollen jetzt an den passenden Stellen JOINs verwenden, anstatt die Tabellen einfach aufzulisten.
Übung 3 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Erstellen Sie eine Abfrage zur Tabelle Versicherungsvertrag mit den wichtigsten Informationen (einschließlich der IDs auf andere Tabellen). Beim Versicherungsnehmer sollen dessen Name und Vorname angezeigt werden. Es werden nur Verträge ab 1990 gesucht.
Übung 4 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Erweitern Sie die Abfrage von Aufgabe 3, sodass Name und Vorname des Mitarbeiters sowie das Fahrzeug-Kennzeichen eines jeden Vertrags angezeigt werden.
Übung 5 | Sinnvolle Verknüpfung von Tabellen | Zur Lösung |
Ändern Sie die Abfrage von Aufgabe 4 so, dass die ausgewählten Zeilen den folgenden Bedingungen entsprechen:
- Es geht ausschließlich um Eigene Kunden.
- Vollkasko-Verträge sollen immer angezeigt werden, ebenso Fahrzeuge aus dem Kreis Recklinghausen 'RE'.
- Teilkasko-Verträge sollen angezeigt werden, wenn sie nach 1990 abgeschlossen wurden.
- Haftpflicht-Verträge sollen angezeigt werden, wenn sie nach 1985 abgeschlossen wurden.
Lösung zu Übung 1 | Definition von JOINs | Zur Übung |
- Falsch; es liefert einen Teil des kartesischen Produkts, der durch die ON-Bedingung bestimmt wird.
- Richtig.
- Diese Bedingung ist zulässig, aber nicht sinnvoll. JOIN-ON passt in der Regel nur für Gleichheiten.
- Diese Bedingung ist zulässig. Besser ist es aber, eine Einschränkung der Auswahl in die WHERE-Klausel zu setzen.
Lösung zu Übung 2 | Definition von JOINs | Zur Übung |
Richtig ist beispielsweise die folgende Version. Als Haupttabelle wurde wegen der WHERE-Klausel die Tabelle Schadensfall gewählt; wegen der Reihenfolge der Verknüpfungen wäre auch Zuordnung_SF_FZ als Haupttabelle geeignet.
SELECT sf.Datum, SUBSTRING(sf.Ort from 1 for 30) as Ort, sf.Schadenshoehe,
zu.Schadenshoehe as Teilschaden,
fz.Kennzeichen,
vv.Vertragsnummer as Vertrag, vv.Abschlussdatum, vv.Art,
vn.Name as VN_Name, vn.Vorname as VN_Vorname
from Schadensfall sf
join Zuordnung_SF_FZ zu on sf.ID = zu.Schadensfall_ID
join Fahrzeug fz on fz.ID = zu.Fahrzeug_ID
join Versicherungsvertrag vv on fz.ID = vv.Fahrzeug_ID
join Versicherungsnehmer vn on vn.ID = vv.Versicherungsnehmer_ID
where EXTRACT(YEAR from sf.Datum) = 2008
order by zu.Schadensfall_ID, zu.Fahrzeug_ID;
Die Variante aus der Aufgabenstellung enthält folgende Problemstellen:
- Zeile 1: Der Tabellen-Alias sf fehlt bei Schadenshoehe und bei Ort. Bei Datum fehlt er auch, aber das ist kein Problem, weil es diese Spalte nur bei dieser Tabelle gibt.
- Zeile 2: Diese Spalte sollte einen Spalten-Alias bekommen wegen der abweichenden Bedeutung zu sf.Schadenshoehe.
- Zeile 4: Es ist schöner, auch hier mit einem Tabellen-Alias zu arbeiten.
- Zeile 5: Der Bindestrich in der Bezeichnung des Spalten-Alias wird nicht bei allen DBMS akzeptiert.
- Zeile 7, 8, 9: Zur Spalte ID ist jeweils die Tabelle anzugeben, ggf. mit dem Alias. Die JOIN-ON-Bedingung bezieht sich nicht automatisch auf diese Spalte und diese Tabelle.
- Zeile 9, 10: In Zeile 9 ist die Tabelle Versicherungsvertrag vv noch nicht bekannt. Wegen der Verknüpfungen ist zuerst Zeile 10 zu verwenden, danach Zeile 9. Die Verknüpfung über vv.Fahrzeug_ID = zu.Fahrzeug_ID ist nicht glücklich (wenn auch korrekt); besser ist der Bezug auf die direkt zugeordnete Tabelle Fahrzeug und deren PrimaryKey, nämlich ID.
- Zeile 11: Es ist klarer, auch hier den Tabellen-Alias sf zu verwenden.
- Zeile 12: Der Tabellen-Alias zu fehlt bei beiden Spalten. Bei Fahrzeug_ID ist er erforderlich (doppelte Verwendung bei vv), bei Schadensfall_ID sinnvoll.
Lösung zu Übung 3 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT Vertragsnummer, Abschlussdatum, Art,
Name, Vorname,
Fahrzeug_ID,
Mitarbeiter_ID
from Versicherungsvertrag vv
join Versicherungsnehmer vn on vn.ID = vv.Versicherungsnehmer_ID
where vv.Abschlussdatum >= '01.01.1990';
Lösung zu Übung 4 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT vv.Vertragsnummer as Vertrag, vv.Abschlussdatum, vv.Art,
vn.Name as VN_Name, vn.Vorname as VN_Vorname,
fz.Kennzeichen,
mi.Name as MI_Name, mi.Vorname as MI_Vorname
from Versicherungsvertrag vv
join Versicherungsnehmer vn on vn.ID = vv.Versicherungsnehmer_ID
join Fahrzeug fz on fz.ID = vv.Fahrzeug_ID
join Mitarbeiter mi on mi.ID = vv.Mitarbeiter_ID
where vv.Abschlussdatum >= '01.01.1990';
Lösung zu Übung 5 | Sinnvolle Verknüpfung von Tabellen | Zur Übung |
SELECT vv.Vertragsnummer as Vertrag, vv.Abschlussdatum, vv.Art,
vn.Name as VN_Name, vn.Vorname as VN_Vorname,
fz.Kennzeichen,
mi.Name as MI_Name, mi.Vorname as MI_Vorname
from Versicherungsvertrag vv
join Versicherungsnehmer vn on vn.ID = vv.Versicherungsnehmer_ID
join Fahrzeug fz on fz.ID = vv.Fahrzeug_ID
join Mitarbeiter mi on mi.ID = vv.Mitarbeiter_ID
where vn.Eigener_kunde = 'J'
and ( ( vv.Art = 'HP' and vv.Abschlussdatum > '31.12.1985' )
or ( vv.Art = 'TK' and vv.Abschlussdatum > '31.12.1990' )
OR ( vv.Art = 'VK' )
or ( fz.Kennzeichen STARTING WITH 'RE-' ) );
Siehe auch
BearbeitenIn diesem Kapitel werden Sachverhalte der folgenden Themen angesprochen:
OUTER JOIN |
Bei den Abfragen im vorigen Kapitel nach „alle Mitarbeiter und ihre Dienstwagen“ werden nicht alle Mitarbeiter aufgeführt, weil in der Datenbank nicht für alle Mitarbeiter ein Dienstwagen registriert ist. Ebenso gibt es einen Dienstwagen, der keinem bestimmten Mitarbeiter zugeordnet ist.
Mit einem OUTER JOIN werden auch Mitarbeiter ohne Dienstwagen oder Dienstwagen ohne Mitarbeiter aufgeführt.
Die Syntax von OUTER JOIN
BearbeitenDie Syntax entspricht derjenigen von JOIN allgemein. Wegen der speziellen Bedeutung sind die Tabellen nicht gleichberechtigt, sondern werden begrifflich unterschieden:
SELECT <spaltenliste> FROM <linke tabelle> [<join-typ>] JOIN <rechte tabelle> ON <bedingung>
Als Spezialfälle des OUTER JOIN gibt es die JOIN-Typen LEFT JOIN, RIGHT JOIN, FULL JOIN.
Anstelle von <haupttabelle> und <zusatztabelle> wird bei OUTER JOIN von <linke tabelle> und <rechte tabelle> gesprochen, weil diese Tabellen unterschiedlich behandelt werden.
Allgemeine Hinweise zu OUTER JOIN
BearbeitenDas Wort OUTER kann entfallen und wird üblicherweise nicht benutzt, weil durch die Begriffe LEFT, RIGHT, FULL bereits ein OUTER JOIN gekennzeichnet wird.
Die Begriffe <linke tabelle> und <rechte tabelle> beziehen sich auf die beiden Tabellen bezüglich der normalen Lesefolge: Wir lesen von links nach rechts, also ist die unter FROM genannte Tabelle die <linke Tabelle> (bisher <Haupttabelle> genannt) und die unter JOIN genannte Tabelle die <rechte Tabelle> (bisher <Zusatztabelle> genannt). Bei Verknüpfungen mit mehreren Tabellen ist ebenfalls die unter JOIN genannte Tabelle die <rechte Tabelle>; die unmittelbar vorhergehende Tabelle ist die <linke Tabelle>.
Auch wenn die folgenden Beispiele so aussehen, als wenn die Datensätze sinnvoll sortiert wären, ist das Zufall; bitte denken Sie daran, dass SQL unsortierte Datenmengen liefert. Eine bestimmte Reihenfolge erhalten Sie erst durch ORDER BY.
Die Anzeige der Ergebnismengen bei den Beispielen ist in der Regel nur ein Auszug des vollständigen Ergebnisses.
LEFT OUTER JOIN
BearbeitenDieser JOIN liefert alle Datensätze der linken Tabelle, ggf. unter Berücksichtigung der WHERE-Klausel. Aus der rechten Tabelle werden nur diejenigen Datensätze übernommen, die nach der Verknüpfungsbedingung passen.
SELECT <spaltenliste> FROM <linke Tabelle> LEFT [OUTER] JOIN <rechte Tabelle> ON <bedingung>;
Für unser Beispiel sieht das dann so aus:
Hole alle Mitarbeiter und (sofern vorhanden) die Angaben zum Dienstwagen.
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
LEFT JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
-------- ------------ --------- --- ----------- ---
30001 Wagner Gaby 3 DO-WB 423 14
30002 Feyerabend Werner
40001 Langmann Matthias 4 DO-WB 424 14
40002 Peters Michael
50001 Pohl Helmut 5 DO-WB 425 14
50002 Braun Christian 14 DO-WB 352 2
50003 Polovic Frantisek 15 DO-WB 353 3
50004 Kalman Aydin 16 DO-WB 354 4
60001 Aagenau Karolin 6 DO-WB 426 14
60002 Pinkart Petra
Und wenn wir jetzt die beiden Tabellen vertauschen?
Dann erhalten wir alle Dienstwagen und dazu die passenden Mitarbeiter.
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Dienstwagen dw
LEFT JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
------- ------------ --------- --- ----------- ---
80001 Schindler Christina 8 DO-WB 428 14
90001 Janssen Bernhard 9 DO-WB 429 14
100001 Grosser Horst 10 DO-WB 4210 14
110001 Eggert Louis 11 DO-WB 4211 14
120001 Carlsen Zacharias 12 DO-WB 4212 14
13 DO-WB 111 16
50002 Braun Christian 14 DO-WB 352 2
50003 Polovic Frantisek 15 DO-WB 353 3
50004 Kalman Aydin 16 DO-WB 354 4
Bitte überlegen Sie selbst, wie sich WHERE-Klauseln auf das Ergebnis einer Abfrage auswirken.
RIGHT OUTER JOIN
BearbeitenDieser JOIN liefert alle Datensätze der rechten Tabelle, ggf. unter Berücksichtigung der WHERE-Klausel. Aus der linken Tabelle werden nur diejenigen Datensätze übernommen, die nach der Verknüpfungsbedingung passen.
SELECT <spaltenliste> FROM <linke Tabelle> RIGHT [OUTER] JOIN <rechte Tabelle> ON <bedingung>;
Für unser Beispiel „Mitarbeiter und Dienstwagen“ sieht das dann so aus:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
RIGHT JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
------- ------------ --------- --- ----------- ---
80001 Schindler Christina 8 DO-WB 428 14
90001 Janssen Bernhard 9 DO-WB 429 14
100001 Grosser Horst 10 DO-WB 4210 14
110001 Eggert Louis 11 DO-WB 4211 14
120001 Carlsen Zacharias 12 DO-WB 4212 14
13 DO-WB 111 16
50002 Braun Christian 14 DO-WB 352 2
50003 Polovic Frantisek 15 DO-WB 353 3
50004 Kalman Aydin 16 DO-WB 354 4
Nanu, dieses Ergebnis hatten wir doch gerade? Bei genauerem Überlegen wird klar: Beim LEFT JOIN gibt es alle Datensätze der linken Tabelle mit Informationen der rechten Tabelle; nun haben wir die beiden Tabellen vertauscht. Beim RIGHT JOIN werden alle Datensätze der rechten Tabelle mit Daten der linken Tabelle verknüpft; das entspricht diesem Beispiel.
Ob wir also die beiden Tabellen vertauschen oder LEFT gegen RIGHT, bleibt sich zwangsläufig gleich. Kurz und „knackig“ formuliert kann man sich also merken:
"A LEFT JOIN B" liefert dasselbe Ergebnis wie "B RIGHT JOIN A".
Bitte überlegen Sie, welches Ergebnis die Vertauschung der beiden Tabellen beim RIGHT JOIN liefert und welche Auswirkung WHERE-Klauseln haben.
FULL OUTER JOIN
BearbeitenDieser JOIN liefert alle Datensätze beider Tabellen, ggf. unter Berücksichtigung der WHERE-Klausel. Wenn Datensätze nach der Verknüpfungsbedingung zusammenpassen, werden sie in einer Zeile angegeben; wo es keinen „Partner“ gibt, wird ein NULL-Wert angezeigt.
SELECT <spaltenliste> FROM <linke Tabelle> FULL [OUTER] JOIN <rechte Tabelle> ON <bedingung>;
Für unser Beispiel sieht das dann so aus:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
FULL JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
-------- ------------ ---------- --- ----------- ---
100001 Grosser Horst 10 DO-WB 4210 14
110001 Eggert Louis 11 DO-WB 4211 14
120001 Carlsen Zacharias 12 DO-WB 4212 14
13 DO-WB 111 16
50002 Braun Christian 14 DO-WB 352 2
50003 Polovic Frantisek 15 DO-WB 353 3
50004 Kalman Aydin 16 DO-WB 354 4
80002 Aliman Zafer 17 DO-WB 382 2
80003 Langer Norbert 18 DO-WB 383 3
80004 Kolic Ivana 19 DO-WB 384 4
10002 Schneider Daniela
20002 Schmitz Michael
30002 Feyerabend Werner
40002 Peters Michael
Auch hier wollen wir wieder die beiden Tabellen vertauschen:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Dienstwagen dw
FULL JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYP
-------- ------------ ---------- --- ----------- ---
80001 Schindler Christina 8 DO-WB 428 14
80002 Aliman Zafer 17 DO-WB 382 2
80003 Langer Norbert 18 DO-WB 383 3
80004 Kolic Ivana 19 DO-WB 384 4
90001 Janssen Bernhard 9 DO-WB 429 14
90002 Hinkel Martina
100001 Grosser Horst 10 DO-WB 4210 14
100002 Friedrichsen Angelina
110001 Eggert Louis 11 DO-WB 4211 14
110002 Deiters Gisela
120001 Carlsen Zacharias 12 DO-WB 4212 14
120002 Baber Yvonne
13 DO-WB 111 16
Bei detailliertem Vergleich des vollständigen Ergebnisses ergibt sich: Es ist gleich, nur in anderer Reihenfolge. Das sollte nicht mehr verwundern.
Verknüpfung mehrerer Tabellen
BearbeitenAlle bisherigen Beispiele kranken daran, dass als Typ des Dienstwagens nur die ID angegeben ist. Selbstverständlich möchte man die Typbezeichnung und den Hersteller lesen. Dazu müssen die beiden Tabellen Fahrzeugtyp und Fahrzeughersteller eingebunden werden. Beim INNER JOIN war das kein Problem; probieren wir aus, wie es beim OUTER JOIN aussehen könnte.
Mehrere Tabellen parallel
BearbeitenErweitern wir dazu die Aufstellung „alle Dienstwagen zusammen mit den zugeordneten Mitarbeitern“ um die Angabe zu den Fahrzeugen.
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,
ft.Bezeichnung as Typ, ft.Hersteller_ID as FheID
FROM Dienstwagen dw
left JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID
join Fahrzeugtyp ft on dw.Fahrzeugtyp_ID = ft.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID
------ ---------- ---------- --- ----------- ----- --------------- -----
100001 Grosser Horst 10 DO-WB 4210 14 A160 6
110001 Eggert Louis 11 DO-WB 4211 14 A160 6
120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6
13 DO-WB 111 16 W211 (E-Klasse) 6
50002 Braun Christian 14 DO-WB 352 2 Golf 1
50003 Polovic Frantisek 15 DO-WB 353 3 Passat 1
50004 Kalman Aydin 16 DO-WB 354 4 Kadett 2
Der zweite JOIN wurde nicht genauer bezeichnet, ist also ein INNER JOIN. Das gleiche Ergebnis erhalten wir, wenn wir die Tabelle Fahrzeugtyp ausdrücklich als LEFT JOIN verknüpfen (bitte selbst ausprobieren!). Anders sieht es beim Versuch mit RIGHT JOIN oder FULL JOIN aus:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,
ft.Bezeichnung as Typ, ft.Hersteller_ID as FheID
FROM Dienstwagen dw
left JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID
right | full join Fahrzeugtyp ft on dw.Fahrzeugtyp_ID = ft.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID
------ ---------- ---------- --- ----------- ----- --------------- -----
80001 Schindler Christina 8 DO-WB 428 14 A160 6
90001 Janssen Bernhard 9 DO-WB 429 14 A160 6
100001 Grosser Horst 10 DO-WB 4210 14 A160 6
110001 Eggert Louis 11 DO-WB 4211 14 A160 6
120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6
W204 (C-Klasse) 6
13 DO-WB 111 16 W211 (E-Klasse) 6
Saab 9-3 8
S40 9
C30 9
Versuchen wir eine Erklärung: Die beiden JOINs stehen sozusagen auf der gleichen Ebene; jede JOIN-Klausel wird für sich mit der Tabelle Dienstwagen verknüpft. An der Verknüpfung zwischen Dienstwagen und Mitarbeiter ändert sich nichts. Aber für die Fahrzeugtypen gilt:
- Das erste Beispiel benutzt einen INNER JOIN, nimmt also für jeden vorhandenen Dienstwagen genau „seinen“ Typ.
- Wenn man stattdessen einen LEFT JOIN verwendet, erhält man alle vorhandenen Dienstwagen, zusammen mit den passenden Typen. Das ist faktisch identisch mit dem Ergebnis des INNER JOIN.
- Das zweite Beispiel benutzt einen RIGHT JOIN, das liefert alle registrierten Fahrzeugtypen und (soweit vorhanden) die passenden Dienstwagen.
- Wenn man stattdessen einen FULL JOIN verwendet, erhält man alle Kombinationen von Dienstwagen und Mitarbeitern, zusammen mit allen registrierten Fahrzeugtypen. Das ist faktisch identisch mit dem Ergebnis des RIGHT JOIN.
Sie sehen: Es kommt genau auf die gewünschten und die tatsächlich vorhandenen Verknüpfungen an.
Gliederung durch Klammern
BearbeitenFür Verknüpfungen, die durch Klammern gegliedert werden, nehmen wir ein anderes Beispiel, nämlich „Mitarbeiter RIGHT JOIN Dienstwagen“, denn die Fahrzeugtypen sind eine Ergänzung zu den Dienstwagen, nicht zu den Mitarbeitern (auch wenn den Abteilungsleitern ein Mercedes zugestanden wird, aber das ist ein anderes Thema und hat nichts mit SQL zu tun).
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,
ft.Bezeichnung as Typ, ft.Hersteller_ID as FheID
FROM Mitarbeiter mi
right JOIN ( Dienstwagen dw
join Fahrzeugtyp ft on ft.ID = dw.Fahrzeugtyp_id )
ON dw.Mitarbeiter_ID = mi.ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID
------- ---------- ---------- --- ----------- ----- --------------- -----
80001 Schindler Christina 8 DO-WB 428 14 A160 6
90001 Janssen Bernhard 9 DO-WB 429 14 A160 6
100001 Grosser Horst 10 DO-WB 4210 14 A160 6
110001 Eggert Louis 11 DO-WB 4211 14 A160 6
120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6
13 DO-WB 111 16 W211 (E-Klasse) 6
50002 Braun Christian 14 DO-WB 352 2 Golf 1
50003 Polovic Frantisek 15 DO-WB 353 3 Passat 1
50004 Kalman Aydin 16 DO-WB 354 4 Kadett 2
Auch hier erhalten wir ein vergleichbares Ergebnis. Prüfen wir zunächst die Abfrage in der Klammer:
- LEFT JOIN und INNER JOIN haben als Grundlage „alle Dienstwagen“, es wird also eine Datenmenge „alle Dienstwagen“ (mit Zusatzinformationen über die Fahrzeugtypen) erstellt.
- RIGHT JOIN und FULL JOIN gehen aus von „alle Fahrzeugtypen“, es wird also eine Datenmenge „alle Fahrzeugtypen“ (mit Zusatzinformationen über die Dienstwagen) erstellt.
Da der Ausdruck innerhalb der Klammern zuerst ausgewertet wird, wird diese Datenmenge anschließend mit den Mitarbeitern verknüpft, soweit es der Verknüpfungsbedingung auf der Basis von dw.Mitarbeiter_ID entspricht.
Mit diesen Erkenntnissen können wir nun auch den Hersteller mit seinem Namen anzeigen; dazu benutzen wir wegen der bisherigen Erkenntnisse das erste Beispiel:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,
ft.Bezeichnung as Typ, fh.Name as Hersteller
FROM Dienstwagen dw
left JOIN Mitarbeiter mi ON mi.ID = dw.Mitarbeiter_ID
inner join Fahrzeugtyp ft on ft.ID = dw.Fahrzeugtyp_ID
inner join Fahrzeughersteller fh on fh.ID = ft.Hersteller_ID;
MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP HERSTELLER
-------- ---------- ---------- --- ------------ ----- --------------- -------------
80001 Schindler Christina 8 DO-WB 428 14 A160 Mercedes-Benz
90001 Janssen Bernhard 9 DO-WB 429 14 A160 Mercedes-Benz
100001 Grosser Horst 10 DO-WB 4210 14 A160 Mercedes-Benz
110001 Eggert Louis 11 DO-WB 4211 14 A160 Mercedes-Benz
120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 Mercedes-Benz
13 DO-WB 111 16 W211 (E-Klasse) Mercedes-Benz
50002 Braun Christian 14 DO-WB 352 2 Golf Volkswagen
50003 Polovic Frantisek 15 DO-WB 353 3 Passat Volkswagen
50004 Kalman Aydin 16 DO-WB 354 4 Kadett Opel
80002 Aliman Zafer 17 DO-WB 382 2 Golf Volkswagen
80003 Langer Norbert 18 DO-WB 383 3 Passat Volkswagen
80004 Kolic Ivana 19 DO-WB 384 4 Kadett Opel
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie die Verwendung von OUTER JOIN kennen:
- Mit dieser Verknüpfung werden auch Datensätze abgefragt und angezeigt, bei denen es in einer der Tabellen keinen zugeordneten Datensatz gibt.
- Mit einem LEFT JOIN erhält man alle Datensätze der linken Tabelle, ergänzt durch passende Angaben aus der rechten Tabelle.
- Mit einem RIGHT JOIN erhält man alle Datensätze der rechten Tabelle, ergänzt durch passende Angaben aus der linken Tabelle.
- Mit einem FULL JOIN erhält man alle Datensätze beider Tabellen, wenn möglich ergänzt durch passende Angaben aus der jeweils anderen Tabelle.
Bei der Verknüpfung mehrerer Tabellen ist genau auf den JOIN-Typ und ggf. auf Klammerung zu achten.
Übungen
Bearbeiten
Übung 1 | Allgemeines | Zur Lösung |
Welche der folgenden Aussagen sind wahr, welche sind falsch?
- Um alle Mitarbeiter mit Dienstwagen aufzulisten, benötigt man einen LEFT OUTER JOIN.
- LEFT JOIN ist nur eine Kurzschreibweise für LEFT OUTER JOIN und hat keine zusätzliche inhaltliche Bedeutung.
- Ein LEFT JOIN von zwei Tabellen enthält alle Zeilen, die nach Auswahlbedingung in der linken Tabelle enthalten sind.
- Ein RIGHT JOIN von zwei Tabellen enthält nur noch diejenigen Zeilen, die nach der Verknüpfungsbedingung in der linken Tabelle enthalten sind.
- Wenn wir bei einer LEFT JOIN-Abfrage mit zwei Tabellen die beiden Tabellen vertauschen und stattdessen einen RIGHT JOIN verwenden, erhalten wir dieselben Zeilen in der Ergebnismenge.
- Wir erhalten dabei nicht nur dieselben Zeilen, sondern auch dieselbe Reihenfolge.
Übung 2 | Allgemeines | Zur Lösung |
Was ist am folgenden SELECT-Befehl falsch und warum? Die Aufgabe dazu lautet:
Gesucht werden Kombinationen von Fahrzeug-Kennzeichen und Fahrzeugtypen, wobei alle Typen aufgeführt werden sollen; es werden nur die ersten 20 Fahrzeuge nach ID benötigt.
select Kennzeichen, Bezeichnung
from Fahrzeug fz
left join Fahrzeugtyp ft on fz.Fahrzeugtyp_ID = ft.ID
where fz.ID <= 20 ;
Übung 3 | Sinnvollen SELECT-Befehl erstellen | Zur Lösung |
Gesucht werden alle registrierten Versicherungsgesellschaften und (soweit vorhanden) deren Kunden mit Name, Vorname.
Übung 4 | Sinnvollen SELECT-Befehl erstellen | Zur Lösung |
Gesucht werden die Dienstwagen, deren Fahrzeugtypen sowie die Hersteller. Die Liste der Typen soll vollständig sein.
Übung 5 | Sinnvollen SELECT-Befehl erstellen | Zur Lösung |
Gesucht werden Kombinationen von Mitarbeitern und ihren Dienstwagen (einschl. Typ). Es geht um die Abteilungen 1 bis 5; auch nicht-persönliche Dienstwagen sollen aufgeführt werden.
Übung 6 | Sinnvollen SELECT-Befehl erstellen | Zur Lösung |
Gesucht werden alle registrierten Versicherungsgesellschaften sowie alle Kunden mit Name, Vorname, soweit der Nachname mit 'S' beginnt.
Übung 7 | RIGHT oder LEFT | Zur Lösung |
Vertauschen Sie in der Lösung von Übung 5 die beiden Tabellen Mitarbeiter und Dienstwagen und erläutern Sie:
- Warum werden jetzt mehr Mitarbeiter angezeigt, und zwar auch solche ohne Dienstwagen?
- Warum fehlt jetzt der „nicht-persönliche“ Dienstwagen?
Übung 8 | SELECT-Befehl mit mehreren Tabellen | Zur Lösung |
Gesucht werden Angaben zu den Mitarbeitern und den Dienstwagen. Beim Mitarbeiter sollen Name und Vorname angegeben werden, bei den Dienstwagen Bezeichnung und Name des Herstellers. Die Liste aller Fahrzeugtypen soll vollständig sein.
Übung 9 | SELECT-Befehl mit mehreren Tabellen | Zur Lösung |
Ergänzen Sie die Lösung zu Übung 8 insofern, dass nur Mitarbeiter der Abteilungen 1 bis 5 angezeigt werden; die Zeilen ohne Mitarbeiter sollen unverändert ausgegeben werden.
Lösung zu Übung 1 | Allgemeines | Zur Übung |
Die Aussagen 2, 3, 5 sind richtig, die Aussagen 1, 4, 6 sind falsch.
Lösung zu Übung 2 | Allgemeines | Zur Übung |
Richtig ist folgender Befehl:
select Kennzeichen, Bezeichnung
from Fahrzeug fz
right join Fahrzeugtyp ft on fz.Fahrzeugtyp_ID = ft.ID
where fz.ID <= 20 or fz.ID is null;
Weil alle Typen aufgeführt werden sollen, wird ein RIGHT JOIN benötigt. Damit auch der Vermerk „es gibt zu einem Typ keine Fahrzeuge“ erscheint, muss die WHERE-Klausel um die IS NULL-Prüfung erweitert werden.
Lösung zu Übung 3 | Sinnvollen SELECT-Befehl erstellen | Zur Übung |
select Bezeichnung, Name, Vorname
from Versicherungsgesellschaft vg
left join Versicherungsnehmer vn on vg.ID = vn.Versicherungsgesellschaft_ID
order by Bezeichnung, Name, Vorname;
Lösung zu Übung 4 | Sinnvollen SELECT-Befehl erstellen | Zur Übung |
select Kennzeichen, Bezeichnung, Name
from Dienstwagen dw
right join Fahrzeugtyp ft on ft.ID = dw.Fahrzeugtyp_ID
inner join Fahrzeughersteller fh on fh.ID = ft.Hersteller_ID
order by Name, Bezeichnung, Kennzeichen;
Lösung zu Übung 5 | Sinnvollen SELECT-Befehl erstellen | Zur Übung |
SELECT mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
RIGHT JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID
where mi.Abteilung_ID <= 5 or mi.ID is null;
Die IS NULL-Prüfung wird wegen der „nicht-persönlichen“ Dienstwagen benötigt.
Lösung zu Übung 6 | Sinnvollen SELECT-Befehl erstellen | Zur Übung |
SELECT Bezeichnung, Name, Vorname
FROM Versicherungsgesellschaft vg
full JOIN Versicherungsnehmer vn ON vg.id = vn.Versicherungsgesellschaft_id
where vn.Name like 'S%' or vn.id is null
ORDER BY Bezeichnung, Name, Vorname;
Lösung zu Übung 7 | RIGHT oder LEFT | Zur Übung |
- Bei einem RIGHT JOIN werden alle Einträge der rechten Tabelle angezeigt. „Rechts“ stehen jetzt die Mitarbeiter, also werden alle Mitarbeiter der betreffenden Abteilungen angezeigt.
- Bei einem RIGHT JOIN werden die Einträge der linken Tabelle nur dann angezeigt, wenn sie zu einem Eintrag der rechten Tabelle gehören. Der „nicht-persönliche“ Dienstwagen aus der linken Tabelle gehört aber zu keinem der Mitarbeiter.
Lösung zu Übung 8 | SELECT-Befehl mit mehreren Tabellen | Zur Übung |
SELECT mi.Name, mi.Vorname,
dw.Kennzeichen, ft.Bezeichnung, fh.Name as HST
FROM Dienstwagen dw
left join Mitarbeiter mi on mi.id = dw.Mitarbeiter_id
right JOIN Fahrzeugtyp ft on ft.Id = dw.Fahrzeugtyp_id
inner join Fahrzeughersteller fh on fh.Id = ft.Hersteller_id;
Hinweise: Die Reihenfolge der JOINs ist nicht eindeutig; der LEFT JOIN kann auch später kommen. Wichtig ist, dass die Verbindung Dienstwagen ↔ Fahrzeugtyp ein RIGHT JOIN ist (oder bei Vertauschung der Tabellen ein LEFT JOIN).
Lösung zu Übung 9 | SELECT-Befehl mit mehreren Tabellen | Zur Übung |
SELECT mi.Name, mi.Vorname,
dw.Kennzeichen, ft.Bezeichnung, fh.Name as HST
FROM Dienstwagen dw
left join Mitarbeiter mi on mi.id = dw.Mitarbeiter_id
right JOIN Fahrzeugtyp ft on ft.Id = dw.Fahrzeugtyp_id
inner join Fahrzeughersteller fh on fh.Id = ft.Hersteller_id
where (mi.Abteilung_id <= 5) or (mi.id is null);
Mehr zu JOIN |
Die folgenden Ergänzungen zu JOIN sind in besonderen Situationen hilfreich.
Welcher JOIN passt wann?
BearbeitenDiese Frage stellt sich vor allem Anfängern sehr oft. Neben den (theoretischen) Überlegungen der vorigen Kapitel helfen oft besondere Beispiele; vielleicht ist dieses nützlich.
Wir haben zwei Tabellen: Paare (also Eltern) und Kinder. Es gibt kinderlose Paare, Paare mit Kindern und Waisenkinder. Wir wollen die Eltern und Kinder in Abfragen verknüpfen; bei den Symbolen steht der linke Kreis für die Tabelle Paare und der rechte Kreis für die Tabelle Kinder.
Tatsächlich sind oft mehrere Wege möglich, wie bereits im letzten Kapitel gesagt wurde: Das Ergebnis für "A LEFT JOIN B" gleicht dem von "B RIGHT JOIN A".
SELF JOIN – Verknüpfung mit sich selbst
BearbeitenSolche Verknüpfungen sind immer dann nötig, wenn Werte einer einzigen Spalte aus verschiedenen Datensätzen verbunden werden. Der JOIN dafür benutzt auf beiden Seiten dieselbe Tabelle <tabelle>; diese beiden „Instanzen“ müssen durch einen Alias unterschieden werden.
SELECT <spaltenliste> FROM <tabelle> t1 JOIN <tabelle> t2 ON <verknüpfung> WHERE <auswahlbedingungen>
Hinweis: Es funktioniert nicht, wenn eine der beiden Instanzen mit Alias und die andere ohne Alias benutzt wird. Dann kommt das DBMS erst recht durcheinander. In einem Forum stand einmal ein solches Problem mit einer sehr vertrackten Ausgabe, bei dem diese Ursache erst nach längerer Diskussion klar wurde.
Dies soll zunächst an zwei Beispielen umgesetzt werden.
Beispiel 1
BearbeitenZeige zu jedem Fahrzeug andere Fahrzeuge aus dem gleichen Kreis.
Wir beschränken uns darauf, dass in einer Zeile jeweils zwei von allen möglichen Kombinationen angezeigt werden, auch wenn viele Angaben wiederholt werden.
select a.Kennzeichen, b.Kennzeichen
from Fahrzeug a
JOIN Fahrzeug b
on SUBSTRING(a.Kennzeichen from 1 for 3)
= SUBSTRING(b.Kennzeichen from 1 for 3)
where a.Kennzeichen < b.Kennzeichen
order by a.Kennzeichen;
KENNZEICHEN KENNZEICHEN1
----------- ------------
BO-GH 102 BO-KL 678
BOR-NO 234 BOR-PQ 567
BOR-NO 234 BOR-RS 890
BOR-PQ 567 BOR-RS 890
GE-AB 123 GE-AC 246
GE-AB 123 GE-EG 892
GE-AC 246 GE-EG 892
RE-CD 456 RE-LM 901
RE-CD 456 RE-LM 902
RE-CD 456 RE-LM 903
Gesucht werden Kombinationen eines Fahrzeugs mit jeweils einem anderen Fahrzeug, wobei die Bedingung „gleicher Kreis“ erfüllt sein soll. Wir brauchen also innerhalb eines SELECT-Befehls zwei Zugriffe auf die Tabelle Fahrzeug mit einer passenden Vergleichsbedingung. (Diese haben wir etwas ungenau formuliert, damit sie nicht unübersichtlich wird.)
Dies ist gleichzeitig ein Beispiel dafür, dass beliebige Bedingungen möglich sind. Überlegen Sie bitte auch, warum unter WHERE die „kleiner als“-Bedingung benutzt wird.
Beispiel 2
BearbeitenZeige zu jedem Fahrzeug mit mehreren Schadensfällen den zeitlichen Abstand von einem Vorfall zum nächsten an.
Wir benötigen für jedes der Fahrzeuge aus der Tabelle Schadensfall zwei Einträge mit dem Datum sowie den Abstand, der einfach als Differenz benutzt wird und somit die Anzahl der Tage anzeigt. Die Fahrzeuge sind freilich erst über die Tabelle Zuordnung_SF_FZ zu finden und müssen zusätzlich verbunden werden. Außerdem sind die Fahrzeuge und die Reihenfolge der Datumsangaben zu kontrollieren.
SELECT fz.ID, fz.Kennzeichen,
sf1.Datum AS Datum1, sf2.Datum AS Datum2, sf2.Datum - sf1.Datum AS Abstand
FROM Zuordnung_SF_FZ zu1
join Zuordnung_SF_FZ zu2 on zu1.Fahrzeug_ID = zu2.Fahrzeug_ID
join Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID
JOIN Schadensfall sf2 on zu2.Schadensfall_ID = sf2.Id
JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID
where sf1.Datum < sf2.Datum
and sf2.Datum = ( SELECT MIN(sf3.Datum)
FROM Schadensfall sf3
join Zuordnung_SF_FZ zu3 on zu3.Schadensfall_ID = sf3.id
WHERE sf1.Datum < sf3.Datum
and zu3.Fahrzeug_ID = zu1.Fahrzeug_ID )
order by fz.ID, Datum1;
ID KENNZEICHEN DATUM1 DATUM2 ABSTAND
-- -------------- ---------- ---------- -------
4 GE-AB 123 03.02.2007 05.10.2008 610
6 HER-EF 789 19.12.2007 21.06.2009 550
7 BO-GH 102 11.07.2007 13.03.2009 611
7 BO-GH 102 13.03.2009 01.08.2009 141
Alternativen bieten die folgenden Lösungen:
SELECT fz.ID, fz.Kennzeichen,
sf1.Datum AS Datum1, sf2.Datum AS Datum2, sf2.Datum - sf1.Datum AS Abstand
FROM Zuordnung_SF_FZ zu1
join Zuordnung_SF_FZ zu2 on zu1.Fahrzeug_ID = zu2.Fahrzeug_ID
join Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID
JOIN Schadensfall sf2 on zu2.Schadensfall_ID = sf2.Id
and sf1.Datum < sf2.Datum
and sf2.Datum = ( SELECT MIN(sf3.Datum)
FROM Schadensfall sf3
join Zuordnung_SF_FZ zu3
on zu3.Schadensfall_ID = sf3.ID
WHERE sf1.Datum < sf3.Datum
and zu3.Fahrzeug_ID = zu1.Fahrzeug_ID )
JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID
order by fz.ID, Datum1;
SELECT fz.ID, fz.Kennzeichen,
sf1.Datum AS Datum1, MIN(sf2.Datum) AS Datum2, MIN(sf2.Datum - sf1.Datum) AS Abstand
FROM Zuordnung_SF_FZ zu1
join Zuordnung_SF_FZ zu2 on zu1.Fahrzeug_ID = zu2.Fahrzeug_ID
join Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID
JOIN Schadensfall sf2 on zu2.Schadensfall_ID = sf2.Id
and sf1.Datum < sf2.Datum
JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID
group by fz.ID, fz.Kennzeichen, sf1.Datum
order by fz.ID, Datum1;
In dieser Aufgabe stecken mehrere Probleme:
- Die Angaben aus der Spalte Datum der Tabelle Schadensfall müssen zweimal geholt werden.
- Zu jedem Schadensfall wird der Eintrag der Tabelle Zuordnung_SF_FZ benötigt, weil die Schadensfälle für jedes Fahrzeug gesucht werden.
- Das Datum, das zu sf1 gehört, muss immer „kleiner“ sein, also früher liegen als das Datum, das zu sf2 gehört.
- Außerdem benötigen wir „irgendwo“ die Einschränkung, dass zum Vergleich nur der jeweils folgende Schadensfall genommen werden darf, also das MINimum der späteren Einträge:
- Die erste Lösung verwendet dafür eine Unterabfrage für eine Auswahlbedingung.
- Die zweite Lösung arbeitet mit einer Unterabfrage bei der Verknüpfungsbedingung.
- Die dritte Lösung benutzt das MINimum direkt als Aggregatfunktion und verlangt „zum Ausgleich“ eine GROUP BY-Klausel.
Die Lösung benötigt deshalb mehrfach verknüpfte Tabellen:
- Als Grundlage wird die Tabelle der Zuordnungen zwischen Schadensfällen und Fahrzeugen zu1 verwendet.
- Hauptverknüpfung ist der Self-Join zu2 auf dieselbe Tabelle, weil nur solche Einträge verknüpft werden sollen, die sich auf dasselbe Fahrzeug beziehen.
- Zu jedem Schadensfall aus zu1 werden die detaillierten Angaben aus sf1 geholt.
- Zu jedem Schadensfall aus zu2 werden die detaillierten Angaben aus sf2 geholt.
- Ergänzend benötigen wir das Kennzeichen des betreffenden Fahrzeugs, also einen JOIN auf Fahrzeug.
- Vor allem müssen die Vergleichsbedingungen für die Datumsangaben eingebaut werden.
Welche Lösung die Datenbank am wenigsten belastet, kann nicht generell gesagt werden, weil es von zu vielen Umständen abhängt.
Erweiterung durch einen OUTER JOIN
BearbeitenBei diesen Lösungen stehen nicht alle Schadensfälle im Ergebnis, weil es nur um den zeitlichen Abstand ging. Wenn beispielsweise auch die Schadenshöhe gewünscht wird, müssen wir dafür sorgen, dass von sf1 oder sf2 alle Einträge angezeigt werden; wir brauchen also einen OUTER JOIN wie zum Beispiel (auf der Grundlage der letzten Version) so:
SELECT fz.ID, fz.Kennzeichen,
sf1.Datum AS Datum1, MIN(sf2.Datum) AS Datum2, MIN(sf2.Datum - sf1.Datum) AS Abstand,
sf1.Schadenshoehe
FROM Zuordnung_SF_FZ zu1
left join Zuordnung_SF_FZ zu2 on zu1.Fahrzeug_ID = zu2.Fahrzeug_ID
left join Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID
left JOIN Schadensfall sf2 on zu2.Schadensfall_ID = sf2.Id
and sf1.Datum < sf2.Datum
left JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID
group by fz.ID, fz.Kennzeichen, sf1.Datum, sf1.Schadenshoehe
order by fz.ID, Datum1;
ID KENNZEICHEN DATUM1 DATUM2 ABSTAND SCHADENSHOEHE
-- --------------- ---------- ---------- ------- -------------
3 RE-LM 903 27.05.2008 1.438,75
4 GE-AB 123 03.02.2007 05.10.2008 610 1.234,50
4 GE-AB 123 05.10.2008 1.983,00
5 RE-CD 456 11.07.2007 2.066,00
6 HER-EF 789 19.12.2007 21.06.2009 550 3.715,60
6 HER-EF 789 21.06.2009 865,00
7 BO-GH 102 11.07.2007 13.03.2009 611 2.066,00
7 BO-GH 102 13.03.2009 01.08.2009 141 4.092,15
7 BO-GH 102 01.08.2009 2.471,50
Wir nehmen es hin, dass dann alle Schadensfälle aufgeführt werden, auch für die Fahrzeuge, die nur einmal „aufgefallen“ sind. Dies ist eine Folge davon, dass Grundlage aller Verknüpfungen die Tabelle der Zuordnungen sein musste.
Bei allen solchen Situationen müssen Sie genau überlegen, wie die verschiedenen Instanzen miteinander verknüpft werden und wie die übrigen Bedingungen einzubinden sind. Oft führen erst mehrere Versuche zum Ziel. Hilfreich sind auch die Ausführungspläne, die ein DBMS anbieten kann.
Weitere Situationen
BearbeitenZum Schluss sollen noch ein paar andere Beispiele erwähnt werden, bei denen ein Self-Join hilft.
- Wenn bei den Dienstwagen die privat gefahrenen Strecken abgerechnet werden sollen, können der km-Stand beim Fahrtantritt und beim Fahrtende in derselben Spalte, aber in getrennten Datensätzen gespeichert werden.
- Doppelte Adressen innerhalb einer Adressendatei können aufgespürt werden (siehe Übung 3).
- Wenn in der Tabelle Mitarbeiter zu einem Mitarbeiter der Leiter der Abteilung gesucht wird, benötigen wir wegen des doppelten Zugriffs auf dieselbe Tabelle ebenfalls einen Self-Join.
CROSS JOIN – das kartesische Produkt
BearbeitenMit dieser speziellen Formulierung kann man deutlich machen, dass man wirklich ein kartesisches Produkt herstellen will und nicht etwa nur die JOIN-Bedingung vergessen hat:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
CROSS JOIN Dienstwagen dw;
Als Ergebnis wird tatsächlich jede Kombination eines Mitarbeiters mit einem Dienstwagen ausgegeben, also n mal m Sätze – wie beim allerersten Versuch im Kapitel Einfache Tabellenverknüpfung. Man kann die Ergebnismenge auch einschränken durch eine WHERE-Klausel:
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
CROSS JOIN Dienstwagen dw
WHERE mi.Name like 'S%' and CHAR_LENGTH(dw.Kennzeichen) = 10;
Hinweis: Die DBMS verhalten sich bei einem CROSS JOIN unterschiedlich; teilweise ist ein CROSS JOIN mit WHERE-Klausel nichts anderes als ein INNER JOIN.
Der Nutzen des CROSS JOIN wird bei unserer sparsamen Beispieldatenbank nicht klar. Unter Oracle wäre folgendes Verfahren möglich und hilfreich:
Kontrollieren Sie mit der Tabelle Fahrzeugbuchung, welche Fahrzeuge am 2.12.2009 im Fuhrpark zur Verfügung stehen.
SELECT mi.Personalnummer AS MitNr,
mi.Name, mi.Vorname,
dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ
FROM Mitarbeiter mi
CROSS JOIN Dienstwagen dw
INNER JOIN Fahrzeugbuchung fb
ON dw.Kennzeichen = fb.Kennzeichen
WHERE fb.Datum = to_date('02.12.2009','dd.mm.yyyy')
AND fb.Status = 'noch nicht gebucht';
WITH – Inline-View
BearbeitenOft kommt es vor, dass man die Daten aus einer Tabelle erst bearbeiten möchte, bevor man sie mit einer anderen Tabelle verknüpft. Beispiel:
SELECT Kuerzel, Bezeichnung, Anzahl_Mitarbeiter
FROM Abteilung
INNER JOIN ( select Abteilung_ID, count(*) AS Anzahl_Mitarbeiter
FROM Mitarbeiter
GROUP BY Abteilung_ID
) MA_Anzahl
ON Abteilung.ID = MA_Anzahl.Abteilung_ID
;
Dabei wird zunächst nach der Tabelle Mitarbeiter die Anzahl der Mitarbeiter für jede Abteilung bestimmt. Das Ergebnis wird wie eine Tabelle MA_Anzahl behandelt und über Abteilung_ID mit der Tabelle Abteilung verknüpft.
Diese Syntax ist ziemlich verschachtelt. Man kann sie auch so schreiben:
WITH MA_Anzahl as
( select Abteilung_ID, count(*) AS Anzahl_Mitarbeiter
FROM Mitarbeiter
GROUP BY Abteilung_ID
)
SELECT Kuerzel, Bezeichnung, Anzahl_Mitarbeiter
FROM Abteilung
INNER JOIN MA_Anzahl
ON Abteilung.ID = MA_Anzahl.Abteilung_ID
;
MA_Anzahl wird benutzt wie eine VIEW, die allerdings nicht permanent angelegt wird, sondern die nur für die Ausführung dieses einen SQL-Befehls gültig ist. Der Unterschied liegt „nur“ darin, dass die Unterabfrage herausgelöst wird und durch WITH als separate temporäre Tabelle eingebunden wird.
Ob man die obere oder die untere Variante besser findet, ist sicher Geschmackssache und hat auch damit zu tun, welche Formulierung man gewöhnt ist. Wenn man sich die SQL-Beispiele im Kapitel Fremdschlüssel-Beziehungen daraufhin ansieht, dann würde eine Formulierung ohne WITH viel länger und komplizierter werden.
Zusammenfassung
BearbeitenIn diesem Kapitel lernten Sie einige weitere Möglichkeiten im Zusammenhang mit JOINs kennen.
- Für bestimmte Anforderungen sind Verknüpfungen einer Tabelle mit sich selbst sinnvoll oder notwendig.
- In diesen Fällen sind die Auswahl- und Verknüpfungsbedingungen besonders sorgfältig zu bestimmen.
- Durch WITH können Verknüpfungen über JOINs übersichtlicher werden.
Übungen
Bearbeiten
Übung 1 | Fragen zum Verständnis | Zur Lösung |
Welche der folgenden Aussagen sind wahr, welche falsch?
- Eine Tabelle kann mit sich selbst verknüpft werden.
- SELF JOIN ist nur ein inhaltlicher Begriff, aber kein SQL-Schlüsselwort.
- Bei einem SELF JOIN sind nur INNER JOINs erlaubt.
- Eine bestimmte Tabelle darf in einem SELF JOIN nur zweimal verwendet werden.
- Für einen SELF JOIN können Tabellen-Aliase benutzt werden, aber sie sind nicht überall erforderlich.
- Ein CROSS JOIN ist eine Verknüpfung zweier Tabellen ohne Verknüpfungsbedingung.
- Bei einem CROSS JOIN darf sich die WHERE-Klausel nicht auf die (rechte) Tabelle des JOINs beziehen.
- Die Schreibweise mit WITH ist kein Sonderfall eines JOINs, sondern eine übersichtlichere Schreibweise, wenn mehrere Tabellen verknüpft werden.
Übung 2 | Verknüpfung einer Tabelle mit sich selbst | Zur Lösung |
Suchen Sie zu jedem Mitarbeiter den Namen und Vornamen des Leiters der Abteilung. Die Abteilungsleiter in unserer einfachen Firmenhierarchie haben keinen Vorgesetzten; sie sollen in der Liste deshalb nicht aufgeführt werden.
Übung 3 | Doppelte Adressen suchen | Zur Lösung |
Suchen Sie Einträge in der Tabelle Versicherungsnehmer, bei denen Name, Vorname, PLZ, Strasse übereinstimmen. Jeweils zwei dieser Adressen sollen mit ihrer ID und den übereinstimmenden Angaben aufgeführt werden.
Hinweis: Benutzen Sie einen JOIN, der sich nicht auf übereinstimmende IDs bezieht.
Lösung zu Übung 1 | Fragen zum Verständnis | Zur Übung |
Die Aussagen 1, 2, 6, 8 sind wahr, die Aussagen 3, 4, 5, 7 sind falsch.
Lösung zu Übung 2 | Verknüpfung einer Tabelle mit sich selbst | Zur Übung |
select mi1.Abteilung_ID as Abt, mi1.Name, mi1.Vorname, mi2.Name as LtrName, mi2.Vorname as LtrVorn
from Mitarbeiter mi1
join Abteilung ab on mi1.Abteilung_ID = ab.ID
join Mitarbeiter mi2 on mi2.Abteilung_ID = ab.ID
where mi2.Ist_Leiter = 'J'
and mi1.Ist_Leiter = 'N'
Lösung zu Übung 3 | Doppelte Adressen suchen | Zur Übung |
select a.Name, a.Vorname, a.PLZ, a.Strasse, a.ID, b.ID
from Versicherungsnehmer a
JOIN Versicherungsnehmer b
on a.Name = b.Name and a.Vorname = b.Vorname
and a.PLZ = b.PLZ and a.Strasse = b.Strasse
where a.ID < b.ID;
Siehe auch
BearbeitenBei Wikipedia finden Sie weitere Erläuterungen:
- Auswertungsplan, auch „Ausführungsplan“ genannt
Nützliche Erweiterungen |
In diesem Kapitel werden verschiedene Erweiterungen des SELECT-Befehls genauer behandelt.
Die Beispiele beziehen sich auch hier auf den Anfangsbestand der Beispieldatenbank; auf die Ausgabe der selektierten Datensätze wird wiederum weitgehend verzichtet. Bitte probieren Sie alle Beispiele aus und nehmen Sie verschiedene Änderungen vor, um die Auswirkungen zu erkennen.
DISTINCT – keine doppelten Einträge
BearbeitenWenn Sie den DISTINCT-Parameter bei einem SELECT-Befehl angeben, erhalten Sie nur eindeutige Ergebnisse:
Bitte beachten Sie: Als „eindeutig“ gilt immer die gesamte Zeile, also alle Spalten zusammen. Die folgende Abfrage liefert alle Datensätze; Fahrzeuge mit mehreren Schadensfällen stehen auch mehrfach in der Liste.
Als Gegenstück gibt es den ALL-Parameter, der ausdrücklich alle Datensätze abfragt. Da dies der Standardwert ist, wird er äußerst selten benutzt:
SELECT all Fahrzeug_ID
from Zuordnung_SF_FZ
Beschränkung auf eine Anzahl Zeilen
BearbeitenHäufig will man nicht sofort das gesamte Ergebnis sehen, sondern nur einen Teil der Zeilen.
Vor allem im Netzwerk kostet es seine Zeit, eine größere Menge von Datensätzen zu übertragen. Es ist deshalb oft praktisch, zunächst einen Teil des Ergebnisses zu holen und anzuzeigen. Während der Anwender sich mit diesem Teilergebnis beschäftigt, wird „im Hintergrund“ der nächste Abschnitt geholt usw.
Im SQL-Standard gibt es dafür (noch) kein Verfahren. Abweichend vom üblichen Vorgehen in diesem Buch erhalten Sie Lösungen für verschiedene DBMS.
Anstelle konstanter Werte (ohne Klammern) kann in allen folgenden Fällen auch ein SQL-Ausdruck (in Klammern) angegeben werden.
Firebird: FIRST SKIP oder ROWS
BearbeitenFirebird bietet gleich zwei Lösungen an, die erste mit FIRST / SKIP:
SELECT [DISTINCT] [ FIRST <value1> ] [ SKIP <value2> ] <select list> FROM ... /* usw. */
Der FIRST-Parameter gibt an, wie viele Zeilen am Anfang anzuzeigen sind; der SKIP-Parameter legt fest, wie viele Zeilen davor übersprungen werden sollen. Beide Parameter werden einzeln oder zusammen benutzt; sie folgen direkt als erste Klausel nach DISTINCT, noch vor der Spaltenliste. Einige Beispiele:
➤ Nur FIRST zeigt die ersten Zeilen.
select FIRST 10
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
➤ Nur SKIP überspringt die ersten Zeilen.
select SKIP 10
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
➤ FIRST zeigt die ersten 10 Zeilen an, aber wegen SKIP werden vorher 5 Zeilen übersprungen.
select FIRST 10 SKIP 5
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
➤ Mit einem Ausdruck kann dafür gesorgt werden, dass etwa das erste Viertel der Datensätze abgerufen wird:
select FIRST ( (SELECT count(*) from Mitarbeiter) / 4 )
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
Die zweite Firebird-Variante benutzt mit ROWS direkt Zeilennummern:
SELECT ... FROM ... WHERE ... ORDER BY ... ROWS <value1> [ TO <value2> ]
Die ROWS-Parameter legen fest, dass (nur) eine bestimmte Anzahl Zeilen angezeigt werden sollen, die durch die Zeilennummern gekennzeichnet sind.
- Wenn nur ROWS benutzt wird, bezeichnet <value1> die Gesamtzahl der angezeigten Zeilen.
- Wenn ROWS zusammen mit TO benutzt wird, ist <value1> die erste Zeilennummer und <value2> die letzte Zeilennummer.
Einige Beispiele:
➤ Ausgabe der Zeilen 10 bis 20 (also insgesamt 11 Zeilen)
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name
ROWS 10 TO 20;
➤ Der erste Datensatz gemäß Sortierung
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name
ROWS 1;
➤ Der letzte Datensatz gemäß Sortierung
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name desc
ROWS 1;
Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird, ist oft sehr nützlich.
Bei einer Interbase-Datenbank sind auch prozentuale Angaben möglich; das ist bei Firebird entfallen.
Microsoft SQL: TOP
BearbeitenDer MS-SQL Server benutzt diese Syntax:
SELECT [DISTINCT] TOP ( <value> ) [PERCENT] [WITH TIES] <select list> FROM ... /* usw. */
Mit den TOP-Parametern wird festgelegt, dass (nur) eine bestimmte Anzahl von Zeilen angezeigt werden sollen, die durch die Zeilennummern gekennzeichnet sind. Diese Parameter folgen direkt als erste Klausel nach DISTINCT, noch vor der Spaltenliste.
- <value> bezeichnet die Gesamtzahl der angezeigten Zeilen. Es wird empfohlen, die Klammern immer zu setzen.
- Wenn TOP zusammen mit PERCENT benutzt wird, handelt es sich dabei um die prozentuale Angabe der Zeilenzahl.
- WITH TIES muss zusammen mit ORDER BY benutzt werden und liefert dann zusätzliche doppelte Zeilen, wenn der letzte Wert nach der Reihenfolge gleich ist den Werten in danach folgenden Zeilen.
Einige Beispiele:
➤ Nur TOP zeigt die ersten Zeilen.
select
TOP 10
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
➤ TOP + PERCENT zeigt z. B. das erste Viertel an.
select
TOP 25 PERCENT
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name;
➤ OFFSET NEXT für einen Teil der Daten (Paging), z.B. Zeile 11-20
select
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;;
➤ Der letzte Datensatz gemäß Sortierung
select
TOP 1
ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name desc;
Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird, ist oft sehr nützlich.
MySQL und PostgreSQL: LIMIT
BearbeitenDiese DBMS benutzen den LIMIT-Parameter. Dieser Parameter folgt nach ORDER BY, wobei die Sortierung nicht angegeben werden muss.
SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT <value1> OFFSET <value2>
Dabei wird mit <value1> angegeben, wie viele Zeilen am Anfang angezeigt werden sollen. Mit <value2> kann nach dem Begriff OFFSET außerdem angegeben werden, wie viele Zeilen davor übersprungen werden sollen.
➤ Es werden die ersten 10 Zeilen angezeigt.
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name
LIMIT 10;
➤ Es werden die ersten 10 Zeilen angezeigt, aber vorher werden 5 Zeilen übersprungen.
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name
LIMIT 10 OFFSET 5;
➤ Der letzte Datensatz gemäß Sortierung
select ID, Name, Vorname, Abteilung_ID as Abt
from Mitarbeiter
order BY Name desc
LIMIT 1;
Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird, ist oft sehr nützlich.
Eine andere Schreibweise für diesen Parameter verzichtet auf das Wort OFFSET:
SELECT ... FROM ... WHERE ORDER BY ... LIMIT [ <value2>, ] <value1>
Bei dieser Variante wird genauso mit <value1> angegeben, wie viele Zeilen am Anfang angezeigt werden sollen. Mit <value2> kann außerdem angegeben werden, wie viele Zeilen davor übersprungen werden sollen; dieser Wert wird jedoch zuerst angegeben und durch ein Komma von der gewünschten Zeilenzahl getrennt.
Die Bedeutung beider Varianten ist identisch, sodass Beispiele nicht nötig sind. Es ist wohl Geschmackssache, welche Version „eingängiger“ ist.
Oracle: ROWNUM
BearbeitenBei dem DBMS Oracle gibt es bei jedem SELECT-Ergebnis eine implizite Spalte Rownum. Man kann diese Spalte mit ausgeben lassen. Solange man kein ORDER BY angibt, ist die Reihenfolge der ausgegebenen Sätze nicht festgelegt. Dieselbe Abfrage kann an einem anderen Tag durchaus eine andere Nummerierung der Sätze hervorbringen. Das liegt z. B. daran, dass jemand die Datensätze in der Zwischenzeit reorganisiert hat.
SELECT Name, rownum
FROM Mitarbeiter;
NAME ROWNUM
------- ------
Müller 1
Schneider 2
Meyer 3
Schmitz 4 /* usw. */
Wenn man diese Spalte Rownum nicht angibt, dann wird sie auch nicht ausgegeben. Ihr Vorteil ist, dass man sie auch bei WHERE verwenden kann:
SELECT Name
FROM Mitarbeiter
WHERE Rownum <= 2;
NAME ROWNUM
------- ------
Müller 1
Schneider 2
Folgende Formulierung funktioniert allerdings nicht, wenn man nur den 10. Satz ausgeben will:
SELECT Name
FROM Mitarbeiter;
WHERE Rownum = 10;
Das liegt daran, dass der Zähler Rownum nur die Zeilen zählt, die auch wirklich ausgegeben werden. Wenn die Tabelle 500 Sätze hat, dann wird bei jedem Satz geprüft, ob Rownum bereits den Wert 10 erreicht hat. Das ist jedoch nie der Fall, da der Zähler immer den Wert 0 behält.
Da muss man schon das DBMS zwingen, die Ergebnismenge mit der Rownum zwischenzuspeichern. Dann geht es:
SELECT Name
FROM ( SELECT Name, rownum R
FROM Mitarbeiter )
WHERE R = 10;
Welcher Satz dabei ausgegeben wird, ist jedoch dem Zufall überlassen.
Wenn man die Ergebnismenge sortiert ausgeben will und dabei nur die ersten 6 Sätze ausgegeben haben will, dann funktioniert die folgende Formulierung nicht:
SELECT Name
FROM Mitarbeiter;
WHERE Rownum <= 6
ORDER BY Name;
Man wird feststellen, dass zwar nur 6 Sätze ausgegeben werden, dass das aber keinesfalls immer die alphabetisch ersten 6 Namen sind. Das liegt daran, dass zunächst WHERE ausgewertet wird und danach erst sortiert wird. Es werden also – wie bei den vorangegangenen Beispielen beschrieben – beliebige 6 Sätze gelesen, und nur diese 6 Sätze werden sortiert.
Für die richtige Lösung muss die Datenbank schon etwas mehr tun. Sie muss zuerst alle vorhandenen Sätze sortieren und dann die ersten 6 Sätze ausgeben:
Sybase: ROWCOUNT
BearbeitenBei diesem DBMS wird zuerst die Anzahl der gewünschten Zeilen angegeben, danach folgt der SELECT-Befehl.
SET rowcount 10;
SELECT Name, rownum
FROM Mitarbeiter;