07.10.2013 Aufrufe

Einführung in SQL - wikimedia.org

Einführung in SQL - wikimedia.org

Einführung in SQL - wikimedia.org

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>E<strong>in</strong>führung</strong> <strong>in</strong> <strong>SQL</strong><br />

Datenbanken bearbeiten<br />

Jürgen Thomas<br />

E<strong>in</strong> Wiki-Buch


Bibliografische Information<br />

Diese Publikation ist bei der Deutschen Nationalbibliothek registriert. Detaillierte<br />

Angaben s<strong>in</strong>d im Internet zu erhalten:<br />

https://portal.d-nb.de/opac.htm?method=showOptions#top<br />

Titel „<strong>E<strong>in</strong>führung</strong> <strong>in</strong> <strong>SQL</strong>“, E<strong>in</strong>schränkung auf Jahr „2011–“.<br />

Namen von Programmen und Produkten sowie weitere technische Angaben s<strong>in</strong>d<br />

häufig geschützt. Da es auch freie Bezeichnungen gibt, wird das Symbol ® nicht<br />

verwendet.<br />

Die Onl<strong>in</strong>e-Version, die PDF-Version und die gedruckte Fassung unterscheiden<br />

sich vor allem wegen unterschiedlicher Seitenformate und technischer Möglichkeiten.<br />

ISBN 978-3-9815260-0-4 (Juli 2012)<br />

Verlag: Jürgen Thomas, DE-13189 Berl<strong>in</strong>, http://www.vs-polis.de/verlag<br />

Diese Publikation ist entstanden bei Wikibooks, e<strong>in</strong>em Projekt der Wikimedia<br />

Foundation für Lehr-, Sach- und Fachbücher unter den Lizenzen Creative Commons<br />

Attribution/Share-Alike (CC-BY-SA) und GFDL.<br />

PDF- und Druckversion s<strong>in</strong>d entstanden mit dem Programm wb2pdf unter GPL.<br />

Dabei wurde das Textsatzprogramm LATEX verwendet, das unter der LPPL steht.<br />

Die Grafik auf dem Buchumschlag wurde unter der Lizenz CC-BY-SA-3.0 selbst<br />

erstellt und ist zu f<strong>in</strong>den unter:<br />

http://de.wikibooks.<strong>org</strong>/wiki/Datei:<strong>SQL</strong>-Titelbild.png<br />

E<strong>in</strong>zelheiten zu den Lizenzen und Quellen stehen im Anhang auf Seite 451.<br />

Druck und Verarbeitung: Conrad Citydruck & Copy GmbH, Uhlandstraße 147,<br />

DE-10719 Berl<strong>in</strong>


Übersicht<br />

1. Vorwort 1<br />

I. <strong>E<strong>in</strong>führung</strong> 3<br />

2. E<strong>in</strong> E<strong>in</strong>stieg 5<br />

3. E<strong>in</strong>leitung 11<br />

4. Relationale Datenbanken 17<br />

5. Normalisierung 25<br />

6. Beispieldatenbank 39<br />

II. Grundlagen 45<br />

7. <strong>SQL</strong>-Befehle 47<br />

8. DML (1) – Daten abfragen 57<br />

9. DML (2) – Daten speichern 69<br />

10. DDL – Struktur der Datenbank 79<br />

11. TCL – Ablaufsteuerung 89<br />

12. DCL – Zugriffsrechte 95<br />

13. Datentypen 97<br />

14. Funktionen 109<br />

III. Mehr zu Abfragen 127<br />

15. Ausführliche SELECT-Struktur 129<br />

16. Funktionen (2) 141<br />

17. WHERE-Klausel im Detail 155<br />

18. Mehrere Tabellen 167<br />

19. E<strong>in</strong>fache Tabellenverknüpfung 173<br />

20. Arbeiten mit JOIN 181<br />

21. OUTER JOIN 191<br />

22. Mehr zu JOIN 203<br />

23. Nützliche Erweiterungen 213<br />

24. Berechnete Spalten 235<br />

25. Gruppierungen 241<br />

26. Unterabfragen 251<br />

27. Erstellen von Views 271<br />

III


Übersicht<br />

IV. Erweiterungen 283<br />

28. DDL – E<strong>in</strong>zelheiten 285<br />

29. Fremdschlüssel-Beziehungen 305<br />

30. <strong>SQL</strong>-Programmierung 323<br />

31. Eigene Funktionen 347<br />

32. Prozeduren 357<br />

33. Trigger 377<br />

34. Tipps und Tricks 389<br />

35. Änderung der Datenbankstruktur 397<br />

36. Testdaten erzeugen 407<br />

V. Anhang 417<br />

A. Tabellenstruktur der Beispieldatenbank 419<br />

B. Downloads 423<br />

C. Befehlsreferenz 431<br />

D. Weitere Informationen 445<br />

E. Zu diesem Buch 451<br />

IV


Inhaltsverzeichnis<br />

1. Vorwort 1<br />

I. <strong>E<strong>in</strong>führung</strong> 3<br />

2. E<strong>in</strong> E<strong>in</strong>stieg 5<br />

2.1. Datenbanken enthalten Informationen . . . . . . . . . . . . . . . 5<br />

2.2. Abfrage nach den Mitarbeitern . . . . . . . . . . . . . . . . . . . . . 6<br />

2.3. Neuaufnahme bei den Mitarbeitern . . . . . . . . . . . . . . . . . . 7<br />

2.4. <strong>SQL</strong> und natürliche Sprache . . . . . . . . . . . . . . . . . . . . . . 8<br />

2.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.6. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

3. E<strong>in</strong>leitung 11<br />

3.1. Geschichte von <strong>SQL</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />

3.2. Übersicht über Datenbankmanagementsysteme . . . . . . . . . 12<br />

3.3. Schreibweisen im Buch . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />

3.4. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />

4. Relationale Datenbanken 17<br />

4.1. Grundstruktur relationaler Datenbanken . . . . . . . . . . . . . . 17<br />

4.2. Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20<br />

4.3. Spalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

4.4. Verknüpfungen und Schlüssel . . . . . . . . . . . . . . . . . . . . . 22<br />

4.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />

5. Normalisierung 25<br />

5.1. Grundgedanken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />

5.2. Die 1. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

5.3. Die 2. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

5.4. Die 3. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

5.5. Zusätzliche Maßnahmen . . . . . . . . . . . . . . . . . . . . . . . . 34<br />

5.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />

5.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

6. Beispieldatenbank 39<br />

6.1. Sachverhalt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

V


Inhaltsverzeichnis<br />

6.2. Schematische Darstellung . . . . . . . . . . . . . . . . . . . . . . . . 41<br />

6.3. Tabellenstruktur und Datenbank . . . . . . . . . . . . . . . . . . . 42<br />

6.4. Anmerkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

II. Grundlagen 45<br />

7. <strong>SQL</strong>-Befehle 47<br />

7.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . . . 48<br />

7.2. DML − Data Manipulation Language . . . . . . . . . . . . . . . . . 49<br />

7.3. DDL − Data Def<strong>in</strong>ition Language . . . . . . . . . . . . . . . . . . . 51<br />

7.4. TCL − Transaction Control Language . . . . . . . . . . . . . . . . . 52<br />

7.5. DCL − Data Control Language . . . . . . . . . . . . . . . . . . . . . 52<br />

7.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

7.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

7.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

8. DML (1) – Daten abfragen 57<br />

8.1. SELECT – Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . 57<br />

8.2. Die e<strong>in</strong>fachsten Abfragen . . . . . . . . . . . . . . . . . . . . . . . . 58<br />

8.3. DISTINCT – Ke<strong>in</strong>e doppelten Zeilen . . . . . . . . . . . . . . . . . 59<br />

8.4. WHERE – E<strong>in</strong>grenzen der Ergebnismenge . . . . . . . . . . . . . . 60<br />

8.5. ORDER BY – Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

8.6. FROM – Mehrere Tabellen verknüpfen . . . . . . . . . . . . . . . . 62<br />

8.7. Ausblick auf komplexe Abfragen . . . . . . . . . . . . . . . . . . . . 64<br />

8.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

8.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

9. DML (2) – Daten speichern 69<br />

9.1. INSERT – Daten e<strong>in</strong>fügen . . . . . . . . . . . . . . . . . . . . . . . . 69<br />

9.2. UPDATE – Daten ändern . . . . . . . . . . . . . . . . . . . . . . . . 72<br />

9.3. DELETE – Daten löschen . . . . . . . . . . . . . . . . . . . . . . . . 74<br />

9.4. TRUNCATE – Tabelle leeren . . . . . . . . . . . . . . . . . . . . . . . 75<br />

9.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />

9.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />

10. DDL – Struktur der Datenbank 79<br />

10.1. Allgeme<strong>in</strong>e Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

10.2. Hauptteile der Datenbank . . . . . . . . . . . . . . . . . . . . . . . . 80<br />

10.3. Ergänzungen zu Tabellen . . . . . . . . . . . . . . . . . . . . . . . . 83<br />

10.4. Programmieren mit <strong>SQL</strong> . . . . . . . . . . . . . . . . . . . . . . . . . 85<br />

10.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85<br />

10.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86<br />

10.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87<br />

VI


Inhaltsverzeichnis<br />

11. TCL – Ablaufsteuerung 89<br />

11.1. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

11.2. Transaktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

11.3. Misserfolg regeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

11.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92<br />

11.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92<br />

12. DCL – Zugriffsrechte 95<br />

13. Datentypen 97<br />

13.1. Vordef<strong>in</strong>ierte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . 97<br />

13.2. Konstruierte und benutzerdef<strong>in</strong>ierte Datentypen . . . . . . . . . 101<br />

13.3. Spezialisierte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . 102<br />

13.4. Nationale und <strong>in</strong>ternationale Zeichensätze . . . . . . . . . . . . . 103<br />

13.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

13.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

13.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

14. Funktionen 109<br />

14.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

14.2. Funktionen für Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

14.3. Funktionen für Zeichenketten . . . . . . . . . . . . . . . . . . . . . 113<br />

14.4. Funktionen für Datums- und Zeitwerte . . . . . . . . . . . . . . . 115<br />

14.5. Funktionen für logische und NULL-Werte . . . . . . . . . . . . . . 116<br />

14.6. Konvertierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117<br />

14.7. Spaltenfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

14.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

14.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

14.10. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />

III. Mehr zu Abfragen 127<br />

15. Ausführliche SELECT-Struktur 129<br />

15.1. Allgeme<strong>in</strong>e Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

15.2. Set Quantifier – Mengenquantifizierer . . . . . . . . . . . . . . . . 130<br />

15.3. Select List – Auswahlliste . . . . . . . . . . . . . . . . . . . . . . . . 130<br />

15.4. Table Reference List − Tabellen-Verweise . . . . . . . . . . . . . . 132<br />

15.5. WHERE-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

15.6. GROUP BY- und HAVING-Klausel . . . . . . . . . . . . . . . . . . . 134<br />

15.7. UNION-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135<br />

15.8. ORDER BY-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

15.9. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

15.10. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

VII


Inhaltsverzeichnis<br />

16. Funktionen (2) 141<br />

16.1. Funktionen für Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

16.2. Funktionen für Zeichenketten . . . . . . . . . . . . . . . . . . . . . 144<br />

16.3. Funktionen für Datums- und Zeitwerte . . . . . . . . . . . . . . . 146<br />

16.4. Funktionen für logische und NULL-Werte . . . . . . . . . . . . . . 148<br />

16.5. Verschiedene Funktionen . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />

17. WHERE-Klausel im Detail 155<br />

17.1. E<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung . . . . . . . . . . . . . . . . . . . . . . . . 155<br />

17.2. Mehrere Bed<strong>in</strong>gungen verknüpfen . . . . . . . . . . . . . . . . . . 160<br />

17.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

17.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

17.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165<br />

18. Mehrere Tabellen 167<br />

18.1. Schreibweisen bei mehreren Tabellen . . . . . . . . . . . . . . . . 167<br />

18.2. Mit Hilfe von WHERE – der traditionelle Weg . . . . . . . . . . . . 168<br />

18.3. JOINs – der moderne Weg . . . . . . . . . . . . . . . . . . . . . . . . 168<br />

18.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169<br />

18.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169<br />

19. E<strong>in</strong>fache Tabellenverknüpfung 173<br />

19.1. Alle Komb<strong>in</strong>ationen aller Datensätze . . . . . . . . . . . . . . . . . 173<br />

19.2. Tabellen e<strong>in</strong>fach verb<strong>in</strong>den . . . . . . . . . . . . . . . . . . . . . . . 174<br />

19.3. Verknüpfungs- und Auswahlbed<strong>in</strong>gungen . . . . . . . . . . . . . . 176<br />

19.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

19.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

19.6. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179<br />

20. Arbeiten mit JOIN 181<br />

20.1. Die Syntax von JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . 181<br />

20.2. INNER JOIN von zwei Tabellen . . . . . . . . . . . . . . . . . . . . . 182<br />

20.3. WHERE-Klausel bei JOINs . . . . . . . . . . . . . . . . . . . . . . . . 183<br />

20.4. INNER JOIN mehrerer Tabellen . . . . . . . . . . . . . . . . . . . . 185<br />

20.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185<br />

20.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186<br />

20.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189<br />

21. OUTER JOIN 191<br />

21.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . . . 191<br />

21.2. LEFT OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

21.3. RIGHT OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . 193<br />

VIII


Inhaltsverzeichnis<br />

21.4. FULL OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />

21.5. Verknüpfung mehrerer Tabellen . . . . . . . . . . . . . . . . . . . . 195<br />

21.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198<br />

21.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199<br />

22. Mehr zu JOIN 203<br />

22.1. Welcher JOIN-Typ passt wann? . . . . . . . . . . . . . . . . . . . . . 203<br />

22.2. SELF JOIN – Verknüpfung mit sich selbst . . . . . . . . . . . . . . 204<br />

22.3. CROSS JOIN – das kartesische Produkt . . . . . . . . . . . . . . . . 209<br />

22.4. WITH – Inl<strong>in</strong>e-View . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210<br />

22.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />

22.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />

22.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212<br />

23. Nützliche Erweiterungen 213<br />

23.1. Beschränkung auf e<strong>in</strong>e Anzahl Zeilen . . . . . . . . . . . . . . . . . 213<br />

23.2. Mehrere Abfragen zusammenfassen . . . . . . . . . . . . . . . . . 220<br />

23.3. CASE WHEN – Fallunterscheidungen . . . . . . . . . . . . . . . . . 223<br />

23.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228<br />

23.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229<br />

24. Berechnete Spalten 235<br />

24.1. Ergebnis von Berechnungen . . . . . . . . . . . . . . . . . . . . . . 236<br />

24.2. Zeichenketten verb<strong>in</strong>den und bearbeiten . . . . . . . . . . . . . . 236<br />

24.3. Ergebnis von Funktionen . . . . . . . . . . . . . . . . . . . . . . . . 237<br />

24.4. Unterabfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

24.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

24.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

25. Gruppierungen 241<br />

25.1. Syntax von GROUP BY . . . . . . . . . . . . . . . . . . . . . . . . . . 241<br />

25.2. Gruppierung bei e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . . . 242<br />

25.3. Gruppierung über mehrere Tabellen . . . . . . . . . . . . . . . . . 243<br />

25.4. Voraussetzungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244<br />

25.5. Erweiterungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />

25.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247<br />

25.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247<br />

25.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250<br />

26. Unterabfragen 251<br />

26.1. Ergebnis als e<strong>in</strong>zelner Wert . . . . . . . . . . . . . . . . . . . . . . . 251<br />

26.2. Ergebnis als Liste mehrerer Werte . . . . . . . . . . . . . . . . . . . 254<br />

26.3. Ergebnis <strong>in</strong> Form e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . . . 256<br />

26.4. Verwendung bei Befehlen zum Speichern . . . . . . . . . . . . . . 258<br />

26.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />

IX


Inhaltsverzeichnis<br />

26.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />

27. Erstellen von Views 271<br />

27.1. E<strong>in</strong>e View anlegen und benutzen . . . . . . . . . . . . . . . . . . . 272<br />

27.2. E<strong>in</strong>e View ändern oder löschen . . . . . . . . . . . . . . . . . . . . 276<br />

27.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277<br />

27.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277<br />

27.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282<br />

IV. Erweiterungen 283<br />

28. DDL – E<strong>in</strong>zelheiten 285<br />

28.1. Def<strong>in</strong>ition e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . 285<br />

28.2. Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte . . . . . . . . . . . . . . . . . . . 287<br />

28.3. Tabelle ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291<br />

28.4. CONSTRAINTs – E<strong>in</strong>schränkungen . . . . . . . . . . . . . . . . . . 293<br />

28.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301<br />

28.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301<br />

28.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304<br />

29. Fremdschlüssel-Beziehungen 305<br />

29.1. Problemstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305<br />

29.2. Grundsätze der Lösung . . . . . . . . . . . . . . . . . . . . . . . . . 306<br />

29.3. Syntax und Optionen . . . . . . . . . . . . . . . . . . . . . . . . . . . 307<br />

29.4. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311<br />

29.5. Komb<strong>in</strong>ation von Fremdschlüsseln . . . . . . . . . . . . . . . . . . 312<br />

29.6. Rekursive Fremdschlüssel . . . . . . . . . . . . . . . . . . . . . . . . 314<br />

29.7. Reihenfolge der Maßnahmen beachten . . . . . . . . . . . . . . . 316<br />

29.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317<br />

29.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318<br />

29.10. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

30. <strong>SQL</strong>-Programmierung 323<br />

30.1. Rout<strong>in</strong>en ohne feste Speicherung . . . . . . . . . . . . . . . . . . . 324<br />

30.2. Programmieren <strong>in</strong>nerhalb von Rout<strong>in</strong>en . . . . . . . . . . . . . . . 325<br />

30.3. <strong>SQL</strong>-Programmierung mit Firebird . . . . . . . . . . . . . . . . . . 326<br />

30.4. <strong>SQL</strong>-Programmierung mit MS-<strong>SQL</strong> . . . . . . . . . . . . . . . . . . 331<br />

30.5. <strong>SQL</strong>-Programmierung mit My<strong>SQL</strong> . . . . . . . . . . . . . . . . . . . 334<br />

30.6. <strong>SQL</strong>-Programmierung mit Oracle . . . . . . . . . . . . . . . . . . . 338<br />

30.7. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342<br />

30.8. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343<br />

30.9. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345<br />

X


Inhaltsverzeichnis<br />

31. Eigene Funktionen 347<br />

31.1. Funktion def<strong>in</strong>ieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347<br />

31.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

31.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351<br />

31.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351<br />

31.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356<br />

32. Prozeduren 357<br />

32.1. Die Def<strong>in</strong>ition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358<br />

32.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360<br />

32.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370<br />

32.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370<br />

32.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375<br />

33. Trigger 377<br />

33.1. Die Def<strong>in</strong>ition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378<br />

33.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379<br />

33.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381<br />

33.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382<br />

33.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386<br />

34. Tipps und Tricks 389<br />

34.1. Die letzte ID abfragen . . . . . . . . . . . . . . . . . . . . . . . . . . 389<br />

34.2. Tabellenstruktur auslesen . . . . . . . . . . . . . . . . . . . . . . . . 391<br />

34.3. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395<br />

35. Änderung der Datenbankstruktur 397<br />

35.1. Spalten h<strong>in</strong>zufügen und ändern . . . . . . . . . . . . . . . . . . . . 397<br />

35.2. E<strong>in</strong>schränkungen auf Spalten . . . . . . . . . . . . . . . . . . . . . 399<br />

35.3. Indizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401<br />

35.4. Fremdschlüssel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402<br />

35.5. Weitere Anpassungen . . . . . . . . . . . . . . . . . . . . . . . . . . 402<br />

35.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405<br />

35.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405<br />

36. Testdaten erzeugen 407<br />

36.1. Neue Fahrzeuge registrieren . . . . . . . . . . . . . . . . . . . . . . 408<br />

36.2. Neue Versicherungsverträge registrieren . . . . . . . . . . . . . . . 408<br />

36.3. Probleme mit Testdaten . . . . . . . . . . . . . . . . . . . . . . . . . 414<br />

36.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415<br />

36.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416<br />

XI


Inhaltsverzeichnis<br />

V. Anhang 417<br />

A. Tabellenstruktur der Beispieldatenbank 419<br />

B. Downloads 423<br />

B.1. Die Download-Seite . . . . . . . . . . . . . . . . . . . . . . . . . . . 423<br />

B.2. Verb<strong>in</strong>dung zu den Datenbanksystemen . . . . . . . . . . . . . . . 424<br />

B.3. Die vollständige Beispieldatenbank . . . . . . . . . . . . . . . . . . 427<br />

B.4. Erstellen der Beispieldatenbank . . . . . . . . . . . . . . . . . . . . 427<br />

B.5. Skripte für nachträgliche Änderungen . . . . . . . . . . . . . . . . 428<br />

C. Befehlsreferenz 431<br />

C.1. DDL (Data Def<strong>in</strong>ition Language) . . . . . . . . . . . . . . . . . . . 431<br />

C.2. DML – Data Manipulation Language . . . . . . . . . . . . . . . . . 435<br />

C.3. TCL – Transaction Control Language . . . . . . . . . . . . . . . . . 438<br />

C.4. DCL – Data Control Language . . . . . . . . . . . . . . . . . . . . . 439<br />

C.5. Schlüsselwörter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440<br />

D. Weitere Informationen 445<br />

D.1. Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . 445<br />

D.2. Webl<strong>in</strong>ks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446<br />

E. Zu diesem Buch 451<br />

E.1. H<strong>in</strong>weise zu den Lizenzen . . . . . . . . . . . . . . . . . . . . . . . . 451<br />

E.2. Autoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452<br />

E.3. Bildnachweis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454<br />

XII


1. Vorwort<br />

Dieses Buch ist im Laufe mehrerer Jahre entstanden mit e<strong>in</strong>er Diskussion darüber,<br />

<strong>in</strong>wieweit die Sprache e<strong>in</strong>er bestimmten <strong>SQL</strong>-Datenbank behandelt werden<br />

sollte oder eher e<strong>in</strong> allgeme<strong>in</strong>er Überblick erforderlich ist. Dies sieht man<br />

dem Buch auch heute noch an; aber die Regel ist klar: Es handelt sich um e<strong>in</strong>e<br />

<strong>E<strong>in</strong>führung</strong> <strong>in</strong> den <strong>SQL</strong>-Standard; die Unterschiede zwischen den <strong>SQL</strong>-Dialekten<br />

(wie man die Unterschiede zwischen den Datenbanksystemen nennt) werden<br />

nur soweit behandelt, wie es unbed<strong>in</strong>gt nötig ist.<br />

Dieses Buch richtet sich an:<br />

• Schüler, Studenten und Andere, die sich mit relationalen Datenbanken beschäftigen<br />

wollen bzw. müssen.<br />

Was dieses Buch erreichen will:<br />

• <strong>E<strong>in</strong>führung</strong> <strong>in</strong> <strong>SQL</strong> anhand e<strong>in</strong>er Beispieldatenbank.<br />

• Der Leser soll die Beispiele anhand von Übungen auf se<strong>in</strong>em eigenen Datenbankmanagementsystem<br />

nachvollziehen können.<br />

Die Schwerpunkte liegen hierbei auf folgenden Themen:<br />

• Abfragen von Daten<br />

• Manipulieren von Daten<br />

• E<strong>in</strong>faches Ändern der Datenbankstruktur<br />

Was dieses Buch nicht se<strong>in</strong> soll:<br />

• Ke<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Grundkonzepte relationaler Datenbankmodelle.<br />

• Ke<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Gestaltung relationaler Datenbanken.<br />

• Ke<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Verwaltung von Datenbankmanagementsystemen.<br />

• Ke<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Verbesserung der Geschw<strong>in</strong>digkeit relationaler Datenbanken.<br />

• Ke<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> prozedurale (z. B. PL/<strong>SQL</strong>) oder objektorientierte Sprachen,<br />

die <strong>in</strong>nerhalb der Datenbank gespeichert und genutzt werden können.<br />

1


Vorwort<br />

Hoffentlich gew<strong>in</strong>nen die Leser mit dieser Darstellung – mit vielen Erläuterungen,<br />

Beispielen und Übungen – e<strong>in</strong>en guten E<strong>in</strong>blick <strong>in</strong> die Möglichkeiten von<br />

<strong>SQL</strong>.<br />

Auch wenn ich mich als Hauptautor bezeichne, wäre dieses Buch nicht so umfangreich<br />

und möglichst hilfreich geworden ohne die Vorarbeit vieler anderer<br />

Autoren bei Wikibooks 1 und die Kontrolle durch Leser und Lernende. Bei all diesen<br />

freundlichen Mitmenschen möchte ich mich herzlich für ihre Beteiligung<br />

bedanken.<br />

Jürgen Thomas Berl<strong>in</strong>, im Juli 2012<br />

1 Siehe die Lizenzh<strong>in</strong>weise auf Seite 451<br />

2


Teil I.<br />

<strong>E<strong>in</strong>führung</strong><br />

3


2. E<strong>in</strong> E<strong>in</strong>stieg<br />

2.1. Datenbanken enthalten Informationen . . . . . . . . . . . . . . 5<br />

2.2. Abfrage nach den Mitarbeitern . . . . . . . . . . . . . . . . . . . 6<br />

2.3. Neuaufnahme bei den Mitarbeitern . . . . . . . . . . . . . . . . 7<br />

2.4. <strong>SQL</strong> und natürliche Sprache . . . . . . . . . . . . . . . . . . . . . 8<br />

2.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />

2.6. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />

Nehmen wir e<strong>in</strong>mal an, dass e<strong>in</strong> Versicherungsunternehmen e<strong>in</strong>en neuen Geschäftsführer<br />

bekommt. Dieser sammelt zunächst Informationen, um sich mit<br />

dem Unternehmen vertraut zu machen.<br />

2.1. Datenbanken enthalten Informationen<br />

Der Geschäftsführer f<strong>in</strong>det <strong>in</strong> se<strong>in</strong>em Büro ke<strong>in</strong>e Akten, sondern nur e<strong>in</strong>en PC.<br />

Nach dem E<strong>in</strong>schalten und Anmelden bekommt er folgende Begrüßung:<br />

Wikibooks-Beispieldatenbank<br />

(C) Wiki-<strong>SQL</strong><br />

Bitte geben Sie e<strong>in</strong>en <strong>SQL</strong>-Befehl e<strong>in</strong>:<br />

sql ><br />

Damit wird der Geschäftsführer auf mehrere Punkte der Organisation <strong>in</strong> se<strong>in</strong>em<br />

Unternehmen h<strong>in</strong>gewiesen.<br />

• Die Daten s<strong>in</strong>d <strong>in</strong> e<strong>in</strong>er Datenbank zusammengefasst mit dem Namen Beispieldatenbank.<br />

• Diese Datenbank (DB) stammt von der Firma Wikibooks.<br />

• Um etwas mit dieser DB zu machen, soll er h<strong>in</strong>ter "sql >" e<strong>in</strong>en <strong>SQL</strong>-Befehl<br />

e<strong>in</strong>geben.<br />

Die Inhalte dieser Datenbank s<strong>in</strong>d im Kapitel Beispieldatenbank beschrieben,<br />

E<strong>in</strong>zelheiten unter Tabellenstruktur der Beispieldatenbank.<br />

5


E<strong>in</strong> E<strong>in</strong>stieg<br />

Das Datenbanksystem (DBMS) der Firma Wikibooks wird als Wiki-<strong>SQL</strong> bezeichnet.<br />

Dies bedeutet, dass die Beispieldatenbank zu Wiki-<strong>SQL</strong> passen muss − <strong>in</strong><br />

Bezug auf das Dateiformat, die <strong>in</strong>terne Struktur und den Aufbau der Befehle.<br />

<strong>SQL</strong> ist die Abkürzung für Structured Query Language, also strukturierte Abfragesprache.<br />

Der Geschäftsführer hat schon e<strong>in</strong>mal davon gehört, dass dies e<strong>in</strong>e<br />

standardisierte Form ist, um mit Datenbanken zu arbeiten, und probiert es aus.<br />

2.2. Abfrage nach den Mitarbeitern<br />

Als erstes möchte er e<strong>in</strong>e Liste se<strong>in</strong>er Mitarbeiter haben. Er überlegt, wie die Abfrage<br />

lauten könnte:<br />

Hole Name und Vorname von den Mitarbeitern<br />

Auf Englisch probiert er (mit e<strong>in</strong> paar Synonymen) also so etwas aus:<br />

Get Name, Vorname FROM Mitarbeiter<br />

Fetch Name, Vorname FROM Mitarbeiter<br />

F<strong>in</strong>d Name, Vorname FROM Mitarbeiter<br />

Search Name, Vorname FROM Mitarbeiter<br />

Und schließlich bekommt er ke<strong>in</strong>e Fehlermeldung, sondern e<strong>in</strong> Ergebnis:<br />

SELECT Name, Vorname FROM Mitarbeiter<br />

Die Liste ist ihm zu lang und unübersichtlich. Er will sie zunächst e<strong>in</strong>mal sortieren<br />

und probiert Sortierung, Reihenfolge aus, bis es passt:<br />

SELECT Name, Vorname FROM Mitarbeiter ORDER BY Name<br />

Dann möchte er die Auswahl e<strong>in</strong>schränken, nämlich auf die Mitarbeiter mit Anfangsbuchstaben<br />

'A'. Wieder nach e<strong>in</strong> paar Versuchen weiß er, dass nicht WITH,<br />

sondern WHERE die Lösung liefert.<br />

SELECT Name, Vorname FROM Mitarbeiter WHERE Name < ’B’<br />

Jetzt möchte er beide Abfragen verb<strong>in</strong>den:<br />

6<br />

Quelltext Falsch<br />

SELECT Name, Vorname FROM Mitarbeiter ORDER BY Name WHERE Name < ’B’


<strong>SQL</strong> error code = -104. Token unknown - l<strong>in</strong>e 1, column 53. WHERE.<br />

Neuaufnahme bei den Mitarbeitern<br />

Das kann doch nicht se<strong>in</strong>?! WHERE ist doch das richtige Verfahren für e<strong>in</strong>e solche<br />

E<strong>in</strong>schränkung?! Kommt es etwa auf die Reihenfolge der Zusätze an?<br />

Quelltext Richtig<br />

SELECT Name, Vorname FROM Mitarbeiter WHERE Name < ’B’ ORDER BY Name<br />

NAME VORNAME<br />

Aagenau Karol<strong>in</strong><br />

Aliman Zafer<br />

Welche Informationen s<strong>in</strong>d denn sonst gespeichert? Er weiß auch (z. B. vom DIR-<br />

Befehl des Betriebssystems), dass e<strong>in</strong> Sternchen anstelle von alles gesetzt werden<br />

kann. Und siehe da, es klappt:<br />

SELECT * FROM Mitarbeiter WHERE Name < ’B’ ORDER BY Name<br />

ID PERSONALNUMMER NAME VORNAME GEBURTSDATUM TELEFON (und noch mehr)<br />

13 60001 Aagenau Karol<strong>in</strong> 02.01.1950 0234/66006001 usw.<br />

18 80002 Aliman Zafer 12.11.1965 0201/4012161 usw.<br />

Prima, damit ist klar, wie Informationen aus der Datenbank geholt werden:<br />

SELECT <br />

FROM <br />

WHERE <br />

ORDER BY <br />

2.3. Neuaufnahme bei den Mitarbeitern<br />

Als nächstes möchte der Geschäftsführer sich selbst als Mitarbeiter speichern.<br />

Schnell kommt er auf das „Grundgerüst“ des Befehls:<br />

INSERT INTO Mitarbeiter VALUES<br />

Wenn er danach se<strong>in</strong>en Namen schreibt, bekommt er wieder e<strong>in</strong>e Fehlermeldung<br />

mit "token unknown". Er hat aber schon von der Benutzung von Klammern<br />

<strong>in</strong> der EDV gehört.<br />

7


E<strong>in</strong> E<strong>in</strong>stieg<br />

Quelltext Falsch<br />

INSERT INTO Mitarbeiter VALUES (’Webern’, ’Anton’)<br />

<strong>SQL</strong> error code = -804.<br />

Count of read-write columns does not equal count of values.<br />

Na gut, dann wird eben ausdrücklich angegeben, dass erstmal nur Name und<br />

Vorname e<strong>in</strong>zutragen s<strong>in</strong>d.<br />

Quelltext Falsch<br />

INSERT INTO Mitarbeiter (Name, Vorname) VALUES (’Webern’, ’Anton’)<br />

validation error for column PERSONALNUMMER, value "*** null ***".<br />

Ach so, die Personalnummer muss angegeben werden, und vermutlich alles andere<br />

auch. Aber die ID ist doch gar nicht bekannt? Nun, immerh<strong>in</strong> s<strong>in</strong>d wir auf<br />

diese Grundstruktur des Befehls gekommen:<br />

INSERT INTO <br />

[ ( ) ]<br />

VALUES ( )<br />

2.4. <strong>SQL</strong> und natürliche Sprache<br />

Offensichtlich s<strong>in</strong>d die Befehle von <strong>SQL</strong> der natürlichen englischen Sprache<br />

nachempfunden. (Englisch hat wegen der klaren Satzstruktur und Grammatik<br />

<strong>in</strong>soweit natürlich Vorteile gegenüber der komplizierten deutschen Syntax.)<br />

SELECT für Abfragen<br />

Um Daten abzufragen, gibt es den SELECT-Befehl mit folgenden Details:<br />

8<br />

SELECT wähle aus<br />

[ DISTINCT | ALL ] verschiedene | alle<br />

<br />

FROM aus<br />

[ WHERE ] wobei<br />

[ GROUP BY ] gruppiert durch<br />

[ HAVING ] wobei<br />

[ ORDER BY ] sortiert durch


INSERT für Neuaufnahmen<br />

Zusammenfassung<br />

Um Daten neu zu speichern, gibt es den INSERT-Befehl mit folgenden Details:<br />

INSERT INTO e<strong>in</strong>fügen <strong>in</strong><br />

[ ]<br />

VALUES ( ) Werte<br />

/* oder */<br />

INSERT INTO e<strong>in</strong>fügen <strong>in</strong><br />

[ ]<br />

SELECT durch e<strong>in</strong>e Auswahl<br />

UPDATE für Änderungen<br />

Um Daten zu ändern, gibt es den UPDATE-Befehl mit folgenden Details:<br />

UPDATE aktualisiere<br />

SET = [ , setze fest<br />

= , usw.<br />

= ]<br />

[ WHERE ]; wobei<br />

DELETE für Löschungen<br />

Um Daten zu löschen, gibt es den DELETE-Befehl mit folgenden Details:<br />

DELETE FROM lösche aus<br />

[ WHERE ]; wobei<br />

CREATE TABLE bei der Struktur e<strong>in</strong>er Tabelle<br />

Um e<strong>in</strong>e neue Tabelle zu erstellen, gibt es den CREATE TABLE-Befehl mit folgenden<br />

E<strong>in</strong>zelheiten:<br />

CREATE TABLE erzeuge Tabelle<br />

( ,<br />

)<br />

So e<strong>in</strong>fach kann es gehen? Dann kann man doch auch eigene Daten erzeugen,<br />

speichern, abfragen und auswerten.<br />

2.5. Zusammenfassung<br />

Die e<strong>in</strong>zelnen Teile der <strong>SQL</strong>-Befehle s<strong>in</strong>d leicht verständlich; und es sche<strong>in</strong>t nur<br />

wenige Befehle zu geben, die man als „Anfänger“ wirklich lernen muss. Natürlich<br />

9


E<strong>in</strong> E<strong>in</strong>stieg<br />

kann man nicht sofort alle Möglichkeiten erfassen. Aber angesichts des begrenzten<br />

Umfangs und der klaren Struktur lohnt es sich, sich näher damit zu befassen.<br />

Dies will dieses Buch erleichtern.<br />

2.6. Siehe auch<br />

Wikipedia hat e<strong>in</strong>en Artikel zum Thema <strong>SQL</strong> 1 .<br />

Weitere Informationen gibt es <strong>in</strong> folgenden Kapiteln:<br />

• Beispieldatenbank 2<br />

• Tabellenstruktur der Beispieldatenbank 3<br />

1 http://de.wikipedia.<strong>org</strong>/wiki/<strong>SQL</strong><br />

2 Kapitel 6 auf Seite 39<br />

3 Anhang A auf Seite 419<br />

10


3. E<strong>in</strong>leitung<br />

3.1. Geschichte von <strong>SQL</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . 12<br />

3.2. Übersicht über Datenbankmanagementsysteme . . . . . . . . 12<br />

3.3. Schreibweisen im Buch . . . . . . . . . . . . . . . . . . . . . . . . 14<br />

3.4. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15<br />

Dieses Kapitel gibt Informationen über Inhalt und Aufbau des Buches.<br />

Die Abfragesprache <strong>SQL</strong> ist die etablierte Sprache für die Arbeit mit relationalen<br />

Datenbankmanagementsystemen (DBMS). Es existieren verschiedene Standards,<br />

und jeder Hersteller von DBMS hat se<strong>in</strong>e eigenen Erweiterungen und Besonderheiten<br />

zu den Standards.<br />

Das Buch soll e<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Sprache <strong>SQL</strong> bieten. Ziel ist es, dass der<br />

Leser nach dem Durcharbeiten folgende Aufgaben selbständig lösen kann:<br />

• Eigene Abfragen für relationale Datenbanken erstellen<br />

• Manipulieren von Daten (E<strong>in</strong>fügen, Ändern und Löschen)<br />

• Eigene e<strong>in</strong>fache relationale Datenbanken aufbauen<br />

• Bestehende Datenbankstrukturen erweitern<br />

Um die Ziele zu erreichen, wird <strong>SQL</strong> anhand praxisnaher Beispiele erläutert.<br />

Die Beispiele im Buch wurden unter m<strong>in</strong>destens e<strong>in</strong>em der folgenden DBMS getestet:<br />

• Firebird<br />

• MS-<strong>SQL</strong> Server 2005 oder 2008<br />

• My<strong>SQL</strong> 4.1 oder 5<br />

• Oracle 9, 10 oder 11<br />

Vorzugsweise werden allgeme<strong>in</strong>gültige Schreibweisen nach dem <strong>SQL</strong>-Standard<br />

(siehe unten) benutzt. Deshalb sollten die Befehle <strong>in</strong> aller Regel auf allen<br />

gängigen DBMS funktionieren und höchstens kle<strong>in</strong>ere Änderungen benötigen.<br />

Dort, wo e<strong>in</strong>e Schreibweise wesentlich abweicht, wird das ausdrücklich erwähnt.<br />

11


E<strong>in</strong>leitung<br />

i Achtung<br />

Wegen der unterschiedlichen Schreibweisen der <strong>SQL</strong>-Befehle ist e<strong>in</strong>e vollständige<br />

Prüfung leider nicht möglich. Sie müssen <strong>in</strong> allen Zweifelsfällen <strong>in</strong><br />

der Dokumentation Ihres DBMS die passende Schreibweise nachlesen.<br />

3.1. Geschichte von <strong>SQL</strong><br />

<strong>SQL</strong> ist aus IBM’s SEQUEL <strong>in</strong> den siebziger Jahren entstanden. Der Erfolg der<br />

Sprache <strong>SQL</strong> liegt sicherlich auch dar<strong>in</strong>, dass sie e<strong>in</strong>fach aufgebaut ist und sich<br />

an der englischen Umgangssprache orientiert. Es gibt verschiedene Standards:<br />

• Erster <strong>SQL</strong>-Standard (1986 ANSI)<br />

• <strong>SQL</strong>2 bzw. <strong>SQL</strong>-92 (1992)<br />

• <strong>SQL</strong>3 bzw. <strong>SQL</strong>:1999 (1999 ISO)<br />

• <strong>SQL</strong>:2003 (2003)<br />

• <strong>SQL</strong>:2008 (2008)<br />

Hierbei ist zu beachten, dass die meisten Datenbankmanagementsysteme <strong>SQL</strong>2<br />

unterstützen. Die neueren Versionen s<strong>in</strong>d <strong>in</strong> der Regel nur teilweise oder gar<br />

nicht <strong>in</strong> den e<strong>in</strong>zelnen Datenbankmanagementsystemen umgesetzt.<br />

Alles, was <strong>in</strong> diesem Buch als <strong>SQL</strong>-Standard, also als „offizielle <strong>SQL</strong>-<br />

Feststellung“ angegeben wird, bezieht sich auf die <strong>SQL</strong>-Dokumente von 2003.<br />

Diese Version ist zum Lernen nicht veraltet. Viele Elemente s<strong>in</strong>d nach wie vor<br />

nicht überall verwirklicht. Aber die Grundlagen, die <strong>in</strong> diesem Buch behandelt<br />

werden, s<strong>in</strong>d unverändert die Grundlagen beim Verständnis von <strong>SQL</strong>.<br />

3.2. Übersicht über Datenbankmanagementsysteme<br />

3.2.1. Allgeme<strong>in</strong><br />

Datenbanken s<strong>in</strong>d Systeme (Daten und Programme) zur Verwaltung von Daten.<br />

Es gibt verschiedene Konzepte:<br />

• Relationale DBMS<br />

• Objektrelationale DBMS<br />

• Objektorientierte DBMS<br />

12


Übersicht über Datenbankmanagementsysteme<br />

Bei Wikipedia gibt es e<strong>in</strong>e Liste der Datenbankmanagementsysteme.<br />

Da <strong>SQL</strong> die Abfragesprache für relationale Datenbanken ist, bezieht sich das<br />

Buch nur auf diese Art von Datenbanken. Das Konzept h<strong>in</strong>ter den relationalen<br />

Datenbanken wird im nächsten Kapitel erläutert.<br />

3.2.2. Kommerzielle Datenbankmanagementsysteme<br />

• DB2<br />

• Informix<br />

• Interbase<br />

• Microsoft <strong>SQL</strong> Server<br />

• Oracle<br />

• Sybase<br />

Microsoft und Oracle bieten auch kostenlose Express-Versionen mit e<strong>in</strong>geschränkten<br />

Möglichkeiten oder Nutzungsrechten an.<br />

3.2.3. Freie Datenbankmanagementsysteme<br />

• Firebird<br />

• My<strong>SQL</strong><br />

• Postgre<strong>SQL</strong><br />

• <strong>SQL</strong>ite<br />

Bei My<strong>SQL</strong> ist das duale Lizenzsystem zu beachten: je nach Nutzungsbed<strong>in</strong>gungen<br />

frei oder kostenpflichtig.<br />

Die Unterscheidung zwischen „frei“ und „kommerziell“ ist nicht korrekt.<br />

Bei den „freien“ DBMS steht die freie Verfügbarkeit im Vordergrund,<br />

auch wenn Kosten anfallen oder es nicht als Open Source-<br />

Projekt entwickelt wird. Bei den „kommerziellen“ DBMS steht das<br />

gewerbliche Interesse des Anbieters im Vordergrund, auch wenn es<br />

kostenlose Lizenzen gibt.<br />

3.2.4. Weitere Systeme zur Datenverwaltung<br />

Die folgenden Dateisysteme enthalten ke<strong>in</strong>e Datenbanken im eigentlichen S<strong>in</strong>ne,<br />

sondern Dateien für strukturierte Daten. Auch diese können (je nach verwendetem<br />

Programm) <strong>in</strong> e<strong>in</strong>geschränktem Umfang mit <strong>SQL</strong>-Befehlen umgehen.<br />

• dBASE und se<strong>in</strong>e Varianten<br />

13


E<strong>in</strong>leitung<br />

• MS-Access<br />

• das Datenbankmodul Base von LibreOffice (OpenOffice.<strong>org</strong>)<br />

• Paradox<br />

Auf diese Systeme gehen wir nicht e<strong>in</strong>. Sie müssen <strong>in</strong> der jeweiligen Programm-<br />

Dokumentation nachlesen, welche Befehle und Optionen möglich s<strong>in</strong>d.<br />

3.3. Schreibweisen im Buch<br />

Das Buch ist grundsätzlich schrittweise aufgebaut. Aber nicht immer können <strong>in</strong><br />

e<strong>in</strong>em Beispiel nur bereits bekannte Begriffe verwendet werden. Wenn Bestandteile<br />

erst <strong>in</strong> e<strong>in</strong>em späteren Kapitel erläutert werden, dann gibt es ausdrückliche<br />

H<strong>in</strong>weise, beispielsweise hier:<br />

• Der INSERT-Befehl <strong>in</strong> DML (2) − Daten speichern 1 muss korrekt mit Datentypen<br />

2 umgehen und benutzt dazu auch Funktionen 3 .<br />

• Auch für das Erstellen e<strong>in</strong>er Tabelle <strong>in</strong> DDL − Struktur der Datenbank 4 muss<br />

genau auf die Datentypen geachtet werden.<br />

Wenn Sie die <strong>SQL</strong>-Begriffe aus dem Englischen <strong>in</strong>s Deutsche übersetzen, sollten<br />

Sie den Zusammenhang auch ohne H<strong>in</strong>- und Herblättern verstehen.<br />

Das Wort Statement bezeichnet e<strong>in</strong>en <strong>SQL</strong>-Befehl, manchmal auch den Teil e<strong>in</strong>es<br />

Befehls.<br />

Die Beispiele für <strong>SQL</strong>-Befehle werden nach den folgenden Regeln geschrieben.<br />

1. Alle <strong>SQL</strong>-Befehle und Schlüsselwörter, wie z. B. SELECT, INSERT, DELETE,<br />

WHERE, ORDER BY, werden vorzugsweise groß geschrieben. <strong>SQL</strong> selbst<br />

verlangt das nicht, sondern arbeitet ohne Berücksichtigung von Groß- und<br />

Kle<strong>in</strong>schreibung (case <strong>in</strong>sensitive); dort werden select, Select und sogar<br />

sElEcT gleich behandelt. 5<br />

2. Eigentlich sollten Tabellen- und Spaltennamen vorzugsweise ebenfalls<br />

groß geschrieben werden, und zwar ohne Anführungszeichen. Aber <strong>in</strong> der<br />

Praxis werden solche Namen meistens „gemischt“ geschrieben.<br />

3. Str<strong>in</strong>g-Literale werden mit e<strong>in</strong>fachen Anführungszeichen gekennzeichnet.<br />

Bitte nicht wundern: Manche DBMS geben für Namen oder Literale andere<br />

Regeln zu den Anführungszeichen vor.<br />

1 Kapitel 9 auf Seite 69<br />

2 Kapitel 13 auf Seite 97<br />

3 Kapitel 14 auf Seite 109<br />

4 Kapitel 10 auf Seite 79<br />

5 Im Buch verh<strong>in</strong>dern zurzeit technische E<strong>in</strong>schränkungen, dass alle Begriffe automatisch großgeschrieben<br />

werden. Wir haben uns aber durchgehend um Großschreibung bemüht.<br />

14


4. <strong>SQL</strong>-Befehle werden mit e<strong>in</strong>em Semikolon abgeschlossen.<br />

Siehe auch<br />

5. Optionale Argumente (d. h. solche, die nicht unbed<strong>in</strong>gt erforderlich s<strong>in</strong>d)<br />

werden <strong>in</strong> [ ] e<strong>in</strong>geschlossen.<br />

6. Variable Argumente (d. h. solche, die mit unterschiedlichem Inhalt vorkommen)<br />

werden <strong>in</strong> < > e<strong>in</strong>geschlossen.<br />

7. Wahlmöglichkeiten werden durch das Pipe-Zeichen | (den senkrechten<br />

Strich) getrennt.<br />

8. Listen werden gekennzeichnet durch , wobei dies e<strong>in</strong>e Kurzform<br />

ist für .<br />

9. Sofern das Ergebnis e<strong>in</strong>er Abfrage im Ausgabefenster aufgeführt wird, handelt<br />

es sich überwiegend nur um e<strong>in</strong>en Teil des Ergebnisses, gleichgültig ob<br />

darauf h<strong>in</strong>gewiesen wird oder nicht.<br />

Die Struktur e<strong>in</strong>es Befehls steht <strong>in</strong> e<strong>in</strong>em Rahmen mit Courier-Schrift:<br />

SELECT <br />

FROM <br />

[ WHERE ]<br />

;<br />

Aufgabe: So wird e<strong>in</strong>e Aufgabenstellung angezeigt, die mit dem danach folgenden<br />

Beispiel erledigt werden soll.<br />

E<strong>in</strong> konkretes Beispiel wird mit e<strong>in</strong>em komplexen Rahmen und unterschiedlichen<br />

Inhalten (zusätzlicher H<strong>in</strong>weis bei Fehlern, mit oder ohne Kopf- und Fußzeile,<br />

mit oder ohne Ausgabefenster) dargestellt:<br />

Quelltext Falsch<br />

SELECT * FROM Beispieltabelle<br />

WHERE Spalte1 = ’Abc’;<br />

Hier steht ggf. e<strong>in</strong>e Meldung.<br />

3.4. Siehe auch<br />

Unter Webl<strong>in</strong>ks 6 stehen viele zusätzliche H<strong>in</strong>weise.<br />

MoWeS 7 bietet e<strong>in</strong>e kostenlose PHP-My<strong>SQL</strong>-Umgebung für den USB-Stick, die<br />

sich gut eignet, My<strong>SQL</strong> zu lernen und zu testen.<br />

6 Anhang D auf Seite 445<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/MoWeS<br />

15


4. Relationale Datenbanken<br />

4.1. Grundstruktur relationaler Datenbanken . . . . . . . . . . . . 17<br />

4.2. Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20<br />

4.3. Spalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />

4.4. Verknüpfungen und Schlüssel . . . . . . . . . . . . . . . . . . . . 22<br />

4.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />

Um mit <strong>SQL</strong> auf relationalen Datenbanken zu arbeiten, muss der Anwender e<strong>in</strong><br />

Grundverständnis dafür haben. Dieses soll hier vermittelt werden.<br />

4.1. Grundstruktur relationaler Datenbanken<br />

Bevor man mit der Sprache <strong>SQL</strong> beg<strong>in</strong>nt, muss das Grundpr<strong>in</strong>zip relationaler<br />

Datenbanken geklärt werden. Diese versuchen, e<strong>in</strong>en Bestandteil der Realität <strong>in</strong><br />

e<strong>in</strong>em Datenmodell abzubilden. Für diese Datenmodelle gibt es verschiedene<br />

Abstraktionsebenen. In der Regel unterscheidet man zwischen Entitätenmodell<br />

und Tabellenmodell. Da es sich hier um e<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> handelt, beschränken<br />

wir uns auf das Tabellenmodell, das weniger Theorie voraussetzt.<br />

Grundsätzlich sollen Objekte der Realität betrachtet werden, welche zue<strong>in</strong>ander<br />

<strong>in</strong> Beziehung stehen. Zum e<strong>in</strong>en werden die Objekte mit ihren Eigenschaften untersucht:<br />

Objekte mit gleichen Eigenschaften werden zusammengefasst; Objekte<br />

mit verschiedenen Eigenschaften werden getrennt. Zum anderen werden die<br />

Beziehungen zwischen unterschiedlichen Objekten behandelt. Außerdem geht<br />

es darum, möglichst ke<strong>in</strong>e Informationen unnötigerweise doppelt zu speichern.<br />

4.1.1. Beispielhafte Struktur<br />

In unserer Beispieldatenbank 1 simulieren wir dazu e<strong>in</strong>e Versicherungsgesellschaft.<br />

Unter anderem werden die Verträge mit den dazugehörigen Kunden betrachtet:<br />

1 Kapitel 6 auf Seite 39<br />

17


Relationale Datenbanken<br />

• E<strong>in</strong> Versicherungsvertrag ist durch die Vertragsnummer, das Datum des Abschlusses,<br />

den Versicherungsnehmer, die Art und die Höhe der Police gekennzeichnet.<br />

• E<strong>in</strong> Versicherungsnehmer kann bei der Versicherung e<strong>in</strong>en oder mehrere Verträge<br />

haben. Es kann Kunden geben, die aktuell ke<strong>in</strong>en Vertrag haben; aber es<br />

kann ke<strong>in</strong>en Vertrag ohne zugehörigen Kunden geben.<br />

• E<strong>in</strong> Versicherungsnehmer ist gekennzeichnet durch se<strong>in</strong>en Namen und Anschrift<br />

und bei Personen e<strong>in</strong>en Vornamen und e<strong>in</strong> Geburtsdatum. Außerdem<br />

verfügt er „üblicherweise“ über e<strong>in</strong>e Kundennummer, die ihn e<strong>in</strong>deutig kennzeichnet.<br />

Nun könnte man alle Verträge wie folgt <strong>in</strong> e<strong>in</strong>er e<strong>in</strong>zigen Datei, z. B. e<strong>in</strong>em<br />

Arbeitsblatt e<strong>in</strong>er Tabellenkalkulation, speichern:<br />

NUMMER ABSCHLUSSDATUM ART NAME ANSCHRIFT<br />

BETREUER TELEFON<br />

DG-01 03.05.1974 TK Heckel Obsthandel GmbH 46282 Dorsten<br />

Pohl, Helmut 0201/4014186 Mobil (0171) 4123456<br />

DG-02 11.06.1975 TK Heckel Obsthandel GmbH 46282 Dorsten<br />

Pohl, Helmut 0201/4014186 Mobil (0171) 4123456<br />

DG-03 25.02.1977 TK Heckel Obsthandel GmbH 46282 Dorsten<br />

Pohl, Helmut 0201/4014186 Mobil (0171) 4123456<br />

XC-01 07.08.1974 HP Antonius, Bernhard 45892 Gelsenkirchen<br />

Braun, Christian 0201/4014726 Mobil (0170) 8351647<br />

RH-01 11.12.1976 VK Cornelsen, Dorothea 44577 Castrop-Rauxel<br />

Braun, Christian 0201/4014726 Mobil (0170) 8351647<br />

Dies ist offensichtlich unübersichtlich. Auch werden die persönlichen Daten<br />

e<strong>in</strong>es Versicherungsnehmers und se<strong>in</strong>es Betreuers „zu oft“ gespeichert. Es ist<br />

also s<strong>in</strong>nvoll, dies zu trennen − zum e<strong>in</strong>en die Verträge:<br />

NUMMER ABSCHLUSSDATUM ART KUNDE BETREUER<br />

DG-01 03.05.1974 TK 1 9<br />

DG-02 11.06.1975 TK 1 9<br />

DG-03 25.02.1977 TK 1 9<br />

XC-01 07.08.1974 HP 2 10<br />

RH-01 11.12.1976 VK 3 10<br />

Zum anderen die Kunden:<br />

18<br />

NUMMER NAME ANSCHRIFT<br />

1 Heckel Obsthandel GmbH 46282 Dorsten<br />

2 Antonius, Bernhard 45892 Gelsenkirchen<br />

3 Cornelsen, Dorothea 44577 Castrop-Rauxel


Und schließlich die zuständigen Sachbearbeiter (Betreuer):<br />

NUMMER NAME TELEFON MOBIL<br />

9 Pohl, Helmut 0201/4014186 (0171) 4123456<br />

10 Braun, Christian 0201/4014726 (0170) 8351647<br />

Grundstruktur relationaler Datenbanken<br />

Durch die Angabe der Nummer (Kunde bzw. Betreuer) <strong>in</strong> den Aufstellungen ist<br />

e<strong>in</strong>e klare Verb<strong>in</strong>dung hergestellt. Außerdem zeigt die Wiederholung des Wortes<br />

„Mobil“ an, dass dieser Wert <strong>in</strong> e<strong>in</strong>er eigenen Spalte e<strong>in</strong>getragen werden sollte.<br />

Diese Trennung der Daten erfolgt bei der Normalisierung 2 e<strong>in</strong>er Datenbank.<br />

4.1.2. Eigenschaften der Objekte<br />

Vor allem müssen wir über die Eigenschaften der verschiedene Objekte nachdenken.<br />

Es gibt solche, die e<strong>in</strong> Objekt e<strong>in</strong>deutig kennzeichnen, andere, die immer<br />

anzugeben s<strong>in</strong>d, und weitere, die nur manchmal wichtig s<strong>in</strong>d.<br />

Für e<strong>in</strong>en Versicherungsnehmer gibt es u. a. folgende Eigenschaften:<br />

• NUMMER ist e<strong>in</strong>deutig und e<strong>in</strong>e Pflichtangabe.<br />

• NAME, PLZ, ORT s<strong>in</strong>d Pflichtangaben, ihre Inhalte können aber bei mehreren<br />

Versicherungsnehmern vorkommen.<br />

• VORNAME und GEBURTSDATUM s<strong>in</strong>d bei natürlichen Personen Pflicht, aber<br />

bei juristischen Personen (Firmen) irrelevant.<br />

Für e<strong>in</strong>en Versicherungsvertrag gibt es u. a. folgende Eigenschaften:<br />

• NUMMER ist e<strong>in</strong>deutig und e<strong>in</strong>e Pflichtangabe.<br />

• Auch die anderen bisher genannten Eigenschaften s<strong>in</strong>d Pflicht, aber sie s<strong>in</strong>d<br />

nicht e<strong>in</strong>deutig.<br />

Die verschiedenen Objekte stehen über die Kundennummer mite<strong>in</strong>ander <strong>in</strong><br />

Beziehung. Im Beispiel geht es um die Verknüpfung: „E<strong>in</strong> Kunde kann e<strong>in</strong>en<br />

oder mehrere Verträge oder auch ke<strong>in</strong>en haben.“ Der letzte Fall „ke<strong>in</strong>en Vertrag“<br />

kommt erst am Schluss des Buches vor, wenn wir weitere Testdaten erzeugen 3 .<br />

In e<strong>in</strong>em relationalen Datenbanksystem (DBMS) werden die Objekte als Tabellen<br />

dargestellt. Die Eigenschaften werden über die Spalten der Tabelle abgebildet.<br />

E<strong>in</strong>e Zeile (wahlweise als Datensatz bezeichnet) <strong>in</strong> der Tabelle entspricht<br />

genau e<strong>in</strong>em Objekt <strong>in</strong> der Realität. Die Beziehungen zwischen Tabellen werden<br />

über Fremdschlüssel abgebildet.<br />

2 Kapitel 5 auf Seite 25<br />

3 Kapitel 36 auf Seite 407<br />

19


Relationale Datenbanken<br />

4.2. Tabellen<br />

Tabellen s<strong>in</strong>d zweidimensional gegliederte Informationen. Die Tabelle selbst hat<br />

e<strong>in</strong>en Namen. Anzahl, Bezeichnung und Typ der Spalten (auch Felder oder Attribute<br />

genannt) werden durch die Tabelle def<strong>in</strong>iert. Die Zeilen (Anzahl und Inhalte)<br />

s<strong>in</strong>d variabel und entsprechen jeweils e<strong>in</strong>em wirklichen Objekt des Typs, der<br />

<strong>in</strong> der Tabelle gesammelt wird.<br />

So sieht e<strong>in</strong> Ausschnitt aus der Tabelle Abteilung der Beispieldatenbank aus:<br />

Spaltenname ID KURZBEZEICHNUNG BEZEICHNUNG ORT<br />

Datentyp <strong>in</strong>teger varchar(10) varchar(30) varchar(30)<br />

Zeilen 1 Fibu F<strong>in</strong>anzbuchhaltung Dortmund<br />

2 Albu Anlagenbuchhaltung Dortmund<br />

5 Vert Vertrieb Essen<br />

6 Lagh Lagerhaltung Bochum<br />

Sie enthält also 4 Spalten und 12 Zeilen, von denen hier 4 angezeigt werden.<br />

Dabei handelt es sich um e<strong>in</strong>e Basistabelle (TABLE), die tatsächlich Informationen<br />

speichert. Daneben gibt es „virtuelle“ Arten von Tabellen, nämlich die VIEW<br />

als Sichttabelle und die Ergebnismenge (Resultset) als Ergebnis e<strong>in</strong>er SELECT-<br />

Abfrage.<br />

E<strong>in</strong>e View enthält e<strong>in</strong>e fest vordef<strong>in</strong>ierte Abfrage, die sich auf e<strong>in</strong>e oder mehrere<br />

Tabellen bezieht. Aus Sicht des Anwenders sieht sie wie e<strong>in</strong>e Basistabelle aus,<br />

ist aber nur e<strong>in</strong>e Abbildung realer Tabellen. E<strong>in</strong> Beispiel wäre e<strong>in</strong> Ausschnitt aus<br />

e<strong>in</strong>er View Mitarbeiter_Bochum, nämlich der Mitarbeiter, die zu e<strong>in</strong>er der Abteilungen<br />

<strong>in</strong> Bochum gehören:<br />

PERSNR NAME VORNAME BEZEICHNUNG<br />

60001 Aagenau Karol<strong>in</strong> Lagerhaltung<br />

60002 P<strong>in</strong>kart Petra Lagerhaltung<br />

70001 Olschewski Pjotr Produktion<br />

70002 Nordmann Jörg Produktion<br />

120001 Carlsen Zacharias Forschung und Entwicklung<br />

120002 Baber Yvonne Forschung und Entwicklung<br />

Näheres zu Sichttabellen steht im Kapitel Erstellen von Views 4 .<br />

Jede Ergebnismenge hat zwangsläufig die Struktur e<strong>in</strong>er Tabelle.<br />

Ergänzend sei darauf h<strong>in</strong>gewiesen, dass auch das DBMS selbst sämtliche Schemata<br />

<strong>in</strong> Systemtabellen speichert. Beispielsweise stehen bei Interbase und Firebird<br />

die Def<strong>in</strong>ition von TABLEs und VIEWs <strong>in</strong> der Tabelle RDB$RELATIONS und<br />

die dazugehörigen Felder (Spalten) <strong>in</strong> RDB$RELATION_FIELDS.<br />

4 Kapitel 27 auf Seite 271<br />

20


4.3. Spalten<br />

Spalten<br />

Spalten bezeichnen die Elemente e<strong>in</strong>er Tabellenstruktur. Sie werden e<strong>in</strong>deutig<br />

gekennzeichnet durch ihren Namen; diese E<strong>in</strong>deutigkeit gilt <strong>in</strong>nerhalb e<strong>in</strong>er Tabelle,<br />

verschiedene Tabellen dürfen Spalten mit gleichem Namen (z. B. ID) haben.<br />

Außerdem gehört zur Def<strong>in</strong>ition e<strong>in</strong>er Spalte der Datentyp; dies wird im<br />

Kapitel Datentypen 5 behandelt.<br />

Die Spalten (<strong>in</strong>nerhalb e<strong>in</strong>er Tabelle) werden <strong>in</strong>tern nach Position geordnet.<br />

Spalten an verschiedenen Positionen können denselben Datentyp haben, aber<br />

niemals denselben Namen. Auf e<strong>in</strong>e bestimmte Spalte wird fast immer über den<br />

Namen zugegriffen, nur äußerst selten über die Position.<br />

E<strong>in</strong>e Spalte hat also e<strong>in</strong>en Namen und e<strong>in</strong>en Datentyp. Jede Zeile <strong>in</strong> e<strong>in</strong>er Tabelle<br />

hat genau e<strong>in</strong>en Wert für jede Spalte; wenn mehrere gleichartige Werte e<strong>in</strong>getragen<br />

werden sollen, werden mehrere Spalten benötigt. Jeder Wert <strong>in</strong> e<strong>in</strong>er Zeile<br />

entspricht dem Datentyp der Spalte.<br />

H<strong>in</strong>weis: In dieser H<strong>in</strong>sicht unterscheiden sich Datenbank-Tabellen<br />

ganz wesentlich von denjenigen e<strong>in</strong>er Tabellenkalkulation, bei der<br />

der Datentyp e<strong>in</strong>zelner Zellen abweichen kann von der Spaltendef<strong>in</strong>ition<br />

und e<strong>in</strong>zelne Zellen zusammengezogen werden können.<br />

Die Eigenschaft NULL für e<strong>in</strong>en Wert ist e<strong>in</strong>e Besonderheit, die vor allem E<strong>in</strong>steiger<br />

gerne verwirrt. Dies bedeutet, dass e<strong>in</strong>er Zelle (noch) ke<strong>in</strong> Wert zugeordnet<br />

worden ist. E<strong>in</strong>e bessere Bezeichnung wäre etwas wie UNKNOWN; aber es heißt<br />

nun leider NULL. Bitte beachten Sie deshalb:<br />

• Für e<strong>in</strong> Textfeld werden folgende Werte unterschieden:<br />

1. Der Wert ” ist e<strong>in</strong> leerer Text.<br />

2. Der Wert ’ ’ ist e<strong>in</strong> Text, der genau e<strong>in</strong> Leerzeichen enthält.<br />

3. Der Wert NULL enthält nichts.<br />

• Für e<strong>in</strong> logisches Feld (Datentyp boolean) wird dies unterschieden:<br />

1. Der Wert TRUE bedeutet „wahr“.<br />

2. Der Wert FALSE bedeutet „falsch“.<br />

3. Der Wert NULL bedeutet „unbekannt“.<br />

• Für e<strong>in</strong> Zahlenfeld wird es so unterschieden:<br />

1. Der Wert 0 ist e<strong>in</strong>e bestimmte Zahl, genauso gut wie jede andere.<br />

2. Der Wert NULL bedeutet „unbekannt“.<br />

5 Kapitel 13 auf Seite 97<br />

21


Relationale Datenbanken<br />

i H<strong>in</strong>weis<br />

Der Wert NULL steht nicht für e<strong>in</strong>en bestimmten Wert, sondern kann<br />

immer als „unbekannt“ <strong>in</strong>terpretiert werden.<br />

Dies kann bei jeder Spalte allgeme<strong>in</strong> festgelegt werden: Die Eigenschaft „NOT<br />

NULL“ bestimmt, dass <strong>in</strong> dieser Spalte der NULL-Wert nicht zulässig ist; wenn<br />

Daten gespeichert werden, muss immer e<strong>in</strong> Wert e<strong>in</strong>getragen werden (und sei es<br />

e<strong>in</strong> leerer Text). Wenn dies nicht festgelegt wurde, muss ke<strong>in</strong> Wert e<strong>in</strong>getragen<br />

werden; der Feld<strong>in</strong>halt ist dann NULL.<br />

Bei SELECT-Abfragen (vor allem auch bei Verknüpfungen mehrerer Tabellen)<br />

gibt es unterschiedliche Ergebnisse je nachdem, ob NULL-Werte vorhanden s<strong>in</strong>d<br />

und ob sie berücksichtigt oder ausgeschlossen werden sollen.<br />

4.4. Verknüpfungen und Schlüssel<br />

Mit diesen Verfahren werden die Tabellen <strong>in</strong> Beziehung zue<strong>in</strong>ander gebracht.<br />

Auch dies folgt der Vorstellung, dass die Wirklichkeit abgebildet werden soll.<br />

4.4.1. Verknüpfungen<br />

Diese, nämlich die Beziehungen zwischen den Tabellen, s<strong>in</strong>d e<strong>in</strong> Kern e<strong>in</strong>es relationalen<br />

Datenbanksystems. In der Beispieldatenbank 6 bestehen unter anderem<br />

folgende Beziehungen:<br />

• Die Tabelle Mitarbeiter verweist auf folgende Tabelle:<br />

1. Jeder Mitarbeiter gehört zu e<strong>in</strong>em E<strong>in</strong>trag der Tabelle Abteilung.<br />

• Die Tabelle Zuordnung_SF_FZ verb<strong>in</strong>det Schadensfälle und Fahrzeuge und<br />

verweist auf folgende Tabellen:<br />

1. Jedes beteiligte Fahrzeug gehört zu e<strong>in</strong>em E<strong>in</strong>trag der Tabelle Fahrzeug.<br />

2. Jeder Schadensfall muss <strong>in</strong> der Tabelle Schadensfall registriert se<strong>in</strong>.<br />

• Die Tabelle Versicherungsvertrag verweist auf folgende Tabellen:<br />

1. Jeder Vertrag wird von e<strong>in</strong>er Person aus der Tabelle Mitarbeiter bearbeitet.<br />

2. Zu jedem Vertrag gehört e<strong>in</strong> E<strong>in</strong>trag der Tabelle Fahrzeug.<br />

3. Zu jedem Vertrag gehört e<strong>in</strong> E<strong>in</strong>trag der Tabelle Versicherungsnehmer.<br />

6 Anhang A auf Seite 419<br />

22


Durch diese Verknüpfungen werden mehrere Vorteile erreicht:<br />

Verknüpfungen und Schlüssel<br />

• Informationen werden nur e<strong>in</strong>mal gespeichert.<br />

Beispiel: Der Name und Sitz e<strong>in</strong>er Abteilung muss nicht bei jedem Mitarbeiter<br />

notiert werden.<br />

• Änderungen werden nur e<strong>in</strong>mal v<strong>org</strong>enommen.<br />

Beispiel: Wenn die Abteilung umzieht, muss nur der E<strong>in</strong>trag <strong>in</strong> der Tabelle Abteilung<br />

geändert werden und nicht die Angaben bei jedem Mitarbeiter.<br />

• Der Zusammenhang der Daten wird gewährleistet.<br />

Beispiel: E<strong>in</strong> Versicherungsnehmer kann nicht gelöscht werden, solange er<br />

noch mit e<strong>in</strong>em Vertrag registriert ist.<br />

Damit dies verwirklicht werden kann, werden geeignete Maßnahmen benötigt:<br />

1. Jeder Datensatz muss durch e<strong>in</strong>en Schlüssel e<strong>in</strong>deutig identifiziert werden.<br />

2. Die Schlüssel der verschiedenen mite<strong>in</strong>ander verknüpften Datensätze<br />

müssen sich zuordnen lassen.<br />

4.4.2. Schlüssel<br />

PrimaryKey (PK): Der Primärschlüssel ist e<strong>in</strong>e Spalte <strong>in</strong> der Tabelle, durch die<br />

e<strong>in</strong>deutig jede Zeile identifiziert wird (gerne mit dem Namen ID). Es kann auch<br />

e<strong>in</strong>e Komb<strong>in</strong>ation von Spalten als e<strong>in</strong>deutig festgelegt werden; das ist aber selten<br />

s<strong>in</strong>nvoll. In der Regel sollte diese Spalte auch ke<strong>in</strong>e andere „<strong>in</strong>haltliche“ Bedeutung<br />

haben als die ID.<br />

Beispiele: Die Komb<strong>in</strong>ation Name/Vorname kann bei kle<strong>in</strong>en Datenmengen<br />

zwar praktisch e<strong>in</strong>deutig se<strong>in</strong>, aber niemals theoretisch; irgendwann<br />

kommt e<strong>in</strong> zweiter „Lucas Müller“, und dann? Bei e<strong>in</strong>em<br />

Mehrbenutzersystem werden häufig mehrere E<strong>in</strong>träge „gleichzeitig“<br />

gespeichert; es ist besser, wenn das DBMS die Vergabe der ID selbst<br />

steuert, als dass die Benutzer sich absprechen müssen. In der Beispieldatenbank<br />

wird deshalb <strong>in</strong> der Tabelle Mitarbeiter zwischen der<br />

automatisch vergebenen ID und der ebenfalls e<strong>in</strong>deutigen Personalnummer<br />

unterschieden.<br />

ForeignKey (FK): Über Fremdschlüssel werden die Tabellen mite<strong>in</strong>ander verknüpft.<br />

E<strong>in</strong>em Feld <strong>in</strong> der e<strong>in</strong>en Tabelle wird e<strong>in</strong> Datensatz <strong>in</strong> e<strong>in</strong>er anderen Tabelle<br />

zugeordnet; dieser wird über den Primärschlüssel bereitgestellt. Es kann<br />

auch e<strong>in</strong>e Komb<strong>in</strong>ation von Spalten verwendet werden; da sich der Fremdschlüssel<br />

aber auf e<strong>in</strong>en Primärschlüssel der anderen Tabelle beziehen muss, ist<br />

dies ebenso selten s<strong>in</strong>nvoll. Die „Datenbank-Theorie“ geht sogar soweit, dass die<br />

Schlüsselfelder dem Anwender gar nicht bekannt se<strong>in</strong> müssen.<br />

23


Relationale Datenbanken<br />

Beispiele stehen <strong>in</strong> der obigen Aufstellung. Nähere Erläuterungen<br />

s<strong>in</strong>d im Kapitel Fremdschlüssel-Beziehungen 7 zu f<strong>in</strong>den.<br />

Index: Er dient dazu, e<strong>in</strong>en Suchbegriff schnell <strong>in</strong>nerhalb e<strong>in</strong>er Tabelle zu f<strong>in</strong>den.<br />

Die Mehrzahl lautet nach Duden ‚Indizes‘, auch ‚Indexe‘ ist möglich; <strong>in</strong> der<br />

EDV wird oft auch der englische Plural ‚Indexes‘ verwendet. Dies gehört zwar nicht<br />

zum „Kernbereich“ e<strong>in</strong>es relationalen DBMS, passt aber (auch wegen der umgangssprachlichen<br />

Bedeutung des Wortes „Schlüssel“) durchaus hierher.<br />

• Der Primärschlüssel ist e<strong>in</strong> Suchbegriff, mit dem e<strong>in</strong>deutig e<strong>in</strong> Datensatz gefunden<br />

werden kann.<br />

• Mit e<strong>in</strong>em Index kann die Suche nach e<strong>in</strong>em bestimmten Datensatz oder e<strong>in</strong>er<br />

Datenmenge beschleunigt werden.<br />

Beispiel: die Suche nach Postleitzahl<br />

• Die Werte e<strong>in</strong>er Spalte oder e<strong>in</strong>er Komb<strong>in</strong>ation von Spalten sollen e<strong>in</strong>deutig<br />

se<strong>in</strong>.<br />

Beispiel: die Personalnummer<br />

Nähere Erläuterungen s<strong>in</strong>d <strong>in</strong> den Kapiteln im Teil Erweiterungen ab DDL − E<strong>in</strong>zelheiten<br />

8 zu f<strong>in</strong>den.<br />

4.5. Siehe auch<br />

Über Wikipedia s<strong>in</strong>d weitere Informationen zu f<strong>in</strong>den:<br />

• Relationale Datenbank 9<br />

• Entitätenmodell 10 und Entity-Relationship-Modell 11<br />

• Normalisierung 12<br />

• Tabellenkalkulation 13 im Gegensatz zu Datenbank-Tabellen<br />

• Nullwert 14<br />

7 Kapitel 29 auf Seite 305<br />

8 Kapitel 28 auf Seite 285<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Relationale%20Datenbank<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/Entit%c3%a4t%20(Informatik)<br />

11 http://de.wikipedia.<strong>org</strong>/wiki/Entity-Relationship-Modell<br />

12 http://de.wikipedia.<strong>org</strong>/wiki/Normalisierung%20(Datenbank)<br />

13 http://de.wikipedia.<strong>org</strong>/wiki/Tabellenkalkulation<br />

14 http://de.wikipedia.<strong>org</strong>/wiki/Nullwert<br />

24


5. Normalisierung<br />

5.1. Grundgedanken . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />

5.2. Die 1. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . 28<br />

5.3. Die 2. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />

5.4. Die 3. Normalform . . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />

5.5. Zusätzliche Maßnahmen . . . . . . . . . . . . . . . . . . . . . . . 34<br />

5.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />

5.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />

In diesem Kapitel werden e<strong>in</strong>ige Überlegungen angestellt, wie e<strong>in</strong>e Datenbank<br />

konzipiert werden soll.<br />

Dieses Kapitel geht zwar über die Anforderungen e<strong>in</strong>er <strong>E<strong>in</strong>führung</strong> h<strong>in</strong>aus. Denn<br />

es richtet sich weniger an „e<strong>in</strong>fache“ Anwender, die e<strong>in</strong>e vorhandene Datenbank<br />

benutzen wollen, sondern an (künftige) Programmentwickler, die e<strong>in</strong>e Datenbank<br />

entwerfen und erstellen. Aber wann soll man darüber sprechen, wenn nicht<br />

am Anfang?<br />

5.1. Grundgedanken<br />

Schon das e<strong>in</strong>fache Muster e<strong>in</strong>er unzureichenden Tabelle wie im vorigen Kapitel<br />

weist auf e<strong>in</strong> paar Forderungen h<strong>in</strong>, die offensichtlich an e<strong>in</strong>e s<strong>in</strong>nvolle Struktur<br />

gestellt werden sollten:<br />

Redundanz vermeiden: E<strong>in</strong>e Information, die an mehreren Stellen benötigt<br />

wird, soll nur e<strong>in</strong>mal gespeichert werden. Dadurch müssen auch sämtliche<br />

Änderungen, die solche Informationen betreffen, nur an e<strong>in</strong>er Stelle<br />

erledigt werden.<br />

Im Beispiel waren das u. a. die Angaben zum Fahrzeughalter und zum<br />

Sachbearbeiter (mit se<strong>in</strong>en Telefonnummern), die bei jedem Vertrag angegeben<br />

werden.<br />

Wiederholungen trennen: Die Zusammenfassung gleichartiger Informationen<br />

ist nicht sehr übersichtlich. Im vorigen Kapitel standen zunächst Festnetzund<br />

Mobil-Rufnummer <strong>in</strong> e<strong>in</strong>er Spalte. Das ist praktisch, wenn man sich<br />

25


Normalisierung<br />

ke<strong>in</strong>e Gedanken machen will, welche und wie viele Kontaktnummern es<br />

gibt. Es ist ziemlich unpraktisch, wenn man gezielt e<strong>in</strong>zelne Nummern suchen<br />

will oder von e<strong>in</strong>er Nummer auf den Sachbearbeiter schließen will.<br />

Besser s<strong>in</strong>d Festnetz- und Mobilnummer getrennt zu speichern.<br />

Primärschlüssel verwenden: Jeder Datensatz muss e<strong>in</strong>deutig identifiziert werden,<br />

damit die Informationen verwendet werden können.<br />

In der ungenügenden Tabelle wurde wegen der Zeilennummern darauf<br />

verzichtet. Aber selbst wenn man die Suche <strong>in</strong> zigtausend Zeilen für machbar<br />

hielte, spätestens bei der Aufteilung <strong>in</strong> mehrere Tabellen braucht man<br />

Werte, um die Zusammenhänge e<strong>in</strong>deutig zu regeln.<br />

Bei den bisherigen Überlegungen handelt es sich nur um e<strong>in</strong> paar Gedanken, die<br />

sich e<strong>in</strong>em aufdrängen. Das wollen wir nun besser strukturieren. Überlegen wir<br />

uns zunächst, welche Informationen benötigt werden.<br />

Grundlegende Daten<br />

Für die Verträge müssen die folgenden Angaben gespeichert werden.<br />

• die Vertragsdaten selbst, also Nummer, Abschlussdatum und Art des Vertrags<br />

(HP = Haftpflicht, TK = Teilkasko, VK = Vollkasko) und der Prämienberechnung<br />

• der Vertragspartner mit Name, Anschrift, Telefonnummern, dazu bei natürlichen<br />

Personen Geschlecht und Geburtsdatum sowie bei juristischen Personen<br />

der E<strong>in</strong>trag im Handelsregister o. ä.<br />

• das versicherte Fahrzeug mit Kennzeichen, Hersteller und Typ (die Farbe ist<br />

e<strong>in</strong>e Zusatz<strong>in</strong>formation, die bei manchen Suchv<strong>org</strong>ängen hilfreich se<strong>in</strong> kann)<br />

• der zuständige Sachbearbeiter mit Name, Abteilung und Telefonnummern<br />

E<strong>in</strong> Teil der Angaben muss immer vorhanden se<strong>in</strong>, e<strong>in</strong> anderer Teil je nach Situation<br />

(Geschlecht bei Personen) und weitere nur bei Bedarf (Mobiltelefon).<br />

Schnell stellen wir fest, dass e<strong>in</strong> Versicherungsnehmer auch mehrere Fahrzeuge<br />

versichern kann. Andererseits kann e<strong>in</strong> Fahrzeug zu mehreren Verträgen gehören,<br />

wenn Haftpflicht und Vollkasko getrennt abgeschlossen werden oder wenn<br />

zur Haftpflicht vorübergehend Vollkasko fürs Ausland h<strong>in</strong>zukommt. Jeder Vertrag<br />

ist e<strong>in</strong> eigener Datensatz; also stehen bei jedem Datensatz die Angaben des<br />

Vertragspartners und des Fahrzeugs. Um alle Verträge e<strong>in</strong>es Kunden geme<strong>in</strong>sam<br />

anzuzeigen, brauchen wir folglich e<strong>in</strong> paar Angaben zur Organisation:<br />

• Kundennummer und laufende Nummer se<strong>in</strong>er Verträge als e<strong>in</strong>deutiger Suchbegriff<br />

(als Index, vielleicht auch als Primärschlüssel)<br />

• Vertragsnummer als e<strong>in</strong>deutiger Wert<br />

• Fahrzeug-Kennzeichen als Wert, der <strong>in</strong> der Regel nur e<strong>in</strong>mal vorkommt<br />

26


Schadensfälle<br />

Grundgedanken<br />

Nun sollen zu e<strong>in</strong>em Vertrag auch die Schadensfälle gespeichert werden. Wir benötigen<br />

also e<strong>in</strong>e oder mehrere Spalten mit den erforderlichen Angaben (Datum<br />

und Art des Schadens, Sachbearbeiter der Schadensabwicklung, andere beteiligte<br />

Fahrzeuge); aber wie wird das am besten gespeichert?<br />

• e<strong>in</strong>e geme<strong>in</strong>same Spalte für alle diese Angaben für alle Schadensfälle (denn die<br />

Mehrzahl der Fahrzeuge fährt schadensfrei)<br />

• oder je e<strong>in</strong>e Spalte für alle Angaben e<strong>in</strong>es Schadensfalls (werden dann zwei<br />

oder drei Spalten benötigt, oder muss man für „Mehrfach-Sünder“ gleich zehn<br />

Spalten vorsehen?)<br />

• oder e<strong>in</strong>zelne Datensätze für jeden Schaden e<strong>in</strong>es Fahrzeugs (sodass sich alle<br />

Fahrzeug- und Vertragsdaten <strong>in</strong> diesen Datensätzen wiederholen und nur die<br />

Schadensangaben unterscheiden)<br />

Ungeklärt bleibt dabei noch dieses Problem: Wie viele versicherte Schadensgegner<br />

gibt es denn − ke<strong>in</strong>e (wenn e<strong>in</strong> Reh angefahren wird), e<strong>in</strong>en (z. B. beim Parken)<br />

oder viele (bei e<strong>in</strong>em Auffahrunfall auf der Autobahn)?<br />

Entscheiden wir uns deshalb provisorisch für e<strong>in</strong> eigenes Arbeitsblatt Schadensfälle<br />

mit folgender Struktur:<br />

• je e<strong>in</strong>e Spalte für die Angaben, die direkt zum Schadensfall gehören<br />

• vorläufig 5 Gruppen für maximal 5 beteiligte Fahrzeuge<br />

• jede dieser Gruppen enthält <strong>in</strong> e<strong>in</strong>zelnen Spalten Fahrzeug-Kennzeichen und<br />

die Halter-Angaben (Name, Anschrift)<br />

Damit stehen die Fahrzeugdaten <strong>in</strong> beiden Arbeitsblättern. Als praktisches Problem<br />

kommt h<strong>in</strong>zu: Für die Schadenshäufigkeit e<strong>in</strong>es Fahrzeugs muss man es <strong>in</strong><br />

fünf Spalten heraussuchen. Die Beschränkung auf fünf beteiligte Fahrzeuge wird<br />

im nächsten Schritt aufgelöst, machen wir uns dazu ke<strong>in</strong>e weiteren Gedanken.<br />

Auf diesem Weg kann jedenfalls noch ke<strong>in</strong>e s<strong>in</strong>nvolle Struktur erreicht werden.<br />

„Update-Anomalien“<br />

Mit diesem Begriff werden die folgenden Unklarheiten bezeichnet. Dabei handelt<br />

es sich um weitere Probleme bei e<strong>in</strong>er ungenügenden Struktur.<br />

E<strong>in</strong>fügen-Anomalie: In der ersten Tabelle s<strong>in</strong>d u. a. die Sachbearbeiter enthalten.<br />

Es wäre s<strong>in</strong>nvoll, auch alle anderen Mitarbeiter der Gesellschaft hier<br />

e<strong>in</strong>zutragen. Aber bei e<strong>in</strong>em Vertrag werden zw<strong>in</strong>gend Vertragsnummer<br />

und Abschlussdatum benötigt; dieser Zwang verh<strong>in</strong>dert das Speichern der<br />

Teil<strong>in</strong>formation Sachbearbeiter ohne Bezug auf e<strong>in</strong>en Vertrag.<br />

27


Normalisierung<br />

Löschen-Anomalie: Wenn e<strong>in</strong> Vertrag gekündigt und abgelaufen ist und deshalb<br />

gelöscht wird, s<strong>in</strong>d <strong>in</strong> der ersten Tabelle auch alle Angaben zum Vertragspartner<br />

verloren. Wenn er drei Tage später e<strong>in</strong>en neuen Vertrag abschließt,<br />

müssen alle Angaben neu aufgenommen werden. Und was soll<br />

mit Schadensfällen geschehen, die zu diesem Vertrag gespeichert s<strong>in</strong>d?<br />

Ändern-Anomalie: Wenn der Sachbearbeiter wechselt, muss se<strong>in</strong> Name bei allen<br />

von ihm betreuten Verträgen geändert werden, obwohl e<strong>in</strong>e e<strong>in</strong>zige Änderung<br />

ausreichend wäre. (Dies ist auf die Datenredundanz zurückzuführen,<br />

dass nämlich dieser H<strong>in</strong>weis vielfach gespeichert ist statt e<strong>in</strong>malig.)<br />

All diesen Problemen wollen wir nun durch e<strong>in</strong>e deutlich verbesserte Datenstruktur<br />

begegnen. Nehmen wir dazu zunächst an, dass alle benötigten Informationen<br />

<strong>in</strong> den beiden Arbeitsblättern Verträge und Schadensfälle der Tabellenkalkulation<br />

stehen, und beg<strong>in</strong>nen wir, dies s<strong>in</strong>nvoll zu strukturieren.<br />

5.2. Die 1. Normalform<br />

Am „grausamsten“ für den Aufbau der Tabelle und die praktische Arbeit ist die<br />

vielfache Wiederholung gleicher Informationen.<br />

Sowohl beim Namen des Fahrzeughalters als auch bei den Mitarbeitern steht der<br />

Name bisher <strong>in</strong> e<strong>in</strong>er Spalte <strong>in</strong> der Form „Nachname, Vorname“, was als Suchbegriff<br />

geeignet ist. Nun benötigen wir aber auch e<strong>in</strong>e persönliche Anrede (Name<br />

mit Titel) und e<strong>in</strong>e Briefanschrift (Vorname, Name). Soll dies <strong>in</strong> weiteren Spalten<br />

gespeichert werden? Ne<strong>in</strong>, denn all dies kann aus den E<strong>in</strong>zelangaben „Name“<br />

und „Vorname“ zusammengesetzt werden.<br />

E<strong>in</strong> Schadensfall ist bisher auf fünf beteiligte Fahrzeuge beschränkt, auch wenn<br />

selten mehr als zwei Beteiligungen benötigt werden. Wenn nun e<strong>in</strong> sechstes,<br />

zehntes oder zwanzigstes Fahrzeug beteiligt ist, muss dann jedesmal die Tabellenstruktur<br />

(und jedes Makro, das auf diese Spalten zugreift) geändert werden?!<br />

Damit haben wir bereits zwei Regeln, die als Def<strong>in</strong>ition der ersten Normalform<br />

gelten. H<strong>in</strong>zu kommt e<strong>in</strong>e dritte Regel, die zwar formal nicht erforderlich ist; aber<br />

aus praktischen Gründen gibt es ke<strong>in</strong>e s<strong>in</strong>nvolle Lösung ohne diese Regel.<br />

28<br />

i Die 1. Normalform<br />

1. Jede Spalte enthält nur unteilbare (atomare, atomische) Werte.<br />

2. Spalten, die gleiche oder gleichartige Informationen enthalten, s<strong>in</strong>d <strong>in</strong><br />

eigene Tabellen (Relationen) auszulagern.<br />

3. Jede Tabelle enthält e<strong>in</strong>en Primärschlüssel.


Verletzung der 1. Normalform<br />

Unsere Ausgangstabelle verstößt an vielen Stellen gegen diese Regeln:<br />

• Zusammengesetzte Werte bef<strong>in</strong>den sich z. B. an folgenden Stellen:<br />

Die 1. Normalform<br />

• Fahrzeughalter: Name und Vorname, PLZ und Ort, Straße und Nummer<br />

• Sachbearbeiter: Name und Vorname, Festnetz- und Mobilnummer<br />

• Wiederholungen f<strong>in</strong>den sich vor allem hier:<br />

• mehrere Fahrzeuge bei e<strong>in</strong>em Schadensfall<br />

V<strong>org</strong>ehen zur Herstellung der 1. Normalform<br />

Es werden also zwei Schritte benötigt:<br />

1. Zusammengesetzte Werte werden <strong>in</strong> E<strong>in</strong>zel<strong>in</strong>formationen aufgeteilt: je<br />

e<strong>in</strong>e Spalte für jeden unteilbaren Wert.<br />

2. Wiederholungen werden <strong>in</strong> getrennte Tabellen ausgelagert; welche Daten<br />

zusammengehören, wird durch e<strong>in</strong>e laufende Nummer angegeben.<br />

E<strong>in</strong>e erste Verbesserung<br />

An jeder Stelle, die Namen oder Anschrift enthält, werden getrennte Spalten verwendet,<br />

beispielsweise im Arbeitsblatt Verträge:<br />

Kundennummer Vertrag Abschluss Halter_Name Halter_PLZ Halter_Ort<br />

Sachbearbeiter_N Sachbearbeiter_V Telefon<br />

1 DG-01 03 05 1974 Heckel Obsthandel GmbH 46282 Dorsten<br />

Pohl Helmut 0201/4014186<br />

1 DG-02 04 07 1974 Heckel Obsthandel GmbH 46282 Dorsten<br />

Pohl Helmut 0201/4014186<br />

Die Tabelle Schadensfälle wird auf die eigentlichen Daten beschränkt; die beteiligten<br />

Fahrzeuge stehen <strong>in</strong> e<strong>in</strong>er eigenen Tabelle Zuordnungen.<br />

Nummer Datum Schadensort Beschreibung<br />

Sachbearbeiter_N Sachbearbeiter_V Telefon<br />

1 02 03 2007 Reckl<strong>in</strong>ghausen, Bergknappenstr 144 Gartenzaun gestreift<br />

Sch<strong>in</strong>dler Christ<strong>in</strong>a 0201/4012151<br />

2 11 07 2007 Haltern, Hauptstr 46 beim Ausparken hat ...<br />

Aliman Zafer 0201/4012161<br />

Die Anzahl der beteiligten Fahrzeuge an e<strong>in</strong>em Schadensfall wird durch e<strong>in</strong>e eigene<br />

Tabelle Zuordnungen berücksichtigt:<br />

29


Normalisierung<br />

Nummer Fahrzeug Hersteller Typ Halter_Name Halter_Vorname<br />

Halter_PLZ Halter_Ort Halter_Straße<br />

1 RE-LM 902 Opel Corsa Heckel Obsthandel GmbH<br />

46282 Dorsten Gahlener Str 40<br />

2 BO-GH 102 Volvo C30 Geissler Helga<br />

44809 Bochum Ste<strong>in</strong>bankstr 15<br />

2 RE-CD 456 Renault Tw<strong>in</strong>go Cornelsen Dorothea<br />

44577 Castrop-Rauxel Kiefernweg 9<br />

Die Zusatzbed<strong>in</strong>gung e<strong>in</strong>es Primärschlüssels wurde gleichzeitig erfüllt; die betreffenden<br />

Spalten wurden unterstrichen.<br />

5.3. Die 2. Normalform<br />

E<strong>in</strong>e weitere Wiederholung <strong>in</strong> den „Monster-Tabellen“ s<strong>in</strong>d die Angaben zum<br />

Halter bei den Verträgen und den Zuordnungen oder die Fahrzeugdaten sowohl<br />

bei den Verträgen als auch bei den Zuordnungen. Auch diese werden <strong>in</strong> eigene<br />

Tabellen ausgelagert; das ergibt sich als Def<strong>in</strong>ition aus der folgenden Regel:<br />

i Die 2. Normalform<br />

1. Die Tabelle erfüllt die 1. Normalform.<br />

2. Alle Informationen <strong>in</strong> den Spalten, die nicht Teil des Primärschlüssels<br />

s<strong>in</strong>d, müssen sich auf den gesamten Primärschlüssel beziehen.<br />

Man sagt auch, dass die Informationen funktional abhängig s<strong>in</strong>d von der Gesamtheit<br />

der Schlüsselwerte. Umgekehrt formuliert bedeutet es: Wenn e<strong>in</strong>e Spalte<br />

nur zu e<strong>in</strong>em e<strong>in</strong>zelnen Schlüsselfeld gehört, ist die 2. Normalform nicht erfüllt.<br />

Während sich die 1. Normalform auf die e<strong>in</strong>zelnen Spalten und Wiederholungen<br />

<strong>in</strong>nerhalb e<strong>in</strong>es Datensatzes bezieht, befasst sich die 2. Normalform mit Wiederholungen<br />

bei verschiedenen Datensätzen.<br />

Verletzung der 2. Normalform<br />

Unsere derzeitigen Tabellen verstoßen mehrfach gegen diese Regel:<br />

• Bei den Verträgen beziehen sich Name und Anschrift des Vertragspartners nur<br />

auf die Kundennummer, aber nicht auf die Vertragsnummer.<br />

30


Die 2. Normalform<br />

• Bei den Verträgen stehen auch die Fahrzeugdaten. (Dies wurde bei der Beschreibung<br />

der grundlegenden Daten erwähnt, fehlt aber <strong>in</strong> den bisherigen<br />

Beispielen.) Diese beziehen sich auf die Vertragsnummer, haben aber nur <strong>in</strong>direkt<br />

etwas mit Name und Anschrift des Vertragspartners zu tun.<br />

• Bei den Zuordnungen der Schadensfälle gehören Fahrzeug- und Halterdaten<br />

nur zu e<strong>in</strong>em Fahrzeug (dem Kennzeichen), aber nicht zu e<strong>in</strong>em Schadensfall.<br />

V<strong>org</strong>ehen zur Herstellung der 2. Normalform<br />

Alle „unpassenden“ Informationen gehören <strong>in</strong> eigene Tabellen. Der Primärschlüssel<br />

bezieht sich nur auf die „eigentlichen“ Informationen e<strong>in</strong>er Tabelle.<br />

• Aus den Verträgen werden alle Angaben des Vertragspartners entfernt und<br />

<strong>in</strong> e<strong>in</strong>e Tabelle Versicherungsnehmer übertragen. Der Primärschlüssel besteht<br />

nur noch aus der Spalte Vertrag. Die Spalte Kundennummer ist nur noch e<strong>in</strong><br />

Fremdschlüssel zur Verknüpfung mit der neuen Tabelle Versicherungsnehmer.<br />

• Aus den Verträgen werden alle Angaben des Fahrzeugs entfernt und <strong>in</strong> e<strong>in</strong>e<br />

neue Tabelle Fahrzeuge übertragen. Das Fahrzeug-Kennzeichen ist hier nur<br />

noch e<strong>in</strong> Fremdschlüssel als Verweis auf die Tabelle Fahrzeuge.<br />

• Aus den Zuordnungen werden alle Angaben der Fahrzeuge entfernt und <strong>in</strong> e<strong>in</strong>e<br />

Tabelle Fahrzeuge übertragen. Die Tabelle Zuordnungen besteht nur noch aus<br />

den Spalten des Primärschlüssels.<br />

Damit stehen die Fahrzeugdaten nur noch <strong>in</strong> e<strong>in</strong>er Tabelle – sowohl für die Verträge<br />

als auch für die Schadensfälle (genauer: die Zuordnungen).<br />

E<strong>in</strong>e zweite Verbesserung<br />

Die ursprüngliche Tabelle Verträge beschränkt sich jetzt auf diese Angaben:<br />

Vertrag Abschluss Typ Kd-Nr Fahrzeug Sachb_N Sachb_V Telefon<br />

DG-01 03.05.1974 HP 1 RE-LM 901 Pohl Helmut 0201/4014186<br />

DG-02 04.07.1974 HP 1 RE-LM 902 Pohl Helmut 0201/4014186<br />

Die neue Tabelle Versicherungsnehmer umfasst dessen Daten:<br />

Kd-Nr Name Vorname Geburtsdatum PLZ Ort Straße Nr<br />

1 Heckel Obsthandel GmbH 46282 Dorsten Gahlener Str. 40<br />

5 Geissler Helga 13.01.1953 44809 Bochum Ste<strong>in</strong>bankstr. 15<br />

Die neue Tabelle Fahrzeuge umfasst nur noch die Daten, die sich auf das Fahrzeug<br />

selbst beziehen. Name und Anschrift des Fahrzeughalters werden durch die<br />

Kundennummer, also den Verweis auf die Tabelle Versicherungsnehmer ersetzt.<br />

31


Normalisierung<br />

Fahrzeug Hersteller Typ Farbe Halter<br />

RE-LM 902 Opel Corsa ocker 1<br />

BO-GH 102 Volvo C30 rot 5<br />

RE-CD 456 Renault Tw<strong>in</strong>go ocker 3<br />

Die Tabelle Schadensfälle muss nicht angepasst werden. Die Tabelle Zuordnungen<br />

vere<strong>in</strong>facht sich radikal:<br />

Nummer Fahrzeug<br />

1 RE-LM 902<br />

2 BO-GH 102<br />

2 RE-CD 456<br />

Die 2. Normalform kann ganz e<strong>in</strong>fach dadurch gewährleistet werden, dass sich<br />

der Primärschlüssel nur auf e<strong>in</strong>e Spalte bezieht.<br />

5.4. Die 3. Normalform<br />

Beseitigen wir noch die übrigen Wiederholungen, nämlich die Sachbearbeiter<br />

bei Verträgen und Schadensfällen sowie die Hersteller bei Fahrzeugen. Diese<br />

kommen ebenfalls <strong>in</strong> eigene Tabellen gemäß Def<strong>in</strong>ition nach folgender Regel:<br />

i Die 3. Normalform<br />

1. Die Tabelle erfüllt die 2. Normalform.<br />

2. Informationen <strong>in</strong> den Spalten, die nicht Teil des Primärschlüssels s<strong>in</strong>d,<br />

dürfen funktional nicht vone<strong>in</strong>ander abhängen.<br />

Die 3. Normalform befasst sich also mit Wiederholungen bei verschiedenen Datensätzen,<br />

die nur zusätzliche Informationen bereitstellen.<br />

Verletzung der 3. Normalform<br />

Unsere derzeitigen Tabellen verstoßen <strong>in</strong> folgender H<strong>in</strong>sicht gegen diese Regel:<br />

• Name, Vorname und Telefonnummer e<strong>in</strong>es Sachbearbeiters hängen vone<strong>in</strong>ander<br />

ab. Sie haben aber nur <strong>in</strong>sgesamt etwas mit dem Vertrag bzw. dem Schadensfall<br />

zu tun, nicht als e<strong>in</strong>zelne Information.<br />

• Hersteller und Typ e<strong>in</strong>es Fahrzeugs hängen vone<strong>in</strong>ander ab. Sie haben aber<br />

nur <strong>in</strong>sgesamt etwas mit dem Fahrzeug zu tun, nicht als e<strong>in</strong>zelne Information.<br />

32


Die 3. Normalform<br />

E<strong>in</strong>e andere Erklärung dafür ist, dass die Zusatz<strong>in</strong>formation auch ohne Bezug<br />

zum eigentlichen Datensatz gültig bleibt. Der Sachbearbeiter gehört zum Unternehmen<br />

unabhängig von e<strong>in</strong>em bestimmten Vertrag. Der Fahrzeughersteller<br />

existiert unabhängig davon, ob e<strong>in</strong> bestimmtes Fahrzeug noch fährt oder <strong>in</strong>zwischen<br />

verschrottet worden ist.<br />

V<strong>org</strong>ehen zur Herstellung der 3. Normalform<br />

Alle „unpassenden“ Informationen kommen wieder <strong>in</strong> eigene Tabellen. Ihre<br />

Spalten werden ersetzt durch e<strong>in</strong>en Fremdschlüssel zur Verknüpfung mit der<br />

neuen Tabelle.<br />

• Aus den Verträgen und den Schadensfällen werden alle Angaben zum Sachbearbeiter<br />

entfernt und <strong>in</strong> e<strong>in</strong>e Tabelle Mitarbeiter übertragen. Die Spalte Sachbearbeiter<br />

verweist als Fremdschlüssel auf die neue Tabelle Mitarbeiter.<br />

Dies löst automatisch auch das oben erwähnte Problem: Wir können<br />

nun alle Mitarbeiter <strong>in</strong> e<strong>in</strong>er geme<strong>in</strong>samen Tabelle speichern.<br />

• Aus den Fahrzeugen werden alle Angaben zu Hersteller und Typ entfernt und<br />

<strong>in</strong> e<strong>in</strong>e neue Tabelle Fahrzeugtypen übertragen. Die Spalten Hersteller und Typ<br />

werden ersetzt durch e<strong>in</strong>en Fremdschlüssel zur Verknüpfung mit der Tabelle<br />

Fahrzeugtypen.<br />

Um es korrekt zu machen, gehört der Hersteller <strong>in</strong> e<strong>in</strong>e weitere Tabelle<br />

Fahrzeughersteller; <strong>in</strong> der Tabelle Fahrzeugtypen verweist er als<br />

Fremdschlüssel auf diese weitere Tabelle.<br />

E<strong>in</strong>e weitere Verbesserung<br />

Die Tabellen Verträge und Schadensfälle werden also nochmals vere<strong>in</strong>facht:<br />

Vertrag Abschluss Typ Kundennummer Fahrzeug Sachbearbeiter<br />

DG-01 03.05.1974 HP 1 RE-LM 901 9<br />

DG-02 04.07.1974 HP 1 RE-LM 902 9<br />

Nummer Datum Schadensort Beschreibung Sachbearb<br />

1 02.03.2007 Reckl<strong>in</strong>ghausen, Bergknappe... Gartenzaun gestreift 14<br />

2 11.07.2007 Haltern, Hauptstr. 46 beim Ausparken hat ... 15<br />

H<strong>in</strong>zu kommt die neue Tabelle Mitarbeiter:<br />

Nummer Nachname Vorname Telefon<br />

9 Pohl Helmut 0201/4014186<br />

33


Normalisierung<br />

14 Sch<strong>in</strong>dler Christ<strong>in</strong>a 0201/4012151<br />

15 Aliman Zafer 0201/4012161<br />

In gleicher Weise wird die Tabelle Fahrzeuge gekürzt; die Angaben werden <strong>in</strong> die<br />

neuen Tabellen Fahrzeugtypen und Fahrzeughersteller ausgelagert.<br />

5.5. Zusätzliche Maßnahmen<br />

In der Theorie gibt es noch e<strong>in</strong>e 4. und e<strong>in</strong>e 5. Normalform (und e<strong>in</strong>e Alternative<br />

zur 3. Normalform). Dazu sei auf den Wikipedia-Artikel und die dortigen<br />

H<strong>in</strong>weise (Quellen, Literatur, Webl<strong>in</strong>ks) verwiesen. In der Praxis ist aber e<strong>in</strong>e Datenbank,<br />

die den Bed<strong>in</strong>gungen der 3. Normalform entspricht, bereits sehr gut<br />

konzipiert. Weitere Normalisierungen br<strong>in</strong>gen kaum noch Vorteile; stattdessen<br />

können sie die Übersichtlichkeit und die Geschw<strong>in</strong>digkeit beim Datenzugriff bee<strong>in</strong>trächtigen.<br />

5.5.1. Verzicht auf Zusatztabellen<br />

Beispielsweise wiederholen sich <strong>in</strong> der Tabelle Versicherungsnehmer die Ortsangaben.<br />

Dabei gehören die Komb<strong>in</strong>ationen PLZ/Ort immer zusammen; auch<br />

wenn der Kunde umzieht, ist e<strong>in</strong>e solche Komb<strong>in</strong>ation unverändert gültig. Also<br />

könnte man <strong>in</strong> der Tabelle die Adresse wie folgt speichern:<br />

Kd-Nr Name Vorname Geburtsdatum PLZ Alort Straße Hausnummer<br />

1 Heckel Obsthandel GmbH 46282 10884500 Gahlener Str. 40<br />

5 Geissler Helga 13.01.1953 44809 05902500 Ste<strong>in</strong>bankstr. 15<br />

Der Ortsname ist dann zu f<strong>in</strong>den über die PL-Datei der Deutschen Post AG (mit<br />

PLZ/Alort als Primärschlüssel):<br />

Dateiversion Geltung PLZ Alort PLZ-Arten Ortsname<br />

PL 0509 244 20010101 44809 05902500 06 2 2 Bochum<br />

PL 0509 244 20050329 46282 10884500 06 2 2 Dorsten<br />

Das gleiche Verfahren ist möglich für die Straßennamen. Es s<strong>org</strong>t dafür, dass nur<br />

gültige Anschriften gespeichert s<strong>in</strong>d und auch bei E<strong>in</strong>geme<strong>in</strong>dungen die neuen<br />

Angaben e<strong>in</strong>deutig übernommen werden. Aber selbst wenn man wegen der<br />

Datensicherheit mit Alort arbeiten will, wird man für die praktische Arbeit den<br />

Ortsnamen <strong>in</strong> der Adressendatei behalten wollen.<br />

34


5.5.2. Primärschlüssel nur zur Identifizierung<br />

Zusätzliche Maßnahmen<br />

Im vorigen Kapitel hatten wir kurz darauf h<strong>in</strong>gewiesen, dass der Primärschlüssel<br />

nur die Bedeutung als ID haben sollte.<br />

E<strong>in</strong>e Begründung liefert die obige Tabelle Fahrzeuge, bei der das Kennzeichen<br />

zur Identifizierung benutzt wurde. Wenn der Fahrzeughalter <strong>in</strong> e<strong>in</strong>en anderen<br />

Kreis umzieht, bekommt er e<strong>in</strong> neues Kennzeichen. Dann müsste es an allen<br />

Stellen geändert werden, an denen es gespeichert ist. Nun darf die Vertragsabteilung<br />

nur die Verträge ändern, die Schadensabwicklung die Schadensfälle (und<br />

wer weiß, wo noch darauf Bezug genommen wird). Jede Ummeldung verursacht<br />

also erheblichen Mehraufwand.<br />

S<strong>org</strong>en wir also für mehr Datensicherheit und Arbeitsvere<strong>in</strong>fachung:<br />

• Der Primärschlüssel ist e<strong>in</strong>e automatisch zugewiesene ID. Diese ID wird niemals<br />

geändert.<br />

• Die Identifizierung, die der Benutzer kennt, ist e<strong>in</strong>e e<strong>in</strong>zelne Spalte <strong>in</strong> e<strong>in</strong>er<br />

e<strong>in</strong>zigen Tabelle.<br />

Beispiele: Vertragsnummer, Fahrzeug-Kennzeichen, Personalnummer<br />

• Die Verknüpfungen mit anderen Tabellen regelt die Datenbank mit Hilfe der<br />

ID selbständig.<br />

5.5.3. Änderungsdaten<br />

In vielen Fällen ist es s<strong>in</strong>nvoll, wenn <strong>in</strong> e<strong>in</strong>er Tabelle nicht nur der aktuelle Zustand<br />

gespeichert ist, sondern der Verlauf.<br />

• Beispiel mit Änderung des Kennzeichens: Wenn die Polizei nach e<strong>in</strong>em Unfallverursacher<br />

sucht, der <strong>in</strong>zwischen umgezogen ist, wird für das alte (nicht<br />

mehr gültige) Kennzeichen ke<strong>in</strong> Fahrzeug gefunden.<br />

• Adressenänderungen auf Term<strong>in</strong> legen: Es wäre viel zu aufwändig, wenn alle<br />

Änderungen gleichzeitig an dem Stichtag, an dem sie gelten sollen, e<strong>in</strong>gegeben<br />

werden müssten. Besser ist es, sie sofort zu speichern mit Angabe des<br />

Geltungsdatums; die Datenbank holt abhängig vom aktuellen und dem Geltungsdatum<br />

immer die richtige Version.<br />

• Aktueller Steuersatz: Die Abrechnung e<strong>in</strong>er Versandfirma muss immer mit<br />

dem richtigen Steuersatz rechnen, auch wenn er mitten im Jahr geändert wird.<br />

Als Steuersatz 1 für die Mehrwertsteuer wird dann bei e<strong>in</strong>er „älteren“ Rechnung<br />

mit 16 % und bei e<strong>in</strong>er Rechnung neueren Datums mit 19 % gerechnet.<br />

Für alle solche Fälle erhalten Tabellen gerne zusätzliche Spalten gültig von und<br />

gültig bis. Anstelle e<strong>in</strong>er Änderung werden Datensätze verdoppelt; die neuen Informationen<br />

ersetzen dabei die bisher gültigen.<br />

35


Normalisierung<br />

Selbstverständlich erfordert e<strong>in</strong>e solche Datenbankstruktur höheren Aufwand<br />

sowohl beim Entwickler der Datenbank als auch beim Programmierer der Anwendung.<br />

Der zusätzliche Nutzen rechtfertigt diesen Aufwand.<br />

5.5.4. Reihenfolge ohne Bedeutung<br />

Beim Aufbau e<strong>in</strong>er Datenbank darf es nicht relevant se<strong>in</strong>, <strong>in</strong> welcher Reihenfolge<br />

die Zeilen und Spalten vorkommen: Die Funktionalität der Datenbank darf<br />

nicht davon abhängen, dass zwei Spalten <strong>in</strong> e<strong>in</strong>er bestimmten Folge nache<strong>in</strong>ander<br />

kommen oder dass nach e<strong>in</strong>em Datensatz e<strong>in</strong> bestimmter zweiter folgt.<br />

• E<strong>in</strong> Datensatz (also e<strong>in</strong>e Zeile) steht nur über den Primärschlüssel bereit.<br />

• E<strong>in</strong>e Spalte steht nur über den Spaltennamen zur Verfügung.<br />

Nur ausnahmsweise wird mit der Nummer e<strong>in</strong>er Zeile oder Spalte gearbeitet,<br />

z. B. wenn e<strong>in</strong> „Programmierer-Werkzeug“ nur <strong>in</strong>dizierte Spalten kennt.<br />

5.6. Zusammenfassung<br />

Fassen wir diese Erkenntnisse zusammen zu e<strong>in</strong>igen Regeln, die bei der Entwicklung<br />

e<strong>in</strong>er Datenbankstruktur zu beachten s<strong>in</strong>d.<br />

5.6.1. Allgeme<strong>in</strong>e Regeln<br />

Von der Realität ausgehen: Erstellen Sie Tabellen danach, mit welchen D<strong>in</strong>gen<br />

(Objekten) Sie arbeiten. Benutzen Sie aussagekräftige Namen für Tabellen<br />

und Spalten.<br />

E<strong>in</strong>zelne Informationen klar trennen: Alle Informationen werden <strong>in</strong> e<strong>in</strong>zelne<br />

Spalten mit unteilbaren Werten aufgeteilt.<br />

Vollständigkeit: Alle möglichen Informationen sollten von vornhere<strong>in</strong> berücksichtigt<br />

werden. Nachträgliche Änderungen s<strong>in</strong>d zu vermeiden.<br />

Ke<strong>in</strong>e berechneten Werte speichern: E<strong>in</strong>e Information, die aus vorhandenen<br />

Angaben zusammengestellt werden kann, wird nicht als eigene Spalte gespeichert.<br />

Kle<strong>in</strong>e Tabellen bevorzugen: Wenn e<strong>in</strong>e Tabelle sehr viele Informationen umfasst,<br />

ist es besser, sie zu unterteilen und über e<strong>in</strong>en e<strong>in</strong>heitlichen Primärschlüssel<br />

zu verb<strong>in</strong>den.<br />

Ke<strong>in</strong>e doppelten Speicherungen: Es darf weder ganze doppelte Datensätze geben<br />

noch wiederholte Speicherungen gleicher Werte (Redundanz von Daten).<br />

Doppelte Datensätze werden durch Primärschlüssel und Regeln zur<br />

36


Siehe auch<br />

E<strong>in</strong>deutigkeit <strong>in</strong> Feldern vermieden; Datenredundanz wird durch getrennte<br />

Tabellen ersetzt, sodass es nur die Fremdschlüssel mehrfach gibt.<br />

Wichtig ist auch, dass der Aufbau der Datenbank beschrieben und ggf. begründet<br />

wird. Hilfreich s<strong>in</strong>d dabei graphische Darstellungen, wie sie von vielen Datenbankprogrammen<br />

angeboten werden.<br />

5.6.2. Abweichungen<br />

Die vorstehenden Überlegungen werden nicht immer beachtet. Für fast jede Regel<br />

gibt es begründete Ausnahmen<br />

Beispielsweise enthält e<strong>in</strong>e Datenbank mit Adressen <strong>in</strong> der Regel die Adressen<br />

im Klartext und nicht nur als Verweise auf die Nummern von Orten, Ortsteilen,<br />

Straßen und Straßenabschnitten der Deutschen Post.<br />

Entscheiden Sie erst nach bewusster Abwägung von Vor- und Nachteilen, wenn<br />

Sie von e<strong>in</strong>er der Regeln abweichen wollen.<br />

5.7. Siehe auch<br />

Über Wikipedia s<strong>in</strong>d weitere Informationen zu f<strong>in</strong>den:<br />

• Normalisierung 1<br />

• Relationale Datenbank 2<br />

• Edgar F. Codd 3 und se<strong>in</strong>e 12 Regeln 4 für relationale Datenbanken<br />

• Update-Anomalien 5<br />

• Redundanz von Daten 6<br />

Für e<strong>in</strong>e s<strong>org</strong>fältige Planung e<strong>in</strong>er Adressen-Datenbank hilft die Datenstruktur<br />

der Deutschen Post:<br />

• Broschüre Datafactory 7 (360 kB) als Überblick<br />

Unter Post Direkt 8 kann die Datei 20101230_HB_Leitdaten.pdf mit der Datensatzbeschreibung<br />

kostenfrei bestellt werden.<br />

1 http://de.wikipedia.<strong>org</strong>/wiki/Normalisierung%20(Datenbank)<br />

2 http://de.wikipedia.<strong>org</strong>/wiki/Relationale%20Datenbank<br />

3 http://de.wikipedia.<strong>org</strong>/wiki/Edgar%20F.%20Codd<br />

4 http://de.wikipedia.<strong>org</strong>/wiki/Onl<strong>in</strong>e_Analytical_Process<strong>in</strong>g%2312_Regeln_nach_Codd<br />

5 http://de.wikipedia.<strong>org</strong>/wiki/Anomalie%20(Informatik)<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/Redundanz%20(Informationstheorie)<br />

7 http://www.deutschepost.de/downloadServlet?target=/mlm.nf/dpag/images/d/datafactory<br />

/20100205_datafactory_<strong>in</strong>ternet.pdf<br />

8 mailto:<strong>in</strong>fo@postdirekt.de<br />

37


6. Beispieldatenbank<br />

6.1. Sachverhalt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39<br />

6.2. Schematische Darstellung . . . . . . . . . . . . . . . . . . . . . . 41<br />

6.3. Tabellenstruktur und Datenbank . . . . . . . . . . . . . . . . . . 42<br />

6.4. Anmerkungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />

Dieses Kapitel bespricht die Grundlagen der Beispieldatenbank. Alle E<strong>in</strong>zelheiten<br />

der Tabellen stehen im Anhang unter Tabellenstruktur der Beispieldatenbank<br />

1 .<br />

i H<strong>in</strong>weis<br />

Alle Beispiele, Erläuterungen und Aufgaben beziehen sich auf diese<br />

Datenstruktur. Bitte schlagen Sie (soweit erforderlich) immer dort nach.<br />

6.1. Sachverhalt<br />

Die Beispieldatenbank <strong>in</strong> diesem Buch versucht, e<strong>in</strong>e Versicherungsgesellschaft<br />

für Kfz-Versicherungen abzubilden. Das Beispiel ist e<strong>in</strong>e starke Vere<strong>in</strong>fachung<br />

der Realität; zum Beispiel fehlen alle Teile der laufenden Abrechnung der Prämien<br />

und Schadensfälle.<br />

Die relevanten Begriffe s<strong>in</strong>d für e<strong>in</strong>e bessere Übersichtlichkeit fett gekennzeichnet.<br />

Folgender Sachverhalt wird <strong>in</strong> der Datenbank abgebildet:<br />

Die Versicherungsgesellschaft UnsereFirma verwaltet mit dieser Datenbank ihre<br />

Kundendaten und die Schadensfälle. Wegen der Schadensfälle müssen auch<br />

Daten „fremder“ Kunden und Versicherungen gespeichert werden.<br />

Jeder Versicherungsvertrag wird mit e<strong>in</strong>em Versicherungsnehmer über genau<br />

e<strong>in</strong> Fahrzeug abgeschlossen. Der Versicherungsvertrag wird durch folgende Eigenschaften<br />

gekennzeichnet; zu jeder Eigenschaft gehört e<strong>in</strong>e Spalte:<br />

1 Anhang A auf Seite 419<br />

39


Beispieldatenbank<br />

• Vertragsnummer (Pflicht, e<strong>in</strong>deutig)<br />

• Datum des Abschlusses, Art des Vertrages (Pflicht); dabei gibt es die Arten Haftpflicht<br />

(HP), Haftpflicht mit Teilkasko (TK), Vollkasko (VK).<br />

• Verweis auf den Versicherungsnehmer (Pflicht)<br />

• Verweis auf das Fahrzeug (Pflicht, e<strong>in</strong>deutig)<br />

• Verweis auf den Mitarbeiter, der den Vertrag bearbeitet (Pflicht)<br />

Der Versicherungsnehmer ist gekennzeichnet durch diese Eigenschaften:<br />

• Kundennummer (Pflicht, e<strong>in</strong>deutig)<br />

• Name und Anschrift: PLZ, Ort, Straße, Hausnummer (Pflicht)<br />

• bei natürlichen Personen zusätzlich durch Vorname, Geburtsdatum, Datum<br />

des Führersche<strong>in</strong>s (optional)<br />

• Verweis auf e<strong>in</strong>e „Fremdversicherung“, wenn e<strong>in</strong> (fremdes) Fahrzeug an e<strong>in</strong>em<br />

Unfall beteiligt ist (optional)<br />

Das Fahrzeug ist gekennzeichnet durch diese Eigenschaften:<br />

• polizeiliches Kennzeichen (Pflicht, e<strong>in</strong>deutig)<br />

• Farbe (optional)<br />

• Fahrzeugtyp und damit <strong>in</strong>direkt auch den Fahrzeughersteller (Pflicht)<br />

E<strong>in</strong> Mitarbeiter ist gekennzeichnet durch diese Eigenschaften:<br />

• Name, Vorname, Geburtsdatum (Pflicht)<br />

• Personalnummer (Pflicht, e<strong>in</strong>deutig)<br />

• Verweis auf Abteilung, Vermerk, ob es sich um den Leiter der Abteilung handelt<br />

(Pflicht)<br />

• Kontaktdaten wie Telefon, Mobiltelefon, Email, Raum (optional)<br />

Die Abteilung ist gekennzeichnet durch diese Eigenschaften:<br />

• Nummer (Pflicht, e<strong>in</strong>deutig)<br />

• Kurzbezeichnung, Bezeichnung (Pflicht, e<strong>in</strong>deutig)<br />

• Ort (optional)<br />

Zusätzlich gibt es Dienstwagen. Dabei handelt es sich um e<strong>in</strong>e Tabelle mit den<br />

gleichen Eigenschaften wie bei Fahrzeug und zusätzlich:<br />

• Verweis auf den Mitarbeiter, zu dem e<strong>in</strong> Dienstwagen gehört (optional)<br />

• denn es gibt auch Firmenwagen, die ke<strong>in</strong>em Mitarbeiter persönlich zugeordnet<br />

s<strong>in</strong>d<br />

E<strong>in</strong> Schadensfall ist gekennzeichnet durch diese Eigenschaften:<br />

40


• Datum, Ort und Umstände des Unfalls (Pflicht)<br />

Schematische Darstellung<br />

• Vermerk, ob es Verletzte gab, sowie Höhe des Gesamtschadens (optional, denn<br />

die Angaben könnten erst später bekannt werden)<br />

• Verweis auf den Mitarbeiter, der den Schadensfall bearbeitet (Pflicht)<br />

An e<strong>in</strong>em Schadensfall können mehrere Fahrzeuge unterschiedlicher Versicherungen<br />

beteiligt se<strong>in</strong>. (Unfälle mit Radfahrern und Fußgängern werden nicht betrachtet.)<br />

Deshalb gibt es e<strong>in</strong>e weitere Tabelle Zuordnung_SF_FZ mit diesen Eigenschaften:<br />

• Liste aller Schadensfälle und aller beteiligten Fahrzeuge (Pflicht)<br />

• anteilige Schadenshöhe e<strong>in</strong>es Fahrzeugs an dem betreffenden Unfall (optional)<br />

Über die Verb<strong>in</strong>dung Schadensfall → Fahrzeug → Versicherungsvertrag → Versicherungsnehmer<br />

→ Versicherungsgesellschaft können alle beteiligten Gesellschaften<br />

festgestellt und <strong>in</strong> die Schadensabwicklung e<strong>in</strong>gebunden werden.<br />

Bitte beachten Sie auch die unten stehenden H<strong>in</strong>weise über „Fehlende Spalten<br />

und E<strong>in</strong>schränkungen“.<br />

6.2. Schematische Darstellung<br />

Die vorstehende Struktur kann im folgenden Diagramm dargestellt werden. Die<br />

Verweise, nämlich die Verknüpfungen zwischen den Tabellen s<strong>in</strong>d daraus so abzulesen:<br />

• Von jeder Tabelle gibt es e<strong>in</strong>en Verweis auf e<strong>in</strong>en E<strong>in</strong>trag <strong>in</strong> der direkt darunter<br />

stehenden Tabelle.<br />

• Zu jedem Vertrag gehört e<strong>in</strong> Sachbearbeiter; das wird durch den Pfeil nach<br />

l<strong>in</strong>ks außen angedeutet, der <strong>in</strong> der Tabelle Mitarbeiter von rechts here<strong>in</strong>kommt.<br />

• Zu jedem Vertrag gehört genau e<strong>in</strong> Fahrzeug; zu jedem Fahrzeug gehört e<strong>in</strong><br />

Vertrag.<br />

• Jeder E<strong>in</strong>trag <strong>in</strong> der Liste der Zuordnungen Schadensfall, Fahrzeug bezieht sich<br />

auf e<strong>in</strong>en Schadensfall und e<strong>in</strong> Fahrzeug; dabei gilt:<br />

• Zu jedem Schadensfall gehören e<strong>in</strong> oder mehrere Fahrzeuge.<br />

• Nicht jedes Fahrzeug hat e<strong>in</strong>en Schadensfall.<br />

• E<strong>in</strong> Dienstwagen kann e<strong>in</strong>em Mitarbeiter zugeordnet se<strong>in</strong>, muss es aber nicht:<br />

Es gibt auch nicht-persönliche Dienstwagen.<br />

41


Beispieldatenbank<br />

Tabellen und ihre Zusammenhänge<br />

Doppelte Pfeile zeigen e<strong>in</strong>e Muss-Verknüpfung, e<strong>in</strong>fache e<strong>in</strong>e Kann-Verknüpfung.<br />

Die Kürzel bei den Tabellennamen werden künftig immer wieder als „Alias“ anstelle<br />

des „Langnamens“ verwendet. In dieser Übersicht wurden sie wegen der<br />

Deutlichkeit großgeschrieben; künftig werden sie kle<strong>in</strong>geschrieben.<br />

6.3. Tabellenstruktur und Datenbank<br />

Im Anhang ist die Tabellenstruktur der Beispieldatenbank beschrieben, nämlich<br />

die Liste der Tabellen und aller ihrer Spalten. Innerhalb des Buches wird die Datenbankstruktur<br />

erweitert; dabei werden e<strong>in</strong>ige der folgenden Anmerkungen beachtet.<br />

Im Anhang stehen auch Wege, wie Sie über die Download-Seite 2 die Beispieldatenbank<br />

erhalten. E<strong>in</strong>e kurze Beschreibung für die Installation steht dort. Für<br />

Details nutzen Sie bitte die Dokumentation des jeweiligen Datenbankmanagementsystems;<br />

L<strong>in</strong>ks dazu stehen <strong>in</strong> der E<strong>in</strong>leitung 3 sowie unter Webl<strong>in</strong>ks 4 .<br />

Zum Abschluss werden auch e<strong>in</strong> paar Verfahren behandelt, schnell viele Datensätze<br />

als Testdaten 5 zu erzeugen. Für das Verständnis der Inhalte des Buchs s<strong>in</strong>d<br />

diese Verfahren nicht wichtig. Sie s<strong>in</strong>d aber äußerst nützlich, wenn Sie weitere<br />

Möglichkeiten der <strong>SQL</strong>-Befehle ausprobieren wollen.<br />

2 Anhang B auf Seite 423<br />

3 Kapitel 3 auf Seite 11<br />

4 Anhang D auf Seite 445<br />

5 Kapitel 36 auf Seite 407<br />

42


6.4. Anmerkungen<br />

Anmerkungen<br />

Die Beispieldatenbank wurde erstellt und e<strong>in</strong>gefügt, als sich das Buch noch<br />

ziemlich am Anfang befand. Im Laufe der Zeit wurden bei der Konzeption und<br />

der Umsetzung <strong>in</strong> Beispielen Unstimmigkeiten und Mängel festgestellt. Hier<br />

wird darauf h<strong>in</strong>gewiesen: Teilweise wurden sie beseitigt, teilweise werden wir die<br />

Datenstruktur <strong>in</strong> späteren Kapiteln ändern, teilweise belassen wir es dabei.<br />

Namen von Tabellen und Spalten<br />

Für die Bezeichner haben sich e<strong>in</strong> paar Regeln als s<strong>in</strong>nvoll herausgestellt.<br />

S<strong>in</strong>gular verwenden: Es ist üblicher, e<strong>in</strong>e Tabelle im S<strong>in</strong>gular zu bezeichnen.<br />

Es ist e<strong>in</strong>facher zu sagen: E<strong>in</strong> Versicherungsvertrag hat diese Eigenschaften.<br />

. . Umständlicher wäre: E<strong>in</strong> Datensatz der Tabelle Versicherungsverträge<br />

hat diese Eigenschaften. . .<br />

Vor allem beim Programmieren werden die Namen mit anderen Begriffen<br />

verknüpft; es käme dann etwas wie VertraegePositionenRow zustande<br />

− also die Positionen von Verträgen −, wo nur e<strong>in</strong>e e<strong>in</strong>zelne Zeile, nämlich<br />

e<strong>in</strong>e Position e<strong>in</strong>es Vertrags geme<strong>in</strong>t ist.<br />

In e<strong>in</strong>em Programmiererforum gab es e<strong>in</strong>mal e<strong>in</strong>e Diskussion über<br />

„S<strong>in</strong>gular oder Plural“. Dort wurde das als uraltes Streitthema bezeichnet,<br />

das zu heftigsten Ause<strong>in</strong>andersetzungen führen kann. Im<br />

Ergebnis sollte es eher als Geschmackssache angesehen werden.<br />

Also hat der Autor <strong>in</strong> diesem Buch se<strong>in</strong>en Geschmack durchgesetzt.<br />

Beschreibender Name: Die Spalten von Tabellen sollen den Inhalt klar angeben;<br />

das ist durchgehend berücksichtigt worden. Es ist nicht üblich, e<strong>in</strong><br />

Tabellenkürzel als Teil des Spaltennamens zu verwenden; denn e<strong>in</strong> Spaltenname<br />

wird immer <strong>in</strong> Zusammenhang mit e<strong>in</strong>er bestimmten Tabelle benutzt.<br />

Englische Begriffe: Es ist häufig praktischer, englische Begriffe für Tabellen und<br />

Spalten zu verwenden. Programmierer müssen die Namen weiterverarbeiten;<br />

weil sowohl Datenbanken als auch Programmiersprachen mit Englisch<br />

arbeiten, entstehen ständig komplexe Namen. Dabei ist e<strong>in</strong> Begriff wie CustomerNumberChanged<br />

(also re<strong>in</strong>es Englisch) immer noch besser als etwas<br />

wie KundeNummerChanged, also denglischer Mischmasch.<br />

Auf diese Änderung wird verzichtet, weil es ziemlich umständlich<br />

gewesen wäre. Außerdem spielt die Weiterverwendung durch Programmierer<br />

<strong>in</strong> diesem Buch eigentlich ke<strong>in</strong>e Rolle.<br />

43


Beispieldatenbank<br />

Aufteilung der Tabellen<br />

Die Aufgaben der Versicherungsgesellschaft werden nur e<strong>in</strong>geschränkt behandelt.<br />

Es fehlen Tabellen für Abrechnung und Zahlung der Prämien sowie Abrechnung<br />

der Kosten für Schadensfälle. Auf diese Aufgaben wird hier verzichtet.<br />

Die Tarife und damit die Höhe der Basisprämie gehören <strong>in</strong> e<strong>in</strong>e eigene Tabelle;<br />

e<strong>in</strong> Versicherungsvertrag müsste darauf verweisen. Auf diese Aufteilung wird<br />

wegen der Vere<strong>in</strong>fachung verzichtet.<br />

Sowohl Mitarbeiter als auch Versicherungsnehmer s<strong>in</strong>d Personen: Auch zu e<strong>in</strong>em<br />

Versicherungsnehmer passen Kontaktdaten; auch bei e<strong>in</strong>em Mitarbeiter ist<br />

die Privatanschrift von Bedeutung. Es wäre deshalb vernünftig, e<strong>in</strong>e geme<strong>in</strong>same<br />

Tabelle Person e<strong>in</strong>zurichten; die Tabelle Mitarbeiter würde Verweise auf diese<br />

Tabelle erhalten. Auf diese Änderung wird wegen der Vere<strong>in</strong>fachung verzichtet.<br />

Man kann streiten, ob der Verweis auf e<strong>in</strong>e fremde Versicherungsgesellschaft<br />

besser zum Versicherungsvertrag oder zum Versicherungsnehmer gehört.<br />

Die Verknüpfungen zwischen den Tabellen fehlten <strong>in</strong> der Ursprungsversion<br />

der Beispieldatenbank völlig. Sie werden <strong>in</strong> den Kapiteln Fremdschlüssel-<br />

Beziehungen 6 besprochen und <strong>in</strong> Änderung der Datenbankstruktur 7 e<strong>in</strong>gebaut.<br />

Fehlende Spalten und E<strong>in</strong>schränkungen<br />

Neben den genannten Punkten s<strong>in</strong>d weitere Spalten s<strong>in</strong>nvoll bzw. notwendig:<br />

• Tabelle Versicherungsvertrag: Basisprämie, Prämiensatz, letzte Änderung des<br />

Prämiensatzes<br />

• Tabelle Zuordnung_SC_FZ: Anteil am Verschulden<br />

• Tabellen Versicherungsnehmer und Mitarbeiter: Geschlecht m/w wegen der<br />

Anrede <strong>in</strong> Briefen<br />

Viele E<strong>in</strong>schränkungen fehlen <strong>in</strong> der ursprünglichen Version:<br />

• Spalten, deren Werte e<strong>in</strong>deutig se<strong>in</strong> müssen<br />

• Spalten, die nur bestimmte Werte annehmen dürfen<br />

• Tabellen, bei denen Spaltenwerte <strong>in</strong> gewisser Beziehung stehen müssen<br />

Diese Änderungen werden <strong>in</strong> DDL – E<strong>in</strong>zelheiten 8 besprochen und <strong>in</strong> Änderung<br />

der Datenbankstruktur 9 e<strong>in</strong>gebaut.<br />

6 Kapitel 29 auf Seite 305<br />

7 Kapitel 35 auf Seite 397<br />

8 Kapitel 28 auf Seite 285<br />

9 Kapitel 35 auf Seite 397<br />

44


Teil II.<br />

Grundlagen<br />

45


7. <strong>SQL</strong>-Befehle<br />

7.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . 48<br />

7.2. DML − Data Manipulation Language . . . . . . . . . . . . . . . 49<br />

7.3. DDL − Data Def<strong>in</strong>ition Language . . . . . . . . . . . . . . . . . . 51<br />

7.4. TCL − Transaction Control Language . . . . . . . . . . . . . . . 52<br />

7.5. DCL − Data Control Language . . . . . . . . . . . . . . . . . . . . 52<br />

7.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />

7.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />

7.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />

Manchen E<strong>in</strong>steigern mag <strong>SQL</strong> sehr sperrig ersche<strong>in</strong>en. <strong>SQL</strong> ist weniger für improvisierte<br />

E<strong>in</strong>malabfragen gedacht als vielmehr zur Entwicklung von stabilen,<br />

dauerhaft nutzbaren Abfragen. Wenn die Abfrage e<strong>in</strong>mal entwickelt ist, wird sie<br />

meistens <strong>in</strong> e<strong>in</strong>e GUI- oder HTML-Umgebung e<strong>in</strong>gebunden, sodass der Benutzer<br />

mit dem <strong>SQL</strong>-Code gar nicht mehr <strong>in</strong> Berührung kommt.<br />

Es gibt zwar e<strong>in</strong>en <strong>SQL</strong>-Standard (siehe das Kapitel E<strong>in</strong>leitung 1 ), der möglichst<br />

von allen Datenbanken übernommen werden soll, aber leider hält sich<br />

ke<strong>in</strong> angebotenes DBMS (Datenbank-Management-System) vollständig daran.<br />

Es kommt also je nach DBMS zu leichten bis großen Abweichungen, und für Sie<br />

führt ke<strong>in</strong> Weg daran vorbei, immer wieder <strong>in</strong> der Dokumentation ihrer persönlichen<br />

Datenbank nachzulesen. Das gilt natürlich besonders für Verfahren, die im<br />

<strong>SQL</strong>-Standard gar nicht enthalten s<strong>in</strong>d, von Ihrem DBMS trotzdem angeboten<br />

werden.<br />

i H<strong>in</strong>weis<br />

Die Abweichungen e<strong>in</strong>es bestimmten DBMS vom <strong>SQL</strong>-Standard werden<br />

üblicherweise als <strong>SQL</strong>-Dialekt bezeichnet.<br />

1 Abschnitt 3.1 auf Seite 12<br />

47


<strong>SQL</strong>-Befehle<br />

7.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise<br />

Die gesamte Menge an Befehlen ist recht überschaubar; Schwierigkeiten machen<br />

die vielen Parameter mit zahlreichen Varianten.<br />

Der Übersicht halber wurde <strong>SQL</strong> <strong>in</strong> Teilbereiche gegliedert; allerd<strong>in</strong>gs gibt es<br />

auch für diese Aufteilung Unterschiede bei den DBMS, den Dokumentationen<br />

und <strong>in</strong> Fachbüchern. Diese Aufteilung (siehe Inhaltsverzeichnis dieses Kapitels)<br />

dient aber nicht nur der Übersicht, sondern hat auch praktische Gründe:<br />

• DML-Befehle werden vor allem von „e<strong>in</strong>fachen“ Anwendern benutzt.<br />

• DDL- und TCL-Befehle dienen Programmierern.<br />

• DCL-Befehle gehören zum Aufgabenbereich von Systemadm<strong>in</strong>istratoren.<br />

Dies s<strong>in</strong>d die Bestandteile e<strong>in</strong>es e<strong>in</strong>zelnen Befehls:<br />

• der Name des Befehls<br />

• der Name des Objekts (Datenbank, Tabelle, Spalte usw.)<br />

• e<strong>in</strong> H<strong>in</strong>weis zur Maßnahme, soweit diese nicht durch den Befehl klar ist, sowie<br />

E<strong>in</strong>zelheiten<br />

• das Semikolon als Zeichen für den Abschluss e<strong>in</strong>es <strong>SQL</strong>-Befehls<br />

Damit dies alles e<strong>in</strong>deutig ist, gibt es e<strong>in</strong>e Reihe von Schlüsselwörtern 2 (key<br />

words, reserved words, non-reserved words), anhand derer das DBMS die Informationen<br />

<strong>in</strong>nerhalb e<strong>in</strong>es Befehls erkennt.<br />

Die Schreibweise e<strong>in</strong>es Befehls ist flexibel.<br />

• Groß- und Kle<strong>in</strong>schreibung der Schlüsselwörter werden nicht unterschieden.<br />

• E<strong>in</strong> Befehl kann beliebig auf e<strong>in</strong>e oder mehrere Zeilen verteilt werden; der<br />

wichtigste Gesichtspunkt dabei ist die Lesbarkeit auch für Sie selbst.<br />

• Das Semikolon ist nicht immer erforderlich, wird aber empfohlen. Bei manchen<br />

DBMS wird der Befehl erst nach e<strong>in</strong>em folgenden GO o. ä. ausgeführt.<br />

Für eigene Bezeichner, d. h. die Namen von Tabellen, Spalten oder eigenen<br />

Funktionen gilt:<br />

• Vermeiden Sie unbed<strong>in</strong>gt, Schlüsselwörter dafür zu verwenden; dies führt<br />

schnell zu Problemen auch dort, wo es möglich wäre.<br />

• Das Wort muss <strong>in</strong> der Regel mit e<strong>in</strong>em Buchstaben oder dem Unterstrich a...z<br />

A...Z _ beg<strong>in</strong>nen. Danach folgen beliebig Ziffern und Buchstaben.<br />

• Inwieweit andere Zeichen und länderspezifische Buchstaben (Umlaute) möglich<br />

s<strong>in</strong>d, hängt vom DBMS ab.<br />

2 Anhang C.5 auf Seite 440<br />

48


DML − Data Manipulation Language<br />

In diesem Buch wird von Umlauten und dergleichen abgeraten. Bei<br />

den Erläuterungen zur Beispieldatenbank 3 (Namen von Tabellen und<br />

Spalten) wird sowieso für englische Bezeichner plädiert.<br />

Dieses Buch geht davon aus, dass Schlüsselwörter nicht als Bezeichner dienen<br />

und auch Umlaute nicht benutzt werden.<br />

Kommentare können <strong>in</strong> <strong>SQL</strong>-Befehle fast beliebig e<strong>in</strong>gefügt werden (nur die<br />

Schlüsselwörter dürfen natürlich nicht „zerrissen“ werden). Es gibt zwei Arten<br />

von Kommentaren:<br />

-- (doppelter B<strong>in</strong>destrich, am besten mit Leerzeichen dah<strong>in</strong>ter)<br />

Alles von den beiden Strichen an (e<strong>in</strong>schließlich) bis zum Ende dieser Zeile gilt<br />

als Kommentar und nicht als Bestandteil des Befehls.<br />

/* (längerer Text, gerne auch über mehrere Zeilen) */<br />

Alles, was zwischen /* und */ steht (e<strong>in</strong>schließlich dieser Begrenzungszeichen),<br />

gilt als Kommentar und nicht als Bestandteil des Befehls.<br />

7.2. DML − Data Manipulation Language<br />

DML beschäftigt sich mit dem Inhalt des Datenbestandes. „Manipulation“ ist<br />

dabei nicht nur im S<strong>in</strong>ne von „Manipulieren“ zu verstehen, sondern allgeme<strong>in</strong>er<br />

im S<strong>in</strong>ne von „<strong>in</strong> die Hand nehmen“ (lat. manus = Hand).<br />

Das Kapitel DML (1) – Daten abfragen 4 befasst sich mit dem SELECT-Befehl.<br />

• Gelegentlich f<strong>in</strong>den Sie dafür auch den Begriff Data Query Language (DQL).<br />

Diese <strong>E<strong>in</strong>führung</strong> fasst ihn als Teilgebiet der DML auf; nur aus Gründen der<br />

Übersicht gibt es getrennte Kapitel.<br />

Die Befehle INSERT, UPDATE, DELETE dienen der Speicherung von Daten und<br />

werden <strong>in</strong> DML (2) – Daten speichern 5 behandelt.<br />

Bei diesen Befehlen ist immer genau e<strong>in</strong>e Tabelle − nur bei SELECT auch mehrere<br />

− anzugeben, dazu Art und Umfang der Arbeiten sowie <strong>in</strong> aller Regel mit<br />

WHERE e<strong>in</strong>e Liste von Bed<strong>in</strong>gungen, welche Datensätze zu bearbeitet s<strong>in</strong>d.<br />

Die folgenden Erläuterungen s<strong>in</strong>d e<strong>in</strong>heitlich für alle DML-Befehle zu beachten.<br />

3 Kapitel 6 auf Seite 39<br />

4 Kapitel 8 auf Seite 57<br />

5 Kapitel 9 auf Seite 69<br />

49


<strong>SQL</strong>-Befehle<br />

7.2.1. Datenmengen, nicht e<strong>in</strong>zelne Datensätze<br />

Bitte beachten Sie, dass <strong>SQL</strong> grundsätzlich mengenorientiert arbeitet. DML-<br />

Befehle wirken sich meistens nicht nur auf e<strong>in</strong>en Datensatz aus, sondern auf e<strong>in</strong>e<br />

ganze Menge, die aus 0, e<strong>in</strong>em oder mehreren Datensätzen bestehen kann. Auch<br />

die WHERE-Bed<strong>in</strong>gungen s<strong>org</strong>en „nur“ für den Umfang der Datenmenge; aber<br />

das Ergebnis ist immer e<strong>in</strong>e Datenmenge.<br />

Im E<strong>in</strong>zelfall wissen Sie als Anwender oder Programmierer natürlich häufig, ob<br />

die Datenmenge 0, 1 oder n Datensätze enthalten kann oder soll. Aber Sie müssen<br />

selbst darauf achten, denn <strong>SQL</strong> oder das DBMS können das nicht wissen.<br />

Die Struktur als Datenmenge führt auch dazu, dass es bei e<strong>in</strong>em SELECT-Befehl<br />

ke<strong>in</strong>e „natürliche“ Reihenfolge gibt, <strong>in</strong> der die Daten angezeigt werden. Manchmal<br />

kommen sie <strong>in</strong> der Reihenfolge, <strong>in</strong> der sie gespeichert wurden; aber bei umfangreicheren<br />

Tabellen mit vielen Änderungen sieht es eher nach e<strong>in</strong>em großen<br />

Durche<strong>in</strong>ander aus − es sei denn, Sie verwenden die ORDER BY-Klausel.<br />

7.2.2. <strong>SQL</strong>-Ausdrücke<br />

In vielen Fällen wird der Begriff Ausdruck verwendet. Dies ist e<strong>in</strong> allgeme<strong>in</strong>er<br />

Begriff für verschiedene Situationen:<br />

• An Stellen, an denen e<strong>in</strong> e<strong>in</strong>zelner Wert angegeben werden muss, kann auch<br />

e<strong>in</strong> Werte-Ausdruck verwendet werden: e<strong>in</strong> konstanter Wert (Zahl, Text, boolescher<br />

Wert), das Ergebnis e<strong>in</strong>er Funktion, die e<strong>in</strong>en solchen Wert zurückgibt,<br />

oder e<strong>in</strong>e Abfrage, die als Ergebnis e<strong>in</strong>en e<strong>in</strong>zigen Wert liefert.<br />

• An Stellen, an denen e<strong>in</strong>e oder mehrere Zeilen e<strong>in</strong>er Datenmenge angegeben<br />

werden müssen, kann auch e<strong>in</strong> <strong>SQL</strong>-Ausdruck (im <strong>SQL</strong>-Standard als query expression<br />

bezeichnet) verwendet werden. Dabei handelt es sich um e<strong>in</strong>en <strong>SQL</strong>-<br />

Befehl (meistens SELECT), der e<strong>in</strong>e Menge von Datensätzen liefert.<br />

• An Stellen, an denen e<strong>in</strong>e Liste e<strong>in</strong>zelner Werte angegeben werden muss, kann<br />

ebenfalls e<strong>in</strong> <strong>SQL</strong>-Ausdruck verwendet werden; dieser muss dann als Ergebnis<br />

e<strong>in</strong>e Menge passender Werte liefern.<br />

7.2.3. Daten<strong>in</strong>tegrität<br />

Bei allen Datenmanipulationen ist zu beachten, dass die Bed<strong>in</strong>gungen für Querverweise<br />

erhalten bleiben, siehe das Kapitel Fremdschlüssel-Beziehungen 6 . Beispielsweise<br />

darf e<strong>in</strong> Datensatz <strong>in</strong> der Tabelle Abteilung erst dann gelöscht werden,<br />

wenn alle zugeordneten Mitarbeiter gelöscht oder versetzt wurden.<br />

6 Kapitel 29 auf Seite 305<br />

50


DDL − Data Def<strong>in</strong>ition Language<br />

7.2.4. H<strong>in</strong>weis für Programmierer: Parameter benutzen!<br />

E<strong>in</strong> <strong>SQL</strong>-Befehl <strong>in</strong> e<strong>in</strong>em Programm sollte niemals als „langer Str<strong>in</strong>g“ mit festen<br />

Texten erstellt werden, sondern mit Parametern. Das erleichtert die Wiederverwendung,<br />

die E<strong>in</strong>b<strong>in</strong>dung von Werten, vermeidet Probleme mit der Formatierung<br />

von Zahlen und Datumsangaben und verh<strong>in</strong>dert <strong>SQL</strong>-Injection.<br />

Im Kapitel DML (2) – Daten speichern 7 steht bei UPDATE so e<strong>in</strong> Beispiel:<br />

Dies korrigiert die Schreibweise des Namens beim Mitarbeiter mit der Personalnummer 20001<br />

UPDATE Mitarbeiter<br />

SET Name = ’Mayer’<br />

WHERE Personalnummer = 20001;<br />

Dieses Beispiel sieht besser so aus:<br />

UPDATE Mitarbeiter<br />

SET Name = @Name<br />

WHERE Personalnummer = @PersNr;<br />

Bei ADO.NET und Visual Basic für MS-<strong>SQL</strong> wird dieser Befehl so verwendet.<br />

Visual Basic-Quelltext<br />

Dim sel As Str<strong>in</strong>g = "UPDATE ... @PersNr;"<br />

Dim cmd As SqlCommand = new SqlCommand(sel)<br />

cmd.Parameters.AddWithValue("@Name", "Mayer")<br />

cmd.Parameters.AddWithValue("@PersNr", 20001)<br />

Zwar unterscheidet sich die Angabe der Parameter nach Programmiersprache<br />

und DBMS. Auch ist es etwas mehr Schreibarbeit. Aber viel wichtiger s<strong>in</strong>d die<br />

Unterschiede nach den Datentypen; <strong>in</strong>sgesamt wird die Arbeit sehr viel sicherer.<br />

Parameter gibt es nur für DML-Befehle, nicht für DDL oder DCL.<br />

7.3. DDL − Data Def<strong>in</strong>ition Language<br />

DDL def<strong>in</strong>iert die Struktur e<strong>in</strong>er Datenbank.<br />

Hierzu gibt es die Befehle CREATE, ALTER, DROP; diese werden für Datenbank-<br />

Objekte DATABASE, TABLE, VIEW usw. verwendet.<br />

E<strong>in</strong>zelheiten werden <strong>in</strong> DDL – Struktur der Datenbank 8 behandelt.<br />

7 Kapitel 9 auf Seite 69<br />

8 Kapitel 10 auf Seite 79<br />

51


<strong>SQL</strong>-Befehle<br />

7.4. TCL − Transaction Control Language<br />

Damit die Daten dauerhaft zusammenpassen, also die Integrität der Daten gewahrt<br />

bleibt, sollen Änderungen, die zusammengehören, auch „am Stück“ übertragen<br />

und gespeichert werden. Falls e<strong>in</strong>e e<strong>in</strong>zelne dieser Änderungen nicht<br />

funktioniert, muss der gesamte V<strong>org</strong>ang rückgängig gemacht werden.<br />

Dies wird durch Transaktionen gesteuert, die mit COMMIT oder ROLLBACK abgeschlossen<br />

werden.<br />

E<strong>in</strong> paar Grundlagen werden <strong>in</strong> TCL – Ablaufsteuerung 9 behandelt.<br />

7.5. DCL − Data Control Language<br />

E<strong>in</strong>e „vollwertige“ <strong>SQL</strong>-Datenbank regelt umfassend die Rechte für den Zugriff<br />

auf Objekte (Tabellen, e<strong>in</strong>zelne Felder, <strong>in</strong>terne Funktionen usw.). Dafür kommen<br />

die Befehle GRANT und REVOKE zum E<strong>in</strong>satz.<br />

E<strong>in</strong>ige H<strong>in</strong>weise stehen <strong>in</strong> DCL – Zugriffsrechte 10 .<br />

7.6. Zusammenfassung<br />

In diesem Kapitel lernten wir grundlegende Informationen zu <strong>SQL</strong>-Befehlen<br />

kennen:<br />

• Die Befehle der DML (Data Manipulation Language) bearbeiten die Daten und<br />

gehören zum Aufgabenbereich e<strong>in</strong>es jeden Benutzers.<br />

• Mit SELECT werden Daten abgerufen, mit INSERT und UPDATE gespeichert<br />

und mit DELETE gelöscht.<br />

• Die DML-Befehle arbeiten mengenorientiert; anstelle konkreter Werte werden<br />

häufig Ausdrücke verwendet.<br />

• Die Befehle der DDL (Data Def<strong>in</strong>ition Language) steuern die <strong>in</strong>terne Struktur<br />

der Datenbank und gehören zum Arbeitsbereich von Programmentwicklern.<br />

• Die Befehle der TCL (Transaction Control Language) s<strong>org</strong>en für die Integrität<br />

der Daten beim Speichern und gehören ebenfalls zum Aufgabenbereich von<br />

Programmentwicklern.<br />

• Die Befehle der DCL (Data Control Language) s<strong>org</strong>en für die Zugriffssicherheit<br />

und gehören zum Aufgabenbereich von Systemadm<strong>in</strong>istratoren.<br />

9 Kapitel 11 auf Seite 89<br />

10 Kapitel 12 auf Seite 95<br />

52


Übungen<br />

Die Bestandteile der <strong>SQL</strong>-Befehle werden anhand von Schlüsselwörtern erkannt<br />

und ausgewertet.<br />

7.7. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 54.<br />

Übung 1 – Begriffsklärung<br />

Was versteht man unter „<strong>SQL</strong>“?<br />

1. Süß − Quadratisch − Lecker<br />

2. Server’s Quick Library<br />

3. Structured Query Language<br />

4. Standard Quarterly Lectures<br />

Übung 2 – Begriffsklärung<br />

Was versteht man unter „DBMS“?<br />

1. Datenbankmanagementsprache<br />

2. Datenbankmanagementsystem<br />

3. Data-Base Manipulation Standard<br />

4. Deutsche Bahn Mobilstation<br />

Übung 3 – Begriffsklärung<br />

Was versteht man unter „<strong>SQL</strong>-Dialekt“?<br />

1. die Sprache des Computers<br />

2. die Sprache des Benutzers<br />

3. die Sprache des Programmierers e<strong>in</strong>es DBMS<br />

4. die Abweichungen der <strong>SQL</strong>-Befehle vom <strong>SQL</strong>-Standard<br />

5. die jeweilige Version e<strong>in</strong>es DBMS<br />

Übung 4 – Teilbereiche von <strong>SQL</strong><br />

Zu welchem der <strong>SQL</strong>-Teilbereiche DML (Data Manipulation Language), DDL<br />

(Data Def<strong>in</strong>ition Language), TCL (Transaction Control Language), DCL (Data<br />

Control Language) gehören die folgenden Befehle?<br />

1. wähle Daten aus<br />

53


<strong>SQL</strong>-Befehle<br />

2. erzeuge e<strong>in</strong>e Tabelle <strong>in</strong> der Datenbank<br />

3. erzeuge e<strong>in</strong>e Prozedur<br />

4. ändere die Informationen, die zu e<strong>in</strong>em Mitarbeiter gespeichert s<strong>in</strong>d<br />

5. ändere die Def<strong>in</strong>ition e<strong>in</strong>er Spalte<br />

6. bestätige e<strong>in</strong>e Gruppe von zusammengehörenden Anweisungen<br />

7. gewähre e<strong>in</strong>em Benutzer Zugriffsrechte auf e<strong>in</strong>e Tabelle<br />

8. SELECT ID FROM Mitarbeiter<br />

9. ROLLBACK<br />

10. UPDATE Versicherungsvertrag SET . . .<br />

11. lösche e<strong>in</strong>en Datensatz <strong>in</strong> e<strong>in</strong>er Tabelle<br />

12. lösche e<strong>in</strong>e Tabelle <strong>in</strong>sgesamt<br />

Übung 5 – <strong>SQL</strong>-Kommentare<br />

Welche der folgenden Zeilen enthalten korrekte Kommentare? Mit ... werden<br />

weitere Teile angedeutet, die für die Frage unwichtig s<strong>in</strong>d.<br />

1. SELECT * FROM Mitarbeiter;<br />

2. UPDATE Mitarbeiter SET Name = ’neu’; -- ändert alle Namen<br />

3. DEL/*löschen*/ETE FROM Mitarbeiter WHERE ...<br />

4. -- DELETE FROM Mitarbeiter WHERE ...<br />

5. UPDATE Mitarbeiter /* ändern */ SET Name = ...; /* usw. */<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 53.<br />

Lösung zu Übung 1 – Begriffsklärung<br />

Antwort 3 ist richtig.<br />

Lösung zu Übung 2 – Begriffsklärung<br />

Antwort 2 ist richtig.<br />

Lösung zu Übung 3 – Begriffsklärung<br />

Antwort 4 ist richtig.<br />

54


Lösung zu Übung 4 – Teilbereiche von <strong>SQL</strong><br />

1. DML − 2. DDL − 3. DDL − 4. DML<br />

5. DDL − 6. TCL − 7. DCL − 8. DML<br />

9. TCL − 10. DML − 11. DML − 12. DDL<br />

Lösung zu Übung 5 – <strong>SQL</strong>-Kommentare<br />

1. Diese Zeile enthält ke<strong>in</strong>en Kommentar.<br />

2. Der letzte Teil nach dem Semikolon ist e<strong>in</strong> Kommentar.<br />

Siehe auch<br />

3. Dieser Versuch e<strong>in</strong>es Kommentars zerstört das Befehlswort DELETE und<br />

ist deshalb unzulässig.<br />

4. Die gesamte Zeile zählt als Kommentar.<br />

5. Diese Zeile enthält zwei Kommentare: zum e<strong>in</strong>en zwischen dem Tabellennamen<br />

und dem Schlüsselwort SET sowie den Rest der Zeile h<strong>in</strong>ter dem<br />

Semikolon.<br />

7.8. Siehe auch<br />

Bei Wikipedia gibt es Erläuterungen zu Fachbegriffen:<br />

• Grafische Benutzeroberfläche 11 (GUI)<br />

• HTML 12<br />

• Parameter 13 <strong>in</strong> der Informatik<br />

• <strong>SQL</strong>-Injection 14<br />

• .NET 15 und ADO.NET 16<br />

• Daten<strong>in</strong>tegrität 17<br />

Zur Arbeit mit <strong>SQL</strong>-Parametern unter C# und ADO.NET gibt es e<strong>in</strong>e ausführliche<br />

Darstellung:<br />

• [Artikelserie] Parameter von <strong>SQL</strong>-Befehlen 18<br />

11 http://de.wikipedia.<strong>org</strong>/wiki/Grafische%20Benutzeroberfl%c3%a4che<br />

12 http://de.wikipedia.<strong>org</strong>/wiki/Hypertext%20Markup%20Language<br />

13 http://de.wikipedia.<strong>org</strong>/wiki/Parameter%20(Informatik)<br />

14 http://de.wikipedia.<strong>org</strong>/wiki/<strong>SQL</strong>-Injection<br />

15 http://de.wikipedia.<strong>org</strong>/wiki/.NET<br />

16 http://de.wikipedia.<strong>org</strong>/wiki/ADO.NET<br />

17 http://de.wikipedia.<strong>org</strong>/wiki/Daten<strong>in</strong>tegrit%c3%a4t<br />

18 http://www.mycsharp.de/wbb2/thread.php?threadid=66704<br />

55


8. DML (1) – Daten abfragen<br />

8.1. SELECT – Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . 57<br />

8.2. Die e<strong>in</strong>fachsten Abfragen . . . . . . . . . . . . . . . . . . . . . . . 58<br />

8.3. DISTINCT – Ke<strong>in</strong>e doppelten Zeilen . . . . . . . . . . . . . . . . 59<br />

8.4. WHERE – E<strong>in</strong>grenzen der Ergebnismenge . . . . . . . . . . . . 60<br />

8.5. ORDER BY – Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . 61<br />

8.6. FROM – Mehrere Tabellen verknüpfen . . . . . . . . . . . . . . 62<br />

8.7. Ausblick auf komplexe Abfragen . . . . . . . . . . . . . . . . . . 64<br />

8.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

8.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65<br />

E<strong>in</strong>e Datenbank enthält e<strong>in</strong>e Vielzahl verschiedener Daten. Abfragen dienen dazu,<br />

bestimmte Daten aus der Datenbank auszugeben. Dabei kann die Ergebnismenge<br />

gemäß den Anforderungen e<strong>in</strong>gegrenzt und genauer gesteuert werden.<br />

Dieser Teilbereich der Data Manipulation Language (DML) behandelt den <strong>SQL</strong>-<br />

Befehl SELECT, mit dem Abfragen durchgeführt werden.<br />

Zunächst geht es um e<strong>in</strong>fache Abfragen. Vertieft wird der SELECT-Befehl unter<br />

„Mehr zu Abfragen“ behandelt, beg<strong>in</strong>nend mit Ausführliche SELECT-Struktur 1 ;<br />

unten im Abschnitt „Ausblick auf komplexe Abfragen“ gibt es H<strong>in</strong>weise auf diese<br />

weiteren Möglichkeiten.<br />

8.1. SELECT – Allgeme<strong>in</strong>e H<strong>in</strong>weise<br />

SELECT ist <strong>in</strong> der Regel der erste und wichtigste Befehl, den der <strong>SQL</strong>-Neul<strong>in</strong>g<br />

kennenlernt, und das aus gutem Grund: Man kann damit ke<strong>in</strong>en Schaden anrichten.<br />

E<strong>in</strong> Fehler im Befehl führt höchstens zu e<strong>in</strong>er Fehlermeldung oder dem<br />

Ausbleiben des Abfrageergebnisses, aber nicht zu Schäden am Datenbestand.<br />

Trotzdem erlaubt der Befehl das Herantasten an die wichtigsten Konzepte von<br />

DML, und die anderen Befehle müssen nicht mehr so <strong>in</strong>tensiv erläutert werden.<br />

Dieser Befehl enthält die folgenden Bestandteile („Klauseln“ genannt).<br />

1 Kapitel 15 auf Seite 129<br />

57


DML (1) – Daten abfragen<br />

SELECT [DISTINCT | ALL]<br />

| *<br />

FROM <br />

[WHERE ]<br />

[GROUP BY ]<br />

[HAVING ]<br />

[UNION ]<br />

[ORDER BY ]<br />

;<br />

Die Reihenfolge der Klauseln ist fest im <strong>SQL</strong>-Standard v<strong>org</strong>egeben. Klauseln, die<br />

<strong>in</strong> [ . . . ] stehen, s<strong>in</strong>d nicht nötig, sondern können entfallen; der Name des Befehls<br />

und die FROM-Angaben s<strong>in</strong>d unbed<strong>in</strong>gt erforderlich, das Semikolon als<br />

Standard empfohlen.<br />

Die wichtigsten Teile werden <strong>in</strong> den folgenden Abschnitten erläutert.<br />

Die folgenden Punkte verlangen dagegen vertiefte Beschäftigung mit <strong>SQL</strong>:<br />

• GROUP BY − Daten gruppieren<br />

• HAVING − weitere E<strong>in</strong>schränkungen<br />

• UNION − mehrere Abfragen verb<strong>in</strong>den<br />

Dies sowie weitere E<strong>in</strong>zelheiten zu den wichtigsten Bestandteilen stehen <strong>in</strong> Ausführliche<br />

SELECT-Struktur 2 und anderen „fortgeschrittenen“ Kapiteln.<br />

Die Beispiele beziehen sich auf den Anfangsbestand der Beispieldatenbank; auf<br />

die Ausgabe der selektierten Datensätze wird <strong>in</strong> der Regel verzichtet. Bitte probieren<br />

Sie alle Beispiele aus und nehmen Sie verschiedene Änderungen vor, um<br />

die Auswirkungen zu erkennen.<br />

8.2. Die e<strong>in</strong>fachsten Abfragen<br />

Aufgabe: Gesucht wird der Inhalt der Tabelle der Fahrzeughersteller mit all<br />

ihren Spalten und Datensätzen (Zeilen).<br />

SELECT * FROM Fahrzeughersteller;<br />

Schauen wir uns das Beispiel etwas genauer an:<br />

• Die beiden Begriffe SELECT und FROM s<strong>in</strong>d <strong>SQL</strong>-spezifische Bezeichner.<br />

• Fahrzeughersteller ist der Name der Tabelle, aus der die Daten selektiert und<br />

ausgegeben werden sollen.<br />

2 Kapitel 15 auf Seite 129<br />

58


DISTINCT – Ke<strong>in</strong>e doppelten Zeilen<br />

• Das Sternchen, Asterisk genannt, ist e<strong>in</strong>e Kurzfassung für „alle Spalten“.<br />

Nun wollen wir nur bestimmte Spalten ausgeben, nämlich e<strong>in</strong>e Liste aller Fahrzeughersteller;<br />

das Land <strong>in</strong>teressiert uns dabei nicht. Dazu müssen zwischen<br />

den <strong>SQL</strong>-Bezeichnern SELECT und FROM die auszugebenden Spalten angegeben<br />

werden. S<strong>in</strong>d es mehrere, dann werden diese durch jeweils e<strong>in</strong> Komma getrennt.<br />

Aufgabe: So erhält man die Namensliste aller Fahrzeughersteller:<br />

SELECT Name FROM Fahrzeughersteller;<br />

Aufgabe: Folgendes Beispiel gibt die beiden Spalten für Name und Land des<br />

Herstellers aus. Die Spalten werden durch Komma getrennt.<br />

SELECT Name, Land FROM Fahrzeughersteller;<br />

Für die Ausgabe kann e<strong>in</strong>e (abweichende) Spaltenüberschrift festgelegt werden.<br />

Diese wird als Spalten-Alias bezeichnet. Der Alias kann dem Spaltennamen direkt<br />

folgen oder mit dem B<strong>in</strong>dewort AS angegeben werden. Das vorherige Beispiel<br />

kann also wie folgt mit dem Alias Hersteller für Name und dem Alias Staat<br />

für Land versehen werden:<br />

SELECT Name Hersteller, Land AS Staat<br />

FROM Fahrzeughersteller;<br />

8.3. DISTINCT – Ke<strong>in</strong>e doppelten Zeilen<br />

Wenn Sie bei e<strong>in</strong>em SELECT-Befehl den DISTINCT-Parameter angeben, erhalten<br />

Sie nur e<strong>in</strong>deutige Ergebnisse:<br />

Aufgabe: Gesucht werden die Fahrzeuge, für die Schadensfälle aufgetreten<br />

s<strong>in</strong>d.<br />

SELECT Fahrzeug_ID<br />

FROM Zuordnung_SF_FZ;<br />

Tatsächlich wird für jeden Schadensfall e<strong>in</strong>e Zeile ausgegeben, die Fahrzeuge 2, 5<br />

und 7 ersche<strong>in</strong>en mehrmals. Damit ke<strong>in</strong>e doppelten Zeilen ausgegeben werden,<br />

wird DISTINCT vor den Spaltennamen <strong>in</strong> den <strong>SQL</strong>-Befehl e<strong>in</strong>gefügt:<br />

SELECT DISTINCT Fahrzeug_ID<br />

FROM Zuordnung_SF_FZ;<br />

59


DML (1) – Daten abfragen<br />

Fahrzeug_ID<br />

3<br />

4<br />

5<br />

6<br />

7<br />

Damit ersche<strong>in</strong>t jede Fahrzeug-ID nur e<strong>in</strong>mal <strong>in</strong> der Liste.<br />

Bitte beachten Sie: Als „e<strong>in</strong>deutig“ gilt immer die gesamte Zeile, also alle aufgeführten<br />

Spalten geme<strong>in</strong>sam. Die folgende Abfrage liefert alle Datensätze; Fahrzeuge<br />

mit mehreren Schadensfällen stehen auch mehrfach <strong>in</strong> der Liste.<br />

Quelltext Falsch<br />

SELECT DISTINCT Fahrzeug_ID, ID<br />

FROM Zuordnung_SF_FZ;<br />

Nur theoretisch DISTINCT, praktisch nicht<br />

Die Alternative zu DISTINCT ist das <strong>in</strong> der Syntax genannte ALL, das ausdrücklich<br />

alle Datensätze abfragt. Da dies der Standardwert ist, wird er äußerst selten<br />

benutzt, sondern kann weggelassen werden.<br />

SELECT ALL Fahrzeug_ID<br />

FROM Zuordnung_SF_FZ<br />

8.4. WHERE – E<strong>in</strong>grenzen der Ergebnismenge<br />

Fast immer soll nicht der komplette Inhalt e<strong>in</strong>er Tabelle ausgegeben werden. Dazu<br />

wird die Ergebnismenge mittels Bed<strong>in</strong>gungen <strong>in</strong> der WHERE-Klausel e<strong>in</strong>gegrenzt,<br />

welche nach dem Tabellennamen im SELECT-Befehl steht.<br />

E<strong>in</strong>e Bed<strong>in</strong>gung ist e<strong>in</strong> logischer Ausdruck, dessen Ergebnis WAHR oder FALSCH<br />

ist. In diesen logischen Ausdrücken werden die Inhalte der Spalten (vorwiegend)<br />

mit konstanten Werten verglichen. Hierbei stehen verschiedene Operatoren zur<br />

Verfügung, vor allem:<br />

= gleich ungleich; seltener auch: !=<br />

< kle<strong>in</strong>er als größer als >= größer als oder gleich<br />

Bed<strong>in</strong>gungen können durch die logischen Operatoren OR und AND und die<br />

Klammern () verknüpft werden. Je komplizierter solche Verknüpfungen werden,<br />

desto sicherer ist es, die Bed<strong>in</strong>gungen durch Klammern zu gliedern. Mit diesen<br />

Mitteln lässt sich die Abfrage entsprechend e<strong>in</strong>grenzen.<br />

60


ORDER BY – Sortieren<br />

Aufgabe: Beispielsweise sollen alle Hersteller angezeigt werden, die ihren Sitz<br />

<strong>in</strong> Schweden oder Frankreich haben:<br />

SELECT * FROM Fahrzeughersteller<br />

WHERE ( Land = ’Schweden’ ) OR ( Land = ’Frankreich’ );<br />

Auf die Klammern kann hier verzichtet werden.<br />

H<strong>in</strong>ter der WHERE-Klausel kann man also e<strong>in</strong>e oder mehrere Bed<strong>in</strong>gungen (mit<br />

e<strong>in</strong>em booleschen Operator verknüpft) e<strong>in</strong>fügen. Jede e<strong>in</strong>zelne besteht aus dem<br />

Namen der Spalte, deren Inhalt überprüft werden soll, und e<strong>in</strong>em Wert, wobei<br />

beide mit e<strong>in</strong>em Vergleichsoperator verknüpft s<strong>in</strong>d.<br />

Für die Umkehrung e<strong>in</strong>er solchen Bed<strong>in</strong>gung gibt es mehrere Möglichkeiten:<br />

• Man kann alle anderen Fälle e<strong>in</strong>zeln auflisten.<br />

• Man dreht e<strong>in</strong>fach den Vergleichsoperator um.<br />

• Man kehrt die Bed<strong>in</strong>gung mit NOT um.<br />

Aufgabe: Es sollen alle Fahrzeughersteller angezeigt werden, die außerhalb<br />

Deutschlands sitzen.<br />

- Vergleichsoperator ändern<br />

SELECT * FROM Fahrzeughersteller<br />

WHERE Land ’Deutschland’;<br />

- Bed<strong>in</strong>gung mit NOT umkehren<br />

SELECT * FROM Fahrzeughersteller<br />

WHERE NOT (Land = ’Deutschland’);<br />

Vertiefte Erläuterungen s<strong>in</strong>d unter WHERE-Klausel im Detail 3 zu f<strong>in</strong>den.<br />

8.5. ORDER BY – Sortieren<br />

Nachdem wir nun die Zeilen und Spalten der Ergebnismenge e<strong>in</strong>grenzen können,<br />

wollen wir die Ausgabe der Zeilen sortieren. Hierfür wird die ORDER BY-<br />

Klausel genutzt. Diese ist die letzte im <strong>SQL</strong>-Befehl vor dem abschließenden Semikolon<br />

und enthält die Spalten, nach denen sortiert werden soll.<br />

Aufgabe: Die Liste der Hersteller wird nach dem Namen sortiert:<br />

SELECT * FROM Fahrzeughersteller<br />

ORDER BY Name;<br />

3 Kapitel 17 auf Seite 155<br />

61


DML (1) – Daten abfragen<br />

Anstatt des Spaltennamens kann auch die Nummer der Spalte genutzt werden.<br />

Mit dem folgenden Statement erreichen wir also das gleiche Ergebnis, da Name<br />

die zweite Spalte <strong>in</strong> unserer Ausgabe ist:<br />

SELECT * FROM Fahrzeughersteller<br />

ORDER BY 2;<br />

Die Angabe nach Spaltennummer ist unüblich; sie wird eigentlich höchstens<br />

dann verwendet, wenn die Spalten genau aufgeführt werden und komplizierte<br />

Angaben – z. B. Berechnete Spalten 4 – enthalten.<br />

Die Sortierung erfolgt standardmäßig aufsteigend; das kann auch durch ASC<br />

ausdrücklich angegeben werden. Die Sortierreihenfolge kann mit dem DESC-<br />

Bezeichner <strong>in</strong> absteigend verändert werden.<br />

SELECT * FROM Fahrzeughersteller<br />

ORDER BY Name DESC;<br />

In <strong>SQL</strong> kann nicht nur nach e<strong>in</strong>er Spalte sortiert werden, sondern nach mehreren<br />

Spalten mit e<strong>in</strong>er eigenen Regel für jede Spalte. Dabei werden die Zeilen zuerst<br />

nach der ersten Spalte sortiert und <strong>in</strong>nerhalb dieser Spalte nach der zweiten<br />

Spalte usw. Bei der Sortierung nach Land und Name wird also zuerst nach dem<br />

Land und dann je Land nach Name sortiert. E<strong>in</strong>e Neusortierung nach Name, die<br />

jene Sortierung nach Land wieder verwirft, f<strong>in</strong>det also nicht statt.<br />

Aufgabe: Der folgende Befehl liefert die Hersteller − zuerst absteigend nach<br />

Land und dann aufsteigend sortiert nach dem Namen − zurück.<br />

SELECT * FROM Fahrzeughersteller<br />

ORDER BY Land DESC, Name ASC;<br />

8.6. FROM – Mehrere Tabellen verknüpfen<br />

In fast allen Abfragen werden Informationen aus mehreren Tabellen zusammengefasst.<br />

Die s<strong>in</strong>nvolle Speicherung von Daten <strong>in</strong> getrennten Tabellen ist e<strong>in</strong>es der<br />

Merkmale e<strong>in</strong>es relationalen DBMS; deshalb müssen die Daten bei e<strong>in</strong>er Abfrage<br />

nach praktischen Gesichtspunkten zusammengeführt werden.<br />

4 Kapitel 24 auf Seite 235<br />

62


8.6.1. Traditionell mit FROM und WHERE<br />

FROM – Mehrere Tabellen verknüpfen<br />

Beim „traditonellen“ Weg werden e<strong>in</strong>fach alle Tabellen <strong>in</strong> der FROM-Klausel aufgeführt<br />

und durch jeweils e<strong>in</strong>e Bed<strong>in</strong>gung <strong>in</strong> der WHERE-Klausel verknüpft.<br />

Aufgabe: Ermittle die Angaben der Mitarbeiter, deren Abteilung ihren Sitz <strong>in</strong><br />

Dortmund oder Bochum hat.<br />

SELECT mi.Name, mi.Vorname, mi.Raum, ab.Ort<br />

FROM Mitarbeiter mi, Abteilung ab<br />

WHERE mi.Abteilung_ID = ab.ID<br />

AND ab.Ort <strong>in</strong> (’Dortmund’, ’Bochum’)<br />

ORDER BY mi.Name, mi.Vorname;<br />

Es werden also Daten aus den Tabellen Mitarbeiter (Name und Raum) sowie Abteilung<br />

(Ort) gesucht. Für die Verknüpfung werden diese Bestandteile benötigt:<br />

• In der FROM-Klausel stehen die benötigten Tabellen.<br />

• Zur Vere<strong>in</strong>fachung wird jeder Tabelle e<strong>in</strong> Kürzel als Tabellen-Alias zugewiesen.<br />

• In der Spaltenliste wird jede e<strong>in</strong>zelne Spalte mit dem Namen der betreffenden<br />

Tabelle bzw. dem Alias verbunden. (Der Tabellenname bzw. Alias kann oft weggelassen<br />

werden; aber schon zur Klarheit sollte er immer benutzt werden.)<br />

• Die WHERE-Klausel enthält die Verknüpfungsbed<strong>in</strong>gung „mi.Abteilung_ID =<br />

ab.ID“ − zusätzlich zur E<strong>in</strong>schränkung nach dem Sitz der Abteilung.<br />

Jede Tabelle <strong>in</strong> e<strong>in</strong>er solchen Abfrage benötigt m<strong>in</strong>destens e<strong>in</strong>e direkte Verknüpfung<br />

zu e<strong>in</strong>er anderen Tabelle. Alle Tabellen müssen zum<strong>in</strong>dest <strong>in</strong>direkt mite<strong>in</strong>ander<br />

verknüpft se<strong>in</strong>. Falsche Verknüpfungen s<strong>in</strong>d e<strong>in</strong>e häufige Fehlerquelle.<br />

Vertiefte Erläuterungen s<strong>in</strong>d unter E<strong>in</strong>fache Tabellenverknüpfung 5 zu f<strong>in</strong>den.<br />

8.6.2. Modern mit JOIN . . . ON<br />

Beim „modernen“ Weg steht e<strong>in</strong>e Tabelle <strong>in</strong> der FROM-Klausel, nämlich diejenige,<br />

die als wichtigste oder „Haupttabelle“ der Abfrage anzusehen ist. E<strong>in</strong>e weitere<br />

Tabelle wird durch JOIN und e<strong>in</strong>e Bed<strong>in</strong>gung <strong>in</strong> der ON-Klausel verknüpft.<br />

Das obige Beispiel sieht dann so aus:<br />

SELECT mi.Name, mi.Vorname, mi.Raum, ab.Ort<br />

FROM Mitarbeiter mi<br />

JOIN Abteilung ab<br />

ON mi.Abteilung_ID = ab.ID<br />

WHERE ab.Ort <strong>in</strong> (’Dortmund’, ’Bochum’)<br />

ORDER BY mi.Name, mi.Vorname;<br />

5 Kapitel 19 auf Seite 173<br />

63


DML (1) – Daten abfragen<br />

Für die Verknüpfung der Tabellen werden folgende Bestandteile benötigt:<br />

• In der FROM-Klausel steht e<strong>in</strong>e der benötigten Tabellen.<br />

• In der JOIN-Klausel steht jeweils e<strong>in</strong>e weitere Tabelle.<br />

• Die ON-Klausel enthält die Verknüpfungsbed<strong>in</strong>gung „mi.Abteilung_ID =<br />

ab.ID“.<br />

• Die WHERE-Klausel beschränkt sich auf die wirklich gewünschten E<strong>in</strong>schränkungen<br />

für die Ergebnismenge.<br />

E<strong>in</strong> Tabellen-Alias ist wiederum für alle Tabellen s<strong>in</strong>nvoll. In der Spaltenliste und<br />

auch zur Sortierung können alle Spalten aller Tabellen benutzt werden.<br />

Vertiefte Erläuterungen s<strong>in</strong>d unter Arbeiten mit JOIN 6 zu f<strong>in</strong>den.<br />

8.7. Ausblick auf komplexe Abfragen<br />

Das folgende Beispiel ist erheblich umfangreicher und geht über „Anfängerbedürfnisse“<br />

weit h<strong>in</strong>aus. Es zeigt aber sehr schön, was alles mit <strong>SQL</strong> möglich ist:<br />

Aufgabe: Gesucht werden die Daten der Versicherungsnehmer im Jahr 2008,<br />

und zwar die Adresse, die Höhe des Gesamtschadens und die Anzahl der Schadensfälle.<br />

SELECT vn.Name,<br />

vn.Vorname,<br />

vn.Strasse,<br />

vn.Hausnummer AS HNR,<br />

vn.PLZ,<br />

vn.Ort,<br />

SUM(sf.Schadenshoehe) AS Schaden,<br />

COUNT(sf.ID) AS Anzahl<br />

FROM Versicherungsnehmer vn<br />

JOIN Versicherungsvertrag vv ON vv.Versicherungsnehmer_ID = vn.ID<br />

JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Zuordnung_SF_FZ zu ON zu.Fahrzeug_ID = fz.ID<br />

JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

WHERE EXTRACT(YEAR FROM sf.Datum) = 2008<br />

GROUP BY vn.Name, vn.Vorname, vn.Strasse, vn.Hausnummer, vn.PLZ, vn.Ort<br />

ORDER BY Gesamtschaden, Anzahl;<br />

NAME VORNAME STRASSE HNR PLZ ORT SCHADEN ANZAHL<br />

Heckel Obsthandel GmbH Gahlener Str. 40 46282 Dorsten 1.438,75 1<br />

Antonius Bernhard Coesfelder Str. 23 45892 Gelsenkirchen 1.983,00 1<br />

6 Kapitel 20 auf Seite 181<br />

64


Zusammenfassung<br />

Hierbei kommen die Funktionen SUM (Summe) und COUNT (Anzahl) zum E<strong>in</strong>satz.<br />

Diese können nur e<strong>in</strong>gesetzt werden, wenn die Datenmenge richtig gruppiert<br />

wurde. Deshalb wird mit GROUP BY das Datenmaterial nach allen verbliebenen,<br />

zur Ausgabe v<strong>org</strong>esehenen Datenfeldern gruppiert.<br />

Vertiefte Erläuterungen s<strong>in</strong>d zu f<strong>in</strong>den <strong>in</strong> den Kapiteln Funktionen 7 sowie Gruppierungen<br />

8 .<br />

8.8. Zusammenfassung<br />

In diesem Kapitel lernten wir die Grundlagen e<strong>in</strong>es SELECT-Befehls kennen:<br />

• SELECT-Befehle werden zur Abfrage von Daten aus Datenbanken genutzt.<br />

• Die auszugebenden Spalten können festgelegt werden, <strong>in</strong>dem die Liste der<br />

Spalten zwischen den Bezeichnern SELECT und FROM angegeben wird.<br />

• Mit DISTINCT werden identische Zeilen <strong>in</strong> der Ergebnismenge nur e<strong>in</strong>mal<br />

ausgegeben.<br />

• Die Ergebnismenge wird mittels der WHERE-Klausel e<strong>in</strong>gegrenzt.<br />

• Die WHERE-Klausel enthält logische Ausdrücke. Diese können mit AND und<br />

OR verknüpft werden.<br />

• Mittels der ORDER BY-Klausel kann die Ergebnismenge sortiert werden.<br />

Die Reihenfolge <strong>in</strong>nerhalb e<strong>in</strong>es SELECT-Befehls ist zu beachten. SELECT und<br />

FROM s<strong>in</strong>d hierbei Pflicht, das abschließende Semikolon als Standard empfohlen.<br />

Alle anderen Klauseln s<strong>in</strong>d optional.<br />

8.9. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 67.<br />

Bei den Übungen 3–6 ist jeweils e<strong>in</strong>e Abfrage zur Tabelle Abteilung zu erstellen.<br />

Übung 1 – Richtig oder falsch?<br />

Welche der folgenden Aussagen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

1. Fehlerhafte Verwendung von SELECT kann ke<strong>in</strong>en Schaden am Datenbestand<br />

hervorrufen.<br />

7 Kapitel 14 auf Seite 109<br />

8 Kapitel 25 auf Seite 241<br />

65


DML (1) – Daten abfragen<br />

2. Die gewünschten Spalten werden <strong>in</strong> e<strong>in</strong>er Liste jeweils durch Leerzeichen<br />

getrennt.<br />

3. Wenn alle Spalten gewünscht werden, s<strong>in</strong>d auch alle Spalten <strong>in</strong> dieser Liste<br />

aufzuführen.<br />

4. Es ist Standard, den Parameter ALL bei e<strong>in</strong>em SELECT-Befehl anzugeben.<br />

5. Bei SELECT DISTINCT werden nur solche Zeilen angezeigt, die sich <strong>in</strong> m<strong>in</strong>destens<br />

e<strong>in</strong>er Spalte unterscheiden.<br />

6. Die WHERE-Klausel dient dazu, das Ergebnis der Abfrage wunschgemäß<br />

zu formatieren.<br />

7. E<strong>in</strong>e WHERE-Bed<strong>in</strong>gung ist stets e<strong>in</strong>e Prüfung auf WAHR/FALSCH: Entweder<br />

e<strong>in</strong> Datensatz erfüllt die Bed<strong>in</strong>gung und gehört zum Abfrageergebnis,<br />

oder er gehört nicht dazu.<br />

8. WHERE-Bed<strong>in</strong>gungen können mit AND/OR komb<strong>in</strong>iert und mit NOT umgekehrt<br />

werden.<br />

9. Solche Komb<strong>in</strong>ationen von Bed<strong>in</strong>gungen müssen durch Klammern gegliedert<br />

werden.<br />

10. Um Daten aus mehreren Tabellen zu verknüpfen, ist es möglich, diese Tabellen<br />

e<strong>in</strong>fach <strong>in</strong> der FROM-Klausel aufzuführen.<br />

Übung 2 – Pflichtangaben<br />

Welche Bestandteile e<strong>in</strong>es SELECT-Befehls s<strong>in</strong>d unbed<strong>in</strong>gt erforderlich und können<br />

nicht weggelassen werden?<br />

Übung 3 – Alle Angaben<br />

Geben Sie alle Informationen zu allen Abteilungen aus.<br />

Übung 4 – Angaben mit E<strong>in</strong>schränkung<br />

Geben Sie alle Abteilungen aus, deren Standort Bochum ist.<br />

Übung 5 – Angaben mit E<strong>in</strong>schränkungen<br />

Geben Sie alle Abteilungen aus, deren Standort Bochum oder Essen ist. Hierbei<br />

soll nur der Name der Abteilung ausgegeben werden.<br />

Übung 6 – Abfrage mit Sortierung<br />

Geben Sie nur die Kurzbezeichnungen aller Abteilungen aus. Hierbei sollen die<br />

Abteilungen nach den Standorten sortiert werden.<br />

66


Übung 7 – DISTINCT und ALL<br />

Bitte überprüfen Sie die folgenden Befehle:<br />

-- Variante 1<br />

SELECT DISTINCT COUNT(*)<br />

FROM Mitarbeiter<br />

GROUP BY Abteilung_ID<br />

-- Variante 2<br />

SELECT ALL COUNT(*)<br />

FROM Mitarbeiter<br />

GROUP BY Abteilung_ID<br />

Übungen<br />

Wor<strong>in</strong> unterscheidet sich die Ausgabe? Welche wichtige Information fehlt vor allem<br />

bei Variante 2?<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 65.<br />

Lösung zu Übung 1 – Richtig oder falsch?<br />

Die Aussagen 1, 5, 7, 8, 10 s<strong>in</strong>d richtig. Die Aussagen 2, 3, 4, 6, 9 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – Pflichtangaben<br />

SELECT, Spaltenliste oder '*', FROM, Tabellenname.<br />

Lösung zu Übung 3 – Alle Angaben<br />

SELECT * FROM Abteilung;<br />

Lösung zu Übung 4 – Angaben mit E<strong>in</strong>schränkung<br />

SELECT * FROM Abteilung<br />

WHERE Ort = ’Bochum’;<br />

Lösung zu Übung 5 – Angaben mit E<strong>in</strong>schränkungen<br />

SELECT Bezeichnung FROM Abteilung<br />

WHERE Ort = ’Bochum’ OR Ort = ’Essen’;<br />

67


DML (1) – Daten abfragen<br />

Alternativ ist es auch so möglich:<br />

SELECT Bezeichnung FROM Abteilung<br />

WHERE Ort IN (’Bochum’, ’Essen’);<br />

Lösung zu Übung 6 – Abfrage mit Sortierung<br />

SELECT Kuerzel FROM Abteilung<br />

ORDER BY Ort;<br />

Lösung zu Übung 7 – DISTINCT und ALL<br />

Variante 1 nennt jede Anzahl von Mitarbeitern pro Abteilung genau e<strong>in</strong>mal. Bei<br />

Variante 2 gibt es diese Anzeige für jede Abteilung e<strong>in</strong>zeln. Dabei fehlt vor allem<br />

die Angabe der Abteilung_ID, ohne die die Ausgabe ziemlich s<strong>in</strong>nlos ist.<br />

68


9. DML (2) – Daten speichern<br />

9.1. INSERT – Daten e<strong>in</strong>fügen . . . . . . . . . . . . . . . . . . . . . . . 69<br />

9.2. UPDATE – Daten ändern . . . . . . . . . . . . . . . . . . . . . . . 72<br />

9.3. DELETE – Daten löschen . . . . . . . . . . . . . . . . . . . . . . . 74<br />

9.4. TRUNCATE – Tabelle leeren . . . . . . . . . . . . . . . . . . . . . 75<br />

9.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />

9.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75<br />

Dieser Teilbereich der Data Manipulation Language (DML) behandelt die Befehle,<br />

mit denen die Inhalte der Datenbank geändert werden: Neuaufnahme, Änderung,,<br />

Löschung.<br />

Bitte beachten Sie, dass mit den Befehlen INSERT, UPDATE, DELETE (fast) immer<br />

nur Daten genau e<strong>in</strong>er Tabelle bearbeitet werden können − anders als beim<br />

SELECT-Befehl, der Daten mehrerer Tabellen zusammenfassen kann.<br />

9.1. INSERT – Daten e<strong>in</strong>fügen<br />

Der INSERT-Befehl dient dem Erstellen von neuen Datensätzen. Es gibt ihn <strong>in</strong><br />

zwei Versionen − zum e<strong>in</strong>en durch die Angabe e<strong>in</strong>zelner Werte, zum anderen<br />

mit Hilfe e<strong>in</strong>es SELECT-Befehls.<br />

In beiden Versionen müssen die Datentypen 1 der Werte zu den Datentypen der<br />

Spalten passen. Man sollte nicht versuchen, e<strong>in</strong>er Spalte, die e<strong>in</strong>e Zahl erwartet,<br />

e<strong>in</strong>e Zeichenkette zuzuweisen. Man wird nur selten das Ergebnis erhalten,<br />

welches man erwartet. Das Kapitel Funktionen 2 erläutert im Abschnitt „Konvertierungen“<br />

Möglichkeiten, wie Werte implizit (also automatisch) oder explizit<br />

durch CAST oder CONVERT angepasst werden können.<br />

1 Kapitel 13 auf Seite 97<br />

2 Abschnitt 14.6 auf Seite 117<br />

69


DML (2) – Daten speichern<br />

9.1.1. E<strong>in</strong>zeln mit VALUES<br />

Wenn e<strong>in</strong> e<strong>in</strong>zelner Datensatz durch die Liste se<strong>in</strong>er Werte gespeichert werden<br />

soll, gilt folgende Syntax:<br />

INSERT INTO <br />

[ ( ) ]<br />

VALUES ( )<br />

;<br />

Zu diesem Befehl gehören folgende Angaben:<br />

• INSERT als Name des Befehls, INTO als feststehender Begriff<br />

• als Name der Tabelle, die diesen Datensatz erhalten soll<br />

• <strong>in</strong> Klammern ( ) gesetzt e<strong>in</strong>e Liste von Spalten (Feldnamen), denen Werte zugewiesen<br />

werden<br />

• der Begriff VALUES als H<strong>in</strong>weis darauf, dass e<strong>in</strong>zelne Werte angegeben werden<br />

• <strong>in</strong> Klammern ( ) gesetzt e<strong>in</strong>e Liste von Werten, die <strong>in</strong> den entsprechenden Spalten<br />

gespeichert werden sollen<br />

Wenn e<strong>in</strong>e Liste von Spalten fehlt, bedeutet das, dass alle Spalten dieser Tabelle<br />

<strong>in</strong> der Reihenfolge der Struktur mit Werten versehen werden müssen.<br />

Aufgabe: So wird (wie im Skript der Beispieldatenbank) e<strong>in</strong> E<strong>in</strong>trag <strong>in</strong> der Tabelle<br />

Mitarbeiter gespeichert:<br />

INSERT INTO Mitarbeiter<br />

( Personalnummer, Name, Vorname,<br />

Telefon, Email, Raum, Ist_Leiter, Abteilung_ID, Geburtsdatum )<br />

VALUES ( ’20002’, ’Schmitz’, ’Michael’,<br />

’0231/5556187’, ’michael.schmitz@unserefirma.de’,<br />

’212’, ’N’, 2, ’1959-08-25’ );<br />

Wenn Sie diesen Befehl mit der Tabellenstruktur vergleichen, werden Sie feststellen:<br />

• Die Spalte ID fehlt. Dieser Wert wird von der Datenbank automatisch vergeben.<br />

• Die Spalte Mobil fehlt. In dieser Spalte wird folglich e<strong>in</strong> NULL-Wert gespeichert.<br />

• Die Reihenfolge der Spalten weicht von der Tabellendef<strong>in</strong>ition ab; das ist also<br />

durchaus möglich.<br />

In der Beschreibung der Beispieldatenbank werden sehr viele Spalten als<br />

„Pflicht“ festgelegt. Folgender Befehl wird deshalb zurückgewiesen:<br />

70


INSERT – Daten e<strong>in</strong>fügen<br />

<strong>SQL</strong> Quelltext Falsch<br />

INSERT INTO Mitarbeiter<br />

( Personalnummer, Name, Vorname, Ist_Leiter, Abteilung_ID )<br />

VALUES ( ’17999’, ’Liebich’, ’Andrea’, ’N’, 17);<br />

validation error for column GEBURTSDATUM, value "*** null ***".<br />

Die Spalte Geburtsdatum darf laut Def<strong>in</strong>ition nicht NULL se<strong>in</strong>. E<strong>in</strong>e Angabe fehlt<br />

<strong>in</strong> diesem Befehl: das wird als NULL <strong>in</strong>terpretiert, also mit e<strong>in</strong>er Fehlermeldung<br />

quittiert.<br />

9.1.2. Mengen mit SELECT<br />

Wenn e<strong>in</strong>e Menge von Datensätzen mit Hilfe e<strong>in</strong>es SELECT-Befehls gespeichert<br />

werden soll, gilt folgende Syntax:<br />

INSERT INTO <br />

[ ( ) ]<br />

SELECT <br />

;<br />

Zu diesem Befehl gehören die folgenden Angaben:<br />

• INSERT INTO (wie oben)<br />

• <strong>in</strong> Klammern ( ) gesetzt e<strong>in</strong>e Liste von Spalten (Feldnamen), sofern v<strong>org</strong>esehen<br />

• dazu e<strong>in</strong> vollständiger SELECT-Befehl, mit dem die passenden Inhalte geliefert<br />

werden<br />

Da e<strong>in</strong> SELECT-Befehl auch ohne Bezug auf e<strong>in</strong>e Tabelle nur mit konstanten Werten<br />

möglich ist, kann das obige Beispiel auch so formuliert werden:<br />

INSERT INTO Mitarbeiter<br />

( Personalnummer, Name, Vorname,<br />

Telefon, Email, Raum, Ist_Leiter, Abteilung_ID, Geburtsdatum )<br />

SELECT ’20002’, ’Schmitz’, ’Michael’,<br />

’0231/5556187’, ’michael.schmitz@unserefirma.de’,<br />

’212’, ’N’, 2, ’1959-08-25’<br />

/* from rdb%database (bei Firebird) */ ;<br />

/* from dual (bei Oracle) */ ;<br />

H<strong>in</strong>weis: Firebird und Oracle kennen diese Kurzform des SELECT-Befehls nicht;<br />

dort ist die als Kommentar jeweils e<strong>in</strong>gefügte FROM-Klausel erforderlich.<br />

Wichtig ist diese Art des INSERT-Befehls, wenn neue Datensätze aus vorhandenen<br />

anderen Daten abgeleitet werden wie im Skript der Beispieldatenbank:<br />

71


DML (2) – Daten speichern<br />

Aufgabe: Für jeden Abteilungsleiter aus der Tabelle Mitarbeiter wird e<strong>in</strong> E<strong>in</strong>trag<br />

<strong>in</strong> der Tabelle Dienstwagen gespeichert:<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )<br />

SELECT ’DO-WB 42’ || Abteilung_ID, ’elfenbe<strong>in</strong>’, 14, ID<br />

FROM Mitarbeiter<br />

WHERE Ist_Leiter = ’J’;<br />

Die Spalte ID wird automatisch zugewiesen. Die anderen Spalten erhalten Werte:<br />

• Farbe und Fahrzeugtyp als Konstante<br />

• dazu natürlich die ID des Mitarbeiters, dem der Dienstwagen zugeordnet wird<br />

• und e<strong>in</strong> Kfz-Kennzeichen, das aus e<strong>in</strong>em konstanten Teil mit der ID der Abteilung<br />

zusammengesetzt wird<br />

Manches DBMS erlaubt auch die Erstellung von Tabellen aus e<strong>in</strong>em SELECT-<br />

Ausdruck wie bei MS-<strong>SQL</strong> (nächstes Beispiel) oder Teradata (anschließend):<br />

SELECT [ ( ) ]<br />

INTO <br />

FROM <br />

CREATE TABLE AS<br />

SELECT [ ( ) ]<br />

FROM <br />

9.2. UPDATE – Daten ändern<br />

Der UPDATE-Befehl dient zum Ändern e<strong>in</strong>er Menge von Datensätzen:<br />

UPDATE <br />

SET <br />

[WHERE ];<br />

Jede Änderung e<strong>in</strong>es Feldes ist so e<strong>in</strong>zutragen:<br />

= ,<br />

Zu diesem Befehl gehören die folgenden Angaben:<br />

• UPDATE als Name des Befehls<br />

• als Name der Tabelle, <strong>in</strong> der die Daten zu ändern s<strong>in</strong>d<br />

72


• SET als Anfang der Liste von Änderungen<br />

UPDATE – Daten ändern<br />

• als Name der Spalte, die e<strong>in</strong>en neuen Inhalt erhalten soll, dazu<br />

das Gleichheitszeichen und die Zuweisung von als neuer Inhalt<br />

• e<strong>in</strong> Komma als H<strong>in</strong>weis, dass e<strong>in</strong> weiteres Feld zu ändern ist; vor der WHERE-<br />

Klausel oder dem abschließenden Semikolon muss das Komma entfallen<br />

• die WHERE-Klausel mit Bed<strong>in</strong>gungen, welche Datensätze zu ändern s<strong>in</strong>d: e<strong>in</strong>er<br />

oder e<strong>in</strong>e bestimmte Menge<br />

Die Struktur der WHERE-Klausel ist identisch mit derjenigen beim SELECT-<br />

Befehl. Wenn alle Datensätze geändert werden sollen, kann die WHERE-<br />

Bed<strong>in</strong>gung entfallen; aber beachten Sie unbed<strong>in</strong>gt:<br />

Ohne WHERE-Bed<strong>in</strong>gung wird wirklich alles sofort geändert.<br />

An den Beispielen ist zu sehen, dass die Änderung aller Datensätze nur selten<br />

s<strong>in</strong>nvoll ist und meistens mit WHERE-Bed<strong>in</strong>gung gearbeitet wird.<br />

Wie beim INSERT-Befehl muss der Datentyp e<strong>in</strong>es Wertes zum Datentyp der<br />

Spalte passen. Beispiele:<br />

Aufgabe: Korrigiere die Schreibweise des Namens bei e<strong>in</strong>em Mitarbeiter.<br />

UPDATE Mitarbeiter<br />

SET Name = ’Mayer’<br />

WHERE Personalnummer = 20001;<br />

Aufgabe: Ändere nach e<strong>in</strong>er E<strong>in</strong>geme<strong>in</strong>dung PLZ und Ortsname für alle betroffenen<br />

Adressen.<br />

UPDATE Versicherungsnehmer<br />

SET Ort = ’Leipzig’,<br />

PLZ = ’04178’<br />

WHERE PLZ = ’04430’;<br />

Aufgabe: Erhöhe bei allen Schadensfällen die Schadenshöhe um 10 % (das ist<br />

natürlich ke<strong>in</strong>e s<strong>in</strong>nvolle Maßnahme):<br />

UPDATE Schadensfall<br />

SET Schadenshoehe = Schadenshoehe * 1.1;<br />

Aufgabe: Berichtige das Geburtsdatum für e<strong>in</strong>en Versicherungsnehmer:<br />

UPDATE Versicherungsnehmer<br />

SET Geburtsdatum = ’14.03.1963’<br />

WHERE Name = ’Zenep’ AND Geburtsdatum = ’13.02.1963’;<br />

0 row(s) affected.<br />

73


DML (2) – Daten speichern<br />

Nanu, ke<strong>in</strong>e Zeilen wurden geändert? Bei diesem Befehl wurde zur Kontrolle,<br />

welcher Datensatz geändert werden sollte, nicht nur der Nachname, sondern<br />

auch das bisher notierte Geburtsdatum angegeben − und dieses war falsch.<br />

Tatsächlich ändert der UPDATE-Befehl also e<strong>in</strong>e Menge von Datensätzen: je<br />

nach WHERE-Klausel null, e<strong>in</strong>en, mehrere oder alle Zeilen der Tabelle.<br />

9.3. DELETE – Daten löschen<br />

Der DELETE-Befehl löscht e<strong>in</strong>e Menge von Datensätzen <strong>in</strong> e<strong>in</strong>er Tabelle:<br />

DELETE FROM <br />

[ WHERE ] ;<br />

Zu diesem Befehl gehören folgende Angaben:<br />

• DELETE als Name des Befehls, FROM als feststehender Begriff<br />

• für die Tabelle, aus der die Datenmenge zu entfernen ist<br />

• die WHERE-Klausel mit Bed<strong>in</strong>gungen, welche Datensätze zu löschen s<strong>in</strong>d: e<strong>in</strong>er<br />

oder e<strong>in</strong>e bestimmte Menge<br />

Die Struktur der WHERE-Klausel ist identisch mit derjenigen beim SELECT-<br />

Befehl. Wenn alle Datensätze gelöscht werden sollen, kann die WHERE-<br />

Bed<strong>in</strong>gung entfallen; aber beachten Sie unbed<strong>in</strong>gt:<br />

Ohne WHERE-Bed<strong>in</strong>gung wird wirklich alles sofort gelöscht.<br />

Beispiele:<br />

Aufgabe: Der Mitarbeiter mit der Personalnummer 20001 ist ausgeschieden.<br />

DELETE FROM Mitarbeiter<br />

WHERE Personalnummer = 20001;<br />

Aufgabe: Die Abteilung 1 wurde ausgelagert, alle Mitarbeiter gehören nicht<br />

mehr zum Unternehmen.<br />

DELETE FROM Mitarbeiter<br />

WHERE Abteilung_ID = 1;<br />

Aufgabe: Entferne den gesamten Inhalt der Tabelle.<br />

DELETE FROM Schadensfall;<br />

Achtung: Dies löscht ohne weitere Rückfrage alle Schadensfälle; die Struktur der<br />

Tabelle selbst bleibt erhalten. E<strong>in</strong> solcher Befehl sollte unbed<strong>in</strong>gt nur nach e<strong>in</strong>er<br />

74


TRUNCATE – Tabelle leeren<br />

vorherigen Datensicherung ausgeführt werden. Auch der Versuch ist „strafbar“<br />

und führt zum sofortigen Datenverlust.<br />

9.4. TRUNCATE – Tabelle leeren<br />

Wenn Sie entgegen den oben genannten H<strong>in</strong>weisen wirklich alle Datensätze<br />

e<strong>in</strong>er Tabelle löschen wollen, können Sie (soweit vorhanden) anstelle von<br />

DELETE den TRUNCATE-Befehl benutzen. Damit werden (ohne Verb<strong>in</strong>dung mit<br />

WHERE) immer alle Datensätze gelöscht; dies geschieht schneller und e<strong>in</strong>facher,<br />

weil auf das <strong>in</strong>terne Änderungsprotokoll der Datenbank verzichtet wird.<br />

TRUNCATE TABLE Schadensfall;<br />

9.5. Zusammenfassung<br />

In diesem Kapitel lernten wir die <strong>SQL</strong>-Befehle kennen, mit denen der Datenbestand<br />

geändert wird:<br />

• Mit INSERT + VALUES wird e<strong>in</strong> e<strong>in</strong>zelner Datensatz e<strong>in</strong>gefügt.<br />

• Mit INSERT + SELECT wird e<strong>in</strong>e Menge von Datensätzen mit Hilfe e<strong>in</strong>er Abfrage<br />

e<strong>in</strong>gefügt.<br />

• Mit UPDATE wird e<strong>in</strong>e Menge von Datensätzen geändert; die Menge wird<br />

durch WHERE festgelegt.<br />

• Mit DELETE wird e<strong>in</strong>e Menge von Datensätzen gelöscht; die Menge wird durch<br />

WHERE festgelegt.<br />

• Mit TRUNCATE werden alle Datensätze e<strong>in</strong>er Tabelle gelöscht.<br />

Die WHERE-Bed<strong>in</strong>gungen s<strong>in</strong>d hier besonders wichtig, damit ke<strong>in</strong>e falschen<br />

Speicherungen erfolgen.<br />

9.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 76.<br />

Übung 1 – Daten e<strong>in</strong>zeln e<strong>in</strong>fügen<br />

Welche Angaben werden benötigt, wenn e<strong>in</strong> e<strong>in</strong>zelner Datensatz <strong>in</strong> der Datenbank<br />

gespeichert werden soll?<br />

75


DML (2) – Daten speichern<br />

Übung 2 – Daten e<strong>in</strong>zeln e<strong>in</strong>fügen<br />

Speichern Sie <strong>in</strong> der Tabelle Mitarbeiter e<strong>in</strong>en neuen Datensatz und lassen Sie<br />

alle Spalten und Werte weg, die nicht benötigt werden.<br />

Übung 3 – Daten e<strong>in</strong>fügen<br />

Begründen Sie, warum der Spalte ID beim E<strong>in</strong>fügen <strong>in</strong> der Regel ke<strong>in</strong> Wert zugewiesen<br />

wird.<br />

Übung 4 – Daten e<strong>in</strong>fügen<br />

Begründen Sie, warum die folgenden Befehle nicht ausgeführt werden können.<br />

/* Mitarbeiter */<br />

INSERT INTO Mitarbeiter<br />

( ID, Personalnummer, Name, Vorname, Geburtsdatum, Ist_Leiter, Abteilung_ID )<br />

VALUES ( 4, ’PD-348’, ’Çiçek’, ’Yasem<strong>in</strong>’, ’23.08.1984’, ’J’, 9 );<br />

/* Dienstwagen 1 */<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )<br />

VALUES ( ’DO-UF 1234’, null, null, null );<br />

/* Dienstwagen 2 */<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )<br />

VALUES ( ’HAM-AB 1234’, ’rot’, 7, null );<br />

Übung 5 – Daten ändern und löschen<br />

Warum gibt es selten UPDATE- oder DELETE-Befehle ohne e<strong>in</strong>e WHERE-<br />

Klausel?<br />

Übung 6 – Daten ändern<br />

Schreiben Sie e<strong>in</strong>en <strong>SQL</strong>-Befehl für folgende Änderung: Alle Mitarbeiter, die bisher<br />

noch ke<strong>in</strong>en Mobil-Anschluss hatten, sollen unter e<strong>in</strong>er e<strong>in</strong>heitlichen Nummer<br />

erreichbar se<strong>in</strong>.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 75.<br />

Lösung zu Übung 1 – Daten e<strong>in</strong>zeln e<strong>in</strong>fügen<br />

• der INSERT-Befehl selbst<br />

76


• INTO mit Angabe der Tabelle<br />

Übungen<br />

• bei Bedarf <strong>in</strong> Klammern die Liste der Spalten, die mit Werten versehen werden<br />

• VALUES zusammen mit (<strong>in</strong> Klammern) der Liste der zugeordneten Werte<br />

Lösung zu Übung 2 – Daten e<strong>in</strong>zeln e<strong>in</strong>fügen<br />

Beispielsweise so:<br />

INSERT INTO Mitarbeiter<br />

( Personalnummer, Name, Vorname, Geburtsdatum, Ist_Leiter, Abteilung_ID )<br />

VALUES ( ’PD-348’, ’Çiçek’, ’Yasem<strong>in</strong>’, ’23.08.1984’, ’J’, 9 );<br />

Lösung zu Übung 3 – Daten e<strong>in</strong>fügen<br />

Dieser soll von der Datenbank automatisch zugewiesen werden; er muss deshalb<br />

weggelassen oder mit NULL v<strong>org</strong>egeben werden.<br />

Lösung zu Übung 4 – Daten e<strong>in</strong>fügen<br />

• Mitarbeiter: Diese ID ist schon vergeben; sie muss aber e<strong>in</strong>deutig se<strong>in</strong>.<br />

• Dienstwagen 1: Der Fahrzeugtyp ist e<strong>in</strong>e Pflicht-Angabe; die Fahrzeugtyp_ID<br />

darf nicht null se<strong>in</strong>.<br />

• Dienstwagen 2: Das Kennzeichen ist zu lang; es darf maximal e<strong>in</strong>e Länge von<br />

10 Zeichen haben.<br />

Lösung zu Übung 5 – Daten ändern und löschen<br />

Dies würde die gleiche Änderung bzw. die Löschung für alle Datensätze ausführen;<br />

das ist selten s<strong>in</strong>nvoll bzw. gewünscht.<br />

Lösung zu Übung 6 – Daten ändern<br />

UPDATE Mitarbeiter<br />

SET Mobil = ’(0177) 44 55 66 77’<br />

WHERE Mobil IS NULL OR Mobil = ”;<br />

77


10. DDL – Struktur der Datenbank<br />

10.1. Allgeme<strong>in</strong>e Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . 79<br />

10.2. Hauptteile der Datenbank . . . . . . . . . . . . . . . . . . . . . . 80<br />

10.3. Ergänzungen zu Tabellen . . . . . . . . . . . . . . . . . . . . . . . 83<br />

10.4. Programmieren mit <strong>SQL</strong> . . . . . . . . . . . . . . . . . . . . . . . 85<br />

10.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 85<br />

10.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86<br />

10.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87<br />

Mit den Befehlen der Data Def<strong>in</strong>ition Language (DDL) wird die Struktur der Datenbank<br />

gesteuert. Diese Aufgaben gehen über e<strong>in</strong>e <strong>E<strong>in</strong>führung</strong> <strong>in</strong> <strong>SQL</strong> h<strong>in</strong>aus.<br />

Hier werden deshalb nur e<strong>in</strong>ige grundlegende Informationen und Beispiele behandelt,<br />

für welche Objekte e<strong>in</strong>er Datenbank diese Befehle verwendet werden.<br />

Vor allem bei DDL-Befehlen muss e<strong>in</strong>e Benutzer<strong>in</strong> über die nötigen Zugriffsrechte<br />

1 verfügen. E<strong>in</strong>ige Erweiterungen zu DDL folgen gegen Ende des Buches:<br />

• DDL − E<strong>in</strong>zelheiten 2<br />

• Änderung der Datenbankstruktur 3<br />

• <strong>SQL</strong>-Programmierung 4<br />

10.1. Allgeme<strong>in</strong>e Syntax<br />

Die DDL-Befehle s<strong>in</strong>d grundsätzlich so aufgebaut:<br />

BEFEHL OBJEKTTYP []<br />

CREATE erzeugt e<strong>in</strong> Objekt wie e<strong>in</strong>e Datentabelle oder gar e<strong>in</strong>e Datenbank.<br />

ALTER ermöglicht die Änderung e<strong>in</strong>es Objekts, z. B. e<strong>in</strong>er Tabelle. In neueren<br />

Versionen gibt es auch den geme<strong>in</strong>samen Aufruf (unterschiedlich je nach<br />

1 Kapitel 12 auf Seite 95<br />

2 Kapitel 28 auf Seite 285<br />

3 Kapitel 35 auf Seite 397<br />

4 Kapitel 30 auf Seite 323<br />

79


DDL – Struktur der Datenbank<br />

DBMS); das DBMS entscheidet dann selbst: Wenn das Objekt schon existiert,<br />

wird es geändert, andernfalls erzeugt.<br />

• CREATE OR ALTER<br />

• CREATE OR REPLACE<br />

• RECREATE<br />

DROP dient dazu, das Objekt, z. B. e<strong>in</strong>e Tabelle wieder zu löschen.<br />

10.2. Hauptteile der Datenbank<br />

10.2.1. DATABASE – die Datenbank selbst<br />

Der Befehl zum Erstellen e<strong>in</strong>er Datenbank lautet:<br />

CREATE DATABASE [ ] ;<br />

Der ist meistens e<strong>in</strong> vollständiger Name e<strong>in</strong>schließlich Pfad; <strong>in</strong> e<strong>in</strong>er<br />

solchen Datei werden alle Teile der Datenbank zusammengefasst. Zu den<br />

gehören z. B. der Benutzername des Eigentümers der Datenbank<br />

mit se<strong>in</strong>em Passwort, der Zeichensatz mit Angaben zur Standardsortierung, die<br />

Aufteilung <strong>in</strong> e<strong>in</strong>e oder mehrere Dateien usw.<br />

Jedes DBMS bietet sehr verschiedene Optionen; wir können hier ke<strong>in</strong>e Geme<strong>in</strong>samkeiten<br />

vorstellen und müssen deshalb ganz auf Beispiele verzichten.<br />

Wegen der vielen Möglichkeiten ist zu empfehlen, dass e<strong>in</strong>e Datenbank nicht per<br />

<strong>SQL</strong>-Befehl, sondern <strong>in</strong>nerhalb e<strong>in</strong>er Benutzeroberfläche erstellt wird.<br />

Mit ALTER DATABASE werden die Optionen geändert, mit DROP DATABASE<br />

wird die Datenbank gelöscht. Diese Befehle kennt nicht jedes DBMS.<br />

10.2.2. TABLE – e<strong>in</strong>e e<strong>in</strong>zelne Tabelle<br />

CREATE TABLE<br />

Um e<strong>in</strong>e Tabelle zu erzeugen, s<strong>in</strong>d wesentlich konkretere umfangreiche Angaben<br />

nötig.<br />

80<br />

CREATE TABLE <br />

( <br />

[ , ]<br />

);


Zum Erzeugen e<strong>in</strong>er Tabelle werden folgende Angaben benutzt:<br />

Hauptteile der Datenbank<br />

• der Name der Tabelle, mit dem die Daten über die DML-Befehle gespeichert<br />

und abgerufen werden<br />

• die Liste der Spalten (Felder), und zwar vor allem mit dem jeweiligen Datentyp<br />

• Angaben wie der Primärschlüssel (PRIMARY KEY, PK) oder weitere Indizes<br />

Die Spalten und Zusatzangaben werden <strong>in</strong> Klammern ( ) zusammengefasst. Jede<br />

Angabe wird mit e<strong>in</strong>em Komma abgeschlossen; dieses entfällt vor der schließenden<br />

Klammer. Die Zusatzangaben werden häufig nicht sofort festgelegt, sondern<br />

durch anschließende ALTER TABLE-Befehle; sie werden deshalb weiter unten<br />

besprochen.<br />

Aufgabe: In der Beispieldatenbank wird e<strong>in</strong>e Tabelle so erzeugt:<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE TABLE Dienstwagen<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Kennzeichen VARCHAR(30) NOT NULL,<br />

Farbe VARCHAR(30),<br />

Fahrzeugtyp_ID INTEGER NOT NULL,<br />

Mitarbeiter_ID INTEGER<br />

);<br />

Die e<strong>in</strong>zelnen Spalten berücksichtigen mit ihren Festlegungen unterschiedliche<br />

Anforderungen:<br />

• ID ist e<strong>in</strong>e ganze Zahl, darf nicht NULL se<strong>in</strong>, wird automatisch hochgezählt<br />

und dient dadurch gleichzeitig als Primärschlüssel.<br />

• Das Kennzeichen ist e<strong>in</strong>e Zeichenkette von variabler Länge (maximal 30 Zeichen),<br />

die unbed<strong>in</strong>gt erforderlich ist.<br />

• Die Farbe ist ebenfalls e<strong>in</strong>e Zeichenkette, deren Angabe auch fehlen kann.<br />

• Für den Fahrzeugtyp wird dessen ID benötigt, wie er <strong>in</strong> der Tabelle Fahrzeugtyp<br />

gespeichert ist; diese Angabe muss se<strong>in</strong> − e<strong>in</strong> „unbekannter“ Fahrzeugtyp<br />

macht bei e<strong>in</strong>em Dienstwagen ke<strong>in</strong>en S<strong>in</strong>n.<br />

• Für den Mitarbeiter, dem e<strong>in</strong> Dienstwagen zugeordnet ist, wird dessen ID aus<br />

der Tabelle Mitarbeiter benötigt. Dieser Wert kann entfallen, wenn es sich nicht<br />

um e<strong>in</strong>en „persönlichen“ Dienstwagen handelt.<br />

E<strong>in</strong>e besondere Bedeutung hat die Spalte ID als automatischer Zähler, der durch<br />

AUTO_INCREMENT e<strong>in</strong>gerichtet wird. Er gehört genau zu der betreffenden Tabelle<br />

und hat als Primärschlüssel ke<strong>in</strong>e weitere <strong>in</strong>haltliche Bedeutung.<br />

81


DDL – Struktur der Datenbank<br />

ALTER TABLE<br />

Die Struktur e<strong>in</strong>er Tabelle wird mit dieser Syntax geändert:<br />

ALTER TABLE <br />

Mit der Aufgabe ADD CONSTRAINT wird e<strong>in</strong>e <strong>in</strong>terne E<strong>in</strong>schränkung − Constra<strong>in</strong>t<br />

genannt − h<strong>in</strong>zugefügt:<br />

Aufgabe: E<strong>in</strong> Primärschlüssel kann auch nachträglich festgelegt werden, z. B.<br />

wie folgt:<br />

Firebird-Quelltext<br />

ALTER TABLE Dienstwagen<br />

ADD CONSTRAINT Dienstwagen_PK PRIMARY KEY (ID);<br />

Die E<strong>in</strong>schränkung bekommt den Namen Dienstwagen_PK und legt fest, dass es<br />

sich dabei um den PRIMARY KEY unter Verwendung der Spalte ID handelt.<br />

Aufgabe: In der Tabelle Mitarbeiter muss auch die Personalnummer e<strong>in</strong>deutig<br />

se<strong>in</strong> (zusätzlich zur ID, die als PK sowieso e<strong>in</strong>deutig ist):<br />

ALTER TABLE Mitarbeiter<br />

ADD CONSTRAINT Mitarbeiter_PersNr UNIQUE (Personalnummer);<br />

Aufgabe: In der Tabelle Zuordnung_SF_FZ − Verknüpfung zwischen den Schadensfällen<br />

und den Fahrzeugen − wird e<strong>in</strong> Feld für sehr lange Texte e<strong>in</strong>gefügt:<br />

ALTER TABLE Zuordnung_SF_FZ<br />

ADD Beschreibung BLOB;<br />

Mit ALTER . . . DROP Beschreibung kann dieses Feld auch wieder gelöscht werden.<br />

DROP TABLE<br />

Aufgabe: Damit wird e<strong>in</strong>e Tabelle mit allen Daten gelöscht (die folgende Tabelle<br />

gab es <strong>in</strong> e<strong>in</strong>er früheren Version der Beispieldatenbank):<br />

DROP TABLE ZUORD_VNE_SCF;<br />

Warnung: Dies löscht die Tabelle e<strong>in</strong>schließlich aller Daten unwiderruflich!<br />

82


10.2.3. USER – Benutzer<br />

Ergänzungen zu Tabellen<br />

Aufgabe: Auf diese Weise wird e<strong>in</strong> neuer Benutzer für die Arbeit mit der aktuellen<br />

Datenbank registriert.<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE USER Hans_Dampf IDENTIFIED BY ’cH4y37X1P’;<br />

Dieser Benutzer muss sich mit dem Namen Hans_Dampf und dem Passwort<br />

'cH4y37X1P' anmelden. − Jedes DBMS kennt eigene Optionen für die Zuordnung<br />

des Passworts und die Verwendung von Anführungszeichen.<br />

10.3. Ergänzungen zu Tabellen<br />

Weitere Objekte <strong>in</strong> der Datenbank erleichtern die Arbeit mit Tabellen.<br />

10.3.1. VIEW – besondere Ansichten<br />

E<strong>in</strong>e VIEW ist e<strong>in</strong>e Sicht auf e<strong>in</strong>e oder mehrere Tabellen. Für den Anwender sieht<br />

es wie e<strong>in</strong>e eigene Tabelle aus; es handelt sich aber „nur“ um e<strong>in</strong>e fest gespeicherte<br />

Abfrage. Dabei wird nur die Abfrage fest gespeichert, nicht die Ergebnismenge;<br />

diese wird bei jedem neuen Aufruf nach den aktuellen Daten neu erstellt.<br />

E<strong>in</strong>zelheiten werden unter Erstellen von Views 5 behandelt.<br />

10.3.2. INDEX – Datenzugriff beschleunigen<br />

E<strong>in</strong> Index beschleunigt die Suche nach Datensätzen. Um <strong>in</strong> der Tabelle Versicherungsnehmer<br />

nach dem Namen „Schulze“ zu suchen, würde es zu lange dauern,<br />

wenn das DBMS alle Zeilen durchgehen müsste, bis es auf diesen Namen träfe.<br />

Stattdessen wird e<strong>in</strong> Index angelegt (ähnlich wie <strong>in</strong> e<strong>in</strong>em Telefon- oder Wörterbuch),<br />

sodass schnell alle passenden Datensätze gefunden werden.<br />

Aufgabe: So wird e<strong>in</strong> Index mit der Bezeichnung Versicherungsnehmer_Name<br />

für die Komb<strong>in</strong>ation „Name, Vorname“ angelegt:<br />

CREATE INDEX Versicherungsnehmer_Name<br />

ON Versicherungsnehmer (Name, Vorname);<br />

Es ist dr<strong>in</strong>gend zu empfehlen, dass Indizes für alle Spalten bzw. Komb<strong>in</strong>ationen<br />

von Spalten angelegt werden, die immer wieder zum Suchen benutzt werden.<br />

5 Kapitel 27 auf Seite 271<br />

83


DDL – Struktur der Datenbank<br />

Weitere E<strong>in</strong>zelheiten werden unter DDL − E<strong>in</strong>zelheiten 6 behandelt.<br />

10.3.3. IDENTITY – auch e<strong>in</strong> automatischer Zähler<br />

Anstelle von AUTO_INCREMENT verwendet MS-<strong>SQL</strong> diese Erweiterung für die<br />

automatische Nummerierung neuer Datensätze:<br />

MS-<strong>SQL</strong>-Quelltext<br />

CREATE TABLE Fahrzeug<br />

(ID INTEGER NOT NULL IDENTITY(1,1),<br />

Kennzeichen VARCHAR(10) NOT NULL,<br />

Farbe VARCHAR(30),<br />

Fahrzeugtyp_ID INTEGER NOT NULL,<br />

CONSTRAINT Fahrzeug_PK PRIMARY KEY (ID)<br />

);<br />

Der erste Parameter bezeichnet den Startwert, der zweite Parameter die Schrittweite<br />

zum nächsten ID-Wert.<br />

10.3.4. SEQUENCE – Ersatz für automatischen Zähler<br />

Wenn das DBMS für Spalten ke<strong>in</strong>e automatische Zählung kennt (Firebird,<br />

Oracle), steht dies als Ersatz zur Verfügung.<br />

Firebird-Quelltext<br />

/* zuerst die Folge def<strong>in</strong>ieren */<br />

CREATE SEQUENCE Versicherungsnehmer_ID;<br />

/* dann den Startwert festlegen */<br />

ALTER SEQUENCE Versicherungsnehmer_ID RESTART WITH 1;<br />

/* und im Trigger (s.u.) ähnlich wie e<strong>in</strong>e Funktion benutzen */<br />

NEXT VALUE FOR Versicherungsnehmer_ID<br />

Während e<strong>in</strong>e AUTO_INCREMENT-Spalte genau zu der betreffenden Tabelle gehört,<br />

bezieht sich e<strong>in</strong>e „Sequenz“ auf die gesamte Datenbank. Es ist ohne Weiteres<br />

möglich, e<strong>in</strong>e e<strong>in</strong>zige Sequenz AllMyIDs zu def<strong>in</strong>ieren und die neue ID e<strong>in</strong>er<br />

jeden Tabelle daraus abzuleiten. Dies ist durchaus s<strong>in</strong>nvoll, weil die ID als<br />

Primärschlüssel sowieso ke<strong>in</strong>e weitere <strong>in</strong>haltliche Bedeutung haben darf. In der<br />

Beispieldatenbank benutzen wir getrennte Sequenzen, weil sie für die verschiedenen<br />

DBMS „ähnlich“ aussehen soll.<br />

Oracle arbeitet (natürlich) mit anderer Syntax (mit mehr Möglichkeiten) und benutzt<br />

dann NEXTVAL.<br />

6 Kapitel 28 auf Seite 285<br />

84


10.4. Programmieren mit <strong>SQL</strong><br />

Programmieren mit <strong>SQL</strong><br />

Die Funktionalität e<strong>in</strong>er <strong>SQL</strong>-Datenbank kann erweitert werden, und zwar auch<br />

mit Bestandteilen e<strong>in</strong>er „vollwertigen“ Programmiersprache, z. B. Schleifen und<br />

IF-Abfragen.<br />

Dies wird unter Programmierung 7 behandelt; dabei gibt es relativ wenig Geme<strong>in</strong>samkeiten<br />

zwischen den DBMS.<br />

FUNCTION − Benutzerdef<strong>in</strong>ierte Funktionen<br />

Eigene Funktionen ergänzen die (<strong>in</strong>ternen) Skalarfunktionen des DBMS.<br />

PROCEDURE − Gespeicherte Prozeduren<br />

E<strong>in</strong>e Prozedur − gespeicherte Prozedur, engl. StoredProcedure (SP) − ist<br />

v<strong>org</strong>esehen für Arbeitsabläufe, die „immer wiederkehrende“ Arbeiten ausführen<br />

sollen. Es gibt sie mit und ohne Argumente und Rückgabewerte.<br />

TRIGGER − Ereignisse beim Speichern<br />

E<strong>in</strong> Trigger ist e<strong>in</strong> Arbeitsablauf, der automatisch beim Speichern <strong>in</strong> e<strong>in</strong>er<br />

Tabelle ausgeführt wird. Dies dient E<strong>in</strong>gabeprüfungen oder zusätzlichen<br />

Maßnahmen; beispielsweise holt e<strong>in</strong> Trigger den NEXT VALUE e<strong>in</strong>er SE-<br />

QUENCE (siehe oben).<br />

TRIGGER werden unterschieden nach INSERT-, UPDATE- oder DELETE-<br />

Befehlen und können vor oder nach dem Speichern sowie <strong>in</strong> e<strong>in</strong>er bestimmten<br />

Reihenfolge ausgeführt werden.<br />

10.5. Zusammenfassung<br />

In diesem Kapitel lernten wir die Grundbegriffe für e<strong>in</strong>e Datenbankstruktur<br />

kennen:<br />

• DATABASE selbst sowie TABLE s<strong>in</strong>d die wichtigsten Objekte, e<strong>in</strong> USER arbeitet<br />

damit.<br />

• E<strong>in</strong>e VIEW ist e<strong>in</strong>e gespeicherte Abfrage, die wie e<strong>in</strong>e Tabelle abgerufen wird.<br />

• E<strong>in</strong> INDEX beschleunigt den Datenzugriff.<br />

Darüber h<strong>in</strong>aus wurde auf weitere Möglichkeiten wie SEQUENCE, Funktionen,<br />

Prozeduren und Trigger h<strong>in</strong>gewiesen.<br />

7 Kapitel 30 auf Seite 323<br />

85


DDL – Struktur der Datenbank<br />

10.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 86.<br />

Übung 1 – Objekte bearbeiten<br />

Welche der folgenden <strong>SQL</strong>-Befehle enthalten Fehler?<br />

1. CREATE DATABASE C:\DBFILES\employee.gdb DEFAULT CHARACTER SET<br />

UTF8;<br />

2. DROP DATABASE;<br />

3. CREATE TABLE Person<br />

( ID PRIMARY KEY, Name VARCHAR(30), Vorname VARCHAR(30) );<br />

4. ALTER TABLE ADD UNIQUE KEY (Name);<br />

Übung 2 – Tabelle erstellen<br />

Auf welchen zwei Wegen kann der Primärschlüssel (Primary Key, PK) e<strong>in</strong>er Tabelle<br />

festgelegt werden? E<strong>in</strong> weiterer Weg ist ebenfalls üblich, aber noch nicht erwähnt<br />

worden.<br />

Übung 3 – Tabelle ändern<br />

Skizzieren Sie e<strong>in</strong>en Befehl, mit dem die Tabelle Mitarbeiter um Felder für die<br />

Anschrift erweitert werden kann.<br />

Übung 4 – Tabellen<br />

Wor<strong>in</strong> unterscheiden sich TABLE und VIEW <strong>in</strong> erster L<strong>in</strong>ie?<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 86.<br />

Lösung zu Übung 1 – Objekte bearbeiten<br />

Bei 2. fehlt der Name der Datenbank.<br />

Bei 3. fehlt der Datentyp zur Spalte ID.<br />

Bei 4. fehlt der Name der Tabelle, die geändert werden soll.<br />

86


Lösung zu Übung 2 – Tabelle erstellen<br />

Siehe auch<br />

1. Im CREATE TABLE-Befehl zusammen mit der Spalte, die als PK benutzt<br />

wird, z. B.:<br />

ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

2. Durch e<strong>in</strong>en ALTER TABLE-Befehl, der den PK h<strong>in</strong>zufügt:<br />

ALTER TABLE Dienstwagen ADD PRIMARY KEY (ID);<br />

Lösung zu Übung 3 – Tabelle ändern<br />

ALTER TABLE Mitarbeiter<br />

ADD PLZ CHAR(5),<br />

ADD Ort VARCHAR(24),<br />

ADD Strasse VARCHAR(24),<br />

ADD Hausnummer VARCHAR(10);<br />

Lösung zu Übung 4 – Tabellen<br />

E<strong>in</strong>e TABLE (Tabelle) ist real <strong>in</strong> der Datenbank gespeichert. E<strong>in</strong>e VIEW (Sichttabelle)<br />

ist e<strong>in</strong>e Sicht auf e<strong>in</strong>e oder mehrere tatsächlich vorhandene Tabellen; sie<br />

enthält e<strong>in</strong>e Abfrage auf diese Tabellen, die als Abfrage <strong>in</strong> der Datenbank gespeichert<br />

ist, aber für den Anwender wie e<strong>in</strong>e eigene Tabelle aussieht.<br />

10.7. Siehe auch<br />

Bei diesem Kapitel s<strong>in</strong>d die folgenden Erläuterungen zu beachten:<br />

• Datentypen 8<br />

Manche Themen werden <strong>in</strong> den folgenden Kapiteln genauer behandelt:<br />

• Eigene Funktionen 9 als Ergänzung zu denen, die <strong>in</strong> Funktionen 10 und Funktionen<br />

(2) 11 behandelt werden.<br />

• Prozeduren 12<br />

• Trigger 13<br />

8 Kapitel 13 auf Seite 97<br />

9 Kapitel 31 auf Seite 347<br />

10 Kapitel 14 auf Seite 109<br />

11 Kapitel 16 auf Seite 141<br />

12 Kapitel 32 auf Seite 357<br />

13 Kapitel 33 auf Seite 377<br />

87


11. TCL – Ablaufsteuerung<br />

11.1. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89<br />

11.2. Transaktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90<br />

11.3. Misserfolg regeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91<br />

11.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 92<br />

11.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92<br />

Dieses Kapitel gibt e<strong>in</strong>e kurze <strong>E<strong>in</strong>führung</strong> <strong>in</strong> die Transaction Control Language<br />

(TCL). Deren Befehle s<strong>org</strong>en für die Datensicherheit <strong>in</strong>nerhalb e<strong>in</strong>er Datenbank.<br />

My<strong>SQL</strong> verfolgt e<strong>in</strong>e „offenere“ Philosophie und arbeitet neben Transaktionen<br />

auch mit anderen Sicherungsmaßnahmen. Der Ersteller e<strong>in</strong>er Datenbank muss<br />

sich für e<strong>in</strong> Verfahren entscheiden, kann aber auch danach noch variieren.<br />

11.1. Beispiele<br />

E<strong>in</strong>e <strong>SQL</strong>-Datenbank speichert Daten <strong>in</strong> der Regel <strong>in</strong> verschiedenen Tabellen.<br />

Dabei ist es nötig, dass die betreffenden Befehle immer „gleichzeitig“ ausgeführt<br />

werden. Da das nicht möglich ist − zwei Befehle können nur nache<strong>in</strong>ander erledigt<br />

werden −, muss sichergestellt se<strong>in</strong>, dass nicht der e<strong>in</strong>e Befehl ausgeführt<br />

wird, während der andere Befehl scheitert.<br />

• Zu e<strong>in</strong>er Überweisung bei der Bank gehören immer zwei Buchungen: die Gutschrift<br />

auf dem e<strong>in</strong>en und die Lastschrift auf dem anderen Konto; häufig gehören<br />

die Konten zu verschiedenen Banken. Es wäre völlig unerträglich, wenn die<br />

Gutschrift ausgeführt würde und die (externe) Lastschrift nicht, weil <strong>in</strong> diesem<br />

Moment die Datenleitung unterbrochen wird.<br />

• Wenn <strong>in</strong> dem Versicherungsunternehmen der Beispieldatenbank e<strong>in</strong> neuer<br />

Vertrag abgeschlossen wird, gehören dazu mehrere INSERT-Befehle, und zwar<br />

<strong>in</strong> die Tabellen Fahrzeug, Versicherungsnehmer, Versicherungsvertrag. Zuerst<br />

müssen Fahrzeug und Versicherungsnehmer gespeichert werden; aber wenn<br />

das Speichern des Vertrags „schiefgeht“, hängen die beiden anderen Datensätze<br />

nutzlos <strong>in</strong> der Datenbank herum.<br />

89


TCL – Ablaufsteuerung<br />

• Wenn dort e<strong>in</strong>e Abteilung ausgelagert wird, werden alle ihre Mitarbeiter gestrichen,<br />

weil sie nicht mehr zum Unternehmen gehören. Wie soll verfahren<br />

werden, wenn nur e<strong>in</strong> Teil der DELETE-Befehle erfolgreich war?<br />

• E<strong>in</strong>e Menge e<strong>in</strong>zelner Befehle (z. B. 1000 INSERTs <strong>in</strong>nerhalb e<strong>in</strong>er Schleife)<br />

dauert „ewig lange“.<br />

Solche Probleme können nicht nur durch die Hardware entstehen, sondern auch<br />

dadurch, dass parallel andere Nutzer denselben Datenbestand ändern wollen.<br />

11.2. Transaktionen<br />

Alle solche Ungereimtheiten werden vermieden, <strong>in</strong>dem <strong>SQL</strong>-Befehle <strong>in</strong> Transaktionen<br />

zusammengefasst und ausgeführt werden:<br />

• Entweder alle Befehle können ausgeführt werden. Dann wird die Transaktion<br />

bestätigt und erfolgreich abgeschlossen.<br />

• Oder (m<strong>in</strong>destens) e<strong>in</strong> Befehl kann nicht ausgeführt werden. Dann wird die<br />

Transaktion für ungültig erklärt; alle Befehle werden rückgängig gemacht.<br />

• Auch das Problem mit der langen Arbeitszeit von 1000 INSERTs wird vermieden,<br />

wenn das DBMS nicht jeden Befehl e<strong>in</strong>zeln prüft und bestätigt,<br />

Es gibt verschiedene Arten von Transaktionen. Diese hängen vom DBMS und<br />

dessen Version, der Hardware (E<strong>in</strong>zel- oder Mehrplatzsystem) und dem Datenzugriff<br />

(direkt oder über Anwenderprogramme) ab.<br />

Der Beg<strong>in</strong>n e<strong>in</strong>er Transaktion wird durch e<strong>in</strong>e dieser Maßnahmen geregelt.<br />

• Wenn die Datenbank auf AUTOCOMMIT e<strong>in</strong>gestellt ist, wird jeder <strong>SQL</strong>-Befehl<br />

als e<strong>in</strong>zelne Transaktion behandelt und sofort gültig. (Das wäre die Situation<br />

mit der langen Arbeitszeit von 1000 INSERTs.)<br />

• Wenn e<strong>in</strong> Stapel von Befehlen mit COMMIT bestätigt oder mit ROLLBACK verworfen<br />

wird, dann wird mit dem nächsten Befehl implizit e<strong>in</strong>e neue Transaktion<br />

begonnen.<br />

• Mit e<strong>in</strong>em ausdrücklichen TRANSACTION-Befehl wird explizit e<strong>in</strong>e neue<br />

Transaktion begonnen:<br />

BEGIN TRANSACTION ; /* bei MS-<strong>SQL</strong> */<br />

SET TRANSACTION ; /* bei Firebird */<br />

START TRANSACTION; /* bei My<strong>SQL</strong> */<br />

E<strong>in</strong>e Transaktion kann mit e<strong>in</strong>em Namen versehen werden. Dies ist vor allem<br />

dann nützlich, wenn Transaktionen geschachtelt werden. Außerdem gibt es je<br />

nach DBMS viele weitere Optionen zur detaillierten Steuerung.<br />

90


Misserfolg regeln<br />

• E<strong>in</strong>e Transaktion, die implizit oder explizit begonnen wird, ist ausdrücklich abzuschließen<br />

durch COMMIT oder ROLLBACK. Andernfalls wird sie erst dann<br />

beendet, wenn die Verb<strong>in</strong>dung mit der Datenbank geschlossen wird.<br />

Der erfolgreiche Abschluss e<strong>in</strong>er Transaktion wird so geregelt:<br />

COMMIT [ TRANSACTION | WORK ] ;<br />

Die genaue Schreibweise und Varianten müssen <strong>in</strong> der DBMS-Dokumentation<br />

nachgelesen werden.<br />

Dieser Befehl bestätigt alle vorangegangenen Befehle e<strong>in</strong>er Transaktion und<br />

s<strong>org</strong>t dafür, dass sie „am Stück“ gespeichert werden.<br />

Auszug aus dem Skript zum Erstellen der Beispieldatenbank:<br />

Firebird-Quelltext<br />

COMMIT;<br />

/* damit wird die vorherige Transaktion abgeschlossen und implizit e<strong>in</strong>e<br />

neue Transaktion gestartet */<br />

INSERT INTO Dienstwagen (Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID)<br />

VALUES (’DO-WB 111’, ’elfenbe<strong>in</strong>’, 16, NULL);<br />

INSERT INTO Dienstwagen (Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID)<br />

SELECT ’DO-WB 3’ || Abteilung_ID || SUBSTRING(Personalnummer FROM 5 FOR 1),<br />

’gelb’, SUBSTRING(Personalnummer FROM 5 FOR 1), ID<br />

FROM Mitarbeiter<br />

WHERE Abteilung_ID IN (5, 8) AND Ist_Leiter = ’N’;<br />

/* damit wird diese Transaktion abgeschlossen */<br />

COMMIT;<br />

11.3. Misserfolg regeln<br />

Mit dem folgenden Befehl wird e<strong>in</strong>e Transaktion <strong>in</strong> „sichere“ Abschnitte geteilt:<br />

SAVEPOINT ;<br />

Bis zu diesem Sicherungspunkt werden die Befehle auch dann als gültig abgeschlossen,<br />

wenn die Transaktion am Ende für ungültig erklärt wird.<br />

Wenn e<strong>in</strong>e Transaktion missglückt, wird sie für ungültig erklärt:<br />

ROLLBACK [ TRANSACTION | WORK ] [ TO ] ;<br />

91


TCL – Ablaufsteuerung<br />

Damit werden alle Befehle der Transaktion für ungültig erklärt und<br />

rückgängig gemacht. Sofern e<strong>in</strong> Sicherungspunkt angegeben ist, werden<br />

die Befehle bis zu diesem Sicherungspunkt für gültig erklärt und erst alle<br />

folgenden für ungültig.<br />

Schreibweise und Varianten s<strong>in</strong>d <strong>in</strong> der DBMS-Dokumentation nachzulesen.<br />

Aufgabe: Im folgenden Beispiel werden zu Testzwecken e<strong>in</strong>ige Daten geändert<br />

und abgerufen; abschließend werden die Änderungen rückgängig gemacht.<br />

UPDATE Dienstwagen<br />

SET Farbe = ’goldgelb/violett gestreift’<br />

WHERE ID >= 14;<br />

SELECT * FROM Dienstwagen;<br />

ROLLBACK;<br />

11.4. Zusammenfassung<br />

In diesem Kapitel lernten Sie die Grundbegriffe von Transaktionen kennen:<br />

• E<strong>in</strong>e Transaktion wird implizit oder explizit begonnen.<br />

• E<strong>in</strong>e Transaktion wird mit e<strong>in</strong>em ausdrücklichen Befehl (oder durch Ende der<br />

Verb<strong>in</strong>dung) abgeschlossen.<br />

• Mit COMMIT wird e<strong>in</strong>e Transaktion erfolgreich abgeschlossen; die Daten werden<br />

abschließend gespeichert.<br />

• Mit ROLLBACK werden die Änderungen verworfen, ggf. ab e<strong>in</strong>em bestimmten<br />

SAVEPOINT.<br />

11.5. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 93.<br />

Übung 1 – Zusammengehörende Befehle<br />

Skizzieren Sie die Befehle, die geme<strong>in</strong>sam ausgeführt werden müssen, wenn<br />

e<strong>in</strong> neues Fahrzeug mit e<strong>in</strong>em noch nicht registrierten Fahrzeugtyp und Fahrzeughersteller<br />

gespeichert werden soll.<br />

Übung 2 – Zusammengehörende Befehle<br />

Skizzieren Sie die Befehle, die geme<strong>in</strong>sam ausgeführt werden müssen, wenn e<strong>in</strong><br />

neuer Schadensfall ohne weitere beteiligte Fahrzeuge registriert werden soll.<br />

92


Übung 3 – Zusammengehörende Befehle<br />

Übungen<br />

Skizzieren Sie die Befehle, die geme<strong>in</strong>sam ausgeführt werden müssen, wenn e<strong>in</strong><br />

neuer Schaden durch e<strong>in</strong>en „Eigenen Kunden“ gemeldet wird; dabei sollen e<strong>in</strong><br />

zweiter „Eigener Kunde“ sowie e<strong>in</strong> „Fremdkunde“ e<strong>in</strong>er bisher nicht gespeicherten<br />

Versicherungsgesellschaft beteiligt se<strong>in</strong>. Erwähnen Sie dabei auch den Inhalt<br />

des betreffenden E<strong>in</strong>trags.<br />

Übung 4 – Transaktionen<br />

Welche der folgenden Maßnahmen starten immer e<strong>in</strong>e neue Transaktion, welche<br />

unter Umständen, welche s<strong>in</strong>d unzulässig?<br />

1. das Herstellen der Verb<strong>in</strong>dung zur Datenbank<br />

2. der Befehl START TRANSACTION;<br />

3. der Befehl SET TRANSACTION ACTIVE;<br />

4. der Befehl SAVEPOINT wird ausgeführt<br />

5. der Befehl ROLLBACK wird ausgeführt<br />

Übung 5 – Transaktionen<br />

Welche der folgenden Maßnahmen beenden immer e<strong>in</strong>e Transaktion, welche<br />

unter Umständen, welche s<strong>in</strong>d unzulässig?<br />

1. das Schließen der Verb<strong>in</strong>dung zur Datenbank<br />

2. der Befehl END TRANSACTION;<br />

3. der Befehl SET TRANSACTION INACTIVE;<br />

4. der Befehl SAVEPOINT wird ausgeführt<br />

5. der Befehl ROLLBACK wird ausgeführt<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 92.<br />

Lösung zu Übung 1 – Zusammengehörende Befehle<br />

• INSERT INTO Fahrzeughersteller<br />

• INSERT INTO Fahrzeugtyp<br />

• INSERT INTO Fahrzeug<br />

93


TCL – Ablaufsteuerung<br />

Lösung zu Übung 2 – Zusammengehörende Befehle<br />

• INSERT INTO Schadensfall<br />

• INSERT INTO Zuordnung_SF_FZ<br />

Lösung zu Übung 3 – Zusammengehörende Befehle<br />

• INSERT INTO Schadensfall<br />

• INSERT INTO Versicherungsgesellschaft /* für den weiteren<br />

Fremdkunden */<br />

• INSERT INTO Versicherungsnehmer /* für den weiteren Fremdkunden */<br />

• INSERT INTO Fahrzeug /* für den weiteren Fremdkunden */<br />

• INSERT INTO Zuordnung_SF_FZ /* für den weiteren Fremdkunden */<br />

• INSERT INTO Zuordnung_SF_FZ /* für den beteiligten Eigenen Kunden */<br />

• INSERT INTO Zuordnung_SF_FZ /* für den Eigenen Kunden laut<br />

Schadensmeldung */<br />

94<br />

Lösung zu Übung 4 – Transaktionen<br />

1. ja, sofern AUTOCOMMIT festgelegt ist<br />

2. ja, aber nur, wenn das DBMS diese Variante vorsieht (wie My<strong>SQL</strong>)<br />

3. ja, aber nur, wenn das DBMS diese Variante vorsieht (wie Firebird); denn<br />

das Wort „ACTIVE“ wird nicht als Schlüsselwort, sondern als Name der<br />

Transaction <strong>in</strong>terpretiert<br />

4. ne<strong>in</strong>, das ist nur e<strong>in</strong> Sicherungspunkt <strong>in</strong>nerhalb e<strong>in</strong>er Transaktion<br />

5. ja, nämlich implizit für die folgenden Befehle<br />

Lösung zu Übung 5 – Transaktionen<br />

1. ja, und zwar immer<br />

2. ne<strong>in</strong>, diesen Befehl gibt es nicht<br />

3. ne<strong>in</strong>, dieser Befehl ist unzulässig; wenn das DBMS diese Variante kennt<br />

(wie Firebird), dann ist es der Start e<strong>in</strong>er Transaktion namens „INACTIVE“<br />

4. teilweise, das ist e<strong>in</strong> Sicherungspunkt <strong>in</strong>nerhalb e<strong>in</strong>er Transaktion und bestätigt<br />

die bisherigen Befehle, setzen aber dieselbe Transaktion fort<br />

5. ja, nämlich explizit durch Widerruf aller Befehle


12. DCL – Zugriffsrechte<br />

E<strong>in</strong>e „vollwertige“ <strong>SQL</strong>-Datenbank enthält umfassende Regelungen über die<br />

Vergabe von Rechten für den Zugriff auf Objekte (Tabellen, e<strong>in</strong>zelne Felder, <strong>in</strong>terne<br />

Funktionen usw.). Am Anfang stehen diese Rechte nur dem Ersteller der Datenbank<br />

und dem Systemadm<strong>in</strong>istrator zu. Andere Benutzer müssen ausdrücklich<br />

zu e<strong>in</strong>zelnen Handlungen ermächtigt werden.<br />

Da es sich dabei nicht um Maßnahmen für E<strong>in</strong>steiger handelt, beschränken wir<br />

uns auf e<strong>in</strong> paar Beispiele.<br />

GRANT<br />

Dieser Befehl gestattet e<strong>in</strong>e bestimmte Aktion mit Zugriff auf e<strong>in</strong> bestimmtes Objekt.<br />

Aufgabe: Der Benutzer Herr_Mueller darf Abfragen auf die Tabelle Abteilungen<br />

ausführen.<br />

GRANT SELECT ON Abteilung TO Herr_Mueller<br />

Aufgabe: Die Benutzer<strong>in</strong> Frau_Schulze darf Daten <strong>in</strong> der Tabelle Abteilungen<br />

ändern.<br />

GRANT UPDATE ON Abteilung TO Frau_Schulze<br />

REVOKE<br />

Dieser Befehl verweigert e<strong>in</strong>e bestimmte Aktion mit Zugriff auf e<strong>in</strong> bestimmtes<br />

Objekt.<br />

Aufgabe: Herr_Mueller darf künftig ke<strong>in</strong>e solche Abfragen mehr ausführen.<br />

REVOKE SELECT ON Abteilung FROM Herr_Mueller<br />

95


13. Datentypen<br />

13.1. Vordef<strong>in</strong>ierte Datentypen . . . . . . . . . . . . . . . . . . . . . . 97<br />

13.2. Konstruierte und benutzerdef<strong>in</strong>ierte Datentypen . . . . . . . 101<br />

13.3. Spezialisierte Datentypen . . . . . . . . . . . . . . . . . . . . . . 102<br />

13.4. Nationale und <strong>in</strong>ternationale Zeichensätze . . . . . . . . . . . 103<br />

13.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

13.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />

13.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108<br />

<strong>SQL</strong> kennt verschiedene Arten von Datentypen: vordef<strong>in</strong>ierte, konstruierte und<br />

benutzerdef<strong>in</strong>ierte. Dieses Kapitel erklärt Arten und Verwendungen.<br />

13.1. Vordef<strong>in</strong>ierte Datentypen<br />

Laut <strong>SQL</strong>-Standard s<strong>in</strong>d die folgenden Datentypen vordef<strong>in</strong>iert. Die fettgedruckten<br />

Begriffe s<strong>in</strong>d die entsprechenden reservierten Schlüsselwörter 1 .<br />

i Achtung<br />

Bei allen Datentypen weichen die <strong>SQL</strong>-Dialekte mehr oder weniger vom<br />

Standard ab.<br />

Die Abweichungen betreffen vor allem die folgenden Punkte:<br />

• bei den möglichen Werten, z. B. dem maximalen Wert e<strong>in</strong>er ganzen Zahl oder<br />

der Länge e<strong>in</strong>er Zeichenkette<br />

• bei der Bearbeitung e<strong>in</strong>zelner Typen; z. B. kennt Firebird noch nicht sehr lange<br />

den BOOLEAN-Typ und musste mit dem Ersatz „ganze Zahl gleich 1“ leben<br />

• bei der Art der Werte, z. B. ob Datum und Zeit getrennt oder geme<strong>in</strong>sam gespeichert<br />

werden<br />

1 Anhang C.5 auf Seite 440<br />

97


Datentypen<br />

13.1.1. Zeichen und Zeichenketten<br />

Es gibt Zeichenketten mit fester, variabler und sehr großer Länge.<br />

• CHARACTER(n), CHAR(n)<br />

Hierbei handelt es sich um Zeichenketten mit fester Länge von genau n Zeichen.<br />

Für e<strong>in</strong> e<strong>in</strong>zelnes Zeichen muss die Länge (1) nicht angegeben werden.<br />

• CHARACTER VARYING(n), VARCHAR(n)<br />

Dies s<strong>in</strong>d Zeichenketten mit variabler Länge bei maximal n Zeichen.<br />

• CHARACTER LARGE OBJECT (CLOB)<br />

Hierbei handelt es sich um beliebig große Zeichenketten. Diese Variante ist<br />

relativ umständlich zu nutzen; sie wird vorwiegend für die Speicherung ganzer<br />

Textdateien verwendet.<br />

Zu allen diesen Varianten gibt es auch die Festlegung e<strong>in</strong>es nationalen Zeichensatzes<br />

durch NATIONAL CHARACTER bzw. NCHAR oder NATIONAL CHARAC-<br />

TER VARYING bzw. NVARCHAR. Erläuterungen dazu siehe unten im Abschnitt<br />

über Zeichensätze.<br />

Die maximale Länge von festen und variablen Zeichenketten hängt vom DBMS<br />

ab. In früheren Versionen betrug sie oft nur 255, heute s<strong>in</strong>d 32767 verbreitet.<br />

Die maximale Feldlänge bei Zeichenketten ist für E<strong>in</strong>gabe, Ausgabe und <strong>in</strong>terne<br />

Speicherung wichtig. Die DBMS verhalten sich unterschiedlich, ob am Anfang<br />

oder Ende stehende Leerzeichen gespeichert oder entfernt werden. Wichtig ist,<br />

dass bei fester Feldlänge e<strong>in</strong> gelesener Wert immer mit genau dieser Anzahl von<br />

Zeichen zurückgegeben wird und bei Bedarf rechts mit Leerzeichen aufgefüllt<br />

wird.<br />

In der Praxis s<strong>in</strong>d folgende Gesichtspunkte von Bedeutung:<br />

• Für <strong>in</strong>dizierte Felder (also Spalten, denen e<strong>in</strong> Index zugeordnet wird) s<strong>in</strong>d feste<br />

Längen vorzuziehen; beachten Sie aber die nächsten H<strong>in</strong>weise.<br />

• Als CHAR(n), also mit fester Länge, s<strong>in</strong>d Felder vorzusehen, deren Länge bei<br />

der überwiegenden Zahl der Datensätze konstant ist. Beispiel: die deutschen<br />

Postleitzahlen mit CHAR(5).<br />

• Als VARCHAR(n), also mit variabler Länge, s<strong>in</strong>d Felder vorzusehen, deren Länge<br />

stark variiert. Beispiel: Namen und Vornamen mit VARCHAR(30).<br />

• In Zweifelsfällen ist pragmatisch vorzugehen. Beispiel: Die <strong>in</strong>ternationalen<br />

Postleitzahlen (Post-Code, Zip-Code) benötigen bis zu 10 Zeichen. Wenn e<strong>in</strong>e<br />

Datenbank überwiegend nur deutsche Adressen enthält, passt VARCHAR(10)<br />

besser, bei hohem Anteil von britischen, US-amerikanischen, kanadischen<br />

und ähnlichen Adressen ist CHAR(10) zu empfehlen.<br />

98


13.1.2. Zahlen mit exakter Größe<br />

Vordef<strong>in</strong>ierte Datentypen<br />

Werte dieser Datentypen werden mit der genauen Größe gespeichert.<br />

• INTEGER bzw. INT<br />

Ganze Zahl mit Vorzeichen. Der Größenbereich hängt von der Implementierung<br />

ab; auf e<strong>in</strong>em 32-bit-System entspricht es meistens ±2 31 -1, genauer von<br />

–2 147 483 648 bis +2 147 483 647.<br />

• SMALLINT<br />

Ebenfalls e<strong>in</strong> Datentyp für ganze Zahlen, aber mit kle<strong>in</strong>erem Wertebereich als<br />

INTEGER, oft von -32 768 bis +32 767.<br />

• BIGINT<br />

Ebenfalls e<strong>in</strong> Datentyp für ganze Zahlen, aber mit größerem Wertebereich als<br />

INTEGER. Auch der <strong>SQL</strong>-Standard akzeptiert, dass e<strong>in</strong> DBMS diesen Typ nicht<br />

kennt.<br />

• NUMERIC(p,s) sowie DECIMAL(p,s)<br />

Datentypen für Dezimalzahlen mit exakter Speicherung, also „Festkommazahlen“,<br />

wobei p die Genauigkeit und s die Anzahl der Dezimalstellen angibt.<br />

Dabei muss 0 ≤ s ≤ p se<strong>in</strong>, und s hat e<strong>in</strong>en V<strong>org</strong>abewert von 0. Der Parameter<br />

s = 0 kann entfallen; der V<strong>org</strong>abewert für p hängt vom DBMS ab.<br />

Diese Dezimalzahlen s<strong>in</strong>d wegen der genauen Speicherung z. B. für Daten der<br />

Buchhaltung geeignet. Bei vielen DBMS gibt es ke<strong>in</strong>en Unterschied zwischen<br />

NUMERIC und DECIMAL.<br />

13.1.3. Zahlen mit „näherungsweiser“ Größe<br />

Werte dieser Datentypen werden nicht unbed<strong>in</strong>gt mit der genauen Größe gespeichert,<br />

sondern <strong>in</strong> vielen Fällen nur näherungsweise.<br />

• FLOAT, REAL, DOUBLE PRECISION<br />

Diese Datentypen haben grundsätzlich die gleiche Bedeutung. Je nach DBMS<br />

gibt FLOAT(p,s) die Genauigkeit oder die Anzahl der Dezimalstellen an; auch<br />

der Wertebereich und die Genauigkeit hängen vom DBMS ab.<br />

Diese „Gleitkommazahlen“ s<strong>in</strong>d für technisch-wissenschaftliche Werte geeignet<br />

und umfassen auch die Exponentialdarstellung. Wegen der Speicherung im B<strong>in</strong>ärformat<br />

s<strong>in</strong>d sie aber für Geldbeträge nicht geeignet, weil sich beispielsweise<br />

der Wert 0,10 =C (entspricht 10 Cent) nicht exakt abbilden lässt. Es kommt immer<br />

wieder zu Rundungsfehlern.<br />

99


Datentypen<br />

13.1.4. Zeitpunkte und Zeit<strong>in</strong>tervalle<br />

Für Datum und Uhrzeit gibt es die folgenden Datentypen:<br />

• DATE<br />

Das Datum enthält die Bestandteile YEAR, MONTH, DAY, wobei die Monate<br />

<strong>in</strong>nerhalb e<strong>in</strong>es Jahres und die Tage <strong>in</strong>nerhalb e<strong>in</strong>es Monats geme<strong>in</strong>t s<strong>in</strong>d.<br />

• TIME<br />

Die Uhrzeit enthält die Bestandteile HOUR, MINUTE, SECOND, wobei die M<strong>in</strong>ute<br />

<strong>in</strong>nerhalb e<strong>in</strong>er Stunde und die Sekunden <strong>in</strong>nerhalb e<strong>in</strong>er M<strong>in</strong>ute geme<strong>in</strong>t<br />

s<strong>in</strong>d. Sehr oft gibt es auch Millisekunden als Bruchteile von Sekunden.<br />

• TIMESTAMP<br />

Der „Zeitstempel“ enthält Datum und Uhrzeit zusammen.<br />

Zeitangaben können WITH TIME ZONE oder WITHOUT TIME ZONE deklariert<br />

werden. Ohne die Zeitzone ist <strong>in</strong> der Regel die lokale Zeit geme<strong>in</strong>t, mit der Zeitzone<br />

wird die Koord<strong>in</strong>ierte Weltzeit (UTC) gespeichert.<br />

Bei Datum und Uhrzeit enden die Geme<strong>in</strong>samkeiten der <strong>SQL</strong>-Dialekte endgültig;<br />

sie werden unterschiedlich mit „eigenen“ Datentypen realisiert. Man kann<br />

allenfalls annehmen, dass e<strong>in</strong> Tag <strong>in</strong>tern mit e<strong>in</strong>er ganzen Zahl und e<strong>in</strong> Zeitwert<br />

mit e<strong>in</strong>em Bruchteil e<strong>in</strong>er ganzen Zahl gespeichert wird.<br />

Beispiele:<br />

DBMS Datentyp Geltungsbereich Genauigkeit<br />

MS-<strong>SQL</strong> datetime 01.01.1753 bis 31.12.9999 3,33 Millisek.<br />

Server 2005 smalldatetime 01.01.1900 bis 06.06.2079 1 M<strong>in</strong>ute<br />

MS-<strong>SQL</strong> date 01.01.0001 bis 31.12.9999 1 Tag<br />

Server 2008 time 00:00:00.00 bis 23:59:59.99 100 Nanosek.<br />

datetime 01.01.0001 bis 31.12.9999 3,33 Millisek.<br />

smalldatetime 01.01.1900 bis 06.06.2079 1 M<strong>in</strong>ute<br />

Firebird DATE 01.01.0100 bis 29.02.32768 1 Tag<br />

TIME 00:00 bis 23:59.9999 6,67 Millisek.<br />

My<strong>SQL</strong> 5.x DATETIME 01.01.1000 00:00:00 bis<br />

31.12.9999 23:59:59<br />

1 Sekunde<br />

DATE 01.01.1000 bis 31.12.9999 1 Tag<br />

TIME –838:59:59 bis 838:59:59 1 Sekunde<br />

YEAR 1901 bis 2055 1 Jahr<br />

Bitte wundern Sie sich nicht: bei jedem DBMS gibt es noch weitere Datentypen<br />

und Bezeichnungen.<br />

Die Deklaration von TIME bei My<strong>SQL</strong> zeigt schon: Statt e<strong>in</strong>er Uhrzeit kann es<br />

auch e<strong>in</strong>en Zeitraum, d. h. e<strong>in</strong> Intervall darstellen.<br />

100


Konstruierte und benutzerdef<strong>in</strong>ierte Datentypen<br />

• INTERVAL<br />

E<strong>in</strong> Intervall setzt sich − je nach betrachteter Zeitdauer − zusammen aus:<br />

• YEAR, MONTH für längere Zeiträume (der <strong>SQL</strong>-Standard kennt auch nur die<br />

Bezeichnung "year-month-<strong>in</strong>terval")<br />

• DAY, HOUR, MINUTE, SECOND für Zeiträume <strong>in</strong>nerhalb e<strong>in</strong>es Tages oder<br />

über mehrere Tage h<strong>in</strong>weg<br />

13.1.5. Große Objekte<br />

BLOB (B<strong>in</strong>ary Large Object, b<strong>in</strong>äre große Objekte) ist die allgeme<strong>in</strong>e Bezeichnung<br />

für unbestimmt große Objekte.<br />

• BLOB<br />

Allgeme<strong>in</strong> werden b<strong>in</strong>äre Objekte z. B. für Bilder oder Bilddateien verwendet,<br />

nämlich dann, wenn der Inhalt nicht näher strukturiert ist und auch Bytes enthalten<br />

kann, die ke<strong>in</strong>e Zeichen s<strong>in</strong>d.<br />

• CLOB<br />

Speziell werden solche Objekte, die nur „echte“ Zeichen enthalten, zum Speichern<br />

von großen Texten oder Textdateien verwendet.<br />

Je nach DBMS werden BLOB-Varianten durch Sub_Type oder spezielle Datentypen<br />

für unterschiedliche Maximalgrößen oder Verwendung gekennzeichnet.<br />

13.1.6. Boolean<br />

Der Datentyp BOOLEAN ist für logische Werte v<strong>org</strong>esehen. Solche Felder können<br />

die Werte TRUE (wahr) und FALSE (falsch) annehmen; auch NULL ist möglich<br />

und wird als UNKNOWN (unbekannt) <strong>in</strong>terpretiert.<br />

Wenn e<strong>in</strong> DBMS diesen Datentyp (noch) nicht kennt − wie My<strong>SQL</strong> −, dann ist<br />

mit e<strong>in</strong>em der numerischen Typen e<strong>in</strong>e e<strong>in</strong>fache Ersatzlösung möglich (wie früher<br />

bei Interbase und Firebird); siehe unten im Abschnitt über Doma<strong>in</strong>s.<br />

13.2. Konstruierte und benutzerdef<strong>in</strong>ierte<br />

Datentypen<br />

Diese Datentypen, die aus den vordef<strong>in</strong>ierten Datentypen zusammengesetzt<br />

werden, werden hier nur der Vollständigkeit halber erwähnt; sie s<strong>in</strong>d <strong>in</strong> der Praxis<br />

e<strong>in</strong>es Anwenders ziemlich unwichtig.<br />

101


Datentypen<br />

• ROW<br />

E<strong>in</strong>e Zeile ist e<strong>in</strong>e Sammlung von Feldern; jedes Feld besteht aus dem Namen<br />

und dem Datentyp. Nun ja, e<strong>in</strong>e Zeile <strong>in</strong> e<strong>in</strong>er Tabelle ist (natürlich) von diesem<br />

Typ.<br />

• REF<br />

Referenztypen s<strong>in</strong>d zwar im <strong>SQL</strong>-Standard v<strong>org</strong>esehen, treten aber <strong>in</strong> der Praxis<br />

nicht auf.<br />

• ARRAY, MULTISET<br />

Felder und Mengen s<strong>in</strong>d Typen von Sammlungen ("collection type"), <strong>in</strong> denen<br />

jedes Element vom gleichen Datentyp ist. E<strong>in</strong> Array gibt die Elemente durch<br />

die Position an, e<strong>in</strong> Multiset ist e<strong>in</strong>e ungeordnete Menge. Wegen der Notwendigkeit,<br />

Tabellen zu normalisieren, s<strong>in</strong>d diese Typen <strong>in</strong> der Praxis unwichtig.<br />

• Benutzerdef<strong>in</strong>ierte Typen<br />

Dazu gehören nicht nur e<strong>in</strong> Typ, sondern auch Methoden zu ihrer Verwendung.<br />

Auch für solche Typen s<strong>in</strong>d ke<strong>in</strong>e s<strong>in</strong>nvollen Anwendungen zu f<strong>in</strong>den.<br />

13.3. Spezialisierte Datentypen<br />

Datentypen können mit E<strong>in</strong>schränkungen, also CONSTRAINTs versehen werden;<br />

auch der Begriff Doma<strong>in</strong> wird verwendet. (E<strong>in</strong>en vernünftigen deutschen<br />

Begriff gibt es dafür nicht.) Der <strong>SQL</strong>-Standard macht nicht viele V<strong>org</strong>aben.<br />

Der Befehl zum Erstellen e<strong>in</strong>er Doma<strong>in</strong> sieht allgeme<strong>in</strong> so aus:<br />

CREATE DOMAIN [] []<br />

Unter Interbase/Firebird wurde auf diese Weise e<strong>in</strong> Ersatz für den BOOLEAN-<br />

Datentyp erzeugt; weitere Beispiele stehen bei der Def<strong>in</strong>ition von CHECK 2 .<br />

Firebird-Quelltext<br />

CREATE DOMAIN BOOLEAN<br />

-- def<strong>in</strong>iere diesen Datentyp<br />

AS INT<br />

-- als Integer<br />

DEFAULT 0 NOT NULL<br />

-- V<strong>org</strong>abewert 0, hier ohne NULL-Werte<br />

CHECK (VALUE BETWEEN 0 AND 1);<br />

-- Werte können nur 0 (= false) und 1 (= true) se<strong>in</strong><br />

Bei My<strong>SQL</strong> können Spalten mit ENUM oder SET auf bestimmte Werte e<strong>in</strong>geschränkt<br />

werden, allerd<strong>in</strong>gs nur auf Zeichen.<br />

2 Abschnitt 28.4.5 auf Seite 299<br />

102


Nationale und <strong>in</strong>ternationale Zeichensätze<br />

13.4. Nationale und <strong>in</strong>ternationale Zeichensätze<br />

Aus der Frühzeit der EDV ist das Problem der nationalen Zeichen geblieben: Mit<br />

1 Byte (= 8 Bit) können höchstens 256 Zeichen (abzüglich 32 Steuerzeichen, Ziffern<br />

und e<strong>in</strong>er Reihe von Satz- und Sonderzeichen) dargestellt werden; und das<br />

reicht nicht e<strong>in</strong>mal für die Akzentbuchstaben (Umlaute) aller westeuropäischen<br />

Sprachen. Erst mit Unicode gibt es e<strong>in</strong>en Standard, der weltweit alle Zeichen<br />

(z. B. auch die ch<strong>in</strong>esischen Zeichen) darstellen und speichern soll.<br />

Da ältere EDV-Systeme (Computer, Programme, Programmiersprachen, Datenbanken)<br />

weiterh<strong>in</strong> benutzt werden, muss die Verwendung nationaler Zeichensätze<br />

nach wie vor berücksichtigt werden. Dafür gibt es verschiedene Maßnahmen<br />

− jedes DBMS folgt eigenen Regeln für CHARACTER SET (Zeichensatz) und<br />

COLLATE (alphabetische Sortierung) und benutzt eigene Bezeichner für die Zeichensätze<br />

und die Regeln der Reihenfolge.<br />

Vor allem wegen der DBMS-spezifischen Bezeichnungen kommen Sie nicht um<br />

<strong>in</strong>tensive Blicke <strong>in</strong> die jeweilige Dokumentation herum.<br />

13.4.1. Zeichensatz festlegen mit CHARACTER SET / CHARSET<br />

Wenn e<strong>in</strong>e Datenbank erstellt wird, muss der Zeichensatz festgelegt werden.<br />

Ohne ausdrückliche Festlegung regelt jedes DBMS selbst je nach Version, welcher<br />

Zeichensatz als Standard gelten soll. Wenn e<strong>in</strong> Programm Zugriff auf e<strong>in</strong>e<br />

vorhandene Datenbank nimmt, muss ebenso der Zeichensatz angegeben werden;<br />

dieser muss mit dem ursprünglich festgelegten übere<strong>in</strong>stimmen. Wenn<br />

Umlaute falsch angezeigt werden, dann stimmen <strong>in</strong> der Regel diese Angaben<br />

nicht.<br />

E<strong>in</strong>e neue Datenbank sollte, wenn das DBMS und die Programmiersprache dies<br />

unterstützen, möglichst mit Unicode (<strong>in</strong> der Regel als UTF8) angelegt werden.<br />

In neueren Versionen steht die Bezeichnung NCHAR (= NATIONAL CHAR) oft<br />

nicht für e<strong>in</strong>en speziellen nationalen Zeichensatz, sondern für den allgeme<strong>in</strong>en<br />

Unicode-Zeichensatz. Bei CHAR bzw. VARCHAR wird e<strong>in</strong> spezieller Zeichensatz<br />

verwendet, abhängig von der Installation oder der Datenbank.<br />

In diesem Abschnitt kann deshalb nur beispielhaft gezeigt werden, wie Zeichensätze<br />

behandelt werden.<br />

Firebird, Interbase; My<strong>SQL</strong><br />

CHARACTER SET beschreibt den Zeichensatz e<strong>in</strong>er Datenbank. Dieser gilt als<br />

V<strong>org</strong>abewert für alle Zeichenketten: CHAR(n), VARCHAR(n), CLOB. Für e<strong>in</strong>e e<strong>in</strong>zelne<br />

Spalte kann e<strong>in</strong> abweichender Zeichensatz festgelegt werden. Beispiel:<br />

103


Datentypen<br />

Firebird-Quelltext<br />

CREATE DATABASE ’europe.fb’ DEFAULT CHARACTER SET ISO8859_1;<br />

ALTER TABLE xyz ADD COLUMN lname VARCHAR(30) NOT NULL CHARACTER SET CYRL;<br />

Es kommt auch vor, dass e<strong>in</strong> Programm mit e<strong>in</strong>em anderen Zeichensatz arbeitet<br />

als die Datenbank. Dann können die Zeichensätze angepasst werden:<br />

Firebird-Quelltext<br />

SET NAMES DOS437;<br />

CONNECT ’europe.fb’ USER ’JAMES’ PASSWORD ’U4EEAH’;<br />

-- die Datenbank selbst arbeitet mit ISO8859_1, das Programm mit DOS-Codepage 437<br />

MS-<strong>SQL</strong><br />

Die Dokumentation geht nur allgeme<strong>in</strong> auf „Nicht-Unicode-Zeichendaten“ e<strong>in</strong>.<br />

Es gibt ke<strong>in</strong>erlei Erläuterung, wie e<strong>in</strong> solcher Zeichensatz festgelegt wird.<br />

13.4.2. Sortierungen mit COLLATE<br />

COLLATE legt fest, nach welchen Regeln die Reihenfolge von Zeichenketten<br />

(englisch: Collation Order) bestimmt wird. Der V<strong>org</strong>abewert für die Datenbank<br />

bzw. Tabelle hängt direkt vom Zeichensatz ab. Abweichende Regeln können getrennt<br />

gesteuert werden für Spalten, Vergleiche sowie Festlegungen bei ORDER<br />

BY und GROUP BY.<br />

Firebird-Quelltext<br />

CREATE DATABASE ’europe.fb’ DEFAULT CHARACTER SET ISO8859_1;<br />

-- dies legt automatisch die Sortierung nach ISO8859_1 fest<br />

ALTER TABLE xyz ADD COLUMN lname VARCHAR(30) NOT NULL COLLATE FR_CA;<br />

-- dies legt die Sortierung auf kanadisches Französisch fest<br />

SELECT ... WHERE lname COLLATE FR_FR


13.5. Zusammenfassung<br />

In diesem Kapitel lernten Sie die Datentypen unter <strong>SQL</strong> kennen:<br />

Zusammenfassung<br />

• Bei Zeichenketten ist zwischen fester und variabler Länge zu unterscheiden<br />

und der Zeichensatz − UNICODE oder national − zu beachten.<br />

• Für Zahlen ist zwischen exakter und näherungsweiser Speicherung zu unterscheiden<br />

und die Genauigkeit zu beachten.<br />

• Für Datums- und Zeitwerte ist vor allem auf den jeweiligen Geltungsbereich<br />

und die Genauigkeit zu achten.<br />

• Für spezielle Zwecke gibt es weitere Datentypen wie BLOB oder BOOLEAN.<br />

13.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 106.<br />

Zahlen und Datumsangaben verwenden immer die im deutschsprachigen Raum<br />

üblichen Schreibweisen. Zeichen werden mit Hochkommata begrenzt.<br />

Übung 1 – Texte und Zahlen<br />

Geben Sie zu den Werten jeweils an, welche Datentypen passen, welche fraglich<br />

s<strong>in</strong>d (also u. U. möglich, aber nicht s<strong>in</strong>nvoll oder unklar) und welche falsch s<strong>in</strong>d.<br />

1. 'A' als Char, Char(20), Varchar, Varchar(20)<br />

2. der Ortsname 'Bietigheim-Biss<strong>in</strong>gen' als Char, Char(20), Varchar, Varchar(20)<br />

3. das Wort 'Übungen' als Varchar(20), NVarchar(20)<br />

4. 123.456 als Integer, Small<strong>in</strong>t, Float, Numeric, Varchar(20)<br />

5. 123,456 als Integer, Small<strong>in</strong>t, Float, Numeric, Varchar(20)<br />

6. 789,12 [ =C] als Integer, Small<strong>in</strong>t, Float, Numeric, Varchar(20)<br />

Übung 2 – Datum und Zeit<br />

Geben Sie jeweils an, welche Datentypen passen, welche fraglich s<strong>in</strong>d (also u. U.<br />

möglich, aber nicht s<strong>in</strong>nvoll oder unklar) und welche falsch s<strong>in</strong>d.<br />

1. '27.11.2009' als Date, Time, Timestamp, Char(10), Varchar(20)<br />

2. '11:42:53' als Date, Time, Timestamp, Char(10), Varchar(20)<br />

3. '27.11.2009 11:42:53' als Date, Time, Timestamp, Char(10), Varchar(20)<br />

4. 'November 2009' als Date, Time, Timestamp, Char(10), Varchar(20)<br />

105


Datentypen<br />

Übung 3 – Personen<br />

Bereiten Sie e<strong>in</strong>e Tabelle Person vor: Notieren Sie mit möglichst konsequenter<br />

Aufteilung der Bestandteile die möglichen Spaltennamen und deren Datentypen;<br />

berücksichtigen Sie dabei auch <strong>in</strong>ternationale Adressen, aber ke<strong>in</strong>e Kontaktdaten<br />

(wie Telefon).<br />

Übung 4 – Buchhaltung<br />

Bereiten Sie e<strong>in</strong>e Tabelle Kassenbuch vor: Notieren Sie die möglichen Spaltennamen<br />

und deren Datentypen; berücksichtigen Sie dabei auch, dass Angaben der<br />

Buchhaltung geprüft werden müssen.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 105.<br />

Lösung zu Übung 1 – Texte und Zahlen<br />

Die „richtige“ Festlegung hängt vom Zusammenhang <strong>in</strong>nerhalb e<strong>in</strong>er Tabelle ab.<br />

1. Char und Varchar(20) passen, Varchar ist nicht s<strong>in</strong>nvoll, Char(20) ist falsch.<br />

2. Char(20) und Varchar(20) passen, Char und Varchar s<strong>in</strong>d falsch.<br />

3. Je nach verwendetem Zeichensatz können beide Varianten richtig, ungeeignet<br />

oder falsch se<strong>in</strong>.<br />

4. Integer und Numeric s<strong>in</strong>d richtig, Float ist möglich, Small<strong>in</strong>t ist falsch, Varchar(20)<br />

ist nicht ganz ausgeschlossen.<br />

5. Float und Numeric s<strong>in</strong>d richtig, Integer und Small<strong>in</strong>t s<strong>in</strong>d falsch, Varchar(20)<br />

ist nicht ganz ausgeschlossen.<br />

6. Numeric ist richtig, Float ist möglich, Integer und Small<strong>in</strong>t s<strong>in</strong>d falsch, Varchar(20)<br />

ist nicht ganz ausgeschlossen.<br />

Lösung zu Übung 2 – Datum und Zeit<br />

106<br />

1. Date und Timestamp s<strong>in</strong>d richtig, Char(10) und Varchar(20) s<strong>in</strong>d möglich,<br />

Time ist falsch.<br />

2. Time und Timestamp s<strong>in</strong>d richtig, Varchar(20) ist möglich, Char(10) und<br />

Date s<strong>in</strong>d falsch.<br />

3. Timestamp ist richtig, Varchar(20) ist möglich, Date, Time und Char(10)<br />

s<strong>in</strong>d falsch.<br />

4. Varchar(20) ist richtig, alles andere falsch.


Lösung zu Übung 3 – Personen<br />

Übungen<br />

Bitte wundern Sie sich nicht über unerwartete Unterteilungen: Bei der folgenden<br />

Lösung werden auch Erkenntnisse der Datenbank-Theorie und der praktischen<br />

Arbeit mit Datenbanken berücksichtigt.<br />

• ID Integer<br />

• Titel Varchar(15)<br />

• Vorname Varchar(30)<br />

• Adelszusatz Varchar(15) − e<strong>in</strong>e getrennte Spalte ist wegen der alphabetischen<br />

Sortierung s<strong>in</strong>nvoll<br />

• Name Varchar(30)<br />

• Adresszusatz Varchar(30)<br />

• Strasse Varchar(24)<br />

• Hausnr Integer (oder Small<strong>in</strong>t)<br />

• HausnrZusatz Varchar(10) − Trennung ist wegen der numerischen Sortierung<br />

s<strong>in</strong>nvoll<br />

• Länderkennung Char(2) − nach ISO 3166, auch Char(3) möglich, Integer oder<br />

Small<strong>in</strong>t denkbar; der Ländername ist auf jeden Fall unpraktisch und allenfalls<br />

als zusätzliche Spalte s<strong>in</strong>nvoll<br />

• PLZ Char(10) oder Varchar(10) − <strong>in</strong>ternational s<strong>in</strong>d bis zu 10 Zeichen möglich<br />

• Geburtsdatum Date<br />

Lösung zu Übung 4 – Buchhaltung<br />

• ID Integer<br />

• Buchungsjahr Integer oder Small<strong>in</strong>t<br />

• Buchungsnummer Integer<br />

• Buchungsterm<strong>in</strong> Timestamp − je nach Arbeitsweise genügt auch Date<br />

• Betrag Numeric oder Decimal<br />

• V<strong>org</strong>ang Varchar(50) − als Beschreibung der Buchung<br />

• Bearbeiter Varchar(30) − derjenige, der den Kassenbestand ändert; auch<br />

Bearbeiter_ID Integer mit Fremdschlüssel auf e<strong>in</strong>e Tabelle Mitarbeiter ist s<strong>in</strong>nvoll<br />

• Nutzer Varchar(30) − derjenige, der die Buchung registriert; auch Nutzer_ID<br />

Integer ist s<strong>in</strong>nvoll<br />

• Buchhaltung Timestamp − Term<strong>in</strong>, zu dem die Buchung registriert wird<br />

Wenn das Kassenbuch explizit e<strong>in</strong> Teil der Buchhaltung ist, werden auch Spalten<br />

wie Buchungskonto (Haupt- und Gegenkonten) benötigt.<br />

107


Datentypen<br />

13.7. Siehe auch<br />

In Wikipedia gibt es zusätzliche H<strong>in</strong>weise:<br />

• Gleitkommazahlen 3 mit ausführlicher Erläuterung ihrer Ungenauigkeiten<br />

• Koord<strong>in</strong>ierte Weltzeit (UTC) 4<br />

• Unicode 5 als umfassender Zeichensatz<br />

• ISO 3166 6 − Postleitzahl 7 − Liste der Postleitsysteme 8<br />

3 http://de.wikipedia.<strong>org</strong>/wiki/Gleitkommazahlen<br />

4 http://de.wikipedia.<strong>org</strong>/wiki/Koord<strong>in</strong>ierte%20Weltzeit<br />

5 http://de.wikipedia.<strong>org</strong>/wiki/Unicode<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/ISO%203166<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Postleitzahl<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Liste%20der%20Postleitsysteme<br />

108


14. Funktionen<br />

14.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

14.2. Funktionen für Zahlen . . . . . . . . . . . . . . . . . . . . . . . . 110<br />

14.3. Funktionen für Zeichenketten . . . . . . . . . . . . . . . . . . . 113<br />

14.4. Funktionen für Datums- und Zeitwerte . . . . . . . . . . . . . . 115<br />

14.5. Funktionen für logische und NULL-Werte . . . . . . . . . . . . 116<br />

14.6. Konvertierungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117<br />

14.7. Spaltenfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . 120<br />

14.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

14.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123<br />

14.10. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126<br />

Im <strong>SQL</strong>-Standard werden e<strong>in</strong>ige wenige Funktionen festgelegt, die <strong>in</strong> jedem <strong>SQL</strong>-<br />

Dialekt vorkommen. In aller Regel ergänzt jedes DBMS diese Funktionen durch<br />

weitere eigene. E<strong>in</strong>e ganze Reihe davon kann als Standard angesehen werden,<br />

weil sie immer (oder fast immer) vorhanden s<strong>in</strong>d. An vielen Stellen ist aber auf<br />

Unterschiede im Namen oder <strong>in</strong> der Art des Aufrufs h<strong>in</strong>zuweisen.<br />

• Skalarfunktionen verarbeiten Werte oder Ausdrücke aus e<strong>in</strong>zelnen Zahlen,<br />

Zeichenketten oder Datums- und Zeitwerten. Sofern die Werte aus e<strong>in</strong>er Spalte<br />

geholt werden, handelt es sich immer um e<strong>in</strong>en Wert aus e<strong>in</strong>er bestimmten<br />

Zeile. – Das Ergebnis e<strong>in</strong>er solchen Funktion wird auch als Rückgabewert bezeichnet.<br />

• Spaltenfunktionen verarbeiten alle Werte aus e<strong>in</strong>er Spalte.<br />

• Ergänzend kann man für beide Varianten auch benutzerdef<strong>in</strong>ierte Funktionen<br />

erstellen; dies wird unter Programmierung 1 angesprochen.<br />

Dieses Kapitel enthält als Grundlage nur die wichtigsten Skalar- und Spaltenfunktionen.<br />

Unter Funktionen (2) 2 gibt es viele Ergänzungen.<br />

1 Kapitel 30 auf Seite 323<br />

2 Kapitel 16 auf Seite 141<br />

109


Funktionen<br />

14.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise<br />

Die Funktionen können überall dort verwendet werden, wo e<strong>in</strong> <strong>SQL</strong>-Ausdruck<br />

möglich ist. Wichtig ist, dass das Ergebnis der Funktion zu dem Datentyp passt,<br />

der an der betreffenden Stelle erwartet wird.<br />

Firebird kennt ab Version 2.1 viele Funktionen, die vorher als benutzerdef<strong>in</strong>ierte<br />

Funktionen (user def<strong>in</strong>ed functions, UDF) erstellt werden mussten.<br />

Schreibweise für Funktionen: Bei Funktionen müssen die Parameter (auch „Argumente“<br />

genannt) immer <strong>in</strong> Klammern gesetzt werden. Diese Klammern s<strong>in</strong>d<br />

auch bei e<strong>in</strong>er Funktion mit konstantem Wert wie PI erforderlich. E<strong>in</strong>e Ausnahme<br />

s<strong>in</strong>d lediglich die Systemfunktionen CURRENT_DATE u. ä.<br />

Schreibweise der Beispiele: Aus Platzgründen werden Beispiele meistens nur<br />

e<strong>in</strong>fach <strong>in</strong> e<strong>in</strong>en Rahmen gesetzt. Für Funktionen ohne Bezug auf Tabellen und<br />

Spalten genügt <strong>in</strong> der Regel e<strong>in</strong> e<strong>in</strong>facher SELECT-Befehl; <strong>in</strong> manchen Fällen<br />

muss e<strong>in</strong>e Tabelle oder e<strong>in</strong>e fiktive Quelle angegeben werden:<br />

SELECT 2 * 3; /* Normalfall */<br />

SELECT 2 * 3 FROM rdb$database; /* bei Firebird und Interbase */<br />

SELECT 2 * 3 FROM dual; /* bei Oracle */<br />

In diesem Kapitel und auch im zweiten Kapitel zu Funktionen werden diese Verfahren<br />

durch e<strong>in</strong>e verkürzte Schreibweise zusammengefasst:<br />

SELECT 2 * 3 [from fiktiv];<br />

Das ist so zu lesen: Die FROM-Klausel ist für Firebird, Interbase und Oracle notwendig<br />

und muss die jeweils benötigte Tabelle angeben; bei allen anderen DBMS<br />

muss sie entfallen.<br />

14.2. Funktionen für Zahlen<br />

Bei allen numerischen Funktionen müssen Sie auf den genauen Typ achten. Es<br />

ist beispielsweise e<strong>in</strong> Unterschied, ob das Ergebnis e<strong>in</strong>er Division zweier ganzer<br />

Zahlen als ganze Zahl oder als Dezimalzahl behandelt werden soll.<br />

14.2.1. Operatoren<br />

Es gibt die üblichen Operatoren für die Grundrechenarten:<br />

110


+ Addition<br />

- Subtraktion, Negation<br />

* Multiplikation<br />

/ Division|(<br />

Funktionen für Zahlen<br />

Dafür gelten die üblichen mathematischen Regeln (Punkt vor Strich, Klammern<br />

zuerst). Bitte beachten Sie auch folgende Besonderheit:<br />

• Bei der Division ganzer Zahlen ist auch das Ergebnis e<strong>in</strong>e ganze Zahl; man<br />

nennt das „Integer-Division“:<br />

SELECT 8 / 5 [from fiktiv]; /* Ergebnis: 1 */<br />

• Wenn Sie das Ergebnis als Dezimalzahl haben wollen, müssen Sie (m<strong>in</strong>destens)<br />

e<strong>in</strong>e der beiden Zahlen als Dezimalzahl v<strong>org</strong>eben:<br />

SELECT 8.0 / 5 [from fiktiv]; /* Ergebnis: 1.6 */<br />

• Ausnahme: My<strong>SQL</strong> liefert auch bei 8/5 e<strong>in</strong>e Dezimalzahl. Für die „Integer-<br />

Division“ gibt es die DIV-Funktion:<br />

SELECT 8 DIV 5; /* Ergebnis: 1 */<br />

Division durch 0 liefert zunächst e<strong>in</strong>e Fehlermeldung "division by zero has occurred"<br />

und danach das Ergebnis NULL.<br />

14.2.2. MOD – der Rest e<strong>in</strong>er Division<br />

Die Modulo-Funktion MOD bestimmt den Rest bei e<strong>in</strong>er Division ganzer Zahlen:<br />

MOD( , ) /* allgeme<strong>in</strong> */<br />

% /* bei MS-<strong>SQL</strong> und My<strong>SQL</strong> */<br />

Beispiele:<br />

SELECT MOD(7, 3) [from fiktiv]; /* Ergebnis: 1 */<br />

SELECT ID FROM Mitarbeiter WHERE MOD(ID, 10) = 0; /* listet IDs mit ’0’ am Ende ’/<br />

111


Funktionen<br />

14.2.3. CEILING, FLOOR, ROUND, TRUNCATE – die nächste<br />

ganze Zahl<br />

Es gibt mehrere Möglichkeiten, zu e<strong>in</strong>er Dezimalzahl die nächste ganze Zahl zu<br />

bestimmen.<br />

CEILING oder CEIL liefert die nächstgrößere ganze Zahl, genauer: die kle<strong>in</strong>ste<br />

Zahl, die größer oder gleich der gegebenen Zahl ist.<br />

SELECT CEILING(7.3), CEILING(-7.3) [from fiktiv]; /* Ergebnis: 8, -7 */<br />

FLOOR ist das Gegenstück dazu und liefert die nächstkle<strong>in</strong>ere ganze Zahl, genauer:<br />

die größte Zahl, die kle<strong>in</strong>er oder gleich der gegebenen Zahl ist.<br />

SELECT FLOOR(7.3), FLOOR(-7.3) [from fiktiv]; /* Ergebnis: 7, -8 */<br />

TRUNCATE oder TRUNC schneidet den Dezimalanteil ab.<br />

SELECT TRUNCATE(7.3),TRUNCATE(-7.3) [from fiktiv]; /* Ergebnis: 7, -7 */<br />

SELECT TRUNCATE(Schadenshoehe) FROM Schadensfall; /* Euro-Werte ohne Cent */<br />

ROUND liefert e<strong>in</strong>e mathematische Rundung: ab 5 wird aufgerundet, darunter<br />

wird abgerundet.<br />

ROUND( [ , ] )<br />

ist e<strong>in</strong>e beliebige Zahl oder e<strong>in</strong> Ausdruck, der e<strong>in</strong>e beliebige Zahl<br />

liefert.<br />

• Wenn nicht angegeben ist, wird 0 angenommen.<br />

• Bei e<strong>in</strong>em positiven Wert für wird auf entsprechend viele Dezimalstellen<br />

gerundet.<br />

• Bei e<strong>in</strong>em negativen Wert für wird l<strong>in</strong>ks vom Dezimaltrenner<br />

auf entsprechend viele Nullen gerundet.<br />

Beispiele:<br />

SELECT ROUND(12.248,-2) [from fiktiv]; /* Ergebnis: 0,000 */<br />

SELECT ROUND(12.248,-1) [from fiktiv]; /* Ergebnis: 10,000 */<br />

SELECT ROUND(12.248, 0) [from fiktiv]; /* Ergebnis: 12,000 */<br />

SELECT ROUND(12.248, 1) [from fiktiv]; /* Ergebnis: 12,200 */<br />

SELECT ROUND(12.248, 2) [from fiktiv]; /* Ergebnis: 12,250 */<br />

SELECT ROUND(12.248, 3) [from fiktiv]; /* Ergebnis: 12,248 */<br />

SELECT ROUND(12.25, 1) [from fiktiv]; /* Ergebnis: 12,300 */<br />

SELECT ROUND(Schadenshoehe) FROM Schadensfall; /* Euro-Werte gerundet */<br />

112


14.3. Funktionen für Zeichenketten<br />

Funktionen für Zeichenketten<br />

Zur Bearbeitung und Prüfung von Zeichenketten (Str<strong>in</strong>gs) werden viele Funktionen<br />

angeboten.<br />

14.3.1. Verknüpfen von Str<strong>in</strong>gs<br />

Als Operatoren, um mehrere Zeichenketten zu verb<strong>in</strong>den, stehen zur Verfügung:<br />

|| als <strong>SQL</strong>-Standard<br />

+ für MS-<strong>SQL</strong> oder My<strong>SQL</strong><br />

CONCAT für My<strong>SQL</strong> oder Oracle<br />

Der senkrechte Strich wird als „Verkettungszeichen“ bezeichnet und oft „Pipe“-<br />

Zeichen genannt. Es wird auf der deutschen PC-Tastatur unter W<strong>in</strong>dows durch<br />

die Tastenkomb<strong>in</strong>ation Alt Gr + < > erzeugt.<br />

E<strong>in</strong> Beispiel <strong>in</strong> allen diesen Varianten:<br />

SELECT Name || ’, ’ || Vorname from Mitarbeiter;<br />

SELECT Name + ’, ’ + Vorname from Mitarbeiter;<br />

SELECT CONCAT(Name, ’, ’, Vorname) from Mitarbeiter;<br />

Jede Variante liefert das gleiche Ergebnis: Für jeden Datensatz der Tabelle Mitarbeiter<br />

werden Name und Vorname verbunden und dazwischen e<strong>in</strong> weiterer<br />

Str<strong>in</strong>g gesetzt, bestehend aus Komma und e<strong>in</strong>em Leerzeichen.<br />

14.3.2. Länge von Str<strong>in</strong>gs<br />

Um die Länge e<strong>in</strong>er Zeichenkette zu ermitteln, gibt es folgende Funktionen:<br />

CHARACTER_LENGTH( ) <strong>SQL</strong>-Standard<br />

CHAR_LENGTH( ) <strong>SQL</strong>-Standard Kurzfassung<br />

LEN( ) nur für MS-<strong>SQL</strong><br />

Beispiele:<br />

SELECT CHAR_LENGTH(’Hello World’) [from fiktiv]; /* Ergebnis: 11 */<br />

SELECT CHAR_LENGTH(”) [from fiktiv]; /* Ergebnis: 0 */<br />

SELECT CHAR_LENGTH( NULL ) [from fiktiv]; /* Ergebnis: */<br />

SELECT Name FROM Mitarbeiter ORDER BY CHAR_LENGTH(Name) DESC;<br />

/* liefert die Namen der Mitarbeiter, absteigend sortiert nach Länge */<br />

113


Funktionen<br />

14.3.3. UPPER, LOWER – Groß- und Kle<strong>in</strong>buchstaben<br />

UPPER konvertiert den gegebenen Str<strong>in</strong>g zu Großbuchstaben; LOWER gibt<br />

e<strong>in</strong>en Str<strong>in</strong>g zurück, der nur aus Kle<strong>in</strong>buchstaben besteht.<br />

SELECT UPPER(’Abc Äöü Xyzß ÀÉÇ àéç’) [from fiktiv];<br />

/* Ergebnis: ’ABC ÄÖÜ XYZß ÀÉÇ ÀÉÇ’ */<br />

SELECT LOWER(’Abc Äöü Xyzß ÀÉÇ àéç’) [from fiktiv];<br />

/* Ergebnis: ’abc äöü xyzß àéç àéç’ */<br />

SELECT UPPER(Kuerzel), Bezeichnung FROM Abteilung;<br />

/* Kurzbezeichnungen <strong>in</strong> Großbuchstaben */<br />

Ob die Konvertierung bei Umlauten richtig funktioniert, hängt vom verwendeten<br />

Zeichensatz ab.<br />

14.3.4. SUBSTRING – Teile von Zeichenketten<br />

SUBSTRING ist der <strong>SQL</strong>-Standard, um aus e<strong>in</strong>em Str<strong>in</strong>g e<strong>in</strong>en Teil zu holen:<br />

SUBSTRING( FROM FOR ) /* <strong>SQL</strong>-Standard */<br />

SUBSTRING( , , ) /* MS-<strong>SQL</strong>, My<strong>SQL</strong>, Oracle */<br />

Diese Funktion heißt unter Oracle SUBSTR und kann auch bei My<strong>SQL</strong> so bezeichnet<br />

werden.<br />

Der Ausgangstext wird von Position 1 an gezählt. Der Teilstr<strong>in</strong>g beg<strong>in</strong>nt an der<br />

h<strong>in</strong>ter FROM genannten Position und übernimmt so viele Zeichen wie h<strong>in</strong>ter<br />

FOR angegeben ist:<br />

SELECT SUBSTRING(’Abc Def Ghi’ FROM 6 FOR 4) [from fiktiv]; /* Ergebnis: ’ef G’ */<br />

SELECT CONCAT(Name, ’, ’, SUBSTRING(Vorname FROM 1 FOR 1), ’.’) FROM Mitarbeiter;<br />

/* liefert den Namen und vom Vornamen den Anfangsbuchstaben */<br />

Wenn der -Parameter fehlt, wird alles bis zum Ende von übernommen:<br />

SELECT SUBSTRING(’Abc Def Ghi’ FROM 6) [from fiktiv]; /* Ergebnis: ’ef Ghi’ */<br />

Wenn der -Parameter 0 lautet, werden 0 Zeichen übernommen, man<br />

erhält also e<strong>in</strong>e leere Zeichenkette.<br />

My<strong>SQL</strong> bietet noch e<strong>in</strong>e Reihe weiterer Varianten.<br />

114


Funktionen für Datums- und Zeitwerte<br />

H<strong>in</strong>weis: Nach <strong>SQL</strong>-Standard liefert das Ergebnis von SUBSTRING seltsamerweise<br />

e<strong>in</strong>en Text von gleicher Länge wie der ursprüngliche Text; die jetzt zwangsläufig<br />

folgenden Leerzeichen müssen ggf. mit der TRIM-Funktion (im zweiten<br />

Kapitel über Funktionen 3 ) entfernt werden.<br />

14.4. Funktionen für Datums- und Zeitwerte<br />

Bei den Datums- und Zeitfunktionen gilt das gleiche wie für Datum und Zeit als<br />

Datentyp: Jeder <strong>SQL</strong>-Dialekt hat sich se<strong>in</strong>en eigenen „Standard“ ausgedacht. Wir<br />

beschränken uns deshalb auf die wichtigsten Funktionen, die es so ähnlich „immer“<br />

gibt, und verweisen auf die DBMS-Dokumentationen.<br />

Vor allem My<strong>SQL</strong> bietet viele zusätzliche Funktionen an. Teilweise s<strong>in</strong>d es nur<br />

verkürzte und spezialisierte Schreibweisen der Standardfunktionen, teilweise<br />

liefern sie zusätzliche Möglichkeiten.<br />

14.4.1. Systemdatum und -uhrzeit<br />

Nach <strong>SQL</strong>-Standard werden aktuelle Uhrzeit und Datum abgefragt:<br />

CURRENT_DATE<br />

CURRENT_TIME<br />

CURRENT_TIMESTAMP<br />

In Klammern kann als die Anzahl der Dezimalstellen bei den Sekunden<br />

angegeben werden.<br />

Beispiele:<br />

SELECT CURRENT_TIMESTAMP [from fiktiv]; /* Ergebnis: ’19.09.2009 13:47:49’ */<br />

UPDATE Versicherungsvertrag SET Aenderungsdatum = CURRENT_DATE WHERE irgendetwas;<br />

Bei MS-<strong>SQL</strong> gibt es nur CURRENT_TIMESTAMP als Standardfunktion, dafür<br />

aber andere Funktionen mit höherer Genauigkeit.<br />

14.4.2. Teile von Datum oder Uhrzeit bestimmen<br />

Für diesen Zweck gibt es vor allem EXTRACT als Standardfunktion:<br />

3 Kapitel 16 auf Seite 141<br />

115


Funktionen<br />

EXTRACT ( FROM )<br />

ist der Wert des betreffenden Datums und/oder der Uhrzeit, die aufgeteilt<br />

werden soll. Als wird der gewünschte Teil des Datums angegeben:<br />

YEAR | MONTH | DAY | HOUR | MINUTE | SECOND | MILLISECOND<br />

Bei e<strong>in</strong>em DATE-Feld ohne Uhrzeit s<strong>in</strong>d HOUR usw. natürlich unzulässig, bei<br />

e<strong>in</strong>em TIME-Feld nur mit Uhrzeit die Bestandteile YEAR usw. Beispiele:<br />

SELECT ID, Datum, EXTRACT(YEAR FROM Datum) AS Jahr, EXTRACT(MONTH FROM Datum) AS Monat<br />

FROM Schadensfall ORDER BY Jahr, Monat;<br />

SELECT ’Stunde = ’ || EXTRACT(HOUR FROM CURRENT_TIME) [from fiktiv];<br />

/* Ergebnis: ’Stunde = 14’ */<br />

Bei MS-<strong>SQL</strong> heißt diese Standardfunktion DATEPART; als können viele<br />

weitere Varianten genutzt werden.<br />

Sehr oft gibt es weitere Funktionen, die direkt e<strong>in</strong>en Bestandteil abfragen, zum<br />

Beispiel:<br />

YEAR( ) liefert das Jahr<br />

MONTH( ) liefert den Monat usw.<br />

MINUTE( ) liefert die M<strong>in</strong>ute usw.<br />

DAYOFWEEK( ) gibt den Wochentag an (als Zahl, 1 für Sonntag usw.)<br />

Wie gesagt: Lesen Sie <strong>in</strong> der DBMS-Dokumentation nach, was es sonst noch gibt.<br />

14.5. Funktionen für logische und NULL-Werte<br />

Wenn man es genau nimmt, gehören dazu auch Prüfungen wie „Ist e<strong>in</strong> Wert<br />

NULL?“. Der <strong>SQL</strong>-Standard hat dazu und zu anderen Prüfungen verschiedene<br />

spezielle Ausdrücke v<strong>org</strong>esehen. Diese gehören vor allem zur WHERE-Klausel 4<br />

des SELECT-Befehls:<br />

= < > usw. Größenvergleich zweier Werte<br />

BETWEEN AND Werte zwischen zwei Grenzen<br />

LIKE Ähnlichkeiten (1)<br />

CONTAINS u.a. Ähnlichkeiten (2)<br />

IS NULL null-Werte prüfen<br />

4 Kapitel 17 auf Seite 155<br />

116


IN genauer Vergleich mit e<strong>in</strong>er Liste<br />

EXISTS schneller Vergleich mit e<strong>in</strong>er Liste<br />

Konvertierungen<br />

Alle diese Ausdrücke liefern e<strong>in</strong>en der logischen Werte TRUE, FALSE − also<br />

WAHR oder FALSCH − und ggf. NULL − also − zurück und können<br />

als boolescher Wert weiterverarbeitet oder ausgewertet werden.<br />

14.5.1. Operatoren<br />

Zur Verknüpfung logischer Werte gibt es die „booleschen Operatoren“:<br />

NOT als Negation<br />

AND als Konjunktion<br />

OR als Adjunktion<br />

XOR als Kontravalenz /* nur bei My<strong>SQL</strong> */<br />

Auch diese Operatoren werden bei der WHERE-Klausel behandelt.<br />

Zur Verknüpfung von NULL-Werten gibt es vielfältige Regeln, je nach dem Zusammenhang,<br />

<strong>in</strong> dem das von Bedeutung ist. Man kann sich aber folgende Regel<br />

merken:<br />

Wenn e<strong>in</strong> Ausgangswert NULL ist, also , und dieser<br />

„Wert“ mit etwas verknüpft wird (z. B. mit e<strong>in</strong>er Zahl addiert wird),<br />

kann das Ergebnis nur se<strong>in</strong>, also NULL lauten.<br />

14.6. Konvertierungen<br />

In der EDV − also auch bei <strong>SQL</strong>-Datenbanken − ist der verwendete Datentyp<br />

immer von Bedeutung. Mit Zahlen kann gerechnet werden, mit Zeichenketten<br />

nicht. Größenvergleiche von Zahlen gelten immer und überall; bei Zeichenketten<br />

hängt die Sortierung auch von der Sprache ab. E<strong>in</strong> Datum wird <strong>in</strong> Deutschland<br />

durch '11.09.2001' beschrieben, <strong>in</strong> England durch '11/09/2001' und <strong>in</strong> den<br />

USA durch '09/11/2001'. Wenn wir etwas aufschreiben (z. B. e<strong>in</strong>en <strong>SQL</strong>-Befehl),<br />

dann benutzen wir zwangsläufig immer Zeichen bzw. Zeichenketten, auch wenn<br />

wir Zahlen oder e<strong>in</strong> Datum me<strong>in</strong>en.<br />

In vielen Fällen „versteht“ das DBMS, was wir mit e<strong>in</strong>er solchen Schreibweise<br />

me<strong>in</strong>en; dann werden durch „implizite Konvertierung“ die Datentypen automatisch<br />

<strong>in</strong>e<strong>in</strong>ander übertragen. In anderen Fällen muss der Anwender dem DBMS<br />

durch e<strong>in</strong>e Konvertierungsfunktion explizit erläutern, was wie zu verstehen ist.<br />

117


Funktionen<br />

14.6.1. Implizite Konvertierung<br />

Datentypen, die problemlos vergleichbar s<strong>in</strong>d, werden „automatisch“ <strong>in</strong>e<strong>in</strong>ander<br />

übergeführt.<br />

• Die Zahl 17 kann je nach Situation e<strong>in</strong> INTEGER, e<strong>in</strong> SMALLINT, e<strong>in</strong> BIGINT,<br />

aber auch NUMERIC oder FLOAT se<strong>in</strong>.<br />

• Die Zahl 17 kann <strong>in</strong> e<strong>in</strong>em SELECT-Befehl auch als Str<strong>in</strong>g '17' erkannt und verarbeitet<br />

werden.<br />

SELECT ID, Name, Vorname FROM Mitarbeiter WHERE ID = ’17’;<br />

• Je nach DBMS kann e<strong>in</strong> Str<strong>in</strong>g wie '11.09.2001' als Datum erkannt und verarbeitet<br />

werden. Vielleicht verlangt es aber auch e<strong>in</strong>e andere Schreibweise wie<br />

'11/09/2001'. Die Schreibweise '2009-09-11' nach ISO 8601 sollte dagegen immer<br />

richtig verstanden werden (aber auch da gibt es Abweichungen).<br />

Es gibt bereits durch den <strong>SQL</strong>-Standard ausführliche Regeln, welche Typen immer,<br />

unter bestimmten Umständen oder niemals <strong>in</strong>e<strong>in</strong>ander übergeführt werden<br />

können. Jedes DBMS ergänzt diese allgeme<strong>in</strong>en Regeln durch eigene.<br />

Wenn e<strong>in</strong> solcher Befehl ausgeführt wird, dürfte es niemals Missverständnisse<br />

geben, sondern er wird „mit an Sicherheit grenzender Wahrsche<strong>in</strong>lichkeit“ korrekt<br />

erledigt. Wenn e<strong>in</strong> Wert nicht e<strong>in</strong>deutig ist, wird das DBMS eher zu früh als<br />

zu spät mit e<strong>in</strong>er Fehlermeldung wie "conversion error from str<strong>in</strong>g. . . " reagieren.<br />

Dann ist es Zeit für e<strong>in</strong>e explizite Konvertierung, meistens durch CAST.<br />

14.6.2. CAST<br />

Die CAST-Funktion ist der <strong>SQL</strong>-Standard für die Überführung e<strong>in</strong>es Wertes von<br />

e<strong>in</strong>em Datentyp <strong>in</strong> e<strong>in</strong>en anderen.<br />

CAST ( AS )<br />

Als ist etwas angegeben, was den „falschen“ Typ hat, nämlich e<strong>in</strong><br />

Wert oder Ausdruck. Mit wird der Datentyp angegeben, der an der betreffenden<br />

Stelle gewünscht oder benötigt wird. Das Ergebnis des CAST<strong>in</strong>gs (der<br />

Konvertierung) ist dann genau von diesem Typ.<br />

Beispiele für Datum:<br />

SELECT EXTRACT(DAY FROM ’07.14.2009’) [from fiktiv];<br />

/* expression evaluation not supported */<br />

SELECT EXTRACT(DAY FROM CAST(’07.14.2009’ AS DATE)) [from fiktiv];<br />

118


Konvertierungen<br />

/* conversion error from str<strong>in</strong>g "07.14.2009" */<br />

SELECT EXTRACT(DAY FROM CAST(’14.07.2009’ AS DATE)) [from fiktiv];<br />

/* Ergebnis: ’14’ */<br />

SELECT Name, Vorname, CAST(Geburtsdatum AS CHAR(10)) FROM Versicherungsnehmer;<br />

/* Ergebnis wird <strong>in</strong> der Form ’1953-01-13’ angezeigt */<br />

Kürzere Zeichenketten können schnell verlängert werden, wenn es nötig ist:<br />

SELECT ID, CAST(Kuerzel AS CHAR(20)) FROM Abteilung;<br />

-- 20 Zeichen Länge statt eigentlich 10 Zeichen<br />

Das Verkürzen funktioniert nicht immer so e<strong>in</strong>fach. Ob bei Überschreitung e<strong>in</strong>er<br />

Maximallänge e<strong>in</strong>fach abgeschnitten wird oder ob es zu e<strong>in</strong>er Fehlermeldung<br />

"str<strong>in</strong>g truncation" kommt, hängt vom DBMS ab; dann müssen Sie ggf. e<strong>in</strong>e<br />

SUBSTRING-Variante benutzen.<br />

-- vielleicht funktioniert es so:<br />

SELECT CAST(Name AS CHAR(15)) || Vorname from Versicherungsnehmer;<br />

-- aber sicherer klappt es so:<br />

SELECT SUBSTRING(Name FROM 1 FOR 15) || Vorname from Versicherungsnehmer;<br />

-- ggf. unter Berücksichtigung des H<strong>in</strong>weises bei SUBSTRING:<br />

SELECT TRIM( SUBSTRING(Name FROM 1 FOR 15) ) || Vorname from Versicherungsnehmer;<br />

Bitte lesen Sie <strong>in</strong> Ihrer <strong>SQL</strong>-Dokumentation nach, zwischen welchen Datentypen<br />

implizite Konvertierung möglich ist und wie die explizite Konvertierung mit<br />

CAST ausgeführt wird.<br />

14.6.3. CONVERT<br />

Nach dem <strong>SQL</strong>-Standard ist CONVERT v<strong>org</strong>esehen zum Konvertieren von Zeichenketten<br />

<strong>in</strong> verschiedenen Zeichensätzen:<br />

CONVERT ( USING ) <strong>SQL</strong>-Standard<br />

CONVERT ( , ) alternative Schreibweise<br />

Firebird kennt diese Funktion überhaupt nicht. MS-<strong>SQL</strong> benutzt e<strong>in</strong>e andere<br />

Syntax und bietet vor allem für Datums- und Zeitformate viele weitere Möglichkeiten:<br />

CONVERT ( , [ , ] )<br />

Wegen dieser starken Abweichungen verzichten wir auf weitere Erläuterungen<br />

und verweisen auf die jeweilige Dokumentation.<br />

119


Funktionen<br />

14.6.4. Datum und Zeit<br />

Vor allem für die Konvertierung mit Datums- und Zeitangaben bieten die verschiedenen<br />

DBMS Erweiterungen. Beispiele:<br />

• MS-<strong>SQL</strong> hat die Syntax von CONVERT für diesen Zweck erweitert.<br />

• My<strong>SQL</strong> konvertiert vor allem mit den Funktionen STR_TO_DATE und<br />

DATE_FORMAT.<br />

• Oracle kennt e<strong>in</strong>e Reihe von Funktionen wie TO_DATE usw.<br />

Noch e<strong>in</strong> Grund für das Studium der Dokumentation. . .<br />

14.7. Spaltenfunktionen<br />

Die Spaltenfunktionen werden auch als Aggregatfunktionen bezeichnet, weil<br />

sie e<strong>in</strong>e Menge von Werten − nämlich aus allen Zeilen e<strong>in</strong>er Spalte − zusammenfassen<br />

und daraus e<strong>in</strong>en Wert bestimmen. In der Regel geht es um e<strong>in</strong>e Spalte aus<br />

e<strong>in</strong>er der beteiligten Tabellen; es kann aber auch e<strong>in</strong> sonstiger <strong>SQL</strong>-Ausdruck mit<br />

e<strong>in</strong>em e<strong>in</strong>zelnen Wert als Ergebnis se<strong>in</strong>. Das Ergebnis der Funktion ist dann e<strong>in</strong><br />

Wert, der aus allen passenden Zeilen der Abfrage berechnet wird.<br />

Bei Abfragen kann das Ergebnis e<strong>in</strong>er Spaltenfunktion auch nach e<strong>in</strong>er oder<br />

mehreren Spalten oder Berechnungen gruppiert werden. Die Funktionen liefern<br />

dann für jede Gruppe e<strong>in</strong> Teilergebnis – Näheres unter Gruppierungen 5 .<br />

14.7.1. COUNT – Anzahl<br />

Die Funktion COUNT zählt alle Zeilen, die e<strong>in</strong>en e<strong>in</strong>deutigen Wert enthalten,<br />

also nicht NULL s<strong>in</strong>d. Sie kann auf alle Datentypen angewendet werden, da für<br />

jeden Datentyp NULL def<strong>in</strong>iert ist. Beispiel:<br />

SELECT COUNT(Farbe) AS Anzahl_Farbe FROM Fahrzeug;<br />

Die Spalte Farbe ist als VARCHAR(30), also als Text variabler Länge def<strong>in</strong>iert und<br />

optional. Hier werden also alle Zeilen gezählt, die <strong>in</strong> dieser Spalte e<strong>in</strong>en E<strong>in</strong>trag<br />

haben. Dasselbe funktioniert auch mit e<strong>in</strong>er numerischen Spalte:<br />

SELECT COUNT(Schadenshoehe) AS Anzahl_Schadenshoehe FROM Schadensfall;<br />

Hier ist die Spalte numerisch und optional. Die Zahl 0 ist bekanntlich nicht<br />

NULL. Wenn <strong>in</strong> der Spalte e<strong>in</strong>e 0 steht, wird sie mitgezählt.<br />

5 Kapitel 25 auf Seite 241<br />

120


Spaltenfunktionen<br />

E<strong>in</strong> Spezialfall ist der Asterisk '*' als Parameter. Dies bezieht sich dann nicht auf<br />

e<strong>in</strong>e e<strong>in</strong>zelne Spalte, sondern auf e<strong>in</strong>e ganze Zeile. So wird also die Anzahl der<br />

Zeilen <strong>in</strong> der Tabelle gezählt:<br />

SELECT COUNT(*) AS Anzahl_Zeilen FROM Schadensfall;<br />

Die Funktion COUNT liefert niemals NULL zurück, sondern immer e<strong>in</strong>e Zahl;<br />

wenn alle Werte <strong>in</strong> e<strong>in</strong>er Spalte NULL s<strong>in</strong>d, ist das Ergebnis die Zahl 0 (es gibt 0<br />

Zeilen mit e<strong>in</strong>em Wert ungleich NULL <strong>in</strong> dieser Spalte).<br />

14.7.2. SUM – Summe<br />

Die Funktion SUM kann (natürlich) nur auf numerische Datentypen angewendet<br />

werden. Im Gegensatz zu COUNT liefert SUM nur dann e<strong>in</strong>en Wert zurück,<br />

wenn wenigstens e<strong>in</strong> E<strong>in</strong>gabewert nicht NULL ist. Als Beispiel für e<strong>in</strong>e e<strong>in</strong>zelne<br />

numerische Spalte werden alle Werte der Spalte Schadenshoehe aufsummiert:<br />

SELECT SUM(Schadenshoehe) AS Summe_Schadenshoehe<br />

FROM Schadensfall;<br />

Als Parameter kann nicht nur e<strong>in</strong>e e<strong>in</strong>zelne numerische Spalte, sondern auch<br />

e<strong>in</strong>e Berechnung übergeben werden, die als Ergebnis e<strong>in</strong>e e<strong>in</strong>zelne Zahl liefert.<br />

Aufgabe: Hier werden Euro-Beträge aus Schadenshoehe zuerst <strong>in</strong> US-Dollar<br />

nach e<strong>in</strong>em Tageskurs umgerechnet und danach aufsummiert.<br />

SELECT SUM(Schadenshoehe * 1.5068) AS Summe_Schadenshoehe_-<br />

Dollar FROM Schadensfall;<br />

E<strong>in</strong>e Besonderheit ist das Berechnen von Vergleichen. E<strong>in</strong> Vergleich wird als<br />

WAHR oder FALSCH ausgewertet. Sofern das DBMS (wie bei My<strong>SQL</strong> oder Access)<br />

das Ergebnis als Zahl benutzt, ist das Ergebnis e<strong>in</strong>es Vergleichs daher 1 oder<br />

0 (bei Access –1 oder 0). Um alle Fälle zu zählen, deren Schadenshöhe größer als<br />

1000 ist, müsste der Befehl so aussehen:<br />

SELECT SUM(Schadenshoehe > 1000) AS Anzahl_Schadenshoehe_gt_1000<br />

FROM Schadensfall;<br />

Dabei werden nicht etwa die Schäden aufsummiert, sondern nur das Ergebnis<br />

des Vergleichs, also 0 oder 1, im Grunde also gezählt. Die Funktion COUNT kann<br />

hier nicht genommen werden, da sie sowohl die 1 als auch die 0 zählen würde.<br />

E<strong>in</strong>ige DBMS (z. B. DB2, Oracle) haben e<strong>in</strong>e strengere Typenkontrolle; Firebird<br />

nimmt e<strong>in</strong>e Zwischenstellung e<strong>in</strong>. Dabei haben Vergleichsausdrücke grundsätzlich<br />

e<strong>in</strong> boolesches Ergebnis, das nicht summiert werden kann. Dann kann man<br />

sich mit der CASE-Konstruktion behelfen, die dem Wahrweitswert TRUE e<strong>in</strong>e 1<br />

zuordnet und dem Wert FALSE e<strong>in</strong>e 0:<br />

121


Funktionen<br />

SELECT SUM(CASE WHEN Schadenshoehe > 1000 THEN 1<br />

ELSE 0<br />

END) AS Anzahl_Schadenshoehe_gt_1000<br />

FROM Schadensfall;<br />

14.7.3. MAX, MIN – Maximum, M<strong>in</strong>imum<br />

Diese Funktionen können auf jeden Datentyp angewendet werden, für den e<strong>in</strong><br />

Vergleich e<strong>in</strong> gültiges Ergebnis liefert. Dies gilt für numerische Werte, Datumswerte<br />

und Textwerte, nicht aber für z. B. BLOBs (B<strong>in</strong>ary Large Objects). Bei Textwerten<br />

ist zu bedenken, dass die Sortierung je nach verwendetem Betriebssystem,<br />

DBMS und Zeichensatze<strong>in</strong>stellungen der Tabelle oder Spalte unterschiedlich<br />

ist, die Funktion demnach auch unterschiedliche Ergebnisse liefern kann.<br />

Aufgabe: Suche den kle<strong>in</strong>sten, von NULL verschiedenen Schadensfall.<br />

SELECT MIN(Schadenshoehe) AS M<strong>in</strong>imum_Schadenshoehe FROM Schadensfall;<br />

Kommen nur NULL-Werte vor, wird NULL zurückgegeben. Gibt es mehrere Zeilen,<br />

die den kle<strong>in</strong>sten Wert haben, wird trotzdem nur e<strong>in</strong> Wert zurückgegeben.<br />

Welche Zeile diesen Wert liefert, ist nicht def<strong>in</strong>iert.<br />

Für MAX gilt Entsprechendes wie für MIN.<br />

14.7.4. AVG – Mittelwert<br />

AVG (average = Durchschnitt) kann nur auf numerische Werte angewendet werden.<br />

Das für SUM Gesagte gilt analog auch für AVG. Um die mittlere Schadenshöhe<br />

zu berechnen, schreibt man:<br />

SELECT AVG(Schadenshoehe) AS Mittlere_Schadenshoehe FROM Schadensfall;<br />

NULL-Werte fließen dabei nicht <strong>in</strong> die Berechnung e<strong>in</strong>, Nullen aber sehr wohl.<br />

14.7.5. STDDEV – Standardabweichung<br />

Die Standardabweichung STDDEV oder STDEV kann auch nur für numerische<br />

Werte berechnet werden. NULL-Werte fließen nicht mit <strong>in</strong> die Berechnung e<strong>in</strong>,<br />

Nullen schon. Wie bei SUM können auch Berechnungen als Werte genommen<br />

werden. Die Standardabweichung der Schadensfälle wird so berechnet:<br />

122<br />

SELECT STDDEV(Schadenshoehe) AS StdAbw_Schadenshoehe FROM Schadensfall;


14.8. Zusammenfassung<br />

Zusammenfassung<br />

In diesem Kapitel lernten wir die wichtigsten „e<strong>in</strong>gebauten“ Funktionen kennen:<br />

• Für Zahlen gibt es vor allem die Operatoren, dazu die modulo-Funktionen und<br />

Möglichkeiten für Rundungen.<br />

• Für Zeichenketten gibt es vor allem das Verknüpfen und Aufteilen, dazu die<br />

Längenbestimmung und die Umwandlung <strong>in</strong> Groß- und Kle<strong>in</strong>buchstaben.<br />

• Für Datums- und Zeitwerte gibt es neben Systemfunktionen die Verwendung<br />

e<strong>in</strong>zelner Teile.<br />

• Für logische und NULL-Werte gibt es vor allem Vergleiche und Komb<strong>in</strong>ationen<br />

durch Operatoren.<br />

• Konvertierungen − implizite, CAST, CONVERT − dienen dazu, dass e<strong>in</strong> Wert<br />

des e<strong>in</strong>en Datentyps als Wert e<strong>in</strong>es anderen Typs verwendet werden kann.<br />

Mit Spaltenfunktionen werden alle Werte e<strong>in</strong>er Spalte geme<strong>in</strong>sam ausgewertet.<br />

14.9. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 125.<br />

Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Feststellungen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

1. E<strong>in</strong>e Skalarfunktion bestimmt aus allen Werten e<strong>in</strong>es Feldes e<strong>in</strong>en geme<strong>in</strong>samen<br />

Wert.<br />

2. E<strong>in</strong>e Spaltenfunktion bestimmt aus allen Werten e<strong>in</strong>es Feldes e<strong>in</strong>en geme<strong>in</strong>samen<br />

Wert.<br />

3. E<strong>in</strong>e Skalarfunktion kann ke<strong>in</strong>e Werte aus den Spalten e<strong>in</strong>er Tabelle verarbeiten.<br />

4. E<strong>in</strong>e benutzerdef<strong>in</strong>ierte Funktion kann als Skalarfunktion, aber nicht als<br />

Spaltenfunktion dienen.<br />

5. Wenn e<strong>in</strong>er Funktion ke<strong>in</strong>e Argumente übergeben werden, kann auf die<br />

Klammern h<strong>in</strong>ter dem Funktionsnamen verzichtet werden.<br />

6. Wenn e<strong>in</strong>e Funktion für e<strong>in</strong>en <strong>SQL</strong>-Ausdruck benutzt wird, muss das Ergebnis<br />

der Funktion vom Datentyp her mit dem übere<strong>in</strong>stimmen, der an<br />

der betreffenden Stelle erwartet wird.<br />

123


Funktionen<br />

Übung 2 – Def<strong>in</strong>itionen<br />

Welche der folgenden Funktionen s<strong>in</strong>d Spaltenfunktionen? Welche s<strong>in</strong>d Skalarfunktionen,<br />

welche davon s<strong>in</strong>d Konvertierungen?<br />

1. Bestimme den Rest bei e<strong>in</strong>er Division ganzer Zahlen.<br />

2. Bestimme die Länge des Namens des Mitarbeiters mit der ID 13.<br />

3. Bestimme die maximale Länge aller Mitarbeiter-Namen.<br />

4. Bestimme die Gesamtlänge aller Mitarbeiter-Namen.<br />

5. Verwandle e<strong>in</strong>e Zeichenkette, die nur Ziffern enthält, <strong>in</strong> e<strong>in</strong>e ganze Zahl.<br />

6. Verwandle e<strong>in</strong>e Zeichenkette, die ke<strong>in</strong>e Ziffern enthält, <strong>in</strong> e<strong>in</strong>en Str<strong>in</strong>g, der<br />

nur Großbuchstaben enthält.<br />

7. Bestimme das aktuelle Datum.<br />

Übung 3 – Funktionen mit Zahlen<br />

Benutzen Sie für die folgenden Berechnungen die Spalte Schadenshoehe der<br />

Tabelle Schadensfall. Welche Datensätze benutzt werden, also der Inhalt der<br />

WHERE-Klausel, soll uns dabei nicht <strong>in</strong>teressieren. Auch geht es nur um die Formeln,<br />

nicht um e<strong>in</strong>en SELECT-Befehl.<br />

1. Berechnen Sie (ohne AVG-Funktion) die durchschnittliche Schadenshöhe.<br />

2. Bestimmen Sie den prozentualen Anteil e<strong>in</strong>es bestimmten Schadensfalls<br />

an der gesamten Schadenshöhe.<br />

Übung 4 – Funktionen mit Zeichenketten<br />

Schreiben Sie Name, Vorname und Abteilung der Mitarbeiter <strong>in</strong> tabellarischer<br />

Form (nehmen wir an, dass das Kuerzel der Abteilung <strong>in</strong> der Tabelle Mitarbeiter<br />

stünde); benutzen Sie dazu nache<strong>in</strong>ander die folgenden Teilaufgaben:<br />

1. Br<strong>in</strong>gen Sie die Namen auf e<strong>in</strong>e e<strong>in</strong>heitliche Länge von 20 Zeichen.<br />

2. Br<strong>in</strong>gen Sie die Namen auf e<strong>in</strong>e e<strong>in</strong>heitliche Länge von 10 Zeichen.<br />

3. Br<strong>in</strong>gen Sie die Vornamen ebenso auf e<strong>in</strong>e Länge von 10 Zeichen.<br />

4. Setzen Sie diese Teilergebnisse zusammen und fügen Sie dazwischen je<br />

zwei Leerzeichen e<strong>in</strong>.<br />

Übung 5 – Funktionen mit Datum und Zeit<br />

Gegeben ist e<strong>in</strong> Timestamp-Wert mit dem Spaltennamen Zeitstempel und dem<br />

Inhalt "16. Dezember 2009 um 19:53 Uhr". Zeigen Sie diesen Wert als Zeichenkette<br />

im Format "12/2009; 16. Tag; 7 M<strong>in</strong>uten vor 20 Uhr" an. (Für die Str<strong>in</strong>g-<br />

Verknüpfung benutzen wir jetzt das Plus-Zeichen. Die Leerzeichen zwischen den<br />

124


Übungen<br />

Bestandteilen können Sie ignorieren. Auch muss es ke<strong>in</strong>e allgeme<strong>in</strong>gültige Lösung<br />

se<strong>in</strong>, die alle Eventualitäten beachtet.)<br />

Übung 6 – Funktionen mit Datum und Zeit<br />

Gegeben ist e<strong>in</strong>e Zeichenkette datum mit dem Inhalt "16122009". S<strong>org</strong>en Sie dafür,<br />

dass das Datum für jedes DBMS gültig ist. Zusatzfrage: Muss dafür CAST verwendet<br />

werden und warum bzw. warum nicht?<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 123.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Richtig s<strong>in</strong>d 2 und 6, falsch s<strong>in</strong>d 1, 3, 4, 5.<br />

Lösung zu Übung 2 – Def<strong>in</strong>itionen<br />

Spaltenfunktionen s<strong>in</strong>d 3 und 4; beide benutzen das Ergebnis von Skalarfunktionen.<br />

Alle anderen s<strong>in</strong>d Skalarfunktionen, wobei 5 e<strong>in</strong>e Konvertierung ist und 7<br />

e<strong>in</strong>e Systemfunktion, die ohne Klammern geschrieben wird.<br />

Lösung zu Übung 3 – Funktionen mit Zahlen<br />

1. SUM(Schadenshoehe) / COUNT(Schadenshoehe)<br />

2. ROUND( Schadenshoehe * 100 / SUM(Schadenshoehe) )<br />

Lösung zu Übung 4 – Funktionen mit Zeichenketten<br />

1. CAST(Name AS CHAR(20))<br />

2. SUBSTRING( CAST(Name AS CHAR(20)) FROM 1 FOR 10 )<br />

3. SUBSTRING( CAST(Vorname AS CHAR(20)) FROM 1 FOR 10 )<br />

4. SUBSTRING( CAST(Name AS CHAR(20)) FROM 1 FOR 10 ) {{!!}} ’ ’ {{!!}}<br />

SUBSTRING( CAST(Vorname AS CHAR(20)) FROM 1 FOR 10 ) {{!!}} ’ ’ {{!!}} Kuerzel<br />

Lösung zu Übung 5 – Funktionen mit Datum und Zeit<br />

EXTRACT(MONTH FROM CURRENT_TIMESTAMP) + ’/’<br />

+ EXTRACT(YEAR FROM CURRENT_TIMESTAMP) + ’;’<br />

+ EXTRACT(DAY FROM CURRENT_TIMESTAMP) + ’.Tag; ’<br />

125


Funktionen<br />

+ CAST((60 - EXTRACT(MINUTE FROM CURRENT_TIMESTAMP)) AS Varchar(2)) + ’ M<strong>in</strong>uten vor ’<br />

+ CAST( (EXTRACT(HOUR FROM CURRENT_TIMESTAMP) + 1) AS Varchar(2)) + ’ Uhr’<br />

Lösung zu Übung 6 – Funktionen mit Datum und Zeit<br />

SUBSTRING(datum FROM 5 FOR 4) + ’-’<br />

+ SUBSTRING(datum FROM 3 FOR 2) + ’-’<br />

+ SUBSTRING(datum FROM 1 FOR 2)<br />

Auf CAST kann (fast immer) verzichtet werden, weil mit dieser Substr<strong>in</strong>g-<br />

Verwendung die Standardschreibweise '2009-12-16' nach ISO 8601 erreicht wird.<br />

14.10. Siehe auch<br />

E<strong>in</strong>ige H<strong>in</strong>weise s<strong>in</strong>d <strong>in</strong> den folgenden Kapiteln zu f<strong>in</strong>den:<br />

• <strong>SQL</strong>-Befehle 6 beschreibt auch den Begriff „<strong>SQL</strong>-Ausdruck“.<br />

• Datentypen 7<br />

Weitere Erläuterungen stehen bei Wikipedia:<br />

• Senkrechter Strich 8 oder „Pipe“-Zeichen<br />

• Boolesche Operatoren 9<br />

• ISO 8601 10 zur Schreibweise von Datumsangaben<br />

6 Kapitel 7 auf Seite 47<br />

7 Kapitel 13 auf Seite 97<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Senkrechter%20Strich<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Boolescher%20Operator<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/ISO%208601<br />

126


Teil III.<br />

Mehr zu Abfragen<br />

127


15. Ausführliche SELECT-Struktur<br />

15.1. Allgeme<strong>in</strong>e Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . 129<br />

15.2. Set Quantifier – Mengenquantifizierer . . . . . . . . . . . . . . 130<br />

15.3. Select List – Auswahlliste . . . . . . . . . . . . . . . . . . . . . . . 130<br />

15.4. Table Reference List − Tabellen-Verweise . . . . . . . . . . . . . 132<br />

15.5. WHERE-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133<br />

15.6. GROUP BY- und HAVING-Klausel . . . . . . . . . . . . . . . . . . 134<br />

15.7. UNION-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135<br />

15.8. ORDER BY-Klausel . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

15.9. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

15.10. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136<br />

Dieses Kapitel erläutert die Syntax des SELECT-Befehls. Anstelle von Beispielen<br />

gibt es Verweise auf diejenigen Kapitel, die die betreffenden Klauseln genauer<br />

behandeln.<br />

Bitte beachten Sie: Der SELECT-Befehl bietet so umfangreiche Möglichkeiten,<br />

dass auch bei dieser Übersicht nicht alle E<strong>in</strong>zelheiten v<strong>org</strong>estellt werden können.<br />

15.1. Allgeme<strong>in</strong>e Syntax<br />

Der SELECT-Befehl wird als oder , also<br />

Abfrage-Ausdruck bezeichnet und setzt sich grundsätzlich aus diesen Bestandteilen<br />

zusammen:<br />

SELECT<br />

[ DISTINCT | ALL ]<br />

<br />

FROM <br />

[ ]<br />

[ ]<br />

[ ]<br />

[ UNION [ALL] ]<br />

[ ]<br />

129


Ausführliche SELECT-Struktur<br />

Diese Bestandteile setzen sich je nach Bedarf aus weiteren e<strong>in</strong>fachen oder komplexen<br />

Teilen zusammen und werden <strong>in</strong> den folgenden Abschnitten erläutert.<br />

Unbed<strong>in</strong>gt erforderlich s<strong>in</strong>d:<br />

• das Schlüsselwort SELECT<br />

• e<strong>in</strong>e Liste von Spalten<br />

• das Schlüsselwort FROM mit e<strong>in</strong>em oder mehreren Verweisen auf Tabellen<br />

Alle anderen Bestandteile s<strong>in</strong>d optional, können also auch weggelassen werden.<br />

Andernfalls müssen sie <strong>in</strong> genau der genannten Reihenfolge verwendet werden.<br />

15.2. Set Quantifier – Mengenquantifizierer<br />

Als Mengenquantifizierer stehen DISTINCT und ALL zur Verfügung. Alles Nötige<br />

wurde schon im Kapitel DML (1) – Daten abfragen 1 erwähnt.<br />

Außerdem ist es sehr oft möglich (wenn auch nicht im <strong>SQL</strong>-Standard v<strong>org</strong>eschrieben),<br />

das Ergebnis auf e<strong>in</strong>e bestimmte Anzahl von Zeilen zu beschränken,<br />

z. B. FIRST 3 oder LIMIT 7.<br />

Erläuterungen zu dieser Beschränkung stehen unter Nützliche Erweiterungen 2 .<br />

15.3. Select List – Auswahlliste<br />

Die Liste der Spalten, die ausgewählt werden sollen, wird angegeben durch den<br />

Asterisk oder mit e<strong>in</strong>er .<br />

15.3.1. Asterisk *<br />

Der Asterisk, d. h. das Sternchen *, ist e<strong>in</strong>e Kurzfassung und steht für die Liste<br />

aller Spalten (Felder) e<strong>in</strong>er e<strong>in</strong>zelnen Tabelle.<br />

Wenn mehrere Tabellen verknüpft werden, ist der Asterisk zusammen mit dem<br />

Namen oder dem Alias e<strong>in</strong>er Tabelle anzugeben.<br />

.* /* oder */<br />

.*<br />

Erläuterungen und Beispiele dazu f<strong>in</strong>den sich <strong>in</strong> allen Kapiteln, die sich mit Abfragen<br />

befassen.<br />

1 Kapitel 8 auf Seite 57<br />

2 Kapitel 23 auf Seite 213<br />

130


15.3.2. Column List – Spaltenliste<br />

Select List – Auswahlliste<br />

Die ist e<strong>in</strong>e Liste von e<strong>in</strong>em oder mehreren Elementen, die <strong>in</strong> der<br />

<strong>SQL</strong>-Dokumentation als bezeichnet werden:<br />

[ , , , ... ]<br />

Jedes e<strong>in</strong>zelne Element ist e<strong>in</strong>e Spalte e<strong>in</strong>er Tabelle oder e<strong>in</strong>e abgeleitete Spalte<br />

− siehe den nächsten Abschnitt. Jedem Element kann e<strong>in</strong><br />

Spalten-Alias zugewiesen, das Wort AS kann dabei auch weggelassen werden:<br />

[ [AS] ]<br />

Bei Spalten aus Tabellen wird e<strong>in</strong>fach deren Name angegeben. Wenn mehrere<br />

Tabellen verknüpft werden und e<strong>in</strong> Spaltenname nicht e<strong>in</strong>deutig ist, ist der Spaltenname<br />

zusammen mit dem Namen oder dem Alias e<strong>in</strong>er Tabelle anzugeben.<br />

/* oder */<br />

. /* oder */<br />

.<br />

H<strong>in</strong>weis: In manchen DBMS darf der Tabellenname nicht mehr benutzt werden,<br />

wenn e<strong>in</strong> Tabellen-Alias angegeben ist.<br />

Erläuterungen und Beispiele dazu f<strong>in</strong>den sich <strong>in</strong> allen Kapiteln, die sich mit Abfragen<br />

befassen.<br />

15.3.3. Abgeleitete Spalte<br />

E<strong>in</strong>e abgeleitete Spalte bezeichnet das Ergebnis e<strong>in</strong>es . Das kann e<strong>in</strong> beliebiger Ausdruck se<strong>in</strong>, der für jede Zeile genau<br />

e<strong>in</strong>en Wert liefert – vor allem e<strong>in</strong>e Funktion oder e<strong>in</strong>e passende Unterabfrage.<br />

Erläuterungen dazu f<strong>in</strong>den sich vor allem <strong>in</strong> den Kapiteln Berechnete Spalten 3<br />

und Unterabfragen 4 .<br />

3 Kapitel 24 auf Seite 235<br />

4 Kapitel 26 auf Seite 251<br />

131


Ausführliche SELECT-Struktur<br />

15.4. Table Reference List − Tabellen-Verweise<br />

Als Bestandteil der FROM-Klausel werden die beteiligten Tabellen und Verweise<br />

darauf aufgeführt:<br />

FROM /* nämlich */<br />

FROM [ , , ... ]<br />

In der stehen e<strong>in</strong>e oder (mit Komma getrennt) mehrere Verweise<br />

(Referenzen); diese können direkt aus der Datenbank übernommen oder auf<br />

verschiedene Arten abgeleitet werden.<br />

Jede dieser Varianten kann erweitert werden:<br />

[ [ AS ] <br />

[ ( ) ] ]<br />

Mit AS kann e<strong>in</strong> Alias-Name dem Verweis h<strong>in</strong>zugefügt werden; das Schlüsselwort<br />

AS kann auch entfallen. Diesem Tabellen-Alias kann (je nach DBMS) <strong>in</strong> Klammern<br />

e<strong>in</strong>e Liste von Spaltennamen h<strong>in</strong>zugefügt werden, die anstelle der eigentlichen<br />

Spaltennamen angezeigt werden sollen.<br />

Die folgenden Varianten s<strong>in</strong>d als Verweise möglich.<br />

Tabellen, Views: Primär verwendet man die Tabellen sowie die VIEWs (fest def<strong>in</strong>ierte<br />

Sichten).<br />

Erläuterungen dazu f<strong>in</strong>den sich <strong>in</strong> allen Kapiteln, die sich mit Abfragen<br />

befassen, sowie <strong>in</strong> Erstellen von Views 5 .<br />

Derived Table − Abgeleitete Tabellen: Vor allem können Unterabfragen wie<br />

e<strong>in</strong>e Tabelle benutzt werden.<br />

H<strong>in</strong>weis: Es gibt e<strong>in</strong>e Reihe weiterer Varianten, um andere Tabellen „vorübergehend“<br />

abzuleiten. Wegen der DBMS-Unterschiede würde die Übersicht<br />

zu kompliziert; wir verzichten deshalb auf e<strong>in</strong>e ausführlichere Darstellung.<br />

Erläuterungen dazu f<strong>in</strong>den sich im Kapitel Unterabfragen 6 .<br />

Jo<strong>in</strong>ed Table − Verknüpfte Tabelle: Wie e<strong>in</strong>e Tabelle kann auch e<strong>in</strong>e Verknüpfung<br />

verwendet werden. Dabei handelt es sich um zwei Tabellen, die nach<br />

bestimmten Bed<strong>in</strong>gungen verbunden werden.<br />

5 Kapitel 27 auf Seite 271<br />

6 Kapitel 26 auf Seite 251<br />

132


WHERE-Klausel<br />

Für die Verknüpfung zweier (oder mehrerer) Tabellen gibt es zwei Verfahren:<br />

Beim (traditionellen) direkten Weg werden die beiden Tabellen e<strong>in</strong>fach<br />

nache<strong>in</strong>ander aufgeführt. Beim (modernen) Weg wird zu e<strong>in</strong>er Tabelle<br />

e<strong>in</strong>e weitere mit JOIN genannt, die durch e<strong>in</strong>e Verknüpfungsbed<strong>in</strong>gung<br />

über ON verbunden wird.<br />

Erläuterungen dazu f<strong>in</strong>den sich ab dem Kapitel Mehrere Tabellen 7 .<br />

15.5. WHERE-Klausel<br />

Mit der WHERE-Klausel wird e<strong>in</strong>e Suchbed<strong>in</strong>gung festgelegt, welche Zeilen der<br />

Ergebnistabelle zur Auswahl gehören sollen. Dieser Filter wird zuerst erstellt; erst<br />

anschließend werden Bed<strong>in</strong>gungen wie GROUP BY und ORDER BY ausgewertet.<br />

WHERE <br />

Die Suchbed<strong>in</strong>gung ist e<strong>in</strong>e Konstruktion mit e<strong>in</strong>em e<strong>in</strong>deutigen<br />

Prüfergebnis: Entweder die Zeile gehört zur Auswahl, oder sie gehört nicht<br />

zur Auswahl. Es handelt sich also um e<strong>in</strong>e logische Verknüpfung von e<strong>in</strong>er oder<br />

mehreren booleschen Variablen.<br />

Erläuterungen zu den folgenden E<strong>in</strong>zelheiten f<strong>in</strong>den sich vor allem <strong>in</strong> den Kapiteln<br />

WHERE-Klausel im Detail 8 und Unterabfragen 9 .<br />

15.5.1. E<strong>in</strong>e e<strong>in</strong>zelne Suchbed<strong>in</strong>gung<br />

E<strong>in</strong>e Suchbed<strong>in</strong>gung hat e<strong>in</strong>e der folgenden Formen, deren Ergebnis immer<br />

WAHR oder FALSCH (TRUE bzw. FALSE) lautet:<br />

[ NOT ] <br />

[ NOT ] BETWEEN AND <br />

[ NOT ] LIKE [ ESCAPE ]<br />

[ NOT ] CONTAINING <br />

[ NOT ] STARTING [ WITH ] <br />

IS [ NOT ] NULL<br />

[ NOT ] IN <br />

[ NOT ] EXISTS <br />

NOT <br />

7 Kapitel 18 auf Seite 167<br />

8 Kapitel 17 auf Seite 155<br />

9 Kapitel 26 auf Seite 251<br />

133


Ausführliche SELECT-Struktur<br />

Anstelle e<strong>in</strong>es Wertes kann auch e<strong>in</strong> Wertausdruck stehen,<br />

also e<strong>in</strong>e Unterabfrage, die genau e<strong>in</strong>en Wert als Ergebnis liefert.<br />

Anstelle e<strong>in</strong>er Werteliste kann auch e<strong>in</strong> Auswahl-Ausdruck <br />

stehen, also e<strong>in</strong>e Unterabfrage, die e<strong>in</strong>e Liste mehrerer Werte als Ergebnis liefert.<br />

Als s<strong>in</strong>d folgende Vergleiche möglich, und zwar sowohl für Zahlen als<br />

auch für Zeichenketten und Datumsangaben:<br />

= < > = <br />

Wenn das Prüfergebnis „umgekehrt“ werden soll – statt TRUE soll FALSE und<br />

statt FALSE soll TRUE gelten –, hat man mehrere Möglichkeiten:<br />

• Man ersetzt den Vergleichsoperator: Statt nach Gleichheit wird nach Ungleichheit<br />

geprüft usw.<br />

• Man setzt NOT vor den Operator, wie <strong>in</strong> der Übersicht bei jeder e<strong>in</strong>zelnen<br />

Suchbed<strong>in</strong>gung notiert. Dies bewirkt die Umkehrung des Operators und dadurch<br />

des Prüfergebnisses.<br />

• Man setzt NOT vor e<strong>in</strong>e Suchbed<strong>in</strong>gung, wie <strong>in</strong> der Übersicht bei der letzten<br />

Suchbed<strong>in</strong>gung notiert. Dies bewirkt die Umkehrung des Prüfergebnisses.<br />

15.5.2. Mehrere Suchbed<strong>in</strong>gungen<br />

Mehrere Suchbed<strong>in</strong>gungen können mite<strong>in</strong>ander verbunden werden:<br />

NOT <br />

AND <br />

OR <br />

Bitte beachten Sie: NOT hat die stärkste Verb<strong>in</strong>dung und wird zuerst ausgewertet.<br />

Danach hat AND e<strong>in</strong>e stärkere Verb<strong>in</strong>dung und wird als nächstes untersucht.<br />

Erst zum Schluss kommen die OR-Verb<strong>in</strong>dungen. Um Unklarheiten zu vermeiden,<br />

wird dr<strong>in</strong>gend empfohlen, zusammengesetzte Suchbed<strong>in</strong>gungen <strong>in</strong> Klammern<br />

zu setzen, um Prioritäten deutlich zu machen.<br />

15.6. GROUP BY- und HAVING-Klausel<br />

Mit der GROUP BY-Klausel werden alle Zeilen, die <strong>in</strong> e<strong>in</strong>er oder mehreren Spalten<br />

den gleichen Wert enthalten, <strong>in</strong> jeweils e<strong>in</strong>er Gruppe zusammengefasst. Dies<br />

macht <strong>in</strong> der Regel nur dann S<strong>in</strong>n, wenn <strong>in</strong> der Spaltenliste des SELECT-Befehls<br />

e<strong>in</strong>e gruppenweise Auswertung, also e<strong>in</strong>e der Spaltenfunktionen enthalten ist.<br />

134


Die allgeme<strong>in</strong>e Syntax lautet:<br />

GROUP BY <br />

UNION-Klausel<br />

Die Spaltenliste enthält, durch Komma getrennt, die Namen von e<strong>in</strong>er oder mehreren<br />

Spalten. Bei jeder Spalte kann e<strong>in</strong>e eigene Sortierung angegeben werden<br />

(wie bei den Datentypen 10 erläutert):<br />

<br />

-- oder<br />

COLLATE <br />

Die HAVING-Klausel dient dazu, nicht alle ausgewählten Zeilen <strong>in</strong> die Ausgabe<br />

zu übernehmen, sondern nur diejenigen, die den zusätzlichen Bed<strong>in</strong>gungen<br />

entsprechen. Sie wird <strong>in</strong> der Praxis überwiegend als Ergänzung zur GROUP BY-<br />

Klausel verwendet und folgt ggf. direkt danach.<br />

HAVING <br />

Erläuterungen dazu f<strong>in</strong>den sich vor allem im Kapitel Gruppierungen 11 .<br />

15.7. UNION-Klausel<br />

Mit der UNION-Klausel werden mehrere eigentlich getrennte Abfragen zusammengefasst,<br />

um e<strong>in</strong> e<strong>in</strong>heitliches Ergebnis zu liefern. Dabei s<strong>in</strong>d die e<strong>in</strong>zelnen<br />

Bed<strong>in</strong>gungen zu komplex, um sie zusammenzufassen; oder sie können nicht<br />

s<strong>in</strong>nvoll verbunden werden. Es setzt e<strong>in</strong>e weitgehend identische Spaltenliste<br />

voraus.<br />

SELECT FROM WHERE <br />

UNION<br />

SELECT FROM WHERE <br />

Erläuterungen dazu f<strong>in</strong>den sich im Kapitel Nützliche Erweiterungen 12 .<br />

10 Kapitel 13 auf Seite 97<br />

11 Kapitel 25 auf Seite 241<br />

12 Kapitel 23 auf Seite 213<br />

135


Ausführliche SELECT-Struktur<br />

15.8. ORDER BY-Klausel<br />

Mit der ORDER BY-Klausel werden die Datensätze der Ergebnismenge <strong>in</strong> e<strong>in</strong>er<br />

bestimmten Sortierung ausgegeben.<br />

Erläuterungen und Beispiele dazu f<strong>in</strong>den sich <strong>in</strong> allen Kapiteln, die sich mit Abfragen<br />

befassen.<br />

15.9. Zusammenfassung<br />

In diesem Kapitel erhielten Sie e<strong>in</strong>en umfangreichen Überblick über die Syntax<br />

des SELECT-Befehls:<br />

• Die Listen der gewünschten Spalten und der beteiligten Tabellen s<strong>in</strong>d Pflichtangaben,<br />

alle anderen Klauseln s<strong>in</strong>d optional.<br />

• Für die Verknüpfung mehrerer Tabellen gibt es e<strong>in</strong>en (traditionellen) direkten<br />

Weg und den (modernen) Weg über JOIN.<br />

• Die WHERE-Klausel ermöglicht komplexe Bed<strong>in</strong>gungen darüber, welche Datensätze<br />

abgefragt werden sollen.<br />

• Mit Gruppierung, Sortierung und Zusammenfassung gibt es weitere Möglichkeiten<br />

für Abfragen.<br />

15.10. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 138.<br />

Übung 1 – Allgeme<strong>in</strong>e Syntax<br />

Br<strong>in</strong>gen Sie die folgenden Bestandteile des SELECT-Befehls <strong>in</strong> die richtige Reihenfolge<br />

(es gibt Begriffe, die an zwei bzw. drei Stellen gehören):<br />

− DISTINCT − FROM − GROUP BY − HAVING −<br />

ORDER BY − SELECT − − − WHERE<br />

Übung 2 – Allgeme<strong>in</strong>e Syntax<br />

Welche der genannten Bestandteile e<strong>in</strong>es SELECT-Befehls s<strong>in</strong>d unbed<strong>in</strong>gt erforderlich?<br />

136


Übung 3 – Spaltenliste<br />

Übungen<br />

Welche der folgenden Spaltenlisten aus der Beispieldatenbank s<strong>in</strong>d richtig, welche<br />

nicht?<br />

1. SELECT * FROM Mitarbeiter;<br />

2. SELECT ID, Name FROM Mitarbeiter;<br />

3. SELECT ID, Name FROM Mitarbeiter, Abteilung;<br />

4. SELECT ID, Name, Kuerzel FROM Mitarbeiter, Abteilung;<br />

5. SELECT ab.ID, Name FROM Mitarbeiter, Abteilung ab;<br />

6. SELECT ab.ID, Name, Krz Kuerzel FROM Mitarbeiter, Abteilung ab;<br />

7. SELECT ab.ID, Name, Kuerzel Krz FROM Mitarbeiter, Abteilung ab;<br />

8. SELECT ab.ID, mi.Name, ab.Kuerzel FROM Mitarbeiter mi, Abteilung<br />

ab;<br />

Übung 4 – Spaltenliste<br />

Schreiben Sie für folgende Abfragen die Spalten und Tabellen auf.<br />

1. Zeige alle Informationen zu den Mitarbeitern.<br />

2. Zeige zu jedem Mitarbeiter Name, Vorname und Nummer der Abteilung.<br />

3. Zeige zu jedem Mitarbeiter Name, Vorname und Kuerzel der Abteilung.<br />

4. Zeige zu jedem Mitarbeiter ID, Name, Vorname sowie das Kennzeichen des<br />

Dienstwagens.<br />

Übung 5 – Suchbed<strong>in</strong>gungen<br />

Welche der folgenden Suchbed<strong>in</strong>gungen s<strong>in</strong>d richtig, welche nicht? Welche korrekten<br />

Bed<strong>in</strong>gungen liefern immer FALSE als Ergebnis?<br />

1. WHERE Name NOT = ’Meyer’;<br />

2. WHERE 1 = 2;<br />

3. WHERE NOT Name LIKE ’M%’;<br />

4. WHERE Geburtsdatum LIKE ’1980’;<br />

5. WHERE ID BETWEEN 20 AND 10;<br />

6. WHERE Mobil IS NULL;<br />

7. WHERE Name IS NULL;<br />

8. WHERE Name STARTING WITH ’L’ AND CONTAINING ’a’;<br />

9. WHERE ID IN (1, 3, ’A’);<br />

10. WHERE ID IN (1, 3, ’5’);<br />

137


Ausführliche SELECT-Struktur<br />

Übung 6 – Suchbed<strong>in</strong>gungen<br />

Formulieren Sie die folgenden Aussagen als Bed<strong>in</strong>gungen der WHERE-Klausel<br />

zur Tabelle Mitarbeiter.<br />

1. Der Vorname lautet 'Petra'.<br />

2. Der Name enthält die Zeichen 'mann'.<br />

3. Der Name beg<strong>in</strong>nt mit 'A', es handelt sich um Abteilung 8.<br />

4. Es ist ke<strong>in</strong>e Mobil-Nummer gespeichert.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 136.<br />

Lösung zu Übung 1 – Allgeme<strong>in</strong>e Syntax<br />

Richtig ist dies (mit Vervollständigung zu e<strong>in</strong>em Befehl):<br />

SELECT DISTINCT <br />

FROM <br />

WHERE <br />

GROUP BY <br />

HAVING <br />

ORDER BY ;<br />

Lösung zu Übung 2 – Allgeme<strong>in</strong>e Syntax<br />

SELECT FROM <br />

Lösung zu Übung 3 – Spaltenliste<br />

Richtig s<strong>in</strong>d 1, 2, 5, 7, 8.<br />

Falsch s<strong>in</strong>d 3, 4 (ID ist mehrdeutig), 6 (Spaltenname und -Alias <strong>in</strong> falscher Reihenfolge).<br />

Lösung zu Übung 4 – Spaltenliste<br />

138<br />

1. SELECT * FROM Mitarbeiter<br />

2. SELECT Name, Vorname, Abteilung_ID FROM Mitarbeiter<br />

3. SELECT Name, Vorname, Kuerzel FROM Mitarbeiter, Abteilung<br />

/* oder mit Tabellen-Alias: */


SELECT mi.Name, mi.Vorname, ab.Kuerzel FROM Mitarbeiter mi,<br />

Abteilung ab<br />

4. SELECT Name, Vorname, Dienstwagen.ID, Kennzeichen FROM<br />

Mitarbeiter, Dienstwagen<br />

/* auch e<strong>in</strong>heitlich mit Tabellen-Namen oder Tabellen-Alias<br />

möglich */<br />

Lösung zu Übung 5 – Suchbed<strong>in</strong>gungen<br />

1. Richtig.<br />

2. Richtig, immer FALSE: 1 ist immer ungleich 2.<br />

3. Falsch, das NOT gehört h<strong>in</strong>ter , also h<strong>in</strong>ter Name.<br />

4. Richtig, weil das Jahr laut ISO 8601 am Anfang steht.<br />

Übungen<br />

5. Richtig, immer FALSE: es gibt ke<strong>in</strong>e Zahl „größer/gleich 20“ und gleichzeitig<br />

„kle<strong>in</strong>er/gleich 10“.<br />

6. Richtig.<br />

7. Richtig, immer FALSE, weil der Name als Pflichtangabe niemals NULL se<strong>in</strong><br />

kann.<br />

8. Falsch, weil Name <strong>in</strong> der zweiten Bed<strong>in</strong>gung h<strong>in</strong>ter AND fehlt.<br />

9. Falsch, weil 'A' ke<strong>in</strong>e Zahl ist, aber zu ID bei IN e<strong>in</strong>e Liste von Zahlen gehört.<br />

10. Richtig, weil '5' automatisch als Zahl konvertiert wird.<br />

Lösung zu Übung 6 – Suchbed<strong>in</strong>gungen<br />

1. WHERE Vorname = ’Petra’;<br />

2. WHERE Name CONTAINING ’mann’;<br />

3. WHERE Name STARTING WITH ’A’ AND Abteilung_ID = 8;<br />

4. WHERE Mobil IS NULL;<br />

139


16. Funktionen (2)<br />

16.1. Funktionen für Zahlen . . . . . . . . . . . . . . . . . . . . . . . . 141<br />

16.2. Funktionen für Zeichenketten . . . . . . . . . . . . . . . . . . . 144<br />

16.3. Funktionen für Datums- und Zeitwerte . . . . . . . . . . . . . . 146<br />

16.4. Funktionen für logische und NULL-Werte . . . . . . . . . . . . 148<br />

16.5. Verschiedene Funktionen . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150<br />

16.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154<br />

Dieses Kapitel behandelt weitere Skalarfunktionen <strong>in</strong> Ergänzung zu den grundlegenden<br />

Funktionen 1 . Auch hier gelten die dort aufgeführten H<strong>in</strong>weise:<br />

• Jedes DBMS bietet eigene Funktionen sowie Varianten.<br />

• Die Klammern werden <strong>in</strong> den Beschreibungen oft nicht angegeben.<br />

• Die Beispiele benutzen e<strong>in</strong>e verkürzte Schreibweise, wobei der Zusatz [from<br />

fiktiv] als optional gekennzeichnet ist; für Firebird/Interbase ist er durch<br />

[from rdb$database] und für Oracle durch [from dual] zu ersetzen.<br />

SELECT 2 * 3 [from fiktiv];<br />

16.1. Funktionen für Zahlen<br />

Auch bei diesen weiteren Funktionen müssen Sie auf den genauen Datentyp<br />

(ganze Zahl oder Dezimalzahl und den Größenbereich) achten.<br />

POWER und SQRT – Potenzen und Wurzeln<br />

Mit POWER wird e<strong>in</strong>e beliebige Potenz oder Wurzel berechnet:<br />

POWER( , )<br />

1 Kapitel 14 auf Seite 109<br />

141


Funktionen (2)<br />

Sowohl für als auch für s<strong>in</strong>d nicht nur ganze positive Zahlen,<br />

sondern alle Zahlen zulässig. Mit Dezimalzahlen als werden (genau<br />

nach mathematischen Regeln) beliebige Wurzeln berechnet; <strong>in</strong> diesem Fall s<strong>in</strong>d<br />

als negative Zahlen unzulässig. Beispiele:<br />

SELECT POWER( 5, 3 ) [from fiktiv]; /* 125,000 */<br />

SELECT POWER( 5, 2.5) [from fiktiv]; /* 55,902 */<br />

SELECT POWER( 5, 0.5) [from fiktiv]; /* 2,236 also Wurzel aus 5 */<br />

SELECT POWER( 0.5, -3 ) [from fiktiv]; /* 8,000 also 3.Potenz zu 2 */<br />

SELECT POWER( 12.35, 1.5) [from fiktiv]; /* 43,401 */<br />

SELECT POWER(-12.35, 1.5) [from fiktiv]; /* expression evaluation not supported */<br />

SELECT POWER( 12.35,-1.5) [from fiktiv]; /* 0,023 */<br />

SELECT POWER(-12.35,-1.5) [from fiktiv]; /* expression evaluation not supported */<br />

Mit SQRT (= Square Root) gibt es für die Quadratwurzel e<strong>in</strong>e kürzere Schreibweise<br />

anstelle von POWER(x,0.5):<br />

SELECT SQRT(12.25) [from fiktiv]; /* 3,500 */<br />

EXP und LOG – Exponentialfunktion und Logarithmen<br />

Mit EXP wird die Exponentialfunktion im engeren S<strong>in</strong>ne bezeichnet, also mit der<br />

Eulerschen Zahl e als Basis.<br />

SELECT EXP(1) [from fiktiv]; /* 2,71828182845905 */<br />

Mit LOG(, ) wird umgekehrt e<strong>in</strong> Logarithmus bestimmt, mit LN<br />

der natürliche und mit LOG10 der dekadische Logarithmus.<br />

SELECT LOG(10, EXP(1)) [from fiktiv]; /* 2,30258509299405 */<br />

SELECT LOG(10, 10) [from fiktiv]; /* 1,000 */<br />

SELECT LN(10) [from fiktiv]; /* 2,30258509299405 */<br />

W<strong>in</strong>kelfunktionen<br />

Die trigonometrischen Funktionen arbeiten mit dem Bogenmaß (nicht mit e<strong>in</strong>er<br />

Grad-Angabe).<br />

SIN S<strong>in</strong>us<br />

COS Cos<strong>in</strong>us<br />

TAN Tangens<br />

COT Cotangens<br />

ASIN Arcuss<strong>in</strong>us als Umkehrfunktion des S<strong>in</strong>us<br />

142


ACOS Arcuscos<strong>in</strong>us als Umkehrfunktion des Cos<strong>in</strong>us<br />

ATAN Arcustangens als Umkehrfunktion des Tangens<br />

Funktionen für Zahlen<br />

Mit DEGREES wird e<strong>in</strong> Bogenmaß <strong>in</strong> Grad umgerechnet, mit RADIANS e<strong>in</strong> Gradmaß<br />

<strong>in</strong> das Bogenmaß.<br />

PI liefert die entsprechende Zahl und kann auch für die trigonometrischen Funktionen<br />

verwendet werden:<br />

SELECT SIN( PI()/6 ) [from fiktiv]; /* π/6 s<strong>in</strong>d 30°, also Ergebnis 0,5 */<br />

ABS, RAND, SIGN – verschiedene Funktionen<br />

Mit ABS wird der absolute Betrag der gegebenen Zahl zurückgegeben.<br />

SIGN liefert als H<strong>in</strong>weis auf das Vorzeichen der gegebenen Zahl e<strong>in</strong>en der Werte<br />

1, 0, –1 − je nachdem, ob die gegebene Zahl positiv, 0 oder negativ ist.<br />

SELECT SIGN(12.34), SIGN(0), SIGN(-5.67) [from fiktiv];<br />

/* Ergebnis: 1 0 -1 */<br />

RAND liefert e<strong>in</strong>e Zufallszahl im Bereich zwischen 0 und 1 (jeweils e<strong>in</strong>schließlich).<br />

Dies s<strong>in</strong>d aber ke<strong>in</strong>e echten Zufallszahlen, sondern Pseudozufallszahlen.<br />

Mit RAND() wird <strong>in</strong>nerhalb e<strong>in</strong>er Sitzung immer dieselbe Zufallszahl<br />

erzeugt.<br />

Mit e<strong>in</strong>er Komb<strong>in</strong>ation von RAND und FLOOR erhält man e<strong>in</strong>e „zufällige“ Folge<br />

ganzer Zahlen:<br />

FLOOR( + ( RAND() * - + 1 ) )<br />

Beispielsweise liefert die mehrfache Wiederholung der folgenden Abfrage diese<br />

Zahlen zwischen 7 und 12:<br />

SELECT FLOOR(7 + (RAND() * 6)) [from fiktiv];<br />

/* Ergebnisse: 10 9 9 7 8 7 9 9 10 12 usw. */<br />

Diese Funktion ist geeignet, um Datensätze mit SELECT <strong>in</strong> beliebiger Sortierung<br />

oder e<strong>in</strong>en zufällig ausgewählten Datensatz abzurufen:<br />

SELECT * FROM ORDER BY RAND();<br />

SELECT FIRST 1 * FROM ORDER BY RAND();<br />

143


Funktionen (2)<br />

16.2. Funktionen für Zeichenketten<br />

Auch zur Bearbeitung und Prüfung von Str<strong>in</strong>gs gibt es weitere Funktionen.<br />

Verknüpfen von Str<strong>in</strong>gs<br />

Zu den Standardverfahren || + CONCAT gibt es Ergänzungen.<br />

My<strong>SQL</strong> bietet mit CONCAT_WS e<strong>in</strong>e nützliche Erweiterung, bei der zwischen<br />

den Teiltexten e<strong>in</strong> Trennzeichen gesetzt wird.<br />

SPACE(n) − für MS-<strong>SQL</strong> und My<strong>SQL</strong> − erzeugt e<strong>in</strong>en Str<strong>in</strong>g, der aus n Leerzeichen<br />

besteht.<br />

REPEAT( , ) bei My<strong>SQL</strong> und REPLICATE( , ) bei MS-<strong>SQL</strong><br />

erzeugen e<strong>in</strong>e Zeichenkette, <strong>in</strong> der der n-mal wiederholt wird.<br />

Mit LPAD wird , sofern erforderlich, auf die gewünschte gebracht<br />

und dabei von l<strong>in</strong>ks mit bzw. Leerzeichen aufgefüllt. Mit RPAD wird von<br />

rechts aufgefüllt. MS-<strong>SQL</strong> kennt diese Funktionen nur für Access.<br />

LPAD ( , [ , ] )<br />

RPAD ( , [ , ] )<br />

Wenn der dadurch erzeugte Text zu lang wird, wird zuerst und notfalls<br />

auch abgeschnitten. Das erste Beispiel verwirrt zunächst:<br />

SELECT LPAD( CAST(12345 AS CHAR(8)), 10, ’0’) [from fiktiv]; -- also: ’0012345 ’<br />

Nanu, das s<strong>in</strong>d doch nur 7 statt 10 Ziffern? Ach so, zuerst wird mit CAST e<strong>in</strong> 8<br />

Zeichen langer Str<strong>in</strong>g erzeugt; dann ist nur noch Platz für 2 Nullen. Also muss es<br />

mit e<strong>in</strong>er dieser Varianten gehen:<br />

SELECT LPAD( CAST(12345 AS CHAR(5)), 10, ’0’) [from fiktiv]; -- also: ’0000012345’<br />

SELECT LPAD( 12345, 10, ’0’ ) [from fiktiv]; -- also: ’0000012345’<br />

Noch e<strong>in</strong> paar Beispiele:<br />

SELECT LPAD( ’Hilfe’, 10, ’-_/’) [from fiktiv]; -- also: ’-_/-_Hilfe’<br />

SELECT LPAD( ’Ich brauche Hilfe’, 10, ’-_/’) [from fiktiv]; -- also: ’Ich brauch’<br />

SELECT RPAD( ’Hilfe’, 10, ’-_/’) [from fiktiv]; -- also: ’Hilfe-_/-_’<br />

SELECT RPAD( ’Ich brauche Hilfe’, 10, ’-_/’) [from fiktiv]; -- also: ’Ich brauch’<br />

144


LEFT, RIGHT – Teile von Zeichenketten<br />

Funktionen für Zeichenketten<br />

Als Ergänzung zu SUBSTRING wird mit LEFT( , ) der l<strong>in</strong>ke Teil,<br />

also der Anfang e<strong>in</strong>es Textes mit der Länge ausgegeben. Ebenso erhält<br />

man mit RIGHT( , ) den rechten Teil, also das Ende e<strong>in</strong>es Textes.<br />

SELECT LEFT (’Abc Def Ghi’, 5) [from fiktiv]; /* Ergebnis: ’Abc D’ */<br />

SELECT RIGHT(’Abc Def Ghi’, 5) [from fiktiv]; /* Ergebnis: ’f Ghi’ */<br />

TRIM, LTRIM, RTRIM – Leerzeichen u. a. entfernen<br />

Mit der TRIM-Funktion werden bestimmte Zeichen − meistens Leerzeichen −<br />

am Anfang und/oder am Ende e<strong>in</strong>es Textes entfernt:<br />

TRIM( [ [ LEADING | TRAILING | BOTH ] [ ] FROM ] )<br />

Die Parameter werden wie folgt benutzt:<br />

• Es soll entfernt werden. Es kann sich um e<strong>in</strong> e<strong>in</strong>zelnes Zeichen, aber<br />

auch um e<strong>in</strong>en Text handeln.<br />

• Ohne diesen Parameter wird immer nach Leerzeichen gesucht.<br />

• Mit LEADING werden nur führende Zeichen geprüft, bei TRAILING nachfolgende<br />

und bei BOTH sowohl als auch. Ohne Angabe wird BOTH angenommen.<br />

Beispiele:<br />

SELECT TRIM( ’ Dies ist e<strong>in</strong> Text. ’ ) [from fiktiv];<br />

/* Ergebnis: ’Dies ist e<strong>in</strong> Text.’ */<br />

SELECT TRIM( LEADING ’a’ FROM ’abcde’ ) [from fiktiv]; /* Ergebnis: ’bcde’ */<br />

SELECT TRIM( TRAILING ’e’ FROM ’abcde’ ) [from fiktiv]; /* Ergebnis: ’abcd’ */<br />

SELECT TRIM( ’Test’ FROM ’Test als Test’) [from fiktiv]; /* Ergebnis: ’ als ’ */<br />

LTRIM (= Left-Trim) und RTRIM (= Right-Trim) s<strong>in</strong>d Kurzfassungen, bei denen<br />

Leerzeichen am Anfang bzw. am Ende entfernt werden; MS-<strong>SQL</strong> kennt nur diese<br />

beiden Kurzfassungen.<br />

Suchen und Ersetzen<br />

Mit POSITION wird der Anfang e<strong>in</strong>es Textes <strong>in</strong>nerhalb e<strong>in</strong>es anderen gesucht.<br />

POSITION( IN ) <strong>SQL</strong>-Standard<br />

POSITION( , [, ] ) nicht bei My<strong>SQL</strong><br />

LOCATE ( , [, ] ) bei My<strong>SQL</strong><br />

145


Funktionen (2)<br />

Die Bedeutung der Parameter dürfte offensichtlich se<strong>in</strong>:<br />

• ist der Text, <strong>in</strong> dem gesucht werden soll.<br />

• ist e<strong>in</strong> Text, der als Teil <strong>in</strong> gesucht wird.<br />

• Sofern angegeben ist, wird erst ab dieser Position <strong>in</strong>nerhalb <br />

gesucht. Wenn fehlt, wird ab Position 1 gesucht.<br />

• Die Funktion gibt die Startposition von <strong>in</strong>nerhalb von an.<br />

Wenn nicht gefunden wird, lautet das Ergebnis 0.<br />

Beispiele:<br />

SELECT POSITION( ’ch’, ’Ich suche Text’ ) [from fiktiv]; /* Ergebnis: 2 */<br />

SELECT POSITION( ’ch’, ’Ich suche Text’, 3 ) [from fiktiv]; /* Ergebnis: 7 */<br />

SELECT POSITION(’sch’, ’Ich suche Text’ ) [from fiktiv]; /* Ergebnis: 0 */<br />

Bei MS-<strong>SQL</strong> gibt es diese Funktionen nicht; stattdessen kann (mit abweichender<br />

Bedeutung) PATINDEX verwendet werden. Oracle bietet zusätzlich INSTR<br />

(= "<strong>in</strong> str<strong>in</strong>g") an.<br />

REPLACE dient zum Ersetzen e<strong>in</strong>es Teiltextes durch e<strong>in</strong>en anderen <strong>in</strong>nerhalb<br />

e<strong>in</strong>es Gesamttextes:<br />

REPLACE( , , )<br />

Die verschiedenen <strong>SQL</strong>-Dialekte verhalten sich unterschiedlich, ob NULL-Werte<br />

oder leere Zeichenketten zulässig s<strong>in</strong>d.<br />

SELECT REPLACE(’Ich suche Text’, ’ch’, ’sch’) [from fiktiv];<br />

/* Ergebnis: ’Isch susche Text’ */<br />

SELECT REPLACE(’Die liebe Seele’, ’e’, ’’) [from fiktiv];<br />

/* Ergebnis: ’Di lib Sl’ */<br />

REVERSE passt zwar nicht zu dem, was man <strong>in</strong> diesem Zusammenhang erwartet;<br />

aber auch diese Funktion ändert e<strong>in</strong>en vorhandenen Str<strong>in</strong>g, und zwar dadurch,<br />

dass die Reihenfolge aller Zeichen umgekehrt wird:<br />

SELECT REVERSE( ’Hilfe’ ) [from fiktiv]; /* Ergebnis: ’efliH’ */<br />

16.3. Funktionen für Datums- und Zeitwerte<br />

Bitte beachten Sie wiederum, dass die Datentypen je nach DBMS verschieden<br />

def<strong>in</strong>iert s<strong>in</strong>d und sich deshalb unterschiedlich verhalten.<br />

146


Differenzen bei Datum oder Uhrzeit<br />

Funktionen für Datums- und Zeitwerte<br />

Dafür gibt es vor allem die DATEDIFF-Funktion <strong>in</strong> unterschiedlicher Version:<br />

DATEDIFF ( , , ) /* bei MS-<strong>SQL</strong> oder Firebird */<br />

DATEDIFF ( , ) /* bei My<strong>SQL</strong> nur die Anzahl der Tage */<br />

Das Ergebnis ist vom gleichen Typ wie die gesuchte Differenz (also meistens e<strong>in</strong><br />

ganzzahliger Wert). Als gibt es die bekannten Varianten:<br />

YEAR | MONTH | DAY | HOUR | MINUTE | SECOND | MILLISECOND<br />

Beim Vergleich von Start- und Enddatum gilt:<br />

• Die Differenz ist positiv, wenn der zweite Wert später liegt als der erste.<br />

• Die Differenz ist 0, wenn beide Werte gleich s<strong>in</strong>d.<br />

• Die Differenz ist negativ, wenn der zweite Wert früher liegt als der erste.<br />

Bitte beachten Sie: Die DBMS verhalten sich unterschiedlich, ob die Datumsangaben<br />

verglichen werden oder der jeweilige Bestandteil. Beispielsweise kann das<br />

Ergebnis für beide der folgenden Prüfungen 1 lauten, obwohl die „echte“ Differenz<br />

im e<strong>in</strong>en Fall e<strong>in</strong> Tag, im anderen fast zwei Jahre s<strong>in</strong>d:<br />

DATEDIFF( YEAR, ’31.12.2008’, ’01.01.2009’ )<br />

DATEDIFF( YEAR, ’01.01.2008’, ’31.12.2009’ )<br />

Aufgabe: Bestimme die Anzahl der Tage seit dem letzten Schadensfall.<br />

SELECT DATEDIFF(DAY, MAX(Datum), CURRENT_DATE)<br />

FROM Schadensfall; /* Ergebnis: 49 */<br />

Aufgabe: Bestimme die Anzahl der M<strong>in</strong>uten seit Tagesbeg<strong>in</strong>n.<br />

SELECT DATEDIFF(MINUTE, CAST(’00:00’ AS TIME), CURRENT_TIME) [from fiktiv];<br />

/* Ergebnis: 967 */<br />

Datumsangaben können grundsätzlich auch per Subtraktion verglichen werden,<br />

weil „<strong>in</strong>tern“ häufig e<strong>in</strong> Tag gleich 1 ist. Darauf kann man sich aber nicht immer<br />

verlassen; und es ist schwierig, die Bruchteile e<strong>in</strong>es Tages zu berücksichtigen.<br />

Beispiel:<br />

SELECT (CURRENT_DATE - MAX(Datum)) FROM Schadensfall; /* Ergebnis: 49 */<br />

147


Funktionen (2)<br />

Werte für Datum oder Uhrzeit ändern<br />

Sehr häufig muss aus e<strong>in</strong>em vorhandenen Datum oder Uhrzeit e<strong>in</strong> neuer Wert<br />

berechnet werden. Der <strong>SQL</strong>-Standard sieht dazu die direkte Addition und Subtraktion<br />

vor:<br />

+ <br />

- <br />

+ <br />

steht für den gegebenen Wert, für den Zeitraum, der addiert<br />

oder subtrahiert werden soll.<br />

Aufgabe: Aus dem aktuellen Zeitwert '19.09.2009 16:10' wird e<strong>in</strong> neuer Wert<br />

bestimmt, der e<strong>in</strong>en halben Tag <strong>in</strong> der Zukunft liegt:<br />

SELECT CURRENT_TIMESTAMP + 0.5 [from fiktiv]; /* Ergebnis: ’20.09.2009 04:10:39’ */<br />

My<strong>SQL</strong> akzeptiert nur ganze Zahlen; deshalb ist explizit die Art des Intervalls anzugeben<br />

(siehe Dokumentation).<br />

Da das Umrechnen, also die Konvertierung von Zahlen <strong>in</strong> Datums- und Zeitwerte<br />

und umgekehrt für den Anwender umständlich ist, werden viele zusätzliche<br />

Funktionen bereitgestellt. Sehr verbreitet ist DATEADD:<br />

DATEADD( , , ) /* Firebird, MS-<strong>SQL</strong> */<br />

DATE_ADD( , INTERVAL ) /* My<strong>SQL</strong> */<br />

Aufgabe: Welche Versicherungsverträge laufen schon mehr als 10 Jahre?<br />

SELECT ID, Vertragsnummer, Abschlussdatum FROM Versicherungsvertrag<br />

WHERE DATEADD(YEAR, 10, Abschlussdatum)


COALESCE – Suche Wert ungleich NULL<br />

Funktionen für logische und NULL-Werte<br />

Die COALESCE-Funktion sucht <strong>in</strong> e<strong>in</strong>er Liste von Werten (bzw. Ausdrücken) den<br />

ersten, der nicht NULL ist. Wenn alle Werte NULL s<strong>in</strong>d, ist das Ergebnis zwangsläufig<br />

NULL.<br />

Aufgabe: Nenne zu jedem Mitarbeiter e<strong>in</strong>e Kontaktmöglichkeit: vorzugsweise<br />

Mobilnummer, dann Telefonnummer, dann Email-Adresse.<br />

SELECT Name, Vorname, COALESCE(Mobil, Telefon, Email) AS Kontakt FROM Mitarbeiter;<br />

Das Ergebnis überrascht zunächst, denn e<strong>in</strong>ige Mitarbeiter hätten danach ke<strong>in</strong>e<br />

Kontaktmöglichkeit. Zusammen mit IS NULL bei der WHERE-Klausel wird aber<br />

erläutert, dass e<strong>in</strong>e leere Zeichenkette ungleich NULL ist; bei diesen Mitarbeitern<br />

wird also e<strong>in</strong> leerer E<strong>in</strong>trag, aber nicht "nichts" angezeigt.<br />

NULLIF<br />

Bitte nehmen Sie diesen H<strong>in</strong>weis als Empfehlung, lieber NULL zu<br />

speichern als den leeren Str<strong>in</strong>g ’’.<br />

Die Funktion NULLIF vergleicht zwei Werte und liefert NULL zurück, wenn beide<br />

Werte gleich s<strong>in</strong>d; andernfalls liefert der erste Wert das Ergebnis.<br />

Aufgabe: Suche alle Versicherungsnehmer, die im Alter von 18 Jahren ihren<br />

Führersche<strong>in</strong> gemacht haben.<br />

SELECT Name, Vorname, EXTRACT(YEAR FROM Geburtsdatum) + 18 AS Jahr<br />

FROM Versicherungsnehmer<br />

WHERE Geburtsdatum IS NOT NULL<br />

AND NULLIF( EXTRACT(YEAR FROM Geburtsdatum) + 18,<br />

EXTRACT(YEAR FROM Fuehrersche<strong>in</strong>) ) IS NULL;<br />

NAME VORNAME JAHR<br />

Liebermann Maria 1988<br />

H<strong>in</strong>weis: Sowohl COALESCE als auch NULLIF s<strong>in</strong>d Kurzfassungen für spezielle<br />

Fallunterscheidungen mit CASE WHEN, <strong>in</strong> denen zusätzlich IS NULL e<strong>in</strong>gebunden<br />

wird − siehe dazu Nützliche Erweiterungen 2 .<br />

2 Kapitel 23 auf Seite 213<br />

149


Funktionen (2)<br />

16.5. Verschiedene Funktionen<br />

Auch wenn die Funktionen <strong>in</strong> diesem Abschnitt beim <strong>SQL</strong>-Standard v<strong>org</strong>esehen<br />

s<strong>in</strong>d, s<strong>in</strong>d sie nicht immer vorhanden. Wir verzichten deshalb wiederum auf nähere<br />

Erläuterungen und verweisen auf die jeweilige Dokumentation.<br />

ROW_NUMBER – Zeilen nummerieren<br />

Mit der ROW_NUMBER-Funktion werden die Zeilen im Ergebnis e<strong>in</strong>er Abfrage<br />

nach der Sortierung durchnummeriert. MS-<strong>SQL</strong> verwendet folgende Syntax:<br />

ROW_NUMBER() OVER ( [ ] )<br />

CURRENT_USER – der aktuelle Benutzer<br />

Mit CURRENT_USER (<strong>in</strong> der Regel ohne Klammer) wird der aktuelle Benutzername<br />

abgefragt. Dieser kann auch als V<strong>org</strong>abewert bei Neuaufnahmen automatisch<br />

<strong>in</strong> e<strong>in</strong>er Spalte e<strong>in</strong>er Tabelle e<strong>in</strong>getragen werden.<br />

SELECT CURRENT_USER [from fiktiv]; /* Ergebnis: SYSDBA */<br />

16.6. Zusammenfassung<br />

In diesem Kapitel lernten Sie weitere e<strong>in</strong>gebaute Funktionen kennen:<br />

• Für Zahlen gibt es viele mathematische Funktionen wie Potenzen, Wurzeln,<br />

Exponential- oder W<strong>in</strong>kelfunktionen.<br />

• Zeichenketten können auf vielfache Weise verknüpft oder zum Erstellen neuer<br />

Str<strong>in</strong>gs bearbeitet werden.<br />

• Datums- und Zeitwerte können im Detail verglichen und verrechnet werden.<br />

16.7. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 152.<br />

Übung 1 – Funktionen für Zahlen<br />

Geben Sie mit <strong>SQL</strong>-Funktionen die Formeln für die folgenden Aufgaben an. Es<br />

geht nur um die Formeln, nicht um e<strong>in</strong>en passenden SELECT-Befehl. Bei den Aufgaben<br />

1 bis 3 s<strong>in</strong>d jeweils zwei Lösungen möglich.<br />

150


1. Gegeben seien zwei Zahlen a, b. Berechnen Sie a² + 2ab + b².<br />

2. Berechnen Sie die Quadratwurzel von 216,09.<br />

Übungen<br />

3. E<strong>in</strong> Auftragsverwaltungsprogramm speichert <strong>in</strong> der Spalte Bestellung die<br />

Bestellwerte und <strong>in</strong> der Spalte Zahlung die E<strong>in</strong>zahlungen. Bestimmen Sie<br />

mit e<strong>in</strong>em e<strong>in</strong>zigen Ausdruck die Prüfung, ob der Kunde Guthaben oder<br />

Schulden (Zahlungsverpflichtung) hat.<br />

4. Bestimmen Sie den Betrag von Aufgabe 3 (unabhängig davon, ob es sich<br />

um Guthaben oder Schulden handelt).<br />

Übung 2 – Zufallszahlen<br />

Bestimmen Sie mit <strong>SQL</strong>-Funktionen die folgenden Zufallszahlen.<br />

1. e<strong>in</strong>e beliebige Zufallszahl zwischen 0 und 1<br />

2. e<strong>in</strong>e beliebige Zufallszahl zwischen 0 und 4<br />

3. e<strong>in</strong>e beliebige Zufallszahl zwischen 1 und 5<br />

4. e<strong>in</strong>e Zufallszahl als ganze Zahl zwischen 1 und 5<br />

5. e<strong>in</strong>e Zufallszahl als ganze Zahl zwischen 1 und 26<br />

6. e<strong>in</strong>en zufällig ausgewählten Buchstaben aus der Liste letters der Großbuchstaben<br />

'ABC...XYZ'<br />

Übung 3 – Zeichenketten bearbeiten<br />

Aus der Tabelle Mitarbeiter sollen die Werte ID und Abteilung_ID zusammengesetzt<br />

werden. Dabei soll die ID immer 4-stellig und die Abteilung_ID immer 2stellig<br />

geschrieben werden, bei Bedarf sollen die Teile mit '0' aufgefüllt werden.<br />

Übung 4 – Zeichenketten bearbeiten<br />

Geben Sie für die Tabelle Mitarbeiter e<strong>in</strong>e der vorhandenen Telefonnummern an<br />

− vorrangig die Mobilnummer; berücksichtigen Sie dabei auch, ob überhaupt<br />

e<strong>in</strong>e Nummer gespeichert ist.<br />

Übung 5 – Zeichenketten bearbeiten<br />

Zeigen Sie für die Spalte Kennzeichen der Tabelle Fahrzeug den jeweils zugehörigen<br />

Kreis an.<br />

Übung 6 – Zeichenketten bearbeiten<br />

In der Beschreibung für den Schadensfall mit der ID 6 ist das Kennzeichen 'RE-<br />

LM 903' durch 'RE-LM 902' zu berichtigen.<br />

151


Funktionen (2)<br />

Übung 7 – Datum und Zeit bearbeiten<br />

Die folgenden Teilaufgaben werden benutzt, um im Kapitel Testdaten erzeugen 3<br />

Geburtsdatum, Führersche<strong>in</strong>-Erwerb oder Abschluss des Versicherungsvertrags<br />

zufällig zu bestimmen.<br />

1. Bestimmen Sie aus dem Geburtsdatum das Datum des 18. Geburtstags.<br />

2. Bestimmen Sie (ausgehend vom 01.01.1950) Datumsangaben bis zum<br />

31.12.1990, bei denen der Monat und das Jahr per Zufallsfunktion bestimmt<br />

werden.<br />

3. Bestimmen Sie ebenso Datumsangaben, bei denen auch der Tag zufällig<br />

festgelegt wird und immer e<strong>in</strong> gültiges Datum erzeugt wird.<br />

4. Prüfen Sie, ob e<strong>in</strong> neuer Kunde se<strong>in</strong>en Führersche<strong>in</strong> bei Vertragsabschluss<br />

bereits drei Jahre besitzt. Sie können annehmen, dass sowohl Fuehrersche<strong>in</strong><br />

als auch Abschlussdatum <strong>in</strong> derselben Tabelle liegen; Schaltjahre können<br />

ignoriert werden.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 150.<br />

Lösung zu Übung 1 – Funktionen für Zahlen<br />

1. a. POWER(a, 2) + 2*a*b + POWER(b, 2)<br />

b. POWER(a + b, 2) als e<strong>in</strong>fachste B<strong>in</strong>omische Formel<br />

2. a. SQRT(216.09)<br />

b. POWER(216.09, 0.5)<br />

3. a. IF( SUM(Bestellung) > SUM(Zahlung)<br />

b. IF( SIGN( SUM(Bestellung) − SUM(Zahlung) ) = 1)<br />

4. ABS( SUM(Bestellung) − SUM(Zahlung) )<br />

Lösung zu Übung 2 – Zufallszahlen<br />

1. RAND()<br />

2. RAND() * 4<br />

3. 1 + RAND() * 4<br />

4. FLOOR( 1 + RAND() * 4 )<br />

5. FLOOR( 1 + RAND() * 25 )<br />

6. SUBSTRING( letters FROM FLOOR( 1 + RAND() * 25 ) FOR 1 )<br />

3 Kapitel 36 auf Seite 407<br />

152


Lösung zu Übung 3 – Zeichenketten bearbeiten<br />

SELECT LPAD( ID, 4, ’0’) + LPAD( Abteilung_ID, 2, ’0’) FROM Mitarbeiter;<br />

Lösung zu Übung 4 – Zeichenketten bearbeiten<br />

SELECT COALESCE( NULLIF(Mobil, ”), Telefon) AS Tel FROM Mitarbeiter;<br />

Übungen<br />

Erklärung: Wenn Mobil e<strong>in</strong>en Wert enthält, kommt bei NULLIF dieser Wert heraus;<br />

andernfalls wird immer NULL geliefert − entweder als Feld<strong>in</strong>halt oder als<br />

Ergebnis des Vergleichs mit der leeren Zeichenkette.<br />

Lösung zu Übung 5 – Zeichenketten bearbeiten<br />

SELECT SUBSTRING( Kennzeichen FROM 1 FOR Position(’-’, Kennzeichen) - 1)<br />

FROM Fahrzeug;<br />

Lösung zu Übung 6 – Zeichenketten bearbeiten<br />

UPDATE Schadensfall<br />

SET Beschreibung = REPLACE( Beschreibung, ’RE-LM 903’, ’RE-LM 902’ )<br />

WHERE ID = 6;<br />

Lösung zu Übung 7 – Datum und Zeit bearbeiten<br />

Die Teilaufgaben liefern diese E<strong>in</strong>zellösungen.<br />

1. DATEADD( YEAR, 18, Geburtsdatum )<br />

2. DATEADD( MONTH, FLOOR(RAND()*11), DATEADD( YEAR,<br />

FLOOR(RAND()*40), ’1950-01-01’))<br />

Es handelt sich um e<strong>in</strong>e verschachtelte Funktion: Zuerst wird das Jahr neu<br />

bestimmt, es wird maximal e<strong>in</strong> Wert von 40 addiert. Zu diesem Ergebnis<br />

wird der Monat neu bestimmt, es wird maximal 11 addiert. In manchen<br />

Fällen s<strong>in</strong>d e<strong>in</strong>zelne Angaben per CAST genauer zu def<strong>in</strong>ieren.<br />

3. DATEADD( DAY, FLOOR(RAND()*(41*365+10)), ’1950-01-01’)<br />

Es handelt sich um 41 Jahre zu je 365 Tagen, dazu 10 Schalttage.<br />

4. SELECT Fuehrersche<strong>in</strong>, Abschlussdatum FROM (Vertrag/Kunde)<br />

WHERE DATEDIFF( DAY, Fuehrersche<strong>in</strong>, Abschlussdatum) < 3*365<br />

H<strong>in</strong>weis: DATEDIFF(YEAR...) ist nicht unbed<strong>in</strong>gt geeignet, weil e<strong>in</strong> DBMS<br />

nicht die Term<strong>in</strong>e, sondern die Jahreszahlen vergleichen könnte.<br />

153


Funktionen (2)<br />

16.8. Siehe auch<br />

Bei Wikipedia gibt es fachliche Erläuterungen:<br />

• Potenzen 4<br />

• Exponentialfunktion 5<br />

• Trigonometrische Funktionen 6 und Bogenmaß 7<br />

• Betragsfunktion 8 − der absolute Betrag<br />

• Zufallszahl 9 gibt auch H<strong>in</strong>weise zu „Pseudozufallszahlen“.<br />

• B<strong>in</strong>omische Formeln 10<br />

4 http://de.wikipedia.<strong>org</strong>/wiki/Potenz%20(Mathematik)<br />

5 http://de.wikipedia.<strong>org</strong>/wiki/Exponentialfunktion<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/Trigonometrische%20Funktionen<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Bogenma%c3%9f<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Betragsfunktion<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Zufallszahl<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/B<strong>in</strong>omische%20Formel<br />

154


17. WHERE-Klausel im Detail<br />

17.1. E<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung . . . . . . . . . . . . . . . . . . . . . . . 155<br />

17.2. Mehrere Bed<strong>in</strong>gungen verknüpfen . . . . . . . . . . . . . . . . . 160<br />

17.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 162<br />

17.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163<br />

17.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165<br />

In diesem Kapitel werden die E<strong>in</strong>zelheiten der WHERE-Klausel genauer behandelt.<br />

Diese Angaben s<strong>in</strong>d vor allem für den SELECT-Befehl, aber auch für<br />

UPDATE und DELETE von Bedeutung.<br />

Die WHERE-Klausel ist (neben der Verknüpfung mehrerer Tabellen) der wichtigste<br />

Bestandteil des SELECT-Befehls: Je s<strong>org</strong>fältiger die Auswahlbed<strong>in</strong>gungen<br />

formuliert werden, desto genauer ist das Ergebnis der Abfrage.<br />

Neben den hier erläuterten Varianten bietet jedes DBMS noch andere, z. B.<br />

STARTING WITH oder SIMILAR.<br />

Die Beispiele beziehen sich auf den Anfangsbestand der Beispieldatenbank; auf<br />

die Ausgabe der selektierten Datensätze wird verzichtet. Anstelle konstanter<br />

Werte können auch passende Ausdrücke angegeben werden, z. B. Funktionen<br />

oder Unterabfragen.<br />

Bitte probieren Sie alle Beispiele aus und nehmen Sie Änderungen vor, um die<br />

Auswirkungen zu erkennen. Dazu gehört möglichst auch die Umkehrung der<br />

Auswahl mit bzw. ohne NOT. Wie im Kapitel Ausführliche SELECT-Struktur zur<br />

WHERE-Klausel angegeben, gehört das NOT vor e<strong>in</strong>e Suchbed<strong>in</strong>gung, vor e<strong>in</strong>en<br />

Vergleichsoperator oder vor den Parameter-Namen wie BETWEEN.<br />

17.1. E<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung<br />

17.1.1. Größenvergleich zweier Werte<br />

Der e<strong>in</strong>fachste Weg ist der direkte Vergleich zweier Werte, nämlich der Inhalt e<strong>in</strong>er<br />

Spalte mit e<strong>in</strong>em konstanten Wert. Dies ist möglich mit den folgenden Vergleichsoperatoren,<br />

und zwar für alle Datentypen, die verglichen werden können<br />

− Zahlen, Zeichenketten, Datumsangaben.<br />

155


WHERE-Klausel im Detail<br />

= < > = <br />

Beispiele:<br />

Aufgabe: Suche e<strong>in</strong>en Datensatz, bei dem der Wert <strong>in</strong> der Spalte ID gleich ist<br />

zu e<strong>in</strong>em v<strong>org</strong>egebenen Wert.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE ID = 10;<br />

Aufgabe: Suche Datensätze, bei denen der Name kle<strong>in</strong>er als 'B' ist, also mit 'A'<br />

anfängt.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Name < ’B’;<br />

Aufgabe: Suche Führersche<strong>in</strong>-Neul<strong>in</strong>ge.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Fuehrersche<strong>in</strong> >= ’01.01.2007’;<br />

Aufgabe: Suche Fahrzeugtypen mit kurzer Bezeichnung.<br />

SELECT * FROM Fahrzeugtyp<br />

WHERE Char_Length(Bezeichnung)


17.1.3. LIKE – Ähnlichkeiten (1)<br />

E<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung<br />

Die LIKE-Bed<strong>in</strong>gung vergleicht Zeichenketten „ungenau“: Der gesuchte Text soll<br />

<strong>in</strong> e<strong>in</strong>er Spalte enthalten se<strong>in</strong>; dazu werden „Wildcards“ benutzt: Der '_' steht für<br />

e<strong>in</strong> e<strong>in</strong>zelnes Zeichen, das an der betreffenden Stelle vorkommen kann. Das Prozentzeichen<br />

'%' steht für e<strong>in</strong>e beliebige Zeichenkette mit 0 oder mehr Zeichen.<br />

Diese Bed<strong>in</strong>gung wird vor allem <strong>in</strong> zwei Situationen gerne benutzt:<br />

• Der Suchbegriff ist sehr lang; dem Anwender soll es genügen, den Anfang e<strong>in</strong>zugeben.<br />

• Der Suchbegriff ist nicht genau bekannt (z. B. nicht richtig lesbar).<br />

Beispiele:<br />

Aufgabe: Der Ortsname beg<strong>in</strong>nt nicht mit 'B'; der Inhalt dah<strong>in</strong>ter ist beliebig.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Ort NOT LIKE ’B%’;<br />

Aufgabe: Der Ortsname enthält irgendwo 'alt' mit beliebigem Inhalt davor<br />

und dah<strong>in</strong>ter.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Ort LIKE ’%alt%’;<br />

Aufgabe: Der Anfangsbuchstabe des Namens ist unklar, aber danach folgen<br />

die Buchstaben 'ei' und noch etwas mehr.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Name LIKE ’_ei%’;<br />

Schwierig wird es, wenn e<strong>in</strong>es der Wildcard-Zeichen Teil des Suchbegriffs se<strong>in</strong><br />

soll. Dann ist dem LIKE-Parameter mitzuteilen, dass '%' bzw. '_' als „echtes“ Zeichen<br />

zu verstehen ist. Das geschieht dadurch, dass e<strong>in</strong> spezielles Zeichen davor<br />

gesetzt und als „ESCAPE-Zeichen“ angegeben wird:<br />

Aufgabe: Innerhalb der Beschreibung kommt die Zeichenfolge '10%' vor.<br />

SELECT * FROM Schadensfall<br />

WHERE Beschreibung LIKE ’%10\%%’ ESCAPE ’\’;<br />

Das erste und das letzte Prozentzeichen stehen dafür, dass vorher und nachher<br />

beliebige Inhalte möglich s<strong>in</strong>d. Das mittlere Prozentzeichen wird mit dem<br />

Escape-Zeichen '\' verbunden und ist damit Teil der gesuchten Zeichenfolge.<br />

Diese Angabe '\%' ist als e<strong>in</strong> Zeichen zu verstehen.<br />

Vergleichen Sie das Ergebnis, wenn der ESCAPE-Parameter weggelassen wird<br />

oder wenn e<strong>in</strong>es oder mehrere der Sonderzeichen im LIKE-Parameter fehlen.<br />

157


WHERE-Klausel im Detail<br />

17.1.4. CONTAINS u. a. – Ähnlichkeiten (2)<br />

E<strong>in</strong> Problem des LIKE-Parameters ist die Verwendung der Wildcard-Zeichen '%'<br />

und '_', die man gerne vergisst oder (wie im letzten Beispiel) nicht genau genug<br />

beachtet. Deshalb gibt es verschiedene Vere<strong>in</strong>fachungen.<br />

CONTAINS − <strong>in</strong> Firebird CONTAINING − prüft, ob e<strong>in</strong>e Zeichenkette im Feld<strong>in</strong>halt<br />

enthalten ist.<br />

SELECT * FROM Schadensfaelle<br />

WHERE Beschreibung CONTAINS ’10%’;<br />

Teil der Beschreibung ist die Zeichenkette '10%'<br />

Bitte prüfen Sie <strong>in</strong> der Beschreibung Ihres DBMS, welche Möglichkeiten für die<br />

Suche nach Ähnlichkeiten außerdem angeboten werden.<br />

17.1.5. IS NULL – null-Werte prüfen<br />

Wie schon bei den relationalen Datenbanken besprochen, haben NULL-Werte<br />

e<strong>in</strong>e besondere Bedeutung. Mit den folgenden beiden Abfragen werden nicht<br />

alle Datensätze gefunden:<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE Mobil ”;<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE Mobil = ”;<br />

8 Mitarbeiter mit Mobil-Nummer<br />

10 Mitarbeiter ohne Mobil-Nummer<br />

Nanu, es gibt doch 28 Mitarbeiter; wo s<strong>in</strong>d die übrigen geblieben? Für diese Fälle<br />

gibt es mit IS NULL e<strong>in</strong>e spezielle Abfrage:<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE Mobil IS NULL;<br />

10 Mitarbeiter ohne Angabe<br />

Der Vollständigkeit halber sei darauf h<strong>in</strong>gewiesen, dass die folgende Abfrage tatsächlich<br />

die richtige Gegenprobe liefert.<br />

158<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE Mobil IS NOT NULL;<br />

18 Mitarbeiter mit irgende<strong>in</strong>er Angabe (auch mit "leerer" Angabe)


E<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung<br />

Die folgende Abfrage liefert e<strong>in</strong>e leere Ergebnismenge zurück, weil NULL eben<br />

ke<strong>in</strong> Wert ist.<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE Mobil = NULL;<br />

Es gibt ke<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung, die alle Datensätze ohne explizite Mobil-<br />

Angabe auf e<strong>in</strong>mal angibt. Es gibt nur die Möglichkeit, die beiden Bed<strong>in</strong>gungen<br />

"IS NULL" und "ist leer" zu komb<strong>in</strong>ieren:<br />

SELECT ID, Name, Vorname, Mobil<br />

FROM Mitarbeiter<br />

WHERE ( Mobil IS NULL ) OR ( Mobil = ” );<br />

20 Mitarbeiter ohne ausdrückliche Angabe<br />

Beachten Sie auch bei "WHERE ... IS [NOT] NULL" die Bedeutung von NULL:<br />

• Bei Zeichenketten ist zu unterscheiden zwischen dem „leeren“ Str<strong>in</strong>g und dem<br />

NULL-Wert.<br />

• Bei Zahlen ist zu unterscheiden zwischen der Zahl '0' (null) und dem NULL-<br />

Wert.<br />

• Bei Datumsangaben ist zu unterscheiden zwischen e<strong>in</strong>em vorhandenen Datum<br />

und dem NULL-Wert; e<strong>in</strong> Datum, das der Zahl 0 entspräche, gibt es nicht.<br />

(Man könnte allenfalls das kle<strong>in</strong>ste mögliche Datum wie '01.01.0100' benutzen,<br />

aber dies ist bereits e<strong>in</strong> Datum.)<br />

17.1.6. IN – genauer Vergleich mit e<strong>in</strong>er Liste<br />

Der IN-Parameter vergleicht, ob der Inhalt e<strong>in</strong>er Spalte <strong>in</strong> der angegebenen Liste<br />

enthalten ist. Die Liste kann mit beliebigen Datentypen arbeiten.<br />

Aufgabe: Hole die Liste aller Fahrzeuge, deren Typen als „VW-Kle<strong>in</strong>wagen“ registriert<br />

s<strong>in</strong>d.<br />

SELECT * FROM Fahrzeug<br />

WHERE Fahrzeugtyp_ID IN (1, 2);<br />

Aufgabe: Suche nach e<strong>in</strong>em Unfall Fahrzeuge mit e<strong>in</strong>er von mehreren möglichen<br />

Farben.<br />

SELECT * FROM Fahrzeug<br />

WHERE Farbe IN (’ocker’, ’gelb’);<br />

Vor allem das erste Beispiel wird sehr oft mit e<strong>in</strong>er Unterabfrage versehen; vergleichen<br />

Sie dazu auch den folgenden Abschnitt zu EXISTS.<br />

159


WHERE-Klausel im Detail<br />

Aufgabe: Hole die Liste aller Fahrzeuge vom Typ „Volkswagen“.<br />

SELECT * FROM Fahrzeug<br />

WHERE Fahrzeugtyp_ID IN<br />

( SELECT ID FROM Fahrzeugtyp<br />

WHERE Hersteller_ID = 1 );<br />

Dabei wird zuerst mit der Unterabfrage e<strong>in</strong>e Liste aller Fahrzeugtypen-IDs für<br />

den Hersteller 1 (= Volkswagen) zusammengestellt; diese wird dann für den Vergleich<br />

über den IN-Parameter benutzt.<br />

17.1.7. EXISTS – schneller Vergleich mit e<strong>in</strong>er Liste<br />

Im Gegensatz zu den anderen Parametern der WHERE-Klausel arbeitet der<br />

EXISTS-Parameter nicht mit fest v<strong>org</strong>egebenen Werten, sondern nur mit dem<br />

Ergebnis e<strong>in</strong>er Abfrage, also e<strong>in</strong>er Unterabfrage. Das letzte Beispiel zum IN-<br />

Parameter kann auch so formuliert werden:<br />

SELECT * FROM Fahrzeug fz<br />

WHERE EXISTS<br />

( SELECT * FROM Fahrzeugtyp ft<br />

WHERE ft.Hersteller_ID = 1<br />

AND fz.Fahrzeugtyp_ID = ft.ID );<br />

Liste aller Fahrzeuge vom Typ 'Volkswagen'<br />

Zu jedem Datensatz aus der Tabelle Fahrzeug wird zu dieser Fahrzeugtyp_ID<br />

e<strong>in</strong>e Unterabfrage aus den Fahrzeugtypen erstellt: Wenn es dort e<strong>in</strong>en Datensatz<br />

mit passender ID und Hersteller-ID 1 (= Volkswagen) gibt, gehört der Fahrzeug-<br />

Datensatz zur Auswahl, andernfalls nicht.<br />

Da Unterabfragen zuerst ausgeführt werden, wird e<strong>in</strong>e EXISTS-Prüfung <strong>in</strong> aller<br />

Regel schneller erledigt als die entsprechende IN-Prüfung: Bei EXISTS handelt<br />

es sich um e<strong>in</strong>e Feststellung „ist überhaupt etwas vorhanden“; bei IN dagegen<br />

muss e<strong>in</strong> exakter Vergleich mit allen Werten e<strong>in</strong>er Liste durchgeführt werden. Bei<br />

unserer kle<strong>in</strong>en Beispieldatenbank spielt das natürlich ke<strong>in</strong>e Rolle, aber bei e<strong>in</strong>er<br />

„echten“ Datenbank mit Millionen von E<strong>in</strong>trägen schon.<br />

17.2. Mehrere Bed<strong>in</strong>gungen verknüpfen<br />

Bei der WHERE-Klausel geht es darum festzustellen, ob e<strong>in</strong> Datensatz Teil des<br />

Abfrageergebnisses ist oder nicht; bei der handelt sich also<br />

um e<strong>in</strong>en booleschen Ausdruck, d. h. e<strong>in</strong>en Ausdruck, der e<strong>in</strong>en der booleschen<br />

Werte WAHR oder FALSCH − TRUE bzw. FALSE − als Ergebnis hat. Nur bei e<strong>in</strong>fa-<br />

160


Mehrere Bed<strong>in</strong>gungen verknüpfen<br />

chen Abfragen genügt dazu e<strong>in</strong>e e<strong>in</strong>zelne Bed<strong>in</strong>gung; meistens müssen mehrere<br />

Bed<strong>in</strong>gungen verknüpft werden (wie beim letzten Beispiel unter IS NULL).<br />

Dazu gibt es die booleschen Operatoren NOT, AND, OR.<br />

NOT als Negation<br />

Dies liefert die Umkehrung: aus TRUE wird FALSE, aus FALSE wird TRUE.<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE NOT (Fuehrersche<strong>in</strong> >= ’01.01.2007’);<br />

AND als Konjunktion<br />

Siehe oben: ke<strong>in</strong>e Führersche<strong>in</strong>-Neul<strong>in</strong>ge<br />

E<strong>in</strong>e Bed<strong>in</strong>gung, die durch e<strong>in</strong>e AND-Verknüpfung gebildet wird, ist genau dann<br />

TRUE, wenn beide (bzw. alle) Bestandteile TRUE s<strong>in</strong>d.<br />

SELECT ID, Name, Vorname, PLZ, Ort<br />

FROM Versicherungsnehmer<br />

WHERE PLZ BETWEEN ’45000’ AND ’45999’<br />

AND Name < ’K’;<br />

Die nach Alphabet erste Hälfte der Versicherungsnehmer e<strong>in</strong>es PLZ-Bereichs<br />

OR als Adjunktion<br />

E<strong>in</strong>e Bed<strong>in</strong>gung, die durch e<strong>in</strong>e OR-Verknüpfung gebildet wird, ist genau dann<br />

TRUE, wenn m<strong>in</strong>destens e<strong>in</strong> Bestandteil TRUE ist; dabei ist es gleichgültig, ob<br />

die anderen Bestandteile TRUE oder FALSE s<strong>in</strong>d.<br />

SELECT ID, Name, Vorname, PLZ, Ort<br />

FROM Versicherungsnehmer<br />

WHERE PLZ BETWEEN ’45000’ AND ’45999’<br />

OR Name < ’K’;<br />

Die nach Alphabet erste Hälfte der Versicherungsnehmer und alle e<strong>in</strong>es PLZ-Bereichs<br />

Bitte beachten Sie, dass der normale Sprachgebrauch „alle . . . und alle . . . “ sagt.<br />

Geme<strong>in</strong>t ist nach logischen Begriffen aber, dass erfüllt se<strong>in</strong> muss<br />

ODER ODER BEIDE.<br />

XOR als Kontravalenz<br />

E<strong>in</strong>e Bed<strong>in</strong>gung, die durch e<strong>in</strong>e XOR-Verknüpfung gebildet wird, ist genau dann<br />

TRUE, wenn e<strong>in</strong> Bestandteil TRUE ist, aber der andere Bestandteil FALSE ist −<br />

„ausschließendes oder“ bzw. „entweder − oder“. Diese Verknüpfung gibt es selten,<br />

z. B. bei My<strong>SQL</strong>; hier wird es der Vollständigkeit halber erwähnt.<br />

161


WHERE-Klausel im Detail<br />

My<strong>SQL</strong>-Quelltext<br />

SELECT ID, Name, Vorname, PLZ, Ort<br />

FROM Versicherungsnehmer<br />

WHERE PLZ BETWEEN ’45000’ AND ’45999’<br />

XOR Name < ’K’;<br />

Die nach Alphabet erste Hälfte der Versicherungsnehmer oder alle e<strong>in</strong>es PLZ-Bereichs<br />

Bitte beachten Sie, dass hier der normale Sprachgebrauch „oder“ sagt und „entweder<br />

− oder“ geme<strong>in</strong>t ist.<br />

Anstelle von XOR kann immer e<strong>in</strong>e Komb<strong>in</strong>ation verwendet werden:<br />

( AND ( NOT ) )<br />

OR ( AND ( NOT ) )<br />

Klammern benutzen oder weglassen?<br />

Bereits im Kapitel „Ausführliche SELECT-Struktur“ steht die Hierarchie:<br />

• NOT ist die engste Verb<strong>in</strong>dung und wird vorrangig ausgewertet.<br />

• AND ist die nächststärkere Verb<strong>in</strong>dung und wird danach ausgewertet.<br />

• OR ist die schwächste Verb<strong>in</strong>dung und wird zuletzt ausgewertet.<br />

• Was <strong>in</strong> Klammern steht, wird vor allem anderen ausgewertet.<br />

Bitte setzen Sie im folgenden Beispiel Klammern an anderen Stellen oder streichen<br />

Sie Klammern, und vergleichen Sie die Ergebnisse.<br />

SELECT ID, Name, Vorname, PLZ, Ort FROM Versicherungsnehmer<br />

WHERE NOT ( PLZ BETWEEN ’45000’ AND ’45999’<br />

AND ( Name LIKE ’B%’<br />

OR Name LIKE ’K%’<br />

OR NOT Name CONTAINING ’ei’<br />

)<br />

)<br />

ORDER BY PLZ, Name;<br />

Es werden ziemlich unterschiedliche Ergebnisse kommen. Es empfiehlt sich deshalb,<br />

an allen s<strong>in</strong>nvollen Stellen Klammern zu setzen − auch dort, wo sie nicht<br />

nötig s<strong>in</strong>d − und das, was zusammengehört, durch E<strong>in</strong>rückungen zu gliedern.<br />

17.3. Zusammenfassung<br />

In diesem Kapitel lernten wir neben dem Vergleich von Werten viele Möglichkeiten<br />

kennen, mit denen Bed<strong>in</strong>gungen für Abfragen festgelegt werden können:<br />

162


• Mit BETWEEN . . . AND werden Werte <strong>in</strong>nerhalb e<strong>in</strong>es Bereichs geprüft.<br />

Übungen<br />

• Mit LIKE und CONTAINS werden Werte gesucht, die mit v<strong>org</strong>egebenen Werten<br />

teilweise übere<strong>in</strong>stimmen.<br />

• Mit IS NULL werden null-Werte gesucht.<br />

• Mit IN und EXISTS werden Spaltenwerte mit e<strong>in</strong>er Liste verglichen.<br />

Mit AND, OR, NOT werden Bed<strong>in</strong>gungen zusammengefasst.<br />

17.4. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 164.<br />

Bei den folgenden Aufgaben kommt es nur auf die WHERE-Klausel an; Sie dürfen<br />

e<strong>in</strong> SELECT „mit allen Spalten“ benutzen.<br />

Übung 1 – Auswahl nach Zeichenketten<br />

Suchen Sie alle Versicherungsnehmer, die folgenden Bed<strong>in</strong>gungen entsprechen:<br />

• Der erste Buchstabe des Nachnamens ist nicht bekannt, der zweite ist e<strong>in</strong> 'r'.<br />

• Der Vorname enthält e<strong>in</strong> 'a'.<br />

• Die Postleitzahl gehört zum Bereich Essen (PLZ 45...).<br />

Übung 2 – Auswahl nach Datumsbereich<br />

Suchen Sie alle Versicherungsnehmer, die <strong>in</strong> den Jahren 1967 bis 1970 ihren<br />

18. Geburtstag hatten.<br />

Übung 3 – Auswahl nach Ähnlichkeit<br />

Zeigen Sie alle Schadensfälle an, bei denen <strong>in</strong> der Beschreibung auf e<strong>in</strong>e prozentuale<br />

Angabe h<strong>in</strong>gewiesen wird.<br />

Übung 4 – Auswahl für unbekannte Werte<br />

Suchen Sie die Dienstwagen, die ke<strong>in</strong>em Mitarbeiter persönlich zugeordnet s<strong>in</strong>d.<br />

H<strong>in</strong>weis: Die Prüfung „Mitarbeiter ohne Dienstwagen“ ist komplizierter; das dafür<br />

erforderliche OUTER JOIN wird erst später behandelt.<br />

163


WHERE-Klausel im Detail<br />

Übung 5 – Bed<strong>in</strong>gungen verknüpfen<br />

Zeigen Sie alle Mitarbeiter der Abteilungen „Vertrieb“ (= ’Vert’) und „Ausbildung“<br />

(= ’Ausb’) an.<br />

H<strong>in</strong>weis: Bestimmen Sie zunächst die IDs der gesuchten Abteilungen und benutzen<br />

Sie das Ergebnis für die eigentliche Abfrage.<br />

Übung 6 – Bed<strong>in</strong>gungen verknüpfen<br />

Gesucht werden die Versicherungsverträge für Haftpflicht (= 'HP') und Teilkasko<br />

(= 'TK'), die m<strong>in</strong>destens seit dem Ende des Jahres 1980 bestehen und aktuell<br />

nicht mit dem m<strong>in</strong>imalen Prämiensatz berechnet werden.<br />

H<strong>in</strong>weis: Tragen Sie ausnahmsweise nur die notwendigen Klammern e<strong>in</strong>, nicht<br />

alle s<strong>in</strong>nvollen.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 163.<br />

Lösung zu Übung 1 – Auswahl nach Zeichenketten<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE Name LIKE ’_r%’ AND Vorname LIKE ’%a%’<br />

AND PLZ STARTING WITH ’45’ /* oder: */<br />

PLZ LIKE ’45%’;<br />

Lösung zu Übung 2 – Auswahl nach Datumsbereich<br />

SELECT * FROM Versicherungsnehmer<br />

WHERE DATEADD(YEAR, 18, Geburtsdatum) BETWEEN ’01.01.1967’ AND ’31.12.1970’;<br />

Lösung zu Übung 3 – Auswahl nach Ähnlichkeit<br />

SELECT * FROM Schadensfall<br />

WHERE Beschreibung LIKE ’%\%%’ ESCAPE ’\’;<br />

Lösung zu Übung 4 – Auswahl für unbekannte Werte<br />

164<br />

SELECT * FROM Dienstwagen<br />

WHERE Mitarbeiter_ID IS NULL;


Lösung zu Übung 5 – Bed<strong>in</strong>gungen verknüpfen<br />

SELECT * FROM Mitarbeiter<br />

WHERE Abteilung_ID IN (<br />

SELECT ID FROM Abteilung<br />

WHERE Kuerzel IN (’Vert’, ’Ausb’) );<br />

Lösung zu Übung 6 – Bed<strong>in</strong>gungen verknüpfen<br />

SELECT * FROM Versicherungsvertrag<br />

WHERE (Art = ’HP’ or Art = ’TK’)<br />

AND Abschlussdatum 30;<br />

17.5. Siehe auch<br />

Dieses Kapitel verweist auf die folgenden Kapitel:<br />

• Funktionen 1<br />

• Unterabfragen 2<br />

• Ausführliche SELECT-Struktur 3<br />

• Relationale Datenbanken 4<br />

• OUTER JOIN 5<br />

Bei Wikipedia gibt es weitere fachliche H<strong>in</strong>weise:<br />

• Wildcards 6<br />

• ESCAPE-Zeichen 7<br />

• Boolesche Operatoren 8<br />

Negation 9 – Konjunktion 10 – Adjunktion 11 – Kontravalenz 12<br />

1 Kapitel 14 auf Seite 109<br />

2 Kapitel 26 auf Seite 251<br />

3 Kapitel 15 auf Seite 129<br />

4 Kapitel 4 auf Seite 17<br />

5 Kapitel 21 auf Seite 191<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/Wildcard%20(Informatik)<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Escape-Sequenz<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Boolescher%20Operator<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Negation%23Logik<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/Konjunktion%20(Logik)<br />

11 http://de.wikipedia.<strong>org</strong>/wiki/Disjunktion<br />

12 http://de.wikipedia.<strong>org</strong>/wiki/Disjunktion<br />

Siehe auch<br />

165


18. Mehrere Tabellen<br />

18.1. Schreibweisen bei mehreren Tabellen . . . . . . . . . . . . . . . 167<br />

18.2. Mit Hilfe von WHERE – der traditionelle Weg . . . . . . . . . . 168<br />

18.3. JOINs – der moderne Weg . . . . . . . . . . . . . . . . . . . . . . . 168<br />

18.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 169<br />

18.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169<br />

E<strong>in</strong> besonderes Merkmal von relationalen Datenbanken und damit von <strong>SQL</strong><br />

ist, dass die Informationen fast immer über mehrere Tabellen verteilt s<strong>in</strong>d und<br />

bei Abfragen <strong>in</strong> e<strong>in</strong>er geme<strong>in</strong>samen Ergebnismenge zusammengeführt werden<br />

müssen. Dieses Kapitel gibt e<strong>in</strong>en Überblick über die Möglichkeiten dazu; E<strong>in</strong>zelheiten<br />

stehen <strong>in</strong> den folgenden Kapiteln.<br />

18.1. Schreibweisen bei mehreren Tabellen<br />

Bitte beachten Sie bei allen Befehlen, die mehrere Tabellen verwenden (das s<strong>in</strong>d<br />

zwangsläufig nur SELECT-Befehle):<br />

• Wenn e<strong>in</strong> Spaltenname <strong>in</strong> Bezug auf den gesamten <strong>SQL</strong>-Befehl e<strong>in</strong>deutig ist,<br />

genügt dieser Name.<br />

• Tritt e<strong>in</strong> Spaltenname mehrfach auf (wie ID), dann muss der Tabellenname<br />

vorangesetzt werden; der Spaltenname wird nach e<strong>in</strong>em Punkt angefügt.<br />

SELECT Personalnummer AS MitNr, Name, Vorname,<br />

Dienstwagen.ID, Kennzeichen, Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter, Dienstwagen;<br />

• Wegen der Übersichtlichkeit wird die Tabelle meistens auch dann bei jeder<br />

Spalte angegeben, wenn es wegen der ersten Regel nicht erforderlich wäre.<br />

SELECT Mitarbeiter.Personalnummer AS MitNr,<br />

Mitarbeiter.Name, Mitarbeiter.Vorname,<br />

Dienstwagen.ID AS DIW, Dienstwagen.Kennzeichen,<br />

Dienstwagen.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter, Dienstwagen;<br />

167


Mehrere Tabellen<br />

• Anstelle des Namens e<strong>in</strong>er Tabelle kann überall auch e<strong>in</strong> Tabellen-Alias benutzt<br />

werden; dieser muss e<strong>in</strong>mal h<strong>in</strong>ter ihrem Namen (<strong>in</strong> der FROM- oder <strong>in</strong><br />

der JOIN-Klausel) angegeben werden.<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi, Dienstwagen dw;<br />

Alle diese Befehle für „Liste der Mitarbeiter mit Dienstwagen“ s<strong>in</strong>d gleichwertig.<br />

Zu empfehlen ist die vollständige Schreibweise des vorigen Beispiels mit Alias.<br />

E<strong>in</strong> ähnlicher Befehl unter Verwendung der JOIN-Klausel sieht dann so aus:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

JOIN Dienstwagen dw ON mi.ID = dw.Mitarbeiter_ID;<br />

Der Alias ist nur für den betreffenden <strong>SQL</strong>-Befehl gültig. E<strong>in</strong> und dieselbe Tabelle<br />

kann mal als 'a', dann als 'mi' oder auch als 'xyz' bezeichnet werden. Wegen des<br />

leichteren Verständnisses s<strong>in</strong>d aussagefähige Kürzel s<strong>in</strong>nvoll; auch deshalb s<strong>in</strong>d<br />

sie im Kapitel Tabellenstruktur der Beispieldatenbank 1 angegeben.<br />

18.2. Mit Hilfe von WHERE – der traditionelle Weg<br />

Beim e<strong>in</strong>fachsten Verfahren, mehrere Tabellen gleichzeitig abzufragen, stehen<br />

alle Tabellen <strong>in</strong> der FROM-Klausel; die WHERE-Klausel enthält neben den Auswahlbed<strong>in</strong>gungen<br />

auch Bed<strong>in</strong>gungen zur Verknüpfung der Tabellen.<br />

E<strong>in</strong>zelheiten werden <strong>in</strong> E<strong>in</strong>fache Tabellenverknüpfung 2 behandelt.<br />

18.3. JOINs – der moderne Weg<br />

Beim „modernen“ Weg, mehrere Tabellen <strong>in</strong> e<strong>in</strong>er geme<strong>in</strong>samen Abfrage zu verknüpfen,<br />

wird jede Tabelle <strong>in</strong> e<strong>in</strong>er JOIN-Klausel aufgeführt; der ON-Parameter<br />

enthält die Verknüpfungsbed<strong>in</strong>gung. Die WHERE-Klausel enthält nur die Auswahlbed<strong>in</strong>gungen.<br />

1 Anhang A auf Seite 419<br />

2 Kapitel 19 auf Seite 173<br />

168


Die <strong>E<strong>in</strong>führung</strong> dazu wird <strong>in</strong> Arbeiten mit JOIN 3 besprochen.<br />

Zusammenfassung<br />

Bei Abfragen mit e<strong>in</strong>em „e<strong>in</strong>fachen“ JOIN werden jedoch nicht alle Datensätze<br />

aufgeführt. Wenn es <strong>in</strong> der e<strong>in</strong>en oder anderen Tabelle ke<strong>in</strong>e Verknüpfung<br />

gibt, führt das zu e<strong>in</strong>em NULL-Wert; solche Zeilen fehlen im Ergebnis. Mit e<strong>in</strong>em<br />

OUTER JOIN können auch solche „fehlenden“ Zeilen aufgeführt werden.<br />

E<strong>in</strong>zelheiten dazu werden <strong>in</strong> OUTER JOIN 4 behandelt.<br />

JOIN bietet darüber h<strong>in</strong>aus weitere Möglichkeiten.<br />

• Als SELF JOIN wird e<strong>in</strong>e Tabelle mit sich selbst verknüpft.<br />

• Oft kommt es vor, dass man die Daten aus e<strong>in</strong>er Tabelle erst bearbeiten möchte,<br />

bevor man sie mit e<strong>in</strong>er anderen Tabelle verknüpft. Dazu kann e<strong>in</strong>e „Inl<strong>in</strong>e-<br />

View“ verwendet werden.<br />

Diese Ergänzungen werden <strong>in</strong> Mehr zu JOIN 5 besprochen.<br />

18.4. Zusammenfassung<br />

In diesem Kapitel erhielten wir H<strong>in</strong>weise darauf, wie mehrere Tabellen veknüpft<br />

werden können:<br />

• e<strong>in</strong>fach über die FROM-Klausel und passende WHERE-Bed<strong>in</strong>gungen<br />

• übersichtlich über die JOIN-Klausel mit verschiedenen Varianten<br />

18.5. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 170.<br />

Übung 1 – Was ist an diesem SELECT-Befehl falsch?<br />

Zeigen Sie zu bestimmten Versicherungsverträgen die Daten der Fahrzeuge an.<br />

SELECT ID, Abschlussdatum, Art,<br />

vv.Kennzeichen, Farbe<br />

FROM Versicherungsvertrag vv, Fahrzeug<br />

WHERE vv.Fahrzeug_ID = Fahrzeug.ID<br />

AND Kennzeichen LIKE ’BO%’;<br />

3 Kapitel 20 auf Seite 181<br />

4 Kapitel 21 auf Seite 191<br />

5 Kapitel 22 auf Seite 203<br />

169


Mehrere Tabellen<br />

Übung 2 – Was ist an diesem SELECT-Befehl falsch?<br />

Zeigen Sie zu e<strong>in</strong>em Versicherungsvertrag die Daten des Versicherungsnehmers<br />

und des Sachbearbeiters an.<br />

SELECT ID, Vorname + ’ ’ + Name AS Kunde, Ort<br />

Name AS Sachbearbeiter, Telefon<br />

FROM Versicherungsvertrag, Versicherungsnehmer, Mitarbeiter<br />

WHERE ID = 27<br />

AND Versicherungsvertrag.Versicherungsnehmer_ID = Versicherungsnehmer.ID<br />

AND Versicherungsvertrag.Mitarbeiter_ID = Mitarbeiter.ID;<br />

Übung 3 – Berichtigen Sie den folgenden SELECT-Befehl.<br />

Zeigen Sie zu jedem Mitarbeiter die Daten se<strong>in</strong>es Dienstwagens (Kennzeichen,<br />

Typ, Hersteller) an.<br />

SELECT ID, Name, Vorname,<br />

Kennzeichen, Bezeichnung, Name<br />

FROM Mitarbeiter mi, Dienstwagen dw,<br />

Fahrzeugtyp ft, Fahrzeughersteller fh<br />

WHERE ID = dw.Mitarbeiter_ID<br />

AND ID = dw.Fahrzeugtyp_ID<br />

AND ID = ft.Hersteller_ID<br />

ORDER BY Name, Vorname;<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 169.<br />

Lösung zu Übung 1 – Was ist an diesem SELECT-Befehl falsch?<br />

1. Die ID muss mit Tabellennamen oder Alias versehen se<strong>in</strong>, weil sie <strong>in</strong> beiden<br />

Tabellen enthalten ist.<br />

2. Die Spalte Kennzeichen gehört zur Tabelle Fahrzeug, also ist der Alias vv<br />

falsch.<br />

Lösung zu Übung 2 – Was ist an diesem SELECT-Befehl falsch?<br />

170<br />

1. Die ID muss sowohl <strong>in</strong> der Spaltenliste als auch <strong>in</strong> der WHERE-Klausel mit<br />

Tabellennamen oder Alias versehen se<strong>in</strong>, weil sie <strong>in</strong> allen Tabellen enthalten<br />

ist.<br />

2. Gleiches gilt für Name und Vorname, weil diese Angaben <strong>in</strong> mehreren Tabellen<br />

enthalten s<strong>in</strong>d.


Übungen<br />

Wenn (wie <strong>in</strong> den Anmerkungen zur Beispieldatenbank 6 erwähnt) auch für die<br />

Kunden Kontaktdaten gespeichert wären, müsste das auch bei der Spalte Telefon<br />

beachtet werden. Für die Spalte Ort gilt das nicht, weil diese nicht zur Tabelle<br />

Mitarbeiter gehört, sondern zur Tabelle Abteilung, die hier nicht benutzt wird.<br />

Lösung zu Übung 3 – Berichtigen Sie den folgenden SELECT-Befehl.<br />

SELECT mi.ID, mi.Name, mi.Vorname,<br />

dw.Kennzeichen, ft.Bezeichnung, fh.Name<br />

FROM Mitarbeiter mi, Dienstwagen dw,<br />

Fahrzeugtyp ft, Fahrzeughersteller fh<br />

WHERE mi.ID = dw.Mitarbeiter_ID<br />

AND ft.ID = dw.Fahrzeugtyp_ID<br />

AND fh.ID = ft.Hersteller_ID<br />

ORDER BY mi.Name, mi.Vorname;<br />

6 Kapitel 6 auf Seite 39<br />

171


19. E<strong>in</strong>fache Tabellenverknüpfung<br />

19.1. Alle Komb<strong>in</strong>ationen aller Datensätze . . . . . . . . . . . . . . . 173<br />

19.2. Tabellen e<strong>in</strong>fach verb<strong>in</strong>den . . . . . . . . . . . . . . . . . . . . . 174<br />

19.3. Verknüpfungs- und Auswahlbed<strong>in</strong>gungen . . . . . . . . . . . . 176<br />

19.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

19.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176<br />

19.6. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179<br />

Dieses Kapitel behandelt den „traditionellen“ Weg, mehrere Tabellen gleichzeitig<br />

abzufragen. Dazu werden <strong>in</strong> der FROM-Klausel alle Tabellen aufgeführt; die<br />

WHERE-Klausel enthält neben den Auswahlbed<strong>in</strong>gungen auch Verknüpfungsbed<strong>in</strong>gungen,<br />

wie die Tabellen zue<strong>in</strong>ander gehören.<br />

19.1. Alle Komb<strong>in</strong>ationen aller Datensätze<br />

Der e<strong>in</strong>fachste Weg, Tabellen zu verknüpfen, ist e<strong>in</strong> Befehl, <strong>in</strong> dem Spalten aus<br />

zwei Tabellen zusammengefasst werden – mit e<strong>in</strong>em seltsamen Ergebnis.<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi, Dienstwagen dw;<br />

MITNR NAME VORNAME DIW KENNZEICHEN Typ<br />

10001 Müller Kurt 1 DO-WB 421 14<br />

10002 Schneider Daniela 1 DO-WB 421 14<br />

20001 Meyer Walter 1 DO-WB 421 14<br />

20002 Schmitz Michael 1 DO-WB 421 14<br />

30001 Wagner Gaby 1 DO-WB 421 14<br />

30002 Feyerabend Werner 1 DO-WB 421 14<br />

40001 Langmann Matthias 1 DO-WB 421 14<br />

/* usw. */<br />

10001 Müller Kurt 2 DO-WB 422 14<br />

10002 Schneider Daniela 2 DO-WB 422 14<br />

20001 Meyer Walter 2 DO-WB 422 14<br />

20002 Schmitz Michael 2 DO-WB 422 14<br />

/* usw. */<br />

173


E<strong>in</strong>fache Tabellenverknüpfung<br />

Tatsächlich erzeugt dieser Befehl das „kartesische Produkt“ der beiden Tabellen:<br />

Jeder Datensatz der e<strong>in</strong>en Tabelle wird (mit den gewünschten Spalten) mit jedem<br />

Datensatz der anderen Tabelle verbunden. Das sieht also so aus, als wenn alle<br />

Dienstwagen zu jedem Mitarbeiter gehören würden, was natürlich Quatsch ist.<br />

Diese Variante ist also <strong>in</strong> aller Regel s<strong>in</strong>nlos (wenn auch syntaktisch korrekt).<br />

Nützlich ist sie nur dann, wenn auf e<strong>in</strong>fachem Wege große Mengen von Testdaten<br />

erzeugt werden sollen, wie es im Kapitel Testdaten erzeugen 1 benutzt wird.<br />

19.2. Tabellen e<strong>in</strong>fach verb<strong>in</strong>den<br />

S<strong>in</strong>nvoll wird die vorstehende Abfrage durch e<strong>in</strong>e kle<strong>in</strong>e Ergänzung. Was will<br />

man denn eigentlich wissen?<br />

Gib mir (e<strong>in</strong>ige) Spalten der Tabelle Mitarbeiter zusammen mit (e<strong>in</strong>igen)<br />

Spalten der Tabelle Dienstwagen, und zwar bei jedem Mitarbeiter<br />

denjenigen Dienstwagen, der zu diesem Mitarbeiter gehört.<br />

Woran erkennt man, zu welchem Mitarbeiter e<strong>in</strong> Dienstwagen gehört? Nun, <strong>in</strong><br />

der Tabelle Dienstwagen ist e<strong>in</strong>e Spalte Mitarbeiter_ID enthalten; dieser Wert ist<br />

identisch mit der ID e<strong>in</strong>es E<strong>in</strong>trags <strong>in</strong> der Tabelle Mitarbeiter. Wenn man diese<br />

Anfrage und Information <strong>in</strong> „Pseudocode“ übersetzt, kommt so etwas heraus:<br />

Hole Spalten der Tabelle Mitarbeiter<br />

sowie Spalten der Tabelle Dienstwagen<br />

wobei die Mitarbeiter_ID e<strong>in</strong>es Dienstwagens gleich ist der ID e<strong>in</strong>es Mitarbeiters<br />

Das können wir <strong>in</strong> e<strong>in</strong>e vollständige <strong>SQL</strong>-Abfrage übersetzen; die obige Abfrage<br />

muss nur m<strong>in</strong>imal erweitert werden:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi, Dienstwagen dw<br />

WHERE dw.Mitarbeiter_ID = mi.ID<br />

ORDER BY MitNr;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

100001 Grosser Horst 10 DO-WB 4210 14<br />

10001 Müller Kurt 1 DO-WB 421 14<br />

110001 Eggert Louis 11 DO-WB 4211 14<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14<br />

20001 Meyer Walter 2 DO-WB 422 14<br />

1 Kapitel 36 auf Seite 407<br />

174


30001 Wagner Gaby 3 DO-WB 423 14<br />

40001 Langmann Matthias 4 DO-WB 424 14<br />

50001 Pohl Helmut 5 DO-WB 425 14<br />

50002 Braun Christian 14 DO-WB 352 2<br />

50003 Polovic Frantisek 15 DO-WB 353 3<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4<br />

/* usw. */<br />

Tabellen e<strong>in</strong>fach verb<strong>in</strong>den<br />

Wir bekommen also tatsächlich genau diejenigen Mitarbeiter, die über e<strong>in</strong>en<br />

(persönlichen) Dienstwagen verfügen.<br />

H<strong>in</strong>weis: Wundern Sie sich nicht über die seltsame Reihenfolge. Die Personalnummer<br />

wurde als VARCHAR def<strong>in</strong>iert; also kommt das Ergebnis <strong>in</strong> alphabetischer<br />

und nicht <strong>in</strong> numerischer Reihenfolge.<br />

In der gleichen Weise können auch mehr als zwei Tabellen verknüpft werden. Im<br />

Kapitel Gruppierungen 2 steht e<strong>in</strong> Beispiel ähnlich wie dieses:<br />

Aufgabe: Gesucht wird für jeden Fahrzeughersteller (mit Angabe von ID und<br />

Name) und jedes Jahr die Summe der Schadenshöhe aus der Tabelle Schadensfall.<br />

SELECT fh.ID AS Hersteller_ID,<br />

fh.Name AS Hersteller_Name,<br />

EXTRACT(YEAR FROM sf.Datum) AS Jahr,<br />

SUM(sf.Schadenshoehe) AS Schadenssumme<br />

FROM Schadensfall sf, Zuordnung_SF_FZ zu,<br />

Fahrzeug fz, Fahrzeugtyp ft, Fahrzeughersteller fh<br />

WHERE sf.ID = zu.Schadensfall_ID<br />

AND fz.ID = zu.Fahrzeug_ID<br />

AND ft.ID = fz.Fahrzeugtyp_ID<br />

AND fh.ID = ft.Hersteller_ID<br />

GROUP BY Hersteller_ID, Hersteller_Name, Jahr<br />

ORDER BY Jahr, Hersteller_ID;<br />

Wichtig ist, dass es immer e<strong>in</strong>e e<strong>in</strong>e<strong>in</strong>deutige Zuordnung zwischen je e<strong>in</strong>er Spalte<br />

e<strong>in</strong>er Tabelle und e<strong>in</strong>er Spalte e<strong>in</strong>er anderen Tabelle gibt. Bitte beachten Sie:<br />

• Statt e<strong>in</strong>er e<strong>in</strong>zigen Spalte kann auch e<strong>in</strong>e Gruppe von Spalten verknüpft werden<br />

(z. B. Name + Vorname). Dies macht aber alles umständlicher, unübersichtlicher<br />

und unsicherer. Deshalb sollte vorzugsweise über e<strong>in</strong>deutige IDs<br />

o. ä. verknüpft werden.<br />

• Wenn es zwischen den Tabellen ke<strong>in</strong>e „geme<strong>in</strong>samen“ Spalten gibt, kommt<br />

wieder das kartesische Produkt heraus; das Ergebnis ist dann eher s<strong>in</strong>nlos.<br />

2 Kapitel 25 auf Seite 241<br />

175


E<strong>in</strong>fache Tabellenverknüpfung<br />

19.3. Verknüpfungs- und Auswahlbed<strong>in</strong>gungen<br />

Je mehr Komb<strong>in</strong>ationen benötigt werden, desto unübersichtlicher wird diese<br />

Konstruktion. Dabei enthält die WHERE-Klausel bisher nur die Verknüpfungen<br />

zwischen den Tabellen, aber noch ke<strong>in</strong>e Suchbed<strong>in</strong>gungen wie hier:<br />

SELECT ... FROM ... WHERE ...<br />

AND Jahr IN [2006, 2007, 2008]<br />

AND fhe.Land <strong>in</strong> [’Schweden’, ’Norwegen’, ’F<strong>in</strong>nland’]<br />

ORDER BY Jahr, Hersteller_ID;<br />

Das führt außerdem dazu, dass die WHERE-Klausel sachlich gewünschte Suchbed<strong>in</strong>gungen<br />

und logisch benötigte Verknüpfungsbed<strong>in</strong>gungen vermischt. Wer<br />

soll da noch durchblicken? Besser ist das <strong>in</strong> den nächsten Kapiteln ausführlich<br />

behandelte Verfahren mit JOIN.<br />

19.4. Zusammenfassung<br />

Dieses Kapitel erläutert, wie mehrere Tabellen e<strong>in</strong>fach durch die FROM-Klausel<br />

und passende WHERE-Bed<strong>in</strong>gungen verknüpft werden können:<br />

• In der Spaltenliste sollte immer der jeweilige Tabellenname angegeben werden;<br />

es kann auch e<strong>in</strong> Kürzel als Tabellen-Alias verwendet werden.<br />

• In der FROM-Klausel werden alle Tabellen aufgelistet und <strong>in</strong> der WHERE-<br />

Klausel durch geeignete Bed<strong>in</strong>gungen aufe<strong>in</strong>ander bezogen.<br />

• Durch die Vermischung zwischen Verknüpfungs- und Auswahlbed<strong>in</strong>gungen<br />

wird dieses Verfahren schnell unübersichtlich.<br />

19.5. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 177.<br />

Bei den folgenden Abfragen beziehen wir uns auf die Beispieldatenbank im „Anfangszustand“:<br />

die Tabellen Versicherungsvertrag, Fahrzeug, Mitarbeiter mit jeweils<br />

etwa 28 E<strong>in</strong>trägen und Versicherungsnehmer mit etwa 26 E<strong>in</strong>trägen.<br />

Übung 1 – E<strong>in</strong>e e<strong>in</strong>fache Abfrage<br />

Erstellen Sie e<strong>in</strong>e Abfrage zur Tabelle Versicherungsvertrag, die nur die wichtigsten<br />

Informationen (e<strong>in</strong>schließlich der IDs auf andere Tabellen) enthält. Wie viele<br />

E<strong>in</strong>träge zeigt die Ergebnismenge an?<br />

176


Übung 2 – Das kartesische Produkt<br />

Übungen<br />

Erweitern Sie die Abfrage von Aufgabe 1, sodass anstelle der Versicherungsnehmer_ID<br />

dessen Name und Vorname angezeigt werden, und verzichten Sie auf<br />

e<strong>in</strong>e WHERE-Klausel. Wie viele E<strong>in</strong>träge zeigt die Ergebnismenge an?<br />

Übung 3 – Das kartesische Produkt<br />

Erweitern Sie die Abfrage von Aufgabe 2, sodass anstelle der Fahrzeug_ID das<br />

Kennzeichen und anstelle der Mitarbeiter_ID dessen Name und Vorname angezeigt<br />

werden, und verzichten Sie auf e<strong>in</strong>e WHERE-Klausel. Wie viele E<strong>in</strong>träge<br />

zeigt die Ergebnismenge an?<br />

Übung 4 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Erweitern Sie die Abfrage von Aufgabe 2, sodass Name und Vorname des Versicherungsnehmers<br />

genau zu e<strong>in</strong>em jeden Vertrag passen. Wie viele E<strong>in</strong>träge zeigt<br />

die Ergebnismenge an?<br />

Übung 5 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Erweitern Sie die Abfrage von Aufgabe 3, sodass Name und Vorname des Mitarbeiters<br />

sowie das Fahrzeug-Kennzeichen genau zu e<strong>in</strong>em jeden Vertrag passen.<br />

Wie viele E<strong>in</strong>träge zeigt die Ergebnismenge an?<br />

Übung 6 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Erweitern Sie die Abfrage von Aufgabe 5, sodass die ausgewählten Zeilen den<br />

folgenden Bed<strong>in</strong>gungen entsprechen:<br />

• Es geht ausschließlich um Eigene Kunden.<br />

• Vollkasko-Verträge sollen immer angezeigt werden, ebenso Fahrzeuge aus dem<br />

Kreis Reckl<strong>in</strong>ghausen 'RE'.<br />

• Teilkasko-Verträge sollen angezeigt werden, wenn sie nach 1990 abgeschlossen<br />

wurden.<br />

• Haftpflicht-Verträge sollen angezeigt werden, wenn sie nach 1985 abgeschlossen<br />

wurden.<br />

Wie viele E<strong>in</strong>träge zeigt die Ergebnismenge an?<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 176.<br />

177


E<strong>in</strong>fache Tabellenverknüpfung<br />

Lösung zu Übung 1 – E<strong>in</strong>e e<strong>in</strong>fache Abfrage<br />

Es werden 28 Zeilen angezeigt.<br />

SELECT Vertragsnummer, Abschlussdatum, Art,<br />

Versicherungsnehmer_ID, Fahrzeug_ID, Mitarbeiter_ID<br />

FROM Versicherungsvertrag;<br />

Lösung zu Übung 2 – Das kartesische Produkt<br />

Es werden etwa 728 Zeilen angezeigt.<br />

SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

vn.Name, vn.Vorname,<br />

Fahrzeug_ID,<br />

Mitarbeiter_ID<br />

FROM Versicherungsvertrag vv, Versicherungsnehmer vn;<br />

Lösung zu Übung 3 – Das kartesische Produkt<br />

Es werden etwa 570 752 Zeilen angezeigt.<br />

SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

vn.Name, vn.Vorname,<br />

fz.Kennzeichen,<br />

mi.Name, mi.Vorname<br />

FROM Versicherungsvertrag vv, Versicherungsnehmer vn,<br />

Fahrzeug fz, Mitarbeiter mi;<br />

Lösung zu Übung 4 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Es werden etwa 28 Zeilen angezeigt.<br />

SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

vn.Name, vn.Vorname,<br />

Fahrzeug_ID,<br />

Mitarbeiter_ID<br />

FROM Versicherungsvertrag vv, Versicherungsnehmer vn<br />

WHERE vn.ID = vv.Versicherungsnehmer_ID;<br />

Lösung zu Übung 5 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Es werden etwa 28 Zeilen angezeigt.<br />

178<br />

SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

vn.Name, vn.Vorname,


fz.Kennzeichen,<br />

mi.Name, mi.Vorname<br />

FROM Versicherungsvertrag vv, Versicherungsnehmer vn,<br />

Fahrzeug fz, Mitarbeiter mi<br />

WHERE vn.ID = vv.Versicherungsnehmer_ID<br />

AND fz.ID = vv.Fahrzeug_ID<br />

AND mi.ID = vv.Mitarbeiter_ID;<br />

Lösung zu Übung 6 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Siehe auch<br />

Es werden etwa 19 Zeilen angezeigt. Die OR-Verknüpfungen könnten teilweise<br />

auch mit CASE geschrieben werden.<br />

SELECT vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

vn.Name, vn.Vorname,<br />

fz.Kennzeichen,<br />

mi.Name, mi.Vorname<br />

FROM Versicherungsvertrag vv, Versicherungsnehmer vn,<br />

Fahrzeug fz, Mitarbeiter mi<br />

WHERE vn.ID = vv.Versicherungsnehmer_ID<br />

AND fz.ID = vv.Fahrzeug_ID<br />

AND mi.ID = vv.Mitarbeiter_ID<br />

AND vn.Eigener_kunde = ’J’<br />

AND ( ( vv.Art = ’HP’ AND vv.Abschlussdatum > ’31.12.1985’ )<br />

OR ( vv.Art = ’TK’ AND vv.Abschlussdatum > ’31.12.1990’ )<br />

OR ( vv.Art = ’VK’ )<br />

OR ( fz.Kennzeichen STARTING WITH ’RE-’ ) );<br />

19.6. Siehe auch<br />

Bei Wikipedia stehen weitere H<strong>in</strong>weise:<br />

• Kartesisches Produkt 3<br />

• Pseudocode 4<br />

3 http://de.wikipedia.<strong>org</strong>/wiki/Kartesisches%20Produkt<br />

4 http://de.wikipedia.<strong>org</strong>/wiki/Pseudocode<br />

179


20. Arbeiten mit JOIN<br />

20.1. Die Syntax von JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . 181<br />

20.2. INNER JOIN von zwei Tabellen . . . . . . . . . . . . . . . . . . . 182<br />

20.3. WHERE-Klausel bei JOINs . . . . . . . . . . . . . . . . . . . . . . 183<br />

20.4. INNER JOIN mehrerer Tabellen . . . . . . . . . . . . . . . . . . . 185<br />

20.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 185<br />

20.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186<br />

20.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189<br />

Dieses Kapitel enthält die <strong>E<strong>in</strong>führung</strong> IN den „modernen“ Weg, mehrere Tabellen<br />

gleichzeitig abzufragen. Dazu wird jede verknüpfte Tabelle IN e<strong>in</strong>er JOIN-<br />

Klausel aufgeführt; der ON-Parameter enthält die Verknüpfungsbed<strong>in</strong>gung. Die<br />

WHERE-Klausel enthält „nur“ die Auswahlbed<strong>in</strong>gungen.<br />

20.1. Die Syntax von JOIN<br />

Um Tabellen s<strong>in</strong>nvoll mite<strong>in</strong>ander zu verknüpfen (= verb<strong>in</strong>den, engl. JOIN), wurde<br />

die JOIN-Klausel für den SELECT-Befehl mit folgender Syntax e<strong>in</strong>geführt.<br />

SELECT <br />

FROM <br />

[] JOIN ON <br />

Als stehen zur Verfügung:<br />

• [INNER] JOIN, auch Equi-Jo<strong>in</strong> genannt, ist e<strong>in</strong>e Verknüpfung „<strong>in</strong>nerhalb“<br />

zweier Tabellen, d. h. e<strong>in</strong> Teil des kartesischen Produkts, bei dem e<strong>in</strong> Wert IN<br />

beiden Tabellen vorhanden ist. INNER JOIN ist der Inhalt dieses Kapitels.<br />

• E<strong>in</strong> JOIN ohne nähere Angabe ist immer e<strong>in</strong> INNER JOIN.<br />

• OUTER JOIN bezeichnet Verknüpfungen, bei denen auch Datensätze geliefert<br />

werden, für die e<strong>in</strong>e Auswahlbed<strong>in</strong>gung nicht erfüllt ist.<br />

• LEFT JOIN, RIGHT JOIN, FULL JOIN bezeichnen Spezialfälle von OUTER<br />

JOIN, je nachdem IN welcher Tabelle e<strong>in</strong> gesuchter Wert fehlt.<br />

OUTER JOIN wird im nächsten Kapitel 1 behandelt.<br />

1 Kapitel 21 auf Seite 191<br />

181


Arbeiten mit JOIN<br />

E<strong>in</strong>ige Sonderfälle und Ergänzungen zu JOIN werden im Kapitel Mehr zu JOIN 2<br />

behandelt.<br />

Als wird normalerweise nur e<strong>in</strong>e Übere<strong>in</strong>stimmung (also e<strong>in</strong>e<br />

Gleichheit) zwischen zwei Tabellen geprüft, auch wenn jede Komb<strong>in</strong>ation von<br />

Bed<strong>in</strong>gungen erlaubt ist. Genauer: es geht um die Gleichheit von Werten je e<strong>in</strong>er<br />

Spalte IN zwei Tabellen. (Zwei Beispiele für andere Übere<strong>in</strong>stimmungen lernen<br />

Sie IN „Mehr zu JOIN“ kennen.)<br />

Auch mehrere Verknüpfungen s<strong>in</strong>d möglich, entweder direkt h<strong>in</strong>tere<strong>in</strong>ander:<br />

SELECT <br />

FROM <br />

[] JOIN ON <br />

[] JOIN ON <br />

[] JOIN ON <br />

oder durch Klammern gegliedert:<br />

SELECT <br />

FROM <br />

[] JOIN<br />

( <br />

[] JOIN<br />

( <br />

[] JOIN ON <br />

) ON <br />

) ON <br />

Bitte beachten Sie dabei genau, wo und wie die Klammern und die dazugehörigen<br />

ON-Bed<strong>in</strong>gungen gesetzt werden. Beide Varianten können unterschiedliche<br />

Ergebnisse liefern − abhängig vom JOIN-Typ und dem Zusammenhang zwischen<br />

den Tabellen.<br />

All dies wird IN den nächsten Abschnitten und Kapiteln genauer erläutert.<br />

20.2. INNER JOIN von zwei Tabellen<br />

Aufgabe: Das Beispiel „alle Mitarbeiter mit den zugehörigen Dienstwagen“<br />

aus dem vorigen Kapitel benötigt nur ger<strong>in</strong>ge Änderungen. (Das Ergebnis stimmt<br />

mit der Liste dort übere<strong>in</strong>; wir verzichten deshalb auf die erneute Ausgabe.)<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

2 Kapitel 22 auf Seite 203<br />

182


dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID<br />

ORDER BY MitNr;<br />

WHERE-Klausel bei JOINs<br />

Die zweite Tabelle wird IN die JOIN-Klausel verschoben, die Verknüpfungsbed<strong>in</strong>gung<br />

IN den ON-Parameter − fertig.<br />

20.3. WHERE-Klausel bei JOINs<br />

E<strong>in</strong>e solche Abfrage kann wie üblich durch e<strong>in</strong>e WHERE-Klausel e<strong>in</strong>geschränkt<br />

werden. E<strong>in</strong>e Suchbed<strong>in</strong>gung auf die verknüpfte Tabelle Dienstwagen kann<br />

wahlweise IN der WHERE-Klausel oder IN der JOIN-Klausel stehen. In den beiden<br />

folgenden Beispielen geht es nur um die Dienstwagen von Mercedes. Die<br />

Information, welche Typen zu Mercedes gehören, kommt über e<strong>in</strong>e Unterabfrage,<br />

die ebenfalls e<strong>in</strong>en JOIN verwendet und die IN Klammern gesetzt ist.<br />

Aufgabe: Suche die Dienstwagen vom Typ Mercedes.<br />

<strong>SQL</strong>-Quelltext Falsch<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

JOIN Dienstwagen dw<br />

ON mi.ID = dw.Mitarbeiter_ID<br />

AND dw.Fahrzeugtyp_ID IN ( SELECT ft.ID<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh<br />

ON ft.Hersteller_ID = fh.ID<br />

AND fh.Name = ’Mercedes-Benz’ );<br />

Zulässig, aber nicht so schön, weil Verknüpfungs- und Auswahlbed<strong>in</strong>gung vermischt werden<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

JOIN Dienstwagen dw<br />

ON mi.ID = dw.Mitarbeiter_ID<br />

WHERE dw.Fahrzeugtyp_ID IN ( SELECT ft.ID<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh<br />

ON ft.Hersteller_ID = fh.ID<br />

WHERE fh.Name = ’Mercedes-Benz’);<br />

Besseres V<strong>org</strong>ehen, weil die Auswahlbed<strong>in</strong>gungen als solche direkt zu erkennen s<strong>in</strong>d<br />

Natürlich s<strong>in</strong>d E<strong>in</strong>schränkungen auf beide Tabellen möglich:<br />

183


Arbeiten mit JOIN<br />

Aufgabe: Gesucht werden Mitarbeiter mit 'M' und Mercedes als Dienstwagen.<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

JOIN Dienstwagen dw<br />

ON mi.ID = dw.Mitarbeiter_ID<br />

WHERE dw.Fahrzeugtyp_ID IN ( SELECT ft.ID<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh<br />

ON ft.Hersteller_ID = fh.ID<br />

WHERE fh.Name = ’Mercedes-Benz’)<br />

AND mi.Name like ’M%’;<br />

MITNR NAME VORNAME ID KENNZEICHEN TYP<br />

10001 Müller Kurt 1 DO-WB 421 14<br />

20001 Meyer Walter 2 DO-WB 422 14<br />

Hier wird sofort deutlich, welche Bed<strong>in</strong>gungen die Verknüpfung und welche die<br />

Auswahl bezeichnen. Auf diese Übersichtlichkeit sollten Sie immer achten.<br />

Übrigens gibt es ke<strong>in</strong>e allgeme<strong>in</strong>e Regel, was als Haupttabelle und was als verknüpfte<br />

Tabelle zu verwenden ist. In den bisherigen Beispielen können die beiden<br />

Tabellen ohne Weiteres vertauscht werden:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Dienstwagen dw<br />

JOIN Mitarbeiter mi<br />

ON mi.ID = dw.Mitarbeiter_ID<br />

WHERE dw.Fahrzeugtyp_ID IN ( SELECT ft.ID<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh<br />

ON ft.Hersteller_ID = fh.ID<br />

WHERE fh.Name = ’Mercedes-Benz’)<br />

AND mi.Name like ’M%’;<br />

Mitarbeiter mit 'M' und Mercedes als Dienstwagen<br />

Die Haupttabelle kann nach folgenden Überlegungen gewählt werden:<br />

• Es sollte die Tabelle se<strong>in</strong>, die die „wichtigste“ bei der Abfrage ist.<br />

• Es sollte diejenige mit den größten E<strong>in</strong>schränkungen se<strong>in</strong>; das beschleunigt<br />

die Abfrage besonders stark.<br />

184


20.4. INNER JOIN mehrerer Tabellen<br />

INNER JOIN mehrerer Tabellen<br />

Dazu nehmen wir wiederum das komplexe Beispiel aus dem vorigen Kapitel,<br />

das bei den Gruppierungen genauer besprochen wird. In diesem Fall spielt die<br />

Reihenfolge der JOIN-Klauseln eher ke<strong>in</strong>e Rolle, weil es sich sowieso um direkte<br />

Übere<strong>in</strong>stimmungen handelt und nur solche Datensätze benutzt werden, die es<br />

zu den betreffenden Werten tatsächlich gibt.<br />

Aufgabe: Gesucht wird für jeden Fahrzeughersteller (mit Angabe von ID und<br />

Name) und jedes Jahr die Summe der Schadenshöhe aus der Tabelle Schadensfall.<br />

SELECT fh.ID AS Hersteller_ID,<br />

fh.Name AS Hersteller_Name,<br />

EXTRACT(YEAR FROM sf.Datum) AS Jahr,<br />

SUM(sf.Schadenshoehe) AS Schadenssumme<br />

FROM Schadensfall sf<br />

JOIN Zuordnung_SF_FZ zu ON sf.ID = zu.Schadensfall_ID<br />

JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

JOIN Fahrzeugtyp ft ON ft.ID = fz.Fahrzeugtyp_ID<br />

JOIN Fahrzeughersteller fh ON fh.ID = ft.Hersteller_ID<br />

GROUP BY Hersteller_ID, Hersteller_Name, Jahr<br />

ORDER BY Jahr, Hersteller_ID;<br />

Übrigens dürfen der „traditionelle“ Weg (mehrere Tabellen <strong>in</strong> der FROM-<br />

Klausel) und der „moderne“ Weg über JOIN gemischt werden. Wenn Sie wirklich<br />

ausnahmsweise e<strong>in</strong>mal so vorfahren wollen, sollten Sie erst recht die Übersichtlichkeit<br />

und den Zusammenhang der Bed<strong>in</strong>gungen beachten. Wir können uns<br />

ke<strong>in</strong>e passende Situation vorstellen, aber vielleicht ist es doch e<strong>in</strong>mal s<strong>in</strong>nvoll.<br />

20.5. Zusammenfassung<br />

Dieses Kapitel erklärte die Verknüpfung von Tabellen über die JOIN-Klausel.<br />

• Mit e<strong>in</strong>em INNER JOIN werden Datensätze abgefragt, bei denen e<strong>in</strong> Wert <strong>in</strong> je<br />

e<strong>in</strong>er Spalte beider Tabellen vorhanden ist.<br />

• In der ON-Klausel steht diese Verknüpfungsbed<strong>in</strong>gung.<br />

• In der WHERE-Klausel stehen die „normalen“ Auswahlbed<strong>in</strong>gungen.<br />

Genauso können mehrere Tabellen verknüpft werden.<br />

185


Arbeiten mit JOIN<br />

20.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 187.<br />

Übung 1 – Def<strong>in</strong>ition von JOINs<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche falsch, welche s<strong>in</strong>nvoll?<br />

1. Der INNER JOIN liefert das kartesische Produkt zwischen den Tabellen.<br />

2. LEFT JOIN ist e<strong>in</strong> Spezialfall von OUTER JOIN.<br />

3. Für e<strong>in</strong>en JOIN ist dies e<strong>in</strong>e zulässige Verknüpfungsbed<strong>in</strong>gung:<br />

ON Fahrzeug.ID >= Versicherungsvertrag.Fahrzeug_ID<br />

4. E<strong>in</strong>e E<strong>in</strong>schränkung auf die mit JOIN verknüpfte Tabelle gehört <strong>in</strong> die ON-<br />

Klausel:<br />

... FROM Zuordnung_SF_FZ zu<br />

JOIN Schadensfall sf<br />

ON sf.ID = zu.Schadensfall_ID AND EXTRACT(YEAR FROM sf.Datum) = 2008;<br />

Übung 2 – Def<strong>in</strong>ition von JOINs<br />

Erläutern Sie, was am folgenden Befehl falsch oder äußerst ungünstig ist. Es handelt<br />

sich um diese Abfrage:<br />

Aufgabe: Gesucht s<strong>in</strong>d die Schadensfälle des Jahres 2008. Zu jedem Schadensfall<br />

s<strong>in</strong>d die beteiligten Fahrzeuge, der Schadensanteil sowie die Versicherungsdaten<br />

des Fahrzeugs (e<strong>in</strong>schließlich Name des Halters) anzugeben.<br />

1. SELECT Datum, SUBSTRING(Ort FROM 1 FOR 30) AS Ort, Schadenshoehe,<br />

2. zu.Schadenshoehe,<br />

3. fz.Kennzeichen,<br />

4. Vertragsnummer AS Vertrag, Abschlussdatum, Art,<br />

5. vn.Name AS VN-Name, vn.Vorname AS VN-Vorname<br />

6. FROM Schadensfall sf<br />

7. JOIN Zuordnung_SF_FZ zu ON ID = zu.Schadensfall_ID<br />

8. JOIN Fahrzeug fz ON ID = zu.Fahrzeug_ID<br />

9. JOIN Versicherungsnehmer vn ON ID = vv.Versicherungsnehmer_ID<br />

10. JOIN Versicherungsvertrag vv ON vv.Fahrzeug_ID = zu.Fahrzeug_ID<br />

11. WHERE EXTRACT(YEAR FROM Datum) = 2008<br />

12. ORDER BY Schadensfall_ID, Fahrzeug_ID;<br />

Die folgenden Aufgaben entsprechen teilweise Aufgaben aus dem Kapitel „E<strong>in</strong>fache<br />

Tabellenverknüpfung“. Sie sollen jetzt an den passenden Stellen JOINs verwenden,<br />

anstatt die Tabellen e<strong>in</strong>fach aufzulisten.<br />

186


Übung 3 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Übungen<br />

Erstellen Sie e<strong>in</strong>e Abfrage zur Tabelle Versicherungsvertrag mit den wichtigsten<br />

Informationen (e<strong>in</strong>schließlich der IDs auf andere Tabellen). Beim Versicherungsnehmer<br />

sollen dessen Name und Vorname angezeigt werden. Es werden nur Verträge<br />

ab 1990 gesucht.<br />

Übung 4 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Erweitern Sie die Abfrage von Aufgabe 3, sodass Name und Vorname des Mitarbeiters<br />

sowie das Fahrzeug-Kennzeichen e<strong>in</strong>es jeden Vertrags angezeigt werden.<br />

Übung 5 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

Ändern Sie die Abfrage von Aufgabe 4 so, dass die ausgewählten Zeilen den folgenden<br />

Bed<strong>in</strong>gungen entsprechen:<br />

• Es geht ausschließlich um Eigene Kunden.<br />

• Vollkasko-Verträge sollen immer angezeigt werden, ebenso Fahrzeuge aus dem<br />

Kreis Reckl<strong>in</strong>ghausen 'RE'.<br />

• Teilkasko-Verträge sollen angezeigt werden, wenn sie nach 1990 abgeschlossen<br />

wurden.<br />

• Haftpflicht-Verträge sollen angezeigt werden, wenn sie nach 1985 abgeschlossen<br />

wurden.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 186.<br />

Lösung zu Übung 1 – Def<strong>in</strong>ition von JOINs<br />

1. Falsch; es liefert e<strong>in</strong>en Teil des kartesischen Produkts, der durch die ON-<br />

Bed<strong>in</strong>gung bestimmt wird.<br />

2. Richtig.<br />

3. Diese Bed<strong>in</strong>gung ist zulässig, aber nicht s<strong>in</strong>nvoll. JOIN-ON passt <strong>in</strong> der Regel<br />

nur für Gleichheiten.<br />

4. Diese Bed<strong>in</strong>gung ist zulässig. Besser ist es aber, e<strong>in</strong>e E<strong>in</strong>schränkung der<br />

Auswahl <strong>in</strong> die WHERE-Klausel zu setzen.<br />

187


Arbeiten mit JOIN<br />

Lösung zu Übung 2 – Def<strong>in</strong>ition von JOINs<br />

Richtig ist beispielsweise die folgende Version. Als Haupttabelle wurde wegen<br />

der WHERE-Klausel die Tabelle Schadensfall gewählt; wegen der Reihenfolge der<br />

Verknüpfungen wäre auch Zuordnung_SF_FZ als Haupttabelle geeignet.<br />

SELECT sf.Datum, SUBSTRING(sf.Ort FROM 1 FOR 30) AS Ort, sf.Schadenshoehe,<br />

zu.Schadenshoehe AS Teilschaden,<br />

fz.Kennzeichen,<br />

vv.Vertragsnummer AS Vertrag, vv.Abschlussdatum, vv.Art,<br />

vn.Name AS VN_Name, vn.Vorname AS VN_Vorname<br />

FROM Schadensfall sf<br />

JOIN Zuordnung_SF_FZ zu ON sf.ID = zu.Schadensfall_ID<br />

JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

JOIN Versicherungsvertrag vv ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

WHERE EXTRACT(YEAR FROM sf.Datum) = 2008<br />

ORDER BY zu.Schadensfall_ID, zu.Fahrzeug_ID;<br />

Die Variante aus der Aufgabenstellung enthält folgende Problemstellen:<br />

• Zeile 1: Der Tabellen-Alias sf fehlt bei Schadenshoehe und bei Ort. Bei Datum<br />

fehlt er auch, aber das ist möglich, weil es sie nur bei dieser Tabelle gibt.<br />

• Zeile 2: Diese Spalte sollte e<strong>in</strong>en Spalten-Alias bekommen wegen der abweichenden<br />

Bedeutung zu sf.Schadenshoehe.<br />

• Zeile 4: Es ist schöner, auch hier mit e<strong>in</strong>em Tabellen-Alias zu arbeiten.<br />

• Zeile 5: Der B<strong>in</strong>destrich <strong>in</strong> der Bezeichnung des Spalten-Alias wird nicht bei<br />

allen DBMS akzeptiert.<br />

• Zeile 7, 8, 9: Zur Spalte ID ist jeweils die Tabelle anzugeben, ggf. mit dem Alias.<br />

Die JOIN-ON-Bed<strong>in</strong>gung bezieht sich nicht automatisch auf diese Spalte und<br />

diese Tabelle.<br />

• Zeile 9, 10: In Zeile 9 ist die Tabelle Versicherungsvertrag vv noch nicht bekannt.<br />

Wegen der Verknüpfungen ist zuerst Zeile 10 zu verwenden, danach Zeile 9.<br />

Die Verknüpfung über vv.Fahrzeug_ID = zu.Fahrzeug_ID ist nicht glücklich<br />

(wenn auch korrekt); besser ist der Bezug auf die direkt zugeordnete Tabelle<br />

Fahrzeug und deren PrimaryKey, nämlich ID.<br />

• Zeile 11: Es ist klarer, auch hier den Tabellen-Alias sf zu verwenden.<br />

• Zeile 12: Der Tabellen-Alias zu fehlt bei beiden Spalten. Bei Fahrzeug_ID ist er<br />

erforderlich (doppelte Verwendung bei vv), bei Schadensfall_ID s<strong>in</strong>nvoll.<br />

Lösung zu Übung 3 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

188<br />

SELECT Vertragsnummer, Abschlussdatum, Art,<br />

Name, Vorname,<br />

Fahrzeug_ID,


Mitarbeiter_ID<br />

FROM Versicherungsvertrag vv<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

WHERE vv.Abschlussdatum >= ’01.01.1990’;<br />

Lösung zu Übung 4 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

SELECT vv.Vertragsnummer AS Vertrag, vv.Abschlussdatum, vv.Art,<br />

vn.Name AS VN_Name, vn.Vorname AS VN_Vorname,<br />

fz.Kennzeichen,<br />

mi.Name AS MI_Name, mi.Vorname AS MI_Vorname<br />

FROM Versicherungsvertrag vv<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Mitarbeiter mi ON mi.ID = vv.Mitarbeiter_ID<br />

WHERE vv.Abschlussdatum >= ’01.01.1990’;<br />

Lösung zu Übung 5 – S<strong>in</strong>nvolle Verknüpfung von Tabellen<br />

SELECT vv.Vertragsnummer AS Vertrag, vv.Abschlussdatum, vv.Art,<br />

vn.Name AS VN_Name, vn.Vorname AS VN_Vorname,<br />

fz.Kennzeichen,<br />

mi.Name AS MI_Name, mi.Vorname AS MI_Vorname<br />

FROM Versicherungsvertrag vv<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Mitarbeiter mi ON mi.ID = vv.Mitarbeiter_ID<br />

WHERE vn.Eigener_kunde = ’J’<br />

AND ( ( vv.Art = ’HP’ AND vv.Abschlussdatum > ’31.12.1985’ )<br />

OR ( vv.Art = ’TK’ AND vv.Abschlussdatum > ’31.12.1990’ )<br />

OR ( vv.Art = ’VK’ )<br />

OR ( fz.Kennzeichen STARTING WITH ’RE-’ ) );<br />

20.7. Siehe auch<br />

Siehe auch<br />

In diesem Kapitel werden Sachverhalte der folgenden Themen angesprochen:<br />

• E<strong>in</strong>fache Tabellenverknüpfung 3<br />

• Unterabfragen 4<br />

• Gruppierungen 5<br />

3 Kapitel 19 auf Seite 173<br />

4 Kapitel 26 auf Seite 251<br />

5 Kapitel 25 auf Seite 241<br />

189


21. OUTER JOIN<br />

21.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise . . . . . . . . . . . . . . . . . . . . . . . . . 191<br />

21.2. LEFT OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . 192<br />

21.3. RIGHT OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . 193<br />

21.4. FULL OUTER JOIN . . . . . . . . . . . . . . . . . . . . . . . . . . . 194<br />

21.5. Verknüpfung mehrerer Tabellen . . . . . . . . . . . . . . . . . . 195<br />

21.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 198<br />

21.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199<br />

Bei den Abfragen im vorigen Kapitel nach „alle Mitarbeiter und ihre Dienstwagen“<br />

werden nicht alle Mitarbeiter aufgeführt, weil <strong>in</strong> der Datenbank nicht für<br />

alle Mitarbeiter e<strong>in</strong> Dienstwagen registriert ist. Ebenso gibt es e<strong>in</strong>en Dienstwagen,<br />

der ke<strong>in</strong>em bestimmten Mitarbeiter zugeordnet ist.<br />

Mit e<strong>in</strong>em OUTER JOIN werden auch Mitarbeiter ohne Dienstwagen oder<br />

Dienstwagen ohne Mitarbeiter aufgeführt.<br />

21.1. Allgeme<strong>in</strong>e H<strong>in</strong>weise<br />

Die Syntax entspricht derjenigen von JOIN allgeme<strong>in</strong>. Wegen der speziellen Bedeutung<br />

s<strong>in</strong>d die Tabellen nicht gleichberechtigt, sondern werden begrifflich unterschieden:<br />

SELECT <br />

FROM <br />

[] JOIN ON <br />

Als Spezialfälle des OUTER JOIN gibt es die JOIN-Typen LEFT JOIN, RIGHT<br />

JOIN, FULL JOIN.<br />

Anstelle von und wird bei OUTER JOIN von<br />

und gesprochen, um die unterschiedliche Bedeutung<br />

zu verdeutlichen.<br />

Das Wort OUTER kann entfallen und wird meistens nicht benutzt, weil durch die<br />

Begriffe LEFT, RIGHT, FULL bereits e<strong>in</strong> OUTER JOIN gekennzeichnet wird.<br />

191


OUTER JOIN<br />

Die Begriffe und beziehen sich auf die beiden<br />

Tabellen bezüglich der normalen Lesefolge: Wir lesen von l<strong>in</strong>ks nach rechts,<br />

also ist die unter FROM genannte Tabelle die (bisher <br />

genannt) und die unter JOIN genannte Tabelle die <br />

(bisher genannt). Bei Verknüpfungen mit mehreren Tabellen ist<br />

ebenfalls die unter JOIN genannte Tabelle die ; die unmittelbar<br />

vorhergehende Tabelle ist die .<br />

Auch wenn die Beispiele so aussehen, als wenn die Datensätze s<strong>in</strong>nvoll sortiert<br />

wären, ist das Zufall; bitte denken Sie daran, dass <strong>SQL</strong> ungeordnete Datenmengen<br />

liefert. E<strong>in</strong>e bestimmte Sortierung erhalten Sie erst durch ORDER BY.<br />

Die Anzeige ist <strong>in</strong> der Regel nur e<strong>in</strong> Auszug des vollständigen Ergebnisses.<br />

21.2. LEFT OUTER JOIN<br />

Dieser JOIN liefert alle Datensätze der l<strong>in</strong>ken Tabelle, ggf. unter Berücksichtigung<br />

der WHERE-Klausel. Aus der rechten Tabelle werden nur diejenigen Datensätze<br />

übernommen, die nach der Verknüpfungsbed<strong>in</strong>gung passen.<br />

SELECT <br />

FROM <br />

LEFT [OUTER] JOIN ON ;<br />

Für unser Beispiel sieht das dann so aus:<br />

Aufgabe: Hole alle Mitarbeiter und (sofern vorhanden) die Angaben zum<br />

Dienstwagen.<br />

192<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

LEFT JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

30001 Wagner Gaby 3 DO-WB 423 14<br />

30002 Feyerabend Werner<br />

40001 Langmann Matthias 4 DO-WB 424 14<br />

40002 Peters Michael<br />

50001 Pohl Helmut 5 DO-WB 425 14<br />

50002 Braun Christian 14 DO-WB 352 2<br />

50003 Polovic Frantisek 15 DO-WB 353 3<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4<br />

60001 Aagenau Karol<strong>in</strong> 6 DO-WB 426 14<br />

60002 P<strong>in</strong>kart Petra


Und wenn wir jetzt die beiden Tabellen vertauschen?<br />

RIGHT OUTER JOIN<br />

Dann erhalten wir alle Dienstwagen und dazu die passenden Mitarbeiter.<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14<br />

90001 Janssen Bernhard 9 DO-WB 429 14<br />

100001 Grosser Horst 10 DO-WB 4210 14<br />

110001 Eggert Louis 11 DO-WB 4211 14<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14<br />

13 DO-WB 111 16<br />

50002 Braun Christian 14 DO-WB 352 2<br />

50003 Polovic Frantisek 15 DO-WB 353 3<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4<br />

Bitte überlegen Sie selbst, wie sich WHERE-Klauseln auf das Ergebnis e<strong>in</strong>er Abfrage<br />

auswirken.<br />

21.3. RIGHT OUTER JOIN<br />

Dieser JOIN liefert alle Datensätze der rechten Tabelle, ggf. unter Berücksichtigung<br />

der WHERE-Klausel. Aus der l<strong>in</strong>ken Tabelle werden nur diejenigen Datensätze<br />

übernommen, die nach der Verknüpfungsbed<strong>in</strong>gung passen.<br />

SELECT <br />

FROM <br />

RIGHT [OUTER] JOIN ON ;<br />

Für unser Beispiel „Mitarbeiter und Dienstwagen“ sieht das dann so aus:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

RIGHT JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14<br />

90001 Janssen Bernhard 9 DO-WB 429 14<br />

100001 Grosser Horst 10 DO-WB 4210 14<br />

193


OUTER JOIN<br />

110001 Eggert Louis 11 DO-WB 4211 14<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14<br />

13 DO-WB 111 16<br />

50002 Braun Christian 14 DO-WB 352 2<br />

50003 Polovic Frantisek 15 DO-WB 353 3<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4<br />

Nanu, dieses Ergebnis hatten wir doch gerade? Bei genauerem Überlegen wird<br />

klar: Beim LEFT JOIN gibt es alle Datensätze der l<strong>in</strong>ken Tabelle mit Informationen<br />

der rechten Tabelle; nun haben wir die beiden Tabellen vertauscht. Beim<br />

RIGHT JOIN werden alle Datensätze der rechten Tabelle mit Daten der l<strong>in</strong>ken<br />

Tabelle verknüpft; das entspricht diesem Beispiel.<br />

Ob wir also die beiden Tabellen vertauschen oder LEFT gegen RIGHT, bleibt sich<br />

zwangsläufig gleich. Kurz und „knackig“ formuliert kann man sich also merken:<br />

i Merksatz<br />

"A LEFT JOIN B" liefert dasselbe Ergebnis wie "B RIGHT JOIN A".<br />

Bitte überlegen Sie, welches Ergebnis die Vertauschung der beiden Tabellen<br />

beim RIGHT JOIN liefert und welche Auswirkung WHERE-Klauseln haben.<br />

21.4. FULL OUTER JOIN<br />

Dieser JOIN liefert alle Datensätze beider Tabellen, ggf. unter Berücksichtigung<br />

der WHERE-Klausel. Wenn Datensätze nach der Verknüpfungsbed<strong>in</strong>gung passen,<br />

stehen sie <strong>in</strong> e<strong>in</strong>er Zeile; ohne „Partner“ wird e<strong>in</strong> NULL-Wert angezeigt.<br />

SELECT <br />

FROM <br />

FULL [OUTER] JOIN ON ;<br />

Für unser Beispiel sieht das dann so aus:<br />

194<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

FULL JOIN Dienstwagen dw ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

100001 Grosser Horst 10 DO-WB 4210 14<br />

110001 Eggert Louis 11 DO-WB 4211 14


120001 Carlsen Zacharias 12 DO-WB 4212 14<br />

13 DO-WB 111 16<br />

50002 Braun Christian 14 DO-WB 352 2<br />

50003 Polovic Frantisek 15 DO-WB 353 3<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4<br />

80002 Aliman Zafer 17 DO-WB 382 2<br />

80003 Langer Norbert 18 DO-WB 383 3<br />

80004 Kolic Ivana 19 DO-WB 384 4<br />

10002 Schneider Daniela<br />

20002 Schmitz Michael<br />

30002 Feyerabend Werner<br />

40002 Peters Michael<br />

Auch hier wollen wir wieder die beiden Tabellen vertauschen:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Dienstwagen dw<br />

FULL JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYP<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14<br />

80002 Aliman Zafer 17 DO-WB 382 2<br />

80003 Langer Norbert 18 DO-WB 383 3<br />

80004 Kolic Ivana 19 DO-WB 384 4<br />

90001 Janssen Bernhard 9 DO-WB 429 14<br />

90002 H<strong>in</strong>kel Mart<strong>in</strong>a<br />

100001 Grosser Horst 10 DO-WB 4210 14<br />

100002 Friedrichsen Angel<strong>in</strong>a<br />

110001 Eggert Louis 11 DO-WB 4211 14<br />

110002 Deiters Gisela<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14<br />

120002 Baber Yvonne<br />

13 DO-WB 111 16<br />

Verknüpfung mehrerer Tabellen<br />

Bei detailliertem Vergleich des vollständigen Ergebnisses ergibt sich: Es ist gleich,<br />

nur <strong>in</strong> anderer Sortierung. Das sollte nicht mehr verwundern.<br />

21.5. Verknüpfung mehrerer Tabellen<br />

Alle bisherigen Beispiele kranken daran, dass als Typ des Dienstwagens nur<br />

die ID angegeben ist. Selbstverständlich möchte man die Typbezeichnung und<br />

den Hersteller lesen. Dazu müssen die beiden Tabellen Fahrzeugtyp und Fahrzeughersteller<br />

e<strong>in</strong>gebunden werden. Beim INNER JOIN war das ke<strong>in</strong> Problem;<br />

probieren wir aus, wie es beim OUTER JOIN aussehen könnte.<br />

195


OUTER JOIN<br />

21.5.1. Mehrere Tabellen parallel<br />

Aufgabe: Erweitern wir dazu die Aufstellung „alle Dienstwagen zusammen mit<br />

den zugeordneten Mitarbeitern“ um die Angabe zu den Fahrzeugen.<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,<br />

ft.Bezeichnung AS Typ, ft.Hersteller_ID AS FheID<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID<br />

JOIN Fahrzeugtyp ft ON dw.Fahrzeugtyp_ID = ft.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID<br />

100001 Grosser Horst 10 DO-WB 4210 14 A160 6<br />

110001 Eggert Louis 11 DO-WB 4211 14 A160 6<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6<br />

13 DO-WB 111 16 W211 (E-Klasse) 6<br />

50002 Braun Christian 14 DO-WB 352 2 Golf 1<br />

50003 Polovic Frantisek 15 DO-WB 353 3 Passat 1<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4 Kadett 2<br />

Der zweite JOIN wurde nicht genauer bezeichnet, ist also e<strong>in</strong> INNER JOIN. Das<br />

gleiche Ergebnis erhalten wir, wenn wir die Tabelle Fahrzeugtyp ausdrücklich als<br />

LEFT JOIN verknüpfen (bitte selbst ausprobieren!). Anders sieht es beim Versuch<br />

mit RIGHT JOIN oder FULL JOIN aus:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,<br />

ft.Bezeichnung AS Typ, ft.Hersteller_ID AS FheID<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON dw.Mitarbeiter_ID = mi.ID<br />

RIGHT | FULL JOIN Fahrzeugtyp ft ON dw.Fahrzeugtyp_ID = ft.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14 A160 6<br />

90001 Janssen Bernhard 9 DO-WB 429 14 A160 6<br />

100001 Grosser Horst 10 DO-WB 4210 14 A160 6<br />

110001 Eggert Louis 11 DO-WB 4211 14 A160 6<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6<br />

W204 (C-Klasse) 6<br />

13 DO-WB 111 16 W211 (E-Klasse) 6<br />

Saab 9-3 8<br />

S40 9<br />

C30 9<br />

Die beiden JOINs stehen sozusagen auf der gleichen Ebene; jede JOIN-Klausel<br />

wird für sich mit Dienstwagen verknüpft. An der Verknüpfung zwischen Dienstwagen<br />

und Mitarbeiter ändert sich nichts. Aber für die Fahrzeugtypen gilt:<br />

196


Verknüpfung mehrerer Tabellen<br />

• Das erste Beispiel benutzt e<strong>in</strong>en INNER JOIN, nimmt also für jeden vorhandenen<br />

Dienstwagen genau „se<strong>in</strong>en“ Typ.<br />

• Wenn man stattdessen e<strong>in</strong>en LEFT JOIN verwendet, erhält man alle vorhandenen<br />

Dienstwagen, zusammen mit den passenden Typen. Das ist faktisch identisch<br />

mit dem Ergebnis des INNER JOIN.<br />

• Das zweite Beispiel benutzt e<strong>in</strong>en RIGHT JOIN, das liefert alle registrierten<br />

Fahrzeugtypen und (soweit vorhanden) die passenden Dienstwagen.<br />

• Wenn man stattdessen e<strong>in</strong>en FULL JOIN verwendet, erhält man alle Komb<strong>in</strong>ationen<br />

von Dienstwagen und Mitarbeitern, zusammen mit allen registrierten<br />

Fahrzeugtypen. Das ist faktisch identisch mit dem Ergebnis des RIGHT JOIN.<br />

Sie sehen: Es kommt genau auf die gewünschten und die tatsächlich vorhandenen<br />

Verknüpfungen an.<br />

21.5.2. Gliederung durch Klammern<br />

Für Verknüpfungen, die durch Klammern gegliedert werden, nehmen wir e<strong>in</strong> anderes<br />

Beispiel, nämlich „Mitarbeiter RIGHT JOIN Dienstwagen“, denn die Fahrzeugtypen<br />

s<strong>in</strong>d e<strong>in</strong>e Ergänzung zu den Dienstwagen, nicht zu den Mitarbeitern<br />

(auch wenn den Abteilungsleitern e<strong>in</strong> Mercedes zugestanden wird, aber das ist<br />

e<strong>in</strong> anderes Thema und hat nichts mit <strong>SQL</strong> zu tun).<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,<br />

ft.Bezeichnung AS Typ, ft.Hersteller_ID AS FheID<br />

FROM Mitarbeiter mi<br />

RIGHT JOIN ( Dienstwagen dw<br />

JOIN Fahrzeugtyp ft ON ft.ID = dw.Fahrzeugtyp_id )<br />

ON dw.Mitarbeiter_ID = mi.ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP FHEID<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14 A160 6<br />

90001 Janssen Bernhard 9 DO-WB 429 14 A160 6<br />

100001 Grosser Horst 10 DO-WB 4210 14 A160 6<br />

110001 Eggert Louis 11 DO-WB 4211 14 A160 6<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 6<br />

13 DO-WB 111 16 W211 (E-Klasse) 6<br />

50002 Braun Christian 14 DO-WB 352 2 Golf 1<br />

50003 Polovic Frantisek 15 DO-WB 353 3 Passat 1<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4 Kadett 2<br />

Auch hier erhalten wir e<strong>in</strong> vergleichbares Ergebnis. Prüfen wir zunächst die Abfrage<br />

<strong>in</strong> der Klammer:<br />

197


OUTER JOIN<br />

• LEFT JOIN und INNER JOIN haben als Grundlage „alle Dienstwagen“, es wird<br />

also e<strong>in</strong>e Datenmenge „alle Dienstwagen“ (mit Zusatz<strong>in</strong>formationen über die<br />

Fahrzeugtypen) erstellt.<br />

• RIGHT JOIN und FULL JOIN gehen aus von „alle Fahrzeugtypen“, es wird<br />

also e<strong>in</strong>e Datenmenge „alle Fahrzeugtypen“ (mit Zusatz<strong>in</strong>formationen über<br />

die Dienstwagen) erstellt.<br />

Da der Ausdruck <strong>in</strong>nerhalb der Klammern zuerst ausgewertet wird, wird diese<br />

Datenmenge anschließend mit den Mitarbeitern verknüpft, soweit es der Verknüpfungsbed<strong>in</strong>gung<br />

auf der Basis von dw.Mitarbeiter_ID entspricht.<br />

Damit können wir nun auch den Hersteller mit se<strong>in</strong>em Namen anzeigen; benutzen<br />

wir wegen der bisherigen Erkenntnisse das erste Beispiel:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,<br />

ft.Bezeichnung AS Typ, fh.Name AS Hersteller<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.ID = dw.Mitarbeiter_ID<br />

INNER JOIN Fahrzeugtyp ft ON ft.ID = dw.Fahrzeugtyp_ID<br />

INNER JOIN Fahrzeughersteller fh ON fh.ID = ft.Hersteller_ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP HERSTELLER<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14 A160 Mercedes-Benz<br />

90001 Janssen Bernhard 9 DO-WB 429 14 A160 Mercedes-Benz<br />

100001 Grosser Horst 10 DO-WB 4210 14 A160 Mercedes-Benz<br />

110001 Eggert Louis 11 DO-WB 4211 14 A160 Mercedes-Benz<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 Mercedes-Benz<br />

13 DO-WB 111 16 W211 (E-Klasse) Mercedes-Benz<br />

50002 Braun Christian 14 DO-WB 352 2 Golf Volkswagen<br />

50003 Polovic Frantisek 15 DO-WB 353 3 Passat Volkswagen<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4 Kadett Opel<br />

80002 Aliman Zafer 17 DO-WB 382 2 Golf Volkswagen<br />

80003 Langer Norbert 18 DO-WB 383 3 Passat Volkswagen<br />

80004 Kolic Ivana 19 DO-WB 384 4 Kadett Opel<br />

21.6. Zusammenfassung<br />

In diesem Kapitel lernten Sie die Verwendung von OUTER JOIN kennen:<br />

• Mit dieser Verknüpfung werden auch Datensätze abgefragt und angezeigt, bei<br />

denen es <strong>in</strong> e<strong>in</strong>er der Tabellen ke<strong>in</strong>en zugeordneten Datensatz gibt.<br />

• Mit e<strong>in</strong>em LEFT JOIN erhält man alle Datensätze der l<strong>in</strong>ken Tabelle, ergänzt<br />

durch passende Angaben aus der rechten Tabelle.<br />

198


Übungen<br />

• Mit e<strong>in</strong>em RIGHT JOIN erhält man alle Datensätze der rechten Tabelle, ergänzt<br />

durch passende Angaben aus der l<strong>in</strong>ken Tabelle.<br />

• Mit e<strong>in</strong>em FULL JOIN erhält man alle Datensätze beider Tabellen, wenn möglich<br />

ergänzt durch passende Angaben aus der jeweils anderen Tabelle.<br />

Bei der Verknüpfung mehrerer Tabellen ist genau auf den JOIN-Typ und ggf. auf<br />

Klammerung zu achten.<br />

21.7. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 201.<br />

Übung 1 – Allgeme<strong>in</strong>es<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. Um alle Mitarbeiter mit Dienstwagen aufzulisten, benötigt man e<strong>in</strong>en<br />

LEFT OUTER JOIN.<br />

2. LEFT JOIN ist nur e<strong>in</strong>e Kurzschreibweise für LEFT OUTER JOIN und hat<br />

ke<strong>in</strong>e zusätzliche <strong>in</strong>haltliche Bedeutung.<br />

3. E<strong>in</strong> LEFT JOIN von zwei Tabellen enthält alle Zeilen, die nach Auswahlbed<strong>in</strong>gung<br />

<strong>in</strong> der l<strong>in</strong>ken Tabelle enthalten s<strong>in</strong>d.<br />

4. E<strong>in</strong> RIGHT JOIN von zwei Tabellen enthält nur noch diejenigen Zeilen, die<br />

nach der Verknüpfungsbed<strong>in</strong>gung <strong>in</strong> der l<strong>in</strong>ken Tabelle enthalten s<strong>in</strong>d.<br />

5. Wenn wir bei e<strong>in</strong>er LEFT JOIN-Abfrage mit zwei Tabellen die beiden Tabellen<br />

vertauschen und stattdessen e<strong>in</strong>en RIGHT JOIN verwenden, erhalten<br />

wir dieselben Zeilen <strong>in</strong> der Ergebnismenge.<br />

6. Wir erhalten dabei nicht nur dieselben Zeilen, sondern auch dieselbe Reihenfolge.<br />

Übung 2 – Allgeme<strong>in</strong>es<br />

Was ist am folgenden SELECT-Befehl falsch und warum?<br />

Aufgabe: Gesucht werden Komb<strong>in</strong>ationen von Fahrzeug-Kennzeichen und<br />

Fahrzeugtypen, wobei alle Typen aufgeführt werden sollen; es werden nur die<br />

ersten 20 Fahrzeuge nach ID benötigt.<br />

SELECT Kennzeichen, Bezeichnung<br />

FROM Fahrzeug fz<br />

LEFT JOIN Fahrzeugtyp ft ON fz.Fahrzeugtyp_ID = ft.ID<br />

WHERE fz.ID


OUTER JOIN<br />

Übung 3 – S<strong>in</strong>nvollen SELECT-Befehl erstellen<br />

Gesucht werden alle registrierten Versicherungsgesellschaften und (soweit vorhanden)<br />

deren Kunden mit Name, Vorname.<br />

Übung 4 – S<strong>in</strong>nvollen SELECT-Befehl erstellen<br />

Gesucht werden die Dienstwagen, deren Fahrzeugtypen sowie die Hersteller. Die<br />

Liste der Typen soll vollständig se<strong>in</strong>.<br />

Übung 5 – S<strong>in</strong>nvollen SELECT-Befehl erstellen<br />

Gesucht werden Komb<strong>in</strong>ationen von Mitarbeitern und ihren Dienstwagen (e<strong>in</strong>schließlich<br />

Typ). Es geht um die Abteilungen 1 bis 5; auch nicht-persönliche<br />

Dienstwagen sollen aufgeführt werden.<br />

Übung 6 – S<strong>in</strong>nvollen SELECT-Befehl erstellen<br />

Gesucht werden alle registrierten Versicherungsgesellschaften sowie alle Kunden<br />

mit Name, Vorname, soweit der Nachname mit 'S' beg<strong>in</strong>nt.<br />

Übung 7 – RIGHT oder LEFT<br />

Vertauschen Sie <strong>in</strong> der Lösung von Übung 5 die beiden Tabellen Mitarbeiter und<br />

Dienstwagen und erläutern Sie:<br />

1. Warum werden jetzt mehr Mitarbeiter angezeigt, und zwar auch solche<br />

ohne Dienstwagen?<br />

2. Warum fehlt jetzt der „nicht-persönliche“ Dienstwagen?<br />

Übung 8 – SELECT-Befehl mit mehreren Tabellen<br />

Gesucht werden Angaben zu den Mitarbeitern und den Dienstwagen. Beim Mitarbeiter<br />

sollen Name und Vorname angegeben werden, bei den Dienstwagen Bezeichnung<br />

und Name des Herstellers. Die Liste aller Fahrzeugtypen soll vollständig<br />

se<strong>in</strong>.<br />

Übung 9 – SELECT-Befehl mit mehreren Tabellen<br />

Ergänzen Sie die Lösung zu Übung 8 <strong>in</strong>sofern, dass nur Mitarbeiter der Abteilungen<br />

1 bis 5 angezeigt werden; die Zeilen ohne Mitarbeiter sollen unverändert<br />

ausgegeben werden.<br />

200


Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 199.<br />

Lösung zu Übung 1 – Allgeme<strong>in</strong>es<br />

Die Aussagen 2, 3, 5 s<strong>in</strong>d richtig, die Aussagen 1, 4, 6 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – Allgeme<strong>in</strong>es<br />

Richtig ist folgender Befehl:<br />

SELECT Kennzeichen, Bezeichnung<br />

FROM Fahrzeug fz<br />

RIGHT JOIN Fahrzeugtyp ft ON fz.Fahrzeugtyp_ID = ft.ID<br />

WHERE fz.ID


OUTER JOIN<br />

Die IS NULL-Prüfung wird wegen der „nicht-persönlichen“ Dienstwagen benötigt.<br />

Lösung zu Übung 6 – S<strong>in</strong>nvollen SELECT-Befehl erstellen<br />

SELECT Bezeichnung, Name, Vorname<br />

FROM Versicherungsgesellschaft vg<br />

full JOIN Versicherungsnehmer vn ON vg.id = vn.Versicherungsgesellschaft_id<br />

WHERE vn.Name LIKE ’S%’ OR vn.id IS NULL<br />

ORDER BY Bezeichnung, Name, Vorname;<br />

Lösung zu Übung 7 – RIGHT oder LEFT<br />

1. Bei e<strong>in</strong>em RIGHT JOIN werden alle E<strong>in</strong>träge der rechten Tabelle angezeigt.<br />

„Rechts“ stehen jetzt die Mitarbeiter, also werden alle Mitarbeiter der betreffenden<br />

Abteilungen angezeigt.<br />

2. Bei e<strong>in</strong>em RIGHT JOIN werden die E<strong>in</strong>träge der l<strong>in</strong>ken Tabelle nur dann angezeigt,<br />

wenn sie zu e<strong>in</strong>em E<strong>in</strong>trag der rechten Tabelle gehören. Der „nichtpersönliche“<br />

Dienstwagen aus der l<strong>in</strong>ken Tabelle gehört aber zu ke<strong>in</strong>em<br />

der Mitarbeiter.<br />

Lösung zu Übung 8 – SELECT-Befehl mit mehreren Tabellen<br />

SELECT mi.Name, mi.Vorname,<br />

dw.Kennzeichen, ft.Bezeichnung, fh.Name AS HST<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.id = dw.Mitarbeiter_id<br />

RIGHT JOIN Fahrzeugtyp ft ON ft.Id = dw.Fahrzeugtyp_id<br />

INNER JOIN Fahrzeughersteller fh ON fh.Id = ft.Hersteller_id;<br />

H<strong>in</strong>weise: Die Reihenfolge der JOINs ist nicht e<strong>in</strong>deutig; der LEFT JOIN kann<br />

auch später kommen. Wichtig ist, dass die Verb<strong>in</strong>dung Dienstwagen → Fahrzeugtyp<br />

e<strong>in</strong> RIGHT JOIN ist (oder bei Vertauschung der Tabellen e<strong>in</strong> LEFT JOIN).<br />

Lösung zu Übung 9 – SELECT-Befehl mit mehreren Tabellen<br />

202<br />

SELECT mi.Name, mi.Vorname,<br />

dw.Kennzeichen, ft.Bezeichnung, fh.Name AS HST<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.id = dw.Mitarbeiter_id<br />

RIGHT JOIN Fahrzeugtyp ft ON ft.Id = dw.Fahrzeugtyp_id<br />

INNER JOIN Fahrzeughersteller fh ON fh.Id = ft.Hersteller_id<br />

WHERE (mi.Abteilung_id


22. Mehr zu JOIN<br />

22.1. Welcher JOIN-Typ passt wann? . . . . . . . . . . . . . . . . . . . 203<br />

22.2. SELF JOIN – Verknüpfung mit sich selbst . . . . . . . . . . . . . 204<br />

22.3. CROSS JOIN – das kartesische Produkt . . . . . . . . . . . . . . 209<br />

22.4. WITH – Inl<strong>in</strong>e-View . . . . . . . . . . . . . . . . . . . . . . . . . . 210<br />

22.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />

22.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211<br />

22.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212<br />

Die folgenden Ergänzungen zu JOIN s<strong>in</strong>d <strong>in</strong> besonderen Situationen hilfreich.<br />

22.1. Welcher JOIN-Typ passt wann?<br />

Diese Frage stellt sich vor allem Anfängern sehr oft. Neben den (theoretischen)<br />

Überlegungen der vorigen Kapitel helfen oft besondere Beispiele wie dieses.<br />

Eltern und ihre K<strong>in</strong>der<br />

Wir haben zwei Tabellen: Paare (also Eltern) und K<strong>in</strong>der. Es gibt k<strong>in</strong>derlose Paare,<br />

Paare mit K<strong>in</strong>dern und Waisenk<strong>in</strong>der. Wir wollen die Eltern und K<strong>in</strong>der <strong>in</strong> Abfragen<br />

verknüpfen. Die Kreise entsprechen den Tabellen; also steht der l<strong>in</strong>ke Kreis<br />

für die Tabelle Paare und der rechte Kreis für die Tabelle K<strong>in</strong>der.<br />

LEFT JOIN Alle Paare und<br />

(falls es K<strong>in</strong>der<br />

gibt) auch diese<br />

INNER<br />

JOIN<br />

RIGHT<br />

JOIN<br />

LEFT JOIN<br />

IS NULL<br />

Nur Paare, die<br />

K<strong>in</strong>der haben<br />

Alle K<strong>in</strong>der und<br />

(falls es Eltern<br />

gibt) auch diese<br />

Nur Paare, die<br />

ke<strong>in</strong>e K<strong>in</strong>der<br />

haben<br />

SELECT * FROM Paare<br />

LEFT JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

SELECT * FROM Paare<br />

INNER JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

SELECT * FROM Paare<br />

RIGHT JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

SELECT * FROM Paare<br />

LEFT JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

WHERE K<strong>in</strong>der.Key IS NULL<br />

203


Mehr zu JOIN<br />

RIGHT<br />

JOIN<br />

IS NULL<br />

Nur Waisenk<strong>in</strong>der<br />

FULL JOIN Alle Paare und<br />

alle K<strong>in</strong>der<br />

FULL JOIN<br />

IS NULL<br />

Alle k<strong>in</strong>derlosen<br />

Paare und alle<br />

Waisenk<strong>in</strong>der<br />

SELECT * FROM Paare<br />

RIGHT JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

WHERE Paare.Key IS NULL<br />

SELECT * FROM Paare<br />

FULL JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

SELECT * FROM Paare<br />

FULL JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

WHERE K<strong>in</strong>der.Key IS NULL<br />

OR Paare.Key IS NULL<br />

Die zweite Variante – INNER JOIN – kann man auch so ausdrücken:<br />

LEFT JOIN<br />

IS NOT<br />

NULL<br />

Alle Paare und<br />

(falls es K<strong>in</strong>der<br />

gibt) auch<br />

diese, wobei es<br />

e<strong>in</strong> K<strong>in</strong>d geben<br />

muss<br />

SELECT * FROM Paare<br />

LEFT JOIN K<strong>in</strong>der<br />

ON Paare.Key = K<strong>in</strong>der.Key<br />

WHERE K<strong>in</strong>der.Key<br />

IS NOT NULL<br />

Tatsächlich s<strong>in</strong>d oft mehrere Wege möglich, wie bereits im letzten Kapitel gesagt<br />

wurde: Das Ergebnis für "A LEFT JOIN B" gleicht dem von "B RIGHT JOIN A".<br />

22.2. SELF JOIN – Verknüpfung mit sich selbst<br />

Solche Verknüpfungen s<strong>in</strong>d immer dann nötig, wenn Werte e<strong>in</strong>er e<strong>in</strong>zigen Spalte<br />

aus verschiedenen Datensätzen verbunden werden. Der JOIN dafür benutzt auf<br />

beiden Seiten dieselbe Tabelle ; diese beiden „Instanzen“ müssen durch<br />

e<strong>in</strong>en Alias unterschieden werden.<br />

SELECT <br />

FROM t1<br />

JOIN t2 ON <br />

WHERE <br />

H<strong>in</strong>weis: Es funktioniert nicht, wenn e<strong>in</strong>e der beiden Instanzen mit Alias und die<br />

andere ohne Alias benutzt wird. Dann kommt das DBMS erst recht durche<strong>in</strong>ander.<br />

In e<strong>in</strong>em Forum stand e<strong>in</strong>mal e<strong>in</strong> solches Problem mit e<strong>in</strong>er sehr vertrackten<br />

Ausgabe, bei dem diese Ursache erst nach längerer Diskussion klar wurde.<br />

Dies soll zunächst an zwei Beispielen umgesetzt werden.<br />

204


Beispiel 1<br />

SELF JOIN – Verknüpfung mit sich selbst<br />

Aufgabe: Zeige zu jedem Fahrzeug andere Fahrzeuge aus dem gleichen Kreis.<br />

Wir beschränken uns darauf, dass <strong>in</strong> e<strong>in</strong>er Zeile jeweils zwei von allen möglichen<br />

Komb<strong>in</strong>ationen angezeigt werden, auch wenn viele Angaben wiederholt werden.<br />

SELECT a.Kennzeichen, b.Kennzeichen<br />

FROM Fahrzeug a<br />

JOIN Fahrzeug b<br />

ON SUBSTRING(a.Kennzeichen FROM 1 FOR 3)<br />

= SUBSTRING(b.Kennzeichen FROM 1 FOR 3)<br />

WHERE a.Kennzeichen < b.Kennzeichen<br />

ORDER BY a.Kennzeichen;<br />

KENNZEICHEN KENNZEICHEN1<br />

BO-GH 102 BO-KL 678<br />

BOR-NO 234 BOR-PQ 567<br />

BOR-NO 234 BOR-RS 890<br />

BOR-PQ 567 BOR-RS 890<br />

GE-AB 123 GE-AC 246<br />

GE-AB 123 GE-EG 892<br />

GE-AC 246 GE-EG 892<br />

RE-CD 456 RE-LM 901<br />

RE-CD 456 RE-LM 902<br />

RE-CD 456 RE-LM 903<br />

Gesucht werden Komb<strong>in</strong>ationen e<strong>in</strong>es Fahrzeugs mit jeweils e<strong>in</strong>em anderen<br />

Fahrzeug, wobei die Bed<strong>in</strong>gung „gleicher Kreis“ erfüllt se<strong>in</strong> soll. Wir brauchen<br />

also <strong>in</strong> e<strong>in</strong>em SELECT-Befehl zwei Zugriffe auf die Tabelle Fahrzeug mit e<strong>in</strong>er<br />

passenden Auswahlbed<strong>in</strong>gung. (Diese haben wir ungenau formuliert, nämlich<br />

als Vergleich der ersten drei Zeichen, damit es nicht zu unübersichtlich wird.)<br />

Dies ist gleichzeitig e<strong>in</strong> Beispiel dafür, dass beliebige Bed<strong>in</strong>gungen (nicht nur<br />

die Gleichheit) möglich s<strong>in</strong>d. Überlegen Sie bitte auch, warum unter WHERE die<br />

„kle<strong>in</strong>er als“-Bed<strong>in</strong>gung benutzt wird.<br />

Beispiel 2<br />

Aufgabe: Zeige zu jedem Fahrzeug mit mehreren Schadensfällen den zeitlichen<br />

Abstand von e<strong>in</strong>em Vorfall zum nächsten an.<br />

Lösungsweg 1<br />

Wir benötigen für jedes Fahrzeug aus der Tabelle Schadensfall zwei E<strong>in</strong>träge mit<br />

Datum sowie die Anzahl der Tage als Differenz zweier Datumsangaben. Die Fahrzeuge<br />

s<strong>in</strong>d freilich erst über die Tabelle Zuordnung_SF_FZ zu f<strong>in</strong>den und müssen<br />

205


Mehr zu JOIN<br />

zusätzlich verbunden werden. Außerdem s<strong>in</strong>d die Fahrzeuge und die Reihenfolge<br />

der Datumsangaben zu kontrollieren.<br />

Datumsvergleich als Teil der Auswahlbed<strong>in</strong>gung<br />

SELECT fz.ID, fz.Kennzeichen,<br />

sf1.Datum AS Datum1, sf2.Datum AS Datum2, sf2.Datum - sf1.Datum AS Abstand<br />

FROM Zuordnung_SF_FZ zu1<br />

JOIN Zuordnung_SF_FZ zu2 ON zu1.Fahrzeug_ID = zu2.Fahrzeug_ID<br />

JOIN Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID<br />

JOIN Schadensfall sf2 ON zu2.Schadensfall_ID = sf2.Id<br />

JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID<br />

WHERE sf1.Datum < sf2.Datum<br />

AND sf2.Datum = ( SELECT MIN(sf3.Datum)<br />

FROM Schadensfall sf3<br />

JOIN Zuordnung_SF_FZ zu3<br />

ON zu3.Schadensfall_ID = sf3.id<br />

WHERE sf1.Datum < sf3.Datum<br />

AND zu3.Fahrzeug_ID = zu1.Fahrzeug_ID )<br />

ORDER BY fz.ID, Datum1;<br />

ID KENNZEICHEN DATUM1 DATUM2 ABSTAND<br />

4 GE-AB 123 03.02.2007 05.10.2008 610<br />

6 HER-EF 789 19.12.2007 21.06.2009 550<br />

7 BO-GH 102 11.07.2007 13.03.2009 611<br />

7 BO-GH 102 13.03.2009 01.08.2009 141<br />

Lösungswege 2 und 3<br />

Alternativen bieten die folgenden Lösungen:<br />

206<br />

Datumsvergleich als Teil der Verknüpfungsbed<strong>in</strong>gungen<br />

SELECT fz.ID, fz.Kennzeichen,<br />

sf1.Datum AS Datum1, sf2.Datum AS Datum2, sf2.Datum - sf1.Datum AS Abstand<br />

FROM Zuordnung_SF_FZ zu1<br />

JOIN Zuordnung_SF_FZ zu2 ON zu1.Fahrzeug_ID = zu2.Fahrzeug_ID<br />

JOIN Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID<br />

JOIN Schadensfall sf2<br />

ON zu2.Schadensfall_ID = sf2.Id<br />

AND sf1.Datum < sf2.Datum<br />

AND sf2.Datum = ( SELECT MIN(sf3.Datum)<br />

FROM Schadensfall sf3<br />

JOIN Zuordnung_SF_FZ zu3<br />

ON zu3.Schadensfall_ID = sf3.ID<br />

WHERE sf1.Datum < sf3.Datum<br />

AND zu3.Fahrzeug_ID = zu1.Fahrzeug_ID<br />

)<br />

JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID<br />

ORDER BY fz.ID, Datum1;


SELF JOIN – Verknüpfung mit sich selbst<br />

Datumsvergleich als Funktion bei den SELECT-Spalten<br />

SELECT fz.ID, fz.Kennzeichen,<br />

sf1.Datum AS Datum1, MIN(sf2.Datum) AS Datum2,<br />

MIN(sf2.Datum - sf1.Datum) AS Abstand<br />

FROM Zuordnung_SF_FZ zu1<br />

JOIN Zuordnung_SF_FZ zu2 ON zu1.Fahrzeug_ID = zu2.Fahrzeug_ID<br />

JOIN Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID<br />

JOIN Schadensfall sf2 ON zu2.Schadensfall_ID = sf2.Id<br />

AND sf1.Datum < sf2.Datum<br />

JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID<br />

GROUP BY fz.ID, fz.Kennzeichen, sf1.Datum<br />

ORDER BY fz.ID, Datum1;<br />

Erläuterungen<br />

In dieser Aufgabe stecken mehrere Probleme:<br />

• Die Angaben aus der Spalte Datum der Tabelle Schadensfall müssen zweimal<br />

geholt werden.<br />

• Zu jedem Schadensfall wird der E<strong>in</strong>trag der Tabelle Zuordnung_SF_FZ benötigt,<br />

weil die Schadensfälle für jedes Fahrzeug gesucht werden.<br />

• Das Datum, das zu sf1 gehört, muss immer „kle<strong>in</strong>er“ se<strong>in</strong>, also früher liegen als<br />

das Datum, das zu sf2 gehört.<br />

• Außerdem benötigen wir „irgendwo“ die E<strong>in</strong>schränkung, dass zum Vergleich<br />

nur der jeweils folgende Schadensfall genommen werden darf, also das<br />

MINimum der späteren E<strong>in</strong>träge:<br />

• Die erste Lösung verwendet dafür e<strong>in</strong>e Unterabfrage für e<strong>in</strong>e Auswahlbed<strong>in</strong>gung.<br />

• Die zweite Lösung arbeitet mit e<strong>in</strong>er Unterabfrage bei der Verknüpfungsbed<strong>in</strong>gung.<br />

• Die dritte Lösung benutzt das MINimum direkt als Spaltenfunktion und verlangt<br />

„zum Ausgleich“ e<strong>in</strong>e GROUP BY-Klausel.<br />

Die Lösung benötigt deshalb mehrfach verknüpfte Tabellen:<br />

• Als Grundlage wird die Tabelle der Zuordnungen zwischen Schadensfällen und<br />

Fahrzeugen zu1 verwendet.<br />

• Hauptverknüpfung ist der Self-Jo<strong>in</strong> zu2 auf dieselbe Tabelle, weil nur solche<br />

E<strong>in</strong>träge verknüpft werden sollen, die sich auf dasselbe Fahrzeug beziehen.<br />

• Zu jedem Schadensfall aus zu1 gehören die detaillierten Angaben aus sf1.<br />

• Zu jedem Schadensfall aus zu2 gehören die detaillierten Angaben aus sf2.<br />

• Ergänzend benötigen wir das Kennzeichen des betreffenden Fahrzeugs, also<br />

e<strong>in</strong>en JOIN auf Fahrzeug.<br />

• Vor allem s<strong>in</strong>d die Auswahlbed<strong>in</strong>gungen für die Datumsangaben e<strong>in</strong>zubauen.<br />

207


Mehr zu JOIN<br />

Welche Lösung die Datenbank am wenigsten belastet, kann nicht generell gesagt<br />

werden, weil es von zu vielen Umständen abhängt.<br />

Erweiterung durch e<strong>in</strong>en OUTER JOIN<br />

Bei diesen Lösungen stehen nicht alle Schadensfälle im Ergebnis, weil es nur um<br />

den zeitlichen Abstand g<strong>in</strong>g. Wenn beispielsweise auch die Schadenshöhe gewünscht<br />

wird, müssen wir dafür s<strong>org</strong>en, dass von sf1 oder sf2 alle E<strong>in</strong>träge angezeigt<br />

werden; wir brauchen also e<strong>in</strong>en OUTER JOIN wie zum Beispiel (auf der<br />

Grundlage der letzten Version) so:<br />

SELECT fz.ID, fz.Kennzeichen,<br />

sf1.Datum AS Datum1, MIN(sf2.Datum) AS Datum2,<br />

MIN(sf2.Datum - sf1.Datum) AS Abstand,<br />

sf1.Schadenshoehe<br />

FROM Zuordnung_SF_FZ zu1<br />

LEFT JOIN Zuordnung_SF_FZ zu2 ON zu1.Fahrzeug_ID = zu2.Fahrzeug_ID<br />

LEFT JOIN Schadensfall sf1 ON zu1.Schadensfall_ID = sf1.ID<br />

LEFT JOIN Schadensfall sf2 ON zu2.Schadensfall_ID = sf2.Id<br />

AND sf1.Datum < sf2.Datum<br />

LEFT JOIN Fahrzeug fz ON zu1.Fahrzeug_ID = fz.ID<br />

GROUP BY fz.ID, fz.Kennzeichen, sf1.Datum, sf1.Schadenshoehe<br />

ORDER BY fz.ID, Datum1;<br />

ID KENNZEICHEN DATUM1 DATUM2 ABSTAND SCHADENSHOEHE<br />

3 RE-LM 903 27.05.2008 1.438,75<br />

4 GE-AB 123 03.02.2007 05.10.2008 610 1.234,50<br />

4 GE-AB 123 05.10.2008 1.983,00<br />

5 RE-CD 456 11.07.2007 2.066,00<br />

6 HER-EF 789 19.12.2007 21.06.2009 550 3.715,60<br />

6 HER-EF 789 21.06.2009 865,00<br />

7 BO-GH 102 11.07.2007 13.03.2009 611 2.066,00<br />

7 BO-GH 102 13.03.2009 01.08.2009 141 4.092,15<br />

7 BO-GH 102 01.08.2009 2.471,50<br />

Wir nehmen es h<strong>in</strong>, dass dann alle Schadensfälle aufgeführt werden, auch für<br />

die Fahrzeuge, die nur e<strong>in</strong>mal „aufgefallen“ s<strong>in</strong>d. Dies ist e<strong>in</strong>e Folge davon, dass<br />

Grundlage aller Verknüpfungen die Tabelle der Zuordnungen se<strong>in</strong> musste.<br />

Bei allen solchen Situationen müssen Sie genau überlegen, wie die verschiedenen<br />

Instanzen mite<strong>in</strong>ander verknüpft werden und wie die übrigen Bed<strong>in</strong>gungen<br />

e<strong>in</strong>zub<strong>in</strong>den s<strong>in</strong>d. Oft führen erst mehrere Versuche zum Ziel. Hilfreich s<strong>in</strong>d<br />

auch die Ausführungspläne, die e<strong>in</strong> DBMS anbieten kann.<br />

Weitere Situationen<br />

Zum Schluss sollen noch e<strong>in</strong> paar andere Beispiele erwähnt werden, bei denen<br />

e<strong>in</strong> Self-Jo<strong>in</strong> hilft.<br />

208


CROSS JOIN – das kartesische Produkt<br />

• Wenn bei den Dienstwagen die privat gefahrenen Strecken abgerechnet werden<br />

sollen, können der km-Stand beim Fahrtantritt und beim Fahrtende <strong>in</strong><br />

derselben Spalte, aber <strong>in</strong> getrennten Datensätzen gespeichert werden.<br />

• Doppelte Adressen <strong>in</strong>nerhalb e<strong>in</strong>er Adressendatei können aufgespürt werden<br />

(siehe Übung 3).<br />

• Wenn <strong>in</strong> der Tabelle Mitarbeiter zu e<strong>in</strong>em Mitarbeiter der Leiter der Abteilung<br />

gesucht wird, benötigen wir wegen des doppelten Zugriffs auf dieselbe Tabelle<br />

ebenfalls e<strong>in</strong>en Self-Jo<strong>in</strong>.<br />

22.3. CROSS JOIN – das kartesische Produkt<br />

Mit dieser speziellen Formulierung kann man deutlich machen, dass man wirklich<br />

e<strong>in</strong> kartesisches Produkt herstellen will und nicht etwa nur die Verknüpfungsbed<strong>in</strong>gung<br />

vergessen hat:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

CROSS JOIN Dienstwagen dw;<br />

Als Ergebnis wird tatsächlich jede Komb<strong>in</strong>ation e<strong>in</strong>es Mitarbeiters mit e<strong>in</strong>em<br />

Dienstwagen ausgegeben, also n mal m Sätze − wie beim allerersten Versuch im<br />

Kapitel E<strong>in</strong>fache Tabellenverknüpfung 1 . Man kann die Ergebnismenge auch e<strong>in</strong>schränken<br />

durch e<strong>in</strong>e WHERE-Klausel:<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

CROSS JOIN Dienstwagen dw<br />

WHERE mi.Name LIKE ’S%’ AND CHAR_LENGTH(dw.Kennzeichen) = 10;<br />

H<strong>in</strong>weis: Die DBMS verhalten sich bei e<strong>in</strong>em CROSS JOIN unterschiedlich; teilweise<br />

ist e<strong>in</strong> CROSS JOIN mit WHERE-Klausel nichts anderes als e<strong>in</strong> INNER<br />

JOIN.<br />

Der Nutzen des CROSS JOIN wird bei unserer sparsamen Beispieldatenbank<br />

nicht klar. Unter Oracle wäre folgendes Verfahren möglich und hilfreich:<br />

Aufgabe: Kontrollieren Sie mit der Tabelle Fahrzeugbuchung, welche Fahrzeuge<br />

am 2.12.2009 im Fuhrpark zur Verfügung stehen.<br />

1 Kapitel 19 auf Seite 173<br />

209


Mehr zu JOIN<br />

Oracle-Quelltext<br />

SELECT mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS Typ<br />

FROM Mitarbeiter mi<br />

CROSS JOIN Dienstwagen dw<br />

INNER JOIN Fahrzeugbuchung fb<br />

ON dw.Kennzeichen = fb.Kennzeichen<br />

WHERE fb.Datum = to_date(’02.12.2009’,’dd.mm.yyyy’)<br />

AND fb.Status = ’noch nicht gebucht’;<br />

22.4. WITH – Inl<strong>in</strong>e-View<br />

Oft kommt es vor, dass man die Daten aus e<strong>in</strong>er Tabelle erst bearbeiten möchte,<br />

bevor man sie mit e<strong>in</strong>er anderen Tabelle verknüpft. Beispiel:<br />

SELECT Kuerzel, Bezeichnung, Anzahl_Mitarbeiter FROM Abteilung<br />

INNER JOIN ( SELECT Abteilung_ID, COUNT(*) AS Anzahl_Mitarbeiter<br />

FROM Mitarbeiter<br />

GROUP BY Abteilung_ID<br />

) MA_Anzahl<br />

ON Abteilung.ID = MA_Anzahl.Abteilung_ID ;<br />

Dabei wird zunächst nach der Tabelle Mitarbeiter die Anzahl der Mitarbeiter für<br />

jede Abteilung bestimmt. Das Ergebnis wird wie e<strong>in</strong>e Tabelle MA_Anzahl behandelt<br />

und über Abteilung_ID mit der Tabelle Abteilung verknüpft.<br />

Diese Syntax ist ziemlich verschachtelt. Man kann sie auch so schreiben:<br />

WITH MA_Anzahl AS<br />

( SELECT Abteilung_ID, COUNT(*) AS Anzahl_Mitarbeiter<br />

FROM Mitarbeiter<br />

GROUP BY Abteilung_ID<br />

)<br />

SELECT Kuerzel, Bezeichnung, Anzahl_Mitarbeiter<br />

FROM Abteilung<br />

INNER JOIN MA_Anzahl<br />

ON Abteilung.ID = MA_Anzahl.Abteilung_ID ;<br />

MA_Anzahl wird benutzt wie e<strong>in</strong>e VIEW, die allerd<strong>in</strong>gs nicht permanent angelegt<br />

wird, sondern die nur für die Ausführung dieses e<strong>in</strong>en <strong>SQL</strong>-Befehls gültig ist.<br />

Der Unterschied liegt „nur“ dar<strong>in</strong>, dass die Unterabfrage herausgelöst wird und<br />

durch WITH als temporäre abgeleitete Tabelle e<strong>in</strong>gebunden wird.<br />

Welche Variante man besser f<strong>in</strong>det, ist sicher Geschmackssache und hat auch<br />

damit zu tun, welche Formulierung man gewöhnt ist. In vielen Fällen wären Formulierungen<br />

ohne WITH viel länger und komplizierter.<br />

210


22.5. Zusammenfassung<br />

Zusammenfassung<br />

In diesem Kapitel lernten Sie e<strong>in</strong>ige weitere Möglichkeiten im Zusammenhang<br />

mit JOINs kennen.<br />

• Für bestimmte Anforderungen s<strong>in</strong>d Verknüpfungen e<strong>in</strong>er Tabelle mit sich<br />

selbst s<strong>in</strong>nvoll oder notwendig.<br />

• In diesen Fällen s<strong>in</strong>d die Auswahl- und Verknüpfungsbed<strong>in</strong>gungen besonders<br />

s<strong>org</strong>fältig zu bestimmen.<br />

• Durch WITH können Verknüpfungen über JOINs übersichtlicher werden.<br />

22.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 212.<br />

Übung 1 – Fragen zum Verständnis<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche falsch?<br />

1. E<strong>in</strong>e Tabelle kann mit sich selbst verknüpft werden.<br />

2. SELF JOIN ist nur e<strong>in</strong> <strong>in</strong>haltlicher Begriff, aber ke<strong>in</strong> <strong>SQL</strong>-Schlüsselwort.<br />

3. Bei e<strong>in</strong>em SELF JOIN s<strong>in</strong>d nur INNER JOINs erlaubt.<br />

4. E<strong>in</strong>e bestimmte Tabelle darf <strong>in</strong> e<strong>in</strong>em SELF JOIN nur zweimal verwendet<br />

werden.<br />

5. Für e<strong>in</strong>en SELF JOIN können Tabellen-Aliase benutzt werden, aber sie s<strong>in</strong>d<br />

nicht überall erforderlich.<br />

6. E<strong>in</strong> CROSS JOIN ist e<strong>in</strong>e Verknüpfung zweier Tabellen ohne Verknüpfungsbed<strong>in</strong>gung.<br />

7. Bei e<strong>in</strong>em CROSS JOIN darf sich die WHERE-Klausel nicht auf die (rechte)<br />

Tabelle des JOINs beziehen.<br />

8. Die Schreibweise mit WITH ist ke<strong>in</strong> Sonderfall e<strong>in</strong>es JOINs, sondern e<strong>in</strong>e<br />

übersichtlichere Schreibweise, wenn mehrere Tabellen verknüpft werden.<br />

Übung 2 – Verknüpfung e<strong>in</strong>er Tabelle mit sich selbst<br />

Suchen Sie zu jedem Mitarbeiter den Namen und Vornamen des Leiters der Abteilung.<br />

Die Abteilungsleiter <strong>in</strong> unserer e<strong>in</strong>fachen Firmenhierarchie haben ke<strong>in</strong>en<br />

V<strong>org</strong>esetzten; sie sollen <strong>in</strong> der Liste deshalb nicht aufgeführt werden.<br />

211


Mehr zu JOIN<br />

Übung 3 – Doppelte Adressen suchen<br />

Suchen Sie E<strong>in</strong>träge <strong>in</strong> der Tabelle Versicherungsnehmer, bei denen Name, Vorname,<br />

PLZ, Strasse übere<strong>in</strong>stimmen. Jeweils zwei dieser Adressen sollen mit ihrer<br />

ID und den übere<strong>in</strong>stimmenden Angaben aufgeführt werden.<br />

H<strong>in</strong>weis: Benutzen Sie e<strong>in</strong>en JOIN, der sich nicht auf übere<strong>in</strong>stimmende IDs bezieht.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 211.<br />

Lösung zu Übung 1 – Fragen zum Verständnis<br />

Die Aussagen 1, 2, 6, 8 s<strong>in</strong>d wahr, die Aussagen 3, 4, 5, 7 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – Verknüpfung e<strong>in</strong>er Tabelle mit sich selbst<br />

SELECT mi1.Abteilung_ID AS Abt, mi1.Name, mi1.Vorname,<br />

mi2.Name AS LtrName, mi2.Vorname AS LtrVorn<br />

FROM Mitarbeiter mi1<br />

JOIN Abteilung ab ON mi1.Abteilung_ID = ab.ID<br />

JOIN Mitarbeiter mi2 ON mi2.Abteilung_ID = ab.ID<br />

WHERE mi2.Ist_Leiter = ’J’<br />

AND mi1.Ist_Leiter = ’N’;<br />

Lösung zu Übung 3 – Doppelte Adressen suchen<br />

SELECT a.Name, a.Vorname, a.PLZ, a.Strasse, a.ID, b.ID<br />

FROM Versicherungsnehmer a<br />

JOIN Versicherungsnehmer b<br />

ON a.Name = b.Name AND a.Vorname = b.Vorname<br />

AND a.PLZ = b.PLZ AND a.Strasse = b.Strasse<br />

WHERE a.ID < b.ID;<br />

22.7. Siehe auch<br />

Bei Wikipedia f<strong>in</strong>den Sie weitere Erläuterungen:<br />

• Auswertungsplan 2 , auch „Ausführungsplan“ genannt<br />

2 http://de.wikipedia.<strong>org</strong>/wiki/Auswertungsplan<br />

212


23. Nützliche Erweiterungen<br />

23.1. Beschränkung auf e<strong>in</strong>e Anzahl Zeilen . . . . . . . . . . . . . . . 213<br />

23.2. Mehrere Abfragen zusammenfassen . . . . . . . . . . . . . . . . 220<br />

23.3. CASE WHEN – Fallunterscheidungen . . . . . . . . . . . . . . . 223<br />

23.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 228<br />

23.5. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229<br />

Dieses Kapitel behandelt verschiedene Erweiterungen des SELECT-Befehls.<br />

Die Beispiele beziehen sich auch hier auf den Anfangsbestand der Beispieldatenbank;<br />

auf die Ausgabe der selektierten Datensätze wird wiederum weitgehend<br />

verzichtet. Bitte probieren Sie alle Beispiele aus und nehmen Sie verschiedene<br />

Änderungen vor, um die Auswirkungen zu erkennen.<br />

23.1. Beschränkung auf e<strong>in</strong>e Anzahl Zeilen<br />

Häufig will man nicht sofort das gesamte Ergebnis sehen, sondern nur e<strong>in</strong>en Teil<br />

der Zeilen.<br />

Vor allem im Netzwerk kostet es se<strong>in</strong>e Zeit, e<strong>in</strong>e größere Menge von Datensätzen<br />

zu übertragen. Es ist deshalb oft praktisch, zunächst e<strong>in</strong>en zu holen und anzuzeigen.<br />

Während der Anwender sich mit diesem Teilergebnis beschäftigt, wird<br />

„im H<strong>in</strong>tergrund“ der nächste Abschnitt geholt usw.<br />

Im <strong>SQL</strong>-Standard gibt es dafür (noch) ke<strong>in</strong> Verfahren. Abweichend vom üblichen<br />

V<strong>org</strong>ehen <strong>in</strong> diesem Buch erhalten Sie Lösungen für verschiedene DBMS.<br />

Anstelle konstanter Werte (ohne Klammern) kann <strong>in</strong> allen folgenden Fällen auch<br />

e<strong>in</strong> <strong>SQL</strong>-Ausdruck (<strong>in</strong> Klammern) angegeben werden.<br />

23.1.1. Firebird: FIRST SKIP oder ROWS<br />

Firebird bietet gleich zwei Lösungen an, die erste mit FIRST / SKIP:<br />

213


Nützliche Erweiterungen<br />

SELECT [DISTINCT]<br />

[ FIRST ]<br />

[ SKIP ]<br />

FROM ... /* usw. */<br />

Der FIRST-Parameter gibt an, wie viele Zeilen am Anfang anzuzeigen s<strong>in</strong>d; der<br />

SKIP-Parameter legt fest, wie viele Zeilen davor übersprungen werden sollen.<br />

Beide Parameter werden e<strong>in</strong>zeln oder zusammen benutzt; sie folgen direkt als<br />

erste Klausel nach DISTINCT, noch vor der Spaltenliste. E<strong>in</strong>ige Beispiele:<br />

Nur FIRST zeigt die ersten Zeilen.<br />

Firebird-Quelltext<br />

SELECT FIRST 10<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

Nur SKIP überspr<strong>in</strong>gt die ersten Zeilen.<br />

Firebird Quelltext<br />

SELECT SKIP 10<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

FIRST zeigt die ersten 10 Zeilen an, aber wegen SKIP werden vorher 5 Zeilen<br />

übersprungen.<br />

Firebird-Quelltext<br />

SELECT FIRST 10 SKIP 5<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

Mit e<strong>in</strong>em Ausdruck kann dafür ges<strong>org</strong>t werden, dass etwa das erste Viertel der<br />

Datensätze abgerufen wird:<br />

214<br />

Firebird-Quelltext<br />

SELECT FIRST ( SELECT COUNT(*) FROM Mitarbeiter) / 4 )<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

Der Wert für FIRST wird aus der Anzahl der Datensätze berechnet.


Beschränkung auf e<strong>in</strong>e Anzahl Zeilen<br />

Die zweite Firebird-Variante benutzt mit ROWS direkt Zeilennummern:<br />

SELECT ... FROM ... WHERE ...<br />

ORDER BY ...<br />

ROWS [ TO ]<br />

Die ROWS-Parameter legen fest, dass (nur) e<strong>in</strong>e bestimmte Anzahl Zeilen angezeigt<br />

werden sollen, die durch die Zeilennummern gekennzeichnet s<strong>in</strong>d.<br />

• Wenn nur ROWS benutzt wird, bezeichnet die Gesamtzahl der angezeigten<br />

Zeilen.<br />

• Wenn ROWS zusammen mit TO benutzt wird, ist die erste Zeilennummer<br />

und die letzte Zeilennummer.<br />

E<strong>in</strong>ige Beispiele:<br />

Ausgabe der Zeilen 10 bis 20 (also <strong>in</strong>sgesamt 11 Zeilen)<br />

Firebird Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name<br />

ROWS 10 TO 20;<br />

Der erste Datensatz gemäß Sortierung<br />

Firebird Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name<br />

ROWS 1;<br />

Der letzte Datensatz gemäß Sortierung<br />

Firebird Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name DESC<br />

ROWS 1;<br />

Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird,<br />

ist oft sehr nützlich.<br />

Bei e<strong>in</strong>er Interbase-Datenbank s<strong>in</strong>d auch prozentuale Angaben möglich; das ist<br />

bei Firebird entfallen.<br />

215


Nützliche Erweiterungen<br />

23.1.2. Microsoft <strong>SQL</strong>: TOP<br />

Der MS-<strong>SQL</strong> Server benutzt diese Syntax:<br />

SELECT [DISTINCT]<br />

TOP ( ) [PERCENT] [WITH TIES]<br />

FROM ... /* usw. */<br />

Die TOP-Parameter legen fest, dass (nur) e<strong>in</strong>e bestimmte Anzahl Zeilen angezeigt<br />

werden soll, die durch die Zeilennummern gekennzeichnet s<strong>in</strong>d. Diese Parameter<br />

folgen als erste Klausel nach DISTINCT, noch vor der Spaltenliste.<br />

• bezeichnet die Anzahl aller angezeigten Zeilen. Es wird empfohlen, die<br />

Klammern immer zu setzen.<br />

• Wenn TOP zusammen mit PERCENT benutzt wird, handelt es sich dabei um<br />

die prozentuale Angabe der Zeilenzahl.<br />

• WITH TIES muss zusammen mit ORDER BY benutzt werden und liefert dann<br />

zusätzliche doppelte Zeilen, wenn der letzte Wert nach der Sortierung gleich<br />

ist den Werten <strong>in</strong> danach folgenden Zeilen.<br />

E<strong>in</strong>ige Beispiele:<br />

Nur TOP zeigt die ersten Zeilen.<br />

MS-<strong>SQL</strong>-Quelltext<br />

SELECT TOP ( 10 )<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

TOP + PERCENT zeigt z. B. das erste Viertel an.<br />

MS-<strong>SQL</strong>-Quelltext<br />

SELECT TOP ( 25 ) PERCENT<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name;<br />

Der letzte Datensatz gemäß Sortierung<br />

216<br />

MS-<strong>SQL</strong>-Quelltext<br />

SELECT TOP ( 1 )<br />

ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name DESC;


Beschränkung auf e<strong>in</strong>e Anzahl Zeilen<br />

Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird,<br />

ist oft sehr nützlich.<br />

23.1.3. My<strong>SQL</strong> und Postgre<strong>SQL</strong>: LIMIT<br />

Diese DBMS benutzen den LIMIT-Parameter. Dieser Parameter folgt nach<br />

ORDER BY, wobei die Sortierung nicht angegeben werden muss.<br />

SELECT ... FROM ... WHERE ...<br />

ORDER BY ...<br />

LIMIT OFFSET <br />

Dabei wird mit angegeben, wie viele Zeilen am Anfang angezeigt werden<br />

sollen. Mit kann nach dem Begriff OFFSET außerdem angegeben<br />

werden, wie viele Zeilen davor übersprungen werden sollen.<br />

Es werden die ersten 10 Zeilen angezeigt.<br />

My<strong>SQL</strong>-Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name<br />

LIMIT 10;<br />

Zuerst werden 5 Zeilen übersprungen; die ersten 10 Zeilen, die danach folgen,<br />

werden angezeigt.<br />

My<strong>SQL</strong>-Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name<br />

LIMIT 10 OFFSET 5;<br />

Der letzte Datensatz gemäß Sortierung:<br />

My<strong>SQL</strong>-Quelltext<br />

SELECT ID, Name, Vorname, Abteilung_ID AS Abt<br />

FROM Mitarbeiter<br />

ORDER BY Name DESC<br />

LIMIT 1;<br />

Vor allem das letzte Beispiel, bei dem mit DESC die Sortierung umgekehrt wird,<br />

ist oft sehr nützlich.<br />

E<strong>in</strong>e andere Schreibweise für diesen Parameter verzichtet auf das Wort OFFSET:<br />

217


Nützliche Erweiterungen<br />

SELECT ... FROM ... WHERE<br />

ORDER BY ...<br />

LIMIT [ , ] <br />

Bei dieser Variante wird ebenso mit angegeben, wie viele Zeilen angezeigt<br />

werden sollen. Mit kann außerdem angegeben werden, wie viele<br />

Zeilen davor übersprungen werden sollen; dieser Wert wird jedoch zuerst angegeben<br />

und durch e<strong>in</strong> Komma von der gewünschten Zeilenzahl getrennt.<br />

Die Bedeutung beider Varianten ist identisch, sodass Beispiele nicht nötig s<strong>in</strong>d.<br />

Es ist wohl Geschmackssache, welche Version „e<strong>in</strong>gängiger“ ist.<br />

23.1.4. Oracle: ROWNUM<br />

Bei dem DBMS Oracle gibt es bei jedem SELECT-Ergebnis e<strong>in</strong>e implizite Spalte<br />

Rownum. Man kann diese Spalte mit ausgeben lassen. Solange man ke<strong>in</strong> ORDER<br />

BY angibt, ist die Reihenfolge der ausgegebenen Sätze nicht festgelegt. Dieselbe<br />

Abfrage kann an e<strong>in</strong>em anderen Tag durchaus e<strong>in</strong>e andere Nummerierung<br />

der Sätze hervorbr<strong>in</strong>gen. Das liegt z. B. daran, dass jemand die Datensätze <strong>in</strong> der<br />

Zwischenzeit re<strong>org</strong>anisiert hat.<br />

Oracle-Quelltext<br />

SELECT Name, Rownum<br />

FROM Mitarbeiter;<br />

NAME ROWNUM<br />

Müller 1<br />

Schneider 2<br />

Meyer 3<br />

Schmitz 4 /* usw. */<br />

Rownum als implizite Spalte der Ergebnismenge<br />

Wenn man diese Spalte Rownum nicht angibt, dann wird sie auch nicht ausgegeben.<br />

Ihr Vorteil ist, dass man sie auch bei WHERE verwenden kann:<br />

218<br />

Oracle-Quelltext<br />

SELECT Name<br />

FROM Mitarbeiter<br />

WHERE Rownum


NAME ROWNUM<br />

Müller 1<br />

Schneider 2<br />

Beschränkung auf e<strong>in</strong>e Anzahl Zeilen<br />

Folgende Formulierung funktioniert allerd<strong>in</strong>gs nicht, wenn man nur den 10. Satz<br />

ausgeben will:<br />

Oracle-Quelltext Falsch<br />

SELECT Name<br />

FROM Mitarbeiter;<br />

WHERE Rownum = 10;<br />

Nur den 10. Satz anzeigen − so geht es nicht.<br />

Das liegt daran, dass der Zähler Rownum nur die Zeilen zählt, die auch wirklich<br />

ausgegeben werden. Wenn die Tabelle 500 Sätze hat, dann wird bei jedem Satz<br />

geprüft, ob Rownum bereits den Wert 10 erreicht hat. Das ist jedoch nie der Fall,<br />

da der Zähler immer den Wert 0 behält.<br />

Da muss man schon das DBMS zw<strong>in</strong>gen, die Ergebnismenge mit der Rownum<br />

zwischenzuspeichern. Dann geht es:<br />

Oracle-Quelltext<br />

SELECT Name<br />

FROM ( SELECT Name, Rownum R<br />

FROM Mitarbeiter )<br />

WHERE R = 10;<br />

Nur den 10. Satz anzeigen − so klappt es.<br />

Welcher Satz dabei ausgegeben wird, ist jedoch dem Zufall überlassen.<br />

Wenn man die Ergebnismenge sortiert ausgeben und dabei auf die ersten 6 Sätze<br />

beschränken will, dann funktioniert die folgende Formulierung nicht:<br />

Oracle-Quelltext Falsch<br />

SELECT Name<br />

FROM Mitarbeiter;<br />

WHERE Rownum


Nützliche Erweiterungen<br />

Für die richtige Lösung muss die Datenbank schon etwas mehr tun. Sie muss<br />

zuerst alle vorhandenen Sätze sortieren und dann die ersten 6 Sätze ausgeben:<br />

Oracle-Quelltext<br />

SELECT Name<br />

FROM ( SELECT Name<br />

FROM Mitarbeiter<br />

ORDER BY Name )<br />

WHERE Rownum


SELECT <br />

FROM <br />

WHERE <br />

UNION [ DISTINCT | ALL ]<br />

SELECT <br />

FROM <br />

WHERE <br />

Mehrere Abfragen zusammenfassen<br />

Bei den e<strong>in</strong>zelnen Abfragen können grundsätzlich alle Klauseln benutzt werden.<br />

Bitte beachten Sie folgende Bed<strong>in</strong>gungen:<br />

• Alle E<strong>in</strong>zelabfragen müssen <strong>in</strong> Anzahl und Reihenfolge der Ergebnisspalten<br />

übere<strong>in</strong>stimmen. Die Datentypen müssen je nach DBMS genau gleich se<strong>in</strong><br />

oder zum<strong>in</strong>dest so ähnlich, dass sie automatisch konvertiert werden können.<br />

• Die Spaltennamen werden aus der ersten Spaltenliste übernommen, ggf. unter<br />

Berücksichtigung e<strong>in</strong>es Alias.<br />

• Doppelte Zeilen aus den E<strong>in</strong>zelabfragen werden unterdrückt, d. h. DISTINCT<br />

ist Standard und kann weggelassen werden. Zur Anzeige doppelter Zeilen ist<br />

ALL anzugeben.<br />

• Die Benutzung von Klammern sowie die Sortierung der gesamten Ergebnismenge<br />

durch ORDER BY wird je nach DBMS unterschiedlich geregelt.<br />

Aufgabe: Das folgende Beispiel ist e<strong>in</strong>e e<strong>in</strong>fache Zusammenfassung aller Fahrzeuge<br />

aus den Tabellen Fahrzeuge und Dienstwagen:<br />

SELECT ID, Kennzeichen, Farbe<br />

FROM Dienstwagen<br />

UNION<br />

SELECT ID, Kennzeichen, Farbe<br />

FROM Fahrzeug;<br />

Im folgenden Beispiel werden als Spalte Var Konstanten e<strong>in</strong>getragen, die die Herkunft<br />

der Daten angeben, und es werden verschiedene Auswahlbed<strong>in</strong>gungen benutzt.<br />

SELECT ’D’ AS Var, ID, Kennzeichen, Farbe<br />

FROM Dienstwagen<br />

WHERE Fahrzeugtyp_ID


Nützliche Erweiterungen<br />

My<strong>SQL</strong>-Quelltext<br />

( SELECT ’D’ AS Var, ID, Kennzeichen, Farbe<br />

FROM Dienstwagen<br />

WHERE Fahrzeugtyp_ID


CASE WHEN – Fallunterscheidungen<br />

SELECT FROM WHERE <br />

INTERSECT<br />

SELECT FROM WHERE <br />

Die Ergebnismenge besteht aus allen Zeilen, die sowohl zum ersten SELECT-<br />

Befehl als auch zum zweiten SELECT-Befehl gehören.<br />

EXCEPT (MS-<strong>SQL</strong>) oder MINUS (Oracle) liefern die Differenz der Teilabfragen.<br />

SELECT FROM WHERE <br />

EXCEPT | MINUS<br />

SELECT FROM WHERE <br />

Die Ergebnismenge besteht aus allen Zeilen, die zum ersten SELECT-Befehl gehören,<br />

aber beim zweiten SELECT-Befehl nicht enthalten s<strong>in</strong>d.<br />

Wie bei UNION können auch solche Verknüpfungen durch sehr komplizierte<br />

JOINs und WHERE-Klauseln nachgebildet werden.<br />

23.3. CASE WHEN – Fallunterscheidungen<br />

Oft möchte man bei e<strong>in</strong>em <strong>SQL</strong>-Befehl je nach Situation andere Werte erhalten.<br />

Das e<strong>in</strong>fachste Beispiel s<strong>in</strong>d die Mitarbeiter: Anstelle des Feld<strong>in</strong>halts Ist_Leiter<br />

mit 'J' oder 'N' kann 'Leiter' oder e<strong>in</strong>e leere Zeichenkette angezeigt werden. Dafür<br />

ist der CASE-Ausdruck v<strong>org</strong>esehen, den es <strong>in</strong> zwei Varianten gibt.<br />

Als können wahlweise konstante Werte oder komplexe Ausdrücke<br />

verwendet werden. Der CASE-Ausdruck ist nicht nur für SELECT, sondern auch<br />

für Speichern-Befehle geeignet und tritt nicht nur (wie <strong>in</strong> den meisten Beispielen)<br />

als Teil der Spaltenliste auf, sondern auch <strong>in</strong> der WHERE-Klausel oder an<br />

anderen Stellen, an denen entsprechende Werte benötigt werden.<br />

Der Datentyp des CASE-Ausdrucks ergibt sich aus den Ergebniswerten.<br />

23.3.1. Simple Case – die e<strong>in</strong>fache Fallunterscheidung<br />

Die e<strong>in</strong>fache Variante hat folgende Syntax:<br />

CASE <br />

WHEN THEN <br />

[ WHEN THEN ] /* usw. */<br />

[ ELSE ]<br />

END<br />

223


Nützliche Erweiterungen<br />

Bei dieser Version wird der Wert, der sich durch den ersten Ausdruck beim CASE<br />

ergibt, nache<strong>in</strong>ander mit den Ausdrücken nach WHEN verglichen. Sobald e<strong>in</strong>e<br />

Gleichheit festgestellt wird, wird der Wert des -Ausdrucks als Wert für<br />

den CASE-Ausdruck übernommen. Wenn es ke<strong>in</strong>e Übere<strong>in</strong>stimmung gibt, wird<br />

der -Ausdruck als V<strong>org</strong>abewert übernommen; wenn ELSE nicht vorhanden<br />

ist, wird NULL als Wert genommen. Beispiele:<br />

Aufgabe: E<strong>in</strong> vorhandener Feld<strong>in</strong>halt, nämlich 'J' oder 'N', wird für die Ausgabe<br />

durch andere Texte ersetzt:<br />

SELECT Personalnummer AS Pers, Name, Vorname, Geburtsdatum AS Geb,<br />

CASE Ist_Leiter<br />

WHEN ’J’ THEN ’Leiter’<br />

ELSE ”<br />

END AS Leiter,<br />

Mobil<br />

FROM Mitarbeiter;<br />

PERS NAME VORNAME GEB LEITER MOBIL<br />

40001 Langmann Matthias 28.03.1976 Leiter<br />

40002 Peters Michael 15.11.1973<br />

50001 Pohl Helmut 27.10.1980 Leiter (0171) 4123456<br />

50002 Braun Christian 05.09.1966 (0170) 8351647<br />

Dieselbe Lösung ohne ELSE-Zweig ändert nur die Anzeige:<br />

SELECT Personalnummer AS Pers, Name, Vorname, Geburtsdatum AS Geb,<br />

CASE Ist_Leiter<br />

WHEN ’J’ THEN ’Leiter’<br />

END AS Leiter,<br />

Mobil<br />

FROM Mitarbeiter;<br />

PERS NAME VORNAME GEB LEITER MOBIL<br />

40001 Langmann Matthias 28.03.1976 Leiter<br />

40002 Peters Michael 15.11.1973 <br />

50001 Pohl Helmut 27.10.1980 Leiter (0171) 4123456<br />

50002 Braun Christian 05.09.1966 (0170) 8351647<br />

Aufgabe: Bei der Art der Versicherungsverträge möchten wir die Varianten im<br />

„Klartext“ lesen:<br />

224<br />

SELECT Vertragsnummer AS Vertrag, Abschlussdatum AS Datum,<br />

CASE Art<br />

WHEN ’VK’ THEN ’Vollkasko’<br />

WHEN ’TK’ THEN ’Teilkasko’<br />

WHEN ’HP’ THEN ’Haftpflicht’<br />

END<br />

FROM Versicherungsvertrag;


VERTRAG DATUM CASE<br />

DB-04 25.01.2008 Haftpflicht<br />

RH-01 11.12.1976 Vollkasko<br />

RD-02 29.01.1988 Haftpflicht<br />

RM-03 13.01.1996 Haftpflicht<br />

RD-04 23.11.2006 Haftpflicht<br />

RR-05 29.06.1990 Teilkasko<br />

CASE WHEN – Fallunterscheidungen<br />

23.3.2. Searched Case – die komplexe Fallunterscheidung<br />

Die komplexe Variante hat ke<strong>in</strong>en Ausdruck h<strong>in</strong>ter dem CASE; sie arbeitet nach<br />

folgender Syntax:<br />

CASE<br />

WHEN THEN <br />

[ WHEN THEN ] /* usw. */<br />

[ ELSE ]<br />

END<br />

Bei dieser Variante werden nache<strong>in</strong>ander Bed<strong>in</strong>gungen geprüft. Sobald e<strong>in</strong>e Bed<strong>in</strong>gung<br />

als WAHR festgestellt wird, wird der -Wert h<strong>in</strong>ter THEN als Wert<br />

für den CASE-Ausdruck übernommen. Wenn es ke<strong>in</strong>e Übere<strong>in</strong>stimmung gibt,<br />

wird der -Ausdruck als V<strong>org</strong>abewert übernommen; wenn ELSE nicht<br />

vorhanden ist, wird NULL als Wert genommen. Beispiele:<br />

Aufgabe: In e<strong>in</strong>er e<strong>in</strong>zigen Zeile wird angegeben, wie viele Versicherungsverträge<br />

<strong>in</strong>nerhalb e<strong>in</strong>es Jahrzehnts abgeschlossen wurden.<br />

SELECT<br />

SUM( CASE WHEN EXTRACT(YEAR FROM Abschlussdatum) BETWEEN 1970 AND 1979<br />

THEN 1 ELSE 0<br />

END ) AS S_197_,<br />

SUM( CASE WHEN EXTRACT(YEAR FROM Abschlussdatum) BETWEEN 1980 AND 1989<br />

THEN 1 ELSE 0<br />

END ) AS S_198_,<br />

SUM( CASE WHEN EXTRACT(YEAR FROM Abschlussdatum) BETWEEN 1990 AND 1999<br />

THEN 1 ELSE 0<br />

END ) AS S_199_,<br />

SUM( CASE WHEN EXTRACT(YEAR FROM Abschlussdatum) >= 2000<br />

THEN 1 ELSE 0<br />

END ) AS S_200_<br />

FROM Versicherungsvertrag;<br />

S_197_ S_198_ S_199_ S_200_<br />

6 6 6 5<br />

225


Nützliche Erweiterungen<br />

Dazu wird für jedes Jahrzehnt e<strong>in</strong>e Spalte v<strong>org</strong>esehen. Jede Spalte enthält e<strong>in</strong>en<br />

CASE-Ausdruck mit e<strong>in</strong>er WHEN-Bed<strong>in</strong>gung. Wenn für e<strong>in</strong>e Zeile diese Bed<strong>in</strong>gung<br />

TRUE ergibt, wird 1 zur Summe dieses Jahrzehnts addiert, andernfalls 0.<br />

Aufgabe: Der CASE-Ausdruck soll <strong>in</strong>nerhalb von ORDER BY e<strong>in</strong>e unterschiedliche<br />

Sortierung, abhängig von der Art der Versicherung, erreichen:<br />

SELECT ID, Art,<br />

Abschlussdatum AS Datum, Vertragsnummer AS Vertr,<br />

Mitarbeiter_ID AS Mit, Fahrzeug_ID AS FZG<br />

FROM Versicherungsvertrag<br />

ORDER BY Art,<br />

CASE WHEN Art = ’TK’ THEN ID<br />

WHEN Art = ’VK’ THEN Mitarbeiter_ID<br />

WHEN Art = ’HP’ THEN Fahrzeug_ID<br />

END;<br />

ID ART DATUM VERTR MIT FZG<br />

14 HP 15.03.1998 KG-03 9 16<br />

18 HP 17.05.2000 HG-03 9 17<br />

19 HP 21.09.2004 HB-04 9 19<br />

10 HP 23.11.2006 RD-04 9 20<br />

6 HP 25.01.2008 DB-04 9 21<br />

15 HP 27.03.1988 KV-04 10 22<br />

11 TK 29.06.1990 RR-05 9 23<br />

12 TK 14.02.1978 KB-01 10 6<br />

21 VK 20.06.1982 XH-02 9 8<br />

23 VK 19.07.2002 XO-04 9 18<br />

7 VK 11.12.1976 RH-01 10 5<br />

22 VK 05.06.1992 XW-03 10 13<br />

Hier wurden alle Abschnitte numerisch sortiert. Auch die Teilsortierung nach<br />

Datum funktioniert, muss aber unter Umständen überarbeitet werden:<br />

Firebird-Quelltext Falsch<br />

SELECT ID, Art,<br />

Abschlussdatum AS Datum, Vertragsnummer AS Vertr,<br />

Mitarbeiter_ID AS Mit, Fahrzeug_ID AS FZG<br />

FROM Versicherungsvertrag<br />

ORDER BY Art,<br />

CASE WHEN Art = ’TK’ THEN ID<br />

WHEN Art = ’VK’ THEN Mitarbeiter_ID<br />

WHEN Art = ’HP’ THEN Abschlussdatum<br />

END;<br />

Invalid token. <strong>SQL</strong> error code = -104.<br />

Datatypes are not comparable <strong>in</strong> expression CASE.<br />

Auch wenn Firebird (wie im ersten Beispiel) Namen von Spalten akzeptiert, werden<br />

die Datentypen der Spalten verglichen. Diese s<strong>in</strong>d offensichtlich nicht kom-<br />

226


CASE WHEN – Fallunterscheidungen<br />

patibel; also bricht Firebird diese Abfrage ab. Aber <strong>in</strong> der ORDER BY-Klausel können<br />

auch die Nummern der Spalten angegeben werden; dann klappt es:<br />

Firebird-Quelltext<br />

SELECT ID, Art,<br />

Abschlussdatum AS Datum, Vertragsnummer AS Vertr,<br />

Mitarbeiter_ID AS Mit, Fahrzeug_ID AS FZG<br />

FROM Versicherungsvertrag<br />

ORDER BY Art,<br />

CASE WHEN Art = ’TK’ THEN ID<br />

WHEN Art = ’VK’ THEN Mitarbeiter_ID<br />

WHEN Art = ’HP’ THEN 2<br />

END;<br />

Mit Nummern der Spalten anstelle der Namen.<br />

Bitte wundern Sie sich nicht über das identische Ergebnis wie oben bei der Sortierung<br />

nach Fahrzeug_ID: Die Fahrzeuge wurden <strong>in</strong> der gleichen Reihenfolge<br />

erfasst wie die Verträge; also s<strong>in</strong>d <strong>in</strong> der ersten Zeit beide Sortierungen gleich.<br />

Die Sortierung kann nur e<strong>in</strong>heitlich festgelegt werden, aber nicht mal so, mal so:<br />

Firebird-Quelltext Falsch<br />

SELECT ID, Art,<br />

Abschlussdatum AS Datum, Vertragsnummer AS Vertr,<br />

Mitarbeiter_ID AS Mit, Fahrzeug_ID AS FZG<br />

FROM Versicherungsvertrag<br />

ORDER BY Art,<br />

CASE<br />

WHEN Art = ’TK’ THEN ID ASC<br />

WHEN Art = ’VK’ THEN Mitarbeiter_ID ASC<br />

WHEN Art = ’HP’ THEN 2 DESC<br />

END;<br />

Invalid token. Dynamic <strong>SQL</strong> Error. <strong>SQL</strong> error code = -104.<br />

Token unknown - l<strong>in</strong>e 7, column 39. ASC.<br />

SELECT ID, Art,<br />

Abschlussdatum AS Datum, Vertragsnummer AS Vertr,<br />

Mitarbeiter_ID AS Mit, Fahrzeug_ID AS FZG<br />

FROM Versicherungsvertrag<br />

ORDER BY Art,<br />

CASE<br />

WHEN Art = ’TK’ THEN ID<br />

WHEN Art = ’VK’ THEN Mitarbeiter_ID<br />

WHEN Art = ’HP’ THEN 2<br />

END DESC;<br />

E<strong>in</strong>heitliche Sortierung DESC sollte möglich se<strong>in</strong><br />

227


Nützliche Erweiterungen<br />

Zum<strong>in</strong>dest unter Firebird gel<strong>in</strong>gt dennoch ke<strong>in</strong>e korrekte Sortierung: nur der Abschnitt<br />

'TK' wird absteigend sortiert, die beiden anderen nicht. Sie müssen aber<br />

sowieso selbst ausprobieren, was <strong>in</strong> welchem DBMS möglich ist und was nicht.<br />

23.3.3. CASE-Ausdruck beim Speichern<br />

Wie beim SELECT-Befehl kann e<strong>in</strong> CASE-Ausdruck auch beim Speichern benutzt<br />

werden, vor allem bei den Zuweisungen der Werte für INSERT und UPDATE sowie<br />

bei der WHERE-Klausel für UPDATE und DELETE. Da sich diese Kapitel vor<br />

allem mit Abfragen beschäftigen, beschränken wir uns auf e<strong>in</strong> Beispiel.<br />

Aufgabe: Die Zuständigkeit der Mitarbeiter für die Versicherungsverträge wird<br />

neu geregelt:<br />

• Vollkasko-Verträge gehören zum Aufgabenbereich des Abteilungsleiters<br />

(Mitarbeiter-ID 9).<br />

• Teilkasko-Verträge gehören zum Aufgabenbereich des Mitarbeiters mit ID 12.<br />

• Die Haftpflicht-Verträge werden <strong>in</strong> Abhängigkeit von der ID aufgeteilt auf die<br />

Mitarbeiter 10 und 11.<br />

UPDATE Versicherungsvertrag<br />

SET Mitarbeiter_id =<br />

CASE WHEN Art = ’VK’ THEN 9<br />

WHEN Art = ’TK’ THEN 12<br />

WHEN Art = ’HP’ THEN 10 + MOD(ID, 2)<br />

END;<br />

Es wird also e<strong>in</strong>e normale SET-Anweisung geschrieben. Die CASE-Anweisung<br />

liefert die benötigten Werte, wobei für den Fall „Haftpflicht“ für gerade IDs der<br />

Wert (10+0) und für ungerade IDs der Wert (10+1) gesetzt wird. E<strong>in</strong>e WHERE-<br />

Klausel ist nicht erforderlich, weil alle Verträge neu zugeordnet werden sollen.<br />

23.4. Zusammenfassung<br />

In diesem Kapitel lernten Sie e<strong>in</strong>ige nützliche Erweiterungen vor allem für Abfragen<br />

kennen:<br />

• Je nach DBMS wird unterschiedlich geregelt, wenn nur e<strong>in</strong>e gewisse Anzahl<br />

von Zeilen gewünscht wird.<br />

• Mit UNION wird das Ergebnis von zwei oder mehr Abfragen <strong>in</strong> e<strong>in</strong>er geme<strong>in</strong>samen<br />

Ergebnistabelle zusammengefasst.<br />

• Mit CASE WHEN s<strong>in</strong>d Fallunterscheidungen möglich; dies ist auch beim Speichern<br />

sowohl für die Werte als auch für die WHERE-Klausel hilfreich.<br />

228


23.5. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 231.<br />

Übung 1 – Richtig oder falsch?<br />

Welche der folgenden Aussagen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

Übungen<br />

1. Es ist äußerst selten erforderlich, das Ergebnis e<strong>in</strong>er Abfrage nur <strong>in</strong> Teilen<br />

zu holen.<br />

2. Für die Anzeige oder Verwendung e<strong>in</strong>er ROW_NUMBER hat der <strong>SQL</strong>-<br />

Standard (2003) e<strong>in</strong> Verfahren v<strong>org</strong>eschrieben.<br />

3. E<strong>in</strong> solches Verfahren wird von den meisten DBMS verwendet, ist aber <strong>in</strong><br />

unterschiedlicher Weise verwirklicht.<br />

4. Bei UNION, INTERSECT usw. müssen die ausgewählten Spalten von der<br />

Anzahl her übere<strong>in</strong>stimmen.<br />

5. Bei UNION, INTERSECT usw. müssen die ausgewählten Spalten vom Datentyp<br />

her genau übere<strong>in</strong>stimmen.<br />

Übung 2 – UNION, INTERSECT usw.<br />

Was ist an den folgenden Befehlen falsch oder fragwürdig? Wie kann man das<br />

DBMS dazu br<strong>in</strong>gen, den „fragwürdigen“ Befehl auf jeden Fall auszuführen?<br />

-- Befehl 1<br />

SELECT Name, Vorname FROM Mitarbeiter<br />

UNION<br />

SELECT Name, Vorname, Geburtsdatum FROM Versicherungsnehmer<br />

-- Befehl 2<br />

SELECT Name, Vorname, Abteilung_ID FROM Mitarbeiter<br />

UNION<br />

SELECT Name, Vorname, Geburtsdatum FROM Versicherungsnehmer<br />

Übung 3 – UNION, INTERSECT usw.<br />

Erstellen Sie e<strong>in</strong>e Abfrage für die Tabellen Versicherungsvertrag, Fahrzeug,<br />

Zuordnung_SF-FZ, <strong>in</strong> der folgende Bed<strong>in</strong>gungen berücksichtigt werden:<br />

• Es sollen Vertragsnummer, Abschlussdatum, Kennzeichen sowie ggf. anteilige<br />

Schadenshöhe angezeigt werden.<br />

• Fahrzeuge mit e<strong>in</strong>em Schaden sollen vollständig angezeigt werden.<br />

• Fahrzeuge ohne Schaden sollen nur angezeigt werden, wenn der Vertrag vor<br />

1990 abgeschlossen wurde.<br />

• Das Ergebnis soll nach Schadenshöhe und Kennzeichen sortiert werden.<br />

229


Nützliche Erweiterungen<br />

Benutzen Sie UNION zur Verknüpfung der Verträge mit und ohne Schaden.<br />

Übung 4 – Fallunterscheidung für Nachschlagewerte<br />

Schreiben Sie e<strong>in</strong>en SELECT-Befehl, bei dem die korrekte Briefanrede für die Mitarbeiter<br />

erstellt wird.<br />

H<strong>in</strong>weis: Benutzen Sie CONCAT zum Verknüpfen mehrerer Zeichenketten.<br />

Übung 5 – Fallunterscheidung für Bereiche<br />

Zur Tabelle Fahrzeug soll aus dem Kennzeichen die regionale Herkunft abgeleitet<br />

und angezeigt werden. Schreiben Sie e<strong>in</strong>e Abfrage für diese Spalten Kennzeichen<br />

und Herkunft.<br />

H<strong>in</strong>weis: Benutzen Sie POSITION zur Feststellung des B<strong>in</strong>destrichs sowie SUB-<br />

STRING.<br />

Zusatzfrage: Wie müsste e<strong>in</strong> solches Problem s<strong>in</strong>nvollerweise gelöst werden, falls<br />

e<strong>in</strong>e solche Zuordnung häufiger und allgeme<strong>in</strong>er benötigt wird?<br />

Übung 6 – Fallunterscheidung für mehrere Varianten<br />

Aus der Tabelle Versicherungsnehmer sollen Name, Vorname und Anschrift<br />

angezeigt werden. Außerdem soll jede Adresse e<strong>in</strong>e Markierung bekommen:<br />

F = (eigene) Firmenkunden, P = (eigene) Privatkunden, X = eXterne Verträge (d. h.<br />

Kunden fremder Versicherungsgesellschaften). Begründen Sie die Reihenfolge<br />

der WHEN-ELSE-Bed<strong>in</strong>gungen.<br />

Übung 7 – Fallunterscheidung beim Speichern<br />

Schreiben Sie e<strong>in</strong>en UPDATE-Befehl, der nach Ablauf e<strong>in</strong>es Versicherungsjahres<br />

den Prämiensatz für das nächste Jahr ändert. Berücksichtigen Sie dabei folgende<br />

Bed<strong>in</strong>gungen:<br />

• Neue Verträge, für die noch ke<strong>in</strong> Prämiensatz gilt, werden auf 200 [Prozent]<br />

gesetzt.<br />

• Verträge mit e<strong>in</strong>em Prämiensatz von m<strong>in</strong>destens 100 werden um 20 reduziert.<br />

• Verträge mit e<strong>in</strong>em Prämiensatz von weniger als 100 werden um 10 reduziert.<br />

• Der M<strong>in</strong>destsatz von 30 darf nicht unterschritten werden.<br />

Ignorieren Sie dabei zunächst, dass dies nur im Fall von Schadensfreiheit gelten<br />

darf und <strong>in</strong>nerhalb e<strong>in</strong>es Jahres nur e<strong>in</strong>mal neu berechnet werden darf.<br />

230


Übung 8 – Fallunterscheidung beim Speichern<br />

Bei genauerer Untersuchung von Übung 7 s<strong>in</strong>d weitere Bed<strong>in</strong>gungen nötig.<br />

Übungen<br />

1. Wie wäre die Schadensfreiheit zu berücksichtigen? Abhängig von der Schadenshöhe<br />

soll sich der Prämiensatz gar nicht, wenig oder mehr erhöhen.<br />

2. Wie kann unter Verwendung des Datums Prämienänderung gesichert werden,<br />

dass die Neuberechnung nur e<strong>in</strong>mal jährlich stattf<strong>in</strong>den darf?<br />

Versuchen Sie, dies im selben UPDATE-Befehl zu berücksichtigen. Sie sollen ke<strong>in</strong>en<br />

Befehl schreiben, sondern die nötigen Klauseln erwähnen und erläutern.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 229.<br />

Lösung zu Übung 1 – Richtig oder falsch?<br />

Die Aussagen 3, 4 s<strong>in</strong>d richtig. Die Aussagen 1, 2, 5 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – UNION, INTERSECT usw.<br />

Bei Befehl 1 werden unterschiedlich viele Spalten ausgewählt, das ist unzulässig.<br />

Bei Befehl 2 unterscheiden sich die Datentypen der dritten Spalte; es ist unsicher,<br />

ob das DBMS die unterschiedlichen Spalten e<strong>in</strong>heitlich als Zeichenkette<br />

(zulässig) oder nicht-kompatibel (unzulässig) <strong>in</strong>terpretieren will.<br />

E<strong>in</strong> CAST beider Spalten auf VARCHAR macht die Datentypen kompatibel.<br />

Lösung zu Übung 3 – UNION, INTERSECT usw.<br />

E<strong>in</strong>e mögliche Variante lautet so (Firebird-Version für ORDER BY):<br />

SELECT * FROM (<br />

SELECT Vertragsnummer, Abschlussdatum, Kennzeichen, Schadenshoehe<br />

FROM Versicherungsvertrag vv<br />

INNER JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

RIGHT JOIN Zuordnung_SF_FZ zu ON fz.ID = zu.Fahrzeug_ID<br />

UNION<br />

SELECT Vertragsnummer, Abschlussdatum, Kennzeichen, 0<br />

FROM Versicherungsvertrag vv<br />

INNER JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

WHERE Abschlussdatum < ’01.01.1990’ AND NOT<br />

EXISTS ( SELECT ID FROM Zuordnung_SF_FZ zu WHERE zu.Fahrzeug_ID = fz.ID )<br />

) ORDER BY Schadenshoehe, Kennzeichen;<br />

Der zweite SELECT kann auch so geschrieben werden:<br />

231


Nützliche Erweiterungen<br />

SELECT Vertragsnummer, Abschlussdatum, Kennzeichen, 0<br />

FROM Versicherungsvertrag vv<br />

INNER JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

LEFT JOIN Zuordnung_SF_FZ zu ON fz.ID = zu.Fahrzeug_ID<br />

WHERE Abschlussdatum < ’01.01.1990’ AND zu.ID IS NULL<br />

Lösung zu Übung 4 – Fallunterscheidung für Nachschlagewerte<br />

SELECT CONCAT( ’Sehr geehrte’,<br />

CASE Geschlecht<br />

WHEN ’M’ THEN ’r Herr ’<br />

WHEN ’W’ THEN ’ Frau ’<br />

ELSE ’/r Frau/Herr ’<br />

END,<br />

Name ) AS Anrede<br />

FROM Mitarbeiter;<br />

H<strong>in</strong>weise: CONCAT oder die Str<strong>in</strong>g-Verknüpfung erzeugen u. U. e<strong>in</strong>e falsche<br />

Länge des ersten Teils. Das kann mit TRIM „repariert“ werden; dann ist aber das<br />

abschließende Leerzeichen h<strong>in</strong>zuzufügen. Der ELSE-Zweig ist überflüssig, weil<br />

Geschlecht nicht NULL se<strong>in</strong> kann; er wird nur der Vollständigkeit halber genannt.<br />

Lösung zu Übung 5 – Fallunterscheidung für Bereiche<br />

SELECT Kennzeichen,<br />

CASE SUBSTRING(Kennzeichen FROM 1 FOR POSITION(’-’, Kennzeichen)-1 )<br />

WHEN ’RE’ THEN ’Kreis Reckl<strong>in</strong>ghausen’<br />

WHEN ’GE’ THEN ’Stadt Gelsenkirchen’<br />

WHEN ’E’ THEN ’Stadt Essen’<br />

WHEN ’BO’ THEN ’Stadt Bochum’<br />

WHEN ’HER’ THEN ’Stadt Herne’<br />

ELSE ’unbekannt’<br />

END AS Herkunft<br />

FROM Fahrzeug<br />

ORDER BY 2, 1;<br />

Zur Zusatzfrage: E<strong>in</strong>e Tabelle Region o. ä. würde mit LEFT JOIN über den o. g.<br />

SUBSTRING verknüpft. Auch e<strong>in</strong> INNER JOIN wäre möglich, dann würden aber<br />

Fahrzeuge fehlen, deren Kennzeichen <strong>in</strong> der Tabelle Region fehlen.<br />

Lösung zu Übung 6 – Fallunterscheidung für mehrere Varianten<br />

232<br />

SELECT Name, Vorname, PLZ, Strasse, Hausnummer,<br />

CASE<br />

WHEN Eigener_Kunde = ’N’ THEN ’X’


WHEN Vorname IS NULL OR Fuehrersche<strong>in</strong> IS NULL THEN ’F’<br />

ELSE ’P’<br />

END AS Markierung<br />

FROM Versicherungsnehmer;<br />

Übungen<br />

Zur Reihenfolge: Bei den Fremdverträgen wird nicht zwischen Firmen- und Privatkunden<br />

unterschieden; dies muss also zuerst geprüft werden. Bei Firmenkunden<br />

s<strong>in</strong>d weder Vorname noch Führersche<strong>in</strong>-Datum registriert, das wird als<br />

nächstes geprüft (e<strong>in</strong>es der WHEN-Kriterien würde genügen). Alle übrigen Fälle<br />

s<strong>in</strong>d Privatkunden.<br />

Lösung zu Übung 7 – Fallunterscheidung beim Speichern<br />

UPDATE Versicherungsvertrag<br />

SET Praemiensatz = CASE<br />

WHEN Praemiensatz IS NULL THEN 200<br />

WHEN Praemiensatz >= 100 THEN Praemiensatz - 20<br />

WHEN Praemiensatz >= 40 THEN Praemiensatz - 10<br />

ELSE 30<br />

END<br />

WHERE -- passende Bed<strong>in</strong>gungen<br />

Lösung zu Übung 8 – Fallunterscheidung beim Speichern<br />

1. Es wird e<strong>in</strong>e geschachtelte CASE-Anweisung verwendet. Zunächst wird unterschieden,<br />

ob überhaupt Schadensfälle aufgetreten s<strong>in</strong>d. Im ersten Zweig<br />

„ke<strong>in</strong> Schaden“ wird die Lösung von Übung 7 benutzt. Im zweiten Zweig<br />

„mit Schaden“ wird e<strong>in</strong>e ähnliche CASE-Anweisung e<strong>in</strong>gefügt, die abhängig<br />

von der Schadenshöhe den Prämiensatz erhöht.<br />

2. In der WHERE-Klausel wird geprüft, ob die letzte Prämienänderung überschritten<br />

ist. Gleichzeitig mit der Neuberechnung wird dieses Datum um<br />

e<strong>in</strong> Jahr weitergesetzt.<br />

H<strong>in</strong>weis: Dieses Verfahren wird ausführlich im Kapitel Prozeduren: Automatisches<br />

UPDATE 1 behandelt. Bei Interesse können Sie im dortigen Code den abschließenden<br />

UPDATE-Befehl vergleichen.<br />

1 Abschnitt 32.2.3 auf Seite 363<br />

233


24. Berechnete Spalten<br />

24.1. Ergebnis von Berechnungen . . . . . . . . . . . . . . . . . . . . . 236<br />

24.2. Zeichenketten verb<strong>in</strong>den und bearbeiten . . . . . . . . . . . . 236<br />

24.3. Ergebnis von Funktionen . . . . . . . . . . . . . . . . . . . . . . . 237<br />

24.4. Unterabfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

24.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

24.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238<br />

Mit Abfragen kann man nicht nur Spalten auswählen, sondern auch völlig neue<br />

Spalten aus anderen Spalten oder mit Funktionen erstellen.<br />

E<strong>in</strong>e neue Spalte wird als Teil e<strong>in</strong>er Abfrage wie folgt erstellt:<br />

[AS] <br />

Ausdruck ist allgeme<strong>in</strong> der H<strong>in</strong>weis auf etwas, das an der betreffenden Stelle verwendet<br />

wird: e<strong>in</strong> konstanter Wert, der Inhalt e<strong>in</strong>er oder mehrerer Spalten, e<strong>in</strong>e<br />

Berechnung mit diesen Spalten, das Ergebnis e<strong>in</strong>er Funktion oder das Ergebnis<br />

e<strong>in</strong>er Unterabfrage. Die Spalte, die das Ergebnis aufnimmt, erhält mit AS e<strong>in</strong>en<br />

eigenen Namen als Alias; das AS kann auch entfallen.<br />

Bei manchen DBMS ist die Angabe e<strong>in</strong>es Alias Pflicht, manche erzeugen bei Bedarf<br />

e<strong>in</strong>en zufälligen Namen, und andere meckern erst, wenn man versucht, e<strong>in</strong>e<br />

physische Tabelle mit e<strong>in</strong>er namenlosen Spalte zu füllen.<br />

Bei e<strong>in</strong>er Unterabfrage als berechneter Spalte muss sie unbed<strong>in</strong>gt − wie bei e<strong>in</strong>er<br />

Funktion, Berechnung oder Verknüpfung − für jede Zeile der Hauptabfrage<br />

genau e<strong>in</strong>en Wert ergeben. In e<strong>in</strong>em der Beispiele erreicht dies e<strong>in</strong>e passende<br />

WHERE-Bed<strong>in</strong>gung.<br />

H<strong>in</strong>weis: Fast alles, was hier über Ausdrücke, Funktionen und Konvertierungen<br />

gesagt wird, ist auch bei der Manipulation oder Verarbeitung von Daten wichtig<br />

– siehe auch die Kapitel Funktionen 1 und Funktionen (2) 2 .<br />

1 Kapitel 14 auf Seite 109<br />

2 Kapitel 16 auf Seite 141<br />

235


Berechnete Spalten<br />

24.1. Ergebnis von Berechnungen<br />

Alle Werte e<strong>in</strong>er Spalte werden verrechnet, vorzugsweise mit den Grundrechenarten.<br />

Hier wird <strong>in</strong> der Tabelle Schadensfall aus dem Feld Schadenshoehe der Nettobetrag<br />

(ohne MWSt) errechnet und mit allen weiteren Feldern verbunden:<br />

SELECT Schadenshoehe / 1.19 AS Netto,<br />

*<br />

FROM Schadensfall;<br />

Dieses Ergebnis kann auch für weitere Berechnungen verwendet werden:<br />

Der Alias-Name der neuen Spalte Netto kann nicht nochmals verwendet werden.<br />

Quelltext Falsch<br />

SELECT Schadenshoehe AS Brutto,<br />

Schadenshoehe / 1.19 AS Netto,<br />

Schadenshoehe - Netto AS MWSt<br />

FROM Schadensfall;<br />

Aber die Berechnung kann erneut zugewiesen werden.<br />

SELECT Schadenshoehe AS Brutto,<br />

Schadenshoehe / 1.19 AS Netto,<br />

Schadenshoehe - (Schadenshoehe / 1.19) AS MWSt<br />

FROM Schadensfall;<br />

Man kann davon ausgehen, dass das DBMS die Abfrage soweit optimiert, dass<br />

die Berechnung tatsächlich nur e<strong>in</strong>mal ausgeführt werden muss.<br />

24.2. Zeichenketten verb<strong>in</strong>den und bearbeiten<br />

Ähnlich wie für Zahlen werden Zeichenketten verarbeitet. Das e<strong>in</strong>fachste ist die<br />

Verknüpfung (Verkettung) von Str<strong>in</strong>gs durch den Operator || bzw. +:<br />

SELECT Name || ’, ’ || Vorname AS Gesamtname<br />

FROM Mitarbeiter;<br />

Gesamtname<br />

Müller, Kurt<br />

Schneider, Daniela<br />

In diesem Beispiel werden zwischen die Spalten noch das Komma und e<strong>in</strong> Leerzeichen<br />

als konstanter Text gesetzt.<br />

236


Ergebnis von Funktionen<br />

Bei Textspalten mit fester Feldlänge, die wir <strong>in</strong> der Beispieldatenbank nicht haben,<br />

werden überzählige Leerzeichen mit der Funktion TRIM abgeschnitten:<br />

SELECT TRIM(Name) || ’, ’ || TRIM(Vorname) AS Gesamtname<br />

FROM Mitarbeiter;<br />

Auf diese Weise kann man auch Teile von Texten übernehmen und den Rest abschneiden:<br />

SELECT Name || ’, ’ || TRIM( SUBSTRING(Vorname FROM 1 FOR 1) ) || ’.’ AS<br />

Name_kurz<br />

FROM Mitarbeiter;<br />

Name_kurz<br />

Müller, K.<br />

Schneider, D.<br />

H<strong>in</strong>weis: Wie schon bei den Funktionen 3 erwähnt, endet e<strong>in</strong> Text gemäß <strong>SQL</strong>-<br />

Standard nach SUBSTRING mit überzähligen Leerzeichen, die mit TRIM zu entfernen<br />

s<strong>in</strong>d. Vielleicht ist Ihr DBMS „pfiffiger“ und macht das automatisch.<br />

24.3. Ergebnis von Funktionen<br />

In den bisherigen Beispielen werden Feld<strong>in</strong>halte direkt modifiziert und ausgegeben.<br />

In den beiden Kapiteln über „Funktionen“ werden neue Informationen<br />

berechnet und unabhängig von vorhandenen Zeilen und Spalten ausgegeben:<br />

SELECT COUNT(Farbe) AS Anzahl_Farbe<br />

FROM Fahrzeug;<br />

Bei diesem Beispiel mit e<strong>in</strong>er Aggregatfunktion besteht das Ergebnis aus e<strong>in</strong>er<br />

e<strong>in</strong>zelnen Zahl <strong>in</strong> e<strong>in</strong>er e<strong>in</strong>zigen Zeile. Dies muss aber nicht so se<strong>in</strong>; vor allem<br />

mit Gruppierungen 4 gibt es viele Varianten.<br />

Weitere Funktionen ermöglichen oder erleichtern besondere Abfragen. Dieses<br />

EXTRACT liefert die Liste aller Geburtstage, sortiert nach Monat:<br />

SELECT Name, Vorname,<br />

EXTRACT(MONTH FROM Geburtsdatum) AS Monat,<br />

Geburtsdatum<br />

FROM Mitarbeiter<br />

ORDER BY Monat;<br />

3 Abschnitt 14.3.4 auf Seite 114<br />

4 Kapitel 25 auf Seite 241<br />

237


Berechnete Spalten<br />

NAME VORNAME MONAT GEBURTSDATUM<br />

Aagenau Karol<strong>in</strong> 1 02.01.1950<br />

Langer Norbert 1 13.01.1968<br />

Wagner Gaby 1 18.01.1970<br />

Müller Kurt 1 05.01.1977<br />

Kolic Ivana 2 14.02.1971<br />

Schneider Daniela 2 16.02.1980 // usw.<br />

24.4. Unterabfragen<br />

Wir beschränken uns hier auf e<strong>in</strong> Beispiel; mehr steht im Kapitel Unterabfragen 5 .<br />

Aufgabe: Gesucht s<strong>in</strong>d alle Abteilungen mit der Anzahl ihrer Mitarbeiter:<br />

SELECT ab.ID,<br />

ab.Kuerzel,<br />

ab.Ort,<br />

( SELECT COUNT(*)<br />

FROM Mitarbeiter mi<br />

WHERE mi.Abteilung_ID = ab.ID<br />

) AS Mitarbeiterzahl<br />

FROM Abteilung ab ;<br />

In den Klammern steht zu jeder Abteilung ab.ID die Anzahl ihrer Mitarbeiter; dies<br />

wird mit den anderen Spalten zusammengefasst. Weil es ID <strong>in</strong> beiden Tabellen<br />

gibt, muss die gewünschte Tabelle ausdrücklich erwähnt werden.<br />

24.5. Zusammenfassung<br />

In diesem Kapitel bekamen wir verschiedene Erläuterungen dafür, wie aus Berechnungen,<br />

Verkettung von Zeichenketten oder als Ergebnis von Skalar- oder<br />

Spaltenfunktionen neue Spalten für die Ergebnistabelle entstehen.<br />

24.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 239.<br />

Übung 1 – Zusatzspalten durch Berechnung<br />

Zur Tabelle Versicherungsvertrag sollen Versicherungsnummer, Basisprämie und<br />

Prämiensatz angegeben sowie die aktuelle Prämie berechnet werden.<br />

5 Kapitel 26 auf Seite 251<br />

238


Übung 2 – Zusatzspalten durch Berechnung<br />

Übungen<br />

Geben Sie (unter Verwendung der Lösung von Übung 1) die Gesamtzahl der Versicherungsverträge<br />

sowie den Gesamtbetrag aller aktuellen Prämien an.<br />

Übung 3 – Zusatzspalten durch Str<strong>in</strong>g-Verknüpfung<br />

Erstellen Sie zur Tabelle Versicherungsnehmer per Abfrage die Druckanschrift.<br />

Benutzen Sie CASE, CAST, RPAD und CONCAT; auf TRIM können Sie verzichten.<br />

• Zeile 1 mit Anrede (22 Zeichen, basierend auf der Spalte Geschlecht mit den<br />

Inhalten 'W' bzw. 'M') und der ID am rechten Rand (8 Zeichen rechtsbündig)<br />

• Zeile 2 mit Vorname und Name<br />

• Zeile 3 mit Straße und Hausnummer<br />

• Zeile 4 mit PLZ und Ort<br />

Übung 4 – Neue Spalten durch Spaltenfunktion<br />

Bestimmen Sie, wie viele Fahrzeuge <strong>in</strong> Bochum ('BO') und wie viele <strong>in</strong> Gelsenkirchen<br />

('GE') angemeldet s<strong>in</strong>d. Gruppierungen werden erst im nächsten Kapitel<br />

behandelt; verwenden Sie stattdessen UNION.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 238.<br />

Lösung zu Übung 1 – Zusatzspalten durch Berechnung<br />

SELECT Vertragsnummer,<br />

Basispraemie,<br />

Praemiensatz,<br />

Basispraemie * Praemiensatz / 100 AS Aktuell<br />

FROM Versicherungsvertrag;<br />

Lösung zu Übung 2 – Zusatzspalten durch Berechnung<br />

SELECT COUNT(*) AS Gesamtzahl,<br />

SUM(Basispraemie * Praemiensatz / 100) AS Praemiensumme<br />

FROM Versicherungsvertrag;<br />

239


Berechnete Spalten<br />

Lösung zu Übung 3 – Zusatzspalten durch Str<strong>in</strong>g-Verknüpfung<br />

SELECT CONCAT( CAST( ( CASE Geschlecht<br />

WHEN ’M’ THEN ’Herrn’<br />

WHEN ’W’ THEN ’Frau’<br />

ELSE ”<br />

END ) AS CHAR(22) ),<br />

RPAD( CAST( ID AS VARCHAR(8) ), 8 )<br />

) AS Zeile1,<br />

CASE<br />

WHEN Vorname IS NULL THEN Name<br />

ELSE CONCAT(Vorname, ’ ’, Name)<br />

END AS Zeile2,<br />

CONCAT(Strasse, ’ ’, Hausnummer) AS Zeile3,<br />

CONCAT(PLZ, ’ ’, Ort) AS Zeile4<br />

FROM Versicherungsnehmer vn;<br />

Lösung zu Übung 4 – Neue Spalten durch Spaltenfunktion<br />

SELECT COUNT(*) AS Anzahl, ’BO’ AS Kreis<br />

FROM Fahrzeug<br />

WHERE Kennzeichen STARTING WITH ’BO-’<br />

UNION<br />

SELECT COUNT(*), ’GE’<br />

FROM Fahrzeug<br />

WHERE Kennzeichen STARTING WITH ’GE-’;<br />

Für STARTING WITH gibt es Alternativen; wir haben verschiedentlich SUB-<br />

STRING mit POSITION o. a. verwendet.<br />

240


25. Gruppierungen<br />

25.1. Syntax von GROUP BY . . . . . . . . . . . . . . . . . . . . . . . . . 241<br />

25.2. Gruppierung bei e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . . 242<br />

25.3. Gruppierung über mehrere Tabellen . . . . . . . . . . . . . . . . 243<br />

25.4. Voraussetzungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244<br />

25.5. Erweiterungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245<br />

25.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 247<br />

25.7. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247<br />

25.8. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250<br />

Abfragen werden sehr häufig gruppiert, weil nicht nur e<strong>in</strong>zelne Informationen,<br />

sondern auch Zusammenfassungen gewünscht werden. Durch die GROUP BY-<br />

Klausel im SELECT-Befehl werden alle Zeilen, die <strong>in</strong> e<strong>in</strong>er oder mehreren Spalten<br />

den gleichen Wert enthalten, <strong>in</strong> jeweils e<strong>in</strong>er Gruppe zusammengefasst.<br />

Dies macht <strong>in</strong> der Regel nur S<strong>in</strong>n, wenn <strong>in</strong> der Spaltenliste des SELECT-Befehls<br />

e<strong>in</strong>e gruppenweise Auswertung, also e<strong>in</strong>e der Spaltenfunktionen enthalten ist.<br />

25.1. Syntax von GROUP BY<br />

Die GROUP BY-Klausel hat folgenden allgeme<strong>in</strong>en Aufbau:<br />

GROUP BY <br />

Die Spaltenliste enthält, durch Komma getrennt, die Namen von e<strong>in</strong>er oder mehreren<br />

Spalten. Für jede Spalte kann e<strong>in</strong>e eigene Sortierung angegeben werden:<br />

<br />

-- oder<br />

COLLATE <br />

Die Spalten <strong>in</strong> der Spaltenliste können meistens wahlweise mit dem Spaltennamen<br />

der Tabelle, mit dem Alias-Namen aus der Select-Liste oder mit Spaltennummer<br />

gemäß der Select-Liste (ab 1 gezählt) angegeben werden.<br />

In der Regel enthält die Abfrage e<strong>in</strong>e der Aggregatfunktionen und wird durch<br />

ORDER BY nach den gleichen Spalten sortiert.<br />

241


Gruppierungen<br />

25.2. Gruppierung bei e<strong>in</strong>er Tabelle<br />

Im e<strong>in</strong>fachsten Fall werden Daten nach e<strong>in</strong>er Spalte gruppiert und gezählt.<br />

Aufgabe: Im folgenden Beispiel wird die Anzahl der Abteilungen für jeden Ort<br />

aufgeführt.<br />

SELECT Ort, COUNT(*) AS Anzahl<br />

FROM Abteilung<br />

GROUP BY Ort<br />

ORDER BY Ort;<br />

ORT ANZAHL<br />

Bochum 3<br />

Dortmund 4<br />

Essen 4<br />

Herne 1<br />

Aufgabe: Die folgende Abfrage listet auf, wie viele Mitarbeiter es <strong>in</strong> den Abteilungen<br />

und Raumnummern gibt:<br />

SELECT Abteilung_ID AS Abt,<br />

Raum,<br />

COUNT(*) AS Anzahl<br />

FROM Mitarbeiter<br />

GROUP BY Abt, Raum<br />

ORDER BY Abt, Raum;<br />

ABT RAUM ANZAHL<br />

1 112 1<br />

1 113 1<br />

2 212 2<br />

3 312 1<br />

3 316 1<br />

4 412 2 // usw.<br />

Am folgenden Beispiel wird die Gruppierung besonders deutlich.<br />

Aufgabe: Berechne die mittlere Schadenshöhe für die Schadensfälle mit und<br />

ohne Personenschäden.<br />

242<br />

SELECT Verletzte,<br />

AVG(Schadenshoehe) AS Mittlere_Schadenshoehe<br />

FROM Schadensfall<br />

GROUP BY Verletzte;<br />

VERLETZTE MITTLERE_SCHADENSHOEHE<br />

J 3.903,87<br />

N 1.517,45


Gruppierung über mehrere Tabellen<br />

Die Spalte Verletzte enthält entweder 'J' oder 'N' und ist verpflichtend, kann also<br />

ke<strong>in</strong>e NULL-Werte enthalten. Deshalb werden durch die GROUP BY-Anweisung<br />

e<strong>in</strong>e oder zwei Gruppen gebildet. Für jede Gruppe wird der Mittelwert gesondert<br />

berechnet aus den Werten, die <strong>in</strong> der Gruppe vorkommen. In diesem Fall liefert<br />

die Funktion AVG also e<strong>in</strong> oder zwei Ergebnisse, abhängig davon, welche Werte<br />

<strong>in</strong> der Spalte Verletzte überhaupt vorkommen.<br />

Zeilen, bei denen e<strong>in</strong>er der Werte zur Gruppierung fehlt, oder Zeilen mit NULL-<br />

Werten werden als eigene Gruppe gezählt.<br />

25.3. Gruppierung über mehrere Tabellen<br />

E<strong>in</strong>e Gruppierung kann auch Felder aus verschiedenen Tabellen auswerten. Dafür<br />

s<strong>in</strong>d zunächst die Voraussetzungen für die Verknüpfung mehrerer Tabellen<br />

zu beachten. Bitte beachten Sie das folgende Beispiel.<br />

Aufgabe: Gesucht wird für jeden Fahrzeughersteller (mit Angabe von ID und<br />

Name) und Jahr die Summe der Schadenshöhe aus der Tabelle Schadensfall.<br />

SELECT fh.ID AS Hersteller_ID,<br />

fh.Name AS Hersteller_Name,<br />

EXTRACT(YEAR FROM sf.Datum) AS Jahr,<br />

SUM(sf.Schadenshoehe) AS Schadenssumme<br />

FROM Schadensfall sf<br />

JOIN Zuordnung_SF_FZ zu ON sf.ID = zu.Schadensfall_ID<br />

JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

JOIN Fahrzeugtyp ft ON ft.ID = fz.Fahrzeugtyp_ID<br />

JOIN Fahrzeughersteller fh ON fh.ID = ft.Hersteller_ID<br />

GROUP BY Hersteller_ID, Hersteller_Name, Jahr<br />

ORDER BY Jahr, Hersteller_ID;<br />

HERSTELLER_ID HERSTELLER_NAME JAHR SCHADENSSUMME<br />

9 Volvo 2007 2.066,00<br />

10 Renault 2007 5.781,60<br />

11 Seat 2007 1.234,50<br />

2 Opel 2008 1.438,75<br />

11 Seat 2008 1.983,00<br />

9 Volvo 2009 4.092,15<br />

10 Renault 2009 865,00<br />

Ausgangspunkt ist die Tabelle Schadensfall, aus deren E<strong>in</strong>trägen die Summe gebildet<br />

werden soll. Durch JOIN werden die anderen Tabellen herangezogen, und<br />

zwar mit der ID auf die Verknüpfung: Schadensfall → Zuordnung → Fahrzeug<br />

→ Fahrzeugtyp → Hersteller. Dann stehen ID und Name aus der Tabelle Fahrzeughersteller<br />

zur Verfügung, die für die Gruppierung gewünscht werden.<br />

243


Gruppierungen<br />

Zur Gruppierung genügt eigentlich die Verwendung von Hersteller_ID. Aber man<br />

möchte sich natürlich den Herstellernamen anzeigen lassen. Allerd<strong>in</strong>gs gibt es<br />

e<strong>in</strong>en Fehler, wenn man den Namen nur <strong>in</strong> der SELECT-Liste benutzt und <strong>in</strong> der<br />

GROUP BY-Liste streicht (siehe dazu die Erläuterungen im nächsten Abschnitt):<br />

Quelltext Falsch<br />

SELECT fh.ID AS Hersteller_ID,<br />

fh.Name AS Hersteller_Name,<br />

EXTRACT(YEAR FROM sf.Datum) AS Jahr,<br />

SUM(sf.Schadenshoehe) AS Schadenssumme<br />

FROM Schadensfall sf<br />

JOIN ... (wie oben)<br />

GROUP BY Hersteller_ID, Jahr<br />

ORDER BY Jahr, Hersteller_ID<br />

Ungültiger Ausdruck <strong>in</strong> der Select-Liste<br />

(fehlt entweder <strong>in</strong> e<strong>in</strong>er Aggregatfunktion oder <strong>in</strong> der GROUP BY-Klausel).<br />

25.4. Voraussetzungen<br />

Wie das letzte Beispiel zeigt, muss die GROUP BY-Klausel gewisse Bed<strong>in</strong>gungen<br />

erfüllen. Auch dafür gilt: Jedes DBMS weicht teilweise vom Standard ab.<br />

• Jeder Spaltenname der SELECT-Auswahl, der nicht zu e<strong>in</strong>er Aggregatfunktion<br />

gehört, muss auch <strong>in</strong> der GROUP BY-Klausel benutzt werden.<br />

Diese Bed<strong>in</strong>gung wird im letzten Beispiel verletzt: Hersteller_Name steht <strong>in</strong> der<br />

SELECT-Liste, aber nicht <strong>in</strong> der GROUP BY-Klausel. Hier ist e<strong>in</strong>e Änderung<br />

e<strong>in</strong>fach, weil ID und Name des Herstellers gleichwertig s<strong>in</strong>d. Übrigens erlaubt<br />

My<strong>SQL</strong> auch die Auswahl von Feldern, die <strong>in</strong> der GROUP BY-Klausel fehlen.<br />

Umgekehrt ist es <strong>in</strong> der Regel möglich, e<strong>in</strong>e Spalte per GROUP BY zu gruppieren,<br />

ohne die Spalte selbst <strong>in</strong> der SELECT-Liste zu verwenden.<br />

• GROUP BY kann Spalten der Tabellen, abgeleiteter Tabellen oder VIEWs <strong>in</strong> der<br />

FROM-Klausel oder der JOIN-Klausel enthalten. Sie kann ke<strong>in</strong>er Spalte entsprechen,<br />

die das Ergebnis e<strong>in</strong>er Funktion (genauer: e<strong>in</strong>er Berechnung, e<strong>in</strong>er<br />

Aggregatfunktion oder e<strong>in</strong>er benutzerdef<strong>in</strong>ierten Funktion) s<strong>in</strong>d.<br />

Dies entspricht der gleichen E<strong>in</strong>schränkung, die bei den unter „Ergebnis von Berechnungen“<br />

im Kapitel Berechnete Spalten 1 genannt ist.<br />

Mit der Beispieldatenbank s<strong>in</strong>d ke<strong>in</strong>e passenden Beispiele möglich; wir müssen<br />

uns deshalb auf fiktive Tabellen und Spalten beschränken:<br />

1 Abschnitt 24.1 auf Seite 236<br />

244


Die folgenden Abfragen s<strong>in</strong>d nicht zulässig.<br />

Erweiterungen<br />

Quelltext Falsch<br />

SELECT Spalte1, Spalte2 FROM T GROUP BY Spalte1 + Spalte2;<br />

SELECT Spalte1 + constant + Spalte2 FROM T GROUP BY Spalte1 + Spalte2;<br />

Die folgenden Abfragen s<strong>in</strong>d zulässig.<br />

SELECT Spalte1, Spalte2 FROM T GROUP BY Spalte1, Spalte2;<br />

SELECT Spalte1 + Spalte2 FROM T GROUP BY Spalte1, Spalte2;<br />

SELECT Spalte1 + Spalte2 FROM T GROUP BY Spalte1+ Spalte2;<br />

SELECT Spalte1 + Spalte2 + constant FROM T GROUP BY Spalte1, Spalte2;<br />

• GROUP BY kann nicht benutzt werden <strong>in</strong> e<strong>in</strong>em SELECT-Befehl mit folgenden<br />

Bed<strong>in</strong>gungen:<br />

• Der Befehl enthält e<strong>in</strong>e INTO-Klausel (d. h. er wird benutzt, um e<strong>in</strong>zelne Zeilen<br />

zu verarbeiten − dies wird <strong>in</strong> dieser <strong>E<strong>in</strong>führung</strong> nicht erläutert).<br />

• Der Befehl enthält e<strong>in</strong>e Unterabfrage oder e<strong>in</strong>e VIEW, die selbst mit e<strong>in</strong>er<br />

GROUP BY- oder HAVING-Klausel arbeitet.<br />

• Jeder SELECT-Befehl darf e<strong>in</strong>schließlich aller Unterabfragen höchstens e<strong>in</strong>e<br />

GROUP BY-Klausel enthalten.<br />

Zu dieser Bed<strong>in</strong>gung ist den Autoren ke<strong>in</strong> vernünftiges Beispiel e<strong>in</strong>gefallen. E<strong>in</strong>es,<br />

das dafür konstruiert wurde, hat Firebird trotz klarer Verletzung ausgeführt,<br />

wahrsche<strong>in</strong>lich weil es sich wegen der anderen Bed<strong>in</strong>gungen sowieso nur um jeweils<br />

e<strong>in</strong>e Zeile handelte und ke<strong>in</strong>e Gruppierung erforderlich war.<br />

25.5. Erweiterungen<br />

25.5.1. Zwischensummen mit CUBE<br />

Diese Erweiterung steht nur <strong>in</strong> manchen DBMS zur Verfügung. Es soll deshalb<br />

e<strong>in</strong> kurzer H<strong>in</strong>weis genügen.<br />

GROUP BY CUBE ()<br />

Mit diesem Befehl werden zusätzlich zu den normalerweise von GROUP BY erstellten<br />

Zeilen auch Zwischensummen <strong>in</strong> das Ergebnis aufgenommen. Für jede<br />

mögliche Komb<strong>in</strong>ation von Spalten <strong>in</strong> der wird e<strong>in</strong>e eigene<br />

GROUP BY-Summenzeile zurückgegeben.<br />

Erläuterungen und Beispiele s<strong>in</strong>d zu f<strong>in</strong>den z. B. unter GROUP BY (Microsoft) 2<br />

und weiteren L<strong>in</strong>ks.<br />

2 http://msdn.microsoft.com/de-de/library/ms177673%28<strong>SQL</strong>.90%29.aspx<br />

245


Gruppierungen<br />

25.5.2. Gesamtsummen mit ROLLUP<br />

Auch diese Erweiterung steht nur <strong>in</strong> manchen DBMS zur Verfügung. Es soll wiederum<br />

e<strong>in</strong> kurzer H<strong>in</strong>weis genügen.<br />

GROUP BY WITH ROLLUP<br />

Mit diesem Befehl werden zusätzlich zu den von GROUP BY erstellten Zeilen<br />

auch Gesamtsummen <strong>in</strong> das Ergebnis aufgenommen.<br />

Erläuterungen und Beispiele s<strong>in</strong>d zu f<strong>in</strong>den z. B. unter GROUP BY (My<strong>SQL</strong>) 3 .<br />

25.5.3. Ergebnis mit HAVING e<strong>in</strong>schränken<br />

Diese Erweiterung ist e<strong>in</strong>e selbständige Klausel des SELECT-Befehls und hat eigentlich<br />

nichts mit GROUP BY zu tun. In der Praxis wird sie aber überwiegend<br />

als Ergänzung zur Gruppierung verwendet und folgt ggf. direkt danach.<br />

GROUP BY <br />

HAVING <br />

Dieser Befehl dient dazu, nicht alle Gruppierungen <strong>in</strong> die Ausgabe zu übernehmen,<br />

sondern nur diejenigen, die den zusätzlichen Bed<strong>in</strong>gungen entsprechen.<br />

Im folgenden Beispiel (vergleiche oben unter „Gruppierung bei e<strong>in</strong>er Tabelle“)<br />

wird festgestellt, an welchen Orten sich genau e<strong>in</strong>e Abteilung bef<strong>in</strong>det.<br />

SELECT Ort, COUNT(*) AS Anzahl<br />

FROM Abteilung<br />

GROUP BY Ort<br />

HAVING COUNT(*) = 1<br />

ORDER BY Ort;<br />

Herne 1<br />

Bitte beachten Sie, dass der Alias-Name nicht verwendet werden kann, sondern<br />

die Aggregatfunktion erneut aufgeführt werden muss.<br />

H<strong>in</strong>weis: Die HAVING-Klausel wird als letzter Teil des SELECT-Befehls ausgeführt.<br />

Es ist deshalb zu empfehlen, alle E<strong>in</strong>schränkungen vorher zu regeln, z. B.<br />

als Teil von WHERE-Bed<strong>in</strong>gungen. Nur wenn − wie bei Aggregatfunktionen −<br />

diese E<strong>in</strong>schränkung erst am Schluss geprüft werden kann, ist HAVING zu benutzen.<br />

3 http://dev.mysql.com/doc/refman/5.1/de/GROUP-BY-modifiers.html<br />

246


Möglich, aber nicht günstig.<br />

Zusammenfassung<br />

<strong>SQL</strong>-Quelltext Falsch<br />

SELECT Abteilung_ID,<br />

COUNT(*)<br />

FROM MITARBEITER<br />

GROUP BY Abteilung_ID<br />

HAVING Abteilung_ID < 3;<br />

Besseres Verfahren, weil weniger Datensätze geprüft werden müssen.<br />

<strong>SQL</strong>-Quelltext Richtig<br />

SELECT Abteilung_ID,<br />

COUNT(*)<br />

FROM MITARBEITER<br />

GROUP BY Abteilung_ID<br />

WHERE Abteilung_ID < 3;<br />

25.6. Zusammenfassung<br />

In diesem Kapitel lernten wir E<strong>in</strong>zelheiten über die Gruppierung bei Abfragen.<br />

• Dies wird meistens gleichzeitig mit ORDER BY und <strong>in</strong> Verb<strong>in</strong>dung mit Aggregatfunktionen<br />

verwendet.<br />

• Die Gruppierung ist auch über mehrere Tabellen h<strong>in</strong>weg möglich.<br />

• E<strong>in</strong>ige wichtige E<strong>in</strong>schränkungen s<strong>in</strong>d zu beachten; vor allem s<strong>in</strong>d die Felder<br />

aus der Spaltenliste <strong>in</strong> der Regel auch unter GROUP BY aufzuführen.<br />

Die HAVING-Klausel kann das Abfrageergebnis e<strong>in</strong>schränken, sollte aber zurückhaltend<br />

benutzt werden.<br />

25.7. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 248.<br />

Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Feststellungen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. GROUP BY kann nur zusammen mit (m<strong>in</strong>destens) e<strong>in</strong>er Spaltenfunktion<br />

benutzt werden.<br />

2. GROUP BY kann nur auf „echte“ Spalten angewendet werden, nicht auf berechnete<br />

Spalten.<br />

247


Gruppierungen<br />

3. In der GROUP BY-Klausel kann e<strong>in</strong> Spaltenname ebenso angegeben werden<br />

wie e<strong>in</strong> Spalten-Alias.<br />

4. Die WHERE-Klausel kommt vor der GROUP BY-Klausel.<br />

5. Folgende Gruppierung nach den ersten zwei Ziffern der PLZ ist zulässig.<br />

SELECT PLZ, COUNT(*)<br />

FROM Versicherungsnehmer vn<br />

GROUP BY SUBSTRING(vn.PLZ FROM 1 for 2)<br />

ORDER BY 1;<br />

6. HAVING darf nur zusammen mit e<strong>in</strong>er Gruppierung verwendet werden.<br />

Übung 2 – Gruppierung für 1 Tabelle<br />

Bestimmen Sie die Anzahl der Kunden (Versicherungsnehmer) <strong>in</strong> jedem Briefzentrum<br />

(d. h. die Ziffern 1 und 2 der PLZ).<br />

Übung 3 – Gruppierung für 1 Tabelle<br />

Bestimmen Sie, wie viele Fahrzeuge <strong>in</strong> jedem Kreis angemeldet s<strong>in</strong>d.<br />

Übung 4 – Gruppierung für mehrere Tabellen<br />

Bestimmen Sie, wie viele Fahrzeugtypen pro Hersteller registriert s<strong>in</strong>d, und nennen<br />

Sie Namen und Land der Hersteller. – H<strong>in</strong>weis: Erstellen Sie e<strong>in</strong>e Abfrage<br />

für Anzahl plus Hersteller-ID und verknüpfen Sie das Ergebnis mit der Tabelle<br />

Hersteller.<br />

Übung 5 – Gruppierung für mehrere Tabellen<br />

Bestimmen Sie – gruppiert nach Jahr des Schadensfalls und Kreis des Fahrzeugs<br />

– die Anzahl der Schadensfälle. Es sollen bei den Fahrzeugen nur Schadensfälle<br />

mit e<strong>in</strong>em Schuldanteil von mehr als 50 [Prozent] berücksichtigt werden.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 247.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Richtig s<strong>in</strong>d die Aussagen 3, 4. Falsch s<strong>in</strong>d die Aussagen 1, 2, 5, 6.<br />

248


Lösung zu Übung 2 – Gruppierung für 1 Tabelle<br />

SELECT SUBSTRING(vn.PLZ FROM 1 for 2), COUNT(*)<br />

FROM Versicherungsnehmer vn<br />

GROUP BY 1<br />

ORDER BY 1;<br />

Übungen<br />

H<strong>in</strong>weis: Hierbei handelt es sich um die korrekte Version zur Frage 5 aus<br />

Übung 1.<br />

Lösung zu Übung 3 – Gruppierung für 1 Tabelle<br />

SELECT SUBSTRING(Kennzeichen FROM 1 for POSITION(’-’, Kennzeichen)-1 ) AS Kreis,<br />

COUNT(*) AS Anzahl<br />

FROM Fahrzeug fz<br />

GROUP BY 1<br />

ORDER BY 1;<br />

H<strong>in</strong>weis: Hierbei handelt es sich um die vollständige Version der letzten Übung<br />

zu „berechneten Spalten“.<br />

Lösung zu Übung 4 – Gruppierung für mehrere Tabellen<br />

SELECT Name, Land, Anzahl<br />

FROM (<br />

SELECT ft.Hersteller_ID AS ID, Count(ft.Hersteller_ID) AS Anzahl<br />

FROM Fahrzeugtyp ft<br />

GROUP BY ft.Hersteller_ID<br />

) temp<br />

JOIN Fahrzeughersteller fh<br />

ON fh.ID = temp.ID<br />

ORDER BY Name;<br />

Lösung zu Übung 5 – Gruppierung für mehrere Tabellen<br />

SELECT EXTRACT(YEAR FROM sf.Datum) AS Jahr,<br />

SUBSTRING(Kennzeichen FROM 1 for POSITION(’-’, Kennzeichen)-1 ) AS Kreis,<br />

COUNT(*)<br />

FROM Zuordnung_SF_FZ zu<br />

RIGHT JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

INNER JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

WHERE zu.Schuldanteil > 50<br />

GROUP BY 1, 2<br />

ORDER BY 1, 2;<br />

249


Gruppierungen<br />

Erläuterungen: Die Tabelle der Zuordnungen ist kle<strong>in</strong>er als die diejenige der<br />

Fahrzeuge, und darauf bezieht sich die WHERE-Bed<strong>in</strong>gung; deshalb ist sie als<br />

Haupttabelle am s<strong>in</strong>nvollsten. Wegen der Kennzeichen benötigen wir e<strong>in</strong>en JOIN<br />

auf die Tabelle Fahrzeug. Wegen des Datums des Schadensfalls für die Gruppierung<br />

nach Jahr benötigen wir e<strong>in</strong>en JOIN auf die Tabelle Schadensfall.<br />

25.8. Siehe auch<br />

Ergänzende Informationen s<strong>in</strong>d <strong>in</strong> diesen Kapiteln zu f<strong>in</strong>den:<br />

• Datentypen 4 <strong>in</strong>formiert auch über besondere Sortierungen e<strong>in</strong>er e<strong>in</strong>zelnen<br />

Spalte.<br />

• Mehrere Tabellen 5 verknüpfen<br />

4 Kapitel 13 auf Seite 97<br />

5 Kapitel 18 auf Seite 167<br />

250


26. Unterabfragen<br />

26.1. Ergebnis als e<strong>in</strong>zelner Wert . . . . . . . . . . . . . . . . . . . . . 251<br />

26.2. Ergebnis als Liste mehrerer Werte . . . . . . . . . . . . . . . . . 254<br />

26.3. Ergebnis <strong>in</strong> Form e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . 256<br />

26.4. Verwendung bei Befehlen zum Speichern . . . . . . . . . . . . 258<br />

26.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />

26.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264<br />

Immer wieder werden zur Durchführung e<strong>in</strong>er Abfrage oder e<strong>in</strong>es anderen Befehls<br />

Informationen benötigt, die zuerst durch e<strong>in</strong>e eigene Abfrage geholt werden<br />

müssen. Solche „Unterabfragen“ werden <strong>in</strong> diesem Kapitel behandelt.<br />

• Wenn e<strong>in</strong>e Abfrage als Ergebnis e<strong>in</strong>en e<strong>in</strong>zelnen Wert liefert, kann sie anstelle<br />

e<strong>in</strong>es Wertes benutzt werden.<br />

• Wenn e<strong>in</strong>e Abfrage als Ergebnis e<strong>in</strong>e Liste von Werten liefert, kann sie anstelle<br />

e<strong>in</strong>er solchen Liste benutzt werden.<br />

• Wenn e<strong>in</strong>e Abfrage e<strong>in</strong>e Ergebnismenge, also etwas <strong>in</strong> Form e<strong>in</strong>er Tabelle liefert,<br />

kann sie anstelle e<strong>in</strong>er Tabelle benutzt werden.<br />

Bitte beachten Sie, dass die Unterabfrage immer <strong>in</strong> Klammern gesetzt wird. Auch<br />

wenn e<strong>in</strong> DBMS das nicht verlangen sollte, ist es wegen der Übersichtlichkeit<br />

dr<strong>in</strong>gend zu empfehlen.<br />

Allgeme<strong>in</strong>er H<strong>in</strong>weis: Unterabfragen arbeiten häufig langsamer als andere Verfahren.<br />

Wenn irgend möglich sollten Sie e<strong>in</strong>e der JOIN-Varianten vorziehen.<br />

26.1. Ergebnis als e<strong>in</strong>zelner Wert<br />

26.1.1. Ergebnisse e<strong>in</strong>facher Abfragen<br />

Immer wieder kennt der Anwender den Namen e<strong>in</strong>es Objekts, benötigt aber für<br />

Abfragen die ID. Diese holt er sich mit e<strong>in</strong>er Unterabfrage und übergibt das Ergebnis<br />

an die eigentliche Abfrage.<br />

Aufgabe: Nenne alle Mitarbeiter der Abteilung „Schadensabwicklung“.<br />

251


Unterabfragen<br />

• Lösung Teil 1: Hole die ID dieser Abteilung anhand des Namens.<br />

• Lösung Teil 2: Hole die Mitarbeiter dieser Abteilung unter Benutzung der gefundenen<br />

ID.<br />

SELECT Personalnummer, Name, Vorname<br />

FROM Mitarbeiter<br />

WHERE Abteilung_ID =<br />

( SELECT ID FROM Abteilung<br />

WHERE Kuerzel = ’ScAb’ );<br />

PERSONALNUMMER NAME VORNAME<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a<br />

80002 Aliman Zafer<br />

80003 Langer Norbert<br />

80004 Kolic Ivana<br />

Teil 1 der Lösung ist der SELECT-Befehl <strong>in</strong>nerhalb der Klammern. Das Ergebnis<br />

ist e<strong>in</strong>e e<strong>in</strong>zelne ID. Diese kann anstelle e<strong>in</strong>er konkreten Zahl <strong>in</strong> die WHERE-<br />

Klausel der eigentlichen Abfrage übernommen werden. Das Wörtchen „Diese“<br />

hat <strong>in</strong> diesem Fall sprachlich e<strong>in</strong>e doppelte Bedeutung: zum e<strong>in</strong>en steht es für die<br />

Unterabfrage, zum anderen für die ID als Ergebnis.<br />

H<strong>in</strong>weis: Dies funktioniert nur deshalb auf e<strong>in</strong>fache Weise, weil die Kurzbezeichnung<br />

faktisch e<strong>in</strong>deutig ist und deshalb genau e<strong>in</strong>e ID geliefert wird. Wenn wir<br />

uns darauf nicht verlassen wollen oder können oder wenn das DBMS „empf<strong>in</strong>dlich“<br />

ist und die E<strong>in</strong>deutigkeit des Ergebnisses nicht erkennt, können wir daraus<br />

bewusst e<strong>in</strong>en e<strong>in</strong>zelnen Wert machen:<br />

SELECT Personalnummer, Name, Vorname<br />

FROM Mitarbeiter<br />

WHERE Abteilung_ID =<br />

( SELECT MAX(ID) FROM Abteilung<br />

WHERE Kuerzel = ’ScAb’ );<br />

E<strong>in</strong>e solche Aufgabe kann auch zweimal dieselbe Tabelle benutzen.<br />

Aufgabe: Nenne alle anderen Mitarbeiter der Abteilung, deren Leiter<strong>in</strong> Christ<strong>in</strong>a<br />

Schatz<strong>in</strong>g ist.<br />

• Lösung Teil 1: Hole die Abteilung_ID, die bei Christ<strong>in</strong>a Schatz<strong>in</strong>g registriert ist.<br />

• Lösung Teil 2: Hole die Mitarbeiter dieser Abteilung unter Benutzung der gefundenen<br />

Abteilung_ID.<br />

Zur Sicherheit prüfen wir auch die Eigenschaft Ist_Leiter.<br />

252<br />

SELECT Personalnummer, Name, Vorname<br />

FROM Mitarbeiter<br />

WHERE ( Abteilung_ID =<br />

( SELECT Abteilung_ID


FROM Mitarbeiter<br />

WHERE ( Name = ’Sch<strong>in</strong>dler’ )<br />

AND ( Vorname = ’Christ<strong>in</strong>a’ )<br />

AND ( Ist_Leiter = ’J’ )<br />

)<br />

) AND ( Ist_Leiter = ’N’ );<br />

PERSONALNUMMER NAME VORNAME<br />

80002 Aliman Zafer<br />

80003 Langer Norbert<br />

80004 Kolic Ivana<br />

26.1.2. Ergebnisse von Spaltenfunktionen<br />

Ergebnis als e<strong>in</strong>zelner Wert<br />

Oft werden Ergebnisse von Aggregatfunktionen für die WHERE-Klausel benötigt.<br />

Aufgabe: Hole die Schadensfälle mit unterdurchschnittlicher Schadenshöhe.<br />

• Lösung Teil 1: Berechne die durchschnittliche Höhe aller Schadensfälle.<br />

• Lösung Teil 2: Übernimm das Ergebnis als Vergleich <strong>in</strong> die eigentliche Abfrage.<br />

SELECT ID, Datum, Ort, Schadenshoehe<br />

FROM Schadensfall<br />

WHERE Schadenshoehe <<br />

( SELECT AVG(Schadenshoehe) FROM Schadensfall );<br />

ID DATUM ORT SCHADENSHOEHE<br />

1 03.02.2007 Reckl<strong>in</strong>ghausen, Bergknappenstr. 144 1.234,50<br />

2 11.07.2007 Haltern, Hauptstr. 46 2.066,00<br />

4 27.05.2008 Reckl<strong>in</strong>ghausen, Südgrabenstr. 23 1.438,75<br />

5 05.10.2008 Dorsten, Oberhausener Str. 18 1.983,00<br />

7 21.06.2009 Reckl<strong>in</strong>ghausen, Bergknappenstr. 144 865,00<br />

Aufgabe: Bestimme alle Schadensfälle, die von der durchschnittlichen Schadenshöhe<br />

e<strong>in</strong>es Jahres maximal 300 =C abweichen.<br />

• Lösung Teil 1: Bestimme den Durchschnitt aller Schadensfälle pro Jahr.<br />

• Lösung Teil 2: Hole alle Schadensfälle, deren Schadenshöhe im betreffenden<br />

Jahr <strong>in</strong>nerhalb des Bereichs „Durchschnitt plus/m<strong>in</strong>us 300“ liegen.<br />

SELECT sf.ID, sf.Datum, sf.Schadenshoehe, EXTRACT(YEAR FROM sf.Datum) AS Jahr<br />

FROM Schadensfall sf<br />

WHERE ABS(Schadenshoehe - ( SELECT AVG(sf2.Schadenshoehe)<br />

FROM Schadensfall sf2<br />

WHERE EXTRACT(YEAR FROM sf2.Datum)<br />

= EXTRACT(YEAR FROM sf.Datum)<br />

)<br />

)


Unterabfragen<br />

ID DATUM SCHADENSHOEHE JAHR<br />

2 11.07.2007 2.066,00 2007<br />

4 27.05.2008 1.438,75 2008<br />

5 05.10.2008 1.983,00 2008<br />

8 01.08.2009 2.471,50 2009<br />

Zuerst muss für jeden e<strong>in</strong>zelnen Schadensfall aus sf das Jahr bestimmt werden.<br />

In der Unterabfrage, die <strong>in</strong> der <strong>in</strong>neren Klammer steht, wird für alle Schadensfälle<br />

des betreffenden Jahres die durchschnittliche Schadenshöhe bestimmt. Dieser<br />

Wert wird mit der aktuellen Schadenshöhe verglichen; dazu wird die ABS-<br />

Funktion benutzt, also der absolute Betrag der Differenz der beiden Werte.<br />

i Information<br />

Dies ist e<strong>in</strong> Paradebeispiel dafür, wie Unterabfragen nicht benutzt<br />

werden sollen.<br />

Für jeden e<strong>in</strong>zelnen Datensatz muss <strong>in</strong> der WHERE-Bed<strong>in</strong>gung e<strong>in</strong>e neue Unterabfrage<br />

gestartet werden − mit eigener WHERE-Klausel und Durchschnittsberechnung.<br />

Viel besser ist e<strong>in</strong>e der JOIN-Varianten oder e<strong>in</strong>e der Lösungen im<br />

Abschnitt „Ergebnis <strong>in</strong> Form e<strong>in</strong>er Tabelle“ (siehe unten).<br />

26.2. Ergebnis als Liste mehrerer Werte<br />

Das Ergebnis e<strong>in</strong>er Abfrage kann als Filter für die eigentliche Abfrage benutzt<br />

werden.<br />

Aufgabe: Bestimme alle Fahrzeuge e<strong>in</strong>es bestimmten Herstellers.<br />

• Lösung Teil 1: Hole die ID des gewünschten Herstellers.<br />

• Lösung Teil 2: Hole alle IDs der Tabelle Fahrzeugtyp zu dieser Hersteller-ID.<br />

• Lösung Teil 3: Hole alle Fahrzeuge, die zu dieser Liste von Fahrzeugtypen-IDs<br />

passen.<br />

254<br />

SELECT ID, Kennzeichen, Fahrzeugtyp_ID AS TypID<br />

FROM Fahrzeug<br />

WHERE Fahrzeugtyp_ID <strong>in</strong><br />

( SELECT ID<br />

FROM Fahrzeugtyp<br />

WHERE Hersteller_ID =<br />

( SELECT ID<br />

FROM Fahrzeughersteller<br />

WHERE Name = ’Volkswagen’ ) );


ID KENNZEICHEN TYPID<br />

22 BOR-PQ 567 3<br />

23 BOR-RS 890 2<br />

Ergebnis als Liste mehrerer Werte<br />

Teil 1 der Lösung ist die „<strong>in</strong>nere“ Klammer; dies ist das gleiche Verfahren wie<br />

im Abschnitt „Ergebnisse e<strong>in</strong>facher Abfragen“. Teil 2 der Lösung ist die „äußere“<br />

Klammer; Ergebnis ist e<strong>in</strong>e Liste von IDs der Tabelle Fahrzeugtyp, die als Werte<br />

für den Vergleich der WHERE-IN-Klausel verwendet werden.<br />

Wenn im Ergebnis der Fahrzeugtyp als Text angezeigt werden soll, muss die Abfrage<br />

erweitert werden, weil die Bezeichnung <strong>in</strong> der Tabelle Fahrzeugtyp zu f<strong>in</strong>den<br />

ist. Dafür kann diese Tabelle e<strong>in</strong> zweites Mal benutzt werden, wie es im Kapitel<br />

Mehrere Tabellen 1 erläutert wird; es ist auch e<strong>in</strong> Verfahren möglich, wie es<br />

unten im Abschnitt „Ergebnis <strong>in</strong> Form e<strong>in</strong>er Tabelle“ erläutert wird.<br />

Das Beispiel mit der durchschnittlichen Schadenshöhe geht auch so:<br />

Aufgabe: Gib alle Informationen zu den Schadensfällen des Jahres 2008, die<br />

von der durchschnittlichen Schadenshöhe 2008 maximal 300 =C abweichen.<br />

• Lösung Teil 1: Bestimme den Durchschnitt aller Schadensfälle <strong>in</strong>nerhalb von<br />

2008.<br />

• Lösung Teil 2: Hole alle IDs von Schadensfällen, deren Schadenshöhe <strong>in</strong>nerhalb<br />

des Bereichs „Durchschnitt plus/m<strong>in</strong>us 300“ liegen.<br />

• Lösung Teil 3: Hole alle anderen Informationen zu diesen IDs.<br />

SELECT *<br />

FROM Schadensfall<br />

WHERE ID <strong>in</strong> ( SELECT ID<br />

FROM Schadensfall<br />

WHERE ( ABS(Schadenshoehe<br />

- ( SELECT AVG(sf2.Schadenshoehe)<br />

FROM Schadensfall sf2<br />

WHERE EXTRACT(YEAR FROM sf2.Datum) = 2008<br />

)<br />

)


Unterabfragen<br />

Diese Situation wird dadurch e<strong>in</strong>facher, dass das Jahr 2008 fest v<strong>org</strong>egeben ist.<br />

Die <strong>in</strong>nerste Klammer bestimmt als Teil 1 der Lösung die durchschnittliche Schadenshöhe<br />

dieses Jahres. Die nächste Klammer vergleicht diesen Wert (absolut<br />

gesehen) mit der Schadenshöhe e<strong>in</strong>es jeden e<strong>in</strong>zelnen Schadensfalls im Jahr<br />

2008; alle „passenden“ IDs werden <strong>in</strong> der äußersten Klammer als Teil 2 der Lösung<br />

<strong>in</strong> e<strong>in</strong>er weiteren Unterabfrage zusammengestellt. Diese Liste liefert die<br />

Werte für die eigentliche Abfrage.<br />

Ganz offensichtlich ist dieses Beispiel konstruiert: Weil immer dieselbe Tabelle<br />

verwendet wird, kann die WHERE-Klausel der Unterabfrage <strong>in</strong> der äußersten<br />

Klammer auch als WHERE-Klausel der Hauptabfrage verwendet werden (die<br />

E<strong>in</strong>rückungen wurden zum besseren Vergleich nicht geändert):<br />

SELECT *<br />

FROM Schadensfall<br />

WHERE ( ABS(Schadenshoehe<br />

- ( SELECT AVG(sf2.Schadenshoehe)<br />

FROM Schadensfall sf2<br />

WHERE EXTRACT(YEAR FROM sf2.Datum) = 2008<br />

)<br />

)


Ergebnis <strong>in</strong> Form e<strong>in</strong>er Tabelle<br />

• In der Spaltenliste sollte jeweils e<strong>in</strong> Name als Spalten-Alias vor allem dann<br />

v<strong>org</strong>esehen werden, wenn mehrere Tabellen verknüpft werden; andernfalls erzeugt<br />

<strong>SQL</strong> selbständig Namen wie ID, ID1 usw., die man nicht ohne Weiteres<br />

versteht und zuordnen kann.<br />

• ORDER BY kann nicht s<strong>in</strong>nvoll genutzt werden, weil das Ergebnis der Unterabfrage<br />

als Tabelle behandelt wird und mit der Haupttabelle oder e<strong>in</strong>er anderen<br />

Tabelle verknüpft wird, wodurch e<strong>in</strong>e Sortierung sowieso verloreng<strong>in</strong>ge.<br />

Wie gesagt: E<strong>in</strong>e solche Unterabfrage kann überall stehen, wo e<strong>in</strong>e Tabelle v<strong>org</strong>esehen<br />

ist. In der vorstehenden Syntax steht sie nur beispielhaft <strong>in</strong>nerhalb der<br />

FROM-Klausel.<br />

Überarbeiten wir jetzt, wie oben angekündigt, e<strong>in</strong>ige Beispiele. Dabei wird die<br />

Unterabfrage, die bisher zur WHERE-Klausel gehörte, als Tabelle <strong>in</strong> die FROM-<br />

Klausel e<strong>in</strong>gebaut.<br />

Aufgabe: Bestimme alle Schadensfälle, die von der durchschnittlichen Schadenshöhe<br />

e<strong>in</strong>es Jahres maximal 300 =C abweichen.<br />

• Lösung Teil 1: Stelle alle Jahre zusammen und bestimme den Durchschnitt aller<br />

Schadensfälle <strong>in</strong>nerhalb e<strong>in</strong>es Jahres.<br />

• Lösung Teil 2: Hole alle Schadensfälle, deren Schadenshöhe im jeweiligen Jahr<br />

<strong>in</strong>nerhalb des Bereichs „Durchschnitt plus/m<strong>in</strong>us 300“ liegen.<br />

SELECT sf.ID, sf.Datum, sf.Schadenshoehe, temp.Jahr, temp.Durchschnitt<br />

FROM Schadensfall sf,<br />

( SELECT AVG(sf2.Schadenshoehe) AS Durchschnitt,<br />

EXTRACT(YEAR FROM sf2.Datum) AS Jahr<br />

FROM Schadensfall sf2<br />

GROUP BY EXTRACT(YEAR FROM sf2.Datum)<br />

) temp<br />

WHERE temp.Jahr = EXTRACT(YEAR FROM sf.Datum)<br />

AND ABS(Schadenshoehe - temp.Durchschnitt)


Unterabfragen<br />

• Lösung Teil 2: Hole alle IDs und Bezeichnungen der Tabelle Fahrzeugtyp, die<br />

zu dieser Hersteller-ID gehören.<br />

• Lösung Teil 3: Hole alle Fahrzeuge, die zu dieser Liste von Fahrzeugtyp-IDs gehören.<br />

SELECT Fahrzeug.ID, Kennzeichen, Typen.ID AS TYP, Typen.Bezeichnung<br />

FROM Fahrzeug,<br />

( SELECT ID, Bezeichnung<br />

FROM Fahrzeugtyp<br />

WHERE Hersteller_ID =<br />

( SELECT ID<br />

FROM Fahrzeughersteller<br />

WHERE Name = ’Volkswagen’ )<br />

) Typen<br />

WHERE Fahrzeugtyp_ID = Typen.ID;<br />

ID KENNZEICHEN TYP BEZEICHNUNG<br />

23 BOR-RS 890 2 Golf<br />

22 BOR-PQ 567 3 Passat<br />

Teil 1 der Lösung ist die „<strong>in</strong>nere“ Klammer; dies entspricht dem obigen Verfahren.<br />

Teil 2 der Lösung ist die „äußere“ Klammer; Ergebnis ist e<strong>in</strong>e Tabelle von IDs<br />

und Bezeichnungen, also e<strong>in</strong> Teil der Tabelle Fahrzeugtyp, deren Werte für den<br />

Vergleich der WHERE-Klausel und außerdem für die Ausgabe verwendet werden.<br />

H<strong>in</strong>weis: Mit den Möglichkeiten des nächsten Kapitels Erstellen von Views 2 ergeben<br />

sich wesentliche Verbesserungen: Mit e<strong>in</strong>er VIEW lassen sich Unterabfragen,<br />

die − wie die Liste von Typen und Herstellern − immer wieder benötigt werden,<br />

dauerhaft bereitstellen. Und mit e<strong>in</strong>er Inl<strong>in</strong>e-View werden verschachtelte Abfragen<br />

deutlich übersichtlicher.<br />

26.4. Verwendung bei Befehlen zum Speichern<br />

Bisher hatten wir Abfragen als Teil von anderen Abfragen benutzt; deshalb wird<br />

das Ganze auch als „Unterabfrage“ bezeichnet. Man kann das Ergebnis e<strong>in</strong>er Abfrage<br />

aber auch zum Speichern verwenden: sowohl für e<strong>in</strong>en e<strong>in</strong>zelnen Wert als<br />

auch als vollständigen Datensatz.<br />

26.4.1. Verwendung bei INSERT INTO ... SELECT<br />

E<strong>in</strong>e Abfrage <strong>in</strong> e<strong>in</strong>en INSERT-Befehl e<strong>in</strong>zub<strong>in</strong>den, ist e<strong>in</strong>es der Standardverfahren<br />

für INSERT:<br />

2 Kapitel 27 auf Seite 271<br />

258


INSERT INTO <br />

( )<br />

SELECT <br />

FROM <br />

[ ]<br />

[ WHERE ]<br />

Verwendung bei Befehlen zum Speichern<br />

Der SELECT-Befehl kann dabei beliebig aufgebaut se<strong>in</strong>: Daten aus e<strong>in</strong>er oder<br />

mehreren Tabellen holen, mit oder ohne E<strong>in</strong>schränkungen, mit oder ohne weitere<br />

Festlegungen. Lediglich drei Punkte s<strong>in</strong>d zu beachten:<br />

• Die Spaltenliste <strong>in</strong> der Zieltabelle muss mit den Spalten <strong>in</strong> der SELECT-<br />

Auflistung genau übere<strong>in</strong>stimmen; genauer: Die Datentypen müssen zue<strong>in</strong>ander<br />

passen, also gleich se<strong>in</strong> oder automatisch konvertiert werden können.<br />

• Es ist möglich, Daten aus der Zieltabelle zu holen und somit zu verdoppeln.<br />

Dann muss die ID automatisch vergeben werden können; und es ist unbed<strong>in</strong>gt<br />

mit WHERE zu arbeiten, weil es andernfalls zu e<strong>in</strong>er Endlosschleife kommen<br />

kann (siehe das erste nachfolgende Beispiel).<br />

• ORDER BY kann nicht s<strong>in</strong>nvoll genutzt werden, weil das Ergebnis <strong>in</strong> die Zieltabelle<br />

e<strong>in</strong>gefügt wird, wodurch e<strong>in</strong>e Sortierung sowieso verloreng<strong>in</strong>ge.<br />

Um weitere Testdaten zu erhalten, könnte so verfahren werden:<br />

Aufgabe: Kopiere die vorhandenen Schadensfälle.<br />

INSERT <strong>in</strong>to Schadensfall<br />

( Datum, Ort, Beschreibung, Schadenshoehe, Verletzte, Mitarbeiter_ID )<br />

SELECT Datum, Ort, Beschreibung, Schadenshoehe, Verletzte, Mitarbeiter_ID<br />

FROM Schadensfall<br />

WHERE ID < 10000;<br />

Auf die doppelte Angabe der Spalten kann nicht verzichtet werden, weil ID nicht<br />

benutzt werden darf (sie soll automatisch neu vergeben werden) und das DBMS<br />

wissen muss, welche Werte wie zugeordnet werden sollen. Auf diese Weise kann<br />

man ganz leicht 100 oder 1000 Testdatensätze erzeugen. Für den produktiven<br />

Betrieb wird man diese Syntax wohl eher seltener brauchen.<br />

In e<strong>in</strong>em ersten Versuch fehlte die WHERE-Bed<strong>in</strong>gung; erwartet wurde,<br />

dass nur die vor dem Befehl vorhandenen Datensätze bearbeitet<br />

würden. Tatsächlich hatte Firebird endlos kopiert, bis mit Strg + Alt<br />

+ Entf der „Stecker gezogen“ wurde. Danach war die Datenbank von<br />

3 MB auf 740 MB aufgebläht worden und (natürlich) beschädigt, sodass<br />

auf diese Tabelle nicht mehr richtig zugegriffen werden konnte.<br />

Weitere Versuche mit WHERE-Bed<strong>in</strong>gung arbeiteten wie v<strong>org</strong>esehen:<br />

nur die vorhandenen Datensätze wurden e<strong>in</strong>malig kopiert.<br />

259


Unterabfragen<br />

Die „neuen“ Daten können auch aus e<strong>in</strong>er anderen Tabelle geholt und mit konstanten<br />

Werten gemischt werden, wie es <strong>in</strong> der Beispieldatenbank geschieht:<br />

Aufgabe: Jeder Abteilungsleiter erhält e<strong>in</strong>en persönlichen Dienstwagen.<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )<br />

SELECT ’DO-WB 42’ || Abteilung_ID,<br />

’elfenbe<strong>in</strong>’, 14, ID<br />

FROM Mitarbeiter<br />

WHERE Ist_Leiter = ’J’;<br />

Die Spaltenliste der Zieltabelle Dienstwagen enthält alle Spalten mit Ausnahme<br />

der ID; diese wird automatisch vergeben. Diesen Spalten werden die Spalten der<br />

SELECT-Abfrage zugewiesen:<br />

• Die Mitarbeiter_ID ist das e<strong>in</strong>zige Feld aus der Quelltabelle Mitarbeiter, das<br />

unbed<strong>in</strong>gt benötigt wird und übernommen werden muss.<br />

• Die Farbe wird als Konstante e<strong>in</strong>getragen: Alle Abteilungsleiter bekommen den<br />

gleichen Wagentyp.<br />

• Der Fahrzeugtyp wird ebenso als Konstante e<strong>in</strong>getragen. Auch e<strong>in</strong>e Unterabfrage<br />

wie oben als Ergebnis e<strong>in</strong>er e<strong>in</strong>fachen Abfrage wäre möglich.<br />

• Für das Kennzeichen ist e<strong>in</strong>e Str<strong>in</strong>g-Verknüpfung v<strong>org</strong>esehen, bei der zusätzlich<br />

die Abteilung_ID übernommen wird. Bitte benutzen Sie die für Ihr DBMS<br />

richtige Art der Str<strong>in</strong>g-Verknüpfung.<br />

Die WHERE-Bed<strong>in</strong>gung beschränkt alles auf Abteilungsleiter.<br />

26.4.2. Verwendung bei INSERT INTO ... VALUES<br />

Die andere Version des INSERT-Befehls nutzt direkt e<strong>in</strong>e Liste von Werten:<br />

INSERT INTO <br />

[ ( ) ]<br />

VALUES ( )<br />

E<strong>in</strong> e<strong>in</strong>zelner Befehl sieht dabei wie folgt aus:<br />

Aufgabe: Der Mitarbeiter 2 bekommt e<strong>in</strong>en gelben Dienstwagen Typ 2.<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Farbe, Fahrzeugtyp_ID, Mitarbeiter_ID )<br />

VALUES ( ’DO-WB 202’, ’gelb’, 2, 2 );<br />

Versuchen wir, diesen Befehl variabel zu gestalten, sodass Mitarbeiter_ID und<br />

Kennzeichen (wie oben) aus e<strong>in</strong>er Abfrage kommen können. Dazu setzen wir<br />

260


Verwendung bei Befehlen zum Speichern<br />

diese beiden Spalten h<strong>in</strong>tere<strong>in</strong>ander und ordnen diesen das „Ergebnis als Liste<br />

mehrerer Werte“ zu unter Verwendung der LPAD-Funktion (siehe das Kapitel<br />

Funktionen (2) 3 ).<br />

Quelltext Falsch<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Mitarbeiter_ID, Farbe, Fahrzeugtyp_ID )<br />

VALUES ( ( SELECT ’DO-WB 2’ || LPAD(ID, 2, ’0’), ID<br />

FROM Mitarbeiter WHERE ID = 2 ),<br />

’gelb’, 2 );<br />

<strong>SQL</strong> error code = -804.<br />

Count of read-write columns does not equal count of values.<br />

Aha, der SELECT-Befehl wird nur als e<strong>in</strong> Wert <strong>in</strong>terpretiert, obwohl er zwei passende<br />

Werte liefert. Können wir die Abfrage an beiden Stellen verwenden?<br />

INSERT INTO Dienstwagen<br />

( Kennzeichen, Mitarbeiter_ID, Farbe, Fahrzeugtyp_ID )<br />

VALUES ( ( SELECT ’DO-WB 2’ || LPAD(ID, 2, ’0’) FROM Mitarbeiter WHERE ID = 2 ),<br />

( SELECT ID FROM Mitarbeiter WHERE ID = 2 ),<br />

’gelb’, 2 );<br />

So funktioniert es, aber es ist natürlich nicht schön, wenn e<strong>in</strong>e fast identische<br />

Abfrage doppelt auftauchen muss (auch wenn davon auszugehen ist, dass e<strong>in</strong><br />

DBMS e<strong>in</strong>en vernünftigen Ausführungsplan erstellt). Dann ist die obige Version<br />

mit INSERT INTO ... SELECT mit e<strong>in</strong>er Mischung aus Konstanten und Tabellenspalten<br />

die bessere Lösung.<br />

26.4.3. Verwendung bei UPDATE<br />

Schauen wir uns die grundsätzliche Struktur e<strong>in</strong>es UPDATE-Befehls an:<br />

UPDATE <br />

SET = [ ,<br />

= ]<br />

WHERE ;<br />

Daraus ergibt sich, dass Abfragen benutzt werden können, um e<strong>in</strong>en oder mehrere<br />

Werte zu speichern oder um Vergleichswerte für die Auswahlbed<strong>in</strong>gungen<br />

zu liefern (ebenso wie <strong>in</strong> verschiedenen früheren Beispielen).<br />

3 Abschnitt 16.2 auf Seite 144<br />

261


Unterabfragen<br />

Aufgabe: Die Mitarbeiter am Dienstort „Bochum“ erhalten e<strong>in</strong>e neue Telefonnummer,<br />

die neben der Zentrale die Abteilung und die Personalnummer enthält.<br />

• Lösung 1: Die WHERE-Bed<strong>in</strong>gung muss die betreffenden Datensätze (genauer:<br />

die IDs) der Tabelle Abteilung prüfen und vergleichen.<br />

• Lösung 2: Der zugeordnete neue Wert muss passend gestaltet werden. (LPAD<br />

und SUBSTRING werden nur verwendet, damit mit festen Längen gearbeitet<br />

werden kann.)<br />

UPDATE Mitarbeiter<br />

SET Telefon = ’0234/66’ || LPAD(Abteilung_ID, 3, ’0’)<br />

|| SUBSTRING(LPAD(Personalnummer, 6, ’0’) FROM 4 FOR 3)<br />

WHERE Abteilung_ID IN ( SELECT ID<br />

FROM Abteilung<br />

WHERE Ort = ’Bochum’ );<br />

Diese Lösung enthält nichts Neues: Die Abfrage wird mit der IN-Abfrage <strong>in</strong> die<br />

WHERE-Klausel e<strong>in</strong>gebunden; die neuen Werte werden aus den vorhandenen<br />

(Abteilung und Personalnummer) gebildet.<br />

Aufgabe: Die Abteilung „Ausbildung“ soll dorth<strong>in</strong> umziehen, wo die Abteilung<br />

„Personalverwaltung“ sitzt.<br />

• Lösung: Der gewünschte Ort wird aus e<strong>in</strong>er Abfrage geholt.<br />

UPDATE Abteilung<br />

SET Ort = ( SELECT Ort<br />

FROM Abteilung<br />

WHERE Kuerzel = ’Pers’ )<br />

WHERE Kuerzel = ’Ausb’;<br />

Bitte wundern Sie sich nicht über e<strong>in</strong> solch konstruiertes Beispiel; der neue<br />

Ort könnte natürlich im Klartext angegeben werden. Bei der v<strong>org</strong>egebenen Datenstruktur<br />

ist es manchmal schwer, s<strong>in</strong>nvolle Beispiele zu entwickeln. Wichtig<br />

ist, dass Sie die möglichen Zusammenhänge zwischen e<strong>in</strong>er Abfrage und e<strong>in</strong>em<br />

Speichern-Befehl erkennen.<br />

Man kann <strong>in</strong> der Unterabfrage auch Spalten derjenigen Tabelle, die geändert<br />

werden soll, verwenden. Beispiel (nur teilweiser Bezug auf die Beispieldatenbank):<br />

262<br />

Quelltext Falsch<br />

UPDATE Abteilung<br />

SET Ort = ( SELECT Ort<br />

FROM Adressbuch<br />

WHERE Abteilung.PLZ = Adressbuch.PLZ )<br />

;


Verwendung bei Befehlen zum Speichern<br />

Diese Abfrage führt zu folgendem Problem: Sie funktioniert nur dann, wenn es<br />

im Adressbuch exakt e<strong>in</strong>en e<strong>in</strong>zigen E<strong>in</strong>trag zu e<strong>in</strong>er bestimmten PLZ gibt. Sobald<br />

man zu e<strong>in</strong>er PLZ mehrere Adressen notiert hat, f<strong>in</strong>det die Unterabfrage<br />

mehrere Sätze; das ist bei dieser Unterabfrage nicht zulässig. Damit der Update<br />

auch <strong>in</strong> solchen Fällen funktioniert, muss e<strong>in</strong> DISTINCT e<strong>in</strong>gefügt werden, oder<br />

man verwendet die MAX- oder die MIN-Funktion:<br />

Quelltext Richtig<br />

UPDATE Abteilung<br />

SET Ort = ( SELECT MAX(Ort)<br />

FROM Adressbuch<br />

WHERE Abteilung.PLZ = Adressbuch.PLZ ) ;<br />

Bei solchen Unterabfragen mit e<strong>in</strong>em Bezug zu dem Satz, der verändert werden<br />

soll, kann es vorkommen, dass die Ausführung dieser Anweisung ziemlich lange<br />

dauert. Das liegt daran, dass alle Sätze aus der Tabelle Abteilung verändert<br />

werden sollen. Für jeden Satz muss die Unterabfrage erneut ausgeführt werden.<br />

Wenn e<strong>in</strong>e e<strong>in</strong>zelne Ausführung dieser Unterabfrage e<strong>in</strong>e Sekunde dauert, und<br />

wir haben z. B. 1000 Sätze <strong>in</strong> der Tabelle Abteilung, dann dauert die Ausführung<br />

des gesamten Statements 1000 Sekunden, also ca. 16 M<strong>in</strong>uten.<br />

Man kann auch mehrere Spalten aus Unterabfragen befüllen. Beispiel:<br />

UPDATE Abteilung<br />

SET Ort = ( SELECT MAX(Ort)<br />

FROM Telefonbuch<br />

WHERE PLZ = ’12345’ ),<br />

Leiter = ( SELECT Manager<br />

FROM Mitarbeiter<br />

WHERE Kuerzel = ’A073’ )<br />

WHERE Kuerzel = ’Ausb’;<br />

Wenn man mehrere Werte aus derselben Unterabfrage übernehmen will, dann<br />

könnte man dieselbe Unterabfrage mehrfach angeben. Aber oft kann das Datenbanksystem<br />

nicht erkennen, dass es sich immer wieder um dieselbe Unterabfrage<br />

handelt, und müsste sie mehrfach ausführen. E<strong>in</strong>facher, übersichtlicher und<br />

dann auch schneller ist die folgende Variante, die aber nicht jedes DBMS kennt.<br />

Aufgabe: In der Tabelle Abteilung werden die Spalten Ort, Leiter, Telefon geme<strong>in</strong>sam<br />

geändert.<br />

UPDATE Abteilung<br />

SET ( Ort, Leiter, Telefon )<br />

= ( SELECT Ort, Name, Telefon<br />

FROM Adressbuch<br />

WHERE Adressbuch.Personalnummer = Abteilung.Chef_Personalnummer<br />

);<br />

263


Unterabfragen<br />

Hier werden alle Sätze <strong>in</strong> der Tabelle Abteilung aktualisiert unter Verwendung der<br />

Tabelle Adressbuch; die Personalnummer des Abteilungsleiters kann wie oben<br />

bestimmt werden.<br />

26.4.4. Verwendung bei DELETE<br />

Schauen wir uns noch die grundsätzliche Struktur e<strong>in</strong>es DELETE-Befehls an:<br />

DELETE FROM <br />

[ WHERE ];<br />

Daraus ergibt sich, dass Abfragen nur für Vergleichswerte der Auswahlbed<strong>in</strong>gungen<br />

s<strong>in</strong>nvoll s<strong>in</strong>d (ebenso wie <strong>in</strong> verschiedenen früheren Beispielen).<br />

Aufgabe: Die Abteilung „Forschung und Entwicklung“ wird ausgelagert; alle<br />

zugeordneten Mitarbeiter werden <strong>in</strong> der Datenbank gelöscht.<br />

DELETE FROM Mitarbeiter<br />

WHERE Abteilung_ID <strong>in</strong> ( SELECT ID<br />

FROM Abteilung<br />

WHERE Bezeichnung = ’Forschung und Entwicklung’ ) ;<br />

26.5. Zusammenfassung<br />

In diesem Kapitel benutzten wir Unterabfragen:<br />

• Sowohl e<strong>in</strong>zelne Werte als auch Listen als Ergebnis e<strong>in</strong>er Abfrage können als<br />

Vergleichswerte <strong>in</strong> der WHERE-Klausel verwendet werden.<br />

• E<strong>in</strong>e Tabelle als Ergebnis von Abfragen kann wie jede „echte“ Tabelle als Teil<br />

der FROM- oder JOIN-Klausel verwendet werden.<br />

Ähnlich können Ergebnisse von Abfragen beim Speichern genutzt werden:<br />

• Ganze Datensätze werden mit INSERT <strong>in</strong> e<strong>in</strong>e Tabelle e<strong>in</strong>gefügt werden.<br />

• E<strong>in</strong>zelne Werte kommen <strong>in</strong> die WHERE-Klausel oder SET-Anweisung.<br />

26.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 267.<br />

264


Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Feststellungen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

Übungen<br />

1. Das Ergebnis e<strong>in</strong>er Unterabfrage kann verwendet werden, wenn es e<strong>in</strong> e<strong>in</strong>zelner<br />

Wert oder e<strong>in</strong>e Liste <strong>in</strong> Form e<strong>in</strong>er Tabelle ist. Andere Ergebnisse<br />

s<strong>in</strong>d nicht möglich.<br />

2. E<strong>in</strong> e<strong>in</strong>zelner Wert als Ergebnis kann durch e<strong>in</strong>e direkte Abfrage oder durch<br />

e<strong>in</strong>e Spaltenfunktion erhalten werden.<br />

3. Unterabfragen sollten nicht verwendet werden, wenn die WHERE-<br />

Bed<strong>in</strong>gung für jede Zeile der Hauptabfrage e<strong>in</strong>en anderen Wert erhält und<br />

deshalb die Unterabfrage neu ausgeführt werden muss.<br />

4. Mehrere Unterabfragen können verschachtelt werden.<br />

5. Für die Arbeitsgeschw<strong>in</strong>digkeit ist es gleichgültig, ob mehrere Unterabfragen<br />

oder JOINs verwendet werden.<br />

6. E<strong>in</strong>e Unterabfrage mit e<strong>in</strong>er Tabelle als Ergebnis kann GROUP BY nicht<br />

s<strong>in</strong>nvoll nutzen.<br />

7. E<strong>in</strong>e Unterabfrage mit e<strong>in</strong>er Tabelle als Ergebnis kann ORDER BY nicht<br />

s<strong>in</strong>nvoll nutzen.<br />

8. Bei e<strong>in</strong>er Unterabfrage mit e<strong>in</strong>er Tabelle als Ergebnis ist e<strong>in</strong> Alias-Name für<br />

die Tabelle s<strong>in</strong>nvoll, aber nicht notwendig.<br />

9. Bei e<strong>in</strong>er Unterabfrage mit e<strong>in</strong>er Tabelle als Ergebnis s<strong>in</strong>d Alias-Namen für<br />

die Spalten s<strong>in</strong>nvoll, aber nicht notwendig.<br />

Übung 2 – E<strong>in</strong> e<strong>in</strong>zelner Wert<br />

Welche Verträge (mit e<strong>in</strong>igen Angaben) hat der Mitarbeiter „Braun, Christian“<br />

abgeschlossen? Ignorieren Sie die Möglichkeit, dass es mehrere Mitarbeiter dieses<br />

Namens geben könnte.<br />

Übung 3 – E<strong>in</strong> e<strong>in</strong>zelner Wert<br />

Zeigen Sie alle Verträge, die zum Kunden „Heckel Obsthandel GmbH“ gehören.<br />

Ignorieren Sie die Möglichkeit, dass der Kunde mehrfach gespeichert se<strong>in</strong> könnte.<br />

Übung 4 – E<strong>in</strong>e Liste von Werten<br />

Ändern Sie die Lösung von Übung 3, sodass auch mehrere Kunden mit diesem<br />

Namen als Ergebnis denkbar s<strong>in</strong>d.<br />

265


Unterabfragen<br />

Übung 5 – E<strong>in</strong>e Liste von Werten<br />

Zeigen Sie alle Fahrzeuge, die im Jahr 2008 an e<strong>in</strong>em Schadensfall beteiligt<br />

waren.<br />

Übung 6 – E<strong>in</strong>e Liste von Werten<br />

Zeigen Sie alle Fahrzeugtypen (mit ID, Bezeichnung und Name des Herstellers),<br />

die im Jahr 2008 an e<strong>in</strong>em Schadensfall beteiligt waren.<br />

Übung 7 – E<strong>in</strong>e Tabelle als Ergebnis<br />

Bestimmen Sie alle Fahrzeuge e<strong>in</strong>es bestimmten Herstellers mit Angabe des<br />

Typs.<br />

H<strong>in</strong>weis: Es handelt sich um das letzte Beispiel aus dem Abschnitt „Ergebnis <strong>in</strong><br />

Form e<strong>in</strong>er Tabelle“. Benutzen Sie jetzt JOIN.<br />

Übung 8 – E<strong>in</strong>e Tabelle als Ergebnis<br />

Zeigen Sie zu jedem Mitarbeiter der Abteilung „Vertrieb“ den ersten Vertrag (mit<br />

e<strong>in</strong>igen Angaben) an, den er abgeschlossen hat. Der Mitarbeiter soll mit ID und<br />

Name/Vorname angezeigt werden.<br />

Übung 9 – Speichern mit Unterabfrage<br />

Von der Deutschen Post AG wird e<strong>in</strong>e Tabelle PLZ_Aenderung mit folgenden<br />

Inhalten geliefert:<br />

ID PLZalt Ortalt PLZneu Ortneu<br />

1 45658 Reckl<strong>in</strong>ghausen 45659 Reckl<strong>in</strong>ghausen<br />

2 45721 Hamm-Bossendorf 45721 Haltern OT Hamm<br />

3 45772 Marl 45770 Marl<br />

4 45701 Herten 45699 Herten<br />

Ändern Sie die Tabelle Versicherungsnehmer so, dass bei allen Adressen, bei<br />

denen PLZ/Ort mit PLZalt/Ortalt übere<strong>in</strong>stimmen, diese Angaben durch PLZneu/Ortneu<br />

geändert werden.<br />

H<strong>in</strong>weise: Beschränken Sie sich auf die Änderung mit der ID=3. (Die vollständige<br />

Lösung ist erst mit <strong>SQL</strong>-Programmierung 4 möglich.) Bei dieser Änderungsdatei<br />

handelt es sich nur um fiktive Daten, ke<strong>in</strong>e echten Änderungen.<br />

4 Kapitel 30 auf Seite 323<br />

266


Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 264.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Richtig s<strong>in</strong>d 2, 3, 4, 7, 9; falsch s<strong>in</strong>d 1, 5, 6, 8.<br />

Lösung zu Übung 2 – E<strong>in</strong> e<strong>in</strong>zelner Wert<br />

SELECT ID, Vertragsnummer, Abschlussdatum, Art<br />

FROM Versicherungsvertrag<br />

WHERE Mitarbeiter_ID<br />

IN ( SELECT ID<br />

FROM Mitarbeiter<br />

WHERE Name = ’Braun’<br />

AND Vorname = ’Christian’ );<br />

Lösung zu Übung 3 – E<strong>in</strong> e<strong>in</strong>zelner Wert<br />

SELECT ID, Vertragsnummer, Abschlussdatum, Art<br />

FROM Versicherungsvertrag<br />

WHERE Versicherungsnehmer_ID<br />

= ( SELECT ID FROM Versicherungsnehmer<br />

WHERE Name =’Heckel Obsthandel GmbH’ );<br />

Lösung zu Übung 4 – E<strong>in</strong>e Liste von Werten<br />

SELECT ID, Vertragsnummer, Abschlussdatum, Art<br />

FROM Versicherungsvertrag<br />

WHERE Versicherungsnehmer_ID<br />

IN ( SELECT ID FROM Versicherungsnehmer<br />

WHERE Name =’Heckel Obsthandel GmbH’ );<br />

Lösung zu Übung 5 – E<strong>in</strong>e Liste von Werten<br />

SELECT ID, Kennzeichen, Fahrzeugtyp_ID AS TypID<br />

FROM Fahrzeug fz<br />

WHERE ID IN ( SELECT Fahrzeug_ID<br />

FROM Zuordnung_sf_fz zu<br />

JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

WHERE EXTRACT(YEAR FROM sf.Datum) = 2008 );<br />

Übungen<br />

267


Unterabfragen<br />

Lösung zu Übung 6 – E<strong>in</strong>e Liste von Werten<br />

SELECT dist<strong>in</strong>ct ft.ID AS TypID, ft.Bezeichnung AS Typ, fh.Name AS Hersteller<br />

FROM Fahrzeugtyp ft<br />

INNER JOIN Fahrzeughersteller fh ON fh.ID = ft.Hersteller_ID<br />

RIGHT JOIN Fahrzeug fz ON ft.ID = fz.Fahrzeugtyp_ID<br />

WHERE fz.ID IN ( SELECT Fahrzeug_ID<br />

FROM Zuordnung_sf_fz zu<br />

JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

WHERE EXTRACT(YEAR FROM sf.Datum) = 2008 );<br />

Beachten Sie vor allem, dass die WHERE-Bed<strong>in</strong>gung übernommen werden<br />

konnte, aber die Tabellen anders zu verknüpfen s<strong>in</strong>d. Die Bed<strong>in</strong>gung könnte <strong>in</strong><br />

die ON-Klausel e<strong>in</strong>bezogen werden; da sie aber die Auswahl beschränken soll, ist<br />

die WHERE-Klausel vorzuziehen.<br />

Lösung zu Übung 7 – E<strong>in</strong>e Tabelle als Ergebnis<br />

SELECT fz.ID, fz.Kennzeichen, Typen.ID AS TYP, Typen.Bezeichnung<br />

FROM Fahrzeug fz<br />

JOIN ( SELECT ID, Bezeichnung<br />

FROM Fahrzeugtyp<br />

WHERE Hersteller_ID =<br />

( SELECT ID FROM Fahrzeughersteller<br />

WHERE Name = ’Volkswagen’ )<br />

) Typen ON fz.Fahrzeugtyp_ID = Typen.ID;<br />

Lösung zu Übung 8 – E<strong>in</strong>e Tabelle als Ergebnis<br />

SELECT vv.ID AS VV, vv.Vertragsnummer, vv.Abschlussdatum, vv.Art,<br />

mi.ID AS MI, mi.Name, mi.Vorname<br />

FROM Versicherungsvertrag vv<br />

RIGHT JOIN ( SELECT MIN(vv2.ID) AS ID, vv2.Mitarbeiter_ID<br />

FROM Versicherungsvertrag vv2<br />

GROUP BY vv2.Mitarbeiter_id ) Temp<br />

ON Temp.ID = vv.ID<br />

RIGHT JOIN Mitarbeiter mi ON mi.ID = vv.Mitarbeiter_ID<br />

WHERE mi.Abteilung_ID = ( SELECT ID FROM Abteilung<br />

WHERE Bezeichnung = ’Vertrieb’ );<br />

Erläuterungen: Wir benötigen e<strong>in</strong>e e<strong>in</strong>fache Unterabfrage, um die Liste der Mitarbeiter<br />

für „Vertrieb“ zu erhalten, und wir benötigen e<strong>in</strong>e Unterabfrage, die uns<br />

zur Mitarbeiter-ID die kle<strong>in</strong>ste Vertrags-ID liefert. Wegen der Aufgabenstellung<br />

„zu jedem Mitarbeiter“ sowie „mit e<strong>in</strong>igen Angaben“ muss es sich bei beiden<br />

Verknüpfungen um e<strong>in</strong>en RIGHT JOIN handeln.<br />

268


Lösung zu Übung 9 – Speichern mit Unterabfrage<br />

UPDATE Versicherungsnehmer<br />

SET PLZ, Ort<br />

= ( SELECT PLZneu, Ortneu<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 )<br />

WHERE PLZ = ( SELECT PLZalt<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 )<br />

AND Ort = ( SELECT Ortalt<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 );<br />

Übungen<br />

Vielleicht funktioniert diese Variante bei Ihrem DBMS nicht; dann ist die folgende<br />

Version nötig:<br />

UPDATE Versicherungsnehmer<br />

SET PLZ = ( SELECT PLZneu<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 ),<br />

Ort = ( SELECT Ortneu<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 )<br />

WHERE PLZ = ( SELECT PLZalt<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 )<br />

AND Ort = ( SELECT Ortalt<br />

FROM PLZ_Aenderg<br />

WHERE ID = 3 );<br />

269


27. Erstellen von Views<br />

27.1. E<strong>in</strong>e View anlegen und benutzen . . . . . . . . . . . . . . . . . . 272<br />

27.2. E<strong>in</strong>e View ändern oder löschen . . . . . . . . . . . . . . . . . . . 276<br />

27.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 277<br />

27.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277<br />

27.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282<br />

VIEWs s<strong>in</strong>d Abfragen, die <strong>in</strong> der Datenbank als Objekt fest gespeichert s<strong>in</strong>d.<br />

Sie können als virtuelle Tabellen verstanden werden, deren Inhalt und Struktur<br />

auf anderen Tabellen oder Views basieren, und können <strong>in</strong> (fast) jedem SELECT-<br />

Befehl anstelle e<strong>in</strong>er „echten“ Tabelle verwendet werden.<br />

Bei e<strong>in</strong>er View wird die Abfrage <strong>in</strong> der Datenbank gespeichert, aber nicht das Ergebnis.<br />

Bei jedem neuen Aufruf der View wird die dah<strong>in</strong>terliegende Abfrage neu<br />

ausgeführt, denn sie soll ja das Ergebnis anhand der aktuellen Daten bestimmen.<br />

Die Abfragen, auf denen Views basieren, können grundsätzlich alle Klauseln wie<br />

e<strong>in</strong>e normale Abfrage enthalten. Somit ist es möglich, bestimmte Daten <strong>in</strong> e<strong>in</strong>er<br />

View zu selektieren und zu gruppieren. Hierbei können die Daten aus mehreren<br />

Tabellen oder Views selektiert werden.<br />

Je nach DBMS und Situation kann e<strong>in</strong>e e<strong>in</strong>zelne Klausel der View unwirksam<br />

se<strong>in</strong> oder zu unklaren Ergebnissen führen.<br />

• E<strong>in</strong>e ORDER BY-Klausel der View wird ignoriert, wenn der SELECT-Befehl, der<br />

sie benutzt, selbst e<strong>in</strong>e Sortierung verwendet.<br />

• Bei e<strong>in</strong>er E<strong>in</strong>schränkung durch LIMIT o. ä. weiß das DBMS oft nicht, nach welchen<br />

Regeln diese E<strong>in</strong>schränkung verwirklicht werden soll.<br />

• WHERE-Bed<strong>in</strong>gungen können nur fest e<strong>in</strong>gebaut werden, aber nicht mit variablen<br />

Parametern.<br />

Mit Views wird die stark differenzierte Struktur e<strong>in</strong>es Auswahlbefehls vere<strong>in</strong>facht.<br />

Die View wird mit ihrer komplexen Abfrage e<strong>in</strong>mal angelegt, und die Nutzer<br />

können die Daten dieser View immer wieder abfragen.<br />

Weiterh<strong>in</strong> s<strong>in</strong>d Views geeignet, um den Zugriff auf bestimmte Daten e<strong>in</strong>zuschränken.<br />

Nutzer können Zugriff nur auf bestimmte Views bekommen. Somit<br />

lässt sich der Zugriff für e<strong>in</strong>zelne Nutzer auf bestimmte Daten (Spalten und Datensätze)<br />

beschränken.<br />

271


Erstellen von Views<br />

27.1. E<strong>in</strong>e View anlegen und benutzen<br />

Views werden mit dem Befehl CREATE VIEW mit folgender Syntax angelegt.<br />

CREATE VIEW <br />

[ ( ) ]<br />

AS ;<br />

Zu dieser Def<strong>in</strong>ition gehören folgende Bestandteile:<br />

• CREATE VIEW kennzeichnet den Befehl.<br />

• Unter ist e<strong>in</strong>e Bezeichnung anzugeben, unter der die View <strong>in</strong><br />

e<strong>in</strong>em SELECT-Befehl angesprochen wird. Dieser Name muss e<strong>in</strong>deutig se<strong>in</strong><br />

und darf auch ke<strong>in</strong> Name e<strong>in</strong>er „echten“ Tabelle se<strong>in</strong>.<br />

• Als wird e<strong>in</strong> (beliebiger) SELECT-Befehl e<strong>in</strong>getragen.<br />

• Es wird empfohlen, möglichst bei allen Spalten mit e<strong>in</strong>em Alias zu arbeiten.<br />

• Diese können wahlweise vor dem AS <strong>in</strong> Klammern angegeben werden oder<br />

(wie üblich) Teil des s se<strong>in</strong>.<br />

Die View wird dann wie jede Tabelle benutzt, z. B. e<strong>in</strong>fach:<br />

SELECT * FROM <br />

Oder auch als Teil e<strong>in</strong>er komplexen Abfrage:<br />

SELECT <br />

FROM <br />

JOIN ON /* usw. */<br />

27.1.1. E<strong>in</strong>e e<strong>in</strong>fache View<br />

Im e<strong>in</strong>fachsten Fall greifen wir auf e<strong>in</strong>e e<strong>in</strong>fache Verknüpfung zweier Tabellen zu<br />

und verb<strong>in</strong>den dies mit e<strong>in</strong>er festen Auswahlbed<strong>in</strong>gung.<br />

Aufgabe: Erstelle e<strong>in</strong>e View, die e<strong>in</strong>e Liste aller Fahrzeugtypen deutscher Hersteller<br />

anzeigt.<br />

272<br />

CREATE VIEW Deutscher_Fahrzeugtyp<br />

AS SELECT DISTINCT ft.Bezeichnung AS Fahrzeugtyp, fh.Name AS Hersteller<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh ON ft.Hersteller_ID = fh.ID<br />

WHERE fh.Land = ’Deutschland’;


E<strong>in</strong>e View anlegen und benutzen<br />

Die Abfrage basiert auf den beiden Tabellen Fahrzeugtyp und Fahrzeughersteller.<br />

Es werden nur die Spalten Bezeichnung und Name abgefragt; durch die WHERE-<br />

Klausel wird das Ergebnis auf Fahrzeuge deutscher Hersteller beschränkt. Für die<br />

Spalten werden Spalten-Aliase genutzt.<br />

Diese View wird dann wie e<strong>in</strong>e „normale“ Tabellen <strong>in</strong> Abfragen genutzt.<br />

SELECT * FROM Deutscher_Fahrzeugtyp ORDER BY Hersteller;<br />

FAHRZEUGTYP HERSTELLER<br />

A3 Audi<br />

A4 Audi<br />

325 BMW<br />

525 BMW<br />

Z3 BMW<br />

Fiesta Ford<br />

Focus Ford /* usw. */<br />

In diesem Fall kann die ORDER BY-Klausel ebensogut Teil der View se<strong>in</strong>; das untersuchen<br />

wir später noch.<br />

27.1.2. E<strong>in</strong>e View mit variabler Selektion<br />

Es klappt leider nicht, <strong>in</strong> die WHERE-Klausel e<strong>in</strong>en (variablen) Parameter e<strong>in</strong>zubauen,<br />

der erst beim Aufruf mit e<strong>in</strong>em konkreten Wert versehen wird.<br />

Aufgabe: Gesucht wird e<strong>in</strong>e Abfrage über die Mitarbeiter e<strong>in</strong>er Abteilung; am<br />

Anfang soll der Abteilungsleiter stehen, danach alphabetisch die betreffenden<br />

Mitarbeiter. Die Nummer der Abteilung soll nicht fest v<strong>org</strong>egeben werden, sondern<br />

variabel se<strong>in</strong>.<br />

Quelltext Falsch<br />

CREATE VIEW Mitarbeiter_<strong>in</strong>_Abteilung<br />

AS SELECT Personalnummer, Name, Vorname, Geburtsdatum<br />

FROM Mitarbeiter<br />

WHERE Abteilung_ID = ?<br />

ORDER BY Ist_Leiter, Name, Vorname;<br />

Commit nicht möglich<br />

Invalid token.<br />

Auch Alternativen für das Fragezeichen führen nicht zum Ziel. Es bleibt nur e<strong>in</strong><br />

kle<strong>in</strong>er Umweg, nämlich die Abteilung_ID <strong>in</strong> der View zu berücksichtigen und<br />

später für WHERE zu nutzen:<br />

273


Erstellen von Views<br />

CREATE VIEW Mitarbeiter_<strong>in</strong>_Abteilung<br />

( Pers, Name, Vorname, Geburtsdatum, Abt )<br />

AS SELECT Personalnummer, Name, Vorname, Geburtsdatum, Abteilung_ID<br />

FROM Mitarbeiter<br />

ORDER BY Ist_Leiter, Name, Vorname;<br />

Damit können alle Angaben e<strong>in</strong>er bestimmten Abteilung geholt werden; die<br />

Spalte Abt bleibt zur Verdeutlichung stehen:<br />

SELECT * FROM Mitarbeiter_<strong>in</strong>_Abteilung<br />

WHERE Abt = 5;<br />

PERS NAME VORNAME GEBURTSDATUM ABT<br />

50001 Pohl Helmut 27.10.1980 5<br />

50002 Braun Christian 05.09.1966 5<br />

50004 Kalman Ayd<strong>in</strong> 17.12.1976 5<br />

50003 Polovic Frantisek 26.11.1961 5<br />

Und siehe da: zuerst kommt der Abteilungsleiter, danach die anderen Mitarbeiter<br />

<strong>in</strong> alphabetischer Reihenfolge.<br />

H<strong>in</strong>weis: E<strong>in</strong>e Alternative zu e<strong>in</strong>er VIEW mit variabler WHERE-Bed<strong>in</strong>gung ist<br />

e<strong>in</strong>e „StoredProcedure“, die diese Abfrage enthält und e<strong>in</strong>en Wert als Parameter<br />

entgegennimmt; sie wird <strong>in</strong> e<strong>in</strong>em späteren Kapitel behandelt.<br />

27.1.3. Probleme mit der Sortierung<br />

Ändern wir die obige View deutscher Fahrzeuge dah<strong>in</strong>, dass die Sortierung nach<br />

Hersteller fest e<strong>in</strong>gebaut wird.<br />

• Bitte beachten Sie: Wenn Sie oben die View Deutscher_Fahrzeugtyp fest gespeichert<br />

haben, müssen Sie <strong>in</strong> diesem Abschnitt e<strong>in</strong>en anderen Namen verwenden<br />

oder stattdessen etwas wie CREATE OR ALTER (siehe die DBMS-<br />

Dokumentation) benutzen.<br />

CREATE VIEW Deutscher_Fahrzeugtyp ( Typ, Firma )<br />

AS SELECT DISTINCT ft.Bezeichnung, fh.Name AS Firma<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh ON ft.Hersteller_ID = fh.ID<br />

WHERE fh.Land = ’Deutschland’<br />

ORDER BY Firma;<br />

Bitte beachten Sie, dass <strong>in</strong> diesem Fall der Spalten-Alias Firma auch Teil des<br />

SELECT-Befehls se<strong>in</strong> muss, damit er <strong>in</strong> der ORDER BY-Klausel bekannt ist.<br />

Jetzt wird die Liste wahlweise mit oder ohne Sortierung abgerufen:<br />

274


E<strong>in</strong>e View anlegen und benutzen<br />

SELECT * FROM Deutscher_Fahrzeugtyp; -- automatisch sortiert nach Firma<br />

SELECT * FROM Deutscher_Fahrzeugtyp ORDER BY Typ; -- speziell sortiert nach Typ<br />

27.1.4. Views <strong>in</strong> Verb<strong>in</strong>dung mit JOIN<br />

Die obige Verknüpfung „Fahrzeugtyp plus Hersteller“ benötigen wir <strong>in</strong> der Praxis<br />

ständig, nicht nur <strong>in</strong> der konkreten Abfrage nach deutschen Herstellern. Bisher<br />

− zum Beispiel mit OUTER JOIN − haben wir beide Tabellen separat per JOIN<br />

e<strong>in</strong>gebunden, mussten aber immer auf die Art des JOINs aufpassen. Das kann<br />

man e<strong>in</strong>malig durch e<strong>in</strong>e abgeleitete Tabelle Fahrzeugart, also e<strong>in</strong>e VIEW mit<br />

den benötigten Informationen steuern.<br />

E<strong>in</strong>e solche VIEW erfüllt mehrere Wünsche:<br />

• Die eigentlichen Informationen werden getrennt gespeichert; es ist nicht nötig,<br />

bei jedem Fahrzeugtyp den Hersteller und se<strong>in</strong> Herkunftsland aufzuführen.<br />

Wie wir aus der Wirtschaftspolitik des Jahres 2009 wissen, kann sich e<strong>in</strong><br />

Herkunftsland durchaus ändern; nach den Regeln der Normalisierung ist die<br />

separate Tabelle der Hersteller nicht nur s<strong>in</strong>nvoll, sondern notwendig.<br />

• Bei jeder Abfrage des Fahrzeugtyps erhalten wir sofort auch den Hersteller.<br />

• Jede Abfrage damit wird e<strong>in</strong>facher, weil e<strong>in</strong>e Tabelle weniger benötigt wird.<br />

• Das DBMS kennt se<strong>in</strong>e VIEWs und hat sie „von Haus aus“ optimiert; also wird<br />

jede solche Abfrage auch schneller ausgeführt.<br />

Diese Aussage gilt nicht unbed<strong>in</strong>gt bei jeder Abfrage und jedem<br />

DBMS. Aber nach allen Erkenntnissen über <strong>in</strong>terne Datenbankstrukturen<br />

kann man davon ausgehen.<br />

Das obige „e<strong>in</strong>fache Beispiel“ der VIEW müssen wir nur wenig umschreiben:<br />

Aufgabe: Bereite e<strong>in</strong>e (fiktive) Tabelle Fahrzeugart vor mit allen relevanten Informationen<br />

aus den Tabellen Fahrzeugtyp und Fahrzeughersteller.<br />

CREATE VIEW Fahrzeugart<br />

( ID, Bezeichnung, Hersteller, Land )<br />

AS SELECT ft.ID, ft.Bezeichnung, fh.Name, fh.Land<br />

FROM Fahrzeugtyp ft<br />

JOIN Fahrzeughersteller fh ON ft.Hersteller_ID = fh.ID;<br />

Für den Anwender sieht es tatsächlich so aus, als hätten wir e<strong>in</strong>e e<strong>in</strong>fache Tabelle<br />

mit allen Angaben:<br />

SELECT * FROM Fahrzeugart<br />

ORDER BY Land, Hersteller, Bezeichnung;<br />

275


Erstellen von Views<br />

ID BEZEICHNUNG HERSTELLER LAND<br />

19 C30 Volvo<br />

18 S40 Volvo<br />

12 A3 Audi Deutschland<br />

13 A4 Audi Deutschland<br />

9 325 BMW Deutschland<br />

10 525 BMW Deutschland<br />

11 Z3 BMW Deutschland<br />

Damit kann das letzte der Beispiele zu OUTER JOIN vere<strong>in</strong>facht werden.<br />

Aufgabe: Hole alle Dienstwagen (ggf. mit den zugehörigen Mitarbeitern) und<br />

nenne dazu alle Fahrzeugdaten.<br />

SELECT<br />

mi.Personalnummer AS MitNr,<br />

mi.Name, mi.Vorname,<br />

dw.ID AS DIW, dw.Kennzeichen, dw.Fahrzeugtyp_ID AS TypID,<br />

fa.Bezeichnung AS Typ, fa.Hersteller<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.ID = dw.Mitarbeiter_ID<br />

INNER JOIN Fahrzeugart fa ON fa.ID = dw.Fahrzeugtyp_ID;<br />

MITNR NAME VORNAME DIW KENNZEICHEN TYPID TYP HERSTELLER<br />

80001 Sch<strong>in</strong>dler Christ<strong>in</strong>a 8 DO-WB 428 14 A160 Mercedes-Benz<br />

90001 Janssen Bernhard 9 DO-WB 429 14 A160 Mercedes-Benz<br />

100001 Grosser Horst 10 DO-WB 4210 14 A160 Mercedes-Benz<br />

110001 Eggert Louis 11 DO-WB 4211 14 A160 Mercedes-Benz<br />

120001 Carlsen Zacharias 12 DO-WB 4212 14 A160 Mercedes-Benz<br />

13 DO-WB 111 16 W211 (E-Klasse) Mercedes-Benz<br />

50002 Braun Christian 14 DO-WB 352 2 Golf Volkswagen<br />

50003 Polovic Frantisek 15 DO-WB 353 3 Passat Volkswagen<br />

50004 Kalman Ayd<strong>in</strong> 16 DO-WB 354 4 Kadett Opel<br />

80002 Aliman Zafer 17 DO-WB 382 2 Golf Volkswagen<br />

80003 Langer Norbert 18 DO-WB 383 3 Passat Volkswagen<br />

80004 Kolic Ivana 19 DO-WB 384 4 Kadett Opel<br />

E<strong>in</strong>ige kle<strong>in</strong>e Änderungen vere<strong>in</strong>fachen alles: Die Tabelle Fahrzeugtyp wird<br />

durch die View Fahrzeugart ersetzt; der JOIN auf Fahrzeughersteller entfällt ersatzlos.<br />

Lediglich zur Klarheit ändern wir Tabellen-Alias und Spaltennamen.<br />

27.2. E<strong>in</strong>e View ändern oder löschen<br />

Die Änderung e<strong>in</strong>er VIEW wird unterschiedlich gehandhabt.<br />

• Üblich ist das „normale“ ALTER VIEW.<br />

276


Zusammenfassung<br />

• Firebird behandelt e<strong>in</strong>e Änderung mit RECREATE als Löschung und anschließende<br />

Neuaufnahme.<br />

Die Löschung e<strong>in</strong>er View erfolgt mit dem üblichen Befehl DROP VIEW.<br />

DROP VIEW Deutscher_Fahrzeugtyp;<br />

Hierbei wird nur die View als Objekt <strong>in</strong> der Datenbank gelöscht. Die Tabellen<br />

und Daten <strong>in</strong> den Tabellen, auf denen die View basiert, werden davon nicht bee<strong>in</strong>flusst<br />

– sie werden nicht gelöscht.<br />

27.3. Zusammenfassung<br />

• Views s<strong>in</strong>d Abfragen, die <strong>in</strong> der Datenbank als Objekt gespeichert werden.<br />

• Views können die Komplexität für den Anwender reduzieren.<br />

• Views können für e<strong>in</strong>e detaillierte Zugriffskontrolle genutzt werden.<br />

• Views werden <strong>in</strong> Abfragen wie jede Tabelle benutzt.<br />

• Sie werden mit CREATE VIEW erstellt und mit DROP VIEW gelöscht.<br />

27.4. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 279.<br />

Die Formulierung „e<strong>in</strong>e View kontrollieren“ me<strong>in</strong>t: Mit e<strong>in</strong>er geeigneten Abfrage<br />

soll überprüft werden, ob die View richtig erstellt worden ist.<br />

Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Feststellungen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

1. E<strong>in</strong>e View ist wie e<strong>in</strong>e „normale“ Abfrage, deren Bestandteile <strong>in</strong> der Datenbank<br />

fest gespeichert werden.<br />

2. Das Ergebnis dieser Abfrage wird gleichzeitig gespeichert und steht damit<br />

beim nächsten Aufruf der View sofort zur Verfügung.<br />

3. E<strong>in</strong>e ORDER BY-Klausel kann <strong>in</strong> e<strong>in</strong>er View immer benutzt werden.<br />

4. E<strong>in</strong>e ORDER BY-Klausel ist <strong>in</strong> e<strong>in</strong>er View nicht erforderlich.<br />

5. Wenn diese Klausel <strong>in</strong> e<strong>in</strong>er View benutzt wird, hat diese Sortierung Vorrang<br />

vor e<strong>in</strong>er ORDER BY-Klausel <strong>in</strong> dem SELECT-Befehl, der die View benutzt.<br />

6. Wenn e<strong>in</strong> SELECT-Befehl komplexe JOINs oder andere Klauseln benutzt<br />

und häufiger benutzt wird, ist es s<strong>in</strong>nvoll, ihn <strong>in</strong> e<strong>in</strong>er View zu kapseln.<br />

277


Erstellen von Views<br />

7. Wenn e<strong>in</strong> Anwender nicht alle Daten sehen darf, ist es notwendig, die<br />

Zugriffsrechte auf die Spalten zu beschränken; diese Beschränkung kann<br />

nicht über e<strong>in</strong>e View gesteuert werden.<br />

8. E<strong>in</strong>e View kann <strong>in</strong> e<strong>in</strong>em SELECT-Befehl <strong>in</strong> der FROM-Klausel anstatt e<strong>in</strong>er<br />

Tabelle aufgerufen werden.<br />

9. E<strong>in</strong>e View kann nicht <strong>in</strong> e<strong>in</strong>em JOIN benutzt werden.<br />

Übung 2 – E<strong>in</strong>e View benutzen<br />

Skizzieren Sie e<strong>in</strong>e Abfrage, durch die e<strong>in</strong>e beliebige View benutzt werden kann.<br />

Übung 3 – E<strong>in</strong>e e<strong>in</strong>fache View erstellen<br />

Bei der Suche nach Dienstwagen sollen mit der View Dienstwagen_Anzeige immer<br />

auch angezeigt werden:<br />

• Name und Vorname des Mitarbeiters<br />

• ID und Bezeichnung se<strong>in</strong>er Abteilung<br />

• der Fahrzeugtyp (nur als ID)<br />

Stellen Sie sicher, dass auch nicht-persönliche Dienstwagen immer angezeigt<br />

werden, und kontrollieren Sie das Ergebnis durch e<strong>in</strong>e Abfrage ähnlich diesem<br />

Muster:<br />

SELECT * FROM Dienstwagen_Anzeige<br />

WHERE ( Abt_ID BETWEEN 5 AND 8 ) or ( Mi_Name is null );<br />

Übung 4 – Mehrere Tabellen und Views verb<strong>in</strong>den<br />

Erweitern Sie die vorstehende View so, dass mit Hilfe der View Fahrzeugart auch<br />

Bezeichnung, Hersteller und Land angezeigt werden. Kontrollieren Sie das Ergebnis<br />

durch die o. g. Abfrage.<br />

Dies ist e<strong>in</strong> Beispiel dafür, dass e<strong>in</strong>e View bei Abfragen genauso wie e<strong>in</strong>e „echte“<br />

Tabelle benutzt werden kann.<br />

Übung 5 – E<strong>in</strong>e VIEW auf mehrere Tabellen<br />

Erstellen Sie e<strong>in</strong>e Sicht Vertrag_Anzeige, die zu jedem Vertrag anzeigt:<br />

• ID, Vertragsnummer, Abschlussdatum, Art (als Text)<br />

• Name, Vorname des Mitarbeiters<br />

• Name, Vorname des Versicherungsnehmers<br />

• Kennzeichen des Fahrzeugs<br />

278


Übung 6 – E<strong>in</strong>e VIEW auf mehrere Tabellen<br />

Übungen<br />

Erweitern Sie die vorstehende View so, dass mit Hilfe der View Fahrzeugart auch<br />

Bezeichnung, Hersteller und Land angezeigt werden.<br />

Übung 7 – E<strong>in</strong>e View abschnittsweise kontrollieren<br />

Erstellen Sie e<strong>in</strong>e Abfrage, sodass für e<strong>in</strong>en Teil der Verträge die vorstehende<br />

View kontrolliert wird.<br />

Übung 8 – E<strong>in</strong>e weitere VIEW auf mehrere Tabellen<br />

Erstellen Sie e<strong>in</strong>e Sicht Schaden_Anzeige, bei der zu jedem an e<strong>in</strong>em Schadensfall<br />

beteiligten Fahrzeug angezeigt werden:<br />

• ID, Datum, Gesamthöhe e<strong>in</strong>es Schadensfalls<br />

• Kennzeichen und Typ des beteiligten Fahrzeugs<br />

• Anteiliger Schaden<br />

• ID des Versicherungsvertrags<br />

Übung 9 – E<strong>in</strong>e weitere VIEW auf mehrere Tabellen<br />

Erweitern Sie die vorstehende View so, dass mit Hilfe der View Fahrzeugart auch<br />

Bezeichnung, Hersteller und Land sowie Vertragsnummer und ID des Versicherungsnehmers<br />

angezeigt werden.<br />

Übung 10 – E<strong>in</strong>e View zur Auswertung e<strong>in</strong>er View<br />

Erstellen Sie e<strong>in</strong>e weitere View so, dass die vorstehende View für alle Schadensfälle<br />

des aktuellen Jahres benutzt wird.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 277.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Richtig s<strong>in</strong>d die Aussagen 1, 3, 4, 6, 8. Falsch s<strong>in</strong>d die Aussagen 2, 5, 7, 9.<br />

Lösung zu Übung 2 – E<strong>in</strong>e View benutzen<br />

SELECT * FROM ;<br />

279


Erstellen von Views<br />

Lösung zu Übung 3 – E<strong>in</strong>e View erstellen<br />

CREATE VIEW Dienstwagen_Anzeige<br />

( Kennzeichen, TypId,<br />

Mi_Name, Mi_Vorname,<br />

Ab_ID, Ab_Name )<br />

AS SELECT dw.Kennzeichen, dw.Fahrzeugtyp_ID,<br />

mi.Name, mi.Vorname,<br />

mi.Abteilung_ID,<br />

ab.Bezeichnung<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.ID = dw.Mitarbeiter_ID<br />

LEFT JOIN Abteilung ab ON ab.ID = mi.Abteilung_ID;<br />

Erläuterung: LEFT JOIN <strong>in</strong> beiden Fällen wird benötigt, damit auch NULL-Werte,<br />

nämlich die nicht-persönlichen Dienstwagen angezeigt werden.<br />

Lösung zu Übung 4 – Mehrere Tabellen und Views verb<strong>in</strong>den<br />

ALTER VIEW Dienstwagen_Anzeige<br />

( Kennzeichen, TypId,<br />

Typ, Fz_Hersteller, Fz_Land,<br />

Mi_Name, Mi_Vorname,<br />

Ab_ID, Ab_Name )<br />

AS SELECT dw.Kennzeichen, dw.Fahrzeugtyp_ID,<br />

fa.Bezeichnung, fa.Hersteller, fa.Land,<br />

mi.Name, mi.Vorname,<br />

mi.Abteilung_ID,<br />

ab.Bezeichnung<br />

FROM Dienstwagen dw<br />

LEFT JOIN Mitarbeiter mi ON mi.ID = dw.Mitarbeiter_ID<br />

LEFT JOIN Abteilung ab ON ab.ID = mi.Abteilung_ID<br />

INNER JOIN Fahrzeugart fa ON fa.ID = dw.Fahrzeugtyp_ID;<br />

Lösung zu Übung 5 – E<strong>in</strong>e VIEW auf mehrere Tabellen<br />

280<br />

CREATE VIEW Vertrag_Anzeige<br />

( ID, Vertragsnummer, Abschlussdatum, Art,<br />

Mi_Name, Mi_Vorname,<br />

Vn_Name, Vn_Vorname,<br />

Kennzeichen )<br />

AS SELECT vv.ID, vv.Vertragsnummer, vv.Abschlussdatum,<br />

CASE vv.Art<br />

WHEN ’TK’ THEN ’Teilkasko’<br />

WHEN ’VK’ THEN ’Vollkasko’<br />

ELSE ’Haftpflicht’<br />

END,<br />

mi.Name, mi.Vorname,<br />

vn.Name, vn.Vorname,


fz.Kennzeichen<br />

FROM Versicherungsvertrag vv<br />

JOIN Mitarbeiter mi ON mi.ID = vv.Mitarbeiter_ID<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID;<br />

Übungen<br />

H<strong>in</strong>weis: Weil die Zusatzangaben Pflicht s<strong>in</strong>d, können wir e<strong>in</strong>heitlich mit INNER<br />

JOIN arbeiten.<br />

Lösung zu Übung 6 – E<strong>in</strong>e VIEW auf mehrere Tabellen<br />

ALTER VIEW Vertrag_Anzeige<br />

( ID, Vertragsnummer, Abschlussdatum, Art,<br />

Mi_Name, Mi_Vorname,<br />

Vn_Name, Vn_Vorname,<br />

Kennzeichen, Typ, Hersteller, Land )<br />

AS SELECT vv.ID, vv.Vertragsnummer, vv.Abschlussdatum,<br />

CASE vv.Art<br />

WHEN ’TK’ THEN ’Teilkasko’<br />

WHEN ’VK’ THEN ’Vollkasko’<br />

ELSE ’Haftpflicht’<br />

END,<br />

mi.Name, mi.Vorname,<br />

vn.Name, vn.Vorname,<br />

fz.Kennzeichen, fa.Bezeichnung, fa.Hersteller, fa.Land<br />

FROM Versicherungsvertrag vv<br />

JOIN Mitarbeiter mi ON mi.ID = vv.Mitarbeiter_ID<br />

JOIN Versicherungsnehmer vn ON vn.ID = vv.Versicherungsnehmer_ID<br />

JOIN Fahrzeug fz ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Fahrzeugart fa ON fa.ID = fz.Fahrzeugtyp_ID;<br />

Lösung zu Übung 7 – E<strong>in</strong>e View abschnittsweise kontrollieren<br />

SELECT * FROM Vertrag_Anzeige<br />

WHERE EXTRACT(YEAR FROM Abschlussdatum)


Erstellen von Views<br />

JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

JOIN Versicherungsvertrag vv ON fz.ID = vv.Fahrzeug_ID;<br />

Lösung zu Übung 9 – E<strong>in</strong>e weitere VIEW auf mehrere Tabellen<br />

ALTER VIEW Schaden_Anzeige<br />

( ID, Datum, Gesamtschaden,<br />

Kennzeichen, Typ, Hersteller, Land,<br />

Schadensanteil,<br />

VV_ID, Vertragsnummer, VN_ID )<br />

AS SELECT sf.ID, sf.Datum, sf.Schadenshoehe,<br />

fz.Kennzeichen, fa.Bezeichnung, fa.Hersteller, fa.Land,<br />

zu.Schadenshoehe,<br />

vv.ID, vv.Vertragsnummer, vv.Versicherungsnehmer_ID<br />

FROM Zuordnung_SF_FZ zu<br />

JOIN Schadensfall sf ON sf.ID = zu.Schadensfall_ID<br />

JOIN Fahrzeug fz ON fz.ID = zu.Fahrzeug_ID<br />

JOIN Versicherungsvertrag vv ON fz.ID = vv.Fahrzeug_ID<br />

JOIN Fahrzeugart fa ON fa.ID = fz.Fahrzeugtyp_ID;<br />

Lösung zu Übung 10 – E<strong>in</strong>e View zur Auswertung e<strong>in</strong>er View<br />

CREATE VIEW Schaden_Anzeige_Jahr<br />

AS SELECT *<br />

FROM Schaden_Anzeige<br />

WHERE EXTRACT(YEAR FROM Datum) = EXTRACT(YEAR FROM CURRENT_DATE);<br />

27.5. Siehe auch<br />

Ergänzende Informationen gibt es <strong>in</strong> den folgenden Kapiteln:<br />

• StoredProcedure 1<br />

• Verknüpfung mehrerer Tabellen bei OUTER JOIN 2<br />

1 Kapitel 32 auf Seite 357<br />

2 Abschnitt 21.5.1 auf Seite 196<br />

282


Teil IV.<br />

Erweiterungen<br />

283


28. DDL – E<strong>in</strong>zelheiten<br />

28.1. Def<strong>in</strong>ition e<strong>in</strong>er Tabelle . . . . . . . . . . . . . . . . . . . . . . . . 285<br />

28.2. Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte . . . . . . . . . . . . . . . . . . 287<br />

28.3. Tabelle ändern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291<br />

28.4. CONSTRAINTs – E<strong>in</strong>schränkungen . . . . . . . . . . . . . . . . . 293<br />

28.5. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 301<br />

28.6. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301<br />

28.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304<br />

In diesem Kapitel werden e<strong>in</strong>ige Befehle der Data Def<strong>in</strong>ition Language (DDL)<br />

vertieft behandelt.<br />

Wegen des Umfangs mancher Befehle und Optionen werden die Abschnitte<br />

sachlich gegliedert, nicht nach e<strong>in</strong>em e<strong>in</strong>zelnen Befehl.<br />

28.1. Def<strong>in</strong>ition e<strong>in</strong>er Tabelle<br />

Um e<strong>in</strong>e Tabelle zu erzeugen, s<strong>in</strong>d sehr umfangreiche Angaben nötig.<br />

CREATE TABLE <br />

( <br />

[ , ]<br />

);<br />

Zum Erstellen e<strong>in</strong>er Tabelle gehören folgende Angaben:<br />

• der Name der Tabelle, mit dem die Daten über die DML-Befehle gespeichert<br />

und abgerufen werden<br />

Dazu kommen − <strong>in</strong> Klammern gesetzt − die weiteren Angaben:<br />

• die Liste der Spalten (Felder), und zwar vor allem mit ihren Datentypen.<br />

• Angaben wie der Primärschlüssel (PRIMARY KEY, PK) oder weitere Indizes<br />

Jede Spalte und E<strong>in</strong>schränkung wird mit e<strong>in</strong>em Komma abgeschlossen; dieses<br />

entfällt vor der schließenden Klammer. Die E<strong>in</strong>schränkungen − CONSTRAINTs<br />

285


DDL – E<strong>in</strong>zelheiten<br />

− werden häufig nicht sofort festgelegt, sondern durch anschließende ALTER<br />

TABLE-Befehle; sie werden deshalb getrennt besprochen.<br />

Notwendig s<strong>in</strong>d: der Name des Befehls, der Name der Tabelle, die runden Klammern,<br />

m<strong>in</strong>destens e<strong>in</strong>e Spalte mit Name und Typ. E<strong>in</strong>e solche „M<strong>in</strong>imalversion“<br />

gibt es aber nur für Code-Beispiele; <strong>in</strong> der Praxis gehören immer mehrere Spalten<br />

und der PK dazu.<br />

Beim Entwurf e<strong>in</strong>er Datenbank und ihrer Tabellen sollten Sie immer beachten:<br />

• Datentypen, Art und Umfang der Zusatzangaben hängen vom DBMS ab.<br />

• Primärschlüssel: Manche DBMS verlangen ausdrücklich e<strong>in</strong>en PK − meistens<br />

e<strong>in</strong>e ID o. ä., nur selten aus mehreren Spalten zusammengesetzt. Auch wenn<br />

es nicht verlangt wird, ist e<strong>in</strong> PK dr<strong>in</strong>gend zu empfehlen; e<strong>in</strong>e Tabelle ohne PK<br />

ist selten s<strong>in</strong>nvoll. Dessen Inhalte müssen e<strong>in</strong>deutig se<strong>in</strong> und dürfen sich nicht<br />

wiederholen.<br />

• Dafür gibt es meistens e<strong>in</strong>en automatischen Zähler mit AUTO_INCREMENT,<br />

was ohne Entwicklungsaufwand die Bed<strong>in</strong>gungen e<strong>in</strong>es Primärschlüssels erfüllt.<br />

i H<strong>in</strong>weis<br />

Sie müssen bei Art und Umfang aller Angaben immer die<br />

Besonderheiten Ihres DBMS beachten!<br />

Die Code-Auszüge stammen überwiegend aus den Skripten zur Beispieldatenbank.<br />

Die wichtigsten Bestandteile ersehen Sie aus dem folgenden Beispiel; weitere<br />

Bestandteile werden <strong>in</strong> den späteren Abschnitten behandelt.<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE TABLE Abteilung<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Kuerzel VARCHAR(10) NOT NULL,<br />

Bezeichnung VARCHAR(30) NOT NULL,<br />

Ort VARCHAR(30)<br />

);<br />

Die Tabelle Abteilung wird mit vier Spalten erzeugt:<br />

• ID ist e<strong>in</strong>e ganze Zahl, die nicht NULL se<strong>in</strong> darf, ihre Werte durch die automatische<br />

Zählung erhält und als PRIMARY KEY benutzt wird.<br />

• Kuerzel ist e<strong>in</strong>e Zeichenkette mit variabler Länge (höchstens 10 Zeichen), die<br />

nicht NULL se<strong>in</strong> darf.<br />

• Bezeichnung und Ort s<strong>in</strong>d Zeichenketten mit höchstens 30 Zeichen. Der Ort<br />

darf NULL se<strong>in</strong>, die Bezeichnung nicht.<br />

286


28.2. Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte<br />

Jede e<strong>in</strong>zelne Spalte wird wie folgt def<strong>in</strong>iert.<br />

[ ]<br />

Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte<br />

Jede der Optionen wird mit e<strong>in</strong>em Schlüsselwort und der erforderlichen Angabe<br />

h<strong>in</strong>zugefügt. Die Optionen können <strong>in</strong> der Regel komb<strong>in</strong>iert werden; die Reihenfolge<br />

muss beachtet werden. Verschiedene der E<strong>in</strong>schränkungen (siehe unten)<br />

können auch bei e<strong>in</strong>er e<strong>in</strong>zelnen Spalte angegeben werden.<br />

28.2.1. COLLATE – Sortierungsregel<br />

Jede Spalte kann e<strong>in</strong>e eigene Regel für die alphabetische Sortierung erhalten −<br />

abweichend von der Regel der Tabelle oder Datenbank bzw. von der Standardsortierung<br />

gemäß Zeichensatz.<br />

Aufgabe: In der obigen Def<strong>in</strong>ition der Tabelle Abteilung soll die Bezeichnung<br />

nicht nach den allgeme<strong>in</strong> für die Datenbank gültigen Regeln sortiert werden,<br />

sondern nach denen für kanadisches Französisch:<br />

Firebird-Quelltext<br />

CREATE /* usw. bis */<br />

Bezeichnung VARCHAR(30) NOT NULL COLLATION FR_CA,<br />

/* usw. */<br />

Achtung: So funktioniert der Befehl nicht. Der obige CREATE-Befehl stammt aus<br />

der My<strong>SQL</strong>-Version, diese COLLATION-Ergänzung aber aus Firebird.<br />

28.2.2. NULL-Werte zulässig oder nicht<br />

NULL bzw. NOT NULL legt ausdrücklich fest, ob NULL-Werte <strong>in</strong> der Spalte zulässig<br />

s<strong>in</strong>d. Der Standardwert ist „zulässig“, das NULL kann deshalb entfallen.<br />

Im obigen CREATE-Befehl gilt:<br />

• Die Spalten ID, Kuerzel, Bezeichnung dürfen ke<strong>in</strong>e NULL-Werte enthalten. Die<br />

Informationen <strong>in</strong> diesen Spalten s<strong>in</strong>d wesentlich für e<strong>in</strong>en Datensatz; e<strong>in</strong>e<br />

Speicherung ohne e<strong>in</strong>en dieser Werte wäre s<strong>in</strong>nlos.<br />

• Die Spalte Ort darf dagegen NULL-Werte enthalten. Diese Angabe ist nur e<strong>in</strong>e<br />

zusätzliche Information; die Abteilung steht auch dann e<strong>in</strong>deutig fest, wenn<br />

der Sitz nicht bekannt oder noch nicht festgelegt ist.<br />

287


DDL – E<strong>in</strong>zelheiten<br />

28.2.3. DEFAULT – V<strong>org</strong>abewert<br />

Mit DEFAULT wird e<strong>in</strong> Standardwert festgelegt (als konstanter Wert oder<br />

als Ergebnis e<strong>in</strong>er Funktion); dieser wird immer dann verwendet, wenn bei e<strong>in</strong>er<br />

Neuaufnahme für diese Spalte ke<strong>in</strong> Wert angegeben ist.<br />

Aufgabe: In der Tabelle Mitarbeiter erhält die Spalte Ist_Leiter den V<strong>org</strong>abewert<br />

'N'; denn e<strong>in</strong> Mitarbeiter ist normalerweise ke<strong>in</strong> Abteilungsleiter:<br />

CREATE TABLE Mitarbeiter<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

/* usw. bis */<br />

Ist_Leiter CHAR(1) DEFAULT ’N’,<br />

Abteilung_ID INTEGER NOT NULL<br />

);<br />

Weit verbreitet s<strong>in</strong>d dabei Standardwerte, mit denen Datum/Zeit e<strong>in</strong>er Änderung<br />

und die Bearbeiter<strong>in</strong> registriert werden:<br />

• CURRENT_TIMESTAMP als aktuelles Datum und Uhrzeit<br />

• CURRENT_USER<br />

28.2.4. AUTO_INCREMENT – automatischer Zähler<br />

AUTO_INCREMENT legt fest, dass die Werte <strong>in</strong> dieser Spalte automatisch vom<br />

DBMS hochgezählt werden − siehe das obige Beispiel im Abschnitt Def<strong>in</strong>ition<br />

e<strong>in</strong>er Tabelle.<br />

MS-<strong>SQL</strong> – IDENTITY<br />

ALTER TABLE dbo.doc_exe<br />

ADD column_b INT IDENTITY CONSTRAINT column_b_pk PRIMARY KEY<br />

My<strong>SQL</strong> – AUTO_INCREMENT<br />

CREATE TABLE Abteilung<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARYKEY, /* usw. */<br />

);<br />

Oracle – AUTO INCREMENT<br />

Diese Variante ist nur möglich bei der Lite-Version für mobile Computer;<br />

"AUTO INCREMENT" wird dabei mit Leerzeichen geschrieben.<br />

288


CREATE TABLE Fahrzeug<br />

( ID INTEGER NOT NULL AUTO INCREMENT, /* usw. */<br />

CONSTRAINT Fahrzeug_PK PRIMARY KEY (ID)<br />

) ;<br />

28.2.5. Zähler mit anderen Verfahren<br />

Firebird – SEQUENCE mit TRIGGER<br />

Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte<br />

Es wird e<strong>in</strong> Zähler def<strong>in</strong>iert (wahlweise je Tabelle oder pauschal). In e<strong>in</strong>em<br />

Before-Insert-Trigger wird der nächste Wert abgerufen und e<strong>in</strong>gefügt.<br />

/* Def<strong>in</strong>ition */<br />

CREATE SEQUENCE Fahrzeug_ID;<br />

/* Benutzung im Trigger */<br />

CREATE OR ALTER TRIGGER Fahrzeug_BI0 FOR Fahrzeug<br />

ACTIVE BEFORE INSERT POSITION 0<br />

AS<br />

BEGIN<br />

IF ((new.ID IS NULL) OR (new.ID = 0))<br />

THEN new.ID = NEXT VALUE FOR Fahrzeug_ID;<br />

END<br />

Firebird (veraltet) – GENERATOR mit TRIGGER<br />

Es wird e<strong>in</strong> Zähler def<strong>in</strong>iert (wahlweise je Tabelle oder pauschal). In e<strong>in</strong>em<br />

Before-Insert-Trigger wird der nächste Wert abgerufen und e<strong>in</strong>gefügt.<br />

E<strong>in</strong> GENERATOR sollte für neue Datenbanken nicht mehr benutzt werden.<br />

/* Def<strong>in</strong>ition */<br />

CREATE GENERATOR Fahrzeug_ID;<br />

SET GENERATOR Fahrzeug_ID TO 0;<br />

/* Benutzung im Trigger */<br />

CREATE OR ALTER TRIGGER Fahrzeug_BI0 FOR Fahrzeug<br />

ACTIVE BEFORE INSERT POSITION 0<br />

AS<br />

BEGIN<br />

IF ((new.ID IS NULL) OR (new.ID = 0))<br />

THEN new.ID = GEN_ID(Fahrzeug_ID, 1);<br />

END<br />

289


DDL – E<strong>in</strong>zelheiten<br />

Oracle – SEQUENCE mit TRIGGER<br />

Es wird e<strong>in</strong> Zähler def<strong>in</strong>iert (wahlweise je Tabelle oder pauschal). In e<strong>in</strong>em<br />

Before-Insert-Trigger wird der nächste Wert abgerufen und e<strong>in</strong>gefügt.<br />

/* Def<strong>in</strong>ition */<br />

CREATE SEQUENCE Fahrzeug_ID;<br />

/* Benutzung im Trigger */<br />

CREATE OR REPLACE TRIGGER Fahrzeug_BI<br />

BEFORE INSERT ON Fahrzeug<br />

REFERENCING NEW AS NEW OLD AS OLD<br />

FOR EACH ROW<br />

WHEN (:NEW.ID IS NULL)<br />

BEGIN<br />

/* <strong>in</strong> früheren Oracle-Versionen: */<br />

SELECT Fahrzeug_ID.NEXTVAL INTO :NEW.ID FROM DUAL;<br />

/* ab Version 11g direkte Zuweisung: */<br />

:NEW.ID := Fahrzeug_ID.NEXTVAL;<br />

END<br />

Oracle – SEQUENCE mit INSERT<br />

Es wird e<strong>in</strong> Zähler def<strong>in</strong>iert (wahlweise je Tabelle oder pauschal). Im INSERT-<br />

Befehl wird der nächste Wert abgerufen und übernommen.<br />

Oracle schreibt selbst ausdrücklich, dass die Version mit Trigger vorzuziehen ist.<br />

/* Def<strong>in</strong>ition */<br />

CREATE SEQUENCE Fahrzeug_ID;<br />

/* Benutzung im INSERT-Befehl */<br />

INSERT INTO Fahrzeug<br />

( ID, Kennzeichen /* usw. */ )<br />

VALUES<br />

( Fahrzeug_ID.NEXTVAL, ’BO-CR 123’ /* usw. */ )<br />

;<br />

H<strong>in</strong>weis zu Sequenzen: Bitte beachten Sie auch die allgeme<strong>in</strong>e Anmerkung<br />

im Kapitel DDL – Struktur der Datenbank 1 . Für die meisten Varianten gibt<br />

es Parameter zur genaueren Steuerung, nachzulesen <strong>in</strong> der jeweiligen DBMS-<br />

Dokumentation.<br />

1 Abschnitt 10.3.4 auf Seite 84<br />

290


28.2.6. COMMENT – Beschreibung verwenden<br />

Tabelle ändern<br />

Dies beschreibt den Inhalt der Spalte – nützlich für alle Spalten, sofern e<strong>in</strong> Bezeichner<br />

nicht ganz klar ist oder der Inhalt besondere Bed<strong>in</strong>gungen erfüllen soll.<br />

Damit erleichtern Sie sich und anderen die Arbeit.<br />

CREATE TABLE Schadensfall<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Datum DATE NOT NULL,<br />

Ort VARCHAR(200) NOT NULL<br />

COMMENT ’nicht nur Ortsnamen, sondern auch Straße und Umstände’<br />

, /* usw. */<br />

);<br />

28.3. Tabelle ändern<br />

Mit ALTER TABLE wird die Struktur e<strong>in</strong>er Tabelle geändert:<br />

ALTER TABLE ;<br />

Es können also mehrere Aufgaben mit e<strong>in</strong>em ALTER-Befehl ausgeführt werden.<br />

Die möglichen Aufgaben s<strong>in</strong>d <strong>in</strong> den e<strong>in</strong>zelnen Abschnitten beschrieben.<br />

Der Begriff COLUMN ist nicht immer Teil des Befehls: Bei manchen DBMS kann<br />

er weggelassen werden, bei manchen darf er nicht benutzt werden.<br />

28.3.1. Stop – Aufgabe kann nicht ausgeführt werden<br />

E<strong>in</strong>e Spalte kann oft nicht geändert oder gelöscht werden, wenn sie an anderer<br />

Stelle benutzt wird. Das gilt vor allem dann, wenn diese Spalte beim PRIMARY<br />

KEY, e<strong>in</strong>em INDEX, e<strong>in</strong>em FOREIGN KEY oder <strong>in</strong> e<strong>in</strong>er CHECK-E<strong>in</strong>schränkung<br />

für die Tabelle benutzt wird. In diesen Fällen muss zunächst die „abhängige“<br />

Konstruktion gelöscht, deaktiviert oder geändert werden. Erst danach kann die<br />

Änderung <strong>in</strong> der Tabelle ausgeführt werden.<br />

Der Datentyp kann durch ALTER COLUMN nur dann geändert werden, wenn die<br />

„alten“ Werte automatisch (implizit) <strong>in</strong> den neuen Datentyp konvertiert werden<br />

können.<br />

28.3.2. ADD COLUMN – Spalte h<strong>in</strong>zufügen<br />

Diese Aufgabe fügt der Tabelle e<strong>in</strong>e weitere Spalte h<strong>in</strong>zu.<br />

291


DDL – E<strong>in</strong>zelheiten<br />

Aufgabe: Die Tabelle Versicherungsvertrag wird um Spalten zur Berechnung<br />

und Anpassung der Versicherungsprämie erweitert.<br />

Firebird-Version h<strong>in</strong>sichtlich Schreibweise (ohne "COLUMN") und Reihenfolge der Optionen<br />

Firebird Quelltext<br />

ALTER TABLE Versicherungsvertrag<br />

ADD Basispraemie DECIMAL DEFAULT 500 NOT NULL,<br />

ADD Praemiensatz INTEGER DEFAULT 100 NOT NULL,<br />

ADD Praemienaenderung DATE;<br />

Die bisherigen Inhalte der Tabelle bleiben unverändert. In den neuen Spalten<br />

wird der DEFAULT-Wert e<strong>in</strong>getragen, andernfalls NULL.<br />

28.3.3. ALTER COLUMN – Spalte ändern<br />

Diese Aufgabe ändert e<strong>in</strong>e Spalte dieser Tabelle. Dies kann e<strong>in</strong>e Änderung des<br />

Datentyps, e<strong>in</strong> anderer DEFAULT-Wert oder e<strong>in</strong>e andere E<strong>in</strong>schränkung se<strong>in</strong>.<br />

Aufgabe: In der Tabelle Abteilung ist die Spalte Kuerzel mit VARCHAR(10) def<strong>in</strong>iert,<br />

die e<strong>in</strong>zelnen Werte s<strong>in</strong>d aber immer genau 4 Zeichen lang. Die Spaltendef<strong>in</strong>ition<br />

soll an die Realität angepasst werden.<br />

Firebird-Quelltext Falsch<br />

ALTER TABLE Abteilung<br />

ALTER COLUMN Kuerzel TYPE CHAR(4);<br />

Firebird-Fehlermeldung: This operation is not def<strong>in</strong>ed for system tables.<br />

unsuccessful metadata update.<br />

New size specified for column KUERZEL must be at least 40 characters.<br />

Die Option TYPE ändert den Datentyp. Vorhandene Werte werden möglichst implizit<br />

konvertiert. Beispielsweise My<strong>SQL</strong> hat ke<strong>in</strong>e Probleme damit, den Text entsprechend<br />

zu verkürzen. Firebird weigert sich aber, obwohl die tatsächlichen Inhalte<br />

passen, sondern br<strong>in</strong>gt e<strong>in</strong>e völlig verwirrende Fehlermeldung.<br />

E<strong>in</strong> Umweg dazu wird im Kapitel Änderung der Datenbankstruktur 2 benutzt:<br />

• Fügen Sie e<strong>in</strong>e neue, temporäre Spalte h<strong>in</strong>zu.<br />

• Kopieren Sie alle Inhalte durch e<strong>in</strong>en UPDATE-Befehl aus der „alten“ Spalte,<br />

die geändert werden soll, <strong>in</strong> die temporäre Spalte.<br />

• Löschen Sie die „alte“ Spalte.<br />

2 Abschnitt 35.1.2 auf Seite 398<br />

292


CONSTRAINTs – E<strong>in</strong>schränkungen<br />

• Erzeugen Sie e<strong>in</strong>e neue Spalte unter dem „alten“ Namen mit den „neuen“ Eigenschaften.<br />

• Kopieren Sie alle Inhalte durch e<strong>in</strong>en UPDATE-Befehl aus der temporären<br />

Spalte <strong>in</strong> die neue Spalte, wobei sie passend konvertiert werden müssen.<br />

• Löschen Sie die temporäre Spalte.<br />

28.3.4. DROP COLUMN – Spalte entfernen<br />

Diese Aufgabe entfernt e<strong>in</strong>e Spalte aus der Tabelle, z. B. die eben erwähnte temporäre<br />

Spalte.<br />

ALTER TABLE Abteilung<br />

DROP COLUMN Temp;<br />

Durch Löschen e<strong>in</strong>er Spalte wird nicht der Speicherplatz der Spalte freigegeben.<br />

Dafür muss (sofern erforderlich) e<strong>in</strong>e vollständige Datensicherung – Backup und<br />

Restore – ausgeführt werden. Aber das machen Sie ja sowieso regelmäßig.<br />

28.3.5. ADD CONSTRAINT – E<strong>in</strong>schränkung h<strong>in</strong>zufügen<br />

Diese Aufgabe erweitert die Bed<strong>in</strong>gungen für die Daten der Tabelle.<br />

ALTER TABLE <br />

ADD [ CONSTRAINT ] ;<br />

E<strong>in</strong>zelheiten zum folgen unter „CONSTRAINTs − E<strong>in</strong>schränkungen“.<br />

28.3.6. DROP CONSTRAINT – E<strong>in</strong>schränkung entfernen<br />

Diese Aufgabe löscht e<strong>in</strong>e Bed<strong>in</strong>gung, die für die Daten der Tabelle gültig war.<br />

ALTER TABLE <br />

DROP CONSTRAINT ;<br />

28.4. CONSTRAINTs – E<strong>in</strong>schränkungen<br />

Dabei handelt es sich um Bed<strong>in</strong>gungen, denen e<strong>in</strong> Datensatz entsprechen muss.<br />

Wenn e<strong>in</strong>e der aktuell gültigen Bed<strong>in</strong>gungen verletzt wird, wird der betreffende<br />

Datensatz nicht gespeichert. Die Bed<strong>in</strong>gungen können Folgendes betreffen:<br />

293


DDL – E<strong>in</strong>zelheiten<br />

• die Schlüssel <strong>in</strong>nerhalb der Tabelle: PRIMARY KEY, INDEX<br />

• die Beziehungen zu anderen Tabellen: FOREIGN KEY<br />

• die Werte <strong>in</strong>nerhalb der Spalte: UNIQUE, CHECK<br />

E<strong>in</strong> CONSTRAINT kann mit oder ohne eigenen Namen festgelegt werden. Wir<br />

empfehlen die Benutzung e<strong>in</strong>es Namens, weil dies die Arbeit übersichtlicher<br />

macht: Bei Verletzung e<strong>in</strong>er Regel wird dieser Name meistens angegeben; anhand<br />

des Namens ist das Löschen direkt möglich. Aber das ist Geschmackssache und<br />

hängt wohl auch vom DBMS ab. Das Schlüsselwort CONSTRAINT selbst ist nur<br />

erforderlich bei Verwendung des Namens; ansonsten würden die Schlüsselwörter<br />

der e<strong>in</strong>zelnen Bed<strong>in</strong>gungen ausreichen.<br />

E<strong>in</strong> CONSTRAINT wird auf e<strong>in</strong>e der folgenden Arten festgelegt:<br />

• im CREATE TABLE-Befehl bei e<strong>in</strong>er e<strong>in</strong>zelnen Spalte als Bed<strong>in</strong>gung für diese<br />

Spalte<br />

• im CREATE TABLE-Befehl als Bed<strong>in</strong>gung für die Tabelle, also <strong>in</strong> der Liste der<br />

<br />

• im ALTER TABLE-Befehl durch ADD CONSTRAINT<br />

E<strong>in</strong> CONSTRAINT wird wie folgt gelöscht:<br />

ALTER TABLE <br />

DROP CONSTRAINT ;<br />

E<strong>in</strong> CONSTRAINT kann nicht geändert werden. Es ist nur Löschen und erneute<br />

Festlegung möglich.<br />

Welche Wörter der Schlüsselbegriffe optional s<strong>in</strong>d (z. B. KEY), hängt von der konkreten<br />

Situation ab.<br />

28.4.1. PRIMARY KEY – Primärschlüssel der Tabelle<br />

Der Primärschlüssel − PRIMARY KEY mit PK als gängiger Abkürzung − ist das<br />

wichtigste Mittel, mit dem die Datenbank alle E<strong>in</strong>träge verwaltet. Ohne PK s<strong>in</strong>d<br />

weder Änderungen noch Löschungen e<strong>in</strong>zelner Datensätze möglich, ohne alle<br />

Spalten anzugeben. Im praktischen E<strong>in</strong>satz haben Tabellen ohne Primärschlüssel<br />

ke<strong>in</strong>en S<strong>in</strong>n. Fremdschlüssel (FOREIGN KEYs, FK) wären ohne Primärschlüssel<br />

nicht möglich.<br />

Als Primärschlüssel geeignet s<strong>in</strong>d folgende Arten von Spalten:<br />

• der Datentyp GUID<br />

• e<strong>in</strong>e Spalte mit e<strong>in</strong>em INTEGER-Datentyp, der als AUTO_INCREMENT verwendet<br />

wird oder ersatzweise durch e<strong>in</strong>e SEQUENCE bestimmt wird<br />

294


CONSTRAINTs – E<strong>in</strong>schränkungen<br />

Die Beispieldatenbank benutzt ausnahmslos e<strong>in</strong>e solche Spalte namens<br />

ID.<br />

• e<strong>in</strong>e Spalte mit e<strong>in</strong>em INTEGER-Datentyp, sofern die Werte nach Lage der D<strong>in</strong>ge<br />

e<strong>in</strong>deutig s<strong>in</strong>d und während der „Lebenszeit“ der Datenbank nicht mehr<br />

geändert werden.<br />

Die Beispieldatenbank enthält <strong>in</strong> der Tabelle Mitarbeiter die Spalte<br />

Personalnummer. Diese ist eigentlich e<strong>in</strong>deutig und dürfte deshalb<br />

als PK verwendet werden. Da die Firma aber ihre <strong>in</strong>terne Struktur<br />

ändern und die Personalnummern anpassen könnte, scheidet diese<br />

Spalte als PK aus.<br />

• e<strong>in</strong>e Komb<strong>in</strong>ation aus zwei Spalten, von denen jede dem PK jeweils e<strong>in</strong>er anderen<br />

Tabelle entspricht, wenn die „neue“ Tabelle nur die Verknüpfungen zwischen<br />

den beiden anderen Tabellen darstellt.<br />

Die Tabelle Zuordnung_SF_FZ der Beispieldatenbank enthält die Zuordnungen<br />

Fahrzeuge/Schadensfälle; anstelle e<strong>in</strong>er eigenen ID wäre<br />

auch e<strong>in</strong> Primärschlüssel aus Fahrzeug_ID plus Schadensfall_ID<br />

möglich und s<strong>in</strong>nvoll.<br />

Als Primärschlüssel ungeeignet oder unmöglich s<strong>in</strong>d diese Arten von Spalten:<br />

• Unmöglich s<strong>in</strong>d sämtliche Spalten (wie e<strong>in</strong>e PLZ), bei denen mehrere Datensätze<br />

mit dem gleichen Wert vorkommen können.<br />

• Unmöglich ist e<strong>in</strong>e Komb<strong>in</strong>ation von Name/Vorname bei allen Tabellen mit<br />

Namen, weil über kurz oder lang e<strong>in</strong> „Müller, Lucas“ doppelt vorkommt.<br />

• Auch e<strong>in</strong>e Komb<strong>in</strong>ation von Name/Vorname/Geburtstag scheidet aus dem<br />

gleichen Grund aus.<br />

• E<strong>in</strong>e Komb<strong>in</strong>ation von Name/Geburtstag/Region/lfd. Nr. (ähnlich wie bei der<br />

Versicherungsnummer der Deutschen Rentenversicherung) ist zwar e<strong>in</strong>deutig,<br />

aber als Komb<strong>in</strong>ation von vier Spalten äußerst unpraktisch.<br />

• E<strong>in</strong>e Spalte, deren Werte sich ändern können, ist zwar möglich, aber nicht geeignet.<br />

Das gilt z. B. für das Kfz-Kennzeichen, aber auch (wie schon gesagt) für<br />

etwas wie die Personalnummer.<br />

Der Primärschlüssel kann wie folgt festgelegt werden:<br />

• im CREATE TABLE-Befehl als Zuordnung für e<strong>in</strong>e e<strong>in</strong>zelne Spalte<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE TABLE Abteilung<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Kuerzel VARCHAR(10) NOT NULL,<br />

Bezeichnung VARCHAR(30) NOT NULL,<br />

Ort VARCHAR(30)<br />

);<br />

295


DDL – E<strong>in</strong>zelheiten<br />

Die Spalte ID wird direkt als (e<strong>in</strong>zige) Spalte des PK def<strong>in</strong>iert. Dies ist implizit<br />

e<strong>in</strong> CONSTRAINT, bekommt aber ke<strong>in</strong>en eigenen Namen.<br />

• im CREATE TABLE-Befehl <strong>in</strong> der Liste der <br />

Firebird-Quelltext<br />

CREATE TABLE Abteilung<br />

( ID INTEGER,<br />

Kuerzel VARCHAR(10) NOT NULL,<br />

Bezeichnung VARCHAR(30) NOT NULL,<br />

Ort VARCHAR(30)<br />

CONSTRAINT Abteilung_PK PRIMARY KEY (ID)<br />

);<br />

Der PK bekommt als CONSTRAINT e<strong>in</strong>en eigenen Namen, der Vermerk <strong>in</strong> Klammern<br />

führt die Spalten auf, die als PK verwendet werden (hier wie meistens handelt<br />

es sich um e<strong>in</strong>e e<strong>in</strong>zelne Spalte).<br />

• im ALTER TABLE-Befehl durch ADD CONSTRAINT<br />

My<strong>SQL</strong> Quelltext<br />

My<strong>SQL</strong>-Version mit zwei aufe<strong>in</strong>anderfolgenden Befehlen<br />

CREATE TABLE Abteilung<br />

( ID INTEGER NOT NULL AUTO_INCREMENT,<br />

Kuerzel VARCHAR(10) NOT NULL,<br />

Bezeichnung VARCHAR(30) NOT NULL,<br />

Ort VARCHAR(30)<br />

);<br />

ALTER TABLE Abteilung<br />

ADD CONSTRAINT Abteilung_PK PRIMARY KEY (ID);<br />

Die Tabelle erhält zunächst noch ke<strong>in</strong>en PK, auch wenn das durch AUTO_-<br />

INCREMENT suggeriert und vorbereitet wird. Vielmehr wird der PK anschließend<br />

(mit eigenem Namen) def<strong>in</strong>iert; der Vermerk <strong>in</strong> Klammern führt die Spalten<br />

auf, die als PK verwendet werden.<br />

28.4.2. UNIQUE – E<strong>in</strong>deutigkeit<br />

E<strong>in</strong> UNIQUE KEY s<strong>org</strong>t dafür, dass <strong>in</strong>nerhalb e<strong>in</strong>er Spalte bzw. e<strong>in</strong>er Komb<strong>in</strong>ation<br />

von SpaltenKomb<strong>in</strong>ation!Spalten ke<strong>in</strong> Wert doppelt auftreten kann. Beispiele<br />

s<strong>in</strong>d <strong>in</strong> der Tabelle Mitarbeiter die Spalte Personalnummer und <strong>in</strong> der Tabelle<br />

Fahrzeug die Spalte Kennzeichen.<br />

E<strong>in</strong>e solche E<strong>in</strong>deutigkeitsbed<strong>in</strong>gung kann wie folgt festgelegt werden:<br />

• im CREATE TABLE-Befehl bei e<strong>in</strong>er Spalte als E<strong>in</strong>schränkung für diese Spalte<br />

296


CONSTRAINTs – E<strong>in</strong>schränkungen<br />

CREATE TABLE Fahrzeug<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Kennzeichen VARCHAR(10) NOT NULL UNIQUE,<br />

Farbe VARCHAR(30),<br />

Fahrzeugtyp_ID INTEGER NOT NULL<br />

);<br />

• im CREATE TABLE-Befehl <strong>in</strong> der Liste der mit Bezug auf<br />

e<strong>in</strong>e oder mehrere Spalten<br />

CREATE TABLE Fahrzeug<br />

( ID INTEGER NOT NULL AUTO_INCREMENT,<br />

Kennzeichen VARCHAR(10) NOT NULL,<br />

Farbe VARCHAR(30),<br />

Fahrzeugtyp_ID INTEGER NOT NULL,<br />

CONSTRAINT Fahrzeug_PK PRIMARY KEY (ID),<br />

CONSTRAINT Fahrzeug_Kz UNIQUE (Kennzeichen)<br />

);<br />

• im ALTER TABLE-Befehl durch ADD CONSTRAINT<br />

ALTER TABLE Fahrzeug<br />

ADD [ CONSTRAINT Fahrzeug_Kz ] UNIQUE (Kennzeichen);<br />

28.4.3. INDEX – Suche beschleunigen<br />

E<strong>in</strong> INDEX ist e<strong>in</strong> Verfahren <strong>in</strong>nerhalb e<strong>in</strong>er Datenbank, mit dem schnell auf Datensätze<br />

zugegriffen werden kann. Vor allem der PK benutzt selbst e<strong>in</strong>en Index.<br />

(Intern arbeiten die DBMS unterschiedlich; für den Nutzer sieht es immer so aus,<br />

als wenn der PK e<strong>in</strong> Index ist.) Sämtliche Spalten bzw. Komb<strong>in</strong>ationen von Spalten,<br />

nach denen <strong>in</strong> SELECT-Befehlen häufiger gesucht oder sortiert wird, sollten<br />

mit e<strong>in</strong>em Index versehen werden. Beispiele:<br />

• Name/Vorname sowie PLZ und separat PLZ/Name (vielleicht auch PLZ/Straße)<br />

<strong>in</strong> Tabellen mit Adressen<br />

• solche Spalten, die für den Nutzer wie e<strong>in</strong> PK aussehen, es aber nicht s<strong>in</strong>d,<br />

z. B. <strong>in</strong> der Tabelle Mitarbeiter die Spalte Personalnummer und <strong>in</strong> der Tabelle<br />

Fahrzeug die Spalte Kennzeichen<br />

Die Angabe UNIQUE für e<strong>in</strong>e Spalte s<strong>org</strong>t bereits für e<strong>in</strong>en Index;<br />

e<strong>in</strong>e doppelte Festlegung ist nicht nötig.<br />

• das Datum <strong>in</strong> der Tabelle Schadensfall<br />

Wenn vorzugsweise die größeren Werte benötigt werden, ist mit DESC e<strong>in</strong> absteigender<br />

Index (wie im folgenden Beispiel) s<strong>in</strong>nvoll. In manchen Fällen s<strong>in</strong>d<br />

297


DDL – E<strong>in</strong>zelheiten<br />

durchaus zwei getrennte Indizes auf dieselbe Spalte angebracht − der e<strong>in</strong>e ASC,<br />

der andere DESC.<br />

E<strong>in</strong> Index ist nicht s<strong>in</strong>nvoll, wenn e<strong>in</strong>e Spalte nur wenige verschiedene Werte<br />

enthalten kann wie <strong>in</strong> der Tabelle Versicherungsvertrag die Spalte Art. Dann wäre<br />

der Aufwand für das DBMS, den Index ständig zu aktualisieren, größer als der<br />

Aufwand beim Selektieren und Sortieren.<br />

E<strong>in</strong> Index kann wie folgt festgelegt werden:<br />

• im CREATE TABLE-Befehl <strong>in</strong> der Liste der mit Bezug auf<br />

e<strong>in</strong>e oder mehrere Spalten<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE TABLE Schadensfall<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Datum DATE NOT NULL,<br />

Ort VARCHAR(200) NOT NULL,<br />

/* usw. */<br />

INDEX Schadensfall_Datum (Datum DESC)<br />

);<br />

In der Tabelle wird sofort e<strong>in</strong> Index (mit Namen) für die Spalte Datum angelegt,<br />

und zwar nach absteigenden Werten. (Das bedeutet, dass die Daten mit dem<br />

„größten“ Wert, also die aktuellsten Werte zuerst gefunden werden.) Der Standardwert,<br />

der nicht angegeben werden muss, ist ASC (= aufsteigend).<br />

• mit e<strong>in</strong>em zusätzlichen CREATE INDEX-Befehl <strong>in</strong> folgender Syntax:<br />

CREATE [ UNIQUE ] INDEX <br />

ON ( );<br />

„Irgendwo“ (unterschiedlich nach DBMS) kann außerdem ASC bzw. DESC festgelegt<br />

werden.<br />

Das vorige Beispiel sieht dann unter Firebird (DESC v<strong>org</strong>ezogen) so aus:<br />

Firebird-Quelltext<br />

CREATE DESC INDEX Schadensfall_Datum<br />

ON Schadensfall (Datum);<br />

Die E<strong>in</strong>deutigkeitsbed<strong>in</strong>gung UNIQUE benutzt <strong>in</strong>tern (vermutlich immer) ebenfalls<br />

e<strong>in</strong>en INDEX; dieser kann auch ausdrücklich angegeben werden:<br />

298<br />

ALTER TABLE Fahrzeug<br />

ADD CONSTRAINT Fahrzeug_Kennzeichen UNIQUE (Kennzeichen)<br />

USING INDEX Fahrzeug_Kennzeichen_UK;


28.4.4. FOREIGN KEY – Fremdschlüssel<br />

CONSTRAINTs – E<strong>in</strong>schränkungen<br />

E<strong>in</strong> FOREIGN KEY (FK) regelt die logischen Verknüpfungen zwischen zwei Tabellen:<br />

E<strong>in</strong> Datensatz <strong>in</strong> e<strong>in</strong>er Tabelle darf <strong>in</strong> e<strong>in</strong>er bestimmten Spalte nur solche<br />

Werte benutzen, die <strong>in</strong> e<strong>in</strong>er anderen Tabelle als PK registriert s<strong>in</strong>d. Beispiele:<br />

• In der Tabelle Mitarbeiter darf als Abteilung_ID nur e<strong>in</strong>e gültige ID der Tabelle<br />

Abteilung stehen.<br />

• In der Tabelle Fahrzeug darf als Fahrzeugtyp_ID nur e<strong>in</strong>e gültige ID der Tabelle<br />

Fahrzeugtyp stehen.<br />

• In der Tabelle Fahrzeugtyp darf als Hersteller_ID nur e<strong>in</strong>e gültige ID der Tabelle<br />

Fahrzeughersteller stehen.<br />

E<strong>in</strong> Fremdschlüssel kann wie folgt festgelegt werden:<br />

• im CREATE TABLE-Befehl bei e<strong>in</strong>er e<strong>in</strong>zelnen Spalte als Bed<strong>in</strong>gung für diese<br />

Spalte<br />

• im CREATE TABLE-Befehl als Bed<strong>in</strong>gung für die Tabelle, also <strong>in</strong> der Liste der<br />

<br />

• im ALTER TABLE-Befehl durch ADD CONSTRAINT<br />

E<strong>in</strong>zelheiten werden im Kapitel Fremdschlüssel-Beziehungen 3 behandelt.<br />

28.4.5. CHECK – Werteprüfungen<br />

E<strong>in</strong> CHECK ist e<strong>in</strong>e Prüfung, ob die Werte, die für e<strong>in</strong>en Datensatz gespeichert<br />

werden sollen, bestimmten Regeln entsprechen. Diese Prüfung wird sowohl bei<br />

INSERT als auch bei UPDATE v<strong>org</strong>enommen; sie kann für e<strong>in</strong>e e<strong>in</strong>zelne Spalte<br />

oder für die Tabelle festgelegt werden.<br />

Als Bed<strong>in</strong>gung <strong>in</strong> der CHECK-Klausel kann im Wesentlichen alles stehen, was für<br />

die WHERE-Klausel v<strong>org</strong>esehen ist.<br />

E<strong>in</strong>e solche Prüfung kann wie folgt festgelegt werden:<br />

• im CREATE TABLE-Befehl bei e<strong>in</strong>er e<strong>in</strong>zelnen Spalte als E<strong>in</strong>schränkung für<br />

diese Spalte<br />

Aufgabe: In der Tabelle Schadensfall s<strong>in</strong>d als Schadenshoehe natürlich ke<strong>in</strong>e<br />

negativen Zahlen zulässig. Die Spalte Verletzte ist als CHAR(1) def<strong>in</strong>iert; s<strong>in</strong>nvollerweise<br />

s<strong>in</strong>d nur die Werte 'J' und 'N' zulässig.<br />

3 Kapitel 29 auf Seite 305<br />

299


DDL – E<strong>in</strong>zelheiten<br />

CREATE TABLE Schadensfall<br />

( ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,<br />

Datum DATE NOT NULL,<br />

Ort VARCHAR(200) NOT NULL,<br />

Beschreibung VARCHAR(1000) NOT NULL,<br />

Schadenshoehe DECIMAL(16,2) CHECK(Schadenshoehe >= 0),<br />

Verletzte CHAR(1) NOT NULL<br />

CHECK(Verletzte = ’J’ OR Verletzte = ’N’),<br />

Mitarbeiter_ID INTEGER NOT NULL<br />

);<br />

• im CREATE TABLE-Befehl als Bed<strong>in</strong>gung für die Tabelle, also <strong>in</strong> der Liste der<br />

<br />

Aufgabe: Bei Personen als Versicherungsnehmer benötigt man Vorname, Geburtsdatum<br />

und Führersche<strong>in</strong> sowie e<strong>in</strong> M<strong>in</strong>destalter von 16 Jahren.<br />

CREATE TABLE Versicherungsnehmer<br />

( ID INTEGER NOT NULL AUTO_INCREMENT Primary Key,<br />

Name VARCHAR(30) NOT NULL ,<br />

Vorname VARCHAR(30) NOT NULL ,<br />

Geburtsdatum DATE ,<br />

Fuehrersche<strong>in</strong> DATE ,<br />

/* usw. für alle anderen Spalten, danach: */<br />

CONSTRAINT Versicherungsnehmer_CheckDatum<br />

CHECK( ( (Geburtsdatum IS NULL)<br />

AND (Fuehrersche<strong>in</strong> IS NULL)<br />

AND (Vorname IS NULL OR Vorname = ”) )<br />

OR (Fuehrersche<strong>in</strong> >= Geburtsdatum + 365*16) ));<br />

Die ersten Bed<strong>in</strong>gungen prüfen, ob es sich um e<strong>in</strong>e Person handelt; wenn nicht,<br />

s<strong>in</strong>d Führersche<strong>in</strong>prüfung und Geburtsdatum irrelevant, und der Datensatz<br />

kann gespeichert werden. Wenn es sich um e<strong>in</strong>e Person handelt, wird auch die<br />

letzte Bed<strong>in</strong>gung benötigt; diese wird „näherungsweise“ geprüft und berücksichtigt,<br />

dass das DBMS e<strong>in</strong> Datum <strong>in</strong>tern als ganze Zahl speichert. Alternativ<br />

könnten auch mit EXTRACT() Tag, Monat, Jahr getrennt verglichen werden. Dieses<br />

Verfahren wäre aber deutlich umständlicher; deshalb sollte es hier nicht stehen.<br />

• im ALTER TABLE-Befehl durch ADD CONSTRAINT<br />

Aufgabe: In der Tabelle Versicherungsvertrag s<strong>in</strong>d als Art nur bestimmte Werte<br />

zulässig: 'VK' (= Vollkasko), 'TK' (= Teilkasko <strong>in</strong>cl. Haftpflicht), 'HP' (= Haftpflicht).<br />

300<br />

ALTER TABLE Versicherungsvertrag<br />

ADD CONSTRAINT Vertrag_CheckArt<br />

CHECK (Art IN (’VK’, ’TK’, ’HP’) );


28.5. Zusammenfassung<br />

Zusammenfassung<br />

In diesem Kapitel lernten wir mehrere Verfahren kennen, mit denen e<strong>in</strong>zelne<br />

Spalten und ganze Tabellen genauer festgelegt werden:<br />

• Zu e<strong>in</strong>er Spalte gehören nicht nur der Datentyp, sondern auch die V<strong>org</strong>abe von<br />

Werten und Wertebereichen.<br />

• Für e<strong>in</strong>e Spalte können E<strong>in</strong>schränkungen wie „E<strong>in</strong>deutigkeit“ oder „Teil des<br />

Primärschlüssels“ oder „Teil e<strong>in</strong>es Index“ gelten.<br />

• Für e<strong>in</strong>e Tabelle können Wertebereiche über mehrere Spalten geprüft werden.<br />

• Eigentlich immer gehört zu e<strong>in</strong>er Tabelle e<strong>in</strong> Primärschlüssel.<br />

• Außerdem können Indizes und Fremdschlüssel festgelegt werden.<br />

28.6. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 303.<br />

Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. Zur Def<strong>in</strong>ition e<strong>in</strong>er Tabelle gehört unbed<strong>in</strong>gt die Def<strong>in</strong>ition der Spalten.<br />

2. Zur Def<strong>in</strong>ition e<strong>in</strong>er Tabelle gehört unbed<strong>in</strong>gt die Def<strong>in</strong>ition des Primärschlüssels.<br />

3. Zur Def<strong>in</strong>ition e<strong>in</strong>er Tabelle gehören unbed<strong>in</strong>gt die Klammern.<br />

4. Die Def<strong>in</strong>ition von E<strong>in</strong>schränkungen ist während des CREATE-Befehls oder<br />

durch e<strong>in</strong>en ALTER-Befehl möglich.<br />

5. Als UNIQUE darf nur e<strong>in</strong>e Spalte festgelegt werden.<br />

6. Jede Spalte kann als NOT NULL festgelegt werden.<br />

7. Für jede Spalte können V<strong>org</strong>abewerte festgelegt werden.<br />

8. Es gibt Situationen, <strong>in</strong> denen die Def<strong>in</strong>ition e<strong>in</strong>er Spalte nicht geändert<br />

werden kann.<br />

9. Der Begriff CONSTRAINT gehört zur Def<strong>in</strong>ition e<strong>in</strong>er E<strong>in</strong>schränkung.<br />

10. E<strong>in</strong> Primärschlüssel kann über beliebig viele Spalten festgelegt wird.<br />

11. Es ist üblich, dass der Wert e<strong>in</strong>es Primärschlüssels immer wieder e<strong>in</strong>mal<br />

geändert wird.<br />

301


DDL – E<strong>in</strong>zelheiten<br />

Übung 2 – Tabellendef<strong>in</strong>ition<br />

Bitte geben Sie an, welche Bestandteile der folgenden Def<strong>in</strong>ition falsch s<strong>in</strong>d bzw.<br />

welche Angaben fehlen.<br />

CREATE TABLE Computer<br />

CONSTRAINT ComputerID PRIMARY KEY (Nummer)<br />

UNIQUE Name,<br />

Name NOT NULL VARCHAR COLLATION W<strong>in</strong>1252<br />

Nummer INTEGER PRIMARY KEY<br />

Hersteller VARCHAR(30)<br />

Herstellung DATE<br />

Festplatte LONG DEFAULT 320*1024*1024*1024<br />

Ram_Groesse LONG,<br />

;<br />

H<strong>in</strong>weis: Bei den folgenden Def<strong>in</strong>itionen verwenden Sie bitte für alle E<strong>in</strong>schränkungen<br />

geeignete Namen.<br />

Übung 3 – E<strong>in</strong>schränkungen allgeme<strong>in</strong> def<strong>in</strong>ieren<br />

Erstellen Sie die Def<strong>in</strong>ition für e<strong>in</strong>e Tabelle mit <strong>in</strong>ternationalen Postleitzahlen:<br />

laufende Nummer, Land, Code, Ortsname. Legen Sie für jede Spalte möglichst<br />

viele E<strong>in</strong>zelheiten fest; bei der Reihenfolge der E<strong>in</strong>zelheiten müssen Sie wegen<br />

der Unterschiede der DBMS nur auf die CONSTRAINTS achten.<br />

Übung 4 – Spalten mit E<strong>in</strong>schränkungen h<strong>in</strong>zufügen<br />

Ergänzen Sie die Tabelle Versicherungsvertrag um folgende Spalten:<br />

• Basisprämie für e<strong>in</strong>en Betrag, V<strong>org</strong>abewert 500, ke<strong>in</strong>e negativen Beträge<br />

• Prämiensatz für e<strong>in</strong>e Zahl, V<strong>org</strong>abewert 100, M<strong>in</strong>imalwert 10 [%]<br />

Übung 5 – E<strong>in</strong>schränkung und Index h<strong>in</strong>zufügen<br />

Ändern Sie die Tabelle Versicherungsvertrag so, dass die Spalte Vertragsnummer<br />

e<strong>in</strong>deutig ist und e<strong>in</strong>en (ausdrücklich angegebenen) Index benutzt.<br />

Übung 6 – E<strong>in</strong>schränkung h<strong>in</strong>zufügen<br />

Ändern Sie die Tabelle Versicherungsnehmer so, dass die Spalte Eigener_Kunde<br />

nur die Werte 'J' und 'N' annehmen darf.<br />

302


Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 301.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Übungen<br />

Die Aussagen 1, 3, 4, 6, 7, 8, 10 s<strong>in</strong>d wahr, die Aussagen 2, 5, 9, 11 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – Tabellendef<strong>in</strong>ition<br />

• Es fehlen die Klammern um die gesamte Auflistung aller E<strong>in</strong>zelheiten, und<br />

es fehlen Kommata nach jedem e<strong>in</strong>zelnen Bestandteil. Dagegen ist das letzte<br />

Komma falsch.<br />

• Zeilen 2/3: Diese Zeilen gehören an das Ende: Zuerst müssen die Spalten festgelegt<br />

werden, dann kann darauf Bezug genommen werden.<br />

• Zeile 4: Es fehlt die Größenangabe für die Zeichenkette. Ob die Reihenfolge der<br />

Teile passt, hängt vom DBMS ab.<br />

• Zeile 2 und 5: Doppelte Festlegung des Primary Key. Es bezieht sich zwar <strong>in</strong> beiden<br />

Fällen auf dieselbe Spalte, es s<strong>in</strong>d aber wegen des CONSTRAINT-Namens<br />

unterschiedliche Def<strong>in</strong>itionen.<br />

Lösung zu Übung 3 – E<strong>in</strong>schränkungen allgeme<strong>in</strong> def<strong>in</strong>ieren<br />

CREATE TABLE PLZ_Codes<br />

( ID INTEGER NOT NULL AUTO_INCREMENT<br />

CONSTRAINT PLZ_PK PRIMARY KEY,<br />

Land CHAR ( 2) NOT NULL DEFAULT ’DE’,<br />

Code VARCHAR(10) NOT NULL, -- auch CHAR(10) ist denkbar<br />

Ort VARCHAR(30) NOT NULL,<br />

CONSTRAINT PLZ_UK UNIQUE (Land, Code)<br />

);<br />

Lösung zu Übung 4 – Spalten mit E<strong>in</strong>schränkungen h<strong>in</strong>zufügen<br />

ALTER TABLE Versicherungsvertrag<br />

ADD [COLUMN] Basispraemie DECIMAL<br />

DEFAULT 500 NOT NULL<br />

CONSTRAINT Vertrag_Basispraemie_Check CHECK(Basispraemie > 0),<br />

ADD [COLUMN] Praemiensatz INTEGER<br />

DEFAULT 100 NOT NULL<br />

CONSTRAINT Vertrag_Praemiensatz_Check CHECK(Praemiensatz >= 10);<br />

303


DDL – E<strong>in</strong>zelheiten<br />

Lösung zu Übung 5 – E<strong>in</strong>schränkung und Index h<strong>in</strong>zufügen<br />

ALTER TABLE Versicherungsvertrag<br />

ADD CONSTRAINT Versicherungsvertrag_Nummer UNIQUE (Vertragsnummer)<br />

USING INDEX Versicherungsvertrag_Nummer_UK;<br />

Lösung zu Übung 6 – E<strong>in</strong>schränkung h<strong>in</strong>zufügen<br />

ALTER TABLE Versicherungsnehmer<br />

ADD CONSTRAINT Versicherungsnehmer_Eigener_Kunde<br />

CHECK( Eigener_Kunde = ’J’ OR Eigener_Kunde = ’N’ );<br />

28.7. Siehe auch<br />

In den folgenden Kapiteln s<strong>in</strong>d E<strong>in</strong>zelheiten zu f<strong>in</strong>den:<br />

• Datentypen 4<br />

• Tabellenstruktur der Beispieldatenbank 5<br />

• WHERE-Klausel im Detail 6<br />

Bei Wikipedia gibt es grundlegende Erläuterungen:<br />

• Globally Unique Identifier 7 (GUID) als e<strong>in</strong>deutige Kennung<br />

• Versicherungsnummer 8 der deutschen Rentenversicherung<br />

4 Kapitel 13 auf Seite 97<br />

5 Anhang A auf Seite 419<br />

6 Kapitel 17 auf Seite 155<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Globally%20Unique%20Identifier<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Versicherungsnummer<br />

304


29. Fremdschlüssel-Beziehungen<br />

29.1. Problemstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305<br />

29.2. Grundsätze der Lösung . . . . . . . . . . . . . . . . . . . . . . . . 306<br />

29.3. Syntax und Optionen . . . . . . . . . . . . . . . . . . . . . . . . . 307<br />

29.4. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311<br />

29.5. Komb<strong>in</strong>ation von Fremdschlüsseln . . . . . . . . . . . . . . . . 312<br />

29.6. Rekursive Fremdschlüssel . . . . . . . . . . . . . . . . . . . . . . 314<br />

29.7. Reihenfolge der Maßnahmen beachten . . . . . . . . . . . . . . 316<br />

29.8. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 317<br />

29.9. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318<br />

29.10. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321<br />

In diesem Kapitel werden wir die Verknüpfungen zwischen Tabellen über Fremdschlüssel<br />

behandeln. Bereits <strong>in</strong> e<strong>in</strong>leitenden Kapiteln wurde die „referentielle Integrität“<br />

betont, dass nämlich die Datensätze <strong>in</strong> verschiedenen Tabellen dauerhaft<br />

zue<strong>in</strong>ander passen müssen. Diesem Zweck dienen Fremdschlüssel.<br />

29.1. Problemstellung<br />

In der Beispieldatenbank gibt es viele Verweise von e<strong>in</strong>er Tabelle auf andere Tabellen:<br />

• Zu jedem E<strong>in</strong>trag der Tabelle Versicherungsvertrag gehört genau e<strong>in</strong> E<strong>in</strong>trag der<br />

Tabelle Fahrzeug: Verträge und Fahrzeuge gehören unmittelbar zusammen.<br />

• Zu jedem E<strong>in</strong>trag der Tabelle Versicherungsvertrag gehört e<strong>in</strong> E<strong>in</strong>trag der Tabelle<br />

Versicherungsnehmer: E<strong>in</strong> Vertrag ohne e<strong>in</strong>en Kunden ist s<strong>in</strong>nlos.<br />

• E<strong>in</strong>em E<strong>in</strong>trag der Tabelle Versicherungsnehmer s<strong>in</strong>d E<strong>in</strong>träge der Tabelle Versicherungsvertrag<br />

zugeordnet: E<strong>in</strong> Kunde kann e<strong>in</strong>en oder mehrere Verträge<br />

halten; zu ihm können auch null Verträge gehören, wenn er nämlich aktuell<br />

ke<strong>in</strong>en Vertrag hat, aber als potenzieller Kunde weiterh<strong>in</strong> registriert ist.<br />

• Zu jedem Vertrag gehört e<strong>in</strong> E<strong>in</strong>trag der Tabelle Mitarbeiter: Jeder Vertrag wird<br />

hauptsächlich von e<strong>in</strong>em bestimmten Mitarbeiter bearbeitet. Das ist zwar <strong>in</strong><br />

der Praxis nicht so eng zu sehen, wird aber <strong>in</strong> unserer theoretischen Firma so<br />

geregelt.<br />

305


Fremdschlüssel-Beziehungen<br />

• Umgekehrt gehört nicht zu jedem E<strong>in</strong>trag der Tabelle Mitarbeiter e<strong>in</strong> Vertrag –<br />

z. B. bei der Abteilung „Forschung und Entwicklung“.<br />

Es wäre viel Aufwand, wenn e<strong>in</strong> Anwender all dies selbst beachten müsste. In<br />

e<strong>in</strong>em Büro gibt es immer Störungen; und schon fehlt e<strong>in</strong>e unbed<strong>in</strong>gt nötige Information:<br />

E<strong>in</strong> neuer Vertrag wird gespeichert, aber der Kunde fehlt noch; dann<br />

kommt e<strong>in</strong> Telefonat dazwischen; dann macht die Mitarbeiter<strong>in</strong> mit dem nächsten<br />

Vertrag weiter. Und woh<strong>in</strong> soll die Rechnung zum vorigen Vertrag gehen?<br />

Viel s<strong>in</strong>nvoller ist es, wenn das DBMS diese Bed<strong>in</strong>gungen direkt berücksichtigen<br />

kann. Dies wird über die Fremdschlüssel – englisch: ForeignKeys (FK) – geregelt.<br />

29.2. Grundsätze der Lösung<br />

29.2.1. Beziehungen beschreiben<br />

Bei all diesen Beziehungen geht es um die referentielle Integrität, dass nämlich<br />

die Verb<strong>in</strong>dungen zwischen den Tabellen und Querverweise immer auf dem aktuellen<br />

Stand s<strong>in</strong>d und die Werte <strong>in</strong> allen Tabellen korrekt zusammenpassen.<br />

Dies wird auch als „<strong>in</strong>terne Datensicherheit“ bezeichnet: Die Daten sollen <strong>in</strong>nerhalb<br />

der Datenbank <strong>in</strong>sofern sicher se<strong>in</strong>, dass ke<strong>in</strong>e Widersprüche zwischen den<br />

Daten <strong>in</strong> verschiedenen Tabellen auftreten.<br />

Durch die Fremdschlüssel werden Beziehungen zwischen Datensätzen verschiedener<br />

Tabellen def<strong>in</strong>iert. Das DBMS s<strong>org</strong>t dann für die richtigen Verknüpfungen:<br />

• Ke<strong>in</strong> neuer Vertrag kann ohne Fahrzeug-ID, Versicherungsnehmer-ID und<br />

Mitarbeiter-ID e<strong>in</strong>getragen werden.<br />

• Die entsprechenden Datensätze <strong>in</strong> Tabellen, auf die verwiesen wird, müssen<br />

zuerst gespeichert werden, bevor e<strong>in</strong> Vertrag neu aufgenommen werden kann.<br />

• Ke<strong>in</strong> Versicherungsnehmer kann gelöscht werden, solange noch Verträge vorliegen.<br />

• Bei entsprechender Festlegung können zunächst alle Verträge e<strong>in</strong>es Kunden<br />

und sofort der Kundensatz selbst gelöscht werden.<br />

Ergänzend s<strong>in</strong>d weitere Bed<strong>in</strong>gungen denkbar:<br />

• Wenn Kundennummern geändert werden, müssen die abhängigen Verträge<br />

ebenso geändert werden (Update-Weitergabe); oder Änderungen an Kundennummern<br />

werden erst gar nicht zugelassen (Update-Restriktion).<br />

Die letzten Bed<strong>in</strong>gungen können vernachlässigt werden, da die Beziehungen<br />

über die IDs geregelt werden. Nach den Regeln der relationalen Datenbanken<br />

hat die ID ke<strong>in</strong>e andere Bedeutung als die Identifizierung der Datensätze; es gibt<br />

niemals die Notwendigkeit, sie zu ändern.<br />

306


29.2.2. Beziehungen def<strong>in</strong>ieren<br />

Syntax und Optionen<br />

In der Tabellenstruktur der Beispieldatenbank s<strong>in</strong>d bei den e<strong>in</strong>zelnen Tabellen<br />

<strong>in</strong> der Spalte Erläuterung die Verknüpfungen aufgeführt, beispielsweise:<br />

• Die Tabelle Versicherungsvertrag enthält die folgenden Verweise:<br />

• E<strong>in</strong> E<strong>in</strong>trag <strong>in</strong> der Spalte Versicherungsnehmer-ID verweist auf e<strong>in</strong>e ID der<br />

Tabelle Versicherungsnehmer.<br />

• Die Spalte Fahrzeug-ID verweist auf e<strong>in</strong>e ID der Tabelle Fahrzeug.<br />

• Die Spalte Mitarbeiter-ID verweist auf e<strong>in</strong>e ID der Tabelle Mitarbeiter.<br />

• Die Tabelle Zuordnung_SF_FZ enthält die folgenden Verweise:<br />

• Die Spalte Schadensfall-ID verweist auf e<strong>in</strong>e ID der Tabelle Schadensfall.<br />

• Die Spalte Fahrzeug-ID verweist auf e<strong>in</strong>e ID der Tabelle Fahrzeug.<br />

• Die Tabelle Versicherungsnehmer enthält den folgenden Verweis:<br />

• Die Spalte Versicherungsgesellschaft-ID verweist optional (nämlich nur bei<br />

Fremdkunden) auf e<strong>in</strong>e ID der Tabelle Versicherungsgesellschaft.<br />

In allen Fällen bedeuten die Verweise: In e<strong>in</strong>em Datensatz der jeweils ersten Tabelle<br />

stehen ke<strong>in</strong>e weiteren E<strong>in</strong>zelheiten (z. B. zum Versicherungsnehmer). Stattdessen<br />

steht dort e<strong>in</strong>e ID; die Angaben dazu s<strong>in</strong>d im Datensatz der „weiteren“<br />

Tabelle unter der genannten ID zu f<strong>in</strong>den. Bei e<strong>in</strong>em optionalen Verweis kann<br />

<strong>in</strong> der „ersten“ Tabelle auch der NULL-Wert stehen; ob das mit e<strong>in</strong>em Fremdschlüssel<br />

automatisiert gesteuert werden kann, hängt vom DBMS ab.<br />

29.3. Syntax und Optionen<br />

Es s<strong>in</strong>d also zwei Punkte sicherzustellen:<br />

• Als Fremdschlüssel <strong>in</strong> der e<strong>in</strong>en Tabelle (z. B. beim Versicherungsvertrag) dürfen<br />

nur solche Werte stehen, die als Primärschlüssel <strong>in</strong> der anderen Tabelle<br />

(z. B. beim Versicherungsnehmer) vorhanden s<strong>in</strong>d.<br />

• E<strong>in</strong> Primärschlüssel darf nur dann geändert oder gelöscht werden, wenn er<br />

nicht als Fremdschlüssel <strong>in</strong> e<strong>in</strong>er weiteren Tabelle verwendet wird.<br />

29.3.1. Begriffe<br />

Auf der Ebene von Tabellen werden die folgenden Begriffe verwendet; diese beziehen<br />

sich immer auf e<strong>in</strong>e bestimmte Fremdschlüsel-Beziehung.<br />

• Die Tabelle, auf deren Primärschlüssel verwiesen wird, heißt Primärtabelle;<br />

auch die Begriffe Master-Tabelle oder Parent-Tabelle s<strong>in</strong>d gebräuchlich –<br />

manchmal auch die deutsche Bezeichnung Eltern-Tabelle.<br />

307


Fremdschlüssel-Beziehungen<br />

Im ersten Beispiel der Auflistung s<strong>in</strong>d dies die Tabellen Versicherungsnehmer,<br />

Fahrzeug und Mitarbeiter.<br />

• Die Tabelle, die den bzw. die Fremdschlüssel enthält, bezeichnet man als Detailtabelle<br />

oder Child-Tabelle oder auch abhängige Tabelle – manchmal auch<br />

mit der deutschen Bezeichnung K<strong>in</strong>d-Tabelle.<br />

Im Beispiel ist das die Tabelle Versicherungsvertrag.<br />

Der Begriff Detailtabelle hat im Beispiel nichts damit zu tun, dass <strong>in</strong> der Tabelle<br />

E<strong>in</strong>zelheiten zum Fahrzeug oder zum Sachbearbeiter stünden (das ist gerade<br />

nicht der Fall). Mit dem Beispiel im Wikipedia-Artikel „Fremdschlüssel“ wird die<br />

Bedeutung von Master und Detail verständlich: Die Kunden s<strong>in</strong>d die maßgebende<br />

Tabelle; zu jedem Kunden gehören als Details „se<strong>in</strong>e“ Bestellungen.<br />

Wir sprechen deshalb von Detailtabelle mit Fremdschlüsseln: E<strong>in</strong>e Spalte <strong>in</strong> der<br />

Detailtabelle wird über den Fremdschlüssel verknüpft mit dem Primärschlüssel<br />

<strong>in</strong> der Primärtabelle.<br />

29.3.2. Die Syntax<br />

Die Fremdschlüssel gehören zur Tabellendef<strong>in</strong>ition. Wie bei anderen Befehlen<br />

der Data Def<strong>in</strong>ition Language (DDL) gibt es für FOREIGN KEY mehrere Wege:<br />

• Bei der Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte, die den Fremdschlüssel benutzt:<br />

<br />

-- ergänzt durch:<br />

REFERENCES ( )<br />

[ ]<br />

• Bei der Def<strong>in</strong>ition der Tabelle <strong>in</strong> der Liste der E<strong>in</strong>schränkungen (CON-<br />

STRAINTs):<br />

[ CONSTRAINT ]<br />

FOREIGN KEY ( )<br />

REFERENCES ( )<br />

[ ]<br />

• Als Änderung der Tabellendef<strong>in</strong>ition mit ALTER TABLE:<br />

308<br />

ALTER TABLE <br />

ADD [ CONSTRAINT ]<br />

FOREIGN KEY ( )<br />

REFERENCES ( )<br />

[ ]


Syntax und Optionen<br />

Zu dem Zeitpunkt, an dem e<strong>in</strong> FOREIGN KEY festgelegt wird, muss die Tabelle,<br />

auf die verwiesen wird, bereits def<strong>in</strong>iert se<strong>in</strong>. Der letzte Weg ist deshalb <strong>in</strong> der Regel<br />

am sichersten: Zuerst werden alle Tabellen bestimmt, danach alle FOREIGN<br />

KEYs als Verknüpfung.<br />

Zur eigentlichen Def<strong>in</strong>ition gehören die folgenden Bestandteile:<br />

• CONSTRAINT <br />

Dies legt den Namen dieser E<strong>in</strong>schränkung fest und kann auch entfallen.<br />

• FOREIGN KEY <br />

Dies ist der H<strong>in</strong>weis auf e<strong>in</strong>en Fremdschlüssel und bestimmt, zu welcher Spalte<br />

dieser gehört.<br />

Bei der Def<strong>in</strong>ition e<strong>in</strong>er e<strong>in</strong>zelnen Spalte (erste Variante) ist von vornhere<strong>in</strong> klar,<br />

um welche Spalte es sich handelt; dies muss deshalb nicht wiederholt werden<br />

und entfällt bei dieser Variante.<br />

• REFERENCES <br />

Dies bestimmt die Tabelle, auf die mit dem Fremdschlüssel verwiesen wird.<br />

• <strong>in</strong> Klammern gesetzt<br />

Damit wird festgelegt, welche Spalten <strong>in</strong> den beiden Tabellen mite<strong>in</strong>ander <strong>in</strong><br />

Beziehung gesetzt werden.<br />

Die Optionen werden im nächsten Abschnitt erläutert.<br />

Der Vollständigkeit halber sei darauf h<strong>in</strong>gewiesen: E<strong>in</strong>e solche Verknüpfung<br />

kann sich auch auf mehrere Spalten beziehen, nämlich sowohl beim Primärschlüssel<br />

als auch beim Fremdschlüssel. Da e<strong>in</strong> Primärschlüssel ke<strong>in</strong>e weitere<br />

Bedeutung haben soll, besteht er sowieso (fast) immer aus e<strong>in</strong>er e<strong>in</strong>zelnen Spalte;<br />

deshalb wird <strong>in</strong> der Übersicht nur e<strong>in</strong> erwähnt.<br />

29.3.3. Optionen<br />

Die Optionen e<strong>in</strong>es FOREIGN KEY bestimmen das Verhalten der Tabelle, die die<br />

Verweise (Fremdschlüssel) benutzt – also der Detailtabelle –, sobald <strong>in</strong> der Primärtabelle<br />

die Primärschlüssel geändert werden. Allgeme<strong>in</strong> steht folgendes Verhalten<br />

zur Auswahl:<br />

• NO ACTION − alle Änderungen werden verweigert<br />

• CASCADE − die Weitergabe der Änderung an die Detailtabelle<br />

• RESTRICT − die Verweigerung der Änderung auch <strong>in</strong> der Primärtabelle<br />

• SET NULL − die Änderung des Verweises <strong>in</strong> der Detailtabelle auf NULL<br />

• SET DEFAULT − die Änderung des Verweises <strong>in</strong> der Detailtabelle auf den V<strong>org</strong>abewert<br />

der Spalte<br />

309


Fremdschlüssel-Beziehungen<br />

Die verschiedenen Optionen werden nicht immer unterstützt. Die Option ON<br />

UPDATE CASCADE z. B. wird von den meisten DBMS nicht angeboten.<br />

Im E<strong>in</strong>zelnen wirken sich diese Optionen wie folgt aus.<br />

Bei Neuaufnahmen <strong>in</strong> der Primärtabelle s<strong>in</strong>d Datensätze <strong>in</strong> e<strong>in</strong>er Tabelle mit<br />

Verweisen darauf noch nicht vorhanden. Also kann es ke<strong>in</strong>e Probleme geben;<br />

deshalb muss dies bei den Optionen nicht beachtet werden.<br />

Die folgenden Optionen wirken sich bei Änderungen und Löschungen <strong>in</strong> der<br />

Primärtabelle <strong>in</strong> gleicher Weise aus:<br />

• ON UPDATE NO ACTION und ON DELETE NO ACTION<br />

Die „Inaktivität“ bedeutet: Wenn e<strong>in</strong> Primärschlüssel <strong>in</strong> der Primärtabelle geändert<br />

bzw. gelöscht werden soll und abhängige Sätze <strong>in</strong> der Detailtabelle existieren,<br />

dann wird die Änderung/Löschung mit e<strong>in</strong>em Fehler abgebrochen; es<br />

erfolgt e<strong>in</strong> ROLLBACK.<br />

• ON UPDATE RESTRICT und ON DELETE RESTRICT<br />

Die „Restriktion der Aktualisierung“ bedeutet: Wenn e<strong>in</strong> Primärschlüssel <strong>in</strong> der<br />

Primärtabelle geändert bzw. gelöscht werden soll und abhängige Sätze <strong>in</strong> der<br />

Detailtabelle existieren, dann wird die Änderung/Löschung verweigert.<br />

• ON UPDATE SET NULL und ON DELETE SET NULL<br />

Das „NULL-Setzen“ bedeutet: Wenn e<strong>in</strong> Primärschlüssel <strong>in</strong> der Primärtabelle<br />

geändert bzw. gelöscht wird, dann werden die Verweise <strong>in</strong> der Detailtabelle auf<br />

NULL gesetzt. Das ist nur möglich, wenn die betreffende Spalte NULL-Werte<br />

zulässt (also nicht mit NOT NULL def<strong>in</strong>iert wurde).<br />

• ON UPDATE SET DEFAULT und ON DELETE SET DEFAULT<br />

Das „DEFAULT-Setzen“ bedeutet: Wenn e<strong>in</strong> Primärschlüssel <strong>in</strong> der Primärtabelle<br />

geändert bzw. gelöscht wird, dann werden die Verweise <strong>in</strong> der Detailtabelle<br />

auf den V<strong>org</strong>abewert der betreffenden Spalte gesetzt. Das ist nur möglich,<br />

wenn für die betreffende Spalte e<strong>in</strong> V<strong>org</strong>abewert festgelegt ist.<br />

Die folgende Option unterscheidet sich bei Änderungen und Löschungen:<br />

• ON UPDATE CASCADE – also bei Änderungen<br />

Bei „Weitergabe der Aktualisierung“ werden die Fremdschlüssel <strong>in</strong> der Detailtabelle<br />

genauso geändert wie der Primärschlüssel <strong>in</strong> der Primärtabelle.<br />

• ON DELETE CASCADE – also bei Löschungen<br />

Die „Löschweitergabe“ bedeutet: Zusammen mit dem Datensatz <strong>in</strong> der Primärtabelle<br />

werden auch alle Datensätze <strong>in</strong> der Detailtabelle gelöscht, die sich<br />

auf diesen Schlüssel beziehen.<br />

Wenn der Primärschlüssel „richtig“ def<strong>in</strong>iert ist, nämlich für alle Zeiten unveränderlich<br />

ist, dann wären UPDATE-Optionen eigentlich überflüssig. Aber man<br />

sollte vorbereitet se<strong>in</strong>, falls man doch auf die Idee kommt, e<strong>in</strong>en Primary Key zu<br />

ändern.<br />

310


Auswirkungen<br />

Beispiele<br />

Änderungen <strong>in</strong> der Primärtabelle haben mit den Optionen nichts zu tun. Sie<br />

werden durch e<strong>in</strong>en direkten Befehl – INSERT, UPDATE, DELETE – ausgelöst.<br />

E<strong>in</strong> Fremdschlüssel steuert mit den Optionen nur, <strong>in</strong>wieweit e<strong>in</strong>e Speicherung<br />

Auswirkungen auf die Detailtabelle hat oder nicht. Allerd<strong>in</strong>gs kann es passieren,<br />

dass durch die Restriktion nicht nur die Änderung <strong>in</strong> der Detailtabelle, sondern<br />

auch die Änderung <strong>in</strong> der Primärtabelle verh<strong>in</strong>dert wird.<br />

Änderungen <strong>in</strong> der Detailtabelle werden durch e<strong>in</strong>en FK wie folgt e<strong>in</strong>geschränkt:<br />

Bei INSERT und UPDATE dürfen <strong>in</strong> den Spalten des Fremdschlüssels nur solche<br />

Werte e<strong>in</strong>gefügt werden, die <strong>in</strong> der Primärtabelle als Primärschlüssel vorhanden<br />

s<strong>in</strong>d. E<strong>in</strong>zige Ausnahme ist, wenn die Fremdschlüssel-Spalte als optional def<strong>in</strong>iert<br />

ist. Dann kann hier auch NULL e<strong>in</strong>gefügt werden, obwohl NULL niemals<br />

als Primärschlüssel <strong>in</strong> der Primärtabelle stehen wird.<br />

29.4. Beispiele<br />

29.4.1. Versicherungsvertrag und Kunden<br />

Beg<strong>in</strong>nen wir für die Tabelle Versicherungsvertrag mit dem Verweis von der Spalte<br />

Versicherungsnehmer_ID auf die Tabelle Versicherungsnehmer.<br />

ALTER TABLE Versicherungsvertrag<br />

ADD CONSTRAINT Versicherungsvertrag_VN<br />

FOREIGN KEY (Versicherungsnehmer_ID)<br />

REFERENCES Versicherungsnehmer (ID);<br />

Wie fast immer benutzen wir e<strong>in</strong>e E<strong>in</strong>schränkung mit Namen. Wie bei den anderen<br />

CONSTRAINTs hängen wir an den Namen der Tabelle „etwas“ an (Suffix),<br />

das für die Art der E<strong>in</strong>schränkung steht. Wenn e<strong>in</strong>e Tabelle nur e<strong>in</strong>en Fremdschlüssel<br />

bekommt, wäre FK als Suffix geeignet. Da zur Tabelle Versicherungsvertrag<br />

drei Fremdschlüssel gehören, verwenden wir stattdessen den jeweiligen<br />

Tabellen-Alias wie VN für die Tabelle Versicherungsnehmer.<br />

Mit diesem CONSTRAINT ist festgelegt: E<strong>in</strong> neuer Versicherungsvertrag kann<br />

nur dann registriert werden, wenn die v<strong>org</strong>esehene Versicherungsnehmer_ID <strong>in</strong><br />

der Tabelle Versicherungsnehmer als ID bereits registriert ist.<br />

Wie gesagt: E<strong>in</strong>e „richtige“ ID wird niemals mehr geändert. Vorsichtshalber legen<br />

wir aber auch die Optionen fest:<br />

ALTER TABLE Versicherungsvertrag<br />

ADD CONSTRAINT Versicherungsvertrag_VN<br />

FOREIGN KEY (Versicherungsnehmer_ID)<br />

311


Fremdschlüssel-Beziehungen<br />

REFERENCES Versicherungsnehmer (ID)<br />

ON UPDATE RESTRICT<br />

ON DELETE RESTRICT;<br />

Die Änderung der ID oder die Löschung e<strong>in</strong>es Versicherungsnehmers ist also<br />

nicht zulässig, wenn zu diesem Kunden e<strong>in</strong> Versicherungsvertrag registriert ist.<br />

29.4.2. Mitarbeiter und Abteilung<br />

Die Beziehung zwischen diesen Tabellen kann so festgelegt werden:<br />

ALTER TABLE Mitarbeiter<br />

ADD CONSTRAINT Mitarbeiter_FK<br />

FOREIGN KEY (Abteilung_ID)<br />

REFERENCES Abteilung (ID);<br />

ON UPDATE CASCADE<br />

ON DELETE RESTRICT;<br />

Das DBMS s<strong>org</strong>t damit für die <strong>in</strong>terne Datensicherheit:<br />

• E<strong>in</strong> neuer Mitarbeiter kann nicht e<strong>in</strong>getragen werden, ohne dass die<br />

Abteilung_ID <strong>in</strong> der Tabelle Abteilung als ID vorhanden ist.<br />

• Wenn die Nummer e<strong>in</strong>er Abteilung geändert wird, wird das automatisch bei<br />

allen ihren Mitarbeitern angepasst und ebenfalls geändert.<br />

• Wenn e<strong>in</strong>e Abteilung gelöscht (d. h. geschlossen) werden soll, tritt folgende<br />

Prüfung e<strong>in</strong>:<br />

• Durch ON DELETE RESTRICT kann sie nicht gelöscht werden, solange ihr<br />

Mitarbeiter zugeordnet s<strong>in</strong>d.<br />

• Aber mit ON DELETE CASCADE würden beim Löschen e<strong>in</strong>er Abteilung automatisch<br />

alle dort arbeitenden Mitarbeiter ebenfalls gestrichen.<br />

29.5. Komb<strong>in</strong>ation von Fremdschlüsseln<br />

Grundsätzlich kann e<strong>in</strong>e Detailtabelle gleichzeitig als Primärtabelle für e<strong>in</strong>e<br />

andere Tabelle def<strong>in</strong>iert werden. In unserer Beispieldatenbank betrifft das die<br />

mehrfachen Verknüpfungen:<br />

Versicherungsvertrag ⇔ Versicherungsnehmer ⇔ Versicherungsgesellschaft<br />

⇔ Fahrzeug ⇔ Fahrzeugtyp<br />

⇔ Fahrzeughersteller<br />

⇔ Mitarbeiter ⇔ Abteilung<br />

Fahrzeug ⇔ Zuordnung_SF_FZ ⇔ Schadensfall<br />

312


Komb<strong>in</strong>ation von Fremdschlüsseln<br />

Dann s<strong>in</strong>d aber nicht alle Komb<strong>in</strong>ationen der Optionen CASCADE (Weitergabe)<br />

und RESTRICT (Restriktion) zulässig − weder bei DELETE noch bei UPDATE. Das<br />

DBMS prüft beim Ausführen der DDL-Befehle, ob die gewünschte Regel zulässig<br />

ist oder nicht.<br />

Nicht zulässig s<strong>in</strong>d folgende Verknüpfungen von Optionen:<br />

Tabelle C Tabelle B Tabelle A<br />

(Details zu Tabelle B) (Details zu Tabelle A, (Master zu Tabelle B)<br />

(Master zu Tabelle C)<br />

Versicherungsvertrag ⇔ Versicherungsnehmer ⇔ Versicherungsgesellschaft<br />

ON DELETE RESTRICT ON DELETE CASCADE<br />

Erläuterung: Es ist nicht zulässig, e<strong>in</strong>en Versicherungsnehmer zu löschen, wenn<br />

zu ihm noch (m<strong>in</strong>destens) e<strong>in</strong> Vertrag registriert ist. Andererseits sollen mit e<strong>in</strong>er<br />

Versicherungsgesellschaft auch alle ihre Versicherungsnehmer automatisch<br />

gelöscht werden. Diese automatische Löschung stünde im Widerspruch zur Verh<strong>in</strong>derung<br />

der Löschung bei vorhandenen Verträgen.<br />

Zulässig s<strong>in</strong>d folgende Verknüpfungen von Optionen:<br />

Tabelle C Tabelle B Tabelle A<br />

(Details zu Tabelle B) (Details zu Tabelle A, (Master zu Tabelle B)<br />

(Master zu Tabelle C)<br />

Versicherungsvertrag ⇔ Versicherungsnehmer ⇔ Versicherungsgesellschaft<br />

ON DELETE CASCADE ON DELETE RESTRICT<br />

Erläuterung: Das Löschen der Versicherungsgesellschaft ist nicht zulässig, wenn<br />

noch Versicherungsnehmer registriert s<strong>in</strong>d. Damit wird die Weitergabe oder Restriktion<br />

der Löschung vom Versicherungsnehmer zum Versicherungsvertrag<br />

überhaupt nicht bee<strong>in</strong>flusst.<br />

Möglich s<strong>in</strong>d auch R<strong>in</strong>g-Verkettungen:<br />

Tabelle A ⇔ Tabelle B ⇔ Tabelle C ⇔ Tabelle A<br />

Ob Sie diese Verknüpfungen von rechts nach l<strong>in</strong>ks lesen oder umgekehrt, ist<br />

gleichgültig: E<strong>in</strong>e Tabelle hat Verweise auf e<strong>in</strong>e andere, diese auf e<strong>in</strong>e nächste<br />

und e<strong>in</strong>e weitere wieder auf die erste.<br />

313


Fremdschlüssel-Beziehungen<br />

29.6. Rekursive Fremdschlüssel<br />

Fremdschlüsselbeziehungen können auch rekursiv def<strong>in</strong>iert werden. Dabei verweist<br />

<strong>in</strong> e<strong>in</strong>er Tabelle e<strong>in</strong>e abhängige Spalte auf den eigenen Primärschlüssel.<br />

Als Beispiel verwenden wir e<strong>in</strong>e hierarchische Gliederung der Abteilungen:<br />

CREATE TABLE Abteilung<br />

( AbtNr INTEGER NOT NULL,<br />

UebergeordneteAbt INTEGER,<br />

AbtName VARCHAR(100),<br />

PRIMARY KEY (AbtNr),<br />

FOREIGN KEY (UebergeordneteAbt)<br />

REFERENCES Abteilung (AbtNr)<br />

ON DELETE CASCADE<br />

);<br />

Die gesamte Firma wird mit AbtNr = 1 gespeichert; zu ihr gehört ke<strong>in</strong>e UebergeordneteAbt.<br />

Bei jeder anderen Abteilung wird <strong>in</strong> dieser Spalte die AbtNr derjenigen<br />

Abteilung registriert, der sie direkt zugeordnet ist: E<strong>in</strong>e Hauptabteilung<br />

gehört direkt zur Firma, e<strong>in</strong>e beliebige Abteilung zu ihrer Hauptabteilung, e<strong>in</strong>e<br />

Arbeitsgruppe zu e<strong>in</strong>er bestimmten Abteilung usw.<br />

29.6.1. Praktische Probleme<br />

Rekursive Fremdschlüsselbeziehungen s<strong>in</strong>d etwas problematisch <strong>in</strong> der Handhabung.<br />

Die Neuaufnahme von Datensätzen muss <strong>in</strong> e<strong>in</strong>er bestimmten Reihenfolge geschehen:<br />

zuerst die oberste Ebene (Firma), dann die nächste Ebene (Hauptabteilungen)<br />

usw. Auf jeder Ebene ist e<strong>in</strong>e Neuaufnahme nur dann möglich, wenn<br />

der übergeordnete E<strong>in</strong>trag schon vorhanden ist.<br />

Beim Löschen von Datensätzen kann es zu verschiedenen Problemen kommen.<br />

Mit Lösch-Weitergabe – ON DELETE CASCADE – könnten wesentlich mehr Daten<br />

gelöscht werden, als <strong>in</strong> der WHERE-Bed<strong>in</strong>gung angegeben wird:<br />

DELETE FROM Abteilung<br />

WHERE AbtNr = 33;<br />

Bei diesem Beispiel werden auch alle Sätze gelöscht, die der Abteilung 33 untergeordnet<br />

s<strong>in</strong>d.<br />

Mit Lösch-Restriktion – ON DELETE RESTRICT – wird nach jeder e<strong>in</strong>zelnen Löschung<br />

geprüft, ob es ke<strong>in</strong>e Fremdschlüsselverletzung gibt. Selbst wenn alle Sätze<br />

aus der Tabelle entfernt werden sollen, könnte die Ausführung fehlschlagen.<br />

314


DELETE FROM Abteilung;<br />

Rekursive Fremdschlüssel<br />

Das liegt daran, dass bei der Ausführung des DELETE-Befehls die Sätze <strong>in</strong> e<strong>in</strong>er<br />

beliebigen Reihenfolge gelöscht werden, meistens <strong>in</strong> der Reihenfolge, <strong>in</strong> der sie<br />

„real“ <strong>in</strong> der Tabelle gespeichert s<strong>in</strong>d. Nur wenn die Sätze exakt <strong>in</strong> der richtigen<br />

Reihenfolge (von der untersten Abteilung beg<strong>in</strong>nend bis zur obersten Abteilung)<br />

gespeichert s<strong>in</strong>d, dann kann die Ausführung des DELETE-Befehls gel<strong>in</strong>gen.<br />

Dass der Erfolg e<strong>in</strong>es <strong>SQL</strong>-Befehls von der physischen Speicherreihenfolge der<br />

Sätze abhängig ist, darf <strong>in</strong> e<strong>in</strong>em DBMS nicht vorkommen. Daher bieten e<strong>in</strong>ige<br />

DBMS die Möglichkeit der verzögerten Prüfung bei der Löschweitergabe.<br />

Durch e<strong>in</strong>en e<strong>in</strong>zigen DELETE-Befehl können mehrere Sätze und u. U. auch alle<br />

Sätze e<strong>in</strong>er Tabelle gelöscht werden. Innerhalb e<strong>in</strong>er Transaktion können mehrere<br />

DELETE-Befehle ausgeführt werden. Standardmäßig erfolgt die Prüfung, ob<br />

e<strong>in</strong>e Löschung ausgeführt werden darf, nach jedem e<strong>in</strong>zelnen Satz, der gelöscht<br />

wurde. Das hat den Vorteil, dass bei e<strong>in</strong>er unzulässigen Löschung gleich abgebrochen<br />

werden kann und der ROLLBACK nicht unnötig viel zu tun hat.<br />

29.6.2. Maßnahmen<br />

Um die oben beschriebenen Lösch-Anomalien zu vermeiden, kann bei e<strong>in</strong>igen<br />

DBMS die Prüfung, ob die Löschung zulässig ist, als Gesamtprüfung stattf<strong>in</strong>den<br />

und nicht nach jedem e<strong>in</strong>zelnen Satz. Dies s<strong>in</strong>d e<strong>in</strong> paar Möglichkeiten dafür:<br />

• Nach der Löschung aller Sätze, die durch e<strong>in</strong>en DELETE-Befehl v<strong>org</strong>esehen<br />

s<strong>in</strong>d, wird der Zusammenhang der Daten überprüft.<br />

Diese Variante wird z. B. von DB2 angeboten durch die Option ON DELETE NO<br />

ACTION.<br />

• Erst zum Abschluss der Transaktion wird die Prüfung erledigt.<br />

Diese Variante gibt es z. B. bei Oracle durch die Option INITIALLY IMMEDIATE<br />

DEFERRABLE.<br />

• E<strong>in</strong>ige DBMS können Fremdschlüssel-Beziehungen deaktivieren und später<br />

wieder aktivieren. Bei der Aktivierung muss der gesamte Datenbestand der betroffenen<br />

Tabelle überprüft werden, und es müssen Anweisungen erteilt werden,<br />

wie mit fehlerhaften Sätzen umgegangen werden soll.<br />

• Mit Tools (z. B. Import, Load) kann man bei den meisten DBMS Sätze <strong>in</strong> e<strong>in</strong>e<br />

Tabelle laden, ohne dabei die Fremdschlüssel-Beziehungen zu prüfen.<br />

Bei DB2 z. B. ist die Tabelle danach gesperrt und muss durch das CHECK-Tool<br />

geprüft werden. Erst dann steht die Tabelle wieder für reguläre Zugriffe zur Verfügung.<br />

• Wieder andere DBMS lassen rekursive Fremdschlüsselbeziehungen überhaupt<br />

nicht zu.<br />

315


Fremdschlüssel-Beziehungen<br />

29.7. Reihenfolge der Maßnahmen beachten<br />

Wenn die Tabellen <strong>in</strong> e<strong>in</strong>er Datenbank mit Fremdschlüsseln verbunden s<strong>in</strong>d,<br />

muss beim Bearbeiten der Tabellen e<strong>in</strong>e bestimmte Reihenfolge e<strong>in</strong>gehalten<br />

werden, und zwar sowohl beim E<strong>in</strong>fügen als auch beim Löschen. Ob auch das<br />

Ändern mit Schwierigkeiten verbunden se<strong>in</strong> kann, hängt von der Situation ab.<br />

Alle hier genannten Probleme beziehen sich ausschließlich auf diejenigen Spalten,<br />

die mit Fremdschlüsseln an andere Tabellen gebunden s<strong>in</strong>d. Änderungen <strong>in</strong><br />

anderen Spalten können immer ohne Probleme ausgeführt werden.<br />

29.7.1. Bei INSERT<br />

Man muss mit den Tabellen beg<strong>in</strong>nen, die ke<strong>in</strong>e Fremdschlüssel haben. Danach<br />

können die Tabellen befüllt werden, die auf diese Tabellen verweisen, und so<br />

weiter. In der Beispieldatenbank gilt so für e<strong>in</strong>en neuen Versicherungsvertrag:<br />

• Registriere den Versicherungsnehmer.<br />

• Dazu ist vorher ggf. die Versicherungsgesellschaft zu speichern.<br />

• Registriere das Fahrzeug.<br />

• Dazu ist vorher ggf. der Fahrzeugtyp zu speichern<br />

• und noch e<strong>in</strong>en Schritt früher der Fahrzeughersteller.<br />

• Registriere, soweit notwendig, den Mitarbeiter.<br />

• Dazu ist vorher ggf. die Abteilung zu speichern.<br />

Erst jetzt s<strong>in</strong>d alle Voraussetzungen vorhanden, sodass der Vertrag gespeichert<br />

werden kann.<br />

Bei R<strong>in</strong>g-Verkettungen werden Tools zum <strong>in</strong>itialen Befüllen benötigt; oder es<br />

muss mit Sätzen begonnen werden, die NULL als Fremdschlüssel enthalten.<br />

29.7.2. Bei DELETE<br />

Zum Löschen von Datensätzen muss – sofern nicht mit e<strong>in</strong>er automatischen<br />

Lösch-Weitergabe gearbeitet wird – genau die umgekehrte Reihenfolge e<strong>in</strong>gehalten<br />

werden.<br />

29.7.3. Bei UPDATE<br />

Die Fremdschlüssel-Beziehungen verlangen, dass die Datensätze <strong>in</strong> der Primärtabelle,<br />

auf die aus der Detailtabelle verwiesen wird, vorhanden s<strong>in</strong>d. Ändert<br />

316


Zusammenfassung<br />

man e<strong>in</strong>en Fremdschlüssel <strong>in</strong> der Detailtabelle, muss der neue Wert ebenfalls<br />

<strong>in</strong> der Primärtabelle vorhanden se<strong>in</strong>. Wenn man e<strong>in</strong>en Schlüsselwert <strong>in</strong> der Primärtabelle<br />

ändern will, dann ist das nur möglich, wenn der alte Wert von ke<strong>in</strong>em<br />

Satz <strong>in</strong> der Detailtabelle verwendet wird. Sollte der Wert doch verwendet (referenziert)<br />

werden, dann ist der UPDATE nicht möglich.<br />

Theoretisch wäre auch e<strong>in</strong> UPDATE-CASCADE vorstellbar. Das würde bedeuten,<br />

dass die Änderung e<strong>in</strong>es Wertes <strong>in</strong> der Primärtabelle auch gleichzeitig alle referenzierten<br />

Werte <strong>in</strong> der Detailtabelle ändert. E<strong>in</strong>e solche Funktion wird jedoch<br />

von den meisten Datenbanken nicht angeboten.<br />

Wenn e<strong>in</strong> Schlüsselwert <strong>in</strong> der Primärtabelle zu ändern ist, der von mehreren<br />

Sätzen <strong>in</strong> der Detailtabelle verwendet wird, kann nur so v<strong>org</strong>egangen werden:<br />

• E<strong>in</strong>en Satz mit dem neuen Wert <strong>in</strong> der Primärtabelle e<strong>in</strong>fügen (INSERT)<br />

• Alle Sätze <strong>in</strong> der Detailtabelle ändern auf den neuen Wert (UPDATE)<br />

• Den Satz mit dem alten Wert <strong>in</strong> der Primärtabelle löschen (DELETE)<br />

29.7.4. Bestimme die „E<strong>in</strong>füge-Reihenfolge“<br />

Theoretisch handelt es sich hier um e<strong>in</strong> Problem der „topologischen Sortierung“.<br />

Bei großen Datenmodellen sollte man alle vorhandenen Tabellen <strong>in</strong> der Reihenfolge<br />

notieren, <strong>in</strong> der sie befüllt werden können. Dazu kann man die Struktur der<br />

Datenbank auslesen; Tabellen und Fremdschlüssel müssen also bereits def<strong>in</strong>iert<br />

se<strong>in</strong>. Beispiele dazu s<strong>in</strong>d im Kapitel Tipps und Tricks 1 zu f<strong>in</strong>den.<br />

29.8. Zusammenfassung<br />

In diesem Kapitel erfuhren wir e<strong>in</strong>iges darüber, wie mit Fremdschlüsseln<br />

(= FOREIGN KEYs) die Verb<strong>in</strong>dungen zwischen Tabellen sichergestellt werden:<br />

• Die Def<strong>in</strong>ition gehört zur DDL und erfolgt mit e<strong>in</strong>er entsprechenden E<strong>in</strong>schränkung<br />

(CONSTRAINT), meistens über ALTER TABLE.<br />

• Sowohl beim E<strong>in</strong>fügen von Datensätzen als auch beim Ändern und Löschen<br />

s<strong>in</strong>d diese Beziehungen zu beachten.<br />

• Daraus ergibt sich e<strong>in</strong>e bestimmte Reihenfolge, welche Daten zuerst gespeichert<br />

werden müssen.<br />

• Beim Ändern und Löschen können die Anpassungen durch e<strong>in</strong>e Klausel automatisiert<br />

werden.<br />

1 Abschnitt 34.2 auf Seite 391<br />

317


Fremdschlüssel-Beziehungen<br />

29.9. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 320.<br />

Bei allen <strong>SQL</strong>-Befehlen benutzen Sie bitte CONSTRAINTS mit Namen.<br />

Übung 1 – Def<strong>in</strong>itionen<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. E<strong>in</strong> Fremdschlüssel legt fest, dass <strong>in</strong> e<strong>in</strong>er bestimmten Spalte der e<strong>in</strong>en Tabelle<br />

nur solche Werte verwendet werden können, die als Primärschlüssel<br />

der anderen Tabelle vorhanden s<strong>in</strong>d.<br />

2. Die Tabelle, die den Primärschlüssel enthält, wird als Master-Tabelle bezeichnet,<br />

die Tabelle mit dem Fremdschlüssel als Detail-Tabelle.<br />

3. In unserer Beispieldatenbank ist bei der Verknüpfung Fahrzeugtyp/Fahrzeughersteller<br />

die Tabelle Fahrzeugtyp der „Master“, die Tabelle Fahrzeughersteller<br />

die Detailtabelle.<br />

4. Bei der Verknüpfung Fahrzeug/Fahrzeugtyp gilt die Tabelle Fahrzeug als<br />

Detailtabelle, die Tabelle Fahrzeugtyp als Primärtabelle.<br />

5. E<strong>in</strong> INSERT <strong>in</strong> der Detailtabelle ist immer möglich, ohne die Werte <strong>in</strong> der<br />

Primärtabelle zu beachten.<br />

6. E<strong>in</strong> UPDATE <strong>in</strong> der Primärtabelle ist immer möglich, ohne die Werte <strong>in</strong> der<br />

Detailtabelle zu beachten.<br />

7. E<strong>in</strong> DELETE <strong>in</strong> der Detailtabelle ist immer möglich, ohne die Werte <strong>in</strong> der<br />

Primärtabelle zu beachten.<br />

8. E<strong>in</strong> DELETE <strong>in</strong> der Primärtabelle ist immer möglich, ohne die Werte <strong>in</strong> der<br />

Detailtabelle zu beachten.<br />

9. E<strong>in</strong> FOREIGN KEY wird so def<strong>in</strong>iert: Er wird der Tabelle mit dem Fremdschlüssel<br />

und der betreffenden Spalte zugeordnet und verweist auf die Tabelle<br />

mit dem Primärschlüssel und der betreffenden Spalte.<br />

10. E<strong>in</strong> FOREIGN KEY kann nicht unmittelbar bei der Def<strong>in</strong>ition der<br />

Fremdschlüssel-Spalte angegeben werden.<br />

11. E<strong>in</strong>e Fremdschlüssel-Beziehung kann nur zu jeweils e<strong>in</strong>er Spalte der beiden<br />

Tabellen gehören.<br />

Übung 2 – Zusammenhänge<br />

Welche Fremdschlüssel s<strong>in</strong>d bei den Tabellen Fahrzeug und Mitarbeiter der Beispieldatenbank<br />

vorzusehen? Nennen Sie jeweils die betreffenden Spalten. S<strong>in</strong>d<br />

bei den Primärtabellen weitere Fremdschlüssel vorzusehen?<br />

318


Übung 3 – Fremdschlüssel festlegen<br />

Übungen<br />

Verknüpfen Sie die Tabelle Fahrzeugtyp mit der Tabelle Fahrzeughersteller als<br />

Fremdschlüssel auf die entsprechenden Spalten.<br />

Übung 4 – Fremdschlüssel festlegen<br />

Verknüpfen Sie die Tabelle Dienstwagen mit den Tabellen Mitarbeiter und Fahrzeugtyp<br />

als Fremdschlüssel auf die entsprechenden Spalten.<br />

Übung 5 – Optionen bei Neuaufnahmen<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. Für Neuaufnahmen gibt es ke<strong>in</strong>e Option ON INSERT zur Automatisierung.<br />

2. Bei e<strong>in</strong>er Neuaufnahme <strong>in</strong> der Primärtabelle müssen Verknüpfungen <strong>in</strong> der<br />

Detailtabelle nicht beachtet werden.<br />

3. Bei e<strong>in</strong>er Neuaufnahme <strong>in</strong> der Detailtabelle müssen Verknüpfungen <strong>in</strong> der<br />

Primärtabelle nicht beachtet werden.<br />

4. Bei e<strong>in</strong>er Neuaufnahme <strong>in</strong> der Primärtabelle kann der erforderliche Datensatz<br />

<strong>in</strong> der Detailtabelle automatisch aufgenommen werden.<br />

5. Bei e<strong>in</strong>er Neuaufnahme <strong>in</strong> der Detailtabelle kann der erforderliche Datensatz<br />

<strong>in</strong> der Primärtabelle automatisch aufgenommen werden.<br />

6. Bei Neuaufnahmen <strong>in</strong> beiden Tabellen ist die Reihenfolge zu beachten.<br />

Übung 6 – Optionen bei Änderungen<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. Bei e<strong>in</strong>er Änderung <strong>in</strong> der Primärtabelle müssen Verknüpfungen <strong>in</strong> der Detailtabelle<br />

nicht beachtet werden.<br />

2. Bei e<strong>in</strong>er Änderung <strong>in</strong> der Detailtabelle müssen Verknüpfungen <strong>in</strong> der Primärtabelle<br />

nicht beachtet werden.<br />

3. Bei e<strong>in</strong>er Änderung <strong>in</strong> der Primärtabelle wird der zugehörige Datensatz <strong>in</strong><br />

der Detailtabelle automatisch geändert, sofern ON UPDATE SET DEFAULT<br />

def<strong>in</strong>iert ist.<br />

4. Bei e<strong>in</strong>er Änderung <strong>in</strong> der Primärtabelle wird der zugehörige Datensatz<br />

<strong>in</strong> der Detailtabelle automatisch geändert, sofern ON UPDATE CASCADE<br />

festgelegt ist.<br />

5. E<strong>in</strong>e Änderung <strong>in</strong> der Detailtabelle ändert auch den zugehörigen Datensatz<br />

<strong>in</strong> der Primärtabelle, sofern ON UPDATE CASCADE gilt.<br />

6. Sofern ON UPDATE RESTRICT festgelegt ist, wird e<strong>in</strong>e Änderung <strong>in</strong> der Primärtabelle<br />

immer ausgeführt.<br />

319


Fremdschlüssel-Beziehungen<br />

Übung 7 – Die Reihenfolge<br />

Nennen Sie die Reihenfolge der INSERT-Befehle, wenn e<strong>in</strong> Schadensfall mit drei<br />

beteiligten Fahrzeugen aufzunehmen ist. Dabei soll e<strong>in</strong> Fahrzeug zu e<strong>in</strong>em „Eigenen<br />

Kunden“ und zwei Fahrzeuge zu „Fremdkunden“ gehören; die e<strong>in</strong>e „fremde“<br />

Versicherungsgesellschaft soll schon gespeichert se<strong>in</strong>, die andere nicht.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 318.<br />

Lösung zu Übung 1 – Def<strong>in</strong>itionen<br />

Die Aussagen 1, 2, 4, 5, 8, 10 s<strong>in</strong>d wahr. Die Aussagen 3, 6, 7, 9, 11, 12 s<strong>in</strong>d falsch.<br />

H<strong>in</strong>weis zu Aussage 6: Die Ergänzung „immer“ macht die Aussage falsch. Wenn<br />

die ID ausgenommen würde, wäre die Aussage wahr.<br />

Lösung zu Übung 2 – Zusammenhänge<br />

Fahrzeug: Fahrzeugtyp_ID verweist auf ID der Tabelle Fahrzeugtyp.<br />

Fahrzeugtyp: Hersteller_ID verweist auf ID der Tabelle Fahrzeughersteller.<br />

Mitarbeiter: Abteilung_ID verweist auf ID der Tabelle Abteilung.<br />

Lösung zu Übung 3 – Fremdschlüssel festlegen<br />

ALTER TABLE Fahrzeugtyp<br />

ADD CONSTRAINT Fahrzeugtyp_FK<br />

FOREIGN KEY (Hersteller_ID)<br />

REFERENCES Fahrzeughersteller (ID);<br />

Lösung zu Übung 4 – Fremdschlüssel festlegen<br />

ALTER TABLE Dienstwagen<br />

ADD CONSTRAINT Dienstwagen_FZ<br />

FOREIGN KEY (Fahrzeugtyp_ID) REFERENCES Fahrzeugtyp (ID),<br />

ADD CONSTRAINT Dienstwagen_MI<br />

FOREIGN KEY (Mitarbeiter_ID) REFERENCES Mitarbeiter (ID);<br />

Lösung zu Übung 5 – Optionen bei Neuaufnahmen<br />

Die Aussagen 1, 2, 6 s<strong>in</strong>d wahr. Die Aussagen 3, 4, 5 s<strong>in</strong>d falsch.<br />

320


Lösung zu Übung 6 – Optionen bei Änderungen<br />

Die Aussagen 3, 4 s<strong>in</strong>d wahr. Die Aussagen 1, 2, 5, 6 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 7 – Die Reihenfolge<br />

1. die „neue“ Versicherungsgesellschaft speichern<br />

2. deren Kunden speichern<br />

3. dessen Fahrzeug speichern<br />

4. dessen Versicherungsvertrag speichern<br />

Siehe auch<br />

5. den Fremdkunden der schon registrierten Versicherungsgesellschaft speichern<br />

6. dessen Fahrzeug speichern<br />

7. dessen Versicherungsvertrag speichern<br />

8. den Schadensfall speichern<br />

9. den Schadensfall mit den Fahrzeugen verknüpfen:<br />

a) mit dem Fahrzeug des eigenen Kunden<br />

b) mit dem Fahrzeug des e<strong>in</strong>en Fremdkunden<br />

c) mit dem Fahrzeug des anderen Fremdkunden<br />

29.10. Siehe auch<br />

Weitere E<strong>in</strong>zelheiten s<strong>in</strong>d <strong>in</strong> den folgenden Kapiteln zu f<strong>in</strong>den:<br />

• Relationale Datenbanken 2 – Normalisierung 3<br />

• Tabellenstruktur der Beispieldatenbank 4<br />

• Data Def<strong>in</strong>ition Language (DDL) 5<br />

• DDL – E<strong>in</strong>zelheiten 6 mit E<strong>in</strong>zelheiten zur Tabellendef<strong>in</strong>ition<br />

Wikipedia bietet verschiedene grundlegenden Informationen:<br />

• Fremdschlüssel 7<br />

2 Kapitel 4 auf Seite 17<br />

3 Kapitel 5 auf Seite 25<br />

4 Anhang A auf Seite 419<br />

5 Kapitel 10 auf Seite 79<br />

6 Kapitel 28 auf Seite 285<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Fremdschl%c3%bcssel<br />

321


Fremdschlüssel-Beziehungen<br />

• Referentielle Integrität 8<br />

• Anomalien 9<br />

• Topologische Sortierung 10<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Referentielle%20Integrit%c3%a4t<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Anomalie%20(Informatik)<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/Topologische%20Sortierung<br />

322


30. <strong>SQL</strong>-Programmierung<br />

30.1. Rout<strong>in</strong>en ohne feste Speicherung . . . . . . . . . . . . . . . . . 324<br />

30.2. Programmieren <strong>in</strong>nerhalb von Rout<strong>in</strong>en . . . . . . . . . . . . . 325<br />

30.3. <strong>SQL</strong>-Programmierung mit Firebird . . . . . . . . . . . . . . . . 326<br />

30.4. <strong>SQL</strong>-Programmierung mit MS-<strong>SQL</strong> . . . . . . . . . . . . . . . . 331<br />

30.5. <strong>SQL</strong>-Programmierung mit My<strong>SQL</strong> . . . . . . . . . . . . . . . . . 334<br />

30.6. <strong>SQL</strong>-Programmierung mit Oracle . . . . . . . . . . . . . . . . . 338<br />

30.7. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 342<br />

30.8. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343<br />

30.9. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345<br />

Innerhalb e<strong>in</strong>er Datenbank können Arbeitsabläufe selbst gesteuert werden. Dafür<br />

gibt es Funktionen, Prozeduren und Trigger. Funktionen und Prozeduren<br />

werden oft geme<strong>in</strong>sam als Rout<strong>in</strong>en bezeichnet.<br />

Bei diesen Konstruktionen gibt es relativ wenig Geme<strong>in</strong>samkeiten zwischen<br />

den DBMS. Für Funktionen und Prozeduren lässt bereits der <strong>SQL</strong>-Standard den<br />

Datenbank-Anbietern „alle“ Freiheiten, wie sie diese Möglichkeiten verwirklichen<br />

wollen. Deshalb können auch wir uns nur auf e<strong>in</strong>ige Grundlagen beschränken<br />

und müssen erneut auf die Dokumentation des jeweiligen DBMS verweisen.<br />

Diese Elemente benutzen <strong>in</strong>tegrierte Funktionen, DML-Befehle und teilweise<br />

Datenbank-Operationen, verbunden <strong>in</strong> e<strong>in</strong>er speziellen Programmiersprache,<br />

die prozedurales <strong>SQL</strong> o. ä. bezeichnet wird. In diesem Kapitel gibt es allgeme<strong>in</strong>e<br />

Erklärungen dazu, wie solche Abläufe erstellt und programmiert werden können;<br />

<strong>in</strong> den folgenden Kapiteln werden diese Mittel konkret benutzt.<br />

Funktionen:<br />

E<strong>in</strong>e (benutzerdef<strong>in</strong>ierte Skalar-) Funktion liefert genau e<strong>in</strong>en Wert e<strong>in</strong>es<br />

bestimmten Datentyps. Es handelt sich dabei um e<strong>in</strong>e Ergänzung zu den<br />

<strong>in</strong>ternen Skalarfunktionen des DBMS. Es gibt sie mit und ohne Argumente;<br />

sie werden gezielt vom Anwender bzw. e<strong>in</strong>em Anwendungsprogramm aufgerufen.<br />

E<strong>in</strong>zelheiten dazu werden im Kapitel Eigene Funktionen 1 behandelt.<br />

1 Kapitel 31 auf Seite 347<br />

323


<strong>SQL</strong>-Programmierung<br />

Prozeduren:<br />

E<strong>in</strong>e Prozedur − gespeicherte Prozedur, engl. Stored Procedure (SP) − ist<br />

v<strong>org</strong>esehen für „immer wiederkehrende“ Arbeitsabläufe. Es gibt sie mit<br />

und ohne Argumente und Rückgabewerte; sie werden gezielt vom Anwender<br />

bzw. e<strong>in</strong>em Anwendungsprogramm aufgerufen.<br />

E<strong>in</strong>zelheiten dazu werden unter Prozeduren 2 behandelt.<br />

Trigger:<br />

E<strong>in</strong> Trigger ist e<strong>in</strong> Arbeitsablauf, der automatisch beim Speichern <strong>in</strong> e<strong>in</strong>er<br />

Tabelle ausgeführt wird. Es gibt weder Argumente noch Rückgabewerte<br />

und ke<strong>in</strong>erlei direkte Zusammenarbeit zwischen der Datenbank und dem<br />

Anwender bzw. e<strong>in</strong>em Anwendungsprogramm.<br />

E<strong>in</strong>zelheiten dazu werden unter Trigger 3 behandelt.<br />

30.1. Rout<strong>in</strong>en ohne feste Speicherung<br />

Das, was als Prozedur gespeichert werden kann, kann <strong>in</strong> e<strong>in</strong>em DBMS <strong>in</strong> der Regel<br />

auch direkt ausgeführt werden (ohne Speicherung <strong>in</strong> der Datenbank). Dazu<br />

werden die Def<strong>in</strong>ition von Parametern und Variablen sowie die Anweisungen mit<br />

e<strong>in</strong>er EXECUTE-Anweisung aufgerufen.<br />

Im Kapitel zu Prozeduren gibt es e<strong>in</strong> Beispiel „Testdaten <strong>in</strong> e<strong>in</strong>er Tabelle erzeugen“,<br />

das auch so verwirklicht werden kann:<br />

Firebird-Quelltext<br />

EXECUTE BLOCK ( Anzahl INT = ?anzahl )<br />

RETURNS ( Maxid INT )<br />

AS<br />

DECLARE VARIABLE Temp INT = 0; /* usw. identisch wie bei der Prozedur */<br />

BEGIN<br />

Maxid = 0;<br />

WHILE (Temp < Anzahl) DO<br />

BEGIN /* identischer Arbeitsablauf wie bei der Prozedur */<br />

Temp = Temp + 1;<br />

END<br />

SELECT MAX(ID) FROM Fahrzeug INTO :Maxid;<br />

SUSPEND;<br />

END<br />

Der Aufbau entspricht dem e<strong>in</strong>er Prozedur (siehe unten). Der Unterschied besteht<br />

<strong>in</strong> der direkten Ausführung durch EXECUTE BLOCK.<br />

2 Kapitel 32 auf Seite 357<br />

3 Kapitel 33 auf Seite 377<br />

324


Programmieren <strong>in</strong>nerhalb von Rout<strong>in</strong>en<br />

30.2. Programmieren <strong>in</strong>nerhalb von Rout<strong>in</strong>en<br />

Dieser Abschnitt beschränkt sich auf die wichtigsten Erläuterungen. Die konkreten<br />

<strong>SQL</strong>-Anweisungen s<strong>in</strong>d <strong>in</strong> den folgenden Kapiteln zu f<strong>in</strong>den. Außerdem gibt<br />

es zu fast allen genannten Themen weitere Möglichkeiten.<br />

Bitte haben Sie Nachsicht: Wegen der vielen Varianten bei den DBMS wurde e<strong>in</strong><br />

Teil der folgenden H<strong>in</strong>weise und der Beispiele <strong>in</strong> den nächsten Kapiteln nur nach<br />

der Dokumentation verfasst und nicht <strong>in</strong> der Praxis umgesetzt.<br />

30.2.1. Allgeme<strong>in</strong>es<br />

Rout<strong>in</strong>en − also Funktionen und Prozeduren − werden grundsätzlich mit e<strong>in</strong>er<br />

Syntax ähnlich der folgenden def<strong>in</strong>iert:<br />

CREATE OR ALTER { FUNCTION | PROCEDURE } <br />

( [ ] )<br />

RETURNS <br />

AS<br />

BEGIN<br />

<br />

<br />

END<br />

Die Def<strong>in</strong>ition von Triggern verläuft so ähnlich: Parameter entfallen, aber die Art<br />

der Ausführung kommt h<strong>in</strong>zu. Die H<strong>in</strong>weise zu Variablen und Anweisungen <strong>in</strong><br />

den folgenden Abschnitten gelten für Trigger <strong>in</strong> gleicher Weise wie für Rout<strong>in</strong>en.<br />

Der Teil zwischen BEGIN und END (jeweils e<strong>in</strong>schließlich) wird als Rumpf − engl.<br />

body − bezeichnet, alles davor heißt Kopf − engl. header − der Rout<strong>in</strong>e.<br />

Bitte beachten Sie, dass jedes DBMS se<strong>in</strong>e eigenen Besonderheiten hat. Die<br />

wichtigsten Unterschiede s<strong>in</strong>d:<br />

• Bei My<strong>SQL</strong> müssen CREATE und ALTER getrennt werden, bei Oracle heißt es<br />

CREATE OR REPLACE.<br />

• RETURNS gehört zu Funktionen (bei Oracle: RETURN). Nur Firebird benutzt<br />

es auch bei Prozeduren zur Trennung der Ausgabe-Parameter.<br />

• Ob die Parameter <strong>in</strong> Klammern stehen müssen oder nicht, ist unterschiedlich<br />

geregelt.<br />

• AS kann teilweise auch entfallen, bei Oracle wird auch IS verwendet.<br />

• Die ist Bestandteil der ; bei Firebird und<br />

Oracle steht sie zwischen AS und BEGIN.<br />

Wenn es <strong>in</strong>sgesamt (e<strong>in</strong>schließlich Variablen) nur e<strong>in</strong>e e<strong>in</strong>zige Anweisung gibt,<br />

kann auf BEGIN und END verzichtet werden; der Übersichtlichkeit halber ist ihre<br />

Verwendung aber fast immer zu empfehlen.<br />

325


<strong>SQL</strong>-Programmierung<br />

Gleiches gilt <strong>in</strong>nerhalb e<strong>in</strong>zelner Abschnitte (wie Verzweigungen oder Schleifen):<br />

E<strong>in</strong>e e<strong>in</strong>zelne Anweisung kann ohne BEGIN. . . END angegeben werden; wenn es<br />

die Übersichtlichkeit oder Verschachtelung erfordern, ist die Verwendung dieser<br />

Schlüsselwörter vorzuziehen.<br />

E<strong>in</strong>e Funktion benötigt als (letzte) Anweisung RETURN, mit der e<strong>in</strong> bestimmter<br />

Wert zurückgegeben wird.<br />

Bei Verzweigungen und Schleifen kann durch LABELs der Zusammenhang deutlich<br />

gemacht werden. Meistens gibt es ke<strong>in</strong>e Notwendigkeit dazu, sodass wir <strong>in</strong><br />

der Regel darauf verzichten. Aber BEGIN. . . END sollten vor allem bei verschachtelten<br />

Prüfungen immer benutzt werden; durch E<strong>in</strong>rückungen sollte der Zusammenhang<br />

herv<strong>org</strong>ehoben werden.<br />

30.2.2. Spalten, Variable und Parameter<br />

In diesem Buch werden nur e<strong>in</strong>fache lokale Variable benutzt; deren Gültigkeitsbereich<br />

beschränkt sich auf die aktuelle Rout<strong>in</strong>e. Je nach DBMS stehen auch<br />

globale Variable zur Verfügung. Außerdem kann man über das Schlüsselwort<br />

CURSOR e<strong>in</strong>e ganze Zeile von Tabellen oder Ergebnismengen mit e<strong>in</strong>er Variablen<br />

benutzen.<br />

In allen Fällen, <strong>in</strong> denen die Namen von Variablen oder Parametern auf die Namen<br />

von Tabellenspalten treffen, muss dem DBMS klar se<strong>in</strong>, um welche Art von<br />

Namen es sich handelt:<br />

• MS-<strong>SQL</strong> regelt das mit '@' am Anfang des Namens von Variablen oder Parametern.<br />

• My<strong>SQL</strong> und Oracle unterscheiden nicht. Sie müssen selbst für unterschiedliche<br />

Bezeichner s<strong>org</strong>en.<br />

• Firebird verlangt <strong>in</strong> diesen Situationen e<strong>in</strong>en Doppelpunkt vor dem Namen<br />

von Variablen und Parametern.<br />

Wegen der vielfältigen Unterschiede werden die wichtigsten Möglichkeiten getrennt<br />

behandelt.<br />

30.3. <strong>SQL</strong>-Programmierung mit Firebird<br />

30.3.1. Parameter deklarieren<br />

Jeder Parameter, der <strong>in</strong>nerhalb der Anweisungen benutzt wird und dessen Wert<br />

an die Rout<strong>in</strong>e übergeben oder durch die Bearbeitung zurückgegeben wird,<br />

326


<strong>SQL</strong>-Programmierung mit Firebird<br />

muss im Kopf der Rout<strong>in</strong>e festgelegt werden: Name, Datentyp, V<strong>org</strong>abe- oder Anfangswert.<br />

Mehrere Parameter werden mit Komma verbunden; nach dem letzten<br />

Parameter fehlt es.<br />

Bei Funktionen kann es nur E<strong>in</strong>gabe-Parameter geben; der Ausgabe-Parameter<br />

wird durch RETURNS immer getrennt angegeben.<br />

E<strong>in</strong>gabe-Parameter stehen nach dem Namen der Rout<strong>in</strong>e vor der RETURNS-<br />

Klausel, Ausgabe-Parameter s<strong>in</strong>d Teil der RETURNS-Klausel. Als Datentypen<br />

s<strong>in</strong>d ab Version 2.1 auch DOMAINs zulässig. E<strong>in</strong> e<strong>in</strong>zelner Parameter wird so deklariert:<br />

[ = | DEFAULT ]<br />

Bei E<strong>in</strong>gabe-Parametern s<strong>in</strong>d auch V<strong>org</strong>abewerte möglich, die durch '=' oder<br />

DEFAULT gekennzeichnet werden. Wichtig ist: Wenn e<strong>in</strong> Parameter e<strong>in</strong>en V<strong>org</strong>abewert<br />

erhält und deshalb beim Aufruf <strong>in</strong> der Liste nicht benutzt wird, müssen<br />

alle nachfolgenden Parameter ebenfalls mit V<strong>org</strong>abewert arbeiten.<br />

30.3.2. Variable deklarieren<br />

Jede Variable, die <strong>in</strong>nerhalb der Anweisungen benutzt wird, muss im Kopf der<br />

Rout<strong>in</strong>e festgelegt werden, nämlich zwischen AS und BEGIN: Name, Datentyp,<br />

V<strong>org</strong>abe- oder Anfangswert. Jede Deklaration gilt als e<strong>in</strong>e e<strong>in</strong>zelne Anweisung<br />

und ist mit Semikolon abzuschließen.<br />

DECLARE [VARIABLE] [ =|DEFAULT ];<br />

Als V<strong>org</strong>abewert ist auch e<strong>in</strong> <strong>SQL</strong>-Ausdruck möglich.<br />

30.3.3. Zuweisungen von Werten zu Variablen und Parametern<br />

Der e<strong>in</strong>fachste Weg ist die direkte Zuweisung e<strong>in</strong>es Wertes oder e<strong>in</strong>es Ausdrucks<br />

(e<strong>in</strong>er <strong>in</strong>ternen oder e<strong>in</strong>er eigenen Funktion) zu e<strong>in</strong>er Variablen oder e<strong>in</strong>em Parameter:<br />

= ; /* Standard: nur das Gleichheitszeichen */<br />

Sehr oft werden die Werte aus e<strong>in</strong>em SELECT-Befehl mit Variablen weiterverarbeitet.<br />

Dazu gibt es die INTO-Klausel:<br />

327


<strong>SQL</strong>-Programmierung<br />

SELECT <br />

FROM <br />

INTO ;<br />

Die Liste der Variablen muss von Anzahl und Typ her der Liste der Spalten entsprechen.<br />

Bitte beachten Sie, dass bei Firebird die INTO-Klausel erst am Ende<br />

des Befehls stehen darf.<br />

In ähnlicher Weise kann auch das Ergebnis e<strong>in</strong>er Prozedur übernommen und<br />

<strong>in</strong> der aktuellen Rout<strong>in</strong>e verarbeitet werden:<br />

EXECUTE PROCEDURE [ ]<br />

RETURNING_VALUES ; /* Variablen mit Doppelpunkt */<br />

Jede hier genannte Variable muss (<strong>in</strong> Reihenfolge und Typ) e<strong>in</strong>em der <br />

der Prozedur entsprechen.<br />

Achtung: Um zwischen den Namen von Variablen oder Parametern und den<br />

Namen von Tabellenspalten zu unterscheiden, verlangt Firebird e<strong>in</strong>en Doppelpunkt<br />

vor dem Namen von Variablen und Parametern.<br />

Erst die SUSPEND-Anweisung s<strong>org</strong>t dafür, dass e<strong>in</strong> Ausgabe-Parameter vom „rufenden“<br />

Programm entgegengenommen werden kann. Bei e<strong>in</strong>er Rückgabe e<strong>in</strong>facher<br />

Werte steht diese Anweisung am Ende e<strong>in</strong>er Prozedur; bei e<strong>in</strong>er Funktion<br />

übernimmt RETURN diese Aufgabe. Wenn aber (wie durch e<strong>in</strong>en SELECT-<br />

Befehl) mehrere Zeilen zu übergeben s<strong>in</strong>d, muss SUSPEND bei jeder dieser Zeilen<br />

stehen. E<strong>in</strong> Beispiel steht im Kapitel zu Prozeduren unter Ersatz für e<strong>in</strong>e View<br />

mit Parametern.<br />

30.3.4. Verzweigungen<br />

Die IF-Abfrage steuert den Ablauf nach Bed<strong>in</strong>gungen:<br />

IF ( ) THEN<br />

BEGIN<br />

<br />

END<br />

[ ELSE<br />

BEGIN<br />

<br />

END<br />

]<br />

Diese Abfrage sieht so aus:<br />

328


<strong>SQL</strong>-Programmierung mit Firebird<br />

• Die muss <strong>in</strong> Klammern stehen; sie kann auch mit AND und OR<br />

sowie weiteren Klammern verschachtelt werden.<br />

• Der ELSE-Zweig ist optional; die IF-Abfrage kann auch auf den IF-THEN-<br />

Abschnitt beschränkt werden.<br />

• Der ELSE-Zweig kann durch weitere IF-Abfragen verschachtelt werden.<br />

30.3.5. Schleifen<br />

Es gibt zwei Arten von Schleifen: e<strong>in</strong>e Schleife mit e<strong>in</strong>er Bed<strong>in</strong>gung und e<strong>in</strong>e<br />

Schleife mit e<strong>in</strong>er Ergebnismenge für e<strong>in</strong>e SELECT-Abfrage.<br />

Die WHILE-Schleife prüft e<strong>in</strong>e Bed<strong>in</strong>gung und wird so lange durchlaufen, wie<br />

diese Bed<strong>in</strong>gung wahr ist:<br />

WHILE ( ) DO<br />

BEGIN<br />

<br />

END<br />

Diese Schleife sieht so aus:<br />

• Die muss <strong>in</strong> Klammern stehen; sie kann auch mit AND und OR<br />

sowie weiteren Klammern verschachtelt werden.<br />

• Die wird jeweils am Anfang e<strong>in</strong>es Durchgangs geprüft. Wenn ihr<br />

Wert von Anfang an FALSE ist, wird die Schleife überhaupt nicht durchlaufen.<br />

Die FOR SELECT-Schleife erstellt durch e<strong>in</strong>en SELECT-Befehl e<strong>in</strong>e Ergebnismenge<br />

und führt für jede Zeile des Ergebnisses etwas aus:<br />

FOR SELECT <br />

INTO <br />

DO BEGIN<br />

<br />

END<br />

Diese Schleife sieht so aus:<br />

• Beim SELECT-Befehl handelt es sich um e<strong>in</strong>e beliebige Abfrage.<br />

• Für jede Zeile des Ergebnisses wird der DO-Block e<strong>in</strong>mal durchlaufen.<br />

• Die Ergebnisspalten und ihre Werte s<strong>in</strong>d nur <strong>in</strong>nerhalb des SELECT-Befehls<br />

bekannt, nicht <strong>in</strong>nerhalb der DO-Anweisungen. Die Werte müssen deshalb zunächst<br />

an Variablen übergeben (für jede Spalte e<strong>in</strong>e Variable oder e<strong>in</strong> Ausgabe-<br />

Parameter, mit Doppelpunkt gekennzeichnet), bevor sie <strong>in</strong> den <br />

benutzt werden können.<br />

329


<strong>SQL</strong>-Programmierung<br />

• Wenn diese Werte für jede Zeile e<strong>in</strong>zeln zurückgegeben werden sollen, wird<br />

SUSPEND als e<strong>in</strong>e der Anweisungen benötigt.<br />

• Wenn SUSPEND die e<strong>in</strong>zige Anweisung ist und ke<strong>in</strong> E<strong>in</strong>zelwert außerhalb der<br />

Schleife benötigt wird, kann auf die INTO-Klausel verzichtet werden.<br />

30.3.6. Zulässige Befehle<br />

Innerhalb von Rout<strong>in</strong>en s<strong>in</strong>d ausschließlich DML-Befehle zulässig. Ke<strong>in</strong>erlei anderer<br />

Befehl wird akzeptiert, also vor allem ke<strong>in</strong> DDL-Befehl, aber auch nicht<br />

GRANT/REVOKE (DCL) oder COMMIT/ROLLBACK (TCL).<br />

Sämtliche DML-Befehle, die <strong>in</strong>nerhalb e<strong>in</strong>er Rout<strong>in</strong>e ausgeführt werden, gehören<br />

zur selben Transaktion wie die Befehle, durch die sie aufgerufen bzw. ausgelöst<br />

werden.<br />

30.3.7. Anweisungen begrenzen<br />

Jede e<strong>in</strong>zelne Anweisung <strong>in</strong>nerhalb e<strong>in</strong>er Rout<strong>in</strong>e und jeder <strong>SQL</strong>-Befehl müssen<br />

mit e<strong>in</strong>em Semikolon abgeschlossen werden. Das ist ke<strong>in</strong> Problem, wenn nur e<strong>in</strong><br />

e<strong>in</strong>zelnes CREATE PROCEDURE o. ä. ausgeführt werden soll; dann wird das abschließende<br />

Semikolon weggelassen, und es gibt ke<strong>in</strong>e Unklarheiten (siehe e<strong>in</strong><br />

Firebird-Beispiel im Trigger-Kapitel).<br />

Bei mehreren Rout<strong>in</strong>en nache<strong>in</strong>ander − wie im Skript zur Beispieldatenbank −<br />

muss das DBMS zwischen den verschiedenen Abschlusszeichen unterscheiden.<br />

Dazu dient SET TERM (TERM steht für Term<strong>in</strong>ator, also Begrenzer):<br />

Firebird-Quelltext<br />

SET TERM ˆ ;<br />

CREATE OR ALTER TRIGGER Abteilung_BI0 FOR Abteilung<br />

ACTIVE BEFORE INSERT POSITION 0<br />

AS<br />

BEGIN<br />

IF ((new.ID IS NULL) OR (new.ID = 0))<br />

THEN new.ID = NEXT VALUE FOR Abteilung_ID;<br />

END<br />

ˆ<br />

SET TERM ; ˆ<br />

Zuerst wird der Begrenzer für <strong>SQL</strong>-Befehle auf 'ˆ' geändert; das Abschlusszeichen<br />

für e<strong>in</strong>e e<strong>in</strong>zelne Anweisung bleibt das Semikolon. Dann folgen alle Befehle zur<br />

Trigger-Def<strong>in</strong>ition; jeder e<strong>in</strong>zelne Trigger wird mit dem neuen Begrenzer beendet.<br />

Abschließend wird der Begrenzer wieder auf das Semikolon zurückgesetzt.<br />

330


30.4. <strong>SQL</strong>-Programmierung mit MS-<strong>SQL</strong><br />

<strong>SQL</strong>-Programmierung mit MS-<strong>SQL</strong><br />

Als erste Anweisung e<strong>in</strong>er Rout<strong>in</strong>e sollte immer SET NOCOUNT ON; verwendet werden;<br />

dies beschleunigt die Ausführung.<br />

30.4.1. Parameter deklarieren<br />

Jeder Parameter, dessen Wert der Rout<strong>in</strong>e übergeben oder durch die Bearbeitung<br />

zurückgegeben wird, muss im Kopf festgelegt werden: Name, Datentyp, Verwendung<br />

für E<strong>in</strong>gabe und/oder Ausgabe, V<strong>org</strong>abe- oder Anfangswert. Mehrere Parameter<br />

werden mit Komma verbunden; nach dem letzten Parameter fehlt es.<br />

Bei Funktionen kann es nur E<strong>in</strong>gabe-Parameter geben; der Ausgabe-Parameter<br />

wird durch RETURNS immer getrennt angeben.<br />

E<strong>in</strong> e<strong>in</strong>zelner Parameter wird so deklariert:<br />

[ = ] [ OUT | OUTPUT ]<br />

Parameternamen müssen immer mit '@' beg<strong>in</strong>nen. Ausgabe-Parameter werden<br />

mit OUT bzw. OUTPUT markiert; alle anderen s<strong>in</strong>d E<strong>in</strong>gabe-Parameter. Durch<br />

das Gleichheitszeichen kann e<strong>in</strong> V<strong>org</strong>abewert zugewiesen werden.<br />

30.4.2. Variable deklarieren<br />

Jede Variable, die <strong>in</strong>nerhalb der Anweisungen benutzt wird, ist im Rumpf der<br />

Rout<strong>in</strong>e festzulegen: Name, Datentyp, V<strong>org</strong>abe- oder Anfangswert. Jede Deklaration<br />

gilt als e<strong>in</strong>e e<strong>in</strong>zelne Anweisung und ist mit Semikolon abzuschließen.<br />

DECLARE [AS] [ = ];<br />

Bei MS-<strong>SQL</strong> muss der Name immer mit '@' beg<strong>in</strong>nen. Als V<strong>org</strong>abewert ist auch<br />

e<strong>in</strong> <strong>SQL</strong>-Ausdruck möglich.<br />

30.4.3. Zuweisungen von Werten zu Variablen und Parametern<br />

Der e<strong>in</strong>fachste Weg ist die direkte Zuweisung e<strong>in</strong>es Wertes oder e<strong>in</strong>es Ausdrucks<br />

(e<strong>in</strong>er <strong>in</strong>ternen oder e<strong>in</strong>er eigenen Funktion) zu e<strong>in</strong>er Variablen oder e<strong>in</strong>em Parameter<br />

mit dem SET-Befehl:<br />

331


<strong>SQL</strong>-Programmierung<br />

SET = ;<br />

Sehr oft werden die Werte aus e<strong>in</strong>em SELECT-Befehl mit Variablen weiterverarbeitet.<br />

E<strong>in</strong> e<strong>in</strong>zelner Wert wie das Ergebnis von SELECT COUNT(*) wird ebenfalls<br />

durch SET der Variablen zugewiesen. Mehrere Werte können <strong>in</strong>nerhalb des<br />

SELECT direkt zugewiesen werden:<br />

SELECT @Variable1 = , @Variable2 = <br />

FROM /* usw. weitere Bed<strong>in</strong>gungen */<br />

Für e<strong>in</strong>e Ergebnismenge wird e<strong>in</strong> CURSOR benötigt, der mit FETCH e<strong>in</strong>en Datensatz<br />

holt und den Wert e<strong>in</strong>er Spalte mit INTO an e<strong>in</strong>e Variable übergibt.<br />

E<strong>in</strong>e Rout<strong>in</strong>e wird meistens mit EXECUTE ausgeführt. Rückgabewerte, die über<br />

OUTPUT-Parameter bestimmt werden, werden vorher deklariert und mit dem<br />

der Prozedur verbunden.<br />

DECLARE @<strong>in</strong>putvariable VARCHAR(25);<br />

SET @<strong>in</strong>putvariable = ’Meier’;<br />

DECLARE @outputvariable MONEY;<br />

EXECUTE myprocedure<br />

@<strong>in</strong>putparameter = @<strong>in</strong>putvariable,<br />

@outputparameter = @outputvariable OUTPUT;<br />

SELECT @outputvariable;<br />

Jede hier genannte Variable muss (<strong>in</strong> Reihenfolge und Typ) allen Parametern der<br />

Prozedur entsprechen.<br />

Für EXECUTE (auch die Abkürzung EXEC ist möglich) gibt es viele Varianten für<br />

Aufruf und Zuweisung der Parameter.<br />

30.4.4. Verzweigungen<br />

Die IF-Abfrage steuert den Ablauf nach Bed<strong>in</strong>gungen:<br />

IF <br />

BEGIN<br />

<br />

END<br />

[ ELSE IF <br />

BEGIN<br />

<br />

END ]<br />

[ ELSE<br />

BEGIN<br />

332


END ]<br />

END<br />

Diese Abfrage sieht so aus:<br />

<strong>SQL</strong>-Programmierung mit MS-<strong>SQL</strong><br />

• Bei handelt es sich um e<strong>in</strong>e e<strong>in</strong>fache Prüfung, die nicht mit AND<br />

oder OR erweitert werden kann.<br />

• Wenn e<strong>in</strong> SELECT Teil der ist, muss es <strong>in</strong> Klammern stehen.<br />

• Der ELSE-Zweig ist optional. Es ist auch möglich, dass nur der IF-THEN-<br />

Abschnitt ausgeführt werden muss.<br />

• Durch ELSE IF-Zweige s<strong>in</strong>d Verschachtelungen, also auch weitere Prüfungen<br />

möglich.<br />

30.4.5. Schleifen<br />

Die WHILE-Schleife prüft e<strong>in</strong>e Bed<strong>in</strong>gung und wird so lange durchlaufen, wie<br />

diese Bed<strong>in</strong>gung wahr ist:<br />

WHILE <br />

BEGIN END<br />

Dabei s<strong>in</strong>d folgende Punkte wichtig:<br />

• Bei handelt es sich um e<strong>in</strong>e e<strong>in</strong>fache Prüfung, die nicht mit AND<br />

oder OR erweitert werden kann.<br />

• Wenn e<strong>in</strong> SELECT Teil der ist, muss es <strong>in</strong> Klammern stehen.<br />

• Die wird jeweils am Anfang e<strong>in</strong>es Durchgangs geprüft. Wenn ihr<br />

Wert von Anfang an FALSE ist, wird die Schleife überhaupt nicht durchlaufen.<br />

• Schleifen können verschachtelt werden. Vor allem dann sollten BEGIN. . . END<br />

immer benutzt und durch E<strong>in</strong>rückung den Zusammenhang deutlich machen.<br />

E<strong>in</strong>e Schleife oder e<strong>in</strong> IF-Zweig kann mit BREAK vorzeitig abgebrochen; mit<br />

CONTINUE kann direkt der nächste Durchlauf begonnen werden. Bei Verschachtelung<br />

wird mit BREAK zur nächsthöheren Ebene gesprungen.<br />

Um alle Datensätze e<strong>in</strong>er Ergebnismenge, also e<strong>in</strong>es SELECT-Befehls zu durchlaufen,<br />

wird e<strong>in</strong> CURSOR benötigt. Dies wird <strong>in</strong> diesem Buch nicht behandelt.<br />

333


<strong>SQL</strong>-Programmierung<br />

30.4.6. Zulässige Befehle<br />

E<strong>in</strong>ige Anweisungen können nicht <strong>in</strong> e<strong>in</strong>er Rout<strong>in</strong>e verwendet werden. Dazu<br />

gehören vor allem der Wechsel der aktuellen Datenbank sowie CREATE/ALTER<br />

für Views, Rout<strong>in</strong>en und Trigger. Im Gegensatz zu anderen DBMS ist aber z. B.<br />

CREATE TABLE und unter Umständen auch COMMIT bzw. ROLLBACK möglich.<br />

30.4.7. Anweisungen begrenzen<br />

Bei MS-<strong>SQL</strong> gibt es ke<strong>in</strong>e Notwendigkeit, zwischen Anweisungen <strong>in</strong>nerhalb e<strong>in</strong>er<br />

Rout<strong>in</strong>e und getrennten <strong>SQL</strong>-Befehlen zu unterscheiden: Jede Anweisung<br />

wird mit Semikolon abgeschlossen; e<strong>in</strong> „selbständiger“ <strong>SQL</strong>-Befehl wird durch<br />

GO abgeschlossen und ausgeführt.<br />

30.5. <strong>SQL</strong>-Programmierung mit My<strong>SQL</strong><br />

My<strong>SQL</strong> hat gespeicherte Prozeduren und Funktionen erst mit Version 5 e<strong>in</strong>geführt,<br />

sodass noch nicht alle Wünsche erfüllt werden. Aber das wird natürlich<br />

von Version zu Version besser.<br />

Bitte achten Sie unbed<strong>in</strong>gt darauf, dass sich die Namen von Parametern und Variablen<br />

von Spaltennamen unterscheiden, die <strong>in</strong> derselben Anweisung vorkommen.<br />

Andernfalls gibt es unvorhersehbare Ergebnisse, weil die Namen der Variablen<br />

Vorrang haben gegenüber gleichnamigen Spalten. E<strong>in</strong> gängiges Verfahren<br />

ist es (wie bei MS-<strong>SQL</strong>), dass diese Namen mit '@' beg<strong>in</strong>nen.<br />

30.5.1. Parameter deklarieren<br />

Jeder Parameter, der <strong>in</strong>nerhalb der Anweisungen benutzt wird und dessen Wert<br />

an die Rout<strong>in</strong>e übergeben oder durch die Bearbeitung zurückgegeben wird, ist<br />

im Kopf der Rout<strong>in</strong>e festzulegen: Name, Datentyp, Verwendung für E<strong>in</strong>gabe und/oder<br />

Ausgabe, V<strong>org</strong>abe- oder Anfangswert. Mehrere Parameter werden mit<br />

Komma verbunden; nach dem letzten Parameter fehlt das Komma.<br />

Bei Funktionen kann es nur E<strong>in</strong>gabe-Parameter geben; der Ausgabe-Parameter<br />

wird durch RETURNS immer getrennt angeben.<br />

E<strong>in</strong> e<strong>in</strong>zelner Parameter wird so deklariert:<br />

[ IN | OUT | INOUT ] <br />

Die Festlegung als E<strong>in</strong>gabe-Parameter kann entfallen; IN ist der Standardwert.<br />

334


30.5.2. Variable deklarieren<br />

<strong>SQL</strong>-Programmierung mit My<strong>SQL</strong><br />

Jede Variable, die <strong>in</strong>nerhalb der Anweisungen benutzt wird, ist im Rumpf der<br />

Rout<strong>in</strong>e festzulegen: Name, Datentyp, V<strong>org</strong>abe- oder Anfangswert. Jede Deklaration<br />

gilt als e<strong>in</strong>e e<strong>in</strong>zelne Anweisung und ist mit Semikolon abzuschließen.<br />

DECLARE [ DEFAULT ];<br />

Als V<strong>org</strong>abewert ist auch e<strong>in</strong> <strong>SQL</strong>-Ausdruck möglich.<br />

30.5.3. Zuweisungen von Werten zu Variablen und Parameter<br />

Der e<strong>in</strong>fachste Weg ist die direkte Zuweisung e<strong>in</strong>es Wertes oder e<strong>in</strong>es Ausdrucks<br />

(e<strong>in</strong>er <strong>in</strong>ternen oder e<strong>in</strong>er eigenen Funktion) zu e<strong>in</strong>er Variablen oder e<strong>in</strong>em Parameter<br />

mit dem SET-Befehl:<br />

SET = ;<br />

Sehr oft werden die Werte aus e<strong>in</strong>em SELECT mit Variablen weiterverarbeitet.<br />

Dazu gibt es die INTO-Klausel direkt nach der . Die Liste der Variablen<br />

muss von Anzahl und Typ her der Liste der Spalten entsprechen.<br />

SELECT INTO <br />

FROM ;<br />

E<strong>in</strong>e Prozedur wird mit CALL ausgeführt. Rückgabewerte, die über OUT-<br />

Parameter bestimmt werden, werden vorher deklariert.<br />

DECLARE @<strong>in</strong>putvariable VARCHAR(25);<br />

SET @<strong>in</strong>putvariable = ’Meier’;<br />

DECLARE @outputvariable MONEY = 0;<br />

CALL myprocedure (@<strong>in</strong>putvariable, @outputvariable);<br />

SELECT @outputvariable;<br />

Jede hier genannte Variable muss (<strong>in</strong> Reihenfolge und Typ) e<strong>in</strong>em der <br />

der Prozedur entsprechen.<br />

30.5.4. Verzweigungen<br />

My<strong>SQL</strong> kennt zwei Arten, um auf unterschiedliche Situationen zu reagieren:<br />

Die IF-Abfrage steuert den Ablauf nach Bed<strong>in</strong>gungen:<br />

335


<strong>SQL</strong>-Programmierung<br />

IF THEN<br />

BEGIN<br />

<br />

END<br />

[ ELSEIF THEN<br />

BEGIN<br />

<br />

END ]<br />

[ ELSE<br />

BEGIN<br />

<br />

END ]<br />

END IF<br />

Diese Ablaufprüfung sieht so aus:<br />

• Bei handelt es sich um e<strong>in</strong>e e<strong>in</strong>fache Prüfung, die nicht mit AND<br />

oder OR erweitert werden kann.<br />

• Der ELSE-Zweig ist optional. Es ist auch möglich, dass nur der IF-THEN-<br />

Abschnitt ausgeführt werden muss.<br />

• Durch ELSEIF-Zweige s<strong>in</strong>d Verschachtelungen, also auch weitere Prüfungen<br />

möglich.<br />

• Die Verzweigung wird mit END IF abgeschlossen.<br />

Die CASE-Prüfung entspricht der Fallunterscheidung aus dem Kapitel Nützliche<br />

Erweiterungen:<br />

-- Variante 1<br />

CASE <br />

WHEN THEN BEGIN END<br />

[ WHEN THEN BEGIN END ] /* usw. */<br />

[ ELSE BEGIN END ]<br />

END CASE<br />

-- Variante 2<br />

CASE<br />

WHEN THEN BEGIN END<br />

[ WHEN THEN BEGIN END ] /* usw. */<br />

[ ELSE BEGIN END ]<br />

END CASE<br />

Bei dieser Prüfung gelten die gleichen Prüfungen wie bei der Fallunterscheidung<br />

mit folgenden Abweichungen:<br />

• Im ELSE-Zweig darf es ke<strong>in</strong>e NULL-Anweisung geben.<br />

• Die Verzweigung muss mit END CASE abgeschlossen werden.<br />

336


30.5.5. Schleifen<br />

<strong>SQL</strong>-Programmierung mit My<strong>SQL</strong><br />

My<strong>SQL</strong> kennt mehrere Arten von Schleifen, aber ke<strong>in</strong>e FOR-Schleife. Wenn<br />

ITERATE oder LEAVE verwendet werden, müssen LABELs gesetzt werden.<br />

Die LOOP-Schleife wird – ohne Prüfung am Anfang oder Ende – solange durchlaufen,<br />

bis (vor allem nach e<strong>in</strong>er „<strong>in</strong>neren“ Bed<strong>in</strong>gung) die LEAVE-Anweisung<br />

getroffen wird. Mit ITERATE wird direkt der nächste Durchgang gestartet.<br />

LOOP<br />

BEGIN END<br />

END LOOP ;<br />

Die REPEAT-Schleife wird m<strong>in</strong>destens e<strong>in</strong>mal durchlaufen, und zwar solange,<br />

bis die Ende-Bed<strong>in</strong>gung FALSE ergibt oder bis − aufgrund e<strong>in</strong>er „<strong>in</strong>neren“ Bed<strong>in</strong>gung<br />

− die LEAVE-Anweisung getroffen wird.<br />

REPEAT<br />

BEGIN END<br />

UNTIL <br />

END REPEAT<br />

Die WHILE-Schleife prüft e<strong>in</strong>e Bed<strong>in</strong>gung und wird so lange durchlaufen, wie<br />

diese Bed<strong>in</strong>gung wahr ist:<br />

WHILE DO<br />

BEGIN END<br />

END WHILE<br />

Die wird jeweils am Anfang e<strong>in</strong>es Durchgangs geprüft. Wenn ihr<br />

Wert von Anfang an FALSE ist, wird diese Schleife überhaupt nicht durchlaufen.<br />

Um alle Datensätze e<strong>in</strong>er Ergebnismenge, also e<strong>in</strong>es SELECT-Befehls zu durchlaufen,<br />

wird e<strong>in</strong> CURSOR benötigt. Das wird <strong>in</strong> diesem Buch nicht behandelt.<br />

30.5.6. Zulässige Befehle<br />

Innerhalb von eigenen Funktionen, Prozeduren und Triggern s<strong>in</strong>d nicht alle<br />

<strong>SQL</strong>-Befehle zulässig. Bitte lesen Sie <strong>in</strong> Ihrer Dokumentation E<strong>in</strong>zelheiten nach.<br />

30.5.7. Anweisungen begrenzen<br />

Jede e<strong>in</strong>zelne Anweisung <strong>in</strong>nerhalb e<strong>in</strong>er Rout<strong>in</strong>e und jeder <strong>SQL</strong>-Befehl müssen<br />

mit e<strong>in</strong>em Semikolon abgeschlossen werden. Das ist ke<strong>in</strong> Problem, wenn nur e<strong>in</strong><br />

337


<strong>SQL</strong>-Programmierung<br />

e<strong>in</strong>zelner CREATE PROCEDURE o. ä. ausgeführt werden soll; dann wird das abschließende<br />

Semikolon weggelassen, und es gibt ke<strong>in</strong>e Unklarheiten (siehe e<strong>in</strong><br />

My<strong>SQL</strong>-Beispiel zu Trigger).<br />

Wenn mehrere Rout<strong>in</strong>en nache<strong>in</strong>ander erzeugt werden, muss das DBMS zwischen<br />

den verschiedenen Arten von Abschlusszeichen unterscheiden. Dazu<br />

dient der delimiter-Befehl:<br />

My<strong>SQL</strong>-Quelltext<br />

delimiter //<br />

CREATE OR ALTER TRIGGER Mitarbeiter_BD<br />

ACTIVE BEFORE DELETE ON Mitarbeiter<br />

FOR EACH ROW<br />

BEGIN<br />

UPDATE Dienstwagen<br />

SET Mitarbeiter_ID = NULL<br />

WHERE Mitarbeiter_ID = old.ID;<br />

END<br />

//<br />

delimiter ;<br />

Zuerst wird der Begrenzer für <strong>SQL</strong>-Befehle auf '//' geändert; das Abschlusszeichen<br />

für e<strong>in</strong>zelne Anweisungen bleibt das Semikolon. Dann folgt jede e<strong>in</strong>zelne<br />

Trigger-Def<strong>in</strong>ition mit allen ihren Befehlen und dem neuen Begrenzer als Ende<br />

der Def<strong>in</strong>ition. Abschließend wird der Begrenzer wieder auf das Semikolon zurückgesetzt.<br />

30.6. <strong>SQL</strong>-Programmierung mit Oracle<br />

Bei Oracle gilt das prozedurale <strong>SQL</strong> nicht als Teil des DBMS, sondern als spezielle<br />

Programmiersprache PL/<strong>SQL</strong>. In der Praxis wird beides gemischt verwendet.<br />

Bitte achten Sie unbed<strong>in</strong>gt darauf, dass sich die Namen von Parametern und Variablen<br />

von Spaltennamen unterscheiden, die <strong>in</strong> derselben Rout<strong>in</strong>e vorkommen.<br />

30.6.1. Parameter deklarieren<br />

Jeder Parameter, dessen Wert an die Rout<strong>in</strong>e übergeben oder durch die Bearbeitung<br />

zurückgegeben wird, muss im Kopf der Rout<strong>in</strong>e festgelegt werden: Name,<br />

Datentyp, Verwendung für E<strong>in</strong>gabe und/oder Ausgabe, V<strong>org</strong>abe- oder Anfangswert.<br />

Mehrere Parameter werden mit Komma verbunden; nach dem letzten Parameter<br />

fehlt dies.<br />

338


<strong>SQL</strong>-Programmierung mit Oracle<br />

Bei Funktionen kann es nur E<strong>in</strong>gabe-Parameter geben; der Ausgabe-Parameter<br />

wird durch RETURN (nicht wie sonst oft durch RETURNS) getrennt angegeben.<br />

E<strong>in</strong> e<strong>in</strong>zelner Parameter wird so deklariert:<br />

[ IN | OUT | IN OUT ] <br />

Die Festlegung als E<strong>in</strong>gabe-Parameter kann entfallen; IN ist der Standardwert.<br />

Beim Datentyp darf die Größe nicht angegeben werden: VARCHAR2 ist zulässig,<br />

aber VARCHAR2(20) nicht.<br />

30.6.2. Variable deklarieren<br />

Jede Variable, die <strong>in</strong>nerhalb der Rout<strong>in</strong>e benutzt wird, muss <strong>in</strong> ihrem Kopf festgelegt<br />

werden, nämlich zwischen AS/IS und BEGIN: Name, Datentyp und ggf.<br />

V<strong>org</strong>abewert; das Schlüsselwort DECLARE entfällt. Jede Deklaration gilt als e<strong>in</strong>e<br />

e<strong>in</strong>zelne Anweisung und ist mit Semikolon abzuschließen.<br />

[ := ] ;<br />

Als V<strong>org</strong>abewert ist auch e<strong>in</strong> <strong>SQL</strong>-Ausdruck möglich.<br />

30.6.3. Zuweisungen von Werten zu Variablen und Parameter<br />

Der e<strong>in</strong>fachste Weg ist die direkte Zuweisung e<strong>in</strong>es Wertes oder e<strong>in</strong>es Ausdrucks<br />

zu e<strong>in</strong>er Variablen oder e<strong>in</strong>em Parameter:<br />

:= ;<br />

Bitte beachten Sie, dass (wie bei Pascal) Doppelpunkt und Gleichheitszeichen zu<br />

verwenden s<strong>in</strong>d.<br />

Sehr oft werden die Werte aus e<strong>in</strong>em SELECT-Befehl mit Variablen weiterverarbeitet.<br />

Dazu gibt es die INTO-Klausel, die direkt nach der kommt:<br />

SELECT INTO <br />

FROM ;<br />

Die Liste der Variablen muss von Anzahl und Typ her zur Spaltenliste passen.<br />

In ähnlicher Weise kann auch das Ergebnis e<strong>in</strong>er Prozedur übernommen und<br />

<strong>in</strong> der aktuellen Rout<strong>in</strong>e verarbeitet werden:<br />

339


<strong>SQL</strong>-Programmierung<br />

1. Deklaration der Übergabevariablen:<br />

• direkt:<br />

;<br />

• oder unter Bezug auf den Typ e<strong>in</strong>er def<strong>in</strong>ierten Variablen:<br />

;<br />

2. Aufruf der Prozedur:<br />

( , );<br />

3. Weiterarbeiten mit den Ausgabeparametern aus der Prozedur<br />

Jede hier genannte Variable muss (<strong>in</strong> Reihenfolge und Typ) e<strong>in</strong>em der <br />

der Prozedur entsprechen.<br />

30.6.4. Verzweigungen<br />

Oracle kennt zwei Arten, um auf unterschiedliche Situationen zu reagieren:<br />

Die IF-Abfrage steuert den Ablauf nach Bed<strong>in</strong>gungen:Verzweigung<br />

IF THEN<br />

BEGIN END<br />

[ ELSE<br />

BEGIN END<br />

]<br />

END IF<br />

Diese Abfrage sieht so aus:<br />

• Bei handelt es sich um e<strong>in</strong>e e<strong>in</strong>fache Prüfung, die nicht mit AND<br />

oder OR erweitert werden kann.<br />

• Der ELSE-Zweig ist optional. Es ist auch möglich, dass nur der IF-THEN-<br />

Abschnitt ausgeführt werden muss.<br />

• Durch ELSIF-Zweige s<strong>in</strong>d auch Verschachtelungen möglich.<br />

• Die Verzweigung wird mit END IF abgeschlossen.<br />

Die CASE-Prüfung entspricht der Fallunterscheidung aus dem Kapitel Nützlichen<br />

Erweiterungen:<br />

-- Variante 1<br />

CASE <br />

WHEN THEN BEGIN END<br />

[ WHEN THEN BEGIN END ] /* usw. */<br />

[ ELSE BEGIN END ]<br />

END CASE<br />

340


<strong>SQL</strong>-Programmierung mit Oracle<br />

-- Variante 2<br />

CASE<br />

WHEN THEN BEGIN END<br />

[ WHEN THEN BEGIN END ] /* usw. */<br />

[ ELSE BEGIN END ]<br />

END CASE<br />

Bei dieser Prüfung gelten die gleichen Prüfungen wie bei der Fallunterscheidung<br />

mit folgenden Abweichungen:<br />

• Im ELSE-Zweig darf es ke<strong>in</strong>e NULL-Anweisung geben.<br />

• Die Verzweigung muss mit END CASE abgeschlossen werden.<br />

30.6.5. Schleifen<br />

Oracle kennt mehrere Arten von Schleifen.<br />

Die LOOP-Schleife arbeitet ohne Bed<strong>in</strong>gung am Anfang oder Ende und wird solange<br />

durchlaufen, bis − vor allem aufgrund e<strong>in</strong>er „<strong>in</strong>neren“ Bed<strong>in</strong>gung − die<br />

EXIT-Anweisung getroffen wird.<br />

LOOP<br />

BEGIN END<br />

END LOOP ;<br />

Oracle-Quelltext<br />

DECLARE x NUMBER := 1;<br />

BEGIN<br />

LOOP<br />

X := X + 1;<br />

EXIT WHEN x > 10;<br />

END LOOP;<br />

END;<br />

Die WHILE-Schleife prüft e<strong>in</strong>e Bed<strong>in</strong>gung und wird so lange durchlaufen, wie<br />

diese Bed<strong>in</strong>gung TRUE ist:<br />

WHILE LOOP<br />

BEGIN END<br />

END LOOP;<br />

Die wird jeweils am Anfang e<strong>in</strong>es Durchgangs geprüft. Wenn ihr<br />

Wert von Anfang an FALSE ist, wird die Schleife überhaupt nicht durchlaufen.<br />

Die FOR-Schleife durchläuft e<strong>in</strong>e Liste von Werten von Anfang bis Ende:<br />

341


<strong>SQL</strong>-Programmierung<br />

FOR IN LOOP<br />

BEGIN END<br />

END LOOP;<br />

Bei der FOR-Schleife wird der Wert der „Laufvariablen“ am Beg<strong>in</strong>n jedes weiteren<br />

Durchlaufs automatisch erhöht; bei LOOP und WHILE müssen Sie sich selbst<br />

um die Änderung e<strong>in</strong>er solchen Variablen kümmern.<br />

Um alle Datensätze e<strong>in</strong>er Ergebnismenge, also e<strong>in</strong>es SELECT-Befehls zu durchlaufen,<br />

wird e<strong>in</strong> CURSOR benötigt. Das wird <strong>in</strong> diesem Buch nicht behandelt.<br />

30.6.6. Zulässige Befehle<br />

Neben Anweisungen und den DML-Befehlen s<strong>in</strong>d auch TCL-Befehle (Steuerung<br />

von Transaktionen) sowie die Sperre von Tabellen durch LOCK TABLE möglich.<br />

30.6.7. Anweisungen begrenzen<br />

Jede e<strong>in</strong>zelne Anweisung <strong>in</strong>nerhalb e<strong>in</strong>er Rout<strong>in</strong>e und jeder <strong>SQL</strong>-Befehl müssen<br />

mit e<strong>in</strong>em Semikolon abgeschlossen werden, auch das abschließende END. Das<br />

ist ke<strong>in</strong> Problem, wenn nur e<strong>in</strong> e<strong>in</strong>zelner CREATE PROCEDURE o. ä. ausgeführt<br />

werden soll. Andernfalls folgt <strong>in</strong> e<strong>in</strong>er eigenen Zeile e<strong>in</strong> e<strong>in</strong>zelner Schrägstrich:<br />

BEGIN<br />

<br />

END;<br />

/<br />

30.7. Zusammenfassung<br />

In diesem Kapitel wurden die wichtigsten Bestandteile besprochen, mit denen<br />

<strong>SQL</strong>-Befehle <strong>in</strong> eigenen Funktionen, <strong>in</strong> gespeicherten Prozeduren oder <strong>in</strong> Triggern<br />

verarbeitet werden:<br />

• Deklaration von Parametern und Variablen<br />

• Verwendung von Parametern und Variablen<br />

• Verzweigungen mit IF u. a. sowie Schleifen<br />

• Zulässigkeit von DML- und DDL-Befehlen<br />

• Trennung zwischen e<strong>in</strong>er Rout<strong>in</strong>e <strong>in</strong>sgesamt und e<strong>in</strong>er e<strong>in</strong>zelnen Anweisung<br />

Bei allen E<strong>in</strong>zelheiten müssen die Besonderheiten des DBMS beachtet werden.<br />

342


30.8. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 344.<br />

Übung 1 – Erklärungen<br />

1. Erläutern Sie den Zweck e<strong>in</strong>er (benutzerdef<strong>in</strong>ierten) Funktion.<br />

2. Erläutern Sie den Zweck e<strong>in</strong>er (gespeicherten) Prozedur.<br />

3. Erläutern Sie den Zweck e<strong>in</strong>es Triggers.<br />

4. Wor<strong>in</strong> unterscheiden sich „Eigene Funktionen“ und Prozeduren?<br />

5. Wor<strong>in</strong> unterscheiden sich Prozeduren und Trigger?<br />

Übung 2 – Der Kopf e<strong>in</strong>er Rout<strong>in</strong>e<br />

Übungen<br />

Erläutern Sie die folgenden Bestandteile aus dem „Kopf“ (header) e<strong>in</strong>er Rout<strong>in</strong>e,<br />

d. h. e<strong>in</strong>er Funktion oder e<strong>in</strong>er Prozedur. Erwähnen Sie auch, was zu diesen<br />

Bestandteilen gehört.<br />

1. CREATE OR ALTER bzw. CREATE OR REPLACE<br />

2. <br />

3. E<strong>in</strong>gabe-Parameter<br />

4. Ausgabe-Parameter<br />

5. RETURNS bzw. RETURN<br />

Übung 3 – Der Rumpf e<strong>in</strong>er Rout<strong>in</strong>e<br />

Erläutern Sie die folgenden Bestandteile aus dem „Rumpf“ (body) e<strong>in</strong>er Rout<strong>in</strong>e,<br />

d. h. e<strong>in</strong>er Funktion oder e<strong>in</strong>er Prozedur. Erwähnen Sie auch, was zu diesen Bestandteilen<br />

gehört; die Antworten können sehr knapp ausfallen, weil sie sich <strong>in</strong><br />

Abhängigkeit vom DBMS sehr unterscheiden müssten.<br />

1. BEGIN und END am Anfang und Ende des Rumpfes<br />

2. BEGIN und END <strong>in</strong>nerhalb des Rumpfes<br />

3. Variablen<br />

4. Verzweigungen<br />

5. Schleifen<br />

6. Zuweisung von Werten<br />

343


<strong>SQL</strong>-Programmierung<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 343.<br />

Lösung zu Übung 1 – Erklärungen<br />

1. E<strong>in</strong>e benutzerdef<strong>in</strong>ierte Funktion ist e<strong>in</strong>e Ergänzung zu den <strong>in</strong>ternen<br />

Funktionen des DBMS und liefert immer e<strong>in</strong>en bestimmten Wert zurück.<br />

2. E<strong>in</strong>e gespeicherte Prozedur (StoredProcedure, SP) ist e<strong>in</strong> Arbeitsablauf, der<br />

fest <strong>in</strong>nerhalb der Datenbank gespeichert ist und Aufgaben ausführt, die<br />

<strong>in</strong>nerhalb der Datenbank erledigt werden sollen – im Wesentlichen ohne<br />

„Kommunikation“ mit dem Benutzer bzw. dem auslösenden Programm.<br />

3. E<strong>in</strong> Trigger ist e<strong>in</strong> Arbeitsablauf, der fest <strong>in</strong>nerhalb der Datenbank gespeichert<br />

ist und Aufgaben ausführt, die automatisch beim Speichern <strong>in</strong>nerhalb<br />

der Datenbank erledigt werden sollen.<br />

4. E<strong>in</strong>e Funktion liefert genau e<strong>in</strong>en Wert zurück, der durch RETURNS festgelegt<br />

wird. Prozeduren gibt es mit und ohne Rückgabewert.<br />

5. E<strong>in</strong> Trigger wird automatisch bei e<strong>in</strong>em Speichern-Befehl ausgeführt, und<br />

es gibt ke<strong>in</strong>en Rückgabewert. E<strong>in</strong>e Prozedur wird bewusst aufgerufen, und<br />

sie kann Rückgabewerte liefern.<br />

Lösung zu Übung 2 – Der Kopf e<strong>in</strong>er Rout<strong>in</strong>e<br />

344<br />

1. Dies def<strong>in</strong>iert e<strong>in</strong>e Rout<strong>in</strong>e. Dazu gehören e<strong>in</strong>er der Begriffe FUNCTION<br />

und PROCEDURE sowie der Name der Rout<strong>in</strong>e.<br />

2. Die Parameterliste enthält diejenigen Parameter, die beim Aufruf an<br />

die Rout<strong>in</strong>e übergeben werden (E<strong>in</strong>gabe-Parameter), sowie diejenigen,<br />

die nach Erledigung an den Aufrufer zurückgegeben werden (Ausgabe-<br />

Parameter). Wie E<strong>in</strong>gabe- und Ausgabe-Parameter gekennzeichnet bzw.<br />

unterschieden werden, hängt vom DBMS ab.<br />

3. Alle E<strong>in</strong>gabe-Parameter s<strong>in</strong>d (durch Komma getrennt) aufzuführen; die<br />

Reihenfolge muss beim Aufruf beachtet werden. Zu jedem Parameter gehören<br />

Name und Datentyp; unter Umständen ist e<strong>in</strong> Standardwert möglich.<br />

4. Alle Ausgabe-Parameter s<strong>in</strong>d (durch Komma getrennt) aufzuführen; die<br />

Reihenfolge muss bei der Auswertung im aufrufenden Programm o. ä. beachtet<br />

werden. Zu jedem Parameter gehören Name und Datentyp; unter<br />

Umständen ist e<strong>in</strong> Standardwert möglich.<br />

5. RETURNS bzw. RETURN gibt bei Funktionen den Ausgabe-Parameter, d. h.<br />

das Ergebnis der Funktion an. Nur Firebird benutzt dies, um auch die<br />

Ausgabe-Parameter e<strong>in</strong>er Prozedur anzugeben.


Lösung zu Übung 3 – Der Rumpf e<strong>in</strong>er Rout<strong>in</strong>e<br />

Siehe auch<br />

1. Dies beschreibt Anfang und Ende des gesamten Rumpfes. Wenn der Rumpf<br />

nur e<strong>in</strong>en e<strong>in</strong>zelnen Befehl enthält, kann es entfallen.<br />

2. Dies begrenzt e<strong>in</strong>zelne Abschnitte <strong>in</strong>nerhalb des Rumpfes, vor allem<br />

bei Schleifen und IF-Verzweigungen. Bei e<strong>in</strong>em e<strong>in</strong>zelnen Befehl kann<br />

BEGIN/END entfallen; die Verwendung wird aber stets empfohlen.<br />

3. Dies def<strong>in</strong>iert Werte, die (nur) <strong>in</strong>nerhalb der Rout<strong>in</strong>e benutzt werden. Sie<br />

müssen mit Namen und Datentyp ausdrücklich deklariert werden.<br />

4. Mit IF-ELSE o. ä. können – abhängig von Bed<strong>in</strong>gungen – unterschiedliche<br />

Befehle ausgeführt werden.<br />

5. Mit WHILE, FOR o. a. können e<strong>in</strong>zelne Befehle oder Abschnitte von Befehlen<br />

wiederholt ausgeführt werden. Wie oft bzw. wie lange die Schleife<br />

durchlaufen wird, hängt von der Art der Schleife (unterschiedlich nach<br />

DBMS) und Bed<strong>in</strong>gungen ab.<br />

6. Den Variablen und Parametern können Werte zugewiesen werden, und<br />

zwar durch das Gleichheitszeichen mit konkreten Angaben oder als Ergebnis<br />

von Funktionen, Prozeduren oder SELECT-Befehlen.<br />

30.9. Siehe auch<br />

Verschiedene E<strong>in</strong>zelheiten stehen <strong>in</strong> den folgenden Kapiteln:<br />

• <strong>SQL</strong>-Befehle 4 mit Erläuterung zu <strong>SQL</strong>-Ausdrücken<br />

• Funktionen 5 und Funktionen (2) 6<br />

• Nützliche Erweiterungen 7<br />

Zur <strong>SQL</strong>-Programmierung mit Oracle gibt es das Wikibook PL/<strong>SQL</strong> 8 , das noch<br />

unvollständig ist.<br />

4 Kapitel 7 auf Seite 47<br />

5 Kapitel 14 auf Seite 109<br />

6 Kapitel 16 auf Seite 141<br />

7 Kapitel 23 auf Seite 213<br />

8 http://de.wikibooks.<strong>org</strong>/wiki/PL/<strong>SQL</strong><br />

345


31. Eigene Funktionen<br />

31.1. Funktion def<strong>in</strong>ieren . . . . . . . . . . . . . . . . . . . . . . . . . . 347<br />

31.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349<br />

31.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 351<br />

31.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351<br />

31.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356<br />

Auch wenn e<strong>in</strong> DBMS viele Funktionen zur Verfügung stellt, kommt man <strong>in</strong> der<br />

Praxis immer wieder e<strong>in</strong>mal zu weiteren Wünschen und Anforderungen. Dafür<br />

kann e<strong>in</strong> Benutzer eigene Funktionen def<strong>in</strong>ieren und dem DBMS bekannt geben<br />

oder <strong>in</strong> e<strong>in</strong>er Datenbank speichern. E<strong>in</strong>mal def<strong>in</strong>iert, erspart dies künftig,<br />

die gleiche Rout<strong>in</strong>e immer neu zu erstellen; stattdessen wird die Funktion aufgerufen<br />

und liefert den Rückgabewert.<br />

Firebird hat solche Funktionen erst für Version 3 angekündigt. Zurzeit kann e<strong>in</strong>e<br />

Funktion nur als UDF (user-def<strong>in</strong>ed function) aus e<strong>in</strong>er externen DLL, die mit e<strong>in</strong>er<br />

Programmiersprache erstellt wurde, e<strong>in</strong>gebunden werden.<br />

Bitte haben Sie Nachsicht: Wegen der vielen Varianten bei den DBMS wurde e<strong>in</strong><br />

Teil der H<strong>in</strong>weise und Beispiele auch <strong>in</strong> diesem Kapitel nur nach der Dokumentation<br />

verfasst und nicht <strong>in</strong> der Praxis umgesetzt.<br />

31.1. Funktion def<strong>in</strong>ieren<br />

Benutzerdef<strong>in</strong>ierte Funktionen geben (ebenso wie die eigenen Funktionen des<br />

DBMS) genau e<strong>in</strong>en Wert zurück, s<strong>in</strong>d also Skalarfunktionen.<br />

31.1.1. Funktion erstellen<br />

Mit CREATE FUNCTION <strong>in</strong> der folgenden Syntax (vere<strong>in</strong>fachte Version des <strong>SQL</strong>-<br />

Standards) wird e<strong>in</strong>e Funktion def<strong>in</strong>iert:<br />

347


Eigene Funktionen<br />

CREATE FUNCTION <br />

( [ ] )<br />

RETURNS <br />

[ ]<br />

<br />

Der der Funktion muss <strong>in</strong>nerhalb e<strong>in</strong>es gewissen Bereichs<br />

(„Schema“ genannt) e<strong>in</strong>deutig se<strong>in</strong> und darf auch nicht als Name e<strong>in</strong>er Prozedur<br />

verwendet werden. Teilweise wird verlangt, dass der Name der Datenbank<br />

ausdrücklich angegeben wird. Es empfiehlt sich, ke<strong>in</strong>en Namen e<strong>in</strong>er e<strong>in</strong>gebauten<br />

Funktion zu verwenden.<br />

Die Klammern s<strong>in</strong>d erforderlich und kennzeichnen e<strong>in</strong>e Rout<strong>in</strong>e. E<strong>in</strong>gabeparameter<br />

können vorhanden se<strong>in</strong>, müssen es aber nicht. Mehrere Parameter werden<br />

durch Kommata getrennt, der e<strong>in</strong>zelne wird wie folgt def<strong>in</strong>iert:<br />

[ IN ] <br />

Der muss <strong>in</strong>nerhalb der Funktion e<strong>in</strong>deutig se<strong>in</strong>. Der Zusatz<br />

IN kann entfallen, weil es bei Funktionen nur E<strong>in</strong>gabe-Parameter (ke<strong>in</strong>e<br />

Ausgabe-Parameter) gibt.<br />

Der RETURNS-Parameter ist wesentlich und kennzeichnet e<strong>in</strong>e Funktion.<br />

Der sowohl für den RETURNS-Parameter als auch für die E<strong>in</strong>gabe-<br />

Parameter muss e<strong>in</strong>er der <strong>SQL</strong>-Datentypen des DBMS se<strong>in</strong>.<br />

Für gibt es diverse Festlegungen, wie die Funktion arbeiten<br />

soll, z. B. durch LANGUAGE mit der benutzten Programmiersprache.<br />

– der Rumpf – enthält den eigentlichen Arbeitsablauf, also die<br />

Anweisungen, die <strong>in</strong>nerhalb der Rout<strong>in</strong>e ausgeführt werden sollen. Bei diesen<br />

Befehlen handelt es sich um „normale“ <strong>SQL</strong>-Befehle, die mit Bestandteilen<br />

der <strong>SQL</strong>-Programmiersprache verbunden werden. Zum Semikolon, das den Abschluss<br />

des CREATE-Befehls darstellen sollte, aber <strong>in</strong>nerhalb des Inhalts bei jeder<br />

e<strong>in</strong>zelnen Anweisung steht, beachten Sie bitte auch die DBMS-spezifischen<br />

H<strong>in</strong>weise zur <strong>SQL</strong>-Programmierung unter Anweisungen begrenzen 1 .<br />

Bei e<strong>in</strong>er Funktion muss − beispielsweise durch e<strong>in</strong>e RETURN-Anweisung − der<br />

gesuchte Rückgabewert ausdrücklich festgelegt werden. In der Regel werden die<br />

Befehle durch BEGIN. . . END zusammengefasst; bei e<strong>in</strong>em e<strong>in</strong>zelnen Befehl ist<br />

dies nicht erforderlich.<br />

1 Kapitel 30 auf Seite 323<br />

348


31.1.2. Funktion ausführen<br />

Beispiele<br />

E<strong>in</strong>e benutzerdef<strong>in</strong>ierte Funktion kann wie jede <strong>in</strong>terne Funktion des DBMS benutzt<br />

werden. Sie kann an jeder Stelle benutzt werden, an der e<strong>in</strong> e<strong>in</strong>zelner Wert<br />

erwartet wird, wie <strong>in</strong> den Beispielen zu sehen ist.<br />

31.1.3. Funktion ändern oder löschen<br />

Mit ALTER FUNCTION wird die Def<strong>in</strong>ition e<strong>in</strong>er Funktion geändert. Dafür gilt<br />

die gleiche Syntax:<br />

ALTER FUNCTION <br />

( [ ] )<br />

RETURNS <br />

[ ]<br />

<br />

Mit DROP FUNCTION wird die Def<strong>in</strong>ition e<strong>in</strong>er Funktion gelöscht.<br />

DROP FUNCTION ;<br />

31.2. Beispiele<br />

31.2.1. E<strong>in</strong>fache Bestimmung e<strong>in</strong>es Wertes<br />

Das folgende Beispiel erstellt den Rückgabewert direkt aus der E<strong>in</strong>gabe.<br />

Aufgabe: Zur Begrüßung wird e<strong>in</strong> Name mit e<strong>in</strong>em Standardtext, der von der<br />

Tageszeit abhängt, verbunden.<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE FUNCTION Hello (s CHAR(20))<br />

RETURNS CHAR(50)<br />

RETURN WHEN<br />

CASE CURRENT_TIME < ’12:00’ THEN ’Guten M<strong>org</strong>en, ’ + s + ’!’<br />

CASE CURRENT_TIME < ’18:00’ THEN ’Guten Tag, ’ + s + ’!’<br />

ELSE ’Guten Abend, ’ + s + ’!’<br />

END;<br />

Bei diesem Beispiel kann auf BEGIN. . . END verzichtet werden, weil der „Rumpf“<br />

der Funktion nur e<strong>in</strong>e e<strong>in</strong>zige Anweisung enthält, nämlich RETURN unter Benutzung<br />

des WHEN-Ausdrucks.<br />

349


Eigene Funktionen<br />

E<strong>in</strong>e solche Funktion wird wie jede e<strong>in</strong>gebaute Funktion benutzt.<br />

SELECT Hello(’Juetho’) [from fiktiv];<br />

’Guten Abend, Juetho!’<br />

31.2.2. Text <strong>in</strong> Anführungszeichen e<strong>in</strong>schließen<br />

Im folgenden Beispiel werden mehr Schritte bis zum RETURN-Wert benötigt.<br />

Der Nutzen liegt im Export von Daten aus e<strong>in</strong>em System zu e<strong>in</strong>em anderen.<br />

Aufgabe: E<strong>in</strong> gegebener Text soll <strong>in</strong> Anführungszeichen e<strong>in</strong>geschlossen werden;<br />

Gänsefüßchen <strong>in</strong>nerhalb des Textes müssen verdoppelt werden.<br />

My<strong>SQL</strong>-Quelltext<br />

CREATE FUNCTION Quot<strong>in</strong>g<br />

( <strong>in</strong>str<strong>in</strong>g VARCHAR(80) )<br />

RETURNS ( VARCHAR(100) )<br />

AS<br />

BEGIN<br />

IF (<strong>in</strong>str<strong>in</strong>g CONTAINING(’"’)) THEN BEGIN<br />

<strong>in</strong>str<strong>in</strong>g = REPLACE( <strong>in</strong>str<strong>in</strong>g, ’"’, ’""’ );<br />

END<br />

RETURN CONCAT( ’"’, <strong>in</strong>str<strong>in</strong>g, ’"’);<br />

END<br />

Diese Funktion kann direkt aufgerufen werden:<br />

SELECT Quot<strong>in</strong>g(’Schulze’), Quot<strong>in</strong>g(’Restaurant "Abendrot"’) [from fiktiv];<br />

"Schulze" "Restaurant ""Abendrot"""<br />

31.2.3. Anzahl der Mitarbeiter e<strong>in</strong>er Abteilung<br />

Bei der folgenden Funktion werden zunächst weitere Informationen benötigt.<br />

Aufgabe: Suche zu e<strong>in</strong>er Abteilung gemäß Kuerzel die Anzahl der Mitarbeiter.<br />

350<br />

Oracle-Quelltext<br />

CREATE OR REPLACE FUNCTION AnzahlMitarbeiter<br />

( abt CHAR(4) )<br />

RETURN ( INTEGER )<br />

AS


anzahl INTEGER;<br />

BEGIN<br />

SELECT COUNT(*) INTO anzahl<br />

FROM Mitarbeiter mi<br />

JOIN Abteilung ab ON ab.ID = mi.Abteilung_ID<br />

WHERE ab.Kuerzel = abt;<br />

RETURN anzahl;<br />

END<br />

Zusammenfassung<br />

Damit erhalten wir die Anzahl der Mitarbeiter e<strong>in</strong>er bestimmten Abteilung:<br />

SELECT AnzahlMitarbeiter(’Vert’) [from fiktiv] ;<br />

AnzahlMitarbeiter : 4<br />

31.3. Zusammenfassung<br />

In diesem Kapitel lernten wir die Grundregeln für das Erstellen eigener Funktionen<br />

kennen:<br />

• Benutzerdef<strong>in</strong>ierte Funktionen s<strong>in</strong>d immer Skalarfunktionen, die genau e<strong>in</strong>en<br />

Wert zurückgeben.<br />

• Der Datentyp des Rückgabewerts ist <strong>in</strong> der RETURNS-Klausel anzugeben, der<br />

Wert selbst durch e<strong>in</strong>e RETURN-Anweisung.<br />

• Komplexe Maßnahmen müssen <strong>in</strong> BEGIN. . . END e<strong>in</strong>geschlossen werden; e<strong>in</strong>e<br />

Funktion kann aber auch nur aus der RETURN-Anweisung bestehen.<br />

31.4. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 353.<br />

Wenn bei den folgenden Übungen der Begriff „Funktion“ ohne nähere Erklärung<br />

verwendet wird, ist immer e<strong>in</strong>e „Eigene Funktion“, d. h. e<strong>in</strong>e benutzerdef<strong>in</strong>ierte<br />

Funktion geme<strong>in</strong>t.<br />

Unter „Skizzieren“ (Übung 3, 5) ist geme<strong>in</strong>t: E<strong>in</strong>gabe- und Ausgabeparameter sowie<br />

Variablen mit s<strong>in</strong>nvollen Namen und Datentypen benennen, Arbeitsablauf<br />

möglichst genau mit Pseudo-Code oder normaler Sprache beschreiben.<br />

Tipp: Die Parameter ergeben sich <strong>in</strong> der Regel aus der Aufgabenstellung. Aus der<br />

Überlegung zum Arbeitsablauf folgen die Variablen.<br />

351


Eigene Funktionen<br />

Übung 1 – Def<strong>in</strong>ition e<strong>in</strong>er Funktion<br />

Welche der folgenden Aussagen s<strong>in</strong>d richtig, welche s<strong>in</strong>d falsch?<br />

1. E<strong>in</strong>e Funktion dient zur Bestimmung genau e<strong>in</strong>es Wertes.<br />

2. E<strong>in</strong>e Funktion kann höchstens e<strong>in</strong>en E<strong>in</strong>gabe-Parameter enthalten.<br />

3. E<strong>in</strong>e Funktion kann mehrere Ausgabe-Parameter enthalten.<br />

4. E<strong>in</strong>e Funktion kann e<strong>in</strong>en oder mehrere <strong>SQL</strong>-Befehle verwenden.<br />

5. Der Rückgabewert wird mit e<strong>in</strong>er RETURN-Anweisung angegeben.<br />

6. Der Datentyp des Rückgabewerts ergibt sich automatisch aus der<br />

RETURN-Anweisung.<br />

7. Die Def<strong>in</strong>ition e<strong>in</strong>er Funktion kann nicht geändert werden; sie kann nur<br />

gelöscht und mit anderem Inhalt neu aufgenommen werden.<br />

Übung 2 – Def<strong>in</strong>ition e<strong>in</strong>er Funktion prüfen<br />

Nennen Sie <strong>in</strong> der folgenden Def<strong>in</strong>ition Punkte, die unabhängig vom <strong>SQL</strong>-<br />

Dialekt falsch s<strong>in</strong>d. (Je nach DBMS können noch andere Punkte falsch se<strong>in</strong>, danach<br />

wird aber nicht gefragt.) Die Funktion soll folgende Aufgabe erledigen:<br />

• Es wird e<strong>in</strong>e Telefonnummer <strong>in</strong> beliebiger Formatierung als E<strong>in</strong>gabe-<br />

Parameter übergeben, z. B. als '(0049 / 030) 99 68-32 53'.<br />

• Das Ergebnis soll diese Nummer ganz ohne Trennzeichen zurückgeben.<br />

• Sofern die Ländervorwahl enthalten ist, soll das führende '00' durch '+' ersetzt<br />

werden.<br />

352<br />

1. CREATE Telefon_Standard AS Function<br />

2. ( IN E<strong>in</strong>gabe VARCHAR(20),<br />

3. OUT Ausgabe VARCHAR(20) )<br />

4. AS<br />

5. DECLARE INTEGER x1, i1;<br />

6. DECLARE CHAR c1;<br />

7. BEGIN<br />

8. -- Rückgabewert vorbelegen<br />

9. Ausgabe = ”;<br />

10. -- Länge der Schleife bestimmen<br />

11. i1 = CHAR_LENGTH(E<strong>in</strong>gabe);<br />

12. -- die Schleife für jedes vorhandene Zeichen verarbeiten<br />

13. WHILE (i1 < x1)<br />

14. DO<br />

15. -- das nächste Zeichen auslesen<br />

16. x1 = x1 + 1<br />

17. c1 = SUBSTRING(E<strong>in</strong>gabe FROM x1 FOR 1);<br />

18. -- dieses Zeichen prüfen: ist es e<strong>in</strong>e Ziffer<br />

19. if (c1 >= ’0’ and (c1


23. END<br />

24. END<br />

25.<br />

26. -- Zusatzprüfung: ’00’ durch ’+’ ersetzen<br />

27. IF (Ausgabe STARTS WITH ’00’)<br />

28. THEN<br />

29. Ausgabe = ’+’ || SUBSTRING(Ausgabe FROM 3 FOR 20)<br />

30. END<br />

31.<br />

32. END<br />

Übung 3 – Funktion DateToStr<strong>in</strong>g erstellen<br />

Übungen<br />

Skizzieren Sie e<strong>in</strong>e Funktion DateToStr<strong>in</strong>g: Aus e<strong>in</strong>em Date- oder DateTime-<br />

Wert als E<strong>in</strong>gabe-Parameter soll e<strong>in</strong>e Zeichenkette mit genau 8 Zeichen der Form<br />

'JJJJMMTT' gemacht werden. Benutzen Sie dafür nur die EXTRACT- und LPAD-<br />

Funktionen sowie die „Addition“ von Zeichenketten; Sie können davon ausgehen,<br />

dass e<strong>in</strong>e Zahl bei LPAD korrekt als Zeichenkette verstanden wird.<br />

Übung 4 – Funktion DateToStr<strong>in</strong>g erstellen<br />

Erstellen Sie die Funktion aus der vorigen Übung.<br />

Übung 5 – Funktion Str<strong>in</strong>g_Insert erstellen<br />

Skizzieren Sie e<strong>in</strong>e Funktion Str<strong>in</strong>g_Insert: In e<strong>in</strong>e Zeichenkette soll an e<strong>in</strong>er bestimmten<br />

Position e<strong>in</strong>e zweite Zeichenkette e<strong>in</strong>gefügt werden; alle Zeichenketten<br />

sollen max. 80 Zeichen lang se<strong>in</strong>. Benutzen Sie dafür nur SUBSTRING und<br />

CAST sowie die „Addition“ von Zeichenketten; stellen Sie außerdem sicher, dass<br />

das Ergebnis die mögliche Maximallänge nicht überschreitet und schneiden Sie<br />

ggf. ab.<br />

Übung 6 – Funktion Str<strong>in</strong>g_Insert erstellen<br />

Erstellen Sie die Funktion aus der vorigen Übung.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 351.<br />

Lösung zu Übung 1 – Def<strong>in</strong>ition e<strong>in</strong>er Funktion<br />

Die Aussagen 1, 4, 5 s<strong>in</strong>d richtig. Die Aussagen 2, 3, 6, 7 s<strong>in</strong>d falsch.<br />

353


Eigene Funktionen<br />

Lösung zu Übung 2 – Def<strong>in</strong>ition e<strong>in</strong>er Funktion prüfen<br />

• Zeile 1: Die Def<strong>in</strong>ition erfolgt durch CREATE FUNCTION .<br />

• Zeile 2: Bei e<strong>in</strong>er Funktion gehören <strong>in</strong> die Klammern nur die E<strong>in</strong>gabe-<br />

Parameter.<br />

• Zeile 3: Der Ausgabe-Parameter muss mit RETURNS festgelegt werden.<br />

• Zeile 4: Bei jeder Variablen ist zuerst der Name, dann der Typ anzugeben.<br />

• Zeile 12: Der Startwert für die Schleifenvariable x1 = 0 fehlt.<br />

• Zeile 14: Die Schleife verarbeitet mehrere Befehle, muss also h<strong>in</strong>ter DO auch<br />

e<strong>in</strong> BEGIN erhalten.<br />

• Zeile 16 und 29: Jeder e<strong>in</strong>zelne Befehl muss mit e<strong>in</strong>em Semikolon abgeschlossen<br />

werden.<br />

• Zeile 19: Die Klammern s<strong>in</strong>d falsch gesetzt; am e<strong>in</strong>fachsten wird die zweite öffnende<br />

Klammer entfernt.<br />

• Zeile 20/23: Entweder es wird THEN BEGIN...END verwendet, oder das END<br />

wird gestrichen.<br />

• Zeile 28/30 enthält den gleichen Fehler.<br />

• Zeile 31: Es fehlt die RETURN-Anweisung, mit der der erzeugte Ausgabe-Str<strong>in</strong>g<br />

zurückgegeben wird.<br />

Lösung zu Übung 3 – Funktion DateToStr<strong>in</strong>g erstellen<br />

• E<strong>in</strong>gabe-Parameter: e<strong>in</strong> DateTime-Wert E<strong>in</strong>gabe<br />

• Ausgabe-Parameter: e<strong>in</strong> CHAR(8) Ausgabe<br />

• drei INTEGER-Variable: jjjj, mm, tt<br />

• mit 3x EXTRACT werden die Bestandteile jjjj, mm, tt herausgeholt<br />

• diese werden mit LPAD auf CHAR(2) für Monat und Tag gesetzt<br />

• schließlich werden diese Werte per „Addition“ verknüpft und<br />

• als Ergebnis zurückgegeben<br />

Lösung zu Übung 4 – Funktion DateToStr<strong>in</strong>g erstellen<br />

354<br />

CREATE FUNCTION DateToStr<strong>in</strong>g<br />

( E<strong>in</strong>gabe DATE )<br />

RETURNS ( Ausgabe CHAR(8) )<br />

AS<br />

DECLARE VARIABLE jjjj <strong>in</strong>t;<br />

DECLARE VARIABLE mm <strong>in</strong>t;<br />

DECLARE VARIABLE tt <strong>in</strong>t;<br />

BEGIN<br />

jjjj = EXTRACT(YEAR FROM E<strong>in</strong>gabe);<br />

mm = EXTRACT(MONTH FROM E<strong>in</strong>gabe);


tt = EXTRACT(DAY FROM E<strong>in</strong>gabe);<br />

Ausgabe = jjjj || LPAD(mm, 2, ’0’) || LPAD(tt, 2, ’0’);<br />

RETURN Ausgabe;<br />

END<br />

Lösung zu Übung 5 – Funktion Str<strong>in</strong>g_Insert erstellen<br />

Übungen<br />

• E<strong>in</strong>gabe-Parameter: Orig<strong>in</strong>al-Str<strong>in</strong>g E<strong>in</strong>gabe, pos als Position (der Name Position<br />

ist als <strong>in</strong>terne Funktion verboten), der Zusatz-Str<strong>in</strong>g part<br />

• Ausgabe-Parameter: die neue Zeichenkette Ausgabe<br />

• Variable: temp mit Platz für 160 Zeichen<br />

• es wird e<strong>in</strong>fach e<strong>in</strong>e neue Zeichenkette temp zusammengesetzt aus drei Teilen:<br />

• der Anfang von E<strong>in</strong>gabe mit pos Zeichen<br />

• dann der Zusatz-Str<strong>in</strong>g part<br />

• dann der Rest von E<strong>in</strong>gabe ab Position pos + 1<br />

• falls die Zeichenkette temp zu lang ist, werden mit SUBSTRING nur noch die<br />

ersten 80 Zeichen verwendet<br />

• das Ergebnis muss wegen der seltsamen Def<strong>in</strong>ition von SUBSTRING mit CAST<br />

auf VARCHAR(80) gebracht werden und<br />

• kann dann als Rückgabewert verwendet werden<br />

Lösung zu Übung 6 – Funktion Str<strong>in</strong>g_Insert erstellen<br />

CREATE FUNCTION Str<strong>in</strong>g_Insert<br />

( E<strong>in</strong>gabe VARCHAR(80), pos INTEGER, part VARCHAR(80) )<br />

RETURNS ( Ausgabe VARCHAR(80) )<br />

AS<br />

-- genügend Zwischenspeicher bereitstellen<br />

DECLARE VARIABLE temp VARCHAR(160);<br />

BEGIN<br />

-- Teil 1, dazu Zwischenteil, dann Teil 2<br />

temp = SUBSTRING( E<strong>in</strong>gabe FROM 1 FOR pos )<br />

|| part<br />

|| SUBSTRING( E<strong>in</strong>gabe FROM pos + 1 );<br />

-- auf jeden Fall auf die Maximallänge von 80 Zeichen br<strong>in</strong>gen<br />

IF (CHAR_LENGTH(temp) > 80) THEN<br />

Ausgabe = CAST( SUBSTRING(temp FROM 1 FOR 80) AS VARCHAR(80));<br />

ELSE<br />

Ausgabe = CAST( temp AS VARCHAR(80));<br />

RETURN Ausgabe;<br />

END<br />

355


Eigene Funktionen<br />

31.5. Siehe auch<br />

Verschiedene E<strong>in</strong>zelheiten werden <strong>in</strong> den folgenden Kapiteln behandelt:<br />

• <strong>SQL</strong>-Programmierung 2<br />

• Datentypen 3<br />

• Funktionen 4 und Funktionen (2) 5<br />

2 Kapitel 30 auf Seite 323<br />

3 Kapitel 13 auf Seite 97<br />

4 Kapitel 14 auf Seite 109<br />

5 Kapitel 16 auf Seite 141<br />

356


32. Prozeduren<br />

32.1. Die Def<strong>in</strong>ition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358<br />

32.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360<br />

32.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 370<br />

32.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370<br />

32.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375<br />

E<strong>in</strong>e Prozedur − gespeicherte Prozedur, engl. Stored Procedure (SP) − ist v<strong>org</strong>esehen<br />

für Arbeitsabläufe, die „immer wiederkehrende“ Arbeiten direkt <strong>in</strong>nerhalb<br />

der Datenbank ausführen sollen.<br />

Bitte haben Sie Nachsicht: Wegen der vielen Varianten bei den DBMS beschränkt<br />

sich dieses Kapitel bei den H<strong>in</strong>weisen und Beispielen auf Firebird. Zusammen mit<br />

den speziellen H<strong>in</strong>weisen zur <strong>SQL</strong>-Programmierung sollten sie problemlos an andere<br />

DBMS-Regeln angepasst werden können.<br />

Zur Verwendung von Prozeduren gibt es zwei gegensätzliche Standpunkte, die<br />

mit den Begriffen Fat Server und Fat Client zusammengefasst werden können:<br />

• Beim Fat Server wird so viel Funktionalität wie möglich <strong>in</strong> die (zentral gespeicherte)<br />

Datenbank gelegt.<br />

• Beim Fat Client ist die Datenbank nur für die Speicherung der Daten v<strong>org</strong>esehen;<br />

sämtliche Verarbeitung erfolgt auf dem Arbeitsplatzrechner.<br />

Der große Vorteil e<strong>in</strong>es Fat Server liegt dar<strong>in</strong>, dass Daten direkt <strong>in</strong> der Datenbank<br />

verarbeitet werden können, sofern ke<strong>in</strong>e Ansicht der Daten auf dem Arbeitsplatz<br />

benötigt wird. Es ist natürlich überflüssig, die Daten zuerst von der Datenbank<br />

zum Arbeitsplatz zu kopieren, dann automatisch zu verarbeiten und schließlich<br />

das Arbeitsergebnis (ohne manuelle Überprüfung) auf die Datenbank zurückzukopieren.<br />

E<strong>in</strong>facher ist es, alles <strong>in</strong> der Datenbank ausführen zu lassen.<br />

Der große Vorteil e<strong>in</strong>es Fat Client liegt dar<strong>in</strong>, dass das Client-Programm meistens<br />

schneller und e<strong>in</strong>facher geändert werden kann als die Arbeitsabläufe <strong>in</strong>nerhalb<br />

der Datenbank.<br />

In der Praxis ist e<strong>in</strong>e Mischung beider Verfahren am s<strong>in</strong>nvollsten. Zur Steuerung<br />

der Arbeitsabläufe <strong>in</strong> der Datenbank werden die gespeicherten Prozeduren verwendet;<br />

Beispiele dafür werden <strong>in</strong> diesem Abschnitt behandelt.<br />

357


Prozeduren<br />

32.1. Die Def<strong>in</strong>ition<br />

32.1.1. Prozedur erstellen<br />

Die Syntax für die Def<strong>in</strong>ition e<strong>in</strong>er Prozedur sieht so aus:<br />

CREATE OR ALTER PROCEDURE <br />

( [ ] )<br />

AS<br />

BEGIN<br />

[ ]<br />

<br />

END<br />

Notwendig s<strong>in</strong>d folgende Angaben:<br />

• neben dem Befehlsnamen CREATE PROCEDURE der Name der Prozedur<br />

• das Schlüsselwort AS als Zeichen für den Inhalt<br />

• die Schlüsselwörter BEGIN und END als Begrenzer für den Rumpf, also den<br />

eigentlichen Inhalt<br />

H<strong>in</strong>zu kommen die folgenden Angaben:<br />

• e<strong>in</strong>e Liste von Parametern, sofern Werte übergeben oder abgefragt werden<br />

• Bei den meisten DBMS werden E<strong>in</strong>gabe- und Ausgabe-Parameter durch<br />

Schlüsselwörter wie IN und OUT unterschieden, sie stehen dabei <strong>in</strong>nerhalb<br />

derselben Liste.<br />

• Bei Firebird enthält die Parameterliste nur die E<strong>in</strong>gabe-Parameter; die<br />

Ausgabe-Parameter stehen h<strong>in</strong>ter RETURNS <strong>in</strong> e<strong>in</strong>er eigenen Liste mit<br />

Klammern.<br />

• e<strong>in</strong>e Liste von Variablen, die <strong>in</strong>nerhalb der Prozedur verwendet werden; diese<br />

Liste steht je nach DBMS zwischen AS und BEGIN oder <strong>in</strong>nerhalb des Rumpfes<br />

• die Anweisungen, die <strong>in</strong>nerhalb der Prozedur auszuführen s<strong>in</strong>d<br />

Bei den Befehlen <strong>in</strong>nerhalb der Prozedur handelt es sich um „normale“ <strong>SQL</strong>-<br />

Befehle sowie um Bestandteile der <strong>SQL</strong>-Programmiersprache. Zum Semikolon,<br />

das den Abschluss des CREATE-Befehls darstellen sollte, aber <strong>in</strong>nerhalb des Inhalts<br />

bereits für jede e<strong>in</strong>zelne Anweisung benutzt wird, beachten Sie bitte auch<br />

die H<strong>in</strong>weise zur <strong>SQL</strong>-Programmierung unter Anweisungen begrenzen 1 .<br />

1 Kapitel 30 auf Seite 323<br />

358


32.1.2. Prozedur ausführen<br />

Die Def<strong>in</strong>ition<br />

Es gibt mehrere Verfahren, wie e<strong>in</strong>e Prozedur vom Nutzer, aus e<strong>in</strong>em Anwendungsprogramm<br />

oder aus e<strong>in</strong>er anderen Prozedur oder e<strong>in</strong>em Trigger heraus<br />

benutzt werden kann.<br />

Der direkte Weg geht bei Firebird über Execute Procedure:<br />

EXECUTE PROCEDURE [ ]<br />

[ RETURNING_VALUES ] ;<br />

Dem Namen der Prozedur folgen (soweit erforderlich) die E<strong>in</strong>gabe-Parameter;<br />

mehrere werden mit Komma getrennt. Sofern die Prozedur Werte zurückgibt,<br />

werden sie <strong>in</strong> e<strong>in</strong>e oder mehrere Variablen e<strong>in</strong>getragen, die dem Begriff<br />

RETURNING_VALUES folgen.<br />

Bei den anderen DBMS gehören zu EXECUTE (My<strong>SQL</strong>: CALL) nicht nur die<br />

E<strong>in</strong>gabe-, sondern auch die Ausgabe-Parameter. Beispiele stehen im Kapitel zur<br />

<strong>SQL</strong>-Programmierung bei den Erläuterungen des jeweiligen DBMS.<br />

E<strong>in</strong>e Prozedur kann auch mit Select Procedure wie e<strong>in</strong>e Abfrage verwendet werden.<br />

Dies ist vor allem dann erforderlich, wenn e<strong>in</strong>e Liste von Werten oder Datensätzen<br />

zurückgeliefert werden.<br />

SELECT * FROM [ ] ;<br />

Die Ausgabe-Parameter stehen dann <strong>in</strong> der Liste der Spalten, also <strong>in</strong> der Ergebnismenge<br />

des SELECT-Befehls.<br />

Man könnte auch an EXECUTE BLOCK denken, aber das passt hier überhaupt<br />

nicht. E<strong>in</strong> Block ist e<strong>in</strong>e Menge von Anweisungen, die e<strong>in</strong>malig benutzt werden,<br />

aber eben nicht <strong>in</strong> der Datenbank gespeichert werden. E<strong>in</strong> solcher Block ersetzt<br />

e<strong>in</strong>e gespeicherte Prozedur, wenn das Speichern überflüssig ist, und wird bei der<br />

<strong>SQL</strong>-Programmierung unter Rout<strong>in</strong>en ohne feste Speicherung beschrieben.<br />

32.1.3. Prozedur ändern oder löschen<br />

E<strong>in</strong>e Änderung e<strong>in</strong>er Prozedur ist nur möglich durch e<strong>in</strong>e vollständige Neudef<strong>in</strong>ition<br />

mit allen Bestandteilen. Deshalb wird es heutzutage fast immer durch<br />

CREATE OR ALTER zusammengefasst. Wenn dies nicht möglich ist, dann muss<br />

e<strong>in</strong> e<strong>in</strong>zelner ALTER-Befehl benutzt werden, der alle Bestandteile der obigen Erläuterungen<br />

enthält.<br />

359


Prozeduren<br />

Das Löschen geht wie üblich mit dem DROP-Befehl:<br />

DROP PROCEDURE ;<br />

32.2. Beispiele<br />

Um die Beispiele für e<strong>in</strong> anderes DBMS zu übertragen, s<strong>in</strong>d vor allem folgende<br />

Punkte zu ändern:<br />

• Die Ausgabe-Parameter folgen nicht nach RETURNS, sondern gehören <strong>in</strong> die<br />

Parameterliste.<br />

• Die Deklaration der Variablen ist anzupassen, ebenso die Kennzeichnung mit<br />

Doppelpunkt.<br />

• Schleifen und ähnliche Maßnahmen s<strong>in</strong>d umzuschreiben.<br />

• Dies gilt auch dafür, wie Werte − z. B. e<strong>in</strong>es SELECT − übernommen werden.<br />

32.2.1. Ersatz für e<strong>in</strong>e View mit Parametern<br />

Im Kapitel zu VIEWs wollten wir e<strong>in</strong>e Ansicht mit variabler Selektion erstellen,<br />

aber variable Bed<strong>in</strong>gungen waren nicht möglich. Mit e<strong>in</strong>er Prozedur f<strong>in</strong>den wir<br />

e<strong>in</strong>e passende Lösung.<br />

Aufgabe: Mache aus der View Mitarbeiter_<strong>in</strong>_Abteilung e<strong>in</strong>e Prozedur<br />

Mitarbeiter_aus_Abteilung, die die Abteilungsnummer als Parameter erhält.<br />

Firebird-Quelltext<br />

CREATE OR ALTER PROCEDURE Mitarbeiter_aus_Abteilung<br />

( Abt INTEGER)<br />

RETURNS ( Personalnummer VARCHAR(10),<br />

Name VARCHAR(30),<br />

Vorname VARCHAR(30),<br />

Geburtsdatum DATE )<br />

AS<br />

BEGIN<br />

FOR SELECT Personalnummer, Name, Vorname, Geburtsdatum<br />

FROM Mitarbeiter<br />

WHERE Abteilung_ID = :Abt<br />

ORDER BY Ist_Leiter, Name, Vorname<br />

INTO :Personalnummer, :Name, :Vorname, :Geburtsdatum<br />

DO SUSPEND;<br />

END<br />

Als Prozedur enthält die Abfrage folgende Bestandteile:<br />

360


Beispiele<br />

• Als E<strong>in</strong>gabe-Parameter wird die gewünschte Abteilungsnummer erwartet und<br />

<strong>in</strong> der Variablen Abt gespeichert.<br />

• Als Ausgabe-Parameter werden die gewünschten Spalten der Tabelle Mitarbeiter<br />

aufgeführt.<br />

• Weitere Variable werden nicht benötigt.<br />

Innerhalb von BEGIN. . . END steht der eigentliche Arbeitsablauf mit Anweisungen<br />

gemäß <strong>SQL</strong>-Programmierung.<br />

• Es handelt sich dabei vor allem um e<strong>in</strong>en SELECT-Befehl mit der gewünschten<br />

WHERE-Klausel, die bei der VIEW nicht möglich war.<br />

• Diese Abfrage wird <strong>in</strong> e<strong>in</strong>e FOR-DO-Schleife e<strong>in</strong>gebunden.<br />

• Dabei werden die Spalten e<strong>in</strong>es jeden ausgewählten Datensatzes mit INTO <strong>in</strong><br />

die Ausgabe-Parameter übertragen und per SUSPEND zurückgegeben.<br />

Damit haben wir die gewünschte variable Abfrage. Bei der Verwendung ist es<br />

für den Anwender gleichgültig, ob er sie wie e<strong>in</strong>e View oder wie e<strong>in</strong>e Prozedur<br />

aufzurufen hat:<br />

Aufgabe: Hole alle Mitarbeiter e<strong>in</strong>er bestimmten Abteilung.<br />

SELECT * FROM Mitarbeiter_<strong>in</strong>_Abteilung<br />

WHERE Abt = 5;<br />

Aufruf als View<br />

Aufruf als Prozedur<br />

SELECT * FROM Mitarbeiter_aus_Abteilung (5);<br />

32.2.2. INSERT <strong>in</strong> mehrere Tabellen<br />

Wenn <strong>in</strong> der Beispieldatenbank e<strong>in</strong> neuer Versicherungsvertrag e<strong>in</strong>gegeben werden<br />

soll, benötigt man <strong>in</strong> der Regel drei INSERT-Befehle:<br />

• für e<strong>in</strong>en neuen E<strong>in</strong>trag <strong>in</strong> der Tabelle Fahrzeug<br />

• für e<strong>in</strong>en neuen E<strong>in</strong>trag <strong>in</strong> der Tabelle Versicherungsnehmer<br />

• für e<strong>in</strong>en neuen E<strong>in</strong>trag <strong>in</strong> der Tabelle Versicherungsvertrag mit Verweisen auf<br />

die beiden anderen E<strong>in</strong>träge<br />

Bei den ersten beiden E<strong>in</strong>trägen ist durch SELECT zunächst jeweils die neue<br />

ID abzufragen und beim dritten INSERT zu verwenden. Warum sollte man sich<br />

die Arbeit <strong>in</strong> dieser Weise erschweren? Soll doch die Datenbank e<strong>in</strong>en e<strong>in</strong>zigen<br />

INSERT-Befehl entgegennehmen und die Parameter selbständig auf die beteiligten<br />

Tabellen verteilen.<br />

361


Prozeduren<br />

Aufgabe: Speichere e<strong>in</strong>en neuen Versicherungsvertrag mit allen E<strong>in</strong>zelheiten.<br />

Firebird-Quelltext<br />

CREATE OR ALTER PROCEDURE Insert_Versicherungsvertrag<br />

( /* Bestandteile des Vertrags */<br />

Vertragsnummer VARCHAR(20),<br />

Abschlussdatum DATE,<br />

Art CHAR(2),<br />

Praemiensatz INTEGER,<br />

Basispraemie DECIMAL(9,0),<br />

Mitarbeiter_ID INTEGER,<br />

/* Angaben zum Versicherungsnehmer */<br />

Name VARCHAR(30),<br />

Vorname VARCHAR(30),<br />

Geschlecht CHAR(1),<br />

Geburtsdatum DATE,<br />

Fuehrersche<strong>in</strong> DATE,<br />

Ort VARCHAR(30),<br />

PLZ CHAR(5),<br />

Strasse VARCHAR(30),<br />

Hausnummer VARCHAR(10),<br />

Eigener_Kunde CHAR(1),<br />

Versicherungsgesellschaft_ID INTEGER,<br />

/* Angaben zum Fahrzeug */<br />

Kennzeichen VARCHAR(10),<br />

Farbe VARCHAR(30),<br />

Fahrzeugtyp_ID INTEGER<br />

)<br />

RETURNS( NewID INTEGER )<br />

AS<br />

DECLARE VARIABLE NewFahrzeugID INTEGER;<br />

DECLARE VARIABLE NewVersnehmerID INTEGER;<br />

BEGIN<br />

NewFahrzeugID = NEXT VALUE FOR Fahrzeug_ID;<br />

INSERT INTO Fahrzeug<br />

VALUES ( :NewFahrzeugID, :Kennzeichen, :Farbe, :Fahrzeugtyp_ID );<br />

NewVersnehmerID = NEXT VALUE FOR Versicherungsnehmer_ID;<br />

INSERT INTO Versicherungsnehmer<br />

VALUES ( :NewVersnehmerID, :Name, :Vorname, :Geburtsdatum,<br />

:Fuehrersche<strong>in</strong>, :Ort, :PLZ, :Strasse, :Hausnummer,<br />

:Eigener_Kunde, :Versicherungsgesellschaft_ID, :Geschlecht );<br />

NewID = NEXT VALUE FOR Versicherungsvertrag_ID;<br />

INSERT INTO Versicherungsvertrag<br />

VALUES ( :NewID, :Vertragsnummer, :Abschlussdatum, :Art, :Mitarbeiter_ID,<br />

:NewFahrzeugID, :NewVersnehmerID, :Praemiensatz,<br />

:Abschlussdatum, :Basispraemie );<br />

SUSPEND;<br />

END<br />

Als E<strong>in</strong>gabe-Parameter werden alle Werte benötigt, die der Sachbearbeiter für<br />

die e<strong>in</strong>zelnen Tabellen e<strong>in</strong>geben muss (siehe die durch Kommentare getrennten<br />

Abschnitte).<br />

362


Als Ausgabe-Parameter wollen wir die ID für den neuen Vertrag erhalten.<br />

Beispiele<br />

Als Variable werden die Verweise auf die zugeordneten Tabellen Fahrzeug und<br />

Versicherungsnehmer v<strong>org</strong>esehen.<br />

Der Arbeitsablauf ist ganz e<strong>in</strong>fach strukturiert:<br />

• Hole die nächste ID für die Tabelle Fahrzeug und speichere den nächsten E<strong>in</strong>trag<br />

<strong>in</strong> dieser Tabelle.<br />

• Hole die nächste ID für die Tabelle Versicherungsnehmer und speichere den<br />

nächsten E<strong>in</strong>trag <strong>in</strong> dieser Tabelle.<br />

• Hole die nächste ID für die Tabelle Versicherungsvertrag und speichere den<br />

nächsten E<strong>in</strong>trag <strong>in</strong> dieser Tabelle. Benutze dafür die beiden anderen IDs.<br />

Bei e<strong>in</strong>em DBMS mit automatischem Zähler wird statt der „nächsten ID“ die gerade<br />

neu vergebene ID abgefragt. Möglichkeiten dafür enthält der Abschnitt „Die<br />

letzte ID abfragen“ des Kapitels Tipps und Tricks 2 .<br />

Damit wird e<strong>in</strong> neuer Vertrag mit e<strong>in</strong>em e<strong>in</strong>zigen Befehl erstellt:<br />

Aufgabe: Speichere e<strong>in</strong>en neuen Vertrag mit allen Angaben.<br />

Firebird-Quelltext<br />

EXECUTE PROCEDURE Insert_Versicherungsvertrag<br />

( ’HS-38’, ’03.11.2009’, ’VK’, 125, 870, 11, ’Graef<strong>in</strong>g’, ’Charlotte’,<br />

’W’, ’09.11.1989’, ’26.02.2008’, ’Hatt<strong>in</strong>gen’, ’45529’,<br />

’Laakerweg’, ’17 b’, ’J’, NULL, ’BO-MC 413’, ’gelb’, 8 );<br />

------ Procedure execut<strong>in</strong>g results: ------<br />

NEWID = 29 /* die ID des neuen Vertrags */<br />

32.2.3. Automatisches UPDATE gemäß Bed<strong>in</strong>gungen<br />

Bei e<strong>in</strong>er Versicherungsgesellschaft muss regelmäßig der Prämiensatz e<strong>in</strong>es Vertrags<br />

neu berechnet werden: Bei verschuldeten Schadensfällen wird er (abhängig<br />

von der Höhe des Schadens) erhöht, bei Schadensfreiheit verr<strong>in</strong>gert. Dies ist<br />

e<strong>in</strong>e Aufgabe, die die Datenbank selbständig erledigen kann und soll. Das ist e<strong>in</strong><br />

komplexer Arbeitsablauf mit mehreren Prüfungen; vor allem die unterschiedliche<br />

Zuweisung neuer Werte wird <strong>in</strong> diesem Beispiel stark vere<strong>in</strong>facht.<br />

Aufgabe: Berechne für alle (eigenen) Verträge, ob e<strong>in</strong>e neue Prämienrechnung<br />

ansteht. Dabei ist zu prüfen, ob wegen neuer Schadensfälle der Prämiensatz zu<br />

erhöhen oder wegen Schadensfreiheit zu verr<strong>in</strong>gern ist.<br />

2 Abschnitt 34.1 auf Seite 389<br />

363


Prozeduren<br />

Die e<strong>in</strong>zelnen Schritte werden anhand des folgenden Codes erläutert.<br />

364<br />

Firebird-Quelltext<br />

CREATE OR ALTER PROCEDURE Update_Praemiensatz<br />

( Aktualisierung DATE DEFAULT CURRENT_DATE )<br />

RETURNS ( Erledigt INTEGER )<br />

AS<br />

DECLARE VARIABLE current_id INTEGER;<br />

DECLARE VARIABLE current_fz INTEGER;<br />

DECLARE VARIABLE current_aenderung DATE;<br />

DECLARE VARIABLE vergleich_aenderung DATE;<br />

DECLARE VARIABLE current_satz INTEGER;<br />

DECLARE VARIABLE current_value DECIMAL(16,2);<br />

BEGIN<br />

Erledigt = 0;<br />

SUSPEND;<br />

/* Vorarbeit: Anfänger werden auf Praemiensatz 200 gesetzt; das kann aber<br />

nur für noch nicht behandelte Verträge gelten und muss deshalb noch<br />

vor der nächsten IS NULL-Prüfung erledigt werden. */<br />

UPDATE Versicherungsvertrag vv<br />

SET Praemiensatz = 200<br />

WHERE Praemienaenderung IS NULL<br />

AND Versicherungsnehmer_ID<br />

/* bestimme die Liste der Anfänger, aber nur für eigene Kunden */<br />

IN ( SELECT ID<br />

FROM Versicherungsnehmer vn<br />

/* wenn der Führersche<strong>in</strong> beim Vertragsabschluss<br />

weniger als 2 Jahre alt ist */<br />

WHERE vn.Eigener_kunde = ’J’<br />

AND ( ( DATEADD( YEAR, 2, vn.Fuehrersche<strong>in</strong> ) > vv.Abschlussdatum<br />

/* wenn der VersNehmer beim Führersche<strong>in</strong>-Erwerb<br />

noch nicht 21 Jahre alt ist */<br />

OR ( DATEADD( YEAR, 21, vn.Geburtsdatum ) ><br />

vn.Fuehrersche<strong>in</strong> ) ) );<br />

/* Schritt 1: zu bearbeiten s<strong>in</strong>d alle Verträge für eigene Kunden, deren letzte<br />

Prämienänderung „zu lange“ zurückliegt */<br />

FOR SELECT vv.ID, Fahrzeug_ID,<br />

CASE WHEN Praemienaenderung IS NULL THEN Abschlussdatum<br />

ELSE Praemienaenderung<br />

END,<br />

Praemiensatz<br />

FROM Versicherungsvertrag vv<br />

JOIN Versicherungsnehmer vn ON vn.Id = vv.Versicherungsnehmer_id<br />

WHERE vn.Eigener_Kunde = ’J’<br />

AND ( Praemienaenderung IS NULL OR Praemienaenderung


* Schritt 3: weitere Bearbeitung, sofern die Aktualisierung über das<br />

Vergleichsdatum h<strong>in</strong>ausgeht */<br />

IF (Aktualisierung >= vergleich_aenderung) THEN<br />

BEGIN<br />

/* Schritt 4: suche zu diesem Vertrag, d.h. diesem Fahrzeug alle<br />

Schadensfälle <strong>in</strong> dieser Zeit<br />

und summiere die Schadenssumme nach Schuldanteil */<br />

SELECT SUM( sf.Schadenshoehe * zu.Schuldanteil / 100 )<br />

FROM Zuordnung_SF_FZ zu<br />

JOIN Schadensfall sf ON zu.Schadensfall_id = sf.Id<br />

WHERE zu.Fahrzeug_ID = :current_fz<br />

AND sf.Datum BETWEEN :current_aenderung AND :vergleich_aenderung<br />

INTO :current_value;<br />

Beispiele<br />

/* Schritt 5: abhängig von (anteiliger) Schadenssumme und<br />

bisherigem Prämiensatz<br />

wird der neue Prämiensatz bestimmt und das Datum weitergesetzt */<br />

UPDATE Versicherungsvertrag<br />

SET Praemiensatz =<br />

CASE<br />

WHEN :current_value IS NULL THEN CASE<br />

WHEN :current_satz > 100<br />

THEN :current_satz - 20<br />

WHEN :current_satz BETWEEN 40 AND 100<br />

THEN :current_satz - 10<br />

ELSE 30<br />

END<br />

WHEN :current_value = 0 THEN :current_satz - 10<br />

WHEN :current_value < 500 THEN :current_satz<br />

WHEN :current_value < 1000 THEN :current_satz + 10<br />

WHEN :current_value >= 1000 THEN :current_satz + 30<br />

END,<br />

Praemienaenderung = DATEADD( YEAR, 1, :current_aenderung )<br />

WHERE ID = :current_id;<br />

Erledigt = Erledigt + 1;<br />

END<br />

END<br />

SUSPEND;<br />

END<br />

Als E<strong>in</strong>gabe-Parameter wird nur das Datum der derzeitigen Aktualisierung benötigt.<br />

E<strong>in</strong> Vertrag wird dann geprüft, wenn der Zeitraum der nächsten Prämienrechnung<br />

− vom Datum der letzten Prämienänderung aus e<strong>in</strong> Jahr − das Datum<br />

der Aktualisierung enthält. Mit diesem Verfahren werden immer nur „neue“<br />

Schadensfälle und die jeweils anstehenden Prämienrechnungen berücksichtigt.<br />

Als Ausgabe-Parameter nehmen wir nur die Anzahl der berechneten Verträge,<br />

also der geänderten Erledigt-Vermerke.<br />

Als Variable benötigen wir Zwischenwerte für alle Angaben, die im Arbeitsablauf<br />

benutzt werden.<br />

Der Arbeitsablauf umfasst die folgenden Punkte:<br />

365


Prozeduren<br />

• Der Rückgabewert wird mit dem Anfangswert belegt und angezeigt zum Zeichen<br />

dafür, dass die Prozedur arbeitet.<br />

• Als Vorarbeit wird e<strong>in</strong> „Anfänger“, der als Eigener_Kunde gespeichert ist, auf<br />

e<strong>in</strong>en Prämiensatz von 200 gesetzt. Als Anfänger gilt, wenn der Führersche<strong>in</strong><br />

beim Abschluss des Vertrags noch nicht zwei Jahre alt ist oder wenn der Führersche<strong>in</strong><br />

vor dem 21. Geburtstag erworben wurde.<br />

Bei der Prüfung auf „zwei Jahre“ handelt es sich nicht um e<strong>in</strong>en sachlichen<br />

Fehler: Dieser Arbeitsablauf wird <strong>in</strong>nerhalb des ersten Versicherungsjahres<br />

ausgeführt; später ist die Hauptbed<strong>in</strong>gung „Praemienaenderung<br />

IS NULL“ niemals mehr gegeben. In der Praxis müssen solche<br />

Bed<strong>in</strong>gungen wegen möglicher Missverständnisse vorher ganz genau<br />

formuliert werden.<br />

• Schritt 1 wählt die Verträge aus, die „im Moment“ zu prüfen und ggf. zu bearbeiten<br />

s<strong>in</strong>d: alle eigenen Kunden, deren Prämiensatz noch nie geprüft wurde<br />

(dann wird das Abschlussdatum, also das Datum der ersten Rechnung, zur Berechnung<br />

verwendet) oder deren letzte Prämienänderung vor dem Datum der<br />

Aktualisierung liegt. Durch die FOR-SELECT-Schleife wird jeder dieser Verträge<br />

<strong>in</strong>nerhalb von DO BEGIN. . . END e<strong>in</strong>zeln bearbeitet; die dazu benötigten<br />

Werte werden <strong>in</strong> den Variablen zwischengespeichert.<br />

• Schritt 2 berechnet das Schlussdatum der letzten Prämienrechnung. Wir arbeiten<br />

hier nur mit Jahresrechnungen; da bei BETWEEN beide Grenzwerte e<strong>in</strong>bezogen<br />

werden, müssen wir das Schlussdatum um e<strong>in</strong>en Tag verr<strong>in</strong>gern.<br />

• Schritt 3 erledigt die letzte Vorprüfung:<br />

• Wenn das Datum der Aktualisierung vor dem Schlussdatum liegt, soll noch<br />

ke<strong>in</strong>e neue Rechnung erfolgen.<br />

• Aber wenn das Datum der Aktualisierung das Schlussdatum der letzten Prämienrechnung<br />

überschreitet, ist e<strong>in</strong>e neue Rechnung und damit e<strong>in</strong>e Überprüfung<br />

des Prämiensatzes fällig.<br />

• Schritt 4 berechnet die Summe aller Schadensfälle:<br />

• Berücksichtigt wird die Summe der Schadenshöhe je Vorfall, also aus der Tabelle<br />

Schadensfall.<br />

• Dieser Wert wird bei der Summierung anteilig nach dem Schuldanteil aus<br />

der Tabelle Zuordnung_SF_FZ berücksichtigt.<br />

• Das Ergebnis bei current_value lautet NULL, wenn das Fahrzeug nicht <strong>in</strong><br />

e<strong>in</strong>en Schadensfall verwickelt wurde, es lautet 0 bei fehlender Teilschuld und<br />

größer als 0 bei Mit- oder Vollschuld.<br />

• Schritt 5 berechnet den Prämiensatz neu:<br />

366<br />

• Ohne Schadensfall wird er um 10 Punkte verr<strong>in</strong>gert, wenn er bisher kle<strong>in</strong>er<br />

oder gleich 100 beträgt, und um 20 Punkte, wenn er bisher größer ist.<br />

• Das M<strong>in</strong>imum von 30 kann nicht unterschritten werden.


Beispiele<br />

• Mit Schadensfall ohne Teilschuld wird der Prämiensatz um 10 Punkte verr<strong>in</strong>gert.<br />

• Bei e<strong>in</strong>em sehr ger<strong>in</strong>gen Schaden bleibt der Prämiensatz unverändert.<br />

• Bei e<strong>in</strong>em kle<strong>in</strong>eren Schaden wird er um 10 Punkte, sonst um 30 Punkte<br />

erhöht.<br />

• Gleichzeitig wird das Datum der Prämienänderung um 1 Jahr weitergesetzt.<br />

Mit dieser Prozedur können die neuen Prämiensätze berechnet und für die<br />

nächste Prämienrechnung vorbereitet werden:<br />

Aufgabe: Berechne die Prämiensätze für alle (eigenen) Verträge, bei denen im<br />

3. Quartal 2009 e<strong>in</strong>e neue Prämienrechnung ansteht.<br />

EXECUTE PROCEDURE Update_Praemiensatz(’30.09.2009’);<br />

------ Procedure execut<strong>in</strong>g results: ------<br />

Erledigt = 7<br />

32.2.4. Testdaten <strong>in</strong> e<strong>in</strong>er Tabelle erstellen<br />

Das übliche V<strong>org</strong>ehen, wenn <strong>in</strong> e<strong>in</strong>er Tabelle viele Testdaten gespeichert werden<br />

sollen, werden wir im Kapitel Testdaten erzeugen verwenden:<br />

• In Zusatztabellen werden geeignete Feld<strong>in</strong>halte gesammelt: e<strong>in</strong>e Tabelle mit<br />

möglichen Vornamen, e<strong>in</strong>e mit Nachnamen usw.<br />

• Wie im Kapitel E<strong>in</strong>fache Tabellenverknüpfung beschrieben, werden als „kartesisches<br />

Produkt“ alle Zeilen und Spalten aller Zusatztabellen mite<strong>in</strong>ander<br />

komb<strong>in</strong>iert.<br />

• Oder die Zeilen und Spalten der Zusatztabellen werden per Zufallsfunktion<br />

mite<strong>in</strong>ander verknüpft.<br />

Bei wenigen Spalten der Zieltabelle kann auf Zusatztabellen verzichtet werden;<br />

dieses Verfahren nutzen wir im folgenden Beispiel. (Das Verfahren ist theoretisch<br />

auch für komplexere Tabellen möglich, wird dann aber schnell unübersichtlich;<br />

schon die folgenden CASE-Konstruktionen deuten solche Probleme an.)<br />

Aufgabe: Erzeuge e<strong>in</strong>e bestimmte Anzahl von Datensätzen <strong>in</strong> der Tabelle<br />

Fahrzeug; die Anzahl der neuen E<strong>in</strong>träge soll als Parameter v<strong>org</strong>egeben werden.<br />

Firebird Quelltext<br />

CREATE OR ALTER PROCEDURE Insert_Into_Fahrzeug<br />

( Anzahl INTEGER = 0)<br />

RETURNS ( Maxid INTEGER)<br />

AS<br />

367


Prozeduren<br />

DECLARE VARIABLE Temp INTEGER = 0;<br />

DECLARE VARIABLE Listekz CHAR( 66) = ’E E E DU DU BOTRE RE OB OB GE<br />

GE HERHAMBO BO DO DO UN UN EN EN ’;<br />

DECLARE VARIABLE Listekza CHAR( 26) = ’ABCDEFGHIJKLMNOPQRSTUVWXYZ’;<br />

DECLARE VARIABLE Listefrb CHAR(100)<br />

= ’elfenbe<strong>in</strong> schwarz gelb orange ocker blau<br />

silbern grau braun weiss ’;<br />

DECLARE VARIABLE Kz VARCHAR( 3);<br />

DECLARE VARIABLE Name VARCHAR( 30);<br />

DECLARE VARIABLE Rand1 INTEGER = 0;<br />

DECLARE VARIABLE Rand2 INTEGER = 0;<br />

DECLARE VARIABLE Rand3 INTEGER = 0;<br />

DECLARE VARIABLE Rand4 INTEGER = 0;<br />

DECLARE VARIABLE Rand5 INTEGER = 0;<br />

BEGIN<br />

Maxid = 0;<br />

WHILE (Temp < Anzahl) DO<br />

BEGIN<br />

/* neue Zufallszahlen */<br />

Rand1 = FLOOR(1 + (RAND() * 21)); /* für Kfz-Kz. e<strong>in</strong>e Zufallszahl 1 bis 22 */<br />

Rand2 = FLOOR(0 + (RAND() * 26)); /* ebenso e<strong>in</strong>e Zufallszahl 0 bis 26 */<br />

Rand3 = FLOOR(1 + (RAND() * 25)); /* ebenso e<strong>in</strong>e Zufallszahl 1 bis 26 */<br />

Rand4 = FLOOR(1 + (RAND() * 9)); /* für Farbe e<strong>in</strong>e Zufallszahl 1 bis 10 */<br />

Rand5 = FLOOR(1 + (RAND() * 22)); /* für Fz.Typ e<strong>in</strong>e Zufallszahl 1 bis 23 */<br />

/* hole per Zufall Rand1 e<strong>in</strong>es der Kürzel aus Listekz */<br />

Kz = TRIM(SUBSTRING(:Listekz FROM (:Rand1*3 - 2) FOR 3));<br />

/* mache daraus e<strong>in</strong> vollständiges Kfz-Kennzeichen */<br />

Name = Kz || ’-’ || CASE :Rand2<br />

WHEN 0 THEN ”<br />

ELSE SUBSTRING(:Listekza FROM :Rand2 FOR 1)<br />

END<br />

|| SUBSTRING(:Listekza FROM :Rand3 FOR 1)<br />

|| ’ ’ || CAST( (CASE CHAR_LENGTH(Kz)<br />

WHEN 1 THEN FLOOR(1 + (RAND() * 9998))<br />

ELSE FLOOR(1 + (RAND() * 998))<br />

END)<br />

AS INT);<br />

/* Speichern des neuen Datensatzes */<br />

INSERT INTO Fahrzeug (Kennzeichen, Farbe, Fahrzeugtyp_ID)<br />

VALUES ( :Name,<br />

TRIM(SUBSTRING(:Listefrb FROM (:Rand4*10-9) FOR 10)),<br />

:Rand5 );<br />

Temp = Temp + 1;<br />

END<br />

SELECT MAX(ID) FROM fahrzeug INTO :Maxid;<br />

SUSPEND;<br />

END<br />

Dies def<strong>in</strong>iert die Prozedur mit folgenden Bestandteilen:<br />

• Mit Anzahl als e<strong>in</strong>zigem E<strong>in</strong>gabe-Parameter wird festgelegt, wie viele Datensätze<br />

erzeugt werden sollen. Dazu gehören der Name und Typ der Variablen<br />

sowie e<strong>in</strong> V<strong>org</strong>abewert.<br />

368


Beispiele<br />

• Mit Maxid als e<strong>in</strong>zigem Ausgabe-Parameter wird angegeben, dass e<strong>in</strong><br />

INTEGER-Wert mit diesem Namen erwartet wird. Der Anfangswert wird im<br />

ersten Befehl des Rumpfes der Prozedur festgelegt.<br />

• Zwischen AS und BEGIN stehen die verwendeten Variablen mit ihren Anfangswerten.<br />

• Listekz ist e<strong>in</strong>e Liste von 22 möglichen Kfz-Kennzeichen, Listekza enthält das<br />

Alphabet für die Zufallssuche nach dem zweiten Teil des Kennzeichens und<br />

Listefrb e<strong>in</strong>e Liste von 10 Farben (zu je 10 Zeichen). Diese Listen ersetzen<br />

die temporären Tabellen. Die „langen“ Texte müssen unbed<strong>in</strong>gt <strong>in</strong> e<strong>in</strong>er Zeile<br />

stehen; es geht hier nur wegen der Textbreite nicht anders.<br />

• Temp wird als Zähler für die neuen Datensätze verwendet, Kz und Name als<br />

Zwischenspeicher bei der Bildung des neuen Kennzeichens.<br />

• Rand1 usw. s<strong>in</strong>d Variablen für Zufallszahlen. Darauf könnte auch verzichtet<br />

werden, weil die Formeln für die Zufallszahlen auch direkt <strong>in</strong> der Str<strong>in</strong>g-<br />

Verknüpfung und im VALUES-Teil verwendet werden könnten; aber die<br />

Trennung macht es übersichtlicher.<br />

Innerhalb von BEGIN. . . END steht der eigentliche Arbeitsablauf mit Anweisungen<br />

gemäß <strong>SQL</strong>-Programmierung. Innerhalb der WHILE-Schleife zwischen<br />

DO BEGIN und END wird jeweils e<strong>in</strong> neuer Datensatz erzeugt und gespeichert:<br />

• Zuerst werden neue Zufallszahlen geholt.<br />

• Durch Rand1 wird e<strong>in</strong>e der Zahlen 1, 4, 7 usw. berechnet und gemäß diesem<br />

Wert e<strong>in</strong>es der Kennzeichen aus Listekz geholt.<br />

• Anschließend wird daraus e<strong>in</strong> vollständiges Kfz-Kennzeichen:<br />

• Wenn Rand2 ungleich 0 ist, folgt e<strong>in</strong> Buchstabe nach der Zufallszahl Rand2<br />

aus dem Alphabet, sonst nichts.<br />

• Danach folgt e<strong>in</strong> weiterer Buchstabe nach der Zufallszahl Rand3 aus dem<br />

Alphabet.<br />

• Danach folgt e<strong>in</strong>e (Zufalls-) Zahl: bei e<strong>in</strong>stelligem Kennzeichen e<strong>in</strong>e vierstellige<br />

Zahl, sonst e<strong>in</strong>e dreistellige.<br />

• Schließlich wird e<strong>in</strong> neuer Datensatz e<strong>in</strong>getragen mit dem eben erzeugten<br />

Kennzeichen sowie:<br />

• e<strong>in</strong>er Farbe, die mit der Zufallszahl Rand4 aus Listefrb geholt wird<br />

• e<strong>in</strong>er Fahrzeugtyp-ID, die nach der Zufallszahl Rand5 e<strong>in</strong>em der vorhandenen<br />

Werte <strong>in</strong> der Tabelle Fahrzeugtyp entspricht<br />

Zusätzlich wird die Variable Temp weitergezählt; nach dem Ende der Arbeit wird<br />

der größte Wert von ID bestimmt, <strong>in</strong> die Variable Maxid e<strong>in</strong>getragen und per<br />

SUSPEND als Rückgabewert übernommen.<br />

Ausgeführt wird diese Prozedur durch e<strong>in</strong>en e<strong>in</strong>fachen Befehl.<br />

369


Prozeduren<br />

Aufgabe: Erzeuge 10 neue Datensätze <strong>in</strong> der Tabelle Fahrzeug.<br />

EXECUTE PROCEDURE Insert_<strong>in</strong>to_Fahrzeug (10);<br />

------ Procedure execut<strong>in</strong>g results: ------<br />

MAXID = 145<br />

32.3. Zusammenfassung<br />

In diesem Kapitel lernten wir Prozeduren für Arbeitsabläufe kennen, die „nur“<br />

<strong>in</strong>nerhalb der Datenbank ausgeführt werden:<br />

• E<strong>in</strong>e Prozedur kann mit oder ohne Argumenten und mit oder ohne Rückgabewerte<br />

ausgeführt werden.<br />

• Sie dient zur automatischen Erledigung von Aufgaben, die „von außen“ angestoßen<br />

werden, aber ke<strong>in</strong>e zusätzlichen Maßnahmen des Anwenders oder der<br />

Anwendung benötigen.<br />

• Dabei werden viele Bestandteile e<strong>in</strong>er Programmiersprache benutzt.<br />

32.4. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 372.<br />

Unter „Skizzieren“ (Übung 3, 5, 6) ist wie im vorigen Kapitel geme<strong>in</strong>t: E<strong>in</strong>gabeund<br />

Ausgabeparameter sowie Variablen mit s<strong>in</strong>nvollen Namen und Datentypen<br />

benennen, Arbeitsablauf möglichst genau mit Pseudo-Code oder normaler Sprache<br />

beschreiben.<br />

Tipp: Die Parameter ergeben sich <strong>in</strong> der Regel aus der Aufgabenstellung. Aus der<br />

Überlegung zum Arbeitsablauf folgen die Variablen.<br />

Übung 1 – Prozeduren verwenden<br />

In welchen der folgenden Situationen ist e<strong>in</strong>e Prozedur s<strong>in</strong>nvoll, <strong>in</strong> welchen<br />

nicht? Gehen Sie davon aus, dass alle Informationen <strong>in</strong> der Datenbank gespeichert<br />

s<strong>in</strong>d.<br />

370<br />

1. Erstelle neue Rechnungen nach den aktuellen Prämiensätzen.<br />

2. Berechne die Weihnachtsgratifikationen der Mitarbeiter nach den erfolgten<br />

Abschlüssen.<br />

3. E<strong>in</strong> Versicherungsvertrag wird gekündigt.


Übung 2 – Def<strong>in</strong>ition e<strong>in</strong>er Prozedur kontrollieren<br />

Übungen<br />

Nennen Sie <strong>in</strong> der folgenden Def<strong>in</strong>ition Punkte, die unabhängig vom <strong>SQL</strong>-<br />

Dialekt falsch s<strong>in</strong>d. (Je nach DBMS s<strong>in</strong>d noch andere Punkte falsch, danach wird<br />

aber nicht gefragt.) Die Prozedur soll folgende Aufgabe erledigen:<br />

Mit e<strong>in</strong>em Aufruf sollen bis zu 5 Abteilungen neu gespeichert werden. Die Angaben<br />

sollen wie folgt <strong>in</strong> jeweils e<strong>in</strong>em Str<strong>in</strong>g zusammengefasst werden:<br />

’AbCd-Name der Abteilung-Ort der Abteilung’<br />

zuerst 4 Zeichen für das Kürzel, B<strong>in</strong>destrich, der Name der Abteilung,<br />

B<strong>in</strong>destrich, der Ort der Abteilung<br />

In e<strong>in</strong>er Schleife werden die 5 Zeichenketten verarbeitet, nämlich aufgeteilt und<br />

als Neuaufnahme <strong>in</strong> der Tabelle Abteilung gespeichert. Als Rückgabewert soll die<br />

letzte neu vergebene ID dienen.<br />

1. CREATE Insert_Abteilungen AS PROCEDURE<br />

2. /* E<strong>in</strong>gabe: mehrere neue Abteilungen e<strong>in</strong>zutragen <strong>in</strong> der Schreibweise */<br />

3. ’AbCd-Name der Abteilung-Ort der Abteilung’ */<br />

4. INPUTS Inhalt1, Inhalt2, Inhalt3, Inhalt4, Inhalt5 VARCHAR(70)<br />

5. OUTPUTS lastid INTEGER<br />

6. VARIABLES x1, pos INTEGER,<br />

7. temp VARCHAR(70),<br />

8. krz CHAR(4),<br />

9. name, ort VARCHAR(30)<br />

10. AS BEGIN<br />

11. x1 = 0;<br />

12. WHILE (x1 < 5)<br />

13. DO BEGIN<br />

14. x1 = x1 + 1;<br />

15. temp = CASE x1<br />

16. WHEN 1 THEN Inhalt1<br />

17. WHEN 2 THEN Inhalt2<br />

18. WHEN 3 THEN Inhalt3<br />

19. WHEN 4 THEN Inhalt4<br />

20. WHEN 5 THEN Inhalt5<br />

21. END<br />

22. /* vorzeitiger Abbruch, falls NULL übergeben wird */<br />

23. IF (temp IS NULL)<br />

24. THEN BREAK;<br />

25. /* bestimme durch ’-’, wo der Name aufhört und der Ort anfängt */<br />

26. pos = POSITION( ’-’, temp, 6 );<br />

27. krz = SUBSTRING( temp FROM 1 FOR 4 );<br />

28. name = SUBSTRING( temp FROM 5 FOR (pos-5) );<br />

29. ort = SUBSTRING( temp FROM (pos+1) );<br />

30. /* neuen Datensatz speichern */<br />

31. INSERT INTO Abteilung<br />

32. ( Kuerzel, Bezeichnung, Ort )<br />

33. VALUES ( krz, name, ort );<br />

34. lastid = SELECT ID FROM Abteilung WHERE Kuerzel = krz;<br />

35. END<br />

36. END<br />

371


Prozeduren<br />

Übung 3 – Prozedur Insert_Fahrzeugtyp erstellen<br />

Skizzieren Sie e<strong>in</strong>e Prozedur Insert_Fahrzeugtyp zum Speichern von Fahrzeugtyp<br />

und Hersteller <strong>in</strong> e<strong>in</strong>em Schritt: Es ist zu prüfen, ob der Hersteller schon gespeichert<br />

ist; wenn ja, ist diese ID zu verwenden, andernfalls ist e<strong>in</strong> neuer E<strong>in</strong>trag zu<br />

erstellen und dessen ID zu übernehmen. Mit dieser Hersteller-ID ist der Fahrzeugtyp<br />

zu registrieren.<br />

Übung 4 – Prozedur Insert_Fahrzeugtyp erstellen<br />

Erstellen Sie die Prozedur Insert_Fahrzeugtyp aus der vorigen Übung.<br />

Übung 5 – Prozedur Insert_Schadensfall erstellen<br />

Skizzieren Sie e<strong>in</strong>e Prozedur Insert_Schadensfall zum Speichern e<strong>in</strong>es neuen<br />

Schadens; dabei soll nur das Fahrzeug des Versicherungsnehmers beteiligt se<strong>in</strong>.<br />

Übung 6 – Prozedur Update_Schadensfall erstellen<br />

Skizzieren Sie e<strong>in</strong>e Prozedur Update_Schadensfall zum Ändern e<strong>in</strong>es Schadensfalls;<br />

dabei soll jeweils e<strong>in</strong> weiteres beteiligtes Fahrzeug registriert werden. (H<strong>in</strong>weise:<br />

Sie müssen auch berücksichtigen, wie sich die Schadenshöhe neu verteilt.<br />

Sie können davon ausgehen, dass der Versicherungsnehmer schon gespeichert<br />

ist − egal, ob es sich um e<strong>in</strong>en eigenen Kunden handelt oder nicht.) Beschreiben<br />

Sie zusätzlich, welche Punkte beim Arbeitsablauf und damit bei den E<strong>in</strong>gabe-<br />

Parametern noch geklärt werden müssen.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 370.<br />

Lösung zu Übung 1 – Prozeduren verwenden<br />

372<br />

1. s<strong>in</strong>nvoll, weil es nach den gespeicherten Informationen automatisch erledigt<br />

werden kann<br />

2. nicht s<strong>in</strong>nvoll, weil zwar die Angaben automatisch zusammengestellt werden<br />

können, aber die Gratifikation e<strong>in</strong>e <strong>in</strong>dividuelle Entscheidung der Geschäftsleitung<br />

ist<br />

3. s<strong>in</strong>nvoll, weil mehrere zusammenhängende Maßnahmen auszuführen<br />

s<strong>in</strong>d


Lösung zu Übung 2 – Def<strong>in</strong>ition e<strong>in</strong>er Prozedur kontrollieren<br />

• Zeile 1: Der Befehl lautet: CREATE PROCEDURE .<br />

• Zeile 2/3: Der Kommentar ist falsch abgeschlossen.<br />

Übungen<br />

• Zeile 4 ff.: Die geme<strong>in</strong>same Deklaration mehrerer Parameter oder Variablen ist<br />

nicht zulässig.<br />

• Zeile 4: Die E<strong>in</strong>gabe-Parameter müssen <strong>in</strong> Klammern stehen.<br />

• Zeile 5: Die Ausgabe-Parameter werden bei ke<strong>in</strong>em DBMS durch e<strong>in</strong>e<br />

OUTPUTS-Klausel angegeben.<br />

• Zeile 6 ff.: Die Variablen folgen erst h<strong>in</strong>ter AS und werden anders deklariert.<br />

• Zeile 21: Es fehlt das Semikolon am Ende der Zuweisung.<br />

• Zeile 28: Die Längenangabe im SUBSTRING ist nicht korrekt.<br />

• Zeile 31 ff.: Die E<strong>in</strong>deutigkeit der Namen von Variablen und Spalten wird teilweise<br />

nicht beachtet.<br />

Lösung zu Übung 3 – Prozedur Insert_Fahrzeugtyp erstellen<br />

• E<strong>in</strong>gabe-Parameter: TypBezeichnung Varchar(30), HerstellerName Varchar(30),<br />

HerstellerLand Varchar(30)<br />

• Ausgabe-Parameter: TypID Integer, HerstellerID Integer<br />

• Variable: werden nicht benötigt<br />

• Arbeitsablauf:<br />

1. Suche <strong>in</strong> der Tabelle Fahrzeughersteller den gegebenen HerstellerName<br />

und registriere die gefundene ID im Parameter HerstellerID.<br />

2. Wenn dieser Parameter jetzt NULL ist, fehlt der E<strong>in</strong>trag noch. Also ist er<br />

neu aufzunehmen unter Verwendung der Angaben aus HerstellerName<br />

und HerstellerLand; das liefert den Wert von HerstellerID.<br />

3. Damit kann der neue Datensatz <strong>in</strong> der Tabelle Fahrzeugtyp registriert werden;<br />

die neue ID dieses Datensatzes wird als Wert TypID zurückgegeben.<br />

Lösung zu Übung 4 – Prozedur Insert_Fahrzeugtyp erstellen<br />

Diese Variante der Lösung benutzt die Firebird-Syntax, vor allem h<strong>in</strong>sichtlich der<br />

Trennung von E<strong>in</strong>gabe- und Ausgabeparametern.<br />

CREATE OR ALTER PROCEDURE Insert_Fahrzeugtyp<br />

( TypBezeichnung VARCHAR(30),<br />

HerstellerName VARCHAR(30),<br />

HerstellerLand VARCHAR(30)<br />

)<br />

RETURNS( TypID INTEGER, HerstellerID INTEGER )<br />

AS<br />

BEGIN<br />

373


Prozeduren<br />

/* ist der Hersteller schon registriert? */<br />

SELECT ID FROM Fahrzeughersteller<br />

WHERE Name = :HerstellerName<br />

INTO :HerstellerID;<br />

/* ne<strong>in</strong>, dann als Hersteller neu aufnehmen */<br />

IF (HerstellerID IS NULL) THEN<br />

BEGIN<br />

HerstellerID = NEXT VALUE FOR Fahrzeughersteller_ID;<br />

INSERT INTO Fahrzeughersteller<br />

VALUES ( :HerstellerID, :HerstellerName, :HerstellerLand );<br />

END<br />

/* anschließend den Typ registrieren */<br />

TypID = NEXT VALUE FOR Fahrzeugtyp_ID;<br />

INSERT INTO Fahrzeugtyp<br />

VALUES ( :TypID, :TypBezeichnung, :HerstellerID );<br />

END<br />

Lösung zu Übung 5 – Prozedur Insert_Schadensfall erstellen<br />

• E<strong>in</strong>gabe-Parameter: die Angaben zum Schadensfall (Datum Date, Ort varchar(200),<br />

Beschreibung varchar(1000), Schadenshoehe number, Verletzte<br />

char(1), Mitarbeiter_ID Integer), Fahrzeugbeteiligung (ID Integer oder Kennzeichen<br />

Varchar(10), Schuldanteil Integer)<br />

• Ausgabe-Parameter: neue ID des Schadensfalls<br />

• Arbeitsablauf:<br />

1. Insert <strong>in</strong>to Schadensfall: Registriere den Schadensfall, notiere die neue ID<br />

2. Select from Fahrzeug: Suche ggf. zum Kennzeichen die Fahrzeug-ID<br />

3. Insert <strong>in</strong>to Zuordnung_SF_FZ: Registriere die Zuordnung zwischen Schadensfall<br />

und Fahrzeug<br />

• Variable werden voraussichtlich nicht benötigt.<br />

Lösung zu Übung 6 – Prozedur Change_Schadensfall erstellen<br />

• E<strong>in</strong>gabe-Parameter: Schadensfall-ID Integer, Fahrzeugbeteiligung (Kennzeichen<br />

Varchar(10), anteilige Schadenshöhe Number)<br />

• Ausgabe-Parameter: eigentlich nicht erforderlich, aber als „Erfolgsmeldung“<br />

die ID der neuen Zuordnung<br />

• Arbeitsablauf:<br />

374<br />

1. Select from Fahrzeug: Suche zum Kennzeichen die Fahrzeug-ID.<br />

2. Insert <strong>in</strong>to Zuordnung_SF_FZ: Registriere die Zuordnung zwischen Schadensfall<br />

und dem neuen Fahrzeug.<br />

3. Übernimm die ID dieser neuen Zuordnung als Ausgabe-Parameter.<br />

4. Update Zuordnung_SF_FZ: Ändere im ersten E<strong>in</strong>trag zu diesem Schadensfall<br />

die anteilige Schadenshöhe unter Berücksichtigung des neu re-


Siehe auch<br />

gistrierten beteiligten Fahrzeugs. (Der benötigte „erste E<strong>in</strong>trag“ kann<br />

durch e<strong>in</strong>e passende WHERE-Klausel direkt geändert werden; ersatzweise<br />

kann er auch durch e<strong>in</strong>en eigenen SELECT-Befehl bestimmt werden −<br />

dann wird für die ID e<strong>in</strong>e Variable benötigt.)<br />

• Variable werden voraussichtlich nicht benötigt.<br />

• Unklarheiten: Bei diesem Arbeitsablauf wird davon ausgegangen, dass zu jedem<br />

später registrierten Fahrzeug e<strong>in</strong> Teil des ursprünglichen Schadens gehört;<br />

es wird nicht berücksichtigt, dass sich die Schadensverteilung <strong>in</strong>sgesamt<br />

ändern kann. Völlig offen ist, wie sich der Verschuldensanteil beim ersten E<strong>in</strong>trag<br />

und bei jedem weiteren E<strong>in</strong>trag ändern kann. In beiden Punkten ist lediglich<br />

klar, dass alle E<strong>in</strong>zelwerte zu summieren s<strong>in</strong>d und den Maximalwert (100<br />

beim Schuldanteil bzw. den Gesamtschaden) nicht überschreiten dürfen.<br />

32.5. Siehe auch<br />

Teile dieses Kapitels beziehen sich auf Erläuterungen <strong>in</strong> den folgenden Kapiteln:<br />

• <strong>SQL</strong>-Programmierung 3<br />

• Erstellen von Views 4 im Abschnitt „E<strong>in</strong>e View mit variabler Selektion“<br />

• Testdaten erzeugen 5<br />

• E<strong>in</strong>fache Tabellenverknüpfung 6 − alle Komb<strong>in</strong>ationen aller Datensätze<br />

• Funktionen (2) 7 − u. a. RAND für Zufallszahlen<br />

• Tipps und Tricks 8<br />

Bei Wikipedia gibt es grundlegende H<strong>in</strong>weise:<br />

• Fat Client 9 als e<strong>in</strong> Pr<strong>in</strong>zip der Datenverarbeitung<br />

3 Kapitel 30 auf Seite 323<br />

4 Abschnitt 27.1.2 auf Seite 273<br />

5 Kapitel 36 auf Seite 407<br />

6 Kapitel 19 auf Seite 173<br />

7 Kapitel 16 auf Seite 141<br />

8 Kapitel 34 auf Seite 389<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Fat%20Client<br />

375


33. Trigger<br />

33.1. Die Def<strong>in</strong>ition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378<br />

33.2. Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379<br />

33.3. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 381<br />

33.4. Übungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382<br />

33.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386<br />

E<strong>in</strong> Trigger ist so etwas wie e<strong>in</strong>e Rout<strong>in</strong>e zur Ereignisbehandlung: Immer dann,<br />

wenn sich <strong>in</strong> der Datenbank e<strong>in</strong>e bestimmte Situation ereignet, wird e<strong>in</strong>e spezielle<br />

Prozedur automatisch ausgeführt.<br />

Schon die kurze E<strong>in</strong>leitung weist darauf h<strong>in</strong>: E<strong>in</strong> Trigger wird niemals vom Benutzer<br />

gezielt aufgerufen, sondern immer automatisch vom DBMS erledigt; dar<strong>in</strong><br />

unterscheidet er sich wesentlich von eigenen Funktionen oder Prozeduren.<br />

E<strong>in</strong>e deutsche Übersetzung wäre eigentlich Auslöser; besser passen<br />

würde „etwas, das ausgelöst wird“. Allenfalls spricht man noch von<br />

Ereignisbehandlungsrout<strong>in</strong>en. Solche Formulierungen s<strong>in</strong>d aber ungebräuchlich;<br />

der Begriff Trigger hat sich e<strong>in</strong>gebürgert.<br />

E<strong>in</strong> Trigger ist <strong>in</strong> folgenden Situationen nützlich:<br />

• Werte <strong>in</strong> e<strong>in</strong>er Zeile e<strong>in</strong>er Tabelle sollen festgelegt werden.<br />

• Dies wird vor allem benutzt, wenn ke<strong>in</strong>e Spalte als AUTO_INCREMENT festgelegt<br />

ist, siehe das Beispiel mit der nächsten ID.<br />

• Werte sollen vor dem Speichern auf ihre Plausibilität geprüft werden.<br />

• Veränderungen <strong>in</strong> der Datenbank sollen automatisch protokolliert werden.<br />

• Die Regeln der referenziellen Integrität sollen mit Hilfe der Fremdschlüssel-<br />

Beziehungen überwacht werden.<br />

Bitte haben Sie Nachsicht: Wegen der vielen Varianten bei den DBMS beschränkt<br />

sich auch dieses Kapitel bei den H<strong>in</strong>weisen und Beispielen weitgehend auf Firebird.<br />

Zusammen mit den speziellen H<strong>in</strong>weisen zur <strong>SQL</strong>-Programmierung sollten<br />

sie problemlos an andere DBMS-Regeln angepasst werden können.<br />

377


Trigger<br />

33.1. Die Def<strong>in</strong>ition<br />

33.1.1. Trigger erstellen<br />

Die Syntax für die Def<strong>in</strong>ition e<strong>in</strong>es Triggers sieht grundsätzlich so aus:<br />

CREATE OR ALTER TRIGGER FOR <br />

[ ACTIVE | INACTIVE ]<br />

{ BEFORE | AFTER } /* bei MS-<strong>SQL</strong> heißt es INSTEAD OF */<br />

{ INSERT | UPDATE | DELETE }<br />

[ POSITION ]<br />

AS<br />

BEGIN<br />

[ ]<br />

<br />

END<br />

Auch dabei s<strong>in</strong>d wieder die Besonderheiten des jeweiligen DBMS zu beachten.<br />

Aber die Ähnlichkeit zu den Rout<strong>in</strong>en fällt auf.<br />

Notwendig s<strong>in</strong>d folgende Angaben:<br />

• neben dem Befehlsnamen CREATE TRIGGER der Name des Triggers<br />

• dazu die Tabelle, zu der er gehört<br />

• mehrere Angaben, bei welchem Befehl und an welcher Stelle er wirksam se<strong>in</strong><br />

soll; Oracle kennt neben den Varianten, die bei der Syntax genannt werden,<br />

auch e<strong>in</strong>e WHEN-Bed<strong>in</strong>gung.<br />

• das Schlüsselwort AS als Zeichen für den Inhalt<br />

• die Schlüsselwörter BEGIN und END als Begrenzer für den Rumpf, also den<br />

eigentlichen Inhalt<br />

H<strong>in</strong>zu kommen die folgenden Angaben:<br />

• e<strong>in</strong>e Liste von Variablen, die <strong>in</strong>nerhalb der Rout<strong>in</strong>e verwendet werden; diese<br />

Liste steht je nach DBMS zwischen AS und BEGIN oder <strong>in</strong>nerhalb des Rumpfes<br />

• die Anweisungen, die <strong>in</strong>nerhalb der Prozedur ausgeführt werden sollen<br />

E<strong>in</strong>gabe- und Ausgabe-Parameter gibt es nicht, weil es sich um automatische<br />

Arbeitsabläufe handelt.<br />

Die folgenden Variablen stehen immer zur Verfügung. MS-<strong>SQL</strong> benutzt andere<br />

Verfahren; bei Oracle ist die Dokumentation nicht e<strong>in</strong>deutig. 1<br />

• OLD ist der Datensatz vor e<strong>in</strong>er Speicherung.<br />

1 E<strong>in</strong>erseits heißt es, dass diese Variablen immer bekannt s<strong>in</strong>d; andererseits s<strong>in</strong>d sie sowohl <strong>in</strong> der<br />

Def<strong>in</strong>ition als auch <strong>in</strong> den meisten Beispielen <strong>in</strong> e<strong>in</strong>er REFERENCING-Klausel aufgeführt.<br />

378


• NEW ist der Datensatz nach e<strong>in</strong>er Speicherung.<br />

Beispiele<br />

Mit diesen Variablen zusammen mit den Spaltennamen können die Werte „vorher“<br />

und „nachher“ geprüft und bearbeitet werden (siehe die Beispiele).<br />

Die Anweisungen <strong>in</strong>nerhalb des Triggers s<strong>in</strong>d sowohl „normale“ <strong>SQL</strong>-Befehle als<br />

auch Bestandteile der <strong>SQL</strong>-Programmiersprache. Zum Semikolon, das den Abschluss<br />

des CREATE-Befehls darstellen sollte, aber <strong>in</strong>nerhalb des Inhalts bereits<br />

für jede e<strong>in</strong>zelne Anweisung benutzt wird, beachten Sie bitte wiederum die H<strong>in</strong>weise<br />

zur <strong>SQL</strong>-Programmierung unter Anweisungen begrenzen 2 .<br />

33.1.2. Trigger deaktivieren<br />

Natürlich kann e<strong>in</strong> Trigger gelöscht werden:<br />

DROP TRIGGER ;<br />

Wenn das DBMS es ermöglicht, kann er auch vorübergehend deaktiviert werden.<br />

ALTER TRIGGER INACTIVE;<br />

Bei ALTER s<strong>in</strong>d nur die Angaben erforderlich, die sich ändern.<br />

E<strong>in</strong>en Trigger vorübergehend abzuschalten, ist vor allem während e<strong>in</strong>es längeren<br />

Arbeitsablaufs <strong>in</strong> zwei Situationen hilfreich:<br />

• E<strong>in</strong> Trigger bremst sehr stark; dann kann z. B. auf e<strong>in</strong>e Protokollierung jeder<br />

kle<strong>in</strong>en Änderung verzichtet werden.<br />

• Wenn Widersprüche <strong>in</strong> den Daten auftreten könnten, die sich nach vollständiger<br />

Erledigung „von selbst auflösen“, können sie durch die Deaktivierung<br />

vermieden werden.<br />

33.2. Beispiele<br />

33.2.1. Lege die nächste ID fest<br />

Wenn e<strong>in</strong> DBMS ke<strong>in</strong>en automatischen Zähler, also ke<strong>in</strong>e AUTO_INCREMENT-<br />

Spalte ermöglicht, kann e<strong>in</strong>e ID vom Anwender vergeben werden. Viel besser ist<br />

aber, wenn sich das DBMS selbst darum kümmert. Dies wird <strong>in</strong> der Beispieldatenbank<br />

bei Firebird und Oracle benutzt. Zum e<strong>in</strong>en wird e<strong>in</strong>e SEQUENCE def<strong>in</strong>iert;<br />

der Trigger holt den nächsten Wert:<br />

2 Kapitel 30 auf Seite 323<br />

379


Trigger<br />

Oracle-Quelltext<br />

CREATE OR REPLACE TRIGGER Abteilung_BI<br />

BEFORE INSERT ON Abteilung<br />

FOR EACH ROW<br />

WHEN (new.ID IS NULL)<br />

BEGIN<br />

SELECT Abteilung_ID.NEXTVAL INTO :new.ID FROM DUAL;<br />

END<br />

Der Trigger wird vor e<strong>in</strong>em INSERT-Befehl aufgerufen. Wenn für den neuen Datensatz<br />

− repräsentiert durch die Variable new − ke<strong>in</strong>e ID angegeben ist, dann<br />

soll der NEXTVAL gemäß der SEQUENCE Abteilung_ID geholt werden.<br />

Der Name des Triggers Abteilung_BI soll andeuten, dass er zur Tabelle Abteilung<br />

gehört und wie folgt aktiv ist: B (= Before) I (= Insert). Bei Firebird fügen wir noch<br />

e<strong>in</strong>e '0' an zur Festlegung, dass er an Position Null auszuführen ist.<br />

33.2.2. Protokolliere Zeit und Benutzer bei Speicherungen<br />

In vielen Situationen ist es wichtig, dass Änderungen <strong>in</strong> e<strong>in</strong>er Datenbank kontrolliert<br />

werden können. (Das DBMS macht es für sich sowieso, aber oft soll es<br />

auch „nach außen h<strong>in</strong>“ sichtbar se<strong>in</strong>.) Dafür gibt es solche Spalten (<strong>in</strong> der Beispieldatenbank<br />

verzichten wir darauf):<br />

CREATE TABLE /* usw. bis */<br />

Last_User VARCHAR(30),<br />

Last_Change TIMESTAMP,<br />

/* usw. */<br />

Wenn solche Spalten bei der Tabelle Mitarbeiter vorhanden wären, gäbe es e<strong>in</strong>en<br />

passenden Trigger. Ohne dass sich der Anwender darum kümmern müsste, werden<br />

Benutzer (laut Anmeldung an der Datenbank) und aktuelle Zeit immer gespeichert.<br />

380<br />

Firebird-Quelltext<br />

CREATE OR ALTER TRIGGER Mitarbeiter_BU1 FOR Mitarbeiter<br />

ACTIVE BEFORE INSERT OR UPDATE POSITION 1<br />

AS<br />

BEGIN<br />

new.Last_User = CURRENT_USER;<br />

new.Last_Change = CURRENT_TIMESTAMP;<br />

END


33.2.3. Aktualisiere abhängige Daten<br />

Zusammenfassung<br />

In vielen Fällen gibt es Abhängigkeiten zwischen Tabellen. Durch Fremdschlüssel<br />

können diese Verb<strong>in</strong>dungen bei Änderungen automatisch berücksichtigt<br />

werden. Solche Anpassungen s<strong>in</strong>d auch durch Trigger möglich.<br />

Mit e<strong>in</strong>er Prozedur (siehe dort) hatten wir e<strong>in</strong> INSERT <strong>in</strong> mehrere Tabellen ausgeführt.<br />

Ähnlich kann e<strong>in</strong> Trigger abhängige Datensätze löschen:<br />

• Der Anwender ruft e<strong>in</strong> DELETE für e<strong>in</strong>en Versicherungsvertrag auf.<br />

• Der Trigger löscht dazu den Versicherungsnehmer und das Fahrzeug.<br />

• E<strong>in</strong> Trigger zur Tabelle Fahrzeug löscht alle registrierten Schadensfälle usw.<br />

Aber das ist so komplex und hat Auswirkungen auf weitere Tabellen, dass wir<br />

darauf verzichten.<br />

In e<strong>in</strong>er „echten“ Firma müssten solche Informationen sowieso dauerhaft<br />

gespeichert werden − zum<strong>in</strong>dest so lange wie die Daten der<br />

Buchhaltung. E<strong>in</strong> Vertrag wird also im normalen Betrieb niemals gelöscht,<br />

sondern als „nicht mehr aktiv“ markiert.<br />

Aber auch <strong>in</strong> unserer Firma gibt es s<strong>in</strong>nvolle Maßnahmen:<br />

Aufgabe: Beim Ausscheiden e<strong>in</strong>es Mitarbeiters wird (soweit vorhanden) e<strong>in</strong><br />

persönlicher Dienstwagen gestrichen.<br />

Firebird-Quelltext<br />

CREATE OR ALTER TRIGGER Mitarbeiter_BD1 for Mitarbeiter<br />

ACTIVE BEFORE DELETE POSITION 1<br />

AS<br />

BEGIN<br />

UPDATE Dienstwagen<br />

SET Mitarbeiter_ID = NULL<br />

WHERE Mitarbeiter_ID = old.ID;<br />

END<br />

Bevor e<strong>in</strong> Mitarbeiter gelöscht wird, wird geprüft, ob ihm e<strong>in</strong> persönlicher<br />

Dienstwagen zugewiesen ist. Dieser Vermerk wird (sofern vorhanden) auf NULL<br />

gesetzt; die WHERE-Klausel greift dabei auf die ID des bisherigen Datensatzes<br />

der Tabelle Mitarbeiter zu.<br />

33.3. Zusammenfassung<br />

Dieses Kapitel erläuterte Trigger als automatisch ausgeführte Rout<strong>in</strong>en:<br />

• E<strong>in</strong> Trigger wird vor oder nach e<strong>in</strong>em Speichern-Befehl (Insert, Update, Delete)<br />

ausgeführt.<br />

381


Trigger<br />

• Er dient zur automatischen Prüfung oder Vervollständigung von Werten.<br />

• Damit vere<strong>in</strong>facht er die Arbeit für den Anwender und<br />

• s<strong>org</strong>t für die E<strong>in</strong>haltung von Sicherheitsregeln <strong>in</strong> der Datenbank.<br />

i H<strong>in</strong>weis<br />

Bedenken Sie die konkreten Situationen aus den letzten Kapiteln und den<br />

Übungen. Diese Zusammenhänge zwischen Tabellen müssen bei der Planung<br />

e<strong>in</strong>er Datenbank mit Fremdschlüsseln, Prozeduren und Triggern für<br />

„gleichzeitiges“ E<strong>in</strong>fügen, Ändern oder Löschen und bei notwendigen Maßnahmen<br />

<strong>in</strong> mehreren Tabellen berücksichtigt werden.<br />

Auch <strong>in</strong> dieser H<strong>in</strong>sicht bietet unsere Beispieldatenbank nur e<strong>in</strong>en kle<strong>in</strong>en<br />

E<strong>in</strong>blick <strong>in</strong> das, was mit <strong>SQL</strong> möglich ist.<br />

33.4. Übungen<br />

Die Lösungen beg<strong>in</strong>nen auf Seite 384.<br />

Übung 1 – Trigger def<strong>in</strong>ieren<br />

Welche der folgenden Aussagen s<strong>in</strong>d wahr, welche s<strong>in</strong>d falsch?<br />

1. E<strong>in</strong> Trigger wird durch e<strong>in</strong>e Prozedur aufgerufen.<br />

2. E<strong>in</strong> Trigger hat ke<strong>in</strong>e E<strong>in</strong>gabe-Parameter.<br />

3. E<strong>in</strong> Trigger hat ke<strong>in</strong>e (lokalen) Variablen.<br />

4. E<strong>in</strong> Trigger kann nur die Tabelle bearbeiten, der er zugeordnet ist.<br />

5. E<strong>in</strong> Trigger kann sowohl „normale“ <strong>SQL</strong>-Befehle als auch Elemente der<br />

<strong>SQL</strong>-Programmierung enthalten.<br />

Übung 2 – Trigger verwenden<br />

In welchen der folgenden Situationen ist e<strong>in</strong> Trigger s<strong>in</strong>nvoll, <strong>in</strong> welchen nicht<br />

bzw. nicht möglich? Gehen Sie davon aus, dass alle benötigten Informationen <strong>in</strong><br />

der Firmen-Datenbank zur Speicherung v<strong>org</strong>esehen s<strong>in</strong>d.<br />

382<br />

1. Wenn e<strong>in</strong> Versicherungsvertrag neu aufgenommen wird, sollen auch alle<br />

Datensätze <strong>in</strong> zugeordneten Tabellen neu aufgenommen werden.<br />

2. V<strong>org</strong>abewerte für e<strong>in</strong>zelne Spalten können e<strong>in</strong>getragen werden (wie bei e<strong>in</strong>em<br />

DEFAULT-Wert).<br />

3. Wenn e<strong>in</strong>e Abteilung gelöscht (d. h. geschlossen) wird, sollen auch alle ihre<br />

Mitarbeiter gelöscht werden (weil die Abteilung ausgelagert wird).


Übungen<br />

4. Wenn e<strong>in</strong> Versicherungsvertrag gelöscht wird, sollen auch alle Datensätze<br />

<strong>in</strong> zugeordneten Tabellen bearbeitet werden: Datensätze, die noch <strong>in</strong> anderem<br />

Zusammenhang benutzt werden (z. B. Mitarbeiter), bleiben unverändert;<br />

Datensätze, die sonst nicht mehr benutzt werden (z. B. Fahrzeug),<br />

werden gelöscht.<br />

Übung 3 – Trigger-Def<strong>in</strong>ition kontrollieren<br />

Nennen Sie <strong>in</strong> der folgenden Def<strong>in</strong>ition Punkte, die unabhängig vom <strong>SQL</strong>-<br />

Dialekt falsch s<strong>in</strong>d. Je nach DBMS s<strong>in</strong>d noch andere Punkte falsch, danach wird<br />

aber nicht gefragt. Der Trigger soll folgende Aufgabe erledigen:<br />

• Bei der Aufnahme e<strong>in</strong>es neues Fahrzeugtyps ist der Hersteller zu überprüfen.<br />

• Ist der angegebene Hersteller (d. h. die Hersteller_ID) <strong>in</strong> der Tabelle Fahrzeughersteller<br />

gespeichert? Wenn ja, dann ist nichts weiter zu erledigen.<br />

• Wenn ne<strong>in</strong>, dann ist e<strong>in</strong> Fahrzeughersteller mit dem Namen „unbekannt“ zu<br />

suchen.<br />

• Wenn dieser vorhanden ist, ist dessen ID als Hersteller_ID zu übernehmen.<br />

• Andernfalls ist er mit der angegebenen Hersteller_ID neu als „unbekannt“<br />

zu registrieren.<br />

1. CREATE Fahrzeugtyp_Check_Hersteller AS TRIGGER<br />

2. TO Fahrzeugtyp<br />

3. FOR INSERT<br />

4. AS<br />

5. DECLARE VARIABLE INTEGER fh_id<br />

6. BEGIN<br />

7. -- Initialisierung<br />

8. fh_id = NULL;<br />

9. -- prüfen, ob ID vorhanden ist<br />

10. SELECT ID<br />

11. FROM Fahrzeughersteller<br />

12. WHERE ID = new.Hersteller_ID<br />

13. -- wenn sie nicht gefunden wurde<br />

14. IF (fh_id IS NULL) THEN<br />

15. -- suche stattdessen den Hersteller ’unbekannt’<br />

16. SELECT ID<br />

17. FROM Fahrzeughersteller<br />

18. WHERE Name = ’unbekannt’<br />

19. -- wenn auch dazu die ID nicht gefunden wurde<br />

20. IF (fh_id IS NULL) THEN<br />

21. fh_id = new.Hersteller_ID;<br />

22. INSERT INTO Fahrzeughersteller<br />

23. VALUES ( :fh_id, ’unbekannt’, ’unbekannt’ )<br />

24. END<br />

25. END<br />

26. new.Hersteller_ID = :fh_id<br />

27. END<br />

383


Trigger<br />

Übung 4 – Trigger-Def<strong>in</strong>ition kontrollieren<br />

Berichtigen Sie den Code der vorigen Aufgabe.<br />

Übung 5 – Trigger Mitarbeiter_On_Delete erstellen<br />

Skizzieren Sie den Inhalt e<strong>in</strong>es Triggers Mitarbeiter_On_Delete für die Tabelle<br />

Mitarbeiter, der folgende Aufgaben ausführen soll:<br />

• Zu behandeln ist die Situation, dass e<strong>in</strong> Mitarbeiter aus der Firma ausscheidet,<br />

also zu löschen ist.<br />

• Suche die ID des zugehörigen Abteilungsleiters.<br />

• In allen Tabellen und Datensätzen, <strong>in</strong> denen der Mitarbeiter als Sachbearbeiter<br />

registriert ist, ist stattdessen der Abteilungsleiter als „zuständig“ e<strong>in</strong>zutragen.<br />

Ignorieren Sie die Situation, dass der Abteilungsleiter selbst ausscheidet.<br />

Übung 6 – Trigger Mitarbeiter_On_Delete erstellen<br />

Erstellen Sie den Trigger Mitarbeiter_On_Delete aus der vorigen Übung.<br />

Lösungen<br />

Die Aufgaben beg<strong>in</strong>nen auf Seite 382.<br />

Lösung zu Übung 1 – Trigger def<strong>in</strong>ieren<br />

Die Aussagen 2, 5 s<strong>in</strong>d wahr. Die Aussagen 1, 3, 4 s<strong>in</strong>d falsch.<br />

Lösung zu Übung 2 – Trigger verwenden<br />

1. nicht möglich, weil die Werte für die Datensätze <strong>in</strong> den zugeordneten Tabellen<br />

nicht bekannt s<strong>in</strong>d<br />

2. s<strong>in</strong>nvoll, aber die Festlegung per DEFAULT ist vorzuziehen<br />

3. zur Not s<strong>in</strong>nvoll, wenn die Fremdschlüssel nicht durch ON DELETE geeignet<br />

gesteuert werden können; aber dies ist riskannt wegen der „Nebenwirkungen“,<br />

siehe Übung 5/6<br />

4. zur Not möglich, aber eher weniger s<strong>in</strong>nvoll wegen der vielen zugeordneten<br />

Tabellen und vieler „Nebenwirkungen“<br />

Lösung zu Übung 3 – Trigger-Def<strong>in</strong>ition kontrollieren<br />

• Zeile 1: Der Befehl lautet: CREATE TRIGGER .<br />

384


• Zeile 2: Die Tabelle steht je nach DBMS bei ON oder FOR, nicht bei TO.<br />

Übungen<br />

• Zeile 3: Die Aktivierung BEFORE oder AFTER (bei MS-<strong>SQL</strong> INSTEAD OF) fehlt.<br />

• Zeile 5: Zuerst kommt der Name der Variablen, dann der Datentyp; das abschließende<br />

Semikolon fehlt.<br />

• Zeile 10/12 und 16/18: Es fehlt die Übergabe der ID, die durch SELECT gefunden<br />

wurde, an die Variable fh_id. Je nach DBMS erfolgt dies unterschiedlich,<br />

aber es muss gemacht werden, damit das Ergebnis der Abfrage <strong>in</strong> der Variablen<br />

gespeichert und danach verwendet werden kann.<br />

• Zeile 12, 18, 23, 26: Es fehlt jeweils das abschließende Semikolon.<br />

• Zeile 14 und 20: Mehrere Anweisungen s<strong>in</strong>d <strong>in</strong> BEGIN...END e<strong>in</strong>zuschließen.<br />

Lösung zu Übung 4 – Trigger-Def<strong>in</strong>ition kontrollieren<br />

Firebird-Quelltext<br />

CREATE OR ALTER TRIGGER Fahrzeugtyp_Check_Hersteller<br />

FOR Fahrzeugtyp<br />

ACTIVE BEFORE INSERT POSITION 10<br />

AS<br />

DECLARE VARIABLE fh_id INTEGER;<br />

BEGIN<br />

fh_id = NULL;<br />

SELECT ID<br />

FROM Fahrzeughersteller<br />

WHERE ID = new.Hersteller_ID<br />

INTO :fh_id;<br />

IF (fh_id IS NULL) THEN<br />

BEGIN<br />

SELECT Id<br />

FROM Fahrzeughersteller<br />

WHERE Name = ’unbekannt’<br />

INTO :fh_id;<br />

IF (fh_id IS NULL) THEN<br />

BEGIN<br />

fh_id = new.Hersteller_ID;<br />

INSERT INTO Fahrzeughersteller<br />

VALUES ( :fh_id, ’unbekannt’, ’unbekannt’ );<br />

END<br />

END<br />

new.Hersteller_ID = :fh_id;<br />

END<br />

Lösung zu Übung 5 – Trigger Mitarbeiter_On_Delete erstellen<br />

• Wir brauchen e<strong>in</strong>en Trigger BEFORE DELETE zur Tabelle Mitarbeiter.<br />

• Wir brauchen e<strong>in</strong>e Variable für die ID des Abteilungsleiters.<br />

385


Trigger<br />

• Durch e<strong>in</strong>en SELECT auf die Abteilung_ID des ausscheidenden Mitarbeiters<br />

bekommen wir die ID des Abteilungsleiters; die wird <strong>in</strong> der Variablen gespeichert.<br />

• Für alle betroffenen Tabellen ist e<strong>in</strong> UPDATE zu machen, durch das die<br />

Mitarbeiter_ID durch die ID des Abteilungsleiters ersetzt wird. Dies betrifft<br />

die Tabellen Schadensfall, Versicherungsvertrag.<br />

• Die Tabelle Dienstwagen kann unberücksichtigt bleiben; das erledigt der Trigger<br />

aus dem obigen Beispiel „Aktualisiere abhängige Daten“.<br />

Lösung zu Übung 6 – Trigger Mitarbeiter_On_Delete erstellen<br />

Firebird-Quelltext<br />

CREATE OR ALTER TRIGGER Mitarbeiter_On_Delete<br />

FOR Mitarbeiter<br />

ACTIVE BEFORE DELETE POSITION 10<br />

AS<br />

DECLARE VARIABLE ltr_id INTEGER;<br />

BEGIN<br />

ltr_id = NULL;<br />

-- hole die ID des Abteilungsleiters <strong>in</strong> die Variable<br />

SELECT ID<br />

FROM Mitarbeiter<br />

WHERE Abteilung_id = old.Abteilung_ID<br />

AND Ist_Leiter = ’J’<br />

INTO :ltr_id;<br />

-- ändere die Mitarbeiter_ID für die Schadensfälle<br />

UPDATE Schadensfall<br />

SET Mitarbeiter_ID = :ltr_id<br />

WHERE Mitarbeiter_ID = old.ID;<br />

-- ändere die Mitarbeiter_ID für die Verträge<br />

UPDATE Versicherungsvertrag<br />

SET Mitarbeiter_ID = :ltr_id<br />

WHERE Mitarbeiter_ID = old.ID;<br />

END<br />

33.5. Siehe auch<br />

Verschiedene E<strong>in</strong>zelheiten stehen <strong>in</strong> folgenden Kapiteln:<br />

• Fremdschlüssel-Beziehungen 3<br />

• <strong>SQL</strong>-Programmiersprache 4<br />

3 Kapitel 29 auf Seite 305<br />

4 Kapitel 30 auf Seite 323<br />

386


• Prozeduren 5<br />

Über Wikipedia s<strong>in</strong>d grundlegende Informationen zu erhalten:<br />

• Datenbanktrigger 6<br />

• Ereignisse 7 <strong>in</strong> der Programmierung, Behandlung von Ereignissen<br />

• Referentielle Integrität 8<br />

5 Kapitel 32 auf Seite 357<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/Datenbanktrigger<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Ereignis%20(Programmierung)<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Referentielle%20Integrit%c3%a4t<br />

Siehe auch<br />

387


34. Tipps und Tricks<br />

34.1. Die letzte ID abfragen . . . . . . . . . . . . . . . . . . . . . . . . . 389<br />

34.2. Tabellenstruktur auslesen . . . . . . . . . . . . . . . . . . . . . . 391<br />

34.3. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395<br />

Dieses Kapitel stellt e<strong>in</strong> paar nützliche Verfahren vor, die <strong>in</strong> ke<strong>in</strong>es der anderen<br />

Kapitel passten und „zu wenig Stoff“ für e<strong>in</strong> eigenes Kapitel enthalten.<br />

34.1. Die letzte ID abfragen<br />

Wenn bei e<strong>in</strong>er Tabelle für die Spalte ID die neuen Werte automatisch als<br />

AUTO_INCREMENT vergeben werden, benötigt man den neu vergebenen Wert<br />

häufig für die korrekte Behandlung der Fremdschlüssel-Beziehungen.<br />

34.1.1. Firebird: Rückgabewerte benutzen<br />

Seit Firebird 2.x kann der INSERT-Befehl Maßnahmen, die durch e<strong>in</strong>en Before-<br />

Insert-Trigger ausgeführt werden – also auch die Zuweisung e<strong>in</strong>er Sequence –<br />

durch e<strong>in</strong>e RETURNING-Klausel abfragen:<br />

INSERT INTO ( )<br />

VALUES ( )<br />

RETURNING [ INTO ]<br />

Die INTO-Klausel wird bei Aufgaben <strong>in</strong>nerhalb der <strong>SQL</strong>-Programmierung benutzt.<br />

Beispiel für direkten Aufruf:<br />

Firebird-Quelltext<br />

INSERT INTO Fahrzeug ( Kennzeichen, Fahrzeugtyp_ID )<br />

VALUES ( ’B-JT 1234’, 7 )<br />

RETURNING ID, Farbe;<br />

389


Tipps und Tricks<br />

------ Inserted values ------<br />

ID = 652<br />

FARBE = <br />

Die RETURNING-Klausel gibt es auch bei DB2 und Oracle.<br />

34.1.2. MS-<strong>SQL</strong>: spezielle Abfragen<br />

Je nach Situation wird nach e<strong>in</strong>em INSERT <strong>in</strong> e<strong>in</strong>em weiteren Befehl der neu<br />

zugeordnete Wert abgefragt.<br />

• Variante 1 mit e<strong>in</strong>er lokalen Variablen:<br />

SELECT @@IDENTITY<br />

Dies liefert den letzten für e<strong>in</strong>e IDENTITY-Spalte vergebenen Wert der aktuellen<br />

Verb<strong>in</strong>dung zurück. Hierbei wird ke<strong>in</strong>e Tabelle angegeben; es kann aber<br />

auch der Wert e<strong>in</strong>er anderen Tabelle geliefert werden (beispielsweise wenn <strong>in</strong>direkt<br />

über e<strong>in</strong>en Trigger e<strong>in</strong>e weitere Tabelle bearbeitet wird).<br />

• Variante 2 mit e<strong>in</strong>er Funktion:<br />

SELECT SCOPE_IDENTITY();<br />

Dies liefert den letzten für e<strong>in</strong>e IDENTITY-Spalte vergebenen Wert der aktuellen<br />

Verb<strong>in</strong>dung zurück.<br />

• Variante 3 mit e<strong>in</strong>er Funktion, die sich auf e<strong>in</strong>e bestimmte Tabelle bezieht:<br />

SELECT IDENT_CURRENT(’Fahrzeug’);<br />

Zu den Unterschieden zwischen diesen Verfahren siehe MSDN: @@IDENTITY 1 .<br />

34.1.3. My<strong>SQL</strong>: spezielle Abfragen<br />

Unmittelbar nach e<strong>in</strong>em INSERT wird <strong>in</strong> e<strong>in</strong>em weiteren Befehl der neu zugeordnete<br />

Wert mit e<strong>in</strong>er speziellen Funktion abgefragt:<br />

SELECT LAST_INSERT_ID();<br />

1 http://msdn.microsoft.com/de-de/library/ms187342.aspx<br />

390


Tabellenstruktur auslesen<br />

Dies liefert immer den letzten für e<strong>in</strong>e AUTO_INCREMENT-Spalte vergebenen<br />

Wert der aktuellen Verb<strong>in</strong>dung zurück. Hierbei wird ke<strong>in</strong>e Tabelle angegeben. Je<br />

nach Arbeitsumgebung gibt es auch die <strong>in</strong>terne Funktion mysql_<strong>in</strong>sert_id(), die<br />

manchmal (siehe Dokumentation) abweichende Ergebnisse liefert.<br />

34.1.4. Oracle: Wert abfragen<br />

In Oracle dient die SEQUENCE zur Vergabe e<strong>in</strong>es automatischen Zählers.<br />

Mit .NEXTVAL wird der nächste Wert zugewiesen, mit<br />

.CURRVAL der aktuelle Wert abgefragt.<br />

Im Skript zur Beispieldatenbank wird für die Tabelle Mitarbeiter e<strong>in</strong>e SEQUENCE<br />

Mitarbeiter_ID def<strong>in</strong>iert und verwendet. Für e<strong>in</strong>en neuen Mitarbeiter erhält<br />

man so die zugewiesene ID:<br />

Oracle Quelltext<br />

INSERT INTO MITARBEITER<br />

( ID, PERSONALNUMMER, NAME, VORNAME /* und weitere Angaben */ )<br />

VALUES ( Mitarbeiter_ID.NEXTVAL, ’80017’, ’Schicker’, ’Madela<strong>in</strong>e’ );<br />

SELECT CONCAT(’Last ID = ’ , TO_CHAR(Mitarbeiter_ID.CURRVAL)) FROM dual;<br />

1 row <strong>in</strong>serted;<br />

Last ID = 23<br />

Weitere Informationen über Sequenzen bei Oracle siehe Sequenzen 2 .<br />

34.2. Tabellenstruktur auslesen<br />

Die Tabellen, Spalten, Views, Fremdschlüssel usw. werden <strong>in</strong> der Datenbank <strong>in</strong><br />

systemeigenen Strukturen gespeichert. Sie können genauso wie die „eigenen“<br />

Daten per SELECT abgefragt werden. Auch wenn der <strong>SQL</strong>-Standard dafür mit<br />

INFORMATION_SCHEMA e<strong>in</strong> detailliertes Muster v<strong>org</strong>esehen hat, gibt es Unterschiede<br />

zwischen den DBMS.<br />

34.2.1. DB2<br />

Bei DB2 spricht man von Katalog-Tabellen. Sie bef<strong>in</strong>den sich im Schema<br />

SYSIBM. E<strong>in</strong>ige Beispiel-Zugriffe auf die Katalog-Tabellen.<br />

2 http://de.wikibooks.<strong>org</strong>/wiki/Oracle%3a%20Sequenzen<br />

391


Tipps und Tricks<br />

-- Liste aller Tabellen <strong>in</strong> e<strong>in</strong>er Datenbank, die von P123 erstellt wurden.<br />

SELECT creator, name<br />

FROM sysibm.systables<br />

WHERE creator = ’P123’<br />

AND type = ’T’<br />

--<br />

-- Liste aller Views <strong>in</strong> e<strong>in</strong>er Datenbank, die von P123 erstellt wurden.<br />

SELECT creator, name<br />

FROM sysibm.systables<br />

WHERE creator = ’P123’<br />

AND type = ’V’<br />

--<br />

-- Liste aller Fremdschlüssel-Beziehungen, die von P123 erstellt wurden.<br />

-- TBNAME ist die (Detail-)Tabelle mit dem Fremdschlüssel<br />

-- REFTBNAME ist die (Master-)Tabelle, auf die der Fremdschlüssel verweist<br />

SELECT creator, tbname, reftbname<br />

FROM sysibm.sysrels<br />

WHERE creator = ’P123’<br />

34.2.2. Firebird<br />

Bei Firebird und Interbase beg<strong>in</strong>nen alle Bezeichner der „Systemtabellen“ mit<br />

RDB$.<br />

392<br />

-- listet die Tabellen e<strong>in</strong>er Datenbank auf<br />

SELECT rdb$relation_name FROM rdb$relations<br />

WHERE rdb$system_flag = 0<br />

AND rdb$relation_type = 0;<br />

-- listet die Views e<strong>in</strong>er Datenbank auf<br />

SELECT rdb$relation_name FROM rdb$relations<br />

WHERE rdb$system_flag = 0<br />

AND rdb$relation_type = 1;<br />

-- listet die Spaltennamen mit den dazugehörigen Datentypen e<strong>in</strong>er Tabelle auf<br />

SELECT rdb$relation_name, rdb$field_name, rdb$field_source<br />

FROM rdb$relation_fields<br />

WHERE rdb$system_flag = 0<br />

AND rdb$view_context IS NULL<br />

ORDER BY rdb$relation_name, rdb$field_position<br />

-- listet die Fremdschlüssel e<strong>in</strong>er Tabelle auf (i.d.R. nur zwischen je e<strong>in</strong>er Spalte)<br />

SELECT rel.RDB$Constra<strong>in</strong>t_Name AS ForeignKey,<br />

co1.RDB$Relation_Name AS DetailTable,<br />

CASE idx.RDB$Segment_Count<br />

WHEN 1 THEN fl1.RDB$Field_Name<br />

ELSE idx.RDB$Segment_Count<br />

END AS Fields,<br />

rel.RDB$Const_Name_UQ AS PrimaryKey,<br />

co2.RDB$Relation_Name AS MasterTable,<br />

fl2.RDB$Field_Name AS MasterField<br />

FROM RDB$Ref_Constra<strong>in</strong>ts rel


* RDB$Relation_Constra<strong>in</strong>ts wird zwei Mal benötigt:<br />

als co1 für den Tabellennamen des ForeignKey<br />

als co2 für den Tabellennamen des PrimaryKey,<br />

auf den sich der ForeignKey bezieht */<br />

/* ebenso RDB$Index_Segments<br />

als fl1 für den Spaltennamen des FK<br />

als fl2 für den Spaltennamen des PK */<br />

JOIN RDB$Relation_Constra<strong>in</strong>ts co1<br />

ON rel.RDB$Constra<strong>in</strong>t_Name = co1.RDB$Constra<strong>in</strong>t_Name<br />

JOIN RDB$Indices idx<br />

ON rel.RDB$Constra<strong>in</strong>t_Name = idx.RDB$Index_Name<br />

JOIN RDB$Relation_Constra<strong>in</strong>ts co2<br />

ON rel.RDB$Const_Name_UQ = co2.RDB$Constra<strong>in</strong>t_Name<br />

JOIN RDB$Index_Segments fl1<br />

ON rel.RDB$Constra<strong>in</strong>t_Name = fl1.RDB$Index_Name<br />

JOIN RDB$Index_Segments fl2<br />

ON rel.RDB$Const_Name_UQ = fl2.RDB$Index_Name<br />

WHERE (NOT rel.RDB$Constra<strong>in</strong>t_Name LIKE ’RDB$’)<br />

ORDER BY rel.RDB$Constra<strong>in</strong>t_Name<br />

/* Liste der Indizes<br />

die Zugehörigkeit zu den Tabellen und die Bedeutung<br />

ergibt sich aus dem Namen des Index:<br />

PK = Primary Key<br />

FK = Foreign Key<br />

MI usw. = Foreign Key auf die Tabelle mi = Mitarbeiter<br />

Unter anderen Bed<strong>in</strong>gungen braucht man geeignete JOINs. */<br />

SELECT * FROM RDB$Index_Segments<br />

WHERE rdb$<strong>in</strong>dex_name NOT STARTING WITH ’RDB$’<br />

ORDER BY rdb$<strong>in</strong>dex_name, rdb$field_position<br />

34.2.3. MS-<strong>SQL</strong> Server<br />

Tabellenstruktur auslesen<br />

-- listet die Tabellen e<strong>in</strong>er Datenbank auf<br />

SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES<br />

WHERE TABLE_TYPE = ’BASE TABLE’<br />

-- listet die Views e<strong>in</strong>er Datenbank auf<br />

SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES<br />

WHERE TABLE_TYPE = ’VIEW’<br />

-- listet die Spaltennamen mit den dazugehörigen Datentypen e<strong>in</strong>er Tabelle auf<br />

SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS<br />

WHERE TABLE_NAME = ’Tabellenname’<br />

34.2.4. Oracle<br />

In Oracle gibt es fast alle Dictionary-Views <strong>in</strong> dreifacher Ausführung:<br />

• Views mit dem Präfix USER_ zeigen die eigenen Objekte an, also die Objekte,<br />

die im eigenen Schema erstellt s<strong>in</strong>d.<br />

393


Tipps und Tricks<br />

• Views mit dem Präfix ALL_ zeigen alle Objekte an, für die man das Zugriffsrecht<br />

hat. Das s<strong>in</strong>d die Objekte im eigenen Schema und auch Objekte <strong>in</strong> anderen<br />

Schemata, für die man durch den GRANT-Befehl e<strong>in</strong>e Zugriffsberechtigung<br />

erhalten hat.<br />

• Auf Views mit dem Präfix DBA_ kann man nur zugreifen, wenn man das Recht<br />

zur Adm<strong>in</strong>istration hat. In dieser View werden alle Objekte der gesamten Datenbank<br />

angezeigt, also auch die, auf die man ke<strong>in</strong>e Zugriffsrechte besitzt.<br />

Alle Oracle Dictionary-Views s<strong>in</strong>d im Manual Reference (nicht: <strong>SQL</strong>-Reference)<br />

beschrieben.<br />

Beispiele:<br />

-- listet alle eigenen Tabellen e<strong>in</strong>er Datenbank auf.<br />

SELECT TABLE_NAME FROM USER_TABLES<br />

-- listet alle Tabellen auf, auf die man zugriffsberechtigt ist.<br />

SELECT TABLE_NAME FROM ALL_TABLES<br />

-- listet alle Tabellen auf, die es <strong>in</strong> der gesamten Datenbank gibt.<br />

SELECT TABLE_NAME FROM DBA_TABLES<br />

-- listet die Views auf<br />

SELECT VIEW_NAME FROM USER_VIEWS<br />

-- listet die Indizes auf. In der Spalte UNIQUENESS ist angegeben,<br />

-- ob es sich um e<strong>in</strong>en e<strong>in</strong>deutigen Index handelt (UNIQUE)<br />

-- oder ob die Index-Werte <strong>in</strong> der Tabelle mehrmals vorkommen dürfen (NONUNIQUE)<br />

SELECT INDEX_NAME, TABLE_NAME, UNIQUENESS FROM USER_INDEXES<br />

-- listet die Spaltennamen mit den dazugehörigen Datentypen e<strong>in</strong>er Tabelle auf<br />

SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, DATA_LENGTH,<br />

DATA_PRECISION, DATA_SCALE, NULLABLE, COLUMN_ID<br />

FROM USER_TAB_COLUMNS<br />

WHERE TABLE_NAME = ’Tabellenname’<br />

ORDER BY COLUMN_ID<br />

-- Liste aller Fremdschlüssel-Beziehungen und anderen Constra<strong>in</strong>ts<br />

-- Fremdschlüssel-Beziehungen haben den Typ ’R’<br />

-- Bei DELETE_RULE =’CASCADE’ handelt es sich<br />

um e<strong>in</strong>e Beziehung mit Löschweitergabe<br />

-- bei ’NO ACTION’ um e<strong>in</strong>e Beziehung mit Lösch-Restriktion.<br />

SELECT CONSTRAINT_NAME, TABLE_NAME, R_CONSTRAINT_NAME,<br />

REFERENCED_TABLE, DELETE_RULE<br />

FROM USER_CONSTRAINTS<br />

WHERE CONSTRAINT_TYPE = ’R’<br />

Es gibt noch e<strong>in</strong>e weitere Gruppe von Dictionary-Views. Diese geben über den<br />

Zustand der Datenbank Auskunft. Diese Views haben den Präfix V$; sie s<strong>in</strong>d<br />

hauptsächlich für die Adm<strong>in</strong>istration wichtig.<br />

Beispiele:<br />

394


-- Anzeigen aller Sessions, die gerade aktiv s<strong>in</strong>d oder zuletzt aktiv waren<br />

SELECT * FROM V$SESSION<br />

-- Anzeigen von Informationen über die aktuelle Datenbank-Instanz.<br />

-- Datenbank-Name, auf welchem Server die Datenbank läuft, Oracle-Version,<br />

-- seit wann die Datenbank aktiv ist, ob aktuell Log<strong>in</strong>s möglich s<strong>in</strong>d<br />

-- uns <strong>in</strong> welchem Status sich die Datenbank bef<strong>in</strong>det.<br />

SELECT INSTANCE_NAME, HOST_NAME, VERSION, STARTUP_TIME, LOGINS, DATABASE_STATUS<br />

FROM V$INSTANCE<br />

Siehe auch<br />

Weitere Erläuterungen dazu stehen im Wikibook über Oracle unter Table 3 ; dort<br />

gibt es auch e<strong>in</strong>e Anleitung, um die E<strong>in</strong>fügereihenfolge 4 zu ermitteln.<br />

34.3. Siehe auch<br />

Weitere Erläuterungen s<strong>in</strong>d <strong>in</strong> den folgenden Kapiteln zu f<strong>in</strong>den:<br />

• Fremdschlüssel-Beziehungen 5<br />

• <strong>SQL</strong>-Programmierung 6<br />

3 http://de.wikibooks.<strong>org</strong>/wiki/Oracle%3a%20Table<br />

4 http://de.wikibooks.<strong>org</strong>/wiki/Oracle%3a%20Table%23E<strong>in</strong>f%c3%bcgereihenfolge%<br />

20ermitteln<br />

5 Kapitel 29 auf Seite 305<br />

6 Kapitel 30 auf Seite 323<br />

395


35. Änderung der<br />

Datenbankstruktur<br />

35.1. Spalten h<strong>in</strong>zufügen und ändern . . . . . . . . . . . . . . . . . . 397<br />

35.2. E<strong>in</strong>schränkungen auf Spalten . . . . . . . . . . . . . . . . . . . . 399<br />

35.3. Indizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401<br />

35.4. Fremdschlüssel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402<br />

35.5. Weitere Anpassungen . . . . . . . . . . . . . . . . . . . . . . . . . 402<br />

35.6. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 405<br />

35.7. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405<br />

In diesem Kapitel werden wir die Erkenntnisse aus den vorhergehenden Kapiteln<br />

benutzen, um die Beispieldatenbank zu ergänzen und zu erweitern, wie es dort<br />

<strong>in</strong> den Anmerkungen angesprochen wurde.<br />

35.1. Spalten h<strong>in</strong>zufügen und ändern<br />

Dazu wird der Befehl ALTER TABLE ... ADD/ALTER verwendet.<br />

Die hier besprochenen Änderungen sowie weitere Angaben zu Spalten s<strong>in</strong>d im<br />

Skript-Spalten 1 zusammengefasst.<br />

35.1.1. Neue Spalten e<strong>in</strong>fügen<br />

Aufgabe: Die Tabelle Versicherungsvertrag benötigt mehrere zusätzliche Spalten<br />

wegen Prämienberechnung und Schadenfreiheitsrabatt.<br />

ALTER TABLE Versicherungsvertrag<br />

ADD [COLUMN] Basispraemie DECIMAL<br />

DEFAULT 500 NOT NULL<br />

CONSTRAINT Vertrag_Basispraemie_Check CHECK(Basispraemie > 0),<br />

ADD [COLUMN] Praemiensatz INTEGER<br />

1 Erläuterungen zu diesem und den anderen Skripts stehen am Ende dieses Kapitels.<br />

397


Änderung der Datenbankstruktur<br />

DEFAULT 100 NOT NULL<br />

CONSTRAINT Vertrag_Praemiensatz_Check CHECK(Praemiensatz > 0),<br />

ADD [COLUMN] Praemienaenderung DATE;<br />

Die CHECK-Prüfungen werden als CONSTRAINT mit Namen festgelegt. Ob das<br />

Wort COLUMN benötigt wird oder weggelassen werden kann oder muss, hängt<br />

(wie mehrfach gesagt) vom DBMS ab.<br />

Der V<strong>org</strong>abewert wird bei den e<strong>in</strong>zelnen Verträgen noch an die Versicherungsart<br />

angepasst, siehe später unter „Weitere Anpassungen“, ebenso Prämiensatz<br />

und Prämienänderung auf e<strong>in</strong>en e<strong>in</strong>heitlichen Stand zum 1. Januar 2007. Danach<br />

werden sie durch e<strong>in</strong>e Stored Procedure nach und nach auf den aktuellen<br />

Stand gebracht − siehe das Beispiel für e<strong>in</strong>e Prozedur „automatisches UPDATE<br />

gemäß Bed<strong>in</strong>gungen“.<br />

Normalerweise sollte der V<strong>org</strong>abewert automatisch überall e<strong>in</strong>getragen werden.<br />

Falls das nicht geschieht, genügt e<strong>in</strong> e<strong>in</strong>ziger UPDATE-Befehl dafür.<br />

35.1.2. Datentyp e<strong>in</strong>er Spalte ändern<br />

Dabei ist unbed<strong>in</strong>gt darauf zu achten, dass die bisherigen Daten zum neuen Datentyp<br />

passen, d. h. also automatisch (implizit) zu konvertieren s<strong>in</strong>d.<br />

Aufgabe: In der Tabelle Abteilung ist die Spalte Kuerzel als VARCHAR(10) festgelegt.<br />

Tatsächlich s<strong>in</strong>d die Kürzel maximal vier Zeichen lang. Deshalb soll die<br />

Def<strong>in</strong>ition an die Realität angepasst werden.<br />

My<strong>SQL</strong>-Quelltext<br />

ALTER TABLE Abteilung<br />

ALTER COLUMN Kuerzel TYPE CHAR(4);<br />

Bei MySql wird dieser Befehl ausgeführt. Firebird erkennt die Möglichkeit der<br />

impliziten Konvertierung durch Abschneiden zu langer Werte nicht. (Zu lange<br />

Werte gibt es sowieso nur <strong>in</strong> der Theorie, aber nicht wirklich.) Dann s<strong>in</strong>d, wie<br />

schon bei den DDL-E<strong>in</strong>zelheiten für ALTER COLUMN erwähnt, mehrere E<strong>in</strong>zelmaßnahmen<br />

mit e<strong>in</strong>er temporären Spalte erforderlich:<br />

398<br />

Firebird-Quelltext<br />

/* Erzeugen Sie e<strong>in</strong>e neue, temporäre Spalte. */<br />

ALTER TABLE Abteilung ADD [COLUMN] Temp VARCHAR(10);<br />

/* Kopieren Sie alle Inhalte aus der „alten“ Spalte <strong>in</strong> die temporäre Spalte. */<br />

UPDATE Abteilung SET Temp = Kuerzel;<br />

/* Löschen Sie die „alte“ Spalte. */<br />

ALTER TABLE Abteilung DROP [COLUMN] Kuerzel;


* Erzeugen Sie e<strong>in</strong>e neue Spalte unter dem „alten“ Namen mit den<br />

„neuen“ Eigenschaften. */<br />

ALTER TABLE Abteilung ADD [COLUMN] Kuerzel CHAR(4) NOT NULL;<br />

E<strong>in</strong>schränkungen auf Spalten<br />

/* Kopieren Sie alle Inhalte aus der temporären Spalte <strong>in</strong> die neue Spalte,<br />

wobei sie passend konvertiert werden müssen. */<br />

UPDATE Abteilung SET Kuerzel = SUBSTRING(Temp FROM 1 FOR 4);<br />

/* Löschen Sie die temporäre Spalte. */<br />

ALTER TABLE Abteilung DROP [COLUMN] Temp;<br />

35.1.3. V<strong>org</strong>abewert h<strong>in</strong>zufügen<br />

Für diese Aufgabe gibt es mehrere Varianten; wir beschränken uns auf e<strong>in</strong>e.<br />

Aufgabe: In der Tabelle Mitarbeiter soll die Spalte Ist_Leiter <strong>in</strong> der Regel den<br />

Wert 'N' erhalten.<br />

My<strong>SQL</strong>-Quelltext<br />

ALTER TABLE Abteilung MODIFY Ist_Leiter CHAR(1) NOT NULL DEFAULT ’N’;<br />

Bei Firebird beispielsweise kann e<strong>in</strong> DEFAULT-Wert nicht e<strong>in</strong>fach h<strong>in</strong>zugefügt<br />

werden; dann ist der „Umweg“ mit e<strong>in</strong>er temporären Spalte nötig.<br />

Die gleiche Änderung wäre auch denkbar <strong>in</strong> der Tabelle Versicherungsnehmer für<br />

die Spalte Eigener_Kunde. Da aber nicht abgeschätzt werden kann, welcher Fall<br />

„wesentlich häufiger“ vorkommt, lohnt sich e<strong>in</strong>e solche E<strong>in</strong>schränkung nicht.<br />

35.2. E<strong>in</strong>schränkungen auf Spalten<br />

Dazu wird der Befehl ALTER TABLE − ADD CONSTRAINT verwendet.<br />

Die hier besprochenen Änderungen sowie weitere E<strong>in</strong>schränkungen s<strong>in</strong>d im<br />

Skript-Constra<strong>in</strong>ts zusammengefasst.<br />

35.2.1. E<strong>in</strong>deutigkeit festlegen<br />

Bei mehreren Tabellen gibt es Spalten, deren Werte e<strong>in</strong>deutig se<strong>in</strong> sollen. Dies<br />

wird durch e<strong>in</strong>e UNIQUE-E<strong>in</strong>schränkung geregelt.<br />

Aufgabe: In der Tabelle Mitarbeiter ist die Personalnummer nicht als Primary<br />

Key v<strong>org</strong>esehen, muss aber dennoch e<strong>in</strong>deutig se<strong>in</strong>.<br />

399


Änderung der Datenbankstruktur<br />

ALTER TABLE Mitarbeiter<br />

ADD CONSTRAINT Mitarbeiter_Nummer UNIQUE (Personalnummer)<br />

USING INDEX Mitarbeiter_Nummer_UK;<br />

Wir benutzen dafür e<strong>in</strong>en CONSTRAINT mit Namen und legen außerdem fest,<br />

dass e<strong>in</strong> bestimmter Index (ebenfalls mit Namen) benutzt werden soll.<br />

35.2.2. CHECK-Bed<strong>in</strong>gungen für e<strong>in</strong>e Spalte<br />

Bei verschiedenen Spalten <strong>in</strong> mehreren Tabellen s<strong>in</strong>d nur bestimmte Werte zulässig.<br />

Das wurde oben unter „Neue Spalten e<strong>in</strong>fügen“ berücksichtigt, gilt aber<br />

auch für e<strong>in</strong>ige schon vorhandene Spalten.<br />

Aufgabe: In der Tabelle Versicherungsvertrag gibt es für die Spalte Art nur e<strong>in</strong>e<br />

begrenzte Anzahl von Werten.<br />

ALTER TABLE Versicherungsvertrag<br />

ADD CONSTRAINT Versicherungsvertrag_Art<br />

CHECK( Art = ’HP’ OR Art = ’TK’ OR Art = ’VK’ );<br />

In gleicher Weise ist es vor allem bei den Spalten zu regeln, die als Ersatz für e<strong>in</strong><br />

boolesches Feld nur die Werte 'J' und 'N' akzeptieren.<br />

35.2.3. CHECK-Bed<strong>in</strong>gungen für mehrere Spalten<br />

Die bisherigen Prüfungen beziehen sich immer auf e<strong>in</strong>e e<strong>in</strong>zige Spalte. Genauso<br />

kann auch geprüft werden, ob die Werte mehrerer Spalten zusammenpassen.<br />

Bitte beachten Sie, dass für das folgende Beispiel die Spalte Geschlecht benutzt<br />

wird, die oben durch das Skript-Spalten e<strong>in</strong>gefügt wird.<br />

Aufgabe: In der Tabelle Versicherungsnehmer muss es sich entweder um e<strong>in</strong>e<br />

Firma handeln, d. h. Geschlecht IS NULL, oder e<strong>in</strong>e Reihe anderer Spalten benötigt<br />

für Personen geeignete Werte.<br />

400<br />

ALTER TABLE Versicherungsnehmer<br />

ADD CONSTRAINT Versicherungsnehmer_Person<br />

CHECK( ( Geschlecht IS NULL )<br />

OR ( Vorname IS NOT NULL<br />

AND Geburtsdatum IS NOT NULL<br />

AND Fuehrersche<strong>in</strong> IS NOT NULL<br />

AND Fuehrersche<strong>in</strong> >= Geburtsdatum + 365*16 )<br />

);


Indizes<br />

Gleichzeitig wird geprüft, dass der Versicherungsnehmer den Führersche<strong>in</strong> frühestens<br />

im Alter von 16 Jahren (vere<strong>in</strong>fachte Berechnung, soweit möglich) erhalten<br />

haben kann.<br />

35.3. Indizes<br />

Dazu wird der Befehl CREATE INDEX verwendet.<br />

Die hier besprochenen Indizes sowie weitere Suchschlüssel s<strong>in</strong>d im Skript-<br />

Indizes zusammengefasst.<br />

Aufgabe: In der Tabelle Versicherungsvertrag ist häufig nach dem Fahrzeug,<br />

also nach Fahrzeug_ID zu suchen.<br />

CREATE INDEX Versicherungsvertrag_FZ<br />

ON Versicherungsvertrag (Fahrzeug_ID);<br />

Aufgabe: In der Tabelle Schadensfall ist häufig nach dem Datum zu suchen;<br />

jüngere Schadensfälle sollten zuerst kommen.<br />

CREATE DESC INDEX Schadensfall_Datum<br />

ON Schadensfall (Datum);<br />

Für diesen Zweck benötigen wir also mit DESC e<strong>in</strong>en absteigenden Index.<br />

Aufgabe: In der Tabelle Versicherungsnehmer ist häufig nach dem Namen −<br />

mit oder ohne Vorname − zu suchen.<br />

CREATE INDEX Versicherungsnehmer_Name<br />

ON Versicherungsnehmer (Name, Vorname);<br />

Sie sehen, dass e<strong>in</strong> Schlüssel auch mehrere Spalten benutzen kann. Die DBMS<br />

arbeiten unterschiedlich, ob daraus für Name e<strong>in</strong> eigener Index wird oder ob es<br />

nur den „e<strong>in</strong>heitlichen, geme<strong>in</strong>samen“ Index gibt.<br />

Aufgabe: In der Tabelle Versicherungsnehmer ist oft auch nach der Versicherungsgesellschaft<br />

zu suchen.<br />

CREATE INDEX Versicherungsnehmer_Ges<br />

ON Versicherungsnehmer (Versicherungsgesellschaft_ID);<br />

Es ist nicht sicher, dass e<strong>in</strong> solcher Index e<strong>in</strong>gerichtet werden kann; denn diese<br />

Spalte kann auch NULL-Werte enthalten (nämlich für die „Eigenen Kunden“),<br />

und dabei verhalten sich die DBMS unterschiedlich.<br />

401


Änderung der Datenbankstruktur<br />

35.4. Fremdschlüssel<br />

Dazu wird der Befehl ALTER TABLE − ADD CONSTRAINT − FOREIGN KEY verwendet.<br />

Alle E<strong>in</strong>zelheiten dazu wurden bei den Fremdschlüssel-Beziehungen<br />

behandelt.<br />

Die Fremdschlüssel, die für die Beispieldatenbank vorzusehen s<strong>in</strong>d, s<strong>in</strong>d im<br />

Skript-ForeignKeys zusammengefasst.<br />

35.5. Weitere Anpassungen<br />

Durch die nachträglichen Änderungen <strong>in</strong> den vorstehenden Abschnitten müssen<br />

die bisherigen Daten angepasst werden. Das wollen wir jetzt erledigen.<br />

Die hier besprochenen Anpassungen s<strong>in</strong>d im Skript-Anpassen zusammengefasst.<br />

Dort ist e<strong>in</strong>e der Versionen mit allen Werten enthalten.<br />

35.5.1. Geschlecht ändern<br />

In den Tabellen Mitarbeiter und Versicherungsnehmer wurde als Standardwert<br />

'W' (= weiblich) e<strong>in</strong>getragen. Wir müssen also noch die Männer markieren; das<br />

geht durch manuelle Änderung jeder e<strong>in</strong>zelnen Zeile, durch Bezug auf männliche<br />

Vornamen oder mit e<strong>in</strong>er Liste der IDs von Männern. Im folgenden Code<br />

stehen alle drei Varianten; s<strong>in</strong>nvoll ist es natürlich, nur e<strong>in</strong>e davon zu benutzen.<br />

UPDATE Mitarbeiter<br />

SET Geschlecht = ’M’<br />

WHERE ID = 1<br />

OR Vorname IN (’Kurt’, ’Walter’, ’Michael’)<br />

OR ID IN (10, 11, 12);<br />

Die gleiche Änderung ist <strong>in</strong> der Tabelle Versicherungsnehmer nötig. Mangels V<strong>org</strong>abewert<br />

setzen wir vorher alle Kunden, die ke<strong>in</strong>e Firma s<strong>in</strong>d, auf 'W'.<br />

Quelltext Falsch<br />

UPDATE Versicherungsnehmer<br />

SET Geschlecht = ’W’<br />

WHERE Vorname IS NOT NULL;<br />

Operation violates CHECK constra<strong>in</strong>t on view or table.<br />

Operation violates CHECK constra<strong>in</strong>t VERSICHERUNGSNEHMER_PERSON<br />

on view or table VERSICHERUNGSNEHMER.<br />

402


Weitere Anpassungen<br />

Ach, was ist da denn wieder passiert? Bei der Firma mit ID=1 steht der Vorname<br />

als leere Zeichenkette, aber der oben e<strong>in</strong>gerichtete CHECK mit der Prüfung „Ist<br />

Person“ vergleicht den Vornamen mit dem NULL-Wert. Also muss entweder der<br />

konkrete Wert auf NULL gesetzt werden, oder der CHECK muss geändert werden.<br />

Bei e<strong>in</strong>er „echten“ Datenbank wäre der zweite Weg unbed<strong>in</strong>gt vorzuziehen;<br />

wir machen es uns hier e<strong>in</strong>facher:<br />

UPDATE Versicherungsnehmer<br />

SET Vorname = NULL<br />

WHERE Vorname = ”;<br />

Danach funktioniert der obige Befehl; wir müssten also wie folgt v<strong>org</strong>ehen:<br />

• CHECK für „Ist Person“ löschen.<br />

• CHECK für „Ist Person“ neu registrieren, sodass auch e<strong>in</strong>e leere Zeichenkette<br />

für den Vornamen bei e<strong>in</strong>er Firma zulässig ist.<br />

• Geschlecht auf den V<strong>org</strong>abewert 'W' setzen.<br />

• alle männlichen Personen auf 'M' ändern wie im ersten Beispiel.<br />

35.5.2. V<strong>org</strong>abewert der Basisprämie<br />

In der Tabelle Versicherungsvertrag wurde dieser Wert allgeme<strong>in</strong> auf 500 (Euro<br />

pro Jahr) gesetzt. Damit wir später richtig damit arbeiten können, muss er natürlich<br />

von der Vertragsart abhängen. (Wir unterscheiden dabei nicht nach Fahrzeugtypen<br />

und ignorieren Prämienänderungen im Laufe der Jahre, sondern tun<br />

so, als ob sich Versicherungsprämien niemals erhöhen würden.) Da der V<strong>org</strong>abewert<br />

sowieso auf 500 gesetzt wurde, brauchen nur die davon abweichenden<br />

Werte geändert zu werden.<br />

Quelltext Falsch<br />

UPDATE Versicherungsvertrag<br />

SET Basispraemie = CASE Art<br />

WHEN ’TK’ THEN 550<br />

WHEN ’VK’ THEN 800<br />

END ;<br />

The update failed because a column def<strong>in</strong>ition <strong>in</strong>cludes validation constra<strong>in</strong>ts.<br />

validation error for column BASISPRAEMIE, value "*** null ***".<br />

In der Tat, man muss aufpassen. Bei den „Nützlichen Erweiterungen“ hieß es zur<br />

CASE-Anweisung: „wenn ELSE nicht vorhanden ist, wird NULL als Wert genommen.“<br />

Der bisherige V<strong>org</strong>abewert darf also nicht weggelassen werden, sondern<br />

muss im ELSE-Zweig erneut zugewiesen werden:<br />

403


Änderung der Datenbankstruktur<br />

UPDATE Versicherungsvertrag<br />

SET Basispraemie = CASE Art<br />

WHEN ’TK’ THEN 550<br />

WHEN ’VK’ THEN 800<br />

ELSE 500<br />

END ;<br />

35.5.3. Prämiensatz und Prämienänderung vorbereiten<br />

Da sich unser Datenbestand nicht <strong>in</strong> der Wirklichkeit entwickelt hat, müssen wir<br />

diese Entwicklung simulieren. Die ersten Schadensfälle s<strong>in</strong>d 2007 registriert; also<br />

muss der Datenbestand auf das Jahresende 2006 gebracht werden. Dazu benutzen<br />

wir e<strong>in</strong>e Rout<strong>in</strong>e ohne feste Speicherung (siehe <strong>SQL</strong>-Programmierung).<br />

Firebird-Quelltext<br />

EXECUTE BLOCK<br />

AS<br />

DECLARE VARIABLE jj INTEGER;<br />

DECLARE VARIABLE T1 INTEGER;<br />

DECLARE VARIABLE T2 INTEGER;<br />

BEGIN<br />

FOR SELECT vv.ID, EXTRACT(YEAR FROM Abschlussdatum), Praemiensatz<br />

FROM Versicherungsvertrag vv<br />

JOIN Versicherungsnehmer vn ON vn.Id = vv.Versicherungsnehmer_Id<br />

WHERE Praemienaenderung


Zusammenfassung<br />

Der Prämiensatz 2006 errechnet sich aus der Dauer des Vertrags, also nach dem<br />

Abschlussdatum sowie natürlich dem bisher notierten pauschalen Prämiensatz.<br />

Im UPDATE-Befehl werden für jeden Vertrag − notiert <strong>in</strong> der Variablen T1 − die<br />

neuen Werte berechnet:<br />

• Das Datum Praemienaenderung wird vom Abschlussdatum aus auf das Jahr<br />

2006 weitergesetzt.<br />

• Der Prämiensatz wird neu berechnet, und zwar:<br />

• für neuere Verträge um je 20 Punkte pro Jahr bis zu e<strong>in</strong>em M<strong>in</strong>imum von 80<br />

• für andere Verträge um je 10 Punkte pro Jahr bis zu e<strong>in</strong>em M<strong>in</strong>imum von 30<br />

• Werte, die von den Anfangssätzen 100 oder 200 abweichen, bleiben stehen<br />

35.6. Zusammenfassung<br />

In diesem Kapitel wurden alle Änderungen, die sich für die Beispieldatenbank als<br />

s<strong>in</strong>nvoll erwiesen, besprochen. Für jede Situation wurden Beispiele angegeben;<br />

alle erforderlichen Befehle s<strong>in</strong>d <strong>in</strong> Skripten zusammengefasst:<br />

• Skript-Spalten zum H<strong>in</strong>zufügen und Ändern e<strong>in</strong>zelner Spalten<br />

• Skript-Constra<strong>in</strong>ts zum Anlegen von UNIQUE KEYs und CHECK-Constra<strong>in</strong>ts<br />

• Skript-Indizes zum Anlegen weiterer Schlüssel<br />

• Skript-ForeignKeys zum Anlegen der Fremdschlüssel<br />

• Skript-Anpassung zum Anpassen der betroffenen Datensätze<br />

Weitere H<strong>in</strong>weise dazu gibt es im Kapitel Downloads 2 .<br />

35.7. Siehe auch<br />

Die hier verwendeten Verfahren werden <strong>in</strong> den folgenden Kapiteln behandelt:<br />

• Beispieldatenbank 3 unter „Anmerkungen“<br />

• Prozeduren 4 mit dem Beispiel „automatisches UPDATE gemäß Bed<strong>in</strong>gungen“<br />

• DDL – E<strong>in</strong>zelheiten 5 unter ALTER COLUMN<br />

• Fremdschlüssel-Beziehungen 6<br />

2 Anhang B auf Seite 423<br />

3 Abschnitt 6.4 auf Seite 43<br />

4 Abschnitt 32.2.3 auf Seite 363<br />

5 Abschnitt 28.3.3 auf Seite 292<br />

6 Kapitel 29 auf Seite 305<br />

405


Änderung der Datenbankstruktur<br />

• Nützliche Erweiterungen 7<br />

• <strong>SQL</strong>-Programmierung 8 bei „Rout<strong>in</strong>e ohne feste Speicherung“<br />

35.7.1. Was ist e<strong>in</strong> Skript?<br />

Unter e<strong>in</strong>em Skript 9 versteht man <strong>in</strong> der EDV e<strong>in</strong>e Liste von Befehlen, die durch<br />

e<strong>in</strong>en e<strong>in</strong>zelnen Befehl aufgerufen und automatisch nache<strong>in</strong>ander ausgeführt<br />

werden. Diese Befehle s<strong>in</strong>d meist <strong>in</strong> e<strong>in</strong>er Datei zusammengefasst und werden<br />

dadurch ausgeführt, dass die betreffende Datei aufgerufen wird.<br />

Bei <strong>SQL</strong> s<strong>in</strong>d solche Dateien unter W<strong>in</strong>dows üblicherweise mit der Endung<br />

".sql" gespeichert.<br />

7 Kapitel 23 auf Seite 213<br />

8 Abschnitt 30.1 auf Seite 324<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Skript<br />

406


36. Testdaten erzeugen<br />

36.1. Neue Fahrzeuge registrieren . . . . . . . . . . . . . . . . . . . . . 408<br />

36.2. Neue Versicherungsverträge registrieren . . . . . . . . . . . . . 408<br />

36.3. Probleme mit Testdaten . . . . . . . . . . . . . . . . . . . . . . . 414<br />

36.4. Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . 415<br />

36.5. Siehe auch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416<br />

In diesem Kapitel werden wir die Erkenntnisse aus den vorhergehenden Kapiteln<br />

benutzen, um die Beispieldatenbank um e<strong>in</strong>e Vielzahl zusätzlicher Datensätze<br />

zu ergänzen.<br />

Auch hier gilt: Wegen der vielen Varianten bei den DBMS beschränkt sich dieses<br />

Kapitel bei den H<strong>in</strong>weisen und Beispielen auf Firebird. Zusammen mit den<br />

speziellen H<strong>in</strong>weisen zur <strong>SQL</strong>-Programmierung sollten sie problemlos an andere<br />

DBMS-Regeln angepasst werden können.<br />

Damit man <strong>in</strong> e<strong>in</strong>er Datenbank viele Tests ausführen kann, s<strong>in</strong>d umfangreiche,<br />

möglichst unterschiedliche Testdaten nötig. Dies wollen wir jetzt ansatzweise –<br />

unter Berücksichtigung der bisherigen E<strong>in</strong>sichten – besprechen.<br />

Folgende Grundgedanken s<strong>in</strong>d bei solchen Testdaten immer zu beachten:<br />

• Die e<strong>in</strong>zelnen Datensätze sollen zufällig erzeugte Werte erhalten.<br />

• Die Werte <strong>in</strong> den Spalten sollen halbwegs realistisch se<strong>in</strong>: E<strong>in</strong> Vorname wie<br />

„Blabla“ oder e<strong>in</strong>e Schadenshöhe wie 0,15 =C verbieten sich.<br />

• Verschiedene Spalten müssen zusammenpassen: Zur PLZ 50667 darf es nur<br />

den Ortsnamen 'Köln' geben.<br />

• Am besten sollte auch die Verteilung der Datensätze halbwegs realistisch se<strong>in</strong>:<br />

Es sollte m<strong>in</strong>destens so viele Fahrzeuge mit dem Kfz-Kennzeichen 'D' (Stadt<br />

Düsseldorf) wie mit 'RE' (Stadt- und Landkreis Reckl<strong>in</strong>ghausen) geben; ähnlich<br />

sollten auch die Versicherungsnehmer auf Stadt- und Landkreis passend<br />

verteilt se<strong>in</strong>.<br />

i H<strong>in</strong>weis<br />

Die hier genannten Schritte s<strong>in</strong>d nur e<strong>in</strong> denkbares Verfahren. Wichtig ist<br />

e<strong>in</strong> E<strong>in</strong>blick <strong>in</strong> mögliche V<strong>org</strong>ehensweisen und Probleme.<br />

407


Testdaten erzeugen<br />

36.1. Neue Fahrzeuge registrieren<br />

Für viele neue Datensätze <strong>in</strong> der Tabelle Fahrzeug lernten wir bereits zwei Varianten<br />

des gleichen Verfahrens kennen:<br />

• Bei den Prozeduren werden mit e<strong>in</strong>er WHILE-Schleife mehrere zufällige Werte<br />

für neue Zeilen zusammengesetzt.<br />

• Dies geht auch als Rout<strong>in</strong>e ohne feste Speicherung.<br />

Beide Varianten s<strong>in</strong>d nur s<strong>in</strong>nvoll, wenn e<strong>in</strong>e ger<strong>in</strong>ge Anzahl von Spalten mit<br />

e<strong>in</strong>er ger<strong>in</strong>gen Anzahl möglicher Werte „vervielfältigt“ werden.<br />

36.2. Neue Versicherungsverträge registrieren<br />

Die Prozedur (siehe dort) Insert_Versicherungsvertrag ist für e<strong>in</strong>en solchen automatischen<br />

V<strong>org</strong>ang nicht geeignet, weil sie immer wieder mit neuen Werten aufgerufen<br />

werden müsste. In diesem Kapitel sollen deshalb neue Datensätze durch<br />

das „kartesische Produkt“ zusammengestellt werden. Damit die erforderlichen<br />

Beziehungen gesichert s<strong>in</strong>d, müssen zuerst Fahrzeuge und Versicherungsnehmer<br />

erstellt werden; erst zuletzt dürfen die Verträge registriert werden.<br />

Die folgenden Maßnahmen s<strong>in</strong>d im Skript-Testdaten der Download-Seite 1 zusammengefasst.<br />

Dort stehen auch diejenigen (e<strong>in</strong>facheren) <strong>SQL</strong>-Befehle, die im<br />

Folgenden nicht ausführlich angegeben und besprochen werden. Der Vermerk<br />

Skript verweist bei den folgenden Schritten auf die Befehle, die <strong>in</strong> diesem<br />

Skript stehen.<br />

36.2.1. Die Tabelle Fahrzeug<br />

E<strong>in</strong>e Menge von Fahrzeugen wurde bereits gespeichert mithilfe der Prozedur<br />

Insert_Into_Fahrzeug. Wir prüfen zunächst, wie viele Fahrzeuge (noch) nicht zu<br />

e<strong>in</strong>em Vertrag gehören:<br />

SELECT COUNT(ID) FROM Fahrzeug<br />

WHERE ID NOT IN (SELECT Fahrzeug_ID FROM Versicherungsvertrag);<br />

COUNT : 120<br />

1 Anhang B auf Seite 423<br />

408


Neue Versicherungsverträge registrieren<br />

Wenn das noch nicht genügt, speichern Sie jetzt mit der o. g. Prozedur weitere<br />

Datensätze. Auf diese Daten greifen wir <strong>in</strong> den nächsten Schritten zurück, damit<br />

alle E<strong>in</strong>träge zusammenpassen.<br />

Bei den späteren Maßnahmen gibt es Anmerkungen dazu, wie viele Datensätze<br />

vorkommen können. Die Zahl der Fahrzeuge sollte dem ungefähr entsprechen.<br />

36.2.2. Hilfstabellen<br />

Für die zufällige Komb<strong>in</strong>ation erstellen wir uns zunächst e<strong>in</strong>ige temporäre Tabellen<br />

mit passenden Werten:<br />

• TempName mit e<strong>in</strong>er Liste von 20 Nachnamen, TempVorname mit e<strong>in</strong>er Liste<br />

von 20 Vornamen und Geschlecht Skript<br />

• TempPLZOrt mit e<strong>in</strong>er Liste von etwa 30 Postleitzahlen und zugehörigen Ortsnamen<br />

Skript<br />

• TempStrasse mit e<strong>in</strong>er Liste von etwa 20 Straßennamen (eigentlich müssten<br />

diese zu den Orten passen; aber da es ke<strong>in</strong> „freies“ Straßenverzeichnis für kle<strong>in</strong>ere<br />

Städte und Orte gibt, soll uns das nicht <strong>in</strong>teressieren) Skript<br />

36.2.3. Die Tabelle Versicherungsnehmer<br />

Für e<strong>in</strong>e Menge potenzieller Versicherungsnehmer mischen wir Zufallsfunktionen<br />

und die direkte Verknüpfung der Daten aus den Hilfstabellen.<br />

Aufgabe: Erzeuge viele Testdaten Versicherungsnehmer durch das kartesische<br />

Produkt aus den Hilfstabellen.<br />

Firebird-Quelltext<br />

INSERT INTO Versicherungsnehmer<br />

( Name, Vorname, Geschlecht,<br />

PLZ, Ort,<br />

Strasse,<br />

Hausnummer,<br />

Geburtsdatum, Fuehrersche<strong>in</strong>, Eigener_Kunde, Versicherungsgesellschaft_ID )<br />

SELECT n.Text, v.Text, v.Geschlecht,<br />

p.Plz, p.Ort,<br />

s.Name,<br />

CAST( CAST( FLOOR( 1 + RAND()* 98) AS INTEGER) AS VARCHAR(10)),<br />

’01.01.1950’, ’31.12.2009’, ’J’, NULL<br />

FROM TempName n, TempVorname v, TempPLZOrt p, TempStrasse s<br />

WHERE n.Text


Testdaten erzeugen<br />

gibt ke<strong>in</strong>e Verknüpfung zwischen den vier Hilfstabellen). Dies ist also das „kartesische<br />

Produkt“, das als im Normalfall ungeeignet bezeichnet wurde, aber <strong>in</strong><br />

dieser Spezialsituation nützlich ist.<br />

Geburtsdatum und Fuehrersche<strong>in</strong> werden nachträglich geändert. Der SELECT-<br />

Befehl wäre zu kompliziert, wenn zunächst das Geburtsdatum mit mehreren Zufallsfunktionen<br />

erstellt würde und davon abhängig mit weiteren Zufallszahlen<br />

das Datum des Führersche<strong>in</strong>erwerbs berechnet würde.<br />

i Achtung<br />

Diese Art der Verknüpfung mehrerer Tabellen kann sehr viele Datensätze<br />

erzeugen und belastet deshalb das DBMS für längere Zeit erheblich.<br />

Nur deshalb wird die WHERE-Bed<strong>in</strong>gung benutzt. Mit allen Datensätzen der<br />

temporären Tabellen entstünden 20 · 20 · 47 · 22 (= 413 600) Zeilen, was bei e<strong>in</strong>em<br />

Testlauf etwa 15 M<strong>in</strong>uten dauerte. Mit der E<strong>in</strong>schränkung und e<strong>in</strong>er kle<strong>in</strong>eren<br />

PLZ/Orte-Tabelle gibt es etwa 12 · 12 · 25 · 13 (= 46 800) Zeilen <strong>in</strong> 15 Sekunden.<br />

Aufgabe: Nun werden zufällige Daten für Geburtsdatum und Fuehrersche<strong>in</strong> erzeugt<br />

und bei den neu erstellten Versicherungsnehmern gespeichert.<br />

UPDATE Versicherungsnehmer<br />

SET Geburtsdatum = DATEADD( DAY, CAST( FLOOR(RAND()*27) AS INTEGER),<br />

DATEADD( MONTH, CAST( FLOOR(RAND()*11) AS INTEGER),<br />

DATEADD( YEAR, CAST( FLOOR(RAND()*40) AS INTEGER),<br />

Geburtsdatum )))<br />

WHERE Geburtsdatum = ’01.01.1950’;<br />

COMMIT;<br />

UPDATE Versicherungsnehmer<br />

SET Fuehrersche<strong>in</strong> = DATEADD( DAY, CAST( FLOOR(RAND()*27) AS INTEGER),<br />

DATEADD( MONTH, CAST( FLOOR(RAND()*11) AS INTEGER),<br />

DATEADD( YEAR, CAST( FLOOR(18 + RAND()*(DATEDIFF<br />

(YEAR, Geburtsdatum, CAST(’31.12.2008’ AS DATE))-18)<br />

) AS INTEGER), Geburtsdatum)))<br />

WHERE Fuehrersche<strong>in</strong> = ’31.12.2009’;<br />

COMMIT;<br />

Das Geburtsdatum kann dabei nicht später als der '31.12.1990' liegen; der Führersche<strong>in</strong><br />

kann frühestens mit dem 18. Geburtstag erworben worden se<strong>in</strong>.<br />

36.2.4. Die Tabelle Versicherungsvertrag<br />

Um die bisherigen Teildaten zu Verträgen zusammenzufassen, müssen wir anders<br />

v<strong>org</strong>ehen. Folgende Bed<strong>in</strong>gungen s<strong>in</strong>d zu berücksichtigen:<br />

410


• Jedes Fahrzeug darf nur e<strong>in</strong>mal benutzt werden.<br />

Neue Versicherungsverträge registrieren<br />

• E<strong>in</strong> Versicherungsnehmer darf auch <strong>in</strong> mehreren Verträgen stehen.<br />

• Aber der Wohnort des Kunden sollte zum Kfz-Kennzeichen passen; wir machen<br />

es zur Bed<strong>in</strong>gung.<br />

• Das Abschlussdatum des Vertrags muss nach dem Führersche<strong>in</strong>erwerb liegen.<br />

• Prämiensatz und Prämienänderung müssen ebenso vorbereitet werden wie im<br />

Kapitel Änderung der Datenbankstruktur.<br />

E<strong>in</strong> erster Versuch (zunächst beschränkt auf Fahrzeuge mit 'RE' als Kennzeichen)<br />

konnte nicht s<strong>in</strong>nvoll funktionieren – das DBMS „hängte sich auf“:<br />

Firebird-Quelltext Falsch<br />

SELECT fz.id, fz.Kennzeichen,<br />

vn.Id, vn.Name, vn.Vorname, vn.PLZ<br />

FROM Fahrzeug fz<br />

JOIN Versicherungsnehmer vn ON vn.ID =<br />

( SELECT FIRST 1 ID<br />

FROM Versicherungsnehmer<br />

WHERE ID NOT IN ( SELECT Versicherungsnehmer_ID<br />

FROM Versicherungsvertrag )<br />

AND PLZ = ( SELECT FIRST 1 PLZ<br />

FROM TempPLZOrt<br />

WHERE Kreis = ’RE’<br />

ORDER BY RAND()<br />

)<br />

ORDER BY RAND()<br />

)<br />

WHERE fz.Kennzeichen STARTS WITH ’RE-’;<br />

Dieser Versuch sollte so v<strong>org</strong>ehen (lesen Sie den Befehl „von <strong>in</strong>nen nach außen“):<br />

• Hole nach Zufallsreihenfolge e<strong>in</strong>e PLZ aus der Liste der Orte, die zum Kreis 'RE'<br />

gehören.<br />

• Hole nach Zufallsreihenfolge e<strong>in</strong>en Kunden mit dieser PLZ, sofern er noch<br />

nicht mit e<strong>in</strong>em Vertrag registriert ist.<br />

• Nur dessen Daten sollen mit e<strong>in</strong>em e<strong>in</strong>zelnen Fahrzeug verknüpft werden.<br />

Wenn man auf das JOIN verzichten würde, würde der erste Versicherungsnehmer<br />

mit jedem Fahrzeug verknüpft.<br />

Dieses Verfahren verlangt bei jedem Fahrzeug zwei neue abhängige SELECT-<br />

Befehle, die per Zufallsfunktion aus fast 50 000 Datensätzen jeweils genau e<strong>in</strong>en<br />

Datensatz liefern sollen − e<strong>in</strong>e völlig unsachgemäße Belastung des DBMS.<br />

Stattdessen wird mit e<strong>in</strong>er weiteren Hilfstabelle TempVertrag schrittweise v<strong>org</strong>egangen:<br />

411


Testdaten erzeugen<br />

• Schreibe alle noch „freien“ Fahrzeuge aus den oben verwendeten Kreisen <strong>in</strong><br />

die Hilfstabelle. Skript<br />

• Sortiere sie nach Kreis und nummeriere sie <strong>in</strong> dieser Reihenfolge. Skript<br />

• Hole per Zufallsreihenfolge − getrennt nach jedem Kreis − e<strong>in</strong>en E<strong>in</strong>trag aus<br />

der Tabelle Versicherungsnehmer.<br />

• Übertrage diese Zusammenstellung <strong>in</strong> die Tabelle Versicherungsvertrag.<br />

• Passe Prämiensatz und Prämienänderung an (wie im früheren Kapitel ausgeführt).<br />

Der erste, entscheidende Schritt ist die Zuordnung e<strong>in</strong>es potenziellen Kunden zu<br />

jedem „freien“ Fahrzeug.<br />

Aufgabe: Erzeuge e<strong>in</strong>e zufällige Reihenfolge der Kunden und trage sie <strong>in</strong> die<br />

(temporäre) Liste der Fahrzeuge e<strong>in</strong>.<br />

412<br />

Firebird-Quelltext<br />

EXECUTE BLOCK<br />

AS<br />

DECLARE VARIABLE nextid INTEGER = 0;<br />

DECLARE VARIABLE tempid INTEGER;<br />

DECLARE VARIABLE tkreis VARCHAR(3);<br />

DECLARE VARIABLE Tname CHAR(2);<br />

DECLARE VARIABLE m<strong>in</strong>nr INTEGER;<br />

DECLARE VARIABLE maxnr INTEGER;<br />

DECLARE VARIABLE Tdatum DATE;<br />

BEGIN<br />

FOR SELECT fz_Kreis, M<strong>in</strong>(Nr), Max(Nr)<br />

FROM TempVertrag<br />

GROUP BY fz_Kreis<br />

ORDER BY fz_Kreis<br />

INTO :tkreis, :M<strong>in</strong>nr, :Maxnr<br />

DO BEGIN<br />

/* hole alle möglichen potenziellen Kunden für diesen Kreis<br />

<strong>in</strong> Zufallsreihenfolge */<br />

nextid = :M<strong>in</strong>nr - 1;<br />

FOR SELECT ID,<br />

/* diese komplizierte Konstruktion mit TRIM, CAST ist<br />

wegen SUBSTRING nötig */<br />

CAST( TRIM(SUBSTRING(Name FROM 1 FOR 1))<br />

|| TRIM(SUBSTRING(Ort FROM 1 FOR 1)) AS CHAR(2)),<br />

Fuehrersche<strong>in</strong><br />

FROM Versicherungsnehmer<br />

WHERE PLZ IN ( SELECT PLZ<br />

FROM TempPLZOrt<br />

WHERE Kreis = :Tkreis<br />

)<br />

AND ID NOT IN<br />

( SELECT Versicherungsnehmer_ID<br />

FROM Versicherungsvertrag<br />

)<br />

ORDER BY RAND()


Neue Versicherungsverträge registrieren<br />

INTO :Tempid, :Tname, :Tdatum<br />

DO BEGIN<br />

/* registriere jeden dieser Kunden nache<strong>in</strong>ander<br />

für e<strong>in</strong>es der Fahrzeuge <strong>in</strong> diesem Kreis */<br />

nextid = nextid + 1;<br />

UPDATE TempVertrag<br />

SET vn_ID = :Tempid,<br />

vn_Name = :Tname,<br />

/* per Zufall variable Daten vorbereiten */<br />

Abschlussdatum = DATEADD( DAY, CAST( FLOOR(RAND()*27) AS INTEGER),<br />

DATEADD( MONTH, CAST( FLOOR(RAND()*11) AS INTEGER),<br />

DATEADD( YEAR, CAST( FLOOR(RAND()*<br />

(DATEDIFF (YEAR, :Tdatum,<br />

CAST(’31.12.2008’ AS DATE))<br />

) ) AS INTEGER), :Tdatum)))<br />

WHERE Nr = :nextid;<br />

END<br />

END<br />

END<br />

Dieser Arbeitsablauf berücksichtigt die o. g. Grundgedanken:<br />

• In der Hauptschleife werden die Fahrzeuge nach dem Kreis gruppiert.<br />

• Für jeden Kreis werden der kle<strong>in</strong>ste und der größte Wert der laufenden Nummer<br />

aus der Hilfstabelle notiert.<br />

• Der nächste SELECT bereitet e<strong>in</strong>e Schleife mit potenziellen Kunden vor:<br />

• Zum aktuellen Kreis werden alle Postleitzahlen aus der Hilfstabelle<br />

TempPLZOrte registriert.<br />

• Die E<strong>in</strong>träge der Kunden, die zu diesen PLZ gehören, werden <strong>in</strong> e<strong>in</strong>e zufällige<br />

Reihenfolge gebracht.<br />

• In dieser Schleife wird jeder dieser Kunden bei e<strong>in</strong>em Fahrzeug mit der nächsten<br />

laufenden Nummer e<strong>in</strong>getragen.<br />

• Der Anfangsbuchstabe vom Namen und Ort wird registriert, damit dieser Wert<br />

für die Vertragsnummer zur Verfügung steht.<br />

• Zusätzlich wird als Abschlussdatum des Vertrags e<strong>in</strong> zufälliges Datum zwischen<br />

dem Führersche<strong>in</strong>erwerb und e<strong>in</strong>em festen Schlussdatum erzeugt.<br />

Aufgabe: Jetzt wird für jede dieser Komb<strong>in</strong>ationen von Fahrzeugen und Kunden<br />

aus der Hilfstabelle TempVertrag e<strong>in</strong> Versicherungsvertrag erzeugt:<br />

Firebird-Quelltext<br />

INSERT INTO Versicherungsvertrag<br />

SELECT NULL, /* ID nach Generator */<br />

Vn_name || ’-’ || CAST(Nr AS VARCHAR(3)), /* Vertragsnummer */<br />

Abschlussdatum, /* Vertragsabschluss */<br />

CASE MOD(Fz_id, 4) /* Vertragsart nach Fahrzeug-ID */<br />

WHEN 0 THEN ’VK’<br />

WHEN 1 THEN ’HP’<br />

413


Testdaten erzeugen<br />

ELSE ’TK’<br />

END,<br />

CASE MOD(Fz_id, 4) /* Mitarbeiter-ID nach Fahrzeug */<br />

WHEN 0 THEN 9<br />

WHEN 1 THEN 10<br />

WHEN 2 THEN 11<br />

ELSE 12<br />

END,<br />

Fz_id, /* Fahrzeug-ID */<br />

Vn_id, /* Versicherungsnehmer-ID */<br />

100, /* Prämiensatz */<br />

Abschlussdatum, /* letzte Prämienänderung */<br />

CASE MOD(Fz_id, 4) /* Basisprämie nach Vertragsart */<br />

WHEN 0 THEN 800<br />

WHEN 1 THEN 500<br />

ELSE 550<br />

END<br />

FROM TempVertrag t;<br />

Dabei wird jede Spalte <strong>in</strong> der Tabelle Versicherungsvertrag mit e<strong>in</strong>em Wert versehen,<br />

überwiegend direkt aus der Hilfstabelle TempVertrag. E<strong>in</strong>ige Werte werden<br />

statt e<strong>in</strong>er weiteren Zufallsfunktion aus der Fahrzeug-ID errechnet.<br />

36.3. Probleme mit Testdaten<br />

Bei den vorstehenden E<strong>in</strong>zelschritten gab es wiederholt Fehlermeldungen. Das<br />

gilt natürlich auch für viele andere Versuche. Bitte vergessen Sie deshalb niemals<br />

diesen Ratschlag, der fast immer für jeden e<strong>in</strong>zelnen Teilschritt gilt:<br />

i H<strong>in</strong>weis<br />

Vor jeder umfangreichen oder wichtigen Änderung ist e<strong>in</strong>e<br />

Datensicherung vorzunehmen.<br />

Die hier behandelten Verfahren s<strong>in</strong>d beileibe nicht die e<strong>in</strong>zig möglichen. Sie sollten<br />

aber sehen, wie zum e<strong>in</strong>en verschiedene konstante Daten komb<strong>in</strong>iert und<br />

zum anderen Zufallswerte erzeugt werden können, um viele unterschiedliche<br />

Daten mit s<strong>in</strong>nvollen Komb<strong>in</strong>ationen <strong>in</strong> verschiedenen Tabellen e<strong>in</strong>zufügen. In<br />

gleicher Weise könnten wir noch viele weitere Datensätze <strong>in</strong> den Tabellen der<br />

Beispieldatenbank erzeugen.<br />

Vor allem weitere Schadensfälle wären äußerst nützlich. Dabei s<strong>in</strong>d aber viele<br />

Probleme zu beachten:<br />

• Jeder Schadensfall benötigt e<strong>in</strong>e Schadenssumme, die nicht nur zufällig, sondern<br />

auch s<strong>in</strong>nvoll festgelegt werden sollte.<br />

414


Zusammenfassung<br />

• Die Beschreibung des Schadensfalls sollte wenigstens annähernd realistisch<br />

zur Schadenssumme passen.<br />

• Wir benötigen Schadensfälle mit 1, 2 oder „vielen“ beteiligten Fahrzeugen.<br />

Auch deren Anzahl sowie die anteiligen Schadenssummen sollten annähernd<br />

realistisch zur Schadenssumme und zur Beschreibung passen.<br />

• Wenn e<strong>in</strong> beteiligtes Fahrzeug (genauer: der Versicherungsnehmer) nicht zu<br />

den eigenen Kunden gehört, muss gleichzeitig e<strong>in</strong> Versicherungsvertrag (mit<br />

Versicherungsnehmer und Fahrzeugdaten) gespeichert werden.<br />

• Zum<strong>in</strong>dest <strong>in</strong> der Praxis gibt es auch Unfallfahrer ohne Versicherungsschutz.<br />

Wie soll man das berücksichtigen?<br />

Eher unproblematisch s<strong>in</strong>d Ergänzungen für die folgenden Tabellen; aber weil es<br />

sich <strong>in</strong> der Regel um kle<strong>in</strong>ere Tabellen handelt, muss man sich darüber auch ke<strong>in</strong>e<br />

Gedanken machen, sondern kann jederzeit e<strong>in</strong>zelne Datensätze oder kle<strong>in</strong>e<br />

Prozeduren erstellen:<br />

• Weitere Abteilungen s<strong>in</strong>d kaum nötig; bei Bedarf macht man e<strong>in</strong>zelne INSERT-<br />

Befehle.<br />

• Weitere Mitarbeiter s<strong>in</strong>d nur nötig, wenn die Datenbank auch für andere Arbeitsbereiche<br />

benutzt werden soll.<br />

• Ebenso s<strong>in</strong>d weitere Dienstwagen nicht wichtig.<br />

• Viele weitere Fahrzeugtypen und Fahrzeughersteller wären nützlich, und zwar<br />

schon vor der Maßnahme mit den vielen neuen Versicherungsverträgen. Dafür<br />

s<strong>in</strong>d aber ke<strong>in</strong>e zufällig entstehenden Komb<strong>in</strong>ationen s<strong>in</strong>nvoll; besser ist e<strong>in</strong>e<br />

e<strong>in</strong>fache Prozedur: Der neue Typ und se<strong>in</strong> Hersteller werden im Klartext angegeben;<br />

wenn der Hersteller noch nicht existiert, wird er im gleichen Schritt<br />

(automatisch) registriert (siehe die Übungen zu den Prozeduren).<br />

Insgesamt gibt es so viele praktische Probleme, die nur sehr schwer <strong>in</strong> zufällig<br />

erzeugten Datensätzen berücksichtigt werden könnten. Wir verzichten deshalb<br />

darauf.<br />

i H<strong>in</strong>weis<br />

Für Ihre praktische Arbeit sollten Sie solche „Folgeprobleme“ immer<br />

beachten.<br />

36.4. Zusammenfassung<br />

In diesem Kapitel wurden e<strong>in</strong>e Reihe Verfahren besprochen, mit denen automatisch<br />

e<strong>in</strong>e Menge zusätzlicher Datensätze erstellt werden können:<br />

415


Testdaten erzeugen<br />

• Mit dem kartesischen Produkt können aus Hilfstabellen schnell sehr viele Datensätze<br />

erzeugt werden.<br />

• Prozeduren (oder nicht gespeicherte Rout<strong>in</strong>en) s<strong>in</strong>d <strong>in</strong> anderen Fällen hilfreich.<br />

• In aller Regel s<strong>in</strong>d viele Nebenbed<strong>in</strong>gungen zu berücksichtigen.<br />

36.5. Siehe auch<br />

Dieses Kapitel benutzt Erkenntnisse der folgenden Kapitel.<br />

• E<strong>in</strong>fache Tabellenverknüpfung 2 zum „kartesischen Produkt“<br />

• <strong>SQL</strong>-Programmierung 3 mit allgeme<strong>in</strong>er Beschreibung dazu sowie dem Abschnitt<br />

„Rout<strong>in</strong>en ohne feste Speicherung“<br />

• Prozeduren 4 mit den Abschnitten „Testdaten <strong>in</strong> e<strong>in</strong>er Tabelle erzeugen“,<br />

„INSERT <strong>in</strong> mehrere Tabellen“ und den Übungen<br />

• Änderung der Datenbankstruktur 5 mit dem Abschnitt „Prämiensatz und Prämienänderung<br />

vorbereiten“<br />

Das Skript-Testdaten − siehe Downloads 6 − enthält alle Befehle, die für die Erzeugung<br />

der Testdaten benutzt werden.<br />

2 Kapitel 19 auf Seite 173<br />

3 Kapitel 30 auf Seite 323<br />

4 Kapitel 32 auf Seite 357<br />

5 Kapitel 35 auf Seite 397<br />

6 Anhang B auf Seite 423<br />

416


Teil V.<br />

Anhang<br />

417


A. Tabellenstruktur der<br />

Beispieldatenbank<br />

Hier stehen die Def<strong>in</strong>itionen der e<strong>in</strong>zelnen Tabellen; dabei ist <strong>in</strong> Klammern jeweils<br />

der Tabellen-Alias genannt.<br />

In den Tabellen werden folgende Markierungen verwendet:<br />

• ⋆ – Spalten, die durch die nachträglichen Änderungen e<strong>in</strong>gefügt oder geändert<br />

werden<br />

• PK – PrimaryKey, Primärschlüssel<br />

• FK – ForeignKey, Fremdschlüssel als Verweis auf e<strong>in</strong>e Tabellenverknüpfung<br />

Versicherungsvertrag (vv)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Vertragsnummer varchar(20) Pflicht e<strong>in</strong>deutig<br />

Abschlussdatum date Pflicht<br />

Art char(2) Pflicht nur 'HP' oder 'TK' oder 'VK'<br />

Mitarbeiter_ID <strong>in</strong>teger Pflicht FK Mitarbeiter<br />

Fahrzeug_ID <strong>in</strong>teger Pflicht FK Fahrzeug<br />

Versicherungsnehmer_ID<br />

<strong>in</strong>teger Pflicht FK Versicherungsnehmer<br />

Basispraemie ⋆ number Pflicht größer als 0<br />

Praemiensatz ⋆ <strong>in</strong>teger Pflicht größer als 0<br />

Praemienaenderung ⋆ date optional<br />

Zuordnung_SF_FZ (zu)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Schadensfall_ID <strong>in</strong>teger Pflicht FK Schadensfall<br />

Fahrzeug_ID <strong>in</strong>teger Pflicht Verweis auf e<strong>in</strong> e<strong>in</strong>zelnes<br />

beteiligtes Fahrzeug<br />

419


Tabellenstruktur der Beispieldatenbank<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

Schadenshoehe number optional anteiliger Schaden dieses<br />

Fahrzeugs<br />

Schuldanteil ⋆ <strong>in</strong>teger Pflicht größer/gleich 0<br />

Schadensfall (sf )<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Datum date Pflicht<br />

Ort varchar(200) Pflicht genaue Angabe e<strong>in</strong>schl.<br />

Straße und Umgebung<br />

Beschreibung varchar(1000) Pflicht Angabe der Umstände<br />

Schadenshoehe number optional Angabe, soweit möglich<br />

Verletzte char(1) Pflicht nur 'J' oder 'N'<br />

Mitarbeiter_ID <strong>in</strong>teger Pflicht FK Mitarbeiter<br />

Versicherungsnehmer (vn)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Name varchar(30) Pflicht<br />

Vorname<br />

Geburtsdatum<br />

Fuehrersche<strong>in</strong><br />

varchar(30)<br />

date<br />

date<br />

optional<br />

optional<br />

optional<br />

bei natürlicher Person<br />

Pflicht<br />

Ort varchar(30) Pflicht<br />

PLZ char(5) Pflicht<br />

Strasse varchar(30) Pflicht<br />

Hausnummer varchar(10) Pflicht<br />

Eigener_Kunde char(1) Pflicht nur 'J' oder 'N'<br />

Versicherungsgesell- <strong>in</strong>teger optional FK Versicherungsgesellschaft<br />

schaft_ID<br />

bei Fremdkunden<br />

Geschlecht ⋆ char(1) optional nur 'W' oder 'M' oder NULL<br />

Fahrzeug (fz)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Kennzeichen varchar(10) Pflicht e<strong>in</strong>deutig<br />

Farbe varchar(30) optional<br />

Fahrzeugtyp_ID <strong>in</strong>teger Pflicht FK Fahrzeugtyp<br />

420


Dienstwagen (dw)<br />

Tabellenstruktur der Beispieldatenbank<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Kennzeichen varchar(10) Pflicht e<strong>in</strong>deutig<br />

Farbe varchar(30) optional<br />

Fahrzeugtyp_ID <strong>in</strong>teger Pflicht FK Fahrzeugtyp<br />

Mitarbeiter_ID <strong>in</strong>teger optional ggf. FK Mitarbeiter<br />

Mitarbeiter (mi)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Personalnummer varchar(10) Pflicht e<strong>in</strong>deutig<br />

Name varchar(30) Pflicht<br />

Vorname varchar(30) Pflicht<br />

Geburtsdatum date Pflicht<br />

Telefon varchar(30) optional<br />

Mobil varchar(30) optional<br />

Email varchar(50) optional<br />

Raum varchar(10) optional<br />

Ist_Leiter char(1) Pflicht nur 'J' oder 'N'<br />

Abteilung_ID <strong>in</strong>teger Pflicht FK Abteilung<br />

Geschlecht ⋆ char(1) Pflicht nur 'W' oder 'M'<br />

Versicherungsgesellschaft (vg)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Bezeichnung varchar(30) Pflicht<br />

Ort varchar(30) optional<br />

Fahrzeugtyp (ft)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Bezeichnung varchar(30) Pflicht<br />

Hersteller_ID <strong>in</strong>teger Pflicht FK Fahrzeughersteller<br />

421


Tabellenstruktur der Beispieldatenbank<br />

Abteilung (ab)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Kuerzel varchar(10) Pflicht ⋆Datentyp auf CHAR(4)<br />

ändern<br />

Bezeichnung varchar(30) Pflicht<br />

Ort varchar(30) optional<br />

Fahrzeughersteller (fh)<br />

Spaltenname Datentyp Eigenschaft Erläuterung<br />

ID <strong>in</strong>teger PK<br />

Name varchar(30) Pflicht<br />

Land varchar(30) optional<br />

Änderungen<br />

In den folgenden Kapiteln werden Änderungen an dieser Struktur behandelt:<br />

• Änderung der Datenbankstruktur 1<br />

• Erzeugen von Testdaten 2<br />

1 Kapitel 35 auf Seite 397<br />

2 Kapitel 36 auf Seite 407<br />

422


B. Downloads<br />

B.1. Die Download-Seite . . . . . . . . . . . . . . . . . . . . . . . . . . 423<br />

B.2. Verb<strong>in</strong>dung zu den Datenbanksystemen . . . . . . . . . . . . . 424<br />

B.3. Die vollständige Beispieldatenbank . . . . . . . . . . . . . . . . 427<br />

B.4. Erstellen der Beispieldatenbank . . . . . . . . . . . . . . . . . . 427<br />

B.5. Skripte für nachträgliche Änderungen . . . . . . . . . . . . . . 428<br />

Hier wird beschrieben, wie die Dateien der Download-Seite zu diesem Buch verwendet<br />

werden können.<br />

So kommen Sie zur Beispieldatenbank:<br />

• Entweder Sie holen sich e<strong>in</strong>e fertige Version der Datenbank:<br />

• Laden Sie die komprimierte Datei herunter.<br />

• Benennen Sie die Datei um, soweit erforderlich.<br />

• Extrahieren Sie die Daten und speichern Sie das Ergebnis an e<strong>in</strong>er passenden<br />

Stelle.<br />

• Oder Sie erzeugen die Datenbank mit e<strong>in</strong>em Hilfsprogramm des von Ihnen<br />

verwendeten DBMS:<br />

• Erstellen Sie zunächst e<strong>in</strong>e leere Datenbank, soweit erforderlich.<br />

• Laden bzw. kopieren Sie das passende Skript zum Erzeugen der Datenbanktabellen<br />

und Daten.<br />

• Führen Sie das Skript mit e<strong>in</strong>em Hilfsprogramm des von Ihnen verwendeten<br />

DBMS aus.<br />

B.1. Die Download-Seite<br />

Verschiedene Dateien, die zur Beispieldatenbank gehören, stehen unter <strong>E<strong>in</strong>führung</strong><br />

<strong>in</strong> <strong>SQL</strong> (Downloads) 1 zur Verfügung − sozusagen statt e<strong>in</strong>er Buch-CD:<br />

• Vollständige Beispieldatenbanken<br />

• Skripte zur Erstellung der Beispieldatenbank<br />

1 http://www.vs-polis.de/content/download_sql.html<br />

423


Downloads<br />

• Skripte zur späteren Erweiterung<br />

In den folgenden Abschnitten erhalten Sie Erläuterungen dazu.<br />

B.1.1. Was ist e<strong>in</strong> Skript?<br />

Unter e<strong>in</strong>em Skript 2 versteht man <strong>in</strong> der EDV e<strong>in</strong>e Liste von Befehlen, die durch<br />

e<strong>in</strong>en e<strong>in</strong>zelnen Befehl aufgerufen und automatisch nache<strong>in</strong>ander ausgeführt<br />

werden. Diese Befehle s<strong>in</strong>d meist <strong>in</strong> e<strong>in</strong>er Datei zusammengefasst und werden<br />

dadurch ausgeführt, dass die betreffende Datei aufgerufen wird.<br />

Bei <strong>SQL</strong> werden solche Dateien unter W<strong>in</strong>dows üblicherweise mit der Endung<br />

’.sql’ gespeichert.<br />

B.2. Verb<strong>in</strong>dung zu den Datenbanksystemen<br />

In der Regel gibt es verschiedene Möglichkeiten: über e<strong>in</strong>e Befehlszeile oder mit<br />

e<strong>in</strong>em GUI-Programm. Der jeweils gängigste Weg hängt vom DBMS ab und wird<br />

<strong>in</strong> dessen Dokumentation beschrieben; im Folgenden wird nur e<strong>in</strong> Verfahren besprochen.<br />

B.2.1. Firebird<br />

Wenn Sie nicht mit e<strong>in</strong>em GUI-Programm arbeiten, funktioniert immer das Basisprogramm<br />

I<strong>SQL</strong>. Registrieren Sie zunächst e<strong>in</strong> Kürzel für die Datenbank, wie<br />

<strong>in</strong> der QuickStart-Beschreibung unter Use database aliases beschrieben: In der<br />

Datei aliases.conf im Firebird-Verzeichnis ist e<strong>in</strong> E<strong>in</strong>trag wie folgt e<strong>in</strong>zufügen:<br />

# unter W<strong>in</strong>dows<br />

wb-datenbank = C:\Users\Public\Documents\WikiBooks\<strong>SQL</strong>\Beispieldatenbank.fdb<br />

# unter L<strong>in</strong>ux<br />

wb-datenbank = /home/wikibooks/sql/Beispieldatenbank.fdb<br />

Für diese Änderung werden Adm<strong>in</strong>istrator-Rechte benötigt. Virtuelle Laufwerke<br />

werden nicht erkannt (auch das spricht für den E<strong>in</strong>trag e<strong>in</strong>es Alias-Namens für<br />

die Datenbank). Selbstverständlich müssen Sie im Verzeichnis der Datenbank<br />

alle erforderlichen Rechte erhalten.<br />

2 http://de.wikipedia.<strong>org</strong>/wiki/Skriptsprache<br />

424


Verb<strong>in</strong>dung zu den Datenbanksystemen<br />

Öffnen Sie nun e<strong>in</strong>e Command-Box und wechseln <strong>in</strong> das Verzeichnis, <strong>in</strong> dem<br />

die Datenbank stehen soll oder sich bereits bef<strong>in</strong>det. Starten Sie den <strong>SQL</strong>-<br />

Interpreter:<br />

C:\Programme\Firebird\b<strong>in</strong>\isql.exe<br />

Jeder der folgenden Befehle muss mit e<strong>in</strong>em Semikolon abgeschlossen werden.<br />

Er kann sich auch auf mehrere Zeilen verteilen.<br />

--- So erstellen Sie die neue Datenbank:<br />

<strong>SQL</strong>> create database ’wb-datenbank’<br />

CON> user ’SYSDBA’ password ’masterkey’ default character set UTF8;<br />

--- Führen Sie das Skript zur Erstellung der Datenbank aus:<br />

<strong>SQL</strong>> <strong>in</strong>put ’CreateScript-Firebird.sql’;<br />

--- Schließen Sie den <strong>SQL</strong>-Interpreter:<br />

<strong>SQL</strong>> quit;<br />

--- So öffnen Sie die die vorbereitete oder die erzeugte Datenbank zur Bearbeitung:<br />

<strong>SQL</strong>> connect ’wb-datenbank’ user ’SYSDBA’ password ’masterkey’;<br />

B.2.2. MS-<strong>SQL</strong><br />

Für dieses DBMS gibt es das Programm Microsoft <strong>SQL</strong> Server Management Studio<br />

(SSMS), und zwar für jede Server-Version e<strong>in</strong>e eigene Studio-Version. Beim<br />

Programmstart melden Sie sich am Server an; die Zusammenarbeit mit der Datenbank<br />

geht über den Objekt-Explorer im Abschnitt Databases.<br />

So registrieren Sie e<strong>in</strong>e vorhandene Datenbank, beispielsweise die fertig vorbereitete<br />

Beispieldatenbank:<br />

• Wählen Sie mit e<strong>in</strong>em Rechtsklick auf Databases die Option Attach.<br />

• Wählen Sie über die Schaltfläche Add (H<strong>in</strong>zufügen) im oberen Teil des Fensters<br />

die betreffende mdf-Datei aus und bestätigen Sie die Auswahl mit OK .<br />

So erstellen Sie die Beispieldatenbank mit dem Skript neu:<br />

• Gehen Sie über die Menü-Befehle File | Open | File und wählen Sie die Datei<br />

CreateScript-MS<strong>SQL</strong>2005.sql aus.<br />

• Führen Sie das Skript mit der Schaltfläche Execute bzw. F5 aus.<br />

In gleicher Weise können Sie zu der Beispieldatenbank spätere Skripte laden und<br />

ausführen.<br />

425


Downloads<br />

B.2.3. My<strong>SQL</strong><br />

H<strong>in</strong>weis: Die Datenbankstruktur wurde während der Erstellung dieses Buches<br />

überarbeitet. Für My<strong>SQL</strong> wurde auch das Skript zur Erstellung der Beispieldatenbank<br />

angepasst; es fehlen noch saubere Formulierungen <strong>in</strong> diesem Abschnitt und<br />

e<strong>in</strong> abschließender Test durch e<strong>in</strong>en My<strong>SQL</strong>-Fachmann.<br />

Melden Sie sich am Datenbanksystem an.<br />

Erstellen Sie die neue Datenbank:<br />

> create database Beispieldatenbank;<br />

Geben Sie an, dass diese Datenbank benutzt werden soll:<br />

> use Beispieldatenbank;<br />

Führen Sie das Skript aus:<br />

> source Script-My<strong>SQL</strong>.sql<br />

B.2.4. Oracle<br />

H<strong>in</strong>weis: Die Datenbankstruktur wurde während der Erstellung dieses Buches<br />

überarbeitet. Für Oracle wurde auch das Skript zur Erstellung der Beispieldatenbank<br />

angepasst; es fehlen noch saubere Formulierungen <strong>in</strong> diesem Abschnitt und<br />

e<strong>in</strong> abschließender Test durch e<strong>in</strong>en Oracle-Fachmann.<br />

Für dieses DBMS gibt es das Programm Database Configuration Assistant<br />

(DBCA). Starten Sie es mit den Rechten als Adm<strong>in</strong>istrator des Betriebssystems.<br />

So benutzen Sie e<strong>in</strong>e vorhandene Datenbank, beispielsweise die fertig vorbereitete<br />

Beispieldatenbank:<br />

• Dieser Punkt fehlt leider noch.<br />

So erstellen Sie die Beispieldatenbank mit dem Skript neu:<br />

• Wählen Sie den Punkt Create a Database.<br />

• Bei allen folgenden Schritten können Sie die e<strong>in</strong>fachsten E<strong>in</strong>stellungen übernehmen.<br />

• Im Schritt Database Content wählen Sie das für die Datenbank vorbereitete<br />

Skript CreateScript-Oracle.sql aus.<br />

Nach Fertigstellen bzw. F<strong>in</strong>ish steht die Datenbank zur Verfügung.<br />

426


B.2.5. <strong>SQL</strong>ite<br />

Die vollständige Beispieldatenbank<br />

H<strong>in</strong>weis: Die Datenbankstruktur wurde während der Erstellung dieses Buches<br />

überarbeitet. Für <strong>SQL</strong>ite wurde auch das Skript zur Erstellung der Beispieldatenbank<br />

angepasst; es fehlen noch saubere Formulierungen <strong>in</strong> diesem Abschnitt und<br />

e<strong>in</strong> abschließender Test durch e<strong>in</strong>en <strong>SQL</strong>ite-Fachmann.<br />

Für dieses DBMS gibt es das Kommandozeilen-Tool <strong>SQL</strong>ite3.<br />

So benutzen Sie e<strong>in</strong>e vorhandene Datenbank, beispielsweise die fertig vorbereitete<br />

Beispieldatenbank:<br />

sqlite3 Beispieldatenbank.db<br />

So erstellen Sie die Beispieldatenbank mit dem Skript neu:<br />

sqlite3 -<strong>in</strong>it CreateScript-Sqlite.sql Beispieldatenbank.db<br />

B.3. Die vollständige Beispieldatenbank<br />

E<strong>in</strong>e fertige Beispieldatenbank steht für das e<strong>in</strong>e oder andere DBMS als zip-<br />

Datei zur Verfügung. Sie müssen also so v<strong>org</strong>ehen:<br />

• Drücken Sie auf der Download-Seite im Abschnitt Vollständige Beispieldatenbank<br />

3 auf die Schaltfläche, die zu Ihrem DBMS gehört.<br />

• Speichern Sie die zip-Datei an e<strong>in</strong>er geeigneten Stelle, <strong>in</strong> der Regel <strong>in</strong> Ihrem<br />

Download-Bereich.<br />

• Öffnen Sie die zip-Datei zum Extrahieren.<br />

• Speichern Sie die dar<strong>in</strong> enthaltenen Dateien <strong>in</strong> e<strong>in</strong>em Arbeitsverzeichnis für<br />

die Beispieldatenbank.<br />

Danach können Sie die Beispieldatenbank direkt öffnen und bearbeiten, wie es<br />

oben beim DBMS beschrieben ist.<br />

B.4. Erstellen der Beispieldatenbank<br />

Wenn Sie die Beispieldatenbank selbst erstellen wollen, sollen oder müssen, gehen<br />

Sie bitte so vor:<br />

3 http://www.vs-polis.de/content/download_sql.html#3<br />

427


Downloads<br />

• Drücken Sie auf der Download-Seite im Abschnitt Skripte zur Erstellung 4 auf<br />

die Schaltfläche, die zu Ihrem DBMS gehört.<br />

• Wenn mit der l<strong>in</strong>ken Maustaste der Download nicht automatisch gestartet<br />

wird, dann verwenden Sie die rechte Maustaste und wählen Sie Ziel speichern<br />

unter.<br />

• Speichern Sie die Datei <strong>in</strong> dem Verzeichnis (Ordner), <strong>in</strong> dem die fertige Datenbank<br />

liegen soll, z. B. unter C:\Users\Public\Documents\WikiBooks\<strong>SQL</strong>.<br />

• Speichern Sie die Datei dort unter e<strong>in</strong>em s<strong>in</strong>nvollen Namen, z. B. als<br />

CreateScript-My<strong>SQL</strong>.sql (meistens wird e<strong>in</strong> richtiger Name v<strong>org</strong>egeben).<br />

Danach können Sie die Beispieldatenbank mit Hilfe dieses Skripts erzeugen, wie<br />

es oben beim DBMS beschrieben ist.<br />

Wenn es zu Ihrer Version des DBMS ke<strong>in</strong>e vorbereitete Skript-Datei gibt, dann<br />

holen Sie sich e<strong>in</strong>e andere (möglichst für e<strong>in</strong>e ähnliche Version) und gehen Sie<br />

genauso vor. In diesem Fall s<strong>in</strong>d Fehler beim Erzeugen der Datenbank zu erwarten.<br />

Dann müssen Sie nach der Beschreibung des betreffenden <strong>SQL</strong>-Dialekts<br />

nach und nach im Skript alle Fehler beseitigen, bis die Tabellen und Datensätze<br />

gespeichert werden.<br />

i Bitte arbeiten Sie mit!<br />

Die Autoren und die künftigen Leser s<strong>in</strong>d Ihnen sehr dankbar, wenn Sie danach<br />

die fehlerfreie Fassung der sql-Datei und die Beispieldatenbank dazu<br />

zur Verfügung stellen, wie auf der Download-Seite beschrieben. Damit helfen<br />

Sie bei der Verbesserung und der Erweiterung dieses Buches.<br />

B.5. Skripte für nachträgliche Änderungen<br />

Für verschiedene Arbeiten <strong>in</strong> den Kapiteln Änderung der Datenbankstruktur 5<br />

und Testdaten erzeugen 6 stehen ebenfalls Skript-Dateien bereit.<br />

Bitte beachten Sie unbed<strong>in</strong>gt: Diese Änderungen dürfen erst an den entsprechenden<br />

Stellen <strong>in</strong> diesen Kapiteln ausgeführt werden; vorher fehlt die notwendige<br />

Sachkenntnis. Außerdem muss – wie dort erläutert – die Reihenfolge der Änderungen<br />

beachtet werden, und vergessen Sie nicht die Datensicherung vorher<br />

und zwischendurch.<br />

4 http://www.vs-polis.de/content/download_sql.html#4<br />

5 Kapitel 35 auf Seite 397<br />

6 Kapitel 36 auf Seite 407<br />

428


Um e<strong>in</strong>es dieser Skripte zu nutzen, gehen Sie bitte so vor:<br />

Skripte für nachträgliche Änderungen<br />

• Drücken Sie auf der Download-Seite im Abschnitt Skripte zur späteren Erweiterung<br />

7 auf die Schaltfläche zu dem gewünschten Arbeitsschritt.<br />

• Wenn mit der l<strong>in</strong>ken Maustaste der Download nicht automatisch gestartet<br />

wird, dann verwenden Sie die rechte Maustaste und wählen Sie Ziel speichern<br />

unter.<br />

• Speichern Sie die Datei <strong>in</strong> dem Verzeichnis (Ordner), <strong>in</strong> dem die fertige Datenbank<br />

liegt, z. B. unter C:\Users\Public\Documents\WikiBooks\<strong>SQL</strong>.<br />

• Speichern Sie die Datei dort unter e<strong>in</strong>em s<strong>in</strong>nvollen Namen, z. B. als<br />

Skript-Spalten.sql (normalerweise wird e<strong>in</strong> richtiger Name v<strong>org</strong>egeben).<br />

• Öffnen Sie die Skript-Datei und ändern Sie all das, was „offensichtlich“ nicht<br />

zu Ihrem <strong>SQL</strong>-Dialekt passt.<br />

Achtung<br />

Diese Änderungen s<strong>in</strong>d teilweise sehr komplex. Gehen Sie deshalb immer<br />

schrittweise vor:<br />

1. Machen Sie e<strong>in</strong> Backup der Datenbank mit dem vorherigen,<br />

fehlerfreien Bestand.<br />

2. Führen Sie den nächsten <strong>SQL</strong>-Befehl aus (niemals das vollständige<br />

Skript!).<br />

3. Kontrollieren Sie das Ergebnis.<br />

4. Beim Auftreten von Fehlern ist das letzte Backup zurückzuholen<br />

(Restore).<br />

Anstelle der e<strong>in</strong>zelnen sql-Dateien können Sie auch alle Dateien „am Stück“ als<br />

zip-Datei holen. Dann s<strong>in</strong>d die sql-Dateien zu extrahieren und im Arbeitsverzeichnis<br />

der Datenbank zu speichern.<br />

7 http://www.vs-polis.de/content/download_sql.html#5<br />

429


C. Befehlsreferenz<br />

C.1. DDL (Data Def<strong>in</strong>ition Language) . . . . . . . . . . . . . . . . . . 431<br />

C.2. DML – Data Manipulation Language . . . . . . . . . . . . . . . . 435<br />

C.3. TCL – Transaction Control Language . . . . . . . . . . . . . . . 438<br />

C.4. DCL – Data Control Language . . . . . . . . . . . . . . . . . . . . 439<br />

C.5. Schlüsselwörter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440<br />

Für diese Übersicht 1 gelten die gleichen H<strong>in</strong>weise, wie sie <strong>in</strong> der E<strong>in</strong>leitung 2 genannt<br />

s<strong>in</strong>d.<br />

Die Struktur der Befehle richtet sich nach der <strong>SQL</strong>-Dokumentation. Beispiele<br />

s<strong>in</strong>d nur dann erwähnt, wenn sie <strong>in</strong> diesem Buch nicht enthalten s<strong>in</strong>d, aber dennoch<br />

von Bedeutung se<strong>in</strong> können.<br />

Bitte beachten Sie vor allem die <strong>SQL</strong>-Dialekte: Verschiedene Optionen gelten nur<br />

manchmal, nicht immer oder mit anderen Schreibweisen.<br />

C.1. DDL (Data Def<strong>in</strong>ition Language)<br />

Zu dieser Kategorie gehören Befehle, die die Datenbank-Objekte beschreiben.<br />

Die grundlegenden Befehle lauten:<br />

CREATE -- erzeugt e<strong>in</strong> Objekt<br />

ALTER -- ändert e<strong>in</strong> Objekt<br />

DROP -- löscht e<strong>in</strong> Objekt<br />

1 Diese Befehlsreferenz war Teil e<strong>in</strong>es nicht mehr vorhandenen Buches <strong>SQL</strong>. Bei der Übernahme<br />

wurden Gliederung und Struktur angepasst, Beispiele weitgehend gestrichen, weil sie sowieso<br />

Teil des Buches s<strong>in</strong>d, und auf die WebL<strong>in</strong>ks verzichtet, weil diese <strong>in</strong> der E<strong>in</strong>leitung {Kapitel 3 auf<br />

Seite 11} und den Webl<strong>in</strong>ks {Anhang D auf Seite 445} stehen. Die Versionsgeschichte und die<br />

Diskussionsseite der Onl<strong>in</strong>e-Version enthalten die früheren Beiträge weiterh<strong>in</strong>.<br />

2 Kapitel 3 auf Seite 11<br />

431


Befehlsreferenz<br />

C.1.1. TABLE – Tabelle bearbeiten<br />

CREATE TABLE<br />

Dieser Befehl legt e<strong>in</strong>e neue Tabelle an. Basissyntax:<br />

CREATE TABLE [() | AS ]<br />

Syntax e<strong>in</strong>er e<strong>in</strong>zelnen :<br />

[()] [] [DEFAULT ]<br />

Als stehen die Datentypen des jeweiligen DBMS zur Verfügung, z. B.<br />

CHAR, VARCHAR, INT, FLOAT.<br />

gibt an, ob das Feld leer bleiben darf (NULL) oder ob e<strong>in</strong> Wert vorhanden<br />

se<strong>in</strong> muss (NOT NULL).<br />

Als kann e<strong>in</strong> Wert v<strong>org</strong>egeben werden, der automatisch gespeichert<br />

wird, falls das Feld nicht leer bleiben darf, aber tatsächlich ke<strong>in</strong> Wert e<strong>in</strong>getragen<br />

wird.<br />

ist e<strong>in</strong>e Abfrage durch SELECT, die als Grundlage für die neue<br />

Tabelle verwendet wird.<br />

Beispiele<br />

-- E<strong>in</strong>e neue Tabelle mit 2 Feldern anlegen<br />

CREATE TABLE testtab (feld1 varchar (20), feld2 number (20))<br />

-- E<strong>in</strong>e neue Tabelle aus e<strong>in</strong>em SELECT-Statement erstellen<br />

CREATE TABLE testtab AS SELECT feld1, feld2 FROM testtab2<br />

ALTER TABLE<br />

Dieser Befehl verändert e<strong>in</strong>e bestehende Tabelle. Basissyntax:<br />

ALTER TABLE <br />

kann e<strong>in</strong>s der folgenden se<strong>in</strong>:<br />

-- Tabelle umbenennen<br />

RENAME TO <br />

432


-- Feld(er) h<strong>in</strong>zufügen<br />

ADD ()<br />

-- Feld ändern<br />

MODIFY <br />

-- Feld(er) löschen<br />

DROP ()<br />

-- Haupt-/ Fremdschlüssel h<strong>in</strong>zufügen<br />

ADD CONSTRAINT <br />

-- Haupt-/ Fremdschlüssel löschen<br />

DROP PRIMARY KEY -- Hauptschlüssel<br />

DROP FOREIGN KEY -- Fremdschlüssel<br />

-- Index erstellen<br />

ADD INDEX [] ()<br />

-- Index löschen<br />

DROP INDEX <br />

kann e<strong>in</strong>e der folgenden se<strong>in</strong>:<br />

DDL (Data Def<strong>in</strong>ition Language)<br />

-- Hauptschlüssel<br />

PRIMARY KEY ()<br />

-- Fremdschlüssel<br />

FOREIGN KEY () REFERENCES ()<br />

kann (<strong>in</strong> den meisten Fällen) folgender se<strong>in</strong>:<br />

UNIQUE<br />

PRIMARY<br />

FULLTEXT<br />

Beispiele<br />

-- Tabelle umbenennen<br />

ALTER, TABLE testtab RENAME TO tab2<br />

-- Feld h<strong>in</strong>zufügen<br />

ALTER TABLE testtab ADD (feld1 number(59))<br />

ALTER TABLE testtab ADD (feld2 date NOT NULL)<br />

ALTER TABLE testtab ADD (feld3 number(50) DEFAULT 10)<br />

-- E<strong>in</strong> Feld auf NUMBER, Länge 50 und NULL ändern:<br />

ALTER TABLE testtab MODIFY feld1 number (50) NULL<br />

-- Feld löschen<br />

433


Befehlsreferenz<br />

ALTER TABLE testtab DROP feld1<br />

-- Primary-Key erstellen:<br />

ALTER TABLE testtab ADD CONSTRAINT primary_key_bezeichnung PRIMARY KEY (feld1)<br />

-- Primary-Key löschen<br />

ALTER TABLE testtab DROP PRIMARY KEY<br />

-- Foreign-Key erstellen:<br />

ALTER TABLE testtab ADD CONSTRAINT foreign_key_bezeichnung<br />

FOREIGN KEY (feld1) REFERENCES tab2 (feld2)<br />

-- Foreign-Key löschen<br />

ALTER TABLE testtab DROP FOREIGN KEY foreign_key_bezeichnung<br />

-- Index erstellen<br />

ALTER TABLE testtab ADD INDEX <strong>in</strong>dex_name <strong>in</strong>dex_typ (feld1)<br />

-- Index löschen<br />

ALTER TABLE testtab DROP INDEX <strong>in</strong>dex_name<br />

DROP TABLE<br />

Tabelle testtab löschen:<br />

DROP TABLE testtab<br />

Tabelle testtab <strong>in</strong>klusive Constra<strong>in</strong>ts löschen:<br />

DROP TABLE testtab CASCADE CONSTRAINTS<br />

C.1.2. INDEX – Index bearbeiten<br />

CREATE INDEX<br />

CREATE INDEX on ();<br />

CREATE INDEX on (,,...);<br />

DROP INDEX<br />

434<br />

DROP INDEX


C.1.3. VIEW – Ansicht auf Tabellen<br />

CREATE VIEW<br />

E<strong>in</strong>e spezielle Sicht auf e<strong>in</strong>e oder mehrere Tabellen erzeugen:<br />

CREATE VIEW AS <br />

DML – Data Manipulation Language<br />

Als kann e<strong>in</strong> „beliebiger“ SELECT-Befehl verwendet werden.<br />

DROP VIEW<br />

DROP VIEW <br />

C.1.4. E<strong>in</strong>zelheiten<br />

Diese Befehle und Varianten werden <strong>in</strong> folgenden Kapiteln genauer behandelt:<br />

• DDL − E<strong>in</strong>zelheiten 3<br />

• Erstellen von Views 4<br />

C.2. DML – Data Manipulation Language<br />

Diese Kategorie umfasst die Befehle, die etwas mit e<strong>in</strong>em Datenbestand machen.<br />

C.2.1. Abfragen mit SELECT<br />

Mit dem SELECT-Befehl werden Daten aus e<strong>in</strong>er oder mehreren Tabellen abgerufen.<br />

SELECT<br />

[ DISTINCT | ALL ]<br />

<br />

FROM <br />

[ WHERE ]<br />

[ GROUP BY ]<br />

[ HAVING ]<br />

3 Kapitel 28 auf Seite 285<br />

4 Kapitel 27 auf Seite 271<br />

435


Befehlsreferenz<br />

[ { UNION [ALL] | INTERSECT | MINUS } ]<br />

[ ORDER BY ]<br />

Dieser Befehl und se<strong>in</strong>e Klauseln werden vor allem unter Ausführliche SELECT-<br />

Struktur 5 behandelt.<br />

C.2.2. Neuaufnahmen mit INSERT<br />

Mit dem INSERT-Befehl werden Daten <strong>in</strong> e<strong>in</strong>er Tabelle erstmals gespeichert. Als<br />

Syntax gibt es vor allem zwei Varianten:<br />

INSERT INTO [ ] VALUES ( )<br />

INSERT INTO [ ] SELECT <br />

Als ist e<strong>in</strong>e vorhandene Tabelle anzugeben. Unter bestimmten starken<br />

E<strong>in</strong>schränkungen kann auch e<strong>in</strong>e VIEW angegeben werden, siehe dort 6 .<br />

Als werden e<strong>in</strong>e oder mehrere Spalten der Tabelle aufgeführt.<br />

• Spalten, die <strong>in</strong> dieser Liste genannt werden, bekommen e<strong>in</strong>en Wert aus der<br />

bzw. dem SELECT-Ausdruck zugewiesen.<br />

• Spalten, die <strong>in</strong> dieser Liste nicht genannt werden, bekommen e<strong>in</strong>en DEFAULT-<br />

Wert oder NULL zugewiesen; dies hängt von der Tabellendef<strong>in</strong>ition für die betreffende<br />

Spalte ab.<br />

• Die Reihenfolge der Spalten <strong>in</strong> der muss mit der Reihenfolge der<br />

Daten <strong>in</strong> der bzw. dem übere<strong>in</strong>stimmen.<br />

• Die Datentypen von Spalten und Werten müssen übere<strong>in</strong>stimmen oder zum<strong>in</strong>dest<br />

soweit „kompatibel“ se<strong>in</strong>, dass sie automatisch (implizit) konvertiert<br />

werden können.<br />

Wenn durch ke<strong>in</strong>e Spalten angegeben werden, müssen alle Spalten<br />

aus der Tabellendef<strong>in</strong>ition <strong>in</strong> dieser Reihenfolge mit Inhalt versehen werden,<br />

und sei es ausdrücklich mit NULL.<br />

Die besteht aus e<strong>in</strong>em oder mehreren Werten (entsprechend der<br />

). Dabei kann es sich um konstante Werte oder Ergebnisse e<strong>in</strong>facher<br />

Funktionen handeln, aber auch um Abfragen. Wenn komplexe Ausdrücke<br />

als Werte zugewiesen werden sollen, ist die Variante mit SELECT zu verwenden.<br />

E<strong>in</strong>zelheiten werden behandelt <strong>in</strong>:<br />

5 Kapitel 15 auf Seite 129<br />

6 Kapitel 27 auf Seite 271<br />

436


• <strong>SQL</strong>-Befehle 7<br />

• DML (2) – Daten speichern 8<br />

• Unterabfragen 9<br />

C.2.3. Änderungen mit UPDATE<br />

DML – Data Manipulation Language<br />

Mit dem UPDATE-Befehl werden Daten <strong>in</strong> e<strong>in</strong>er Tabelle geändert, und zwar für<br />

e<strong>in</strong>e oder mehrere Spalten <strong>in</strong> e<strong>in</strong>em oder mehreren Datensätzen. Der Befehl benutzt<br />

diese Syntax:<br />

UPDATE <br />

SET = [ ,<br />

= , usw.<br />

= ]<br />

[ WHERE ];<br />

Als ist e<strong>in</strong>e vorhandene Tabelle anzugeben. Unter bestimmten starken<br />

E<strong>in</strong>schränkungen kann auch e<strong>in</strong>e VIEW angegeben werden, siehe dazu dort 10 .<br />

Die Wertzuweisungsliste <strong>in</strong> der SET-Klausel besteht aus e<strong>in</strong>er oder (mit Komma<br />

verbunden) mehreren Wertzuweisungen.<br />

Die WHERE-Klausel arbeitet mit e<strong>in</strong>er oder mehreren Bed<strong>in</strong>gungen genauso wie<br />

beim SELECT-Befehl. Ohne WHERE-Bed<strong>in</strong>gung werden alle Zeilen geändert.<br />

E<strong>in</strong>zelheiten werden behandelt <strong>in</strong>:<br />

• <strong>SQL</strong>-Befehle 11<br />

• DML (2) – Daten speichern 12<br />

• Unterabfragen 13<br />

C.2.4. Löschungen mit DELETE<br />

Mit dem DELETE-Befehl werden Daten <strong>in</strong> e<strong>in</strong>er Tabelle gelöscht, und zwar null,<br />

e<strong>in</strong>e, mehrere oder alle Zeilen mit e<strong>in</strong>em e<strong>in</strong>zigen Befehl. Dieser benutzt die folgende<br />

Syntax:<br />

7 Kapitel 7 auf Seite 47<br />

8 Kapitel 9 auf Seite 69<br />

9 Kapitel 26 auf Seite 251<br />

10 Kapitel 27 auf Seite 271<br />

11 Kapitel 7 auf Seite 47<br />

12 Kapitel 9 auf Seite 69<br />

13 Kapitel 26 auf Seite 251<br />

437


Befehlsreferenz<br />

DELETE FROM <br />

[ WHERE ];<br />

Als ist e<strong>in</strong>e vorhandene Tabelle anzugeben. Unter bestimmten starken<br />

E<strong>in</strong>schränkungen kann auch e<strong>in</strong>e VIEW angegeben werden, siehe dazu dort 14 .<br />

Die WHERE-Klausel arbeitet mit e<strong>in</strong>er oder mehreren Bed<strong>in</strong>gungen genauso wie<br />

beim SELECT-Befehl. Ohne WHERE-Bed<strong>in</strong>gung werden alle Zeilen gelöscht.<br />

E<strong>in</strong>zelheiten werden behandelt <strong>in</strong>:<br />

• <strong>SQL</strong>-Befehle 15<br />

• DML (2) – Daten speichern 16<br />

C.2.5. Tabelle leeren mit TRUNCATE<br />

Mit dem TRUNCATE-Befehl werden sämtliche Daten <strong>in</strong> e<strong>in</strong>er Tabelle gelöscht:<br />

TRUNCATE TABLE ;<br />

Dies entspricht dem DELETE-Befehl ohne WHERE-Klausel:<br />

DELETE FROM ;<br />

Dabei gibt es den gravierenden Unterschied, dass die Löschungen nach TRUN-<br />

CATE nicht mehr mit ROLLBACK rückgängig gemacht werden können, da die<br />

Transaktion implizit abgeschlossen wird. Auch deshalb ist diese Anweisung üblicherweise<br />

schneller als e<strong>in</strong>e entsprechende DELETE-Anweisung.<br />

Achtung: TRUNCATE TABLE kann nicht mit Foreign Keys umgehen!<br />

C.3. TCL – Transaction Control Language<br />

Transaktionen 17 s<strong>org</strong>en für die Daten<strong>in</strong>tegrität: Anweisungen, die logisch zusammengehören,<br />

werden entweder alle vollständig oder überhaupt nicht ausgeführt.<br />

Die TCL-Anweisungen steuern Transaktionen.<br />

14 Kapitel 27 auf Seite 271<br />

15 Kapitel 7 auf Seite 47<br />

16 Kapitel 9 auf Seite 69<br />

17 Kapitel 11 auf Seite 89<br />

438


C.3.1. Bestätigen mit COMMIT<br />

DCL – Data Control Language<br />

Abschließen e<strong>in</strong>er Transaktion, wobei alle seit Beg<strong>in</strong>n der Transaktion durchgeführten<br />

Änderungen bestätigt und endgültig gespeichert werden.<br />

COMMIT;<br />

Nicht alle Datenbanken unterstützen Transaktionen. E<strong>in</strong>ige können auch je nach<br />

Konfiguration oder verwendetem Tabellentyp <strong>in</strong> verschiedenen Modi arbeiten.<br />

So f<strong>in</strong>det im sogenannten „Auto Commit“-Modus nach Ausführung jeder DML-<br />

Anweisung sofort e<strong>in</strong> COMMIT statt, die Änderungen können also nicht mehr<br />

über ROLLBACK rückgängig gemacht werden.<br />

C.3.2. Widerruf mit ROLLBACK<br />

Abschließen e<strong>in</strong>er Transaktion, wobei alle seit Beg<strong>in</strong>n der Transaktion durchgeführten<br />

Änderungen rückgängig gemacht werden.<br />

ROLLBACK;<br />

Soweit das verwendete DBMS Transaktionen unterstützt, wird durch die<br />

ROLLBACK-Anweisung der Daten<strong>in</strong>halt auf den Zustand vor Ausführung der<br />

seit dem letzten COMMIT/ROLLBACK durchgeführten Datenmanipulationen<br />

zurückgesetzt.<br />

C.4. DCL – Data Control Language<br />

E<strong>in</strong>e „vollwertige“ <strong>SQL</strong>-Datenbank enthält umfassende Regelungen darüber, wie<br />

Zugriffsrechte 18 auf Objekte (Tabellen, e<strong>in</strong>zelne Felder, <strong>in</strong>terne Funktionen usw.)<br />

vergeben werden. Am Anfang stehen diese Rechte nur dem Ersteller der Datenbank<br />

und dem System-Adm<strong>in</strong>istrator zu. Andere Benutzer müssen ausdrücklich<br />

zu e<strong>in</strong>zelnen Handlungen ermächtigt werden.<br />

Die vielfältigen Abstufungen der Rechte und Optionen s<strong>in</strong>d <strong>in</strong> der jeweiligen<br />

DBMS-Dokumentation nachzulesen.<br />

18 Kapitel 12 auf Seite 95<br />

439


Befehlsreferenz<br />

C.4.1. GRANT<br />

Mit diesem Befehl wird e<strong>in</strong> Recht übertragen:<br />

GRANT ON TO <br />

C.4.2. REVOKE<br />

Mit diesem Befehl wird e<strong>in</strong> Recht widerrufen:<br />

REVOKE ON FROM <br />

C.5. Schlüsselwörter<br />

Mithilfe der Schlüsselwörter kann e<strong>in</strong> DBMS <strong>in</strong> e<strong>in</strong>er beliebigen Zeichenkette<br />

<strong>SQL</strong>-Befehle, weitere feste Begriffe und ergänzende Angaben unterscheiden.<br />

Reservierte Begriffe: Diese werden für <strong>SQL</strong>-Befehle und ähnliche feststehende<br />

Angaben verwendet.<br />

Nicht-reservierte Begriffe: Diese s<strong>in</strong>d nach <strong>SQL</strong>-Standard eigentlich freigegeben<br />

und für spezielle Verwendung nur vorbereitet.<br />

In der Praxis macht diese Unterscheidung ke<strong>in</strong>en S<strong>in</strong>n; <strong>in</strong> der folgenden Aufstellung<br />

wird sie deshalb nur als H<strong>in</strong>weis [res] bzw. [non] erwähnt.<br />

A<br />

i Warnung<br />

Sie sollten Schlüsselwörter niemals für eigene Bezeichner wie Tabellen,<br />

Spalten, Prozeduren usw. verwenden!<br />

[res] ABS ALL ALLOCATE ALTER AND ANY ARE ARRAY AS ASENSITIVE ASYMMETRIC AT<br />

ATOMIC AUTHORIZATION AVG<br />

[non] A ABSOLUTE ACTION ADA ADD ADMIN AFTER ALWAYS ASC ASSERTION ASSIGNMENT<br />

ATTRIBUTE ATTRIBUTES<br />

B<br />

[res] BEGIN BETWEEN BIGINT BINARY BLOB BOOLEAN BOTH BY<br />

440


[non] BEFORE BERNOULLI BREADTH<br />

C<br />

Schlüsselwörter<br />

[res] CALL CALLED CARDINALITY CASCADED CASE CAST CEIL CEILING CHAR CHAR_LENGTH<br />

CHARACTER CHARACTER_LENGTH CHECK CLOB CLOSE COALESCE COLLATE COLLECT COLUMN<br />

COMMIT CONDITION CONNECT CONSTRAINT CONVERT CORR CORRESPONDING COUNT COVAR_POP<br />

COVAR_SAMP CREATE CROSS CUBE CUME_DIST CURRENT CURRENT_DATE<br />

CURRENT_DEFAULT_TRANSFORM_GROUP CURRENT_PATH CURRENT_ROLE CURRENT_TIME<br />

CURRENT_TIMESTAMP CURRENT_TRANSFORM_GROUP_FOR_TYPE CURRENT_USER CURSOR CYCLE<br />

[non] C CASCADE CATALOG CATALOG_NAME CHAIN CHARACTER_SET_CATALOG<br />

CHARACTER_SET_NAME CHARACTER_SET_SCHEMA CHARACTERISTICS CHARACTERS<br />

CLASS_ORIGIN COBOL COLLATION COLLATION_CATALOG COLLATION_NAME COLLATION_SCHEMA<br />

COLUMN_NAME COMMAND_FUNCTION COMMAND_FUNCTION_CODE COMMITTED CONDITION_NUMBER<br />

CONNECTION CONNECTION_NAME CONSTRAINT_CATALOG CONSTRAINT_NAME<br />

CONSTRAINT_SCHEMA CONSTRAINTS CONSTRUCTOR CONTAINS CONTINUE CURSOR_NAME<br />

D<br />

[res] DATE DAY DEALLOCATE DEC DECIMAL DECLARE DEFAULT DELETE DENSE_RANK DEREF<br />

DESCRIBE DETERMINISTIC DISCONNECT DISTINCT DOUBLE DROP DYNAMIC<br />

[non] DATA DATETIME_INTERVAL_CODE DATETIME_INTERVAL_PRECISION DEFAULTS<br />

DEFERRABLE DEFERRED DEFINED DEFINER DEGREE DEPTH DERIVED DESC DESCRIPTOR<br />

DIAGNOSTICS DISPATCH DOMAIN DYNAMIC_FUNCTION DYNAMIC_FUNCTION_CODE<br />

E<br />

[res] EACH ELEMENT ELSE END END-EXEC ESCAPE EVERY EXCEPT EXEC EXECUTE EXISTS<br />

EXP EXTERNAL EXTRACT<br />

[non] EQUALS EXCEPTION EXCLUDE EXCLUDING<br />

F<br />

[res] FALSE FETCH FILTER FLOAT FLOOR FOR FOREIGN FREE FROM FULL FUNCTION<br />

FUSION<br />

[non] FINAL FIRST FOLLOWING FORTRAN FOUND<br />

G<br />

[res] GET GLOBAL GRANT GROUP GROUPING<br />

[non] G GENERAL GENERATED GO GOTO GRANTED<br />

H<br />

[res] HAVING HOLD HOUR<br />

[non] HIERARCHY<br />

441


Befehlsreferenz<br />

I<br />

[res] IDENTITY IN INDICATOR INNER INOUT INSENSITIVE INSERT INT INTEGER<br />

INTERSECT INTERSECTION INTERVAL INTO IS<br />

[non] IMMEDIATE IMPLEMENTATION INCLUDING INCREMENT INITIALLY INPUT INSTANCE<br />

INSTANTIABLE INVOKER ISOLATION<br />

J<br />

[res] JOIN<br />

K<br />

[non] K KEY KEY_MEMBER KEY_TYPE<br />

L<br />

[res] LANGUAGE LARGE LATERAL LEADING LEFT LIKE LN LOCAL LOCALTIME<br />

LOCALTIMESTAMP LOWER<br />

[non] LAST LENGTH LEVEL LOCATOR<br />

M<br />

[res] MATCH MAX MEMBER MERGE METHOD MIN MINUTE MOD MODIFIES MODULE MONTH<br />

MULTISET<br />

[non] M MAP MATCHED MAXVALUE MESSAGE_LENGTH MESSAGE_OCTET_LENGTH MESSAGE_TEXT<br />

MINVALUE MORE MUMPS<br />

N<br />

[res] NATIONAL NATURAL NCHAR NCLOB NEW NO NONE NORMALIZE NOT NULL NULLIF<br />

NUMERIC<br />

[non] NAME NAMES NESTING NEXT NORMALIZED NULLABLE NULLS NUMBER<br />

O<br />

[res] OCTET_LENGTH OF OLD ON ONLY OPEN OR ORDER OUT OUTER OVER OVERLAPS<br />

OVERLAY<br />

[non] OBJECT OCTETS OPTION OPTIONS ORDERING ORDINALITY OTHERS OUTPUT<br />

OVERRIDING<br />

P<br />

[res] PARAMETER PARTITION PERCENT_RANK PERCENTILE_CONT PERCENTILE_DISC<br />

POSITION POWER PRECISION PREPARE PRIMARY PROCEDURE<br />

442


Schlüsselwörter<br />

[non] PAD PARAMETER_MODE PARAMETER_NAME PARAMETER_ORDINAL_POSITION<br />

PARAMETER_SPECIFIC_CATALOG PARAMETER_SPECIFIC_NAME PARAMETER_SPECIFIC_SCHEMA<br />

PARTIAL PASCAL PATH PLACING PLI PRECEDING PRESERVE PRIOR PRIVILEGES PUBLIC<br />

R<br />

[res] RANGE RANK READS REAL RECURSIVE REF REFERENCES REFERENCING REGR_AVGX<br />

REGR_AVGY REGR_COUNT REGR_INTERCEPT REGR_R2 REGR_SLOPE REGR_SXX REGR_SXY<br />

REGR_SYY RELEASE RESULT RETURN RETURNS REVOKE RIGHT ROLLBACK ROLLUP ROW<br />

ROW_NUMBER ROWS<br />

[non] READ RELATIVE REPEATABLE RESTART RESTRICT RETURNED_CARDINALITY<br />

RETURNED_LENGTH RETURNED_OCTET_LENGTH RETURNED_<strong>SQL</strong>STATE ROLE ROUTINE<br />

ROUTINE_CATALOG ROUTINE_NAME ROUTINE_SCHEMA ROW_COUNT<br />

S<br />

[res] SAVEPOINT SCOPE SCROLL SEARCH SECOND SELECT SENSITIVE SESSION_USER SET<br />

SIMILAR SMALLINT SOME SPECIFIC SPECIFICTYPE <strong>SQL</strong> <strong>SQL</strong>EXCEPTION <strong>SQL</strong>STATE<br />

<strong>SQL</strong>WARNING SQRT START STATIC STDDEV_POP STDDEV_SAMP SUBMULTISET SUBSTRING SUM<br />

SYMMETRIC SYSTEM SYSTEM_USER<br />

[non] SCALE SCHEMA SCHEMA_NAME SCOPE_CATALOG SCOPE_NAME SCOPE_SCHEMA SECTION<br />

SECURITY SELF SEQUENCE SERIALIZABLE SERVER_NAME SESSION SETS SIMPLE SIZE<br />

SOURCE SPACE SPECIFIC_NAME STATE STATEMENT STRUCTURE STYLE SUBCLASS_ORIGIN<br />

T<br />

[res] TABLE TABLESAMPLE THEN TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO<br />

TRAILING TRANSLATE TRANSLATION TREAT TRIGGER TRIM TRUE<br />

[non] TABLE_NAME TEMPORARY TIES TOP_LEVEL_COUNT TRANSACTION TRANSACTION_ACTIVE<br />

TRANSACTIONS_COMMITTED TRANSACTIONS_ROLLED_BACK TRANSFORM TRANSFORMS<br />

TRIGGER_CATALOG TRIGGER_NAME TRIGGER_SCHEMA TYPE<br />

U<br />

[res] UESCAPE UNION UNIQUE UNKNOWN UNNEST UPDATE UPPER USER USING<br />

[non] UNBOUNDED UNCOMMITTED UNDER UNNAMED USAGE USER_DEFINED_TYPE_CATALOG<br />

USER_DEFINED_TYPE_CODE USER_DEFINED_TYPE_NAME USER_DEFINED_TYPE_SCHEMA<br />

V<br />

[res] VALUE VALUES VAR_POP VAR_SAMP VARCHAR VARYING<br />

[non] VIEW<br />

W<br />

[res] WHEN WHENEVER WHERE WIDTH_BUCKET WINDOW WITH WITHIN WITHOUT<br />

[non] WORK WRITE<br />

443


Befehlsreferenz<br />

Y<br />

[res] YEAR<br />

Z<br />

[non] ZONE<br />

Diese Aufstellung wurde übernommen aus <strong>SQL</strong>-2003 Documents 19 :<br />

• Jim Melton: Information technology – Database languages – <strong>SQL</strong> – Part 2:<br />

Foundation (<strong>SQL</strong>/Foundation). August 2003 (zuletzt abgerufen am 24. Juni<br />

2012), dar<strong>in</strong> enthalten 5WD-02-Foundation-2003-09.pdf pp.136<br />

19 http://www.wiscorp.com/sql_2003_standard.zip<br />

444


D. Weitere Informationen<br />

D.1. Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . 445<br />

D.2. Webl<strong>in</strong>ks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446<br />

D.1. Literaturverzeichnis<br />

• Faeskorn-Woyke, Heide; Bertelsmeier, Birgit; Riemer, Petra; Bauer, Elena: Datenbanksysteme,<br />

Theorie und Praxis mit <strong>SQL</strong>2003, Oracle und My<strong>SQL</strong>. Pearson-<br />

Studium, München 2007, ISBN 978-3-8273-7266-6.<br />

• Hernandez, Michael J.; Viescas, John: Go To <strong>SQL</strong>. Addison-Wesley, München<br />

2001, ISBN 3-8273-1772-X.<br />

• Kosch, Andreas: InterBase-Datenbankentwicklung mit Delphi. Software & Support<br />

Verlag, Frankfurt a.M. 2001, ISBN 3-935042-09-4.<br />

• Kuhlmann, Gregor; Müllmerstadt, Friedrich: <strong>SQL</strong>. Rowohlt, Re<strong>in</strong>bek 2004,<br />

ISBN 3-499-61245-3.<br />

• Marsch, Jürgen; Fritze, Jörg: Erfolgreiche Datenbankanwendung mit <strong>SQL</strong>3. Praxisorientierte<br />

Anleitung – effizienter E<strong>in</strong>satz – <strong>in</strong>klusive <strong>SQL</strong>-Tun<strong>in</strong>g. Vieweg +<br />

Teubner, Wiesbaden 2002, ISBN 3-528-55210-7.<br />

• Matthiessen, Günter; Unterste<strong>in</strong>, Michael: Relationale Datenbanken und<br />

Standard-<strong>SQL</strong> − Konzepte der Entwicklung und Anwendung. Addison-Wesley,<br />

München 2007, ISBN 3-8273-2085-2.<br />

• Schicker, Edw<strong>in</strong>: Datenbanken und <strong>SQL</strong> − E<strong>in</strong>e praxisorientierte <strong>E<strong>in</strong>führung</strong><br />

mit H<strong>in</strong>weisen zu Oracle und MS-Access. Vieweg + Teubner, Wiesbaden 2000,<br />

ISBN 3-519-22991-9.<br />

• Türker, Can: <strong>SQL</strong> 1999 & <strong>SQL</strong> 2003. dpunkt.Verlag, Heidelberg 2003, ISBN 3-<br />

89864-219-4.<br />

• Warner, Daniel, Günter Leitenbauer: <strong>SQL</strong>. Franzis, Po<strong>in</strong>g 2003, ISBN 3-7723-<br />

7527-8.<br />

445


Weitere Informationen<br />

D.2. Webl<strong>in</strong>ks<br />

Hier gibt es weitere Informationen zum Thema <strong>SQL</strong>.<br />

D.2.1. Bücher <strong>in</strong> Wikibooks<br />

• Wikibooks: PL/<strong>SQL</strong> 1<br />

• Wikibooks: Oracle 2<br />

D.2.2. Allgeme<strong>in</strong>es zur Datenbank-Programmierung<br />

Wikipedia bietet folgende Erläuterungen:<br />

• <strong>SQL</strong> 3<br />

• Duales Lizenzsystem 4<br />

• Open Source 5<br />

• case <strong>in</strong>sensitive 6<br />

Außerdem gibt es Artikel zum Überblick:<br />

• Relationale Datenbank 7<br />

• Objektrelationale Datenbank 8<br />

• Objektorientiertes Datenbankmodell 9<br />

• Liste der Datenbankmanagementsysteme 10<br />

D.2.3. Beiträge zu <strong>SQL</strong> allgeme<strong>in</strong><br />

• <strong>SQL</strong>-2003 Documents 11 (abgerufen am 24. Juni 2012), darunter vor allem:<br />

Jim Melton: Information technology – Database languages – <strong>SQL</strong>. Part 2: Foundation<br />

(<strong>SQL</strong>/Foundation). August 2003 – 5WD-02-Foundation-2003-09.pdf<br />

1 http://de.wikibooks.<strong>org</strong>/wiki/PL/<strong>SQL</strong><br />

2 http://de.wikibooks.<strong>org</strong>/wiki/Oracle<br />

3 http://de.wikipedia.<strong>org</strong>/wiki/<strong>SQL</strong><br />

4 http://de.wikipedia.<strong>org</strong>/wiki/Mehrfachlizenzierung<br />

5 http://de.wikipedia.<strong>org</strong>/wiki/Open%20Source<br />

6 http://de.wikipedia.<strong>org</strong>/wiki/Case%20sensitivity<br />

7 http://de.wikipedia.<strong>org</strong>/wiki/Relationale%20Datenbank<br />

8 http://de.wikipedia.<strong>org</strong>/wiki/Objektrelationale%20Datenbank<br />

9 http://de.wikipedia.<strong>org</strong>/wiki/Objektorientiertes%20Datenbankmodell<br />

10 http://de.wikipedia.<strong>org</strong>/wiki/Liste%20der%20Datenbankmanagementsysteme<br />

11 http://www.wiscorp.com/sql_2003_standard.zip<br />

446


• L<strong>in</strong>ks zum Thema <strong>SQL</strong> 12 im Open Directory Project 13<br />

Webl<strong>in</strong>ks<br />

• <strong>SQL</strong>-Referenz (deutsch) 14 (Referenz mit Stichwortverzeichnis und Tutorials)<br />

• The 1995 <strong>SQL</strong> Reunion: People, Projects, and Politics 15 (zur frühen Geschichte<br />

von <strong>SQL</strong>)<br />

• Interaktiver <strong>SQL</strong>-Tra<strong>in</strong>er 16<br />

D.2.4. Bestimmte <strong>SQL</strong>-Datenbanksysteme<br />

Wenn nichts anderes gesagt wird, handelt es sich um deutschsprachige Seiten.<br />

• allgeme<strong>in</strong>: Das DB2 <strong>SQL</strong> Cookbook von Graeme Birchall 17 (englisch)<br />

enthält viele nützliche <strong>SQL</strong>-Tipps, die auch bei anderen Datenbanken anwendbar<br />

s<strong>in</strong>d.<br />

• DB2 (IBM):<br />

• DB2 18 (Wikipedia-Artikel)<br />

• Dokumentation zu DB2 V9 LUW 19<br />

• Startseite von IBM zu DB2 z/OS 20 (englisch)<br />

• Firebird:<br />

• Firebird 21<br />

• Über Firebird Documentation Index 22 (englisch) s<strong>in</strong>d viele E<strong>in</strong>zelteile zu f<strong>in</strong>den<br />

− teils als HTML-Seiten, teils als PDF-Dateien.<br />

• Firebird 2.0 P<strong>SQL</strong> Reference Manual 23 (englisch) ist e<strong>in</strong>e Ergänzung für die<br />

Programmierung mit <strong>SQL</strong> 24 .<br />

• Informix 25 (Wikipedia-Artikel)<br />

• Interbase 26 (Wikipedia-Artikel)<br />

• Microsoft <strong>SQL</strong> Server:<br />

12 http://dmoz.<strong>org</strong>/World/Deutsch/Computer/Programmieren/Sprachen/<strong>SQL</strong>/<br />

13 http://de.wikipedia.<strong>org</strong>/wiki/Open%20Directory%20Project<br />

14 http://www.sql-referenz.de<br />

15 http://www.mcjones.<strong>org</strong>/System_R/<strong>SQL</strong>_Reunion_95/<br />

16 http://edb.gm.fh-koeln.de/sqltra<strong>in</strong>er/start.jsp?action=wl/<br />

17 http://mysite.verizon.net/Graeme_Birchall/id1.html<br />

18 http://de.wikipedia.<strong>org</strong>/wiki/DB2<br />

19 http://www-306.ibm.com/software/data/db2/udb/support/manualsNLVv9.html#de_ma<strong>in</strong><br />

20 http://www-306.ibm.com/software/data/db2/zos/<br />

21 http://de.wikipedia.<strong>org</strong>/wiki/Firebird%20(Datenbank)<br />

22 http://www.firebirdsql.<strong>org</strong>/<strong>in</strong>dex.php?op=doc<br />

23 http://www.janus-software.com/fbmanual/<strong>in</strong>dex.php?book=psql<br />

24 Kapitel 30 auf Seite 323<br />

25 http://de.wikipedia.<strong>org</strong>/wiki/Informix<br />

26 http://de.wikipedia.<strong>org</strong>/wiki/Interbase<br />

447


Weitere Informationen<br />

• Microsoft <strong>SQL</strong> Server 27 (Wikipedia-Artikel)<br />

• <strong>SQL</strong> Server 2008: Technische Referenz 28 , vor allem im Bereich Transact-<strong>SQL</strong>-<br />

Sprachreferenz<br />

• <strong>SQL</strong> Server 2005: Transact-<strong>SQL</strong>-Referenz 29<br />

• My<strong>SQL</strong>:<br />

• My<strong>SQL</strong> 30 (Wikipedia-Artikel)<br />

• My<strong>SQL</strong> Documentation 31 (englisch)<br />

• My<strong>SQL</strong> 5.1 Referenzhandbuch 32<br />

• Reference Manual 5.5 33 (englisch) für die neueste Version<br />

• Oracle:<br />

• Oracle 34 (Wikipedia-Artikel)<br />

• Oracle Documentation 35 (englisch) als Überblick, speziell beispielsweise:<br />

• Database Reference 11g Release 2 (11.2) 36<br />

• Database Reference 10g Release 2 (10.2) 37<br />

• Postgre<strong>SQL</strong>:<br />

• Postgre<strong>SQL</strong> 38 (Wikipedia-Artikel)<br />

• Dokumentation 39<br />

• <strong>SQL</strong>ite:<br />

• <strong>SQL</strong>ite 40 (Wikipedia-Artikel)<br />

• Dokumentation 41 (englisch)<br />

• Sybase 42 (Wikipedia-Artikel)<br />

27 http://de.wikipedia.<strong>org</strong>/wiki/Microsoft%20<strong>SQL</strong>%20Server<br />

28 http://msdn.microsoft.com/de-de/library/bb500275.aspx<br />

29 http://msdn.microsoft.com/de-de/library/ms189826%28<strong>SQL</strong>.90%29.aspx<br />

30 http://de.wikipedia.<strong>org</strong>/wiki/My<strong>SQL</strong><br />

31 http://dev.mysql.com/doc/<br />

32 http://dev.mysql.com/doc/refman/5.1/de/<br />

33 http://dev.mysql.com/doc/refman/5.5/en/<br />

34 http://de.wikipedia.<strong>org</strong>/wiki/Oracle%20(Datenbanksystem)<br />

35 http://www.oracle.com/technology/documentation/<strong>in</strong>dex.html<br />

36 http://download.oracle.com/docs/cd/E11882_01/server.112/e17110/toc.htm<br />

37 http://download.oracle.com/docs/cd/B19306_01/server.102/b14237/toc.htm<br />

38 http://de.wikipedia.<strong>org</strong>/wiki/Postgre<strong>SQL</strong><br />

39 http://www.postgresql.<strong>org</strong>/docs/books/pghandbuch.html.de<br />

40 http://de.wikipedia.<strong>org</strong>/wiki/<strong>SQL</strong>ite<br />

41 http://www.sqlite.<strong>org</strong>/docs.html<br />

42 http://de.wikipedia.<strong>org</strong>/wiki/Sybase<br />

448


D.2.5. Andere Datenverwaltung<br />

Über Wikipedia gibt es e<strong>in</strong>führende Erläuterungen und Verweise:<br />

• dBASE 43<br />

• LibreOffice 44 als Nachfolger von OpenOffice 45<br />

• MS Access 46<br />

• Paradox 47<br />

43 http://de.wikipedia.<strong>org</strong>/wiki/dBASE<br />

44 http://de.wikipedia.<strong>org</strong>/wiki/LibreOffice<br />

45 http://de.wikipedia.<strong>org</strong>/wiki/OpenOffice.<strong>org</strong><br />

46 http://de.wikipedia.<strong>org</strong>/wiki/Microsoft%20Access<br />

47 http://de.wikipedia.<strong>org</strong>/wiki/Paradox%20(Datenbank)<br />

Webl<strong>in</strong>ks<br />

449


E. Zu diesem Buch<br />

E.1. H<strong>in</strong>weise zu den Lizenzen<br />

Dieses Werk ist entstanden bei Wikibooks 1 , e<strong>in</strong>er Onl<strong>in</strong>e-Bibliothek im Internet<br />

mit Lehr-, Sach- und Fachbüchern. Jeder kann und darf diese Bücher frei nutzen<br />

und bearbeiten. Alle Inhalte stehen unter den Lizenzen „Creative Commons<br />

Attribution/Share-Alike“ (CC-BY-SA 3.0) und GNU-Lizenz für freie Dokumentation<br />

(GFDL).<br />

Für die Konvertierung wb2pdf 2 gilt die GNU General Public License (GPL).<br />

Zum Textsatzprogramm LAT E X 3 gehört die LaTeX Project Public License (LPPL).<br />

H<strong>in</strong>weise zur Nutzung und für Zitate s<strong>in</strong>d zu f<strong>in</strong>den unter:<br />

• Orig<strong>in</strong>alversion der Lizenz CC-BY-SA 3.0<br />

http://creativecommons.<strong>org</strong>/licenses/by-sa/3.0<br />

• Deutsche Version der Lizenz mit Ergänzungen<br />

http://creativecommons.<strong>org</strong>/licenses/by-sa/3.0/deed.de<br />

• Orig<strong>in</strong>alversion der Lizenz GFDL<br />

http://www.gnu.<strong>org</strong>/copyleft/fdl.html<br />

• Orig<strong>in</strong>alversion der Lizenz GPL<br />

http://www.gnu.<strong>org</strong>/licenses/gpl-3.0.html<br />

• Version der LaTeX PPL<br />

http://www.opensource.<strong>org</strong>/licenses/lppl<br />

• Nutzungsbed<strong>in</strong>gungen der Wikimedia Foundation (deutsch)<br />

http://<strong>wikimedia</strong>foundation.<strong>org</strong>/wiki/Nutzungsbed<strong>in</strong>gungen<br />

• Zitieren aus Wikibooks<br />

http://de.wikibooks.<strong>org</strong>/wiki/Hilfe:Zitieren#Zitieren_aus_Wikibooks<br />

Aus dem gedruckten Buch kann natürlich so zitiert werden, wie es bei Büchern<br />

üblich ist.<br />

1 http://de.wikibooks.<strong>org</strong>/wiki/E<strong>in</strong>f%C3%BChrung_<strong>in</strong>_<strong>SQL</strong><br />

2 http://de.wikibooks.<strong>org</strong>/wiki/Benutzer:Dirk_Huenniger/wb2pdf<br />

3 http://de.wikipedia.<strong>org</strong>/wiki/LaTeX<br />

451


Zu diesem Buch<br />

E.2. Autoren<br />

Die folgende Aufstellung enthält gemäß Wikibooks-Lizenzbed<strong>in</strong>gungen 4 alle Autoren,<br />

die an diesem Buch mitgearbeitet haben, und zwar mit Stand vom 22. Juli<br />

2012. (Autoren, die sich nicht angemeldet haben, müssen nach den Lizenzbed<strong>in</strong>gungen<br />

nicht genannt werden.)<br />

Edits User<br />

4 AK-Palme 5<br />

6 Andorna 6<br />

1 Blauerflummi 7<br />

1 BreKla 8<br />

1 Burnstone 9<br />

1 Bullet4myval 10<br />

1 C.hahn 11<br />

1 CarsracBot 12<br />

2 Complex 13<br />

1 Computator 14<br />

2 Daniel B 15<br />

1 Diba 16<br />

13 Dirk Huenniger 17<br />

1 Dr. Gert Blazejewski 18<br />

2 Ezhik 19<br />

2 Felix Re<strong>in</strong>wald 20<br />

2 Gandi 21<br />

1 Jpp 22<br />

4 http://de.wikibooks.<strong>org</strong>/wiki/Wikibooks:Creative_Commons_Attribution-ShareAlike_3.<br />

0_Unported_License<br />

5 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:AK-Palme<br />

6 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Andorna<br />

7 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Blauerflummi<br />

8 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:BreKla<br />

9 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Burnstone<br />

10 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Bullet4myval<br />

11 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:C.hahn<br />

12 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:CarsracBot<br />

13 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Complex<br />

14 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Computator<br />

15 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Daniel_B<br />

16 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Diba<br />

17 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Dirk_Huenniger<br />

18 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Dr._Gert_Blazejewski<br />

19 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Ezhik<br />

20 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Felix_Re<strong>in</strong>wald<br />

21 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Gandi<br />

22 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Jpp<br />

452


1181 Juetho 23<br />

46 Julius-m 24<br />

4 Klaus Eifert 25<br />

1 Knospe 26<br />

2 Mart<strong>in</strong> Fuchs 27<br />

2 MatthiasKabel 28<br />

67 Mcflash 29<br />

7 Mjchael 30<br />

1 Nicky knows 31<br />

2 Phoenix9999 32<br />

1 Polluks 33<br />

1 Sallynase 34<br />

1 Shogun 35<br />

5 Skittle 36<br />

7 Softeis 37<br />

2 Sparti 38<br />

2 Stefan Majewsky 39<br />

1 Sundance Raphael 40<br />

1 ThE cRaCkEr 41<br />

9 ThePacker 42<br />

2 Tirkon 43<br />

16 Turelion 44<br />

2 VÖRBY 45<br />

23 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Juetho<br />

24 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Julius-m<br />

25 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Klaus_Eifert<br />

26 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Knospe<br />

27 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Mart<strong>in</strong>_Fuchs<br />

28 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:MatthiasKabel<br />

29 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Mcflash<br />

30 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Mjchael<br />

31 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Nicky_knows<br />

32 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Phoenix9999<br />

33 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Polluks<br />

34 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Sallynase<br />

35 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Shogun<br />

36 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Skittle<br />

37 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Softeis<br />

38 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Sparti<br />

39 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Stefan_Majewsky<br />

40 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Sundance_Raphael<br />

41 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:ThE_cRaCkEr<br />

42 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:ThePacker<br />

43 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Tirkon<br />

44 http://de.wikibooks.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:Turelion<br />

45 http://de.wikipedia.<strong>org</strong>/w/<strong>in</strong>dex.php?title=Benutzer:VÖRBY<br />

Autoren<br />

453


Zu diesem Buch<br />

E.3. Bildnachweis<br />

In der nachfolgenden Übersicht s<strong>in</strong>d alle Bilder mit ihren Autoren und Lizenzen<br />

aufgelistet.<br />

Umschlag<br />

Lizenz: cc-by-sa-3.0 – Bearbeiter: User:Juetho<br />

Quelle: http://de.wikibooks.<strong>org</strong>/wiki/File:<strong>SQL</strong>-Titelbild.png<br />

Seite 42<br />

Lizenz: cc-by-sa-3.0 – Bearbeiter: User:Juetho<br />

Quelle: http://de.wikibooks.<strong>org</strong>/wiki/File:<strong>SQL</strong>-Struktur%20Beispieldaten<br />

bank.png<br />

Seite 203<br />

Lizenz: PD – Bearbeiter: User:Lipedia<br />

Quellen:<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1010.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1110.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1100.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1011.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1101.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1000.svg<br />

http://commons.<strong>wikimedia</strong>.<strong>org</strong>/wiki/File:Relation1001.svg<br />

Für die Namen der Lizenzen wurden folgende Abkürzungen verwendet:<br />

• CC-BY-SA-3.0: Commons Attribution ShareAlike 3.0 License. 46<br />

• PD: This image is <strong>in</strong> the public doma<strong>in</strong>, because it consists entirely of <strong>in</strong>formation<br />

that is common property and conta<strong>in</strong>s no orig<strong>in</strong>al authorship.<br />

Diese Abbildung ist geme<strong>in</strong>frei, weil sie nur Allgeme<strong>in</strong>gut enthält und die nötige<br />

Schöpfungshöhe nicht erreicht.<br />

46 Siehe dazu den H<strong>in</strong>weis auf Seite 451<br />

454

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!