Relationale Datenbanken und Standard-SQL 3827326567, 9783827326560 [PDF]


159 6 3MB

German Pages 434 Year 2008

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Relationale Datenbanken und Standard-SQL - Konzepte der Entwicklung und Anwendung......Page 1
2 Einführung in das relationale Datenmodell......Page 6
3 Datenbankentwurf......Page 7
4 Datendefinition in SQL......Page 8
5 Datenmanipulation in SQL......Page 9
9 Objektorientierung und SQL......Page 10
Stichwortverzeichnis......Page 11
Vorwort zur vierten Auflage......Page 12
Vorwort zur dritten Auflage......Page 14
Vorwort zur zweiten Auflage......Page 16
Vorwort zur ersten Auflage......Page 18
Hinweise zur CD......Page 20
1 Datenbanken und Datenbanksysteme......Page 22
1.1 Zentralisierung der Datenhaltung......Page 23
1.2 Die Datenbankarchitektur nach ANSI- SPARC......Page 24
1.3 Integritätsregeln......Page 27
1.4 Architektur von Datenbanksystemen......Page 29
2 Einführung in das relationale Datenmodell......Page 32
2.1.1 Attribute......Page 33
2.1.2 Domänen......Page 34
2.1.3 Tupel......Page 37
2.1.4 Relationenschema und Relationen......Page 38
2.1.5 Datenbankschema und Datenbank......Page 41
2.2.1 Primärschlüssel und Entitätsintegrität......Page 42
2.2.2 Fremdschlüssel und referenzielle Integrität......Page 43
2.2.3 Übungsaufgaben......Page 44
2.4 Relationenalgebra......Page 45
2.4.1 Die Auswahl (Selektion)......Page 47
2.4.2 Die Projektion......Page 50
2.4.3 Umbenennung von Attribut-Bezeichnern......Page 52
2.4.4 Adjunktion virtueller Spalten......Page 55
2.4.5 Gruppierung von Daten......Page 56
2.4.6 Der Verbund (Join)......Page 60
2.4.7 Das Kreuzprodukt......Page 68
2.4.8 Der äußere Verbund (Outer Join)......Page 71
2.4.9 Mengenoperationen......Page 74
2.4.10 Übungsaufgaben......Page 75
2.5.1 Die Behandlung von fehlenden Attributwerten......Page 76
2.5.2 Verschiedene Arten von Nullmarken......Page 77
2.5.3 Dreiwertige Logik......Page 82
2.5.4 Übungsaufgaben......Page 83
2.6.1 Multimengen und Tabellen......Page 84
2.6.7 Gruppierung von Daten......Page 85
2.6.10 Multimengen-Operationen......Page 86
2.6.11 Anwendung der Tabellenoperationen auf Relationen......Page 87
2.7 Indizes in relationalen Datenbanken......Page 88
2.7.1 Übungsaufgaben......Page 90
3.1 Anwendungsentwicklung, Prozess- und Datenmodell......Page 92
3.2.1 Entität......Page 97
3.2.3 Entitätentypen und Entitätenmengen......Page 98
3.2.6 Beziehungstyp (Relationship Type)......Page 99
3.2.7 Geschäftsregeln......Page 100
3.2.8 Schlüssel......Page 101
3.2.9 ER-Diagramme......Page 103
3.2.10 Kardinalitäten von zweistelligen Beziehungstypen......Page 104
3.2.12 Fallbeispiel: Versandhandel als ER-Modell......Page 108
3.2.13 Umsetzung eines ER-Modells in ein relationales Datenmodell......Page 110
3.2.14 Übungsaufgaben......Page 117
3.3 Elemente der UML......Page 118
3.3.1 ER-Diagramme und UML-Klassendiagramme......Page 119
3.3.2 Aggregation und Komposition in UML......Page 120
3.3.3 Das Vererbungskonzept......Page 122
3.3.4 Übungsaufgaben......Page 128
3.4 Normalformen in relationalen Datenbanken......Page 129
3.4.1 Funktionale Abhängigkeiten......Page 130
3.4.2 Schlüssel......Page 134
3.4.3 Normalformen, vereinfachte Version......Page 136
3.4.4 Beispiel für eine Normalisierung......Page 140
3.4.5 Normalformen, exakte Version......Page 149
3.4.6 Übungsaufgaben......Page 150
3.4.7 Die Boyce-Codd-Normalform (BCNF)......Page 151
3.4.8 Arten von funktionalen Abhängigkeiten......Page 153
3.4.9 Bewusste Abweichungen von Normalformen......Page 154
3.5 Qualitätskriterien für den Datenbankentwurf......Page 155
3.5.3 Physikalisches Schema für die Zieldatenbank......Page 156
3.5.5 Namenskonventionen......Page 157
3.6.2 Datenübernahme......Page 158
3.6.4 Datensicherungskonzept......Page 159
3.6.6 Historienführung......Page 160
3.6.7 Auswahl eines Datenbank- Verwaltungssystems......Page 161
4.1.1 Ein kleiner Überblick über die Historie von SQL......Page 162
4.1.2 Elemente von SQL......Page 164
4.2 Schemadefinition......Page 166
4.3 DDL: Datendefinition mit SQL......Page 167
4.3.1 Datentypen und Domänen......Page 168
4.3.2 Datentypen in SQL......Page 169
4.3.3 Operationen mit Datentypen......Page 172
4.3.4 Repräsentation der Datentypen......Page 174
4.3.5 CREATE TABLE......Page 175
4.3.6 Globale temporäre Tabellen......Page 187
4.3.7 Ändern der Tabellendefinition......Page 189
4.3.9 CREATE INDEX......Page 190
4.3.10 Übungsaufgaben......Page 192
5 Datenmanipulation in SQL......Page 194
5.1.1 Dateneingabe......Page 196
5.1.2 Daten ansehen......Page 198
5.1.3 Daten ändern......Page 199
5.1.4 Daten löschen......Page 201
5.2.1 SELECT-Anweisung und Abfrage-Anweisung......Page 202
5.2.2 SELECT-Anweisung mit Projektion......Page 203
5.2.3 Sortieren der Ergebnistabelle......Page 205
5.2.4 SELECT mit Projektion und Selektion......Page 206
5.2.5 SELECT mit BETWEEN, IN, LIKE, SIMILAR......Page 215
5.2.6 SELECT mit virtuellen Spalten......Page 219
5.2.7 Funktionen......Page 221
5.2.8 SELECT mit Gruppenbildung......Page 231
5.2.9 SELECT mit GROUP BY......Page 235
5.2.10 SELECT mit GROUP BY und HAVING......Page 240
5.2.11 Erweiterte Gruppierungsmöglichkeiten in SQL/OLAP......Page 242
5.2.12 Reihenfolge der Komponenten der SELECT-Anweisung......Page 249
5.3 Datenabfrage mit mehreren Relationen......Page 250
5.3.2 Der innere Verbund......Page 251
5.3.3 Verbindung einer Tabelle mit sich selbst......Page 257
5.3.4 Outer Join......Page 258
5.4.1 Unterabfragen, die einen Wert liefern......Page 261
5.4.2 Unterabfragen, die eine Menge liefern......Page 265
5.4.3 EXISTS......Page 270
5.4.4 Korrelierte Unterabfrage......Page 271
5.4.5 EXISTS versus IN......Page 272
5.4.6 Unterabfragen, die eine Tabelle liefern......Page 273
5.4.7 Benannte Unterabfragen......Page 276
5.5 Mengenoperationen......Page 277
5.6 Verarbeitung einer Abfrage......Page 280
5.7.1 Datenübertragung mit INSERT-Anweisung......Page 282
5.7.2 DIE MERGE-Anweisung......Page 284
6 Datensichten in SQL......Page 286
6.1 Verhalten von Datensichten bei Abfragen......Page 287
6.2.2 Nutzen von virtuellen Datensichten......Page 289
6.2.3 Datensichten mit virtuellen Spalten......Page 290
6.2.4 Datenmanipulationen an virtuellen Datensichten......Page 291
6.2.5 Änderbarkeit von Daten über Datensichten......Page 293
6.3 Snapshots......Page 295
6.4 Materialisierte Datensichten......Page 296
7.1 Transaktionen in Datenbanken......Page 298
7.1.1 Eigenschaften von Transaktionen......Page 299
7.1.2 Transaktionen in SQL......Page 301
7.1.3 Parallele Ausführung von Transaktionen......Page 304
7.1.4 Sperrmechanismen......Page 306
7.1.5 Isolation-Level in Transaktionen......Page 309
7.1.6 Explizite Sperrung mit LOCK TABLE......Page 311
7.1.7 Deadlock......Page 312
7.2.1 Allgemeine Zugangsprivilegien......Page 313
7.2.2 Objektbezogene Privilegien......Page 314
7.2.3 Rücknahme von Privilegien......Page 316
7.2.4 Rollen und Gruppen......Page 317
7.3.1 Datenbankprozeduren......Page 318
7.3.2 Funktionen in Datenbanken......Page 325
7.3.3 Trigger in Datenbanken......Page 329
7.3.4 Das Cursor-Konzept......Page 332
7.4 Strategien zur Konsistenzsicherung......Page 336
7.4.1 Statische Geschäftsregeln......Page 337
7.4.2 Dynamische Geschäftsregeln......Page 343
8.1 Der Systemkatalog im SQL-Standard......Page 346
8.2 Systemtabellen in SQL-Implementationen......Page 348
Objektorientierung und SQL......Page 356
9.1.1 Grundbausteine......Page 358
9.1.2 Typen......Page 359
9.1.3 Vererbung......Page 361
9.1.4 Verhaltensvererbung und Polymorphismus......Page 363
9.1.5 Klassen und Extents......Page 364
9.1.8 Lebenszeit von Objekten......Page 366
9.1.9 Beziehungen......Page 367
9.2.1 Definition von Typen und Tabellen......Page 368
9.2.2 Konstruktoren für komplexe Typen......Page 370
9.2.3 Referenzen......Page 371
9.3 Objektrelationales Modell der Versand- Datenbank......Page 372
9.3.1 Abstrakte Datentypen als Wertebereich für Attribute......Page 375
9.3.2 Komplexe Objekttypen und Objekttabellen......Page 377
9.3.3 Vererbung......Page 382
9.3.4 Referenzen......Page 384
9.3.5 Abfragen......Page 386
9.3.6 Eingebettete Objekttypen......Page 388
9.3.7 Schreiboperationen in Objekttabellen......Page 393
9.3.8 Object Views......Page 394
9.3.9 Fazit......Page 396
Kapitel 2......Page 398
Kapitel 3......Page 400
Kapitel 4......Page 405
Kapitel 5......Page 407
Kapitel 8......Page 415
B Syntaxnotation......Page 416
C.1 Definition der Domänen und Tabellen......Page 418
C.2 Tabellarische Darstellung der Relationen......Page 421
D Literaturverzeichnis......Page 424
D......Page 429
I......Page 430
O......Page 431
S......Page 432
Z......Page 433
Ins Internet: Weitere Infos zum Buch, Downloads, etc.......Page 0
© Copyright......Page 434
Papiere empfehlen

Relationale Datenbanken und Standard-SQL
 3827326567, 9783827326560 [PDF]

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

Relationale Datenbanken und Standard-SQL

Günter Matthiessen Michael Unterstein

Relationale Datenbanken und Standard-SQL Konzepte der Entwicklung und Anwendung

An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam

Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Abbildungen und Texten wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen und weitere Stichworte und sonstige Angaben, die in diesem Buch verwendet werden, sind als eingetragene Marken geschützt. Da es nicht möglich ist, in allen Fällen zeitnah zu ermitteln, ob ein Markenschutz besteht, wird das ® Symbol in diesem Buch nicht verwendet. Umwelthinweis: Dieses Produkt wurde auf chlorfrei gebleichtem Papier gedruckt. Um Rohstoffe zu sparen, haben wir auf Folienverpackung verzichtet.

5

4

3

2

1

11 10 09 08 ISBN 978-3-8273-2656-0

© 2008 by Addison-Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Marco Lindenbeck, webwo GmbH, [email protected] Lektorat: Sylvia Hasselbach, [email protected] Korrektorat: Sandra Gottmann, [email protected] Herstellung: Philipp Burkart, [email protected] Satz: Reemers Publishing Services Gmbh, Krefeld, www.reemers.de Druck und Verarbeitung: Bercker, Kevelaer Printed in Germany

Inhaltsverzeichnis Vorwort zur vierten Auflage

11

Vorwort zur dritten Auflage

13

Vorwort zur zweiten Auflage

15

Vorwort zur ersten Auflage

17

Hinweise zur CD

19

1 Datenbanken und Datenbanksysteme 1.1 Zentralisierung der Datenhaltung 1.2 Die Datenbankarchitektur nach ANSI-SPARC 1.3 Integritätsregeln 1.4 Architektur von Datenbanksystemen

21 22 23 26 28

2 Einführung in das relationale Datenmodell 2.1 Aufbau relationaler Datenbanken 2.1.1 Attribute 2.1.2 Domänen 2.1.3 Tupel 2.1.4 Relationenschema und Relationen 2.1.5 Datenbankschema und Datenbank 2.2 Schlüssel, Primärschlüssel und Fremdschlüssel 2.2.1 Primärschlüssel und Entitätsintegrität 2.2.2 Fremdschlüssel und referenzielle Integrität 2.2.3 Übungsaufgaben 2.3 Einführung in die Fallstudie (Versandhandel) 2.4 Relationenalgebra 2.4.1 Die Auswahl (Selektion) 2.4.2 Die Projektion 2.4.3 Umbenennung von Attribut-Bezeichnern 2.4.4 Adjunktion virtueller Spalten 2.4.5 Gruppierung von Daten 2.4.6 Der Verbund (Join) 2.4.7 Das Kreuzprodukt 2.4.8 Der äußere Verbund (Outer Join) 2.4.9 Mengenoperationen 2.4.10 Übungsaufgaben

31 32 32 33 36 37 40 41 41 42 43 44 44 46 49 51 54 55 59 67 70 73 74

5

Inhaltsverzeichnis

2.5

2.6

2.7

Nullmarken in Datenbanken 2.5.1 Die Behandlung von fehlenden Attributwerten 2.5.2 Verschiedene Arten von Nullmarken 2.5.3 Dreiwertige Logik 2.5.4 Übungsaufgaben Tabellen und Relationen 2.6.1 Multimengen und Tabellen 2.6.2 Der Distinct-Operator 2.6.3 Die Auswahl (Selektion) 2.6.4 Die Projektion 2.6.5 Umbenennung von Attribut-Bezeichnern 2.6.6 Adjunktion virtueller Spalten 2.6.7 Gruppierung von Daten 2.6.8 Der natürliche Verbund und der äußere Verbund 2.6.9 Das Kreuzprodukt 2.6.10 Multimengen-Operationen 2.6.11 Anwendung der Tabellenoperationen auf Relationen 2.6.12 Übungsaufgaben Indizes in relationalen Datenbanken 2.7.1 Übungsaufgaben

3 Datenbankentwurf 3.1 Anwendungsentwicklung, Prozess- und Datenmodell 3.2 Grundbegriffe des Entity-Relationship-Modells 3.2.1 Entität 3.2.2 Abhängige Entität 3.2.3 Entitätentypen und Entitätenmengen 3.2.4 Attribut 3.2.5 Beziehung 3.2.6 Beziehungstyp (Relationship Type) 3.2.7 Geschäftsregeln 3.2.8 Schlüssel 3.2.9 ER-Diagramme 3.2.10 Kardinalitäten von zweistelligen Beziehungstypen 3.2.11 Domänen 3.2.12 Fallbeispiel: Versandhandel als ER-Modell 3.2.13 Umsetzung eines ER-Modells in ein relationales Datenmodell 3.2.14 Übungsaufgaben 3.3 Elemente der UML 3.3.1 ER-Diagramme und UML-Klassendiagramme 3.3.2 Aggregation und Komposition in UML 3.3.3 Das Vererbungskonzept 3.3.4 Übungsaufgaben

6

75 75 76 81 82 83 83 84 84 84 84 84 84 85 85 85 86 87 87 89 91 91 96 96 97 97 98 98 98 99 100 102 103 107 107 109 116 117 118 119 121 127

Inhaltsverzeichnis

3.4

Normalformen in relationalen Datenbanken 3.4.1 Funktionale Abhängigkeiten 3.4.2 Schlüssel 3.4.3 Normalformen, vereinfachte Version 3.4.4 Beispiel für eine Normalisierung 3.4.5 Normalformen, exakte Version 3.4.6 Übungsaufgaben 3.4.7 Die Boyce-Codd-Normalform (BCNF) 3.4.8 Arten von funktionalen Abhängigkeiten 3.4.9 Bewusste Abweichungen von Normalformen Qualitätskriterien für den Datenbankentwurf 3.5.1 Konzeptuelles Modell 3.5.2 Logisches Datenmodell als Relationenschema 3.5.3 Physikalisches Schema für die Zieldatenbank 3.5.4 Normalformen 3.5.5 Namenskonventionen Weitere Aufgaben bei der Einführung einer Datenbank 3.6.1 Bestimmung eines Mengengerüsts für die Datenbank 3.6.2 Datenübernahme 3.6.3 Datenschutzkonzept 3.6.4 Datensicherungskonzept 3.6.5 Verteilung der Daten und Verteilung der Verarbeitung 3.6.6 Historienführung 3.6.7 Auswahl eines Datenbank-Verwaltungssystems

128 129 133 135 139 148 149 150 152 153 154 155 155 155 156 156 157

4 Datendefinition in SQL 4.1 SQL und SQL-Standard 4.1.1 Ein kleiner Überblick über die Historie von SQL 4.1.2 Elemente von SQL 4.2 Schemadefinition 4.3 DDL: Datendefinition mit SQL 4.3.1 Datentypen und Domänen 4.3.2 Datentypen in SQL 4.3.3 Operationen mit Datentypen 4.3.4 Repräsentation der Datentypen 4.3.5 CREATE TABLE 4.3.6 Globale temporäre Tabellen 4.3.7 Ändern der Tabellendefinition 4.3.8 Entfernung von Datenbankobjekten 4.3.9 CREATE INDEX 4.3.10 Übungsaufgaben

161 161 161 163 165 166 167 168 171 173 174 186 188 189 189 191

3.5

3.6

157 157 158 158 159 159 160

7

Inhaltsverzeichnis

5 Datenmanipulation in SQL 5.1 Datenmanipulationen an einer Relation 5.1.1 Dateneingabe 5.1.2 Daten ansehen 5.1.3 Daten ändern 5.1.4 Daten löschen 5.2 SELECT-Anweisung mit einer Tabelle 5.2.1 SELECT-Anweisung und Abfrage-Anweisung 5.2.2 SELECT-Anweisung mit Projektion 5.2.3 Sortieren der Ergebnistabelle 5.2.4 SELECT mit Projektion und Selektion 5.2.5 SELECT mit BETWEEN, IN, LIKE, SIMILAR 5.2.6 SELECT mit virtuellen Spalten 5.2.7 Funktionen 5.2.8 SELECT mit Gruppenbildung 5.2.9 SELECT mit GROUP BY 5.2.10 SELECT mit GROUP BY und HAVING 5.2.11 Erweiterte Gruppierungsmöglichkeiten in SQL/OLAP 5.2.12 Reihenfolge der Komponenten der SELECTAnweisung 5.3 Datenabfrage mit mehreren Relationen 5.3.1 Tabellen und Tabellenalias 5.3.2 Der innere Verbund 5.3.3 Verbindung einer Tabelle mit sich selbst 5.3.4 Outer Join 5.4 Unterabfragen 5.4.1 Unterabfragen, die einen Wert liefern 5.4.2 Unterabfragen, die eine Menge liefern 5.4.3 EXISTS 5.4.4 Korrelierte Unterabfrage 5.4.5 EXISTS versus IN 5.4.6 Unterabfragen, die eine Tabelle liefern 5.4.7 Benannte Unterabfragen 5.5 Mengenoperationen 5.6 Verarbeitung einer Abfrage 5.7 Datenänderungen mit mehreren Tabellen 5.7.1 Datenübertragung mit INSERT-Anweisung 5.7.2 DIE MERGE-Anweisung

8

193 195 195 197 198 200 201 201 202 204 205 214 218 220 230 234 239 241 248 249 250 250 256 257 260 260 264 269 270 271 272 275 276 279 281 281 283

Inhaltsverzeichnis

6 Datensichten in SQL 6.1 Verhalten von Datensichten bei Abfragen 6.2 Virtuelle Datensicht 6.2.1 CREATE VIEW 6.2.2 Nutzen von virtuellen Datensichten 6.2.3 Datensichten mit virtuellen Spalten 6.2.4 Datenmanipulationen an virtuellen Datensichten 6.2.5 Änderbarkeit von Daten über Datensichten 6.3 Snapshots 6.4 Materialisierte Datensichten

285 286 288 288 288 289 290 292 294 295

7 Konsistenz und Mehrbenutzerbetrieb 7.1 Transaktionen in Datenbanken 7.1.1 Eigenschaften von Transaktionen 7.1.2 Transaktionen in SQL 7.1.3 Parallele Ausführung von Transaktionen 7.1.4 Sperrmechanismen 7.1.5 Isolation-Level in Transaktionen 7.1.6 Explizite Sperrung mit LOCK TABLE 7.1.7 Deadlock 7.2 Zugriffsrechte und Rollen 7.2.1 Allgemeine Zugangsprivilegien 7.2.2 Objektbezogene Privilegien 7.2.3 Rücknahme von Privilegien 7.2.4 Rollen und Gruppen 7.3 Prozedurale Konzepte in SQL 7.3.1 Datenbankprozeduren 7.3.2 Funktionen in Datenbanken 7.3.3 Trigger in Datenbanken 7.3.4 Das Cursor-Konzept 7.4 Strategien zur Konsistenzsicherung 7.4.1 Statische Geschäftsregeln 7.4.2 Dynamische Geschäftsregeln

297 297 298 300 303 305 308 310 311 312 312 313 315 316 317 317 324 328 331 335 336 342

8 Der Systemkatalog 8.1 Der Systemkatalog im SQL-Standard 8.2 Systemtabellen in SQL-Implementationen

345 345 347

9 Objektorientierung und SQL 9.1 Das objektorientierte Datenbankmodell 9.1.1 Grundbausteine 9.1.2 Typen 9.1.3 Vererbung 9.1.4 Verhaltensvererbung und Polymorphismus 9.1.5 Klassen und Extents

355 357 357 358 360 362 363

9

Inhaltsverzeichnis

9.2

9.3

9.1.6 Objektidentität und Gleichheit 9.1.7 Kapselung 9.1.8 Lebenszeit von Objekten 9.1.9 Beziehungen Objektorientierung im SQL-Standard 9.2.1 Definition von Typen und Tabellen 9.2.2 Konstruktoren für komplexe Typen 9.2.3 Referenzen Objektrelationales Modell der Versand-Datenbank 9.3.1 Abstrakte Datentypen als Wertebereich für Attribute 9.3.2 Komplexe Objekttypen und Objekttabellen 9.3.3 Vererbung 9.3.4 Referenzen 9.3.5 Abfragen 9.3.6 Eingebettete Objekttypen 9.3.7 Schreiboperationen in Objekttabellen 9.3.8 Object Views 9.3.9 Fazit

365 365 365 366 367 367 369 370 371 374 376 381 383 385 387 392 393 395

A Ausgewählte Lösungen zu den Aufgaben

397

B Syntaxnotation

415

C Beispieldatenbank

417

D Literaturverzeichnis

423

Stichwortverzeichnis

428

10

Vorwort zur vierten Auflage Die SQL-Norm hat sich seit der letzten Auflage nicht verändert, der aktuelle Stand ist zum Zeitpunkt der Manuskripterstellung SQL:2003. Bei der vorigen Auflage lag uns diese Version bereits als Entwurf vor, sodass wir schon die aktuellsten Änderungen verarbeiten konnten. Einige Erweiterungen haben wir dennoch vorgenommen. So haben wir die im Teil SQL/OLAP spezifizierten erweiterten Gruppenauswertungen nun mit aufgenommen, da die Anwendungen der GROUP BYKlausel bisher recht restringiert waren und sich mit einigen neuen Konstrukten auf komfortable Weise aggregierte Daten erzeugen lassen. Auch haben wir die MERGE-Anweisung, eine Kombination von INSERT und UPDATE, neu aufgenommen. Die restlichen zahlreichen Änderungen im Buch verdanken sich den Erfahrungen, die wir in der Lehre damit gemacht haben, und dem immer währenden Bemühen, größtmögliche Anschauung ohne Verlust an Exaktheit zu präsentieren. Beispielsweise haben wir die Darstellung der Relationenalgebra in Kapitel 2 um Hierarchiediagramme erweitert und machen dort auch einige Aussagen zur Abfrageoptimierung. Die Anzahl der frei verfügbaren relationalen Datenbank-Managementsysteme ist seit der letzten Auflage erheblich gewachsen. Wir haben daher auf der dem Buch beiliegenden CD die Beispieldatenbank sowie die Aufgaben und Lösungen, die ansonsten im Anhang abgedruckt sind, für verschiedene DBMS getestet und in speziellen Verzeichnissen abgelegt. Dazu gehören Sybase Adaptive Server Anywhere, Oracle, MySQL, Firebird, MS-SQL Server, HSQLDB, PostgreSQL. Das DBMS von Sybase liegt in der Developer Edition – ohne Zeitlimit – auf der CD bei. Unsere Leserinnen und Leser haben es damit etwas leichter, die Ausführungen dieses Buchs mit einem Datenbank-Managementsystem ihrer Wahl nachzuvollziehen. Die Darstellung im Text bezieht sich nach wie vor auf die SQL-Norm, sofern nicht anderes dazu gesagt wird. Eine bedeutende Rolle bei den Änderungen und Korrekturen haben Anregungen unserer Leser gespielt, für die wir uns hier noch einmal herzlich bedanken. Unser besonderer Dank gilt Frau Sylvia Hasselbach für die Betreuung seitens des Verlags. Herrn Tobias Philipp danken wir für die erneute Unterstützung durch Bereitstellung der Testversion von Sybase Adaptive Server Anywhere. Gern haben wir auch zur Kenntnis genommen, dass unser Werk offenbar von vielen Leserinnen und Lesern dauerhaft geschätzt wird. Dass sich andere Autoren von »Einsteiger«Büchern in die Thematik SQL dadurch inspirieren lassen, erfüllt uns ebenfalls mit Befriedigung, insofern wir das Original sind. Wo dies nicht der Fall ist – schließlich haben wir das Rad auch nicht erfunden –, haben wir das durch Verweis auf die

11

Vorwort zur vierten Auflage

entsprechenden Quellen kenntlich gemacht, deren Studium wir den Leserinnen und Lesern, die noch tiefer in die Materie einsteigen wollen oder ergänzende Literatur suchen, ausdrücklich empfehlen möchten. Anregungen und Kritik an unserem Buch sind stets willkommen! Bremerhaven und Bremen im Oktober 2007 Günter Matthiessen: [email protected] Michael Unterstein: [email protected]

12

Vorwort zur dritten Auflage Dieses Werk liegt nunmehr in der dritten Auflage vor. Die Erfahrungen, die wir als Autoren mit dem vorliegenden Buch in zahlreichen Lehrveranstaltungen und Firmenseminaren gewonnen haben, die Entwicklungen im SQL-Standard und bei den Datenbanksystemen, insbesondere was die Umsetzung objektrelationaler Konzepte betrifft, haben uns motiviert, eine gründliche Überarbeitung vorzunehmen. Dabei wird an dem bisher bewährten Konzept und Aufbau festgehalten. Der SQL-Standard steht vor einer »Neuauflage«. Während die der bisherigen Auflage zugrunde gelegte Version SQL:1999 teilweise noch weit davon entfernt ist, in der realen Datenbanktechnologie umgesetzt zu werden, steht die Version SQL:200n ins Haus, wobei das n nach optimistischen Schätzungen eine 3 sein wird. Uns liegt eine Entwurfsversion dieses Standards vor, die wir vor allem in den Kapiteln 4 und 5 berücksichtigt haben. In Kapitel 3 über den Datenbankentwurf haben wir die Abschnitte, die sich mit dem Entity-Relationship-Modell und mit UML beschäftigen, im Hinblick auf eine möglichst einheitliche Notation überarbeitet, die von einer Anzahl von Softwarewerkzeugen unterstützt wird. Im Abschnitt über Normalformen haben wir zugunsten besserer Verständlichkeit die Definitionen modifiziert und einige erläuternde Beispiele hinzugefügt. Neu hinzugekommen ist ein Abschnitt über Qualitätsanforderungen an den Datenbankentwurf - wir haben in der Praxis häufig beobachtet, dass die Fähigkeiten der Datenbanksysteme zur Überwachung der Datenintegrität nur zum Teil ausgenutzt werden, und möchten auf einen intensiveren Gebrauch der Möglichkeiten hinwirken. Aufgrund der gewachsenen Unterstützung objektorientierter Konzepte durch relationale Datenbanksysteme haben wir den entsprechenden Abschnitt in Kapitel 9 gründlich überarbeitet. Auf der anderen Seite ist das »rein« objektorientierte Datenbanksystem O2, auf das wir uns bisher in Abschnitt 9.4 bezogen haben, vom Markt verschwunden, weswegen wir diesen Abschnitt gestrichen haben. Ein Teil der vielen Detailänderungen geht auf Anregungen unserer Leserinnen und Leser zurück, für die wir uns hier noch einmal sehr herzlich bedanken. Wir möchten dazu ermuntern, uns weiterhin Anregungen und Kritik zukommen zu lassen, die dann zur weiteren Verbesserung dieses Produktes beitragen können. Unser Dank gilt Herrn Martin Asbach von der Verlagsgruppe Pearson Education für die engagierte Unterstützung dieser Neuauflage und Frau Anne Spranger für das sorgfältige Lektorat. Sehr dankbar sind wir Herrn Philipp von der Firma iAnywhere-Solutions, der uns wiederum die Beilage einer CD mit einer 60-Tage-Testversion des Datenbanksystems Adaptive Server Anywhere und einer Testversion des Power Designer ermöglicht hat, sodass unsere Leser alle SQL-Übungen und darüber hinaus die Entwurfsübungen auf dem eigenen Rechner nachvollziehen kön-

13

Vorwort zur dritten Auflage

nen. Auf der CD finden sich die entsprechenden SQL-Skripte, die wir ebenfalls mit Adaptive Server Anywhere erstellt haben, sodass die Beispiele im Text ohne weitere Anpassungen »laufen«. Ebenfalls dabei sind Skripte zur Erstellung einer OracleVersion unserer Beispieldatenbank und eine Version für MS-Access. Des Weiteren sind Skripte für die Anlage und Abfrage einer objektrelationalen Version unseres Beispiels enthalten. Bremen und Bremerhaven im März 2003

14

Vorwort zur zweiten Auflage Nicht ganz drei Jahre nach der ersten Auflage haben wir uns entschlossen, die zweite Auflage vollständig zu überarbeiten. Zum einen konnten so neuere Entwicklungen in die Überarbeitung einfließen. Im Kapitel über den Datenbankentwurf haben wir zusätzlich zu den »klassischen Entity-Relationship-Diagrammen« UML-Klassendiagramme als Beschreibung für relationale Datenbanken eingeführt und gezeigt, dass es auch im klassischen Anwendungsbereich der relationalen Datenbanken sehr wohl Anwendungen gibt, bei denen das Vererbungsprinzip produktiv angewandt werden kann. Zum anderen sind inzwischen objektrelationale SQL-Datenbanken auf dem Markt erhältlich, sodass durch Einbeziehung dieser Ansätze der Ausblick der ersten Auflage auf die objektorientierten Datenbanken näher an das Thema »SQL« herangerückt ist. Als letzte Entwicklung ist der schon seit etwa fünf Jahren diskutierte Standard SQL-3 jetzt unter der Bezeichnung »SQL:1999« verabschiedet worden. Wir haben uns in der Einführung in SQL jeweils auf den neuesten Standard bezogen. Nichtsdestotrotz ist dieses Buch keine enzyklopädische Einführung in SQL:1999, sondern eine Einführung in die Arbeit mit relationalen Datenbanken unter Verwendung von SQL. Außerdem haben wir uns aufgrund von Anregungen, die an uns herangetragen worden sind, und aus den Erfahrungen, die wir bei Seminaren mit dem Buch gesammelt haben, entschlossen, den Aufbau der ersten drei Kapitel zu ändern. Obwohl natürlich vor dem Arbeiten mit einer Datenbank ein professioneller Datenbankentwurf notwendig ist, scheint es uns doch sinnvoll zu sein, zuerst die Grundlagen der relationalen Datenbanken zu beschreiben und darzustellen, welche »Arbeitsmöglichkeiten« (Relationenalgebra) sie bieten. Bevor ein Architekt sich mit dem Entwurf eines Hauses befasst, muss er auch fundierte Kenntnisse über die möglichen Baumaterialien und ihre Eigenschaften haben. Wir haben auch viele Anfragen und Anregungen über E-Mail bekommen. Wir möchten uns für die Anregungen bedanken und die Leser auffordern, auch in Zukunft nicht mit Anfragen und Kritik zu sparen. Unser Dank für die sehr engagierte Unterstützung dieser überarbeiteten Neuauflage unseres Buchs gilt unserer Lektorin Frau Lauer. Frau Daenecke danken wir wiederum für das sorgfältige Korrektorat. Des Weiteren möchten wir unseren Dank der Firma Sybase, besonders Herrn Philipp, aussprechen. Aufgrund ihrer Unterstützung liegt dem Buch eine CD mit einer vollständigen Testversion des Datenbanksystems Sybase SQL Adaptive Server Anywhere Studio bei. Der Leser erhält dadurch anhand des Buches die Gelegenheit, alle SQL-Übungen auf dem eigenen Rechner nachzuvollziehen. Unsere im Buch verwendete Beispieldaten-

15

Vorwort zur zweiten Auflage

bank ist ebenfalls mit SQL Anywhere realisiert worden, sodass alle Beispiele in den Kapiteln 4, 5, 6 und 7 ohne weitere Anpassungen »laufen«. Bremen und Bremerhaven im November 1999

16

Günter Matthiessen Michael Unterstein

Vorwort zur ersten Auflage Bunt, grafisch und vielfältig präsentiert sich die Datenverarbeitung heute dem Laien wie dem Experten. Ein paar Mausklicks, und schon hat man seine Probleme gelöst – komplizierte Datenreihen werden als Säulendiagramm oder mehrdimensional dargestellt, Verteilungsprobleme sind als Tortendiagramm veranschaulicht. Wer Informationen benötigt, »browst« durchs Internet und holt sich die Multimedia-Show auf den Arbeitsplatz. All diese Beispiele stehen für einen im Vergleich zu früher stark vereinfachten, intuitiven Zugriff auf Informationen aller Art. Dass diese verfügbar sind und sich nach verschiedensten Gesichtspunkten auswerten und darstellen lassen, muss allerdings erst einmal gewährleistet werden. Dafür gibt es Datenbanken. Richtig eingesetzt, eröffnen sie alle Möglichkeiten der Informationsverarbeitung. In unverstandener, unvollkommener Weise genutzt, verkommen sie zum Datenfriedhof, aus dem sich nach dem Zufallsprinzip taugliche oder unbrauchbare Informationen ziehen lassen. Dieses Buch erklärt Datenbanken aus der Sicht des Anwendungsentwicklers und des Benutzers. Es erläutert grundlegende Konzepte der Datenbanktechnologie und ihre Umsetzung bei der Entwicklung und Anwendung von praxistauglichen Informationssystemen. Im Mittelpunkt stehen dabei die relationalen Datenbanken. Systeme, die auf dem relationalen Datenmodell basieren, haben mittlerweile einen hohen Reifegrad erlangt, der es gestattet, verlässliche Informationssysteme beliebiger Größenordnungen zu erstellen und zu erhalten. Die dabei gesetzten Maßstäbe werden von anderen Systemen bisher nicht erreicht. Andererseits haben manche Forderungen des relationalen Datenmodells noch keine durchgängige Umsetzung in verfügbaren Systemen erfahren, sodass hier noch erhebliches Entwicklungspotenzial besteht. Unbestreitbar hat das relationale Datenmodell einige Mängel, die seine Brauchbarkeit speziell für die Verarbeitung komplexer Datenstrukturen einschränken. Hier setzen neue Konzepte, speziell die Objektorientierung, an. Ein Einblick in die Fundamente der relationalen wie der objektorientierten Technologie – wie er auch in diesem Buch gegeben wird – zeigt, dass eine differenzierte Beurteilung der Konzepte im Hinblick auf den Einsatzzweck und nicht die euphorische Identifizierung mit dem »Neuen« oder dem »Bewährten« das Richtige ist. Das Relationenmodell enthält Regeln für die Abbildung von interessierenden Ausschnitten der Realität in einer Datenbank und beschreibt Möglichkeiten der Verarbeitung solcher Abbilder zum Zweck der Informationsgewinnung. Erst die Kenntnis dieser theoretischen Grundlagen versetzt den Entwickler – zum Teil aber auch den Anwender – in die Lage, aussagefähige und stabile Datenbanken zu schaffen oder auszuwerten. Dabei geht es um mehr als das Ablegen und Wiederfinden von

17

Vorwort zur ersten Auflage

Daten. Ebenso sollten die zwischen verschiedenen Daten existierenden logischen Zusammenhänge in der Datenbank ihr Abbild finden. Nur dann kann langfristig gewährleistet werden, dass die gesammelten Daten »konsistent« – also widerspruchsfrei und aussagefähig – bleiben. Die Autoren haben sich das Ziel gesetzt, den Lesern das für die Gestaltung von Informationssystemen und den Umgang mit Datenbanken erforderliche Wissen zu vermitteln. Wir legen dabei gleichermaßen Wert auf theoretische Exaktheit wie auf Anschaulichkeit und Verständlichkeit. Eine durchgängig verwendete Fallstudie liefert das Anschauungsmaterial. Zahlreiche Übungsaufgaben geben dem Leser Gelegenheit, das angeeignete Wissen zu festigen und den Grad des erworbenen Verständnisses selbst zu überprüfen. Dieses Buch ist sowohl zum Selbststudium wie auch als Begleitskript für Seminare und Lehrveranstaltungen in Betrieben und Hochschulen geeignet. Es richtet sich vor allem an: 왘 Entwickler von Datenbanken für betriebliche Anwendungen, darunter System-

analytiker und Anwendungsprogrammierer, 왘 Verantwortliche der betrieblichen Datenverarbeitung, darunter Datenbankad-

ministratoren, 왘 EDV-Organisatoren, die Endanwender beim Zugriff auf zentrale Daten beraten

und betreuen, 왘 Nutzer von existierenden Datenbanken, die über flexibel einsetzbare Arbeits-

platzsoftware auf eine Datenbank zugreifen, sowie 왘 Studenten an Fachhochschulen und Universitäten mit Studienschwerpunkten

im Bereich Informatik oder Wirtschaftsinformatik. Das vorliegende Werk geht auf zwei Grundlagen zurück: zum einen auf das seit vielen Semestern im Einsatz befindliche und beständig fortentwickelte Vorlesungsskript Matthiessen: Datenbanken und Informationssysteme, zum anderen auf das Buch Finkenzeller, Kracke, Unterstein: Systematischer Einsatz von SQL/ORACLE, das als eines der ersten deutschsprachigen Bücher speziell zu ORACLE 1989 bei Addison-Wesley erschienen ist. Beide Autoren bringen nun erstmals gemeinsam ihre zahlreichen Erfahrungen in Datenbankpraxis, Veröffentlichung und Lehre zu Papier. Unser Dank für die engagierte Unterstützung unseres Buchs gilt Herrn Pereira und Frau Lucht beim Verlag Addison Wesley Longman. Frau Daenecke danken wir für das sorgfältige Lektorat. Kommentare, Anregungen und Kritik bitten wir per E-Mail an die Autoren ([email protected] bzw. [email protected]) oder an den Verlag zu senden. Bremen und Bremerhaven im Januar 1997

18

Günter Matthiessen Michael Unterstein

Hinweise zur CD Die CD enthält die Developer Edition des Datenbanksystems Sybase SQL Adaptive Server Anywhere Studio. Interessierte Leserinnen und Leser können damit auf ihrem Rechner die Beispieldatenbank installieren und bearbeiten. Wir haben außerdem im Verzeichnis \Versand verschiedene Versionen unserer Beispieldatenbank abgelegt. Wer mag, kann auch unter Oracle oder Access üben und unsere Ausführungen verfolgen. Diejenigen unter den Leserinnen und Lesern, die das Buch als Lehrunterlage in eigenen Kursen und Seminaren verwenden wollen oder die aus anderen Gründen eigene Präsentationen vorbereiten müssen, finden im Verzeichnis \Grafik die Grafikdateien, die wir in unseren Text eingebunden haben. Sie stehen Ihnen unter dem Vorbehalt des Copyrights und mit der Verpflichtung zum korrekten Zitieren zur Verfügung.

Haftungsausschluss Alle Beispiele, die auf der CD enthalten sind, sind reines Übungsmaterial. Wir haben sie mit der größten Sorgfalt erstellt und getestet. Für mögliche Probleme, die aus ihrem Einsatz durch den Leser entstehen, übernehmen wir keine Verantwortung. Dasselbe gilt für die Datenbanksoftware SQL Anywhere und Power Designer. Sie ist eine freundliche Beigabe der Firmen Sybase und iAnywhere Solutions und dient lediglich zur Unterstützung des Lernens mit dem Buch. Für Probleme, die aus jedwedem Gebrauch des Produkts entstehen, sind weder wir noch die genannten Firmen zur Verantwortung zu ziehen.

Hinweise des Herstellers zu den Testversionen Sehr geehrte Leserinnen und Leser dieses Buches, die auf der CD befindliche Datenbank-Software Sybase SQL Anywhere ist eine Entwicklerversion mit vollem Funktionsumfang. Diese ist zeitlich unbegrenzt für Entwickleraufgaben nutzbar, sobald Sie sich offiziell auf der zugehörigen Website registrieren: http://www.ianywhere.com/forms/downloads/sql_anywhere/sqlany_10_ developer_download.html

19

Hinweise zur CD

Andernfalls wird SQL Anywhere nur als 60 Tage-Testversion installiert. Die kostenlose Weitergabe der Software zur Nutzung von Anwendungen auf Basis von SQL Anywhere ist lizenzpflichtig Die Datenbank Adaptive Server Anywhere ist sowohl auf der Windows-Plattformen als auch auf Linux installierbar. Die Installationsroutinen liegen in separaten Ordnern auf der CD bereit. Ab dem Zeitpunkt der Installation steht die Datenbank mit dem vollen Funktionsumfang zur Verfügung. Damit können Sie die Übungen des Buches sofort in die Tat umsetzen. Adaptive Server Anywhere eignet sich sowohl für Einzelplatz- als auch Mehrbenutzeranwendungen. Die Datenbank bietet Funktionalitäten der High-End-Enterprise-Datenbanken wie z. B. echte Transaktionsverarbeitung, Stored Procedures und Java in der Datenbank. ASA benötigt dafür aber im laufenden Betrieb keine Administration. Das System bietet mit seinen eingebauten Replikationsmechanismen eine ideale Technologie zum automatischen Datenabgleich zwischen zentralen Datenbanken und Außendienstsystemen. Sollten Sie weitere Informationen zum Produkt Sybase Adaptive Server Anywhere benötigen, finden Sie diese unter http://www.sybase.com/detail?id=1043612. Für technische Fragen und kostenlosen Support finden Sie Hilfe im »SQL Anywhere Getting Started Center« unter http://www.ianywhere.com/getting_started/sql_anywhere_getting_ started.html Viel Erfolg bei der Arbeit mit diesem Buch und vor allem mit der Datenbank Sybase SQL Anywhere wünscht Ihnen Tobias Philipp Presales Manager EMEA IAnywhere Solutions – A Sybase Company

20

Datenbanken und Datenbanksysteme Der Computer ist ursprünglich als das eingeführt worden, was er heute noch im Englischen bedeutet, nämlich als Rechner. Er wurde zur Berechnung umfangreicher technisch-wissenschaftlicher Systeme benutzt. Aber schon in den 60er-Jahren verbreitete sich der Computer vorwiegend als Datenverarbeitungsanlage. Dies hat folgende Ursachen: 왘 Die Daten stehen im Mittelpunkt des Interesses der Anwender. 왘 Es sind vielfältige maschinelle Auswertungen von Daten möglich. 왘 Geschäftsprozesse führen zu Änderungen der entsprechenden Daten.

Die Daten sind nicht mehr nur Eingaben, Zwischenergebnisse und Ausgaben von Programmen, sondern sie haben auch eine eigene Existenz unabhängig von Programmen. Ein Kunde hat auch dann einen Namen und eine Adresse, wenn wir ihm gerade keine Rechnung schicken. Dies führte schon Ende der 60er-Jahre zur Entwicklung von Datenbanksystemen. Hier wird zwischen die Daten und das Programm, das die Daten verarbeitet, ein Datenbank-Verwaltungssystem (abgekürzt DBMS für Data Base Management System) gesetzt, das alle Zugriffe auf die Daten verwaltet. Im Deutschen wird das Datenbank-Verwaltungssystem – sprachlich nicht korrekt – häufig als Datenbanksystem bezeichnet. Der Datenbankansatz führt zu folgenden Eigenschaften: 왘 Die Daten werden in einem Format abgelegt, sodass ihre Struktur unabhängig

von speziellen Programmen vom DBMS interpretiert werden kann. 왘 Das DBMS ist der einzige Prozess, der unmittelbar auf die Daten zugreifen und

sie manipulieren darf. Auswertungen und Verarbeitungsprogramme laufen grundsätzlich über das DBMS. 왘 Das DBMS organisiert einen Mehrbenutzerbetrieb unter Verwendung von

individuellen Zugriffsrechten. 왘 Das DBMS organisiert die Koordination von Zugriffen von mehreren

Benutzern gleichzeitig. 왘 Das DBMS enthält Maßnahmen zur Unterstützung der Datenkonsistenz durch

Mechanismen der Datensicherung und der Wiederherstellung der Daten nach einer Betriebsstörung. PC-DBMS erfüllen hiervon in der Regel nur den Punkt 1. Für operative Datenbanken in Unternehmen sind dagegen die weiteren Punkte unverzichtbar.

21

1 Datenbanken und Datenbanksysteme

1.1 Zentralisierung der Datenhaltung Ein wichtiger Grund für die Einführung von Datenbanken im Unternehmen ergibt sich aus der betriebswirtschaftlichen Notwendigkeit, die gesamten Unternehmensdaten so zu organisieren, dass für die gesamten Daten eine verantwortliche Stelle vorhanden ist (Zentralisierung der Datenhaltung). Das muss nicht heißen, dass eine Stelle für alle Daten verantwortlich ist, sondern dass es einen Verantwortlichen (»Datenbankadministrator«) gibt, der für die gesamte Datenorganisation verantwortlich ist und der mit der Unternehmensführung gemeinsam festlegt, wer für einzelne Datenbereiche (z. B. Personal, Kunden, Produktionsdaten, Lieferantendaten, Lagerhaltung ...) verantwortlich ist. Durch das Benutzen einer Datenbank ergibt sich eine Reihe von Vorteilen, unter anderem: 왘 Verringerung bzw. systematische Kontrolle der Redundanz

So müssen z. B. Artikeldaten nur an einer Stelle gepflegt werden. Falls sie dennoch mehrfach (auf verschiedenen Knoten) gespeichert werden sollen, hat das DBMS dafür zu sorgen, dass alle Kopien auf demselben Stand sind. 왘 Konsistenzüberprüfung vom DBMS

So kann das DBMS z. B. verhindern, dass Kundendaten gelöscht werden, wenn noch Rechnungen offen sind. Bei der Erfassung eines Auftrags stellt das DBMS hingegen sicher, dass eine eingegebene Kundennummer auch tatsächlich einem Kunden zugeordnet ist. Eine Kundennummer in der Auftragstabelle, der kein Kundensatz in der Kundentabelle entspricht, wäre nämlich eine Verletzung der Konsistenz der Datenbank. Diese Überprüfung durch das DBMS ist auch durch unabsichtlich oder böswillig fehlerhafte Programme nicht zu umgehen. 왘 Auswertungen nach neuen Gesichtspunkten werden möglich (da Daten zentral vor-

handen sind!) Das Problem des »Anwendungsstaus« kann dadurch abgemildert werden, dass die Datenbank über interaktive Abfrageprogramme nach beliebigen Gesichtspunkten durchsucht werden kann. Hier tritt andererseits das Problem des Datenschutzes auf. 왘 Datenunabhängigkeit

Erweiterungen des Datenbankaufbaus erfordern keine Änderung von Programmen. Hierauf gehen wir näher in Abschnitt 1.2 ein.

22

Die Datenbankarchitektur nach ANSI-SPARC

Datenbankbeschreibung

Daten

Daten

Datenbank-Verwaltungssystem

Programm 1

Programm 2

Auswertung

Abbildung 1.1: Datenbank-Verwaltungssystem zwischen Programm und Daten

1.2 Die Datenbankarchitektur nach ANSI-SPARC ANSI/X3/SPARC/SGDBMS: 왘 ANSI:

American National Standards Institute

왘 X3:

Computer and Information Processing

왘 SPARC:

Standards Planning and Requirements Committee

왘 SGDBMS:

Study Group on Data Base Management Systems

Die ANSI/X3/SPARC-Gruppe hat 1975 einen Report erstellt, wie ein Datenbanksystem aufgebaut sein soll (siehe Abbildung 1.2). Dabei ist die dort beschriebene Architektur unabhängig von der Frage, ob das dem Datenbanksystem zugrunde liegende Datenbankmodell relational oder netzwerkartig oder an einem anderen Modell orientiert ist.

23

1 Datenbanken und Datenbanksysteme

Externes Schema: Präsentation Kundenverwaltung

Auftragsverwaltung

Artikelverwaltung

Konzeptionelles Schema: Information

Unternehmens-Datenmodell (Daten, Integritätsregeln)

Internes Schema: Daten-Speicherung

RDBMS 1

RDBMS 2

Datei-System

Abbildung 1.2: ANSI-SPARC-Architektur

Konzeptionelles Schema Im Zentrum steht das konzeptionelle Schema. Dieses Schema enthält die Beschreibung aller unternehmensrelevanten Datenstrukturen für die Datenbank. Dabei ist es unerheblich, auf welche Weise diese Daten abgelegt werden. Auch für Datenbanken, die physikalisch auf mehreren Rechnern gespeichert werden, gibt es nur ein konzeptionelles Schema. Das konzeptionelle Schema ist sehr stark vom Datenbankentwurf und von dem benutzten Datenmodell (relational, objektorientiert, hierarchisch ...) abhängig. Zum konzeptionellen Schema gehören neben allen relevanten Beschreibungen für die Sammlung der Daten insbesondere auch alle Beschreibungen von Konsistenzbedingungen (z. B. für jeden Artikel im Lager gibt es genau einen Artikel-Stammsatz; ein Zug kann nicht gleichzeitig an mehreren Orten sein; die Kundennummer muss eindeutig sein; jeder Kunde muss eine Kundennummer haben ...). Auf die Realisierung dieser Konzepte gehen wir später ein – insbesondere in den Kapiteln 4.3 und 7. Das konzeptionelle Schema ist nicht unbedingt identisch mit dem logischen Schema der Datenbank oder dem Datenbankkonzept, wie wir es in Kapitel 3 einführen.

24

Die Datenbankarchitektur nach ANSI-SPARC

Internes Schema Im internen Schema sind die Einzelheiten der Implementierung beschrieben, z. B. die Aufteilung der Datenbank auf verschiedene Platten, das Anlegen von Spiegelplatten oder das Anlegen von Indizes zur Beschleunigung der Zugriffe.

Externes Schema In den externen Schemata (Benutzersichten) werden Teilbereiche der Datenbank für verschiedene Benutzergruppen definiert. Hier werden auch die Nutzungsrechte der verschiedenen Benutzer festgelegt. Zum Teil können für den (lesenden) Zugriff abgeleitete Daten (z. B. die Summe der Gehälter in einer Abteilung) in ein externes Schema eingetragen werden, ohne dass die zugehörenden Grunddaten sichtbar gemacht werden.

Datenunabhängigkeit Zwischen den Schemata bestehen natürlich inhaltliche, sachlogische Abhängigkeiten. So kann eine Information aus der Datenbank nur dargestellt werden, wenn sie aus gespeicherten Daten auf irgendeine Weise erzeugt werden kann. Die Art und Weise, wie die Daten von der Festplatte auf den Bildschirm kommen – also der Transfer zwischen den verschiedenen Schemata –, wird aber durch Zugriffsfunktionen (Schnittstellen) geregelt, die im Datenbanksystem implementiert sind. Die technische Frage des Datenzugriffs – beispielsweise wie aus Datenfragmenten, die auf der Festplatte wild verstreut sind, eine Tabelle zusammengesetzt wird – spielt daher bei der Nutzung von Datenbanken so gut wie keine Rolle, und auch bei der Anwendungsentwicklung muss man sich kaum einmal damit beschäftigen. Wird die physikalische Speichertechnik geändert, so muss die entsprechende Zugriffsfunktion angepasst werden, damit am Ende dieselbe Tabelle wie vorher an das konzeptionelle Schema weitergereicht wird. Die DBMS verschiedener Hersteller können das Konzept auf ganz unterschiedliche Weise realisieren, und auch bei Produkten eines Herstellers kann es von Version zu Version erhebliche Unterschiede in der Art der Speicherstrukturen geben, ohne dass dies laufende Anwendungen tangiert. Die drei Schemata oder Ebenen der Datenbankarchitektur können daher in gewissen Grenzen unabhängig voneinander gestaltet und betrachtet werden. Das bezeichnet man als Datenunabhängigkeit. 왘 Physische Datenunabhängigkeit Ein Programmierer oder Benutzer braucht für die Verarbeitung der Daten keine Kenntnis der Datenspeicherung (Blöcke, physische Schlüssel ...). Damit müssen spätere Änderungen des physischen Datenbankaufbaus in den Programmen nicht berücksichtigt werden. 왘 Logische Datenunabhängigkeit Ein Programmierer oder Benutzer muss nur die Beziehungen zwischen den Daten kennen, die auch tatsächlich für dieses Programm benötigt werden. Damit müssen spätere Änderungen im logischen Aufbau (neue Felder, Löschen von Feldern, kompatible Änderungen von Datentypen), die das jeweilige Programm nicht unmittelbar betreffen, nicht berücksichtigt werden.

25

1 Datenbanken und Datenbanksysteme

Für Datenbanken, die auf nicht vernetzten Einzelplatzrechnern installiert sind, gibt es häufig nur ein externes Schema, das mit dem konzeptionellen Schema identisch ist; mit anderen Worten: »Es gibt kein externes Schema.«

1.3 Integritätsregeln Wie wir in Abschnitt 1.1 erwähnt haben, gehören zum konzeptionellen Schema auch eine Reihe von Integritätsregeln.

Beispiele: 왘 Der Kontostand darf das Kreditlimit nicht unterschreiten. 왘 Der Verkaufspreis muss größer als der Einkaufspreis sein – außer bei Sonderan-

geboten. 왘 Es können nur Waren bestellt werden, deren Artikelnummern in der Artikelda-

tei enthalten sind. 왘 Studenten an einer Hochschule können nur Studiengänge studieren, die an der

Hochschule angeboten werden. 왘 Ein Student gehört dem Fachbereich an, der den Studiengang enthält, in dem er

studiert. 왘 Eine Matrikelnummer darf nicht doppelt vergeben werden. 왘 Bei der Stundenplanung ist für einen Vorlesungsblock pro Raum nur maximal

eine Veranstaltung vorzusehen. Das Ziel ist es, dass das DBMS – unabhängig von Anwendungsprogrammen – im Betrieb dafür sorgt, dass diese Integritätsbedingungen eingehalten werden. Das relationale Modell selbst kennt: 왘 Domänenregeln, durch welche die möglichen Werte für die Attribute einge-

schränkt werden, 왘 Primärschlüssel, durch die festgelegt wird, dass jedes Tupel eindeutig identifi-

zierbar ist, 왘 weitere Schlüssel, die ebenfalls eindeutig sein müssen, 왘 Fremdschlüssel, durch welche die Beziehungen zwischen verschiedenen Relatio-

nen auf sichere Weise hergestellt werden können (es kann in einer Bestellung keine Kundennummer geben, die nicht in der Kundenrelation vorhanden ist), und 왘 weitere Geschäftsregeln – Integritätsregeln, die sich durch logische Bezüge von

Daten auf andere Daten ergeben. In der nicht sehr fernen Vergangenheit konnten diese und weitere Arten von Integritätsbedingungen nur dadurch gewährleistet werden, dass sie in Anwendungsprogrammen implementiert wurden. Damit wurden natürlich viele Regeln mehrfach im

26

Integritätsregeln

Programmcode repräsentiert. Die Folge waren unter Umständen Widersprüche in verschiedenen Programmen und auf jeden Fall verschlechterte Wartbarkeit. Schlimmer noch: Das Problem der Integritätskontrolle ist sehr schwer in den Griff zu bekommen, wenn es jedem einzelnen Anwender überlassen bleibt. Dem sind bestimmte Regeln entweder gar nicht bekannt, oder sie werden nicht immer konsequent befolgt und möglicherweise in ihrer Auswirkung auf die gesamte Datenbank nicht überschaut. Dieses Problem wächst noch mit zunehmender Verfügbarkeit an Client-Software aller Art, die den direkten Zugriff von Anwendern auf Datenbanken gestattet. Die Forderung lautet daher: Alle Integritätsbedingungen, welche die Daten unabhängig von etwaigen Abläufen betreffen, müssen als Eigenschaften der Daten selbst behandelt werden. Folglich müssen sie in genau dieser Datenbank niedergelegt werden, also: »Integrität in die Datenbank!« Konsistenzüberprüfungen in den Anwendungsprogrammen (z. B. in Datenerfassungsformularen) sind zwar ein wichtiges Hilfsmittel, um die Konsistenz sicherzustellen; die Anwendungsprogramme sind aber in den externen Schemata angesiedelt. Der Endanwender oder der Entwickler für ein Endanwenderprogramm ist dann dafür verantwortlich, dass die Unternehmensregeln eingehalten werden. In einer unternehmensweiten Datenbank kann die Konsistenz damit nicht sichergestellt werden, solange ein Benutzer die Möglichkeit hat, (absichtlich oder unabsichtlich) Anwendungen zu entwickeln, in denen Konsistenzbedingungen verletzt werden.

Datenbank zentrale Integritätsregeln

App 1

App 2

App 3

App 4

App 5

verschiedene Anwendungen

Abbildung 1.3: Zentral verwaltete Integritätsbedingungen

Andererseits bedeutet die Eintragung von Konsistenzbedingungen in das konzeptionelle Schema nicht, dass der Anwendungsprogrammierer die Konsistenzregeln außer Acht lassen muss. Eine potenzielle Verletzung einer Konsistenzbedingung wird zwar vom Datenbanksystem abgewiesen; wenn aber keine spezielle Fehler-

27

1 Datenbanken und Datenbanksysteme

behandlung im Programm vorgesehen ist, wird das Programm möglicherweise mit einer Fehlermeldung beendet, die für den Endanwender nicht akzeptabel ist.

1.4 Architektur von Datenbanksystemen Wenn mehrere Endanwender Zugriff auf eine Datenbank haben, ist es üblich, dass die Anwendungsprogramme nicht unmittelbar auf die Daten zugreifen (wie es ja bei selbst programmierten Anwendungen mit z. B. VSAM und Cobol ist). Hier wird üblicherweise eine Trennung in Front-End und Back-End vorgenommen. Front-End und Back-End sind eigenständige Prozesse (im Sinne von Unix). Der Front-End-Prozess kommuniziert mit dem Benutzer, der Back-End-Prozess mit der Datenbank, und beide kommunizieren miteinander (vgl. Abbildung 1.4). Diese beiden Prozesse können auf demselben Rechner laufen oder auf verschiedenen Rechnern installiert sein. Wegen ihrer komplementären Rollen bezeichnet man diese Prozesse auch als Client und Server. Falls das Front-End auf einem anderen Rechner läuft als das Back-End, werden diese beiden Rechner als Client und Server bezeichnet.

Front-End Benutzerprogramm mit GUI Benutzer

Back-End DB-Server Datenbank

Abbildung 1.4: Client-Server-Architektur

Die Aufteilung von Anwendung und Datenzugriff auf zwei Prozesse dient zum einen der Datensicherheit, da die Prozesse sich nicht gegenseitig in die Daten sehen können und die Kommunikation nur über vom Betriebssystem gesicherte Kanäle möglich ist. Daneben ermöglicht diese Architektur einen effizienten Zugriff von vielen Benutzern, die gleichzeitig auf die Daten lesend oder auch ändernd zugreifen können. Für Leser, die sich ein wenig mit dem Konzept von Prozessen in Betriebssystemen auskennen, noch einige Anmerkungen: Die Zuordnung zwischen Front-End und Back-End kann 1:1 oder n:1 sein. Im ersteren Fall ist jedem Benutzer (über das Front-End) ein eigener Back-End-Prozess zugeordnet. Die Kommunikation zwischen diesen beiden Prozessen ist dann einfacher – dafür müssen die verschiedenen Back-End-Prozesse Informationen über die benutzten Tabellen austauschen. Im letzteren Fall kommunizieren alle Benutzer mit demselben Back-End-Prozess.

28

Architektur von Datenbanksystemen

Dieses kann dann die Datenbankzugriffe besser optimieren, muss aber eine Verwaltung der verschiedenen Benutzer führen. Eine weitere Variante ist ein BackEnd mit verschiedenen Threads, d. h. parallelen Aufträgen, die von diesem BackEnd verwaltet werden. Ein Thread ist einfacher zu verwalten als ein Prozess, da er nicht wie ein Prozess die gesamte Umgebung speichern muss, sondern intern innerhalb eines Prozesses verwaltet wird. Die Client-Server-Architektur hat in Netzwerken bei Unternehmen und Institutionen eine weltweite Verbreitung gefunden. Auf der einen Seite existiert der Datenbankserver, dem die gesamte Aufgabe der Datenverwaltung und der Erledigung der Abfragen zufällt. Daneben gibt es den Client als Endanwender-Rechner, der die gesamte Dialogführung für die Abfragen und die Aufbereitung der Ergebnisse für den Endanwender übernimmt. Durch diese Architektur wird zum einen das zu übertragende Datenvolumen geringer, als wenn die Datenselektion und Ermittlung der Ergebnisse im Anwenderprozess erfolgen würden. Andererseits ist es hier möglich, im Client grafische Benutzeroberflächen zu verwenden; dies funktioniert nur, wenn die Logik der Oberfläche vom Client verwaltet wird. Zuletzt wird durch eine solche Lösung bei stark frequentierten Datenbanken der Server als zentraler Rechner weniger belastet. Durch die Verbreitung des World Wide Webs sind neue Anforderungen entstanden. Der Zugriff auf Datenbanken soll unabhängig davon möglich sein, dass ein Benutzer eine bestimmte Client-Software installiert hat. Nur die Verfügung über einen Java-fähigen Browser ist vorausgesetzt, für alle Rechnerklassen und Betriebssysteme ist dies heute selbstverständlich. Es gibt höchst unterschiedliche Arten von Anwendungen, die über das Web auf Datenbanken zugreifen: von der einfachen Abfrage eines Datenbestands bis hin zur interaktiven Bearbeitung von komplexen Transaktionen. Daraus ergeben sich auch sehr unterschiedliche Anforderungen an die Anbindung einer Datenbank. Eine populäre Möglichkeit besteht darin, auf dem Webserver kleine Java-Programme – sogenannte Applets – abzulegen, die zusammen mit einer HTML-Seite zum Anwender übertragen werden und auf dessen Webbrowser ausgeführt werden können. Applets können vom Client aus auf Datenbanken zugreifen, wobei ein eigenes, von HTML unterschiedenes Protokoll, z. B. JDBC, benutzt wird. Ein Nachteil dieser Lösung ist, dass die ganze Applikation über das Web auf den Client geladen werden muss. Ein anderes Konzept sieht wegen der Nachteile der Applets bei komplexeren Anwendungen vor, die Darstellung der Daten und die Benutzerinteraktion noch einmal getrennt von der Anwendungssoftware zu implementieren, es ergibt sich dann eine Architektur mit drei oder sogar noch mehr Ebenen (»Multi-Tier«): Der Datenbankserver bleibt erhalten, der Client ebenfalls, aber nur mit reduzierter Funktionalität. Dazwischen tritt ein sogenannter Applikationsserver, der die Anwendungssoftware – die nun beliebig komplex sein kann – beherbergt und ausführt. Er reicht die Anfragen des Clients an den Datenbankserver weiter und sendet dessen Ergebnisse beispielsweise als HTML-Seiten an den Client.

29

1 Datenbanken und Datenbanksysteme

Front-End Web-Browser

Benutzer

Java Virtual Machine

Applet

Web-Server

Back-End DB-Server Datenbank

Abbildung 1.5: Datenbankzugriff über das Web mittels Java-Applet

Front-End Web-Browser HTML Benutzer Applikations-Server

Web-Server

Back-Eend DB-Server Datenbank

Abbildung 1.6: 3-Tier-Architektur mit Applikationsserver

Eine ausführliche Darstellung der diversen Architekturvarianten findet sich in [RaVo03 Kapitel 4].

30

Einführung in das relationale Datenmodell Die erste Arbeit, in der Relationen als mögliche Gegenstände einer mathematisch fundierten Datenbanktheorie untersucht werden, ist der 1970 in den Communications of the ACM erschienene Artikel »A Relational Model of Data for Large Shared Data Banks« von E. F. Codd. Es hat etwa zehn Jahre gedauert, bis die ersten relationalen Datenbank-Verwaltungssysteme kommerziell verfügbar waren. Hier folgt eine sehr knappe Definition relationaler Datenbanksysteme, die Codd 1981 in seiner Rede anlässlich der Verleihung des Turing-Preises gegeben hat.1 Ein System ist relational, wenn: 왘 alle Daten als Werte in Relationen dargestellt sind und jeder Benutzerzugriff auf die Daten über deren Werte erfolgt (und somit der Benutzer keine Kenntnis von Zeigern und Verkettungen haben muss) und 왘 es relationale Operatoren für die Selektion, die Projektion und den Verbund unterstützt.

Jahreszahlen zum relationalen Modell 1970: Codd veröffentlicht erste Arbeiten zur Relationentheorie als Grundlage einer Datenbanktheorie. 1975: Ingres wird mit der Sprache QUEL auf PDP implementiert. 1976: IBM veröffentlicht Studien zum System R und SEQUEL. 1977: Oracle beginnt die Implementierung auf Mainframes mit SEQUEL. 1979: Oracle liefert ein erstes relationales Datenbanksystem mit SQL aus. 1982: Ingres wird als Datenbanksystem auf Minirechnern ausgeliefert. 1982: IBM bringt SQL/DS auf den Markt. 1982: IBM veröffentlicht das R*-Konzept für verteilte Datenbanken. 1989: Sybase, Ashton-Tate und Microsoft bringen SQL-Server auf den Markt. 1992: Neue Versionen einiger DBMS erhalten Trigger und Datenbankprozeduren. 1997: Objektorientierte Erweiterungen von relationalen Datenbanksystemen werden unter der Bezeichnung objektrelationale Datenbanken von Informix, DB2 und Oracle angeboten. 1999: Objektrelationale Datenstrukturen werden in den SQL-Standard aufgenommen. 1 veröffentlicht in [ACM 87; S. 391]

31

2 Einführung in das relationale Datenmodell

Im Folgenden lernen wir den Begriff der Relationen und die darauf beruhenden Operationen und Verknüpfungen kennen, die sog. Relationenalgebra. Hieraus können wir schon sehr komplexe Abfragen an die Datenbank stellen. Viele PC-basierte Datenbanksysteme (z. B. Access) erlauben unmittelbar die Formulierung von Anfragen in einer grafischen Implementierung der Relationenalgebra. Statt der Begriffe des relationalen Datenbankmodells werden in bestimmten Zusammenhängen Begriffe verwandt, die sich an die Anschauung einer zweidimensionalen Tabelle anlehnen (z. B. in SQL) oder die aus der klassischen Dateiorganisation stammen (z. B. Cobol, XBase). relational

SQL

Cobol, XBase

Relation

Tabelle (table)

Datei (file)

Tupel

Zeile (row)

Datensatz (record)

Attribut

Spalte (column)

Feld (field)

2.1 Aufbau relationaler Datenbanken Als Grundgerüst für die Definition von Relationen (im Sinne des relationalen Datenmodells – die Definition in der logischen Programmiersprache Prolog oder in der Prädikatenlogik weicht hiervon zum Teil ab) benötigen wir zum einen eine Menge von Bezeichnern für die Attribute, Tabellen und anderen Datenbankobjekte. Wir gehen davon aus, dass diese Bezeichner ähnlich wie in vielen Programmiersprachen aus Buchstaben, Ziffern und gegebenenfalls einigen Sonderzeichen wie dem Unterstrich gebildet werden; das erste Zeichen muss ein Buchstabe sein. Des Weiteren ist die Benutzung von Groß- und Kleinbuchstaben beliebig. Ihnen kommt keine Unterscheidungsrelevanz zu; d.h., die Bezeichner PLZ, Plz und plz sind als identisch anzusehen.

2.1.1 Attribute In einer Datenbank werden die Objekte, über die wir Daten sammeln, durch Attribute beschrieben. Ein Attribut besteht aus einem Attribut-Bezeichner und einem Wert. Wir schreiben ein Attribut immer in der Form: Attribut-Bezeichner = wert

Beispiele für Attribute: kunden_nr = 101 name = 'Stein' vorname = 'Peter'

32

Aufbau relationaler Datenbanken

geburtstag = DATE '1967-09-13' größe = 184 cm straße = 'Moordamm 24' plz = '23863' ort = 'Kayhude' personenstand = ledig

2.1.2 Domänen Zur Beschreibung von Daten benötigen wir in der Praxis unterschiedliche Wertebereiche (Domänen), aus denen die Attribute ihre Werte beziehen. In vielen Fällen reichen Standarddomänen wie: 왘 CARDINAL

natürliche Zahlen (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ...) 왘 INTEGER

ganze Zahlen (0, 1, 2, 3 ... –1, –2, –3 ...) 왘 NUMERIC

numerische Werte in Dezimaldarstellung 왘 STRING

Zeichenketten 왘 BOOLEAN

logische Werte (falsch/wahr, nein/ja, aus/ein, 0/1 ...) 왘 CHAR

einzelne Zeichen (z. B. A, B, C, a, b, c, 0, 1, 2, +, –, * ...) Des Weiteren können zum Beispiel eine Rolle spielen: 왘 Messwerte und Kapazitäten 왘 Geldbeträge 왘 Bezeichnungen für • Personen • Geräte • Firmen • Artikel 왘 왘 왘 왘

• ... Bilder und Konstruktionszeichnungen Töne Datum, Uhrzeit und Zeitpunkt (= Datum + Uhrzeit) Statusvariablen: • Familienstatus = {ledig, verheiratet, geschieden, verwitwet} • Verleihstatus = {ausgeliehen, vorbestellt, verfügbar, verlegt}

33

2 Einführung in das relationale Datenmodell

• Geschlecht = {männlich, weiblich} • Rechnungsstatus = {ohne, Rechnung geschrieben, erinnert, gemahnt, zweimal gemahnt, Zwangsverfahren} 왘 Mengen mit festgelegten Werten: • Gehaltsgruppen • Steuerklassen • Studiengänge an der Hochschule 왘 Adressdaten: Straße, Postfach, PLZ, Ort, Staat 왘 Telefonnummern Domänen

Name

Ort Anrede

Chen Codd Date von Friedeburg Matthiessen McNamara Meyer-Niemann Nürnberg Unterstein O'Neill Turvenpää ...

Herr Frau

Beijing Bremen Hamburg Kayhude Moskau New York Nürnberg Övelgönne Ostereistedt Paris ...

Abbildung 2.1: Domänen

Für die Werte der Domänen gibt es jeweils eine Repräsentation, in der sie den Benutzern angezeigt werden (z. B. Datumsformat, Darstellung von Bildern und Zeichnungen). Ebenso sind gewisse Repräsentationen zu benutzen, wenn entsprechende Daten eingegeben werden. Diese Repräsentationen müssen nicht unbedingt etwas mit der internen Darstellung der Daten im Rechner zu tun haben.2 Wir

2 So wird in Tabellenkalkulationsprogrammen wie OpenOffice.org Calc oder Excel, aber auch im PC-Datenbanksystem Access der Zeitpunkt '13.9.1967 21:00' durch die Zahl 24728,875 dargestellt, wobei 24728 die Anzahl der Tage seit dem 30.12.1899 ist, und 0,875 ist 7/8 von 24 Stunden, also 21 Stunden seit Mitternacht.

34

Aufbau relationaler Datenbanken

können sogar noch weiter gehen und sagen, dass die interne Darstellung der Daten für den Datenbankentwurf so gut wie keine Rolle spielt. Für Domänen sind in der Regel Operationen definiert, die in der Datenbankanwendung benutzt werden können. Es handelt sich hierbei um:

Homogene Operationen 왘 die Summe von Messwerten 왘 die Summe von Geldbeträgen, 왘 die Multiplikation eines Geldbetrages mit einer festen (Bruch-)Zahl 왘 die Verkettung von Zeichenketten

Heterogene Operationen Das sind Operationen, deren Argumente und Ergebnisse nicht aus derselben Domäne stammen, z. B.: 왘 Datum/Uhrzeit + Zeitintervall 왘 Länge einer Zeichenkette (ist eine natürliche Zahl)

Vergleichsoperationen und andere logische Operationen Diese können auch als Spezialfall der heterogenen Operationen angesehen werden – der Ergebnistyp ist BOOLEAN: 왘 Vergleich zweier Messwerte (, ≥, =, ≠) 왘 Vergleich von zwei Zeitpunkten 왘 Ähnlichkeit von zwei Namen In der Regel können sinnvollerweise nur Werte einer Domäne mit Werten derselben Domäne verglichen werden. Verschiedene Domänen, die durch dieselbe Menge von Werten repräsentiert werden, sind als nicht vergleichbar aufzufassen. Zum Beispiel können wir Kundennummern und Artikelnummern durch natürliche Zahlen repräsentieren. Dennoch muss die Domäne der Kundennummern von der Domäne der Artikelnummern unterschieden werden. Eine Kundennummer kann auch nicht ohne Weiteres mit einer Artikelnummer verglichen werden, sondern nur über eine Standardkonvertierungsfunktion.

Konvertierungsoperationen Das sind gesteuerte Umwandlungen verschiedener Datentypen über ihre Repräsentation, z. B.: • Tag: Datum → Numerisch • Monat: Datum → Numerisch

35

2 Einführung in das relationale Datenmodell

• Jahr: Datum → Numerisch • Wochentag: Datum → Numerisch3 • Datum → Zeichenkette • Zahl → Zeichenkette

2.1.3 Tupel Ein Tupel t ist gegeben durch eine Menge von Attributen. Ein Beispiel für ein Tupel ist im Folgenden dargestellt: t ::= [kunden_nr = 101, name = 'Stein', vorname = 'Peter', geburtstag = DATE '1967-09-13', größe = 184 cm, straße = 'Moordamm 24', plz = '23863', ort = 'Kayhude', personenstand = ledig]

Beachte: 왘 Die Reihenfolge der Attribute in den Klammern spielt keine Rolle. 왘 Ein Attribut-Bezeichner darf in einem Tupel jeweils nur einmal auftreten. Falls

es für ein Attribut mehrere Werte gibt, ist dann der entsprechende Wert als Menge zu bezeichnen, z. B.: Hobby = {Fahrradfahren, Fremdsprachen, Lesen}. 왘 Wir weisen allerdings hier schon darauf hin, dass in relationalen Datenbanken

die Domänen nicht strukturiert sein dürfen, also insbesondere keine Mengen oder Folgen sein können.4 왘 Zwei Tupel sind dann identisch, wenn sie genau dieselben Attribute mit jeweils denselben Werten haben. Für ein Tupel t und einen Attribut-Bezeichner r ist t.r der Wert des Attributes r in dem Tupel t. So hat in dem obigen Beispiel t.ort den Wert 'Kayhude'. Zwei Tupel r und s sind widersprüchlich, wenn es einen Attribut-Bezeichner a gibt, der sowohl in r als auch in s vorkommt und für den r.a ≠ s.a ist. So sind die Tupel r1 ::= [name='Meyer', vorname='Peter']

3 Nach ISO 8601 gilt folgende Zuordnung der Wochentage: 1 für Montag, 2 für Dienstag ... 7 für Sonntag. 4 Auf die Möglichkeiten, strukturierte Datentypen in objektrelationalen Datenbanken zu definieren, gehen wir in Kapitel 9 ein.

36

Aufbau relationaler Datenbanken

und s1 ::= [name='Meyer', vorname='Hans', ort='Bremerhaven']

widersprüchlich, da r2.vorname ≠ s2.vorname. Zwei Tupel, die nicht widersprüchlich sind, werden als widerspruchsfrei bezeichnet. So sind die Tupel r2 ::= [name='Meyer', vorname='Peter', geburtstag = DATE'1966-10-16']

und

s2 ::= [vorname='Peter', name='Meyer', ort='Bremerhaven']

widerspruchsfrei. Widerspruchsfreie Tupel r und s können zu r+s zusammengefasst werden, wobei r+s genau die Attribute enthält, die in r enthalten sind, und auch die Attribute, die in s enthalten sind. Attribute, die in beiden Tupeln enthalten sind, treten nur einmal auf. Da die Tupel widerspruchsfrei sind, ist der Wert dieser Attribute auch jeweils eindeutig festgelegt. So ist r2+s2 = [name='Meyer', vorname='Peter', geburtstag = DATE'1966-10-16', ort = 'Bremerhaven']

Wenn r und s widersprüchlich sind, ist r+s nicht definiert.

2.1.4 Relationenschema und Relationen Üblicherweise werden Relationen als eine Menge von Tupeln mit denselben Attribut-Bezeichnern eingeführt; im Nachhinein werden als Ausnahme von dieser Regel sogenannte Nullmarken zugelassen. Wir führen dagegen von vornherein Relationen als spezielle Mengen von Tupeln ein, die einem gewissen Relationenschema genügen. Hierbei lassen wir auch zu, dass in einem Tupel gewisse Attribute fehlen können, d.h., für sie ist kein Wert angegeben. Ein Relationenschema S ist durch eine (endliche) Menge A von Bezeichnern (Attribut-Bezeichner) und eine Zuordnung gegeben, die jedem Attribut-Bezeichner a aus A eine Domäne d(a) zuordnet. Wir beschreiben ein Relationenschema durch Aufzählung der Attribut-Bezeichner mit ihren Domänen, die paarweise nebeneinander in einer Klammer aufgezählt werden, z. B.: (kunden_nr name strasse plz ort letzte_bestellung

Kunden_Nummern, Namen, Straßen, DECIMAL(5), Orte, DATE)

37

2 Einführung in das relationale Datenmodell

Als Kurzschreibweise in Zusammenhängen, in denen die Domänen nicht interessieren, verzichten wir auf die Angabe der Domänen und schreiben nur die Attribut-Bezeichner in Klammern. In unserem Beispiel also: (kunden_nr, name, strasse, plz, ort, letzte_bestellung)

Wir gehen davon aus, dass die Reihenfolge der Attribute keine Rolle spielt und die Zugriffe auf die Attribute jeweils über die Attribut-Bezeichner erfolgen. Dieser Begriff einer Relation ist ein anderer als der in der Mathematik übliche Begriff der Relation, besonders wenn es um zweistellige Relationen geht: In der Mathematik werden üblicherweise die Paare als Folgen von zwei Elementen beschrieben: dem ersten und dem zweiten Element. Auch größere Tupel werden als Folgen beschrieben mit einem ersten, zweiten, dritten usw. Element. Das heißt jetzt nicht, dass der von uns gewählte Ansatz mathematisch weniger exakt ist, sondern er ist nur allgemeiner und für das vorgesehene Anwendungsgebiet besser geeignet.5 Ein Tupel t ist kompatibel zu einem Relationenschema S, wenn Folgendes gilt: 1) t enthält nur Attribut-Bezeichner, die auch in S vorkommen. 2) Für jeden Attribut-Bezeichner a von t gilt: Der Wert t.a ist ein Element von d(a). Es ist jedoch zulässig, dass ein Attribut-Bezeichner im Relationenschema in dem Tupel nicht auftritt. In diesem Fall sagen wir auch: »Das Attribut hat eine Nullmarke.« (Vgl. Abschnitt 2.5) So ist z.B. [kunden_nr = 101, name = 'Peter Stein', strasse = 'Moordamm 24', plz = 23863, ort = 'Kayhude']

kompatibel zu dem oben definierten Relationenschema. Dagegen ist [kunden_nr = 101, name = 'Peter Stein', strasse = 'Moordamm 24', plz = 23863, ort = 'Kayhude', telefon = '04535 / 4711']

5 Mathematisch gesehen ist ein Tupel in unserem Sinne eine partielle Abbildung aus der Menge der Attribut-Bezeichner des Relationenschemas in die Vereinigungsmenge der entsprechenden Domänen. Nach dem klassischen Relationenbegriff ist ein Tupel eine Abbildung einer natürlichen Zahl n = {0,1...n–1} in die Vereinigungsmenge der entsprechenden Domänen.

38

Aufbau relationaler Datenbanken

nicht kompatibel zu dem oben definierten Relationenschema, da ein Attribut telefon enthalten ist, das nicht im Relationenschema vorkommt. Das Tupel [kunden_nr = 101, name = 'Peter Stein', strasse = 'Moordamm 24', plz = 23863, ort = 'Kayhude', letzte_bestellung = '12 Möhren']

ist nicht kompatibel zu dem Relationenschema, da der Wert des Attributes letzte_bestellung nicht der vorgegebenen Domäne angehört. Die Unterscheidung kompatibler und nicht kompatibler Tupel im Verhältnis zu einem Relationenschema ist von praktischer Bedeutung für die Konsistenz und Verlässlichkeit einer Datenbank. Man fordert von einem DBMS, dass es den Versuch, ein nicht kompatibles Tupel in ein Relationenschema einzufügen, zurückweist. Das unterscheidet ein Datenbanksystem zum Beispiel von einem Kalkulationsblatt. Tabellen mit Daten kann man in beiden speichern, das Tabellenkalkulationsprogramm lässt aber in jeder Zelle jede Art von Eintrag zu, womit seine Tauglichkeit als Datenbankersatz recht bescheiden ist. Die Möglichkeiten der Schemadefinition haben sich im Verlauf der Entwicklung von DBMS erweitert, sodass die Grenze zwischen kompatiblen und nicht kompatiblen Tupeln immer schärfer und strenger gezogen werden kann. Dennoch sind technisch noch längst nicht alle Möglichkeiten realisiert, die theoretisch wünschenswert wären. Es ist beispielsweise eine Tatsache, dass die Möglichkeiten der Domänendefinition in SQL-Datenbanken bis heute noch nicht befriedigend sind. In Systemen der künstlichen Intelligenz (z. B. in Prolog-Systemen) können Mengen von Tupeln verwaltet werden, in denen jedes Tupel einen anderen Aufbau hat. Dagegen werden in Datenbanksystemen sehr große Mengen von »strukturierten« Daten verwaltet, die aus Gründen der effizienten Verwaltung und der einfachen Formulierung von Abfragen einen gleichartigen Aufbau haben müssen. Wenn das Relationenschema eines Tupels aus dem Zusammenhang bekannt ist, benutzen wir an manchen Stellen eine Notation, welche die Attribut-Bezeichner in der Reihenfolge des Relationenschemas benutzt und nicht explizit hinschreibt – nicht vorhandene Attribute werden gegebenenfalls durch ein Sternchen dargestellt. Das erste Tupel auf Seite Peter_Stein ist dann kürzer zu schreiben als: [101, 'Peter Stein', 'Moordamm 24',

23863, 'Kayhude', *]

Dies ändert nichts an der Tatsache, dass die Reihenfolge der Attribut-Bezeichner irrelevant ist – wir brauchen uns nicht an die Anordnung der Attribute halten, wie wir sie in den obigen Beispielen aufgeschrieben haben; wir müssen dann aber explizit jeweils die Attribut-Bezeichner mitschreiben. Aber wozu – manchmal ist es halt kürzer und einfacher. Allerdings ist ein (impliziter) Bezug auf die Reihen-

39

2 Einführung in das relationale Datenmodell

folge der Attribute nur bei Ad-hoc-Anfragen sinnvoll, da wir dann nicht die Menge der Attribute kennen müssen. Bei gespeicherten Anwendungen ist in jedem Falle davon abzuraten!

Zusammenfassung Eine Relation ist: 왘 eine Menge von Tupeln, 왘 die jeweils kompatibel zum Relationenschema sind. Für zwei Relationen, die zu demselben Relationenschema kompatibel sind, sagen wir auch, sie sind vom selben Typ. Die Eigenschaft einer Relation, eine Menge zu sein, hat zwei wesentliche Konsequenzen: 왘 Es können nicht zwei identische Tupel in einer Menge enthalten sein, d.h., je zwei Tupel müssen sich in mindestens einem Attributwert unterscheiden. 왘 Die Reihenfolge der Tupel spielt keine Rolle.

2.1.5 Datenbankschema und Datenbank Ein Schema für eine relationale Datenbank wird durch folgende Angaben festgelegt: 1) eine Menge von Bezeichnern für Relationen 2) für jede Relation ein Relationenschema 3) für jede Relation weitere Konsistenzbedingungen Wir werden die weiteren Konsistenzbedingungen in den folgenden Kapiteln kennenlernen. Unter anderem gehören dazu: 왘 Die Festlegung von Schlüsselfeldern, welche die Tupel der Relation jeweils eindeutig beschreiben (vgl. den folgenden Abschnitt). 왘 Die Festlegung, dass ein Attribut in den auftretenden Tupeln einen Wert haben muss, also keine Nullmarke besitzen darf. 왘 Die Einschränkung auf die möglichen Beziehungen zwischen den Werten (z. B. liefermenge → bestellmenge). 왘 Einschränkungen, die sich durch Beziehungen auf die Werte anderer Relationen ergeben. Eine relationale Datenbank ist somit durch eine Menge von Relationen gegeben, die jeweils durch das entsprechende Relationenschema bestimmt sind und welche die jeweiligen Integritätsregeln erfüllen.

40

Schlüssel, Primärschlüssel und Fremdschlüssel

2.2 Schlüssel, Primärschlüssel und Fremdschlüssel 2.2.1 Primärschlüssel und Entitätsintegrität Nach der Codd’schen Charakterisierung ist ein Wesensmerkmal von relationalen Datenbanken, dass alle Beziehungen zwischen den Daten über Werte hergestellt werden. Um an einzelne Tupel einer Relation heranzukommen, müssen wir die Tupel eindeutig identifizieren können. Dazu benötigen wir einen Schlüssel. Ein Schlüssel einer Relation ist durch eine Menge von Attributen festgelegt. Bei einem Schlüssel, der aus einem Attribut besteht, darf in einer Relation zu einem bestimmten Zeitpunkt jeder Wert für das Attribut nur einmal auftreten. Bei Schlüsseln, die aus mehreren Attributen bestehen, dürfen die entsprechenden Kombinationen der Attributwerte nur jeweils einmal auftreten. Bei Relationen, die konkrete Entitäten (z. B. Gegenstände, Personen, Dokumente, Ereignisse …) bezeichnen, ist der Schlüssel in der Regel durch den Entitätenschlüssel gegeben (vgl. Kapitel 3) und ergibt sich damit aus fachlichen Gegebenheiten. Da natürliche (oder kulturell vereinbarte) Attribute wie Name und Vorname in der Regel nicht die erforderliche Eindeutigkeit bieten, werden üblicherweise künstliche Schlüssel eingeführt wie Matrikelnummern bei Studierenden, Kundennummern in Geschäftsbeziehungen, Personalnummern in Unternehmen, Kfz-Kennzeichen im Straßenverkehr oder Postleitzahlen in der Postadressierung. Bei Relationen, die Beziehungen zwischen verschiedenen Entitäten bezeichnen, ist der Schlüssel üblicherweise zusammengesetzt. Hier können als Beispiele {bestell_nr, artikel_nr} bei Bestellungen genannt werden. Die Eigenschaft, Schlüssel zu sein, sagt hier aus, dass in einer Bestellung ein Artikel nur einmal auftreten kann. Dagegen ist es zulässig, dass in einer Bestellung mehrere verschiedene Artikel auftreten (sonst würde bestell_nr allein schon einen Schlüssel bilden). Ebenso ist es zulässig, dass ein Artikel in mehreren Bestellungen auftritt (sonst würde die artikel_nr für sich schon einen Schlüssel bilden). Es kann mehrere Schlüssel geben. Einer davon kann als Primärschlüssel ausgezeichnet werden. So können zu einem festen Zeitpunkt z. B. Fahrzeuge durch ihre Motornummer, durch ihre Fahrgestellnummer oder durch ihr Kfz-Kennzeichen eindeutig identifiziert werden (durch Ummeldung oder Einbau eines Austauschmotors können sich allerdings diese Schlüssel nachträglich ändern). Die Bedingung, dass es keine zwei Tupel in einer Relation geben kann, die denselben Primärschlüssel haben, wird als Entitätsintegrität bezeichnet.

41

2 Einführung in das relationale Datenmodell

2.2.2 Fremdschlüssel und referenzielle Integrität Wie wir an anderer Stelle schon gesagt haben, erfolgen im relationalen Datenmodell Verknüpfungen zwischen Relationen über Werte. Es gibt keine vordefinierten Verknüpfungspfade zwischen Relationen. Die Schlüsselwerte, die Beziehungen zwischen verschiedenen Relationen beschreiben, werden als Fremdschlüssel bezeichnet. Ein Fremdschlüssel ist eine Folge von Attributen. Die Werte dieser Attribute beziehen sich jeweils auf die Schlüsselwerte des Primärschlüssels einer anderen (gegebenenfalls derselben) Relation. Die Relation mit dem Primärschlüssel nennen wir Vaterrelation, die mit dem Fremdschlüssel abhängige Relation. Eine Beziehung zwischen einem Tupel der abhängigen Relation und einem Tupel der Vaterrelation wird dargestellt, indem der Schlüsselwert des Tupels in der Vaterrelation in den Fremdschlüssel der abhängigen Relation eingesetzt wird. Falls die Vaterrelation einen zusammengesetzten Schlüssel hat, ist auch der Fremdschlüssel der abhängigen Relation zusammengesetzt (hier müssen wir allerdings eine Reihenfolge der Attribute im Schlüssel sowie im Fremdschlüssel vorgeben). Für eine Beziehung über einen Fremdschlüssel wird allgemein die referenzielle Integrität gefordert, was bedeutet, dass der Wert bzw. die Wertekombination des Fremdschlüssels in der abhängigen Relation auf jeden Fall als Schlüssel in der Vaterrelation enthalten sein muss. Es ist dann ausgeschlossen, dass ein neues Tupel in die abhängige Relation eingefügt wird, für das der Fremdschlüsselwert nicht in der Vaterrelation enthalten ist. Ebenso kann der Schlüsselwert nicht auf einen Wert geändert werden, der in der Vatertabelle nicht vorhanden ist. Ein Beispiel hierzu ist durch die beiden folgenden Relationen gegeben, welche die Bestellungen in einem Versandhandel beschreiben und bei denen kunden_nr in der Relation Bestellung ein Fremdschlüssel auf die Relation Kunde ist. In den entsprechenden Tabellen im Anhang C können Sie sehen, dass die Werte von kunden_nr in der Relation Bestellung jeweils genau ein Tupel in der Relation Kunde beschreiben. Bestellung (bestell_nr, kunden_nr, bestelldatum, lieferdatum, rechnungsbetrag) FK: kunden_nr → Kunde Kunde (kunden_nr, status, name, straße, plz, ort, letzte_bestellung, letzte_werbeaktion)

Primärschlüsselattribute sind jeweils unterstrichen. Durch die Angabe FK: attributliste → Relation geben wir an, dass die angegebene Folge von Attributen einen Fremdschlüssel in der angegebenen Relation darstellt. In den entsprechenden Tabellen in Anhang C können Sie sehen, dass für die Werte von kunden_nr in der Relation Bestellung jeweils genau ein Tupel in der Relation Kunde existiert.

42

Schlüssel, Primärschlüssel und Fremdschlüssel

Umgekehrt sehen wir hier auch, dass ein Fremdschlüssel in einer Relation nicht eindeutig sein muss (wie hier die Kundennummer 103, die in zwei Bestellungen auftritt). Ein Fremdschlüssel ist eben kein Schlüssel, sondern eine Beziehung zu einem Schlüssel in einer fremden Relation. Ein Problem kann aber auftreten, wenn ein Tupel in der Vaterrelation gelöscht oder geändert werden soll, auf das sich ein Attributwert in der abhängigen Relation bezieht (z. B. ein Kundensatz, dessen Kundennummer in einer offenen Rechnung auftritt). Hier gibt es drei mögliche Verhaltensweisen: restricted (nicht zulässig): Ein Tupel mit einer offenen Referenz darf nicht gelöscht werden (z. B. ein Kundensatz, auf den eine Rechnung verweist). Der Wert des referenzierten Schlüssels darf nicht geändert werden. cascade (weitergeben): Wenn ein Tupel mit einer offenen Referenz gelöscht wird, ist die offene Referenz mit zu löschen (z. B. sind beim Löschen einer Bestellung alle Bestellpositionen zu löschen, die sich auf diese Bestellung beziehen). Wenn der Wert des referenzierten Schlüssels geändert wird, wird das Attribut in der abhängigen Tabelle genauso geändert. set null (auf Null setzen)/set default (auf Vorgabewert setzen): Wenn ein Tupel in der Vatertabelle mit einer offenen Referenz gelöscht wird, sind die Verweise auf dieses Tupel auf NULL bzw. auf einen vorher festgelegten Standardwert zu setzen. (Z. B.: Wenn in einem Unternehmen eine Abteilung aufgelöst wird, ist für die betroffenen Mitarbeiter als Abteilung – vorläufig – eine Nullmarke zu setzen.)

2.2.3 Übungsaufgaben Aufgabe 2.1 Beschreiben Sie das sinnvolle Verhalten eines Fremdschlüsselattributs im Zusammenhang mit dem Löschen des entsprechenden Satzes der Vaterrelation anhand folgender Beispiele: a) Relation der Abteilungen mit der Personalnummer des Abteilungsleiters als Fremdschlüssel. b) Relation der studentischen Prüfungsleistungen mit der Matrikelnummer des Studenten als Fremdschlüssel. c) Relation der Bestellpositionen mit der Bestellnummer als Fremdschlüssel. d) Relation der Rechnungen mit der Kundennummer als Fremdschlüssel.

43

2 Einführung in das relationale Datenmodell

2.3 Einführung in die Fallstudie (Versandhandel) In Anhang C ist das Datenbankschema einer Beispieldatenbank Versandhandel dargestellt. Als Schreibweise haben wir die entsprechende CREATE TABLE-Anweisung von SQL verwendet. In Kurzform ist das Schema der Beispieldatenbank wie folgt darzustellen: 왘 Artikel (artikel_nr, mwst, bezeichnung, listenpreis, bestand, mindest-

왘 왘 왘 왘



bestand, verpackung, lagerplatz, kann_wegfallen, bestellvorschlag, nachbestellung, nachbestellmenge) FK: mwst → MWSt_Satz MWSt_Satz (mwst, prozent, beschreibung) Kunde (kunden_nr, status, name, straße, plz, ort, letzte_bestellung, letzte_werbeaktion, zahlungsart) Girokonto (kunden_nr, konto_inhaber, blz, konto_nr) FK: kunden_nr → kunde Bestellung (bestell_nr, kunden_nr, bestelldatum, lieferdatum, rechnungsbetrag) FK: kunden_nr → kunde Bestellposition (bestell_nr, artikel_nr, bestellmenge, liefermenge, gesamtpreis) FK: bestell_nr → Bestellung FK: artikel_nr → Artikel

Hier sind die Primärschlüssel jeweils unterstrichen, Fremdschlüssel sind durch den Zusatz FK mit Angabe der jeweiligen Vaterrelation dargestellt. Wir sehen hier, dass ein Primärschlüssel oder Teil eines Primärschlüssels gleichzeitig Fremdschlüssel sein kann.

2.4 Relationenalgebra In der Relationenalgebra werden aus vorhandenen Relationen neue Relationen erzeugt. In den Abfragesprachen der kommerziell verfügbaren Datenbanksysteme (z. B. in SQL) sind Konstruktionen enthalten, welche die Operationen der Relationenalgebra realisieren. Wir benutzen hier eine kompakte Sprache, die im Gegensatz zu SQL die einzelnen Operationen der Relationenalgebra direkt beschreibt. Diese neuen Relationen können in folgender Weise verwandt werden: 왘 Snapshot Es wird eine neue Relation (im Hauptspeicher oder auf der Platte) angelegt, die aus den aktuell gültigen Daten abgeleitet wird. Auf diese Relation kann anschließend zugegriffen werden. Zwischenzeitliche Änderungen in den benutzten Ursprungsdaten bleiben dabei unberücksichtigt.

44

Relationenalgebra 왘 Datenausgabe

Die erzeugte Relation wird lediglich auf dem Bildschirm, Drucker oder in eine Datei ausgegeben und kann anschließend vom Datenbanksystem nicht mehr verarbeitet werden. 왘 Virtuelle Datensicht Die neue Relation wird nicht sofort angelegt, sondern bei jedem Zugriff auf diese Datensicht wird sie aus den aktuell gültigen Daten erzeugt; diese Relation muss daher auch nicht gespeichert werden. Im Folgenden werden wir die Relationen und abgeleiteten Relationen tabellarisch im Text darstellen. Wir verwenden die Relationen mit den Daten entsprechend dem Anhang C. Da die Anzahl der Attribute aber eine übersichtliche Darstellung im Text nicht mehr erlaubt, werden wir bei der Relation »Artikel« die letzten vier Attribute nicht mehr darstellen. Die Schreibweise lehnt sich an die Darstellung von [HeSa95] an. Wir verwenden folgende Operationen der Relationenalgebra: Sel

für Auswahl (Selektion)

Proj für Projektion Ren

für Umbenennung von Attribut-Bezeichnern

Adj

für die Hinzufügung von virtuellen Spalten

Grp

für Datengruppierung (wird üblicherweise nicht zu den relationalen Operationen gezählt – aber jedes Datenbanksystem hat Operatoren, die auf dieser Operation beruhen)

Join für den natürlichen Verbund zweier Relationen sowie für den äußeren Ver-

bund ×

für das Kreuzprodukt zweier Relationen



für Vereinigungsmenge



für Durchschnittsmenge

\

für Differenzmenge

Die Schreibweise ist jeweils Operator [Spezifikation] (Relation) für die einstelligen Operationen und Relation Operator Relation bzw. Relation Operator[Spezifikation] Relation

45

2 Einführung in das relationale Datenmodell

für die zweistelligen Operationen. Zusätzlich ergänzen wir den Verbund-Operator durch die (eigentlich überflüssige, da als Schnittmenge der Attribute der verknüpften Relationen schon festgelegte) Angabe der Verbindungsattribute. Falls mehrere zweistellige Operatoren enthalten sind, sind gegebenenfalls noch Klammern zu setzen.

2.4.1 Die Auswahl (Selektion) Sei R eine Relation, bedingung sei ein logischer Ausdruck (entsprechend einem booleschen Ausdruck in Programmiersprachen wie Pascal oder Java), der 왘 aus Attribut-Bezeichnern von R (als Variablen), 왘 aus Konstanten, 왘 aus den entsprechenden Operatoren und Funktionen, 왘 aus den Vergleichsoperatoren =, ≠, , ≥, 왘 aus den logischen Operatoren ∧, ∨, ¬, die für und, oder und nicht stehen,

gebildet wird. Durch Sel [bedingung] (R)

wird eine Relation definiert, die genau die Tupel enthält, welche die Bedingung erfüllen (d. h. für die der boolesche Ausdruck den Wert TRUE ergibt). Der einfachste Fall der logischen Bedingung ist der Vergleich eines Attributes mit einer Konstanten der entsprechenden Domäne, z. B.: name = 'Meyer', listenpreis < 100,00 _ ankunft < TIME '12:00' ort ≠ 'Bremen'

Des Weiteren können mehrere Attribute miteinander verglichen werden, z. B.: ankunft < abfahrt listenpreis > einkaufspreis wohnort ≠ arbeitsort

Bedingungen können außerdem mit logischen Operatoren verknüpft werden, z. B.: wohnort = 'Bremen' ∨ arbeitsort = 'Bremen' name = 'Matthiessen' ∧ vorname = 'Günter'

46

Relationenalgebra

Schließlich können in die Vergleiche auch berechnete Ausdrücke eingehen, wie z. B.: ankunft + INTERVAL '8' MINUTE < abfahrt

oder länge ≥ 1,41 * breite ∧ länge ≤ 235 mm ∧ breite ≤ 125 mm ∧ höhe ≤ 5 mm ∧ gewicht ≤ 20 g ∧ länge ≥ 140 mm ∧ breite ≥ 90 mm

Beispiel Relation, welche die Artikel enthält, die nachbestellt werden müssen: Sel[bestand < mindestbestand] (Artikel) Artikel_ Nr MwSt.- Bezeichnung Satz

Listen- Bestand preis

Mindest- Verpackung Lagerbestand platz

K003

65,70

20

Karton

2

2

Damenhut

12

L002

1

China-Tee

8,35

42

50

125 g

5

L005

2

Butterkekse

1,72

234

250

250 g

2

Beispiel Relation, welche die Kunden aus dem PLZ-Bezirk 23863 sowie die Kunden aus Husum enthält: Sel[plz = '23863' ∨ ort = 'Husum'] (Kunde) Kunden_ Status Nr

Name

Straße

PLZ

Ort

102

Berger, Uwe

Allee 12 b

25813

Husum

W

100

S

Voss, Hans

Kuhdamm 12

23863

Nienwohld

101

S

Stein, Peter

Moordamm 34

23863

Kayhude

106

W

Staack, Hannes

An der Alster 1

23863

Kayhude

Letzte_ Bestellung

… … …

28.04.2008

… …

Eine Selektion besteht also darin, dass in einer Relation einige Zeilen gestrichen werden und nur die übrig bleiben, welche die angegebene Bedingung erfüllen:

47

2 Einführung in das relationale Datenmodell Kunden_ Status Nr

Name

Straße

PLZ

Ort

Letzte_ Bestellung



102

W

Berger, Uwe

Allee 12 b

25813

Husum



104

G

Andresen, Ute

Am Abhang

24558

Ulzburg



100

S

Voss, Hans

Kuhdamm 12

23863

Nienwohld



101

S

Stein, Peter

Moordamm 34

23863

Kayhude

103

S

Randers, Nis

Am Seeufer 12

23845

105

S

Stuff, Werner

Tarper Weg

24853

106

W

Staack, Hannes An der Alster 1

23863

Kayhude

28.04.2008



Oering

15.05.2008



Eggebek

12.05.2008

… …

Im Spezialfall dient die Selektion auch dazu, einen einzelnen Satz zu selektieren, indem ein eindeutiges Merkmal angegeben wird (z. B. der Schlüssel). Falls dieses Merkmal dann nicht in der Relation vorhanden ist, erhalten wir als Ergebnis die leere Relation, die also nur aus den Spaltenüberschriften besteht.

Beispiel Relation, die genau das Tupel mit den Kundendaten 103 enthält. Sel[kunden_nr = 103] (Kunde) Kunden_ Status Nr

Name

Straße

PLZ

Ort

Letzte_ Bestellung



103 103

Randers, Nis

Am Seeufer 12

23845

Oering

15.05.2008



S

Beispiel Relation, die genau das Tupel mit den Kundendaten des Kunden 128 enthält. Sel[kunden_nr = 128] (Kunde) Kunden_ Nr

Status

Name

Straße

PLZ

Ort

Letzte_ Bestellung



Es handelt sich hierbei um eine leere Relation, die aussagt, dass kein Tupel die geforderte Bedingung erfüllt.

Grafische Darstellung der Operationen der Relationenalgebra Das Wort »Algebra« in »Relationenalgebra« bezieht sich darauf, dass wir verschiedene Operationen der Relationenalgebra verknüpfen können, um damit auf eine Relation zu kommen, welche die gewünschten Daten enthält. Genauso wie wir Zahlen addieren, subtrahieren, multiplizieren und – mit Einschränkung – dividie-

48

Relationenalgebra

ren und daraus Ausdrücke wie (x+1)*(y-1) erstellen können, so können wir mehrere Operationen der Relationenalgebra nacheinander ausführen. Allerdings sind unsere Operationen wie sel[kunden_nr=103] doch unhandlicher als etwa die Symbole für Addition und Multiplikation. Eine für den Menschen übersichtlichere Darstellung bekommen wir, indem wir die Operationen grafisch darstellen. Die Hintereinanderausführung verschiedener Operationen kann dann unmittelbar grafisch erkannt werden. Relationen (in der Datenbank gespeicherte oder abgeleitete) stellen wir in einem Rechteck mit abgerundeten Ecken dar. Ein Operator wird in ein Rechteck geschrieben: In der ersten Zeile steht der Name des Operators, in den folgenden Zeilen die Parameter (wie z. B. die Selektionsbedingung). Das Argument des Operators steht in der Zeichnung unter dem Operator und wird mit diesem durch eine Linie verbunden; das Argument kann selber wieder das Ergebnis einer Operation sein. In diesem Fall wird der Operator mit dem Operator des Ergebnisses verbunden. Für mehrstellige Operatoren stehen die Operanden von links nach rechts unter dem Operator und werden jeweils mit dem Operator verbunden. sel ort = 'Kayhude'

Kunde Abbildung 2.2: Selektion

2.4.2 Die Projektion Sei R eine Relation und {r1,…,rm} eine nicht leere Teilmenge der Attribut-Bezeichner von R. Für jedes Tupel t∈ R ist dann ein neues Tupel t[r1,…,rm] gegeben, das nur die Attribute mit den Bezeichnern r1,…,rm hat. Wenn z. B. t=[kunden_nr = 101,name = 'Peter Stein', strasse = 'Moordamm 24',plz = 23863,ort = 'Kayhude']

Dann ist t[plz,ort] das Tupel [plz = 23863, ort = 'Kayhude']

Die Projektion Proj[r1,…,rm] (R)

49

2 Einführung in das relationale Datenmodell

ist die Relation mit den Attribut-Bezeichnern {r1,…,rm}, welche die Tupel t[r1,…,rm] mit t ∈ R enthält. Es wird also nur die Information der angegebenen Attribute betrachtet, die übrigen Attribute werden ignoriert. Dadurch können Tupel der Relation R zusammenfallen – wenn sie nämlich in allen Attributen r1,… ,rm übereinstimmen, sich aber in mindestens einem Attribut, das nicht in der Projektion auftritt, unterscheiden.

Beispiel Relation, die für jeden Artikel neben der Artikelnummer und der Bezeichnung den Lagerplatz und den Bestand angibt (Bestandsliste). Proj[artikel_nr, bezeichnung, lagerplatz, bestand] (Artikel)

proj artikel_nr, bezeichnung, lagerplatz, bestand

Artik el Abbildung 2.3: Projektion

Artikel_ Nr

Bezeichnung

Lagerplatz

Bestand

G001

Whisky

7

397

G002

Portwein

7

473

G003

Bier

7

1250

K001

Schuhe

2

120

K002

Hose

2

62

K003

Damenhut

2

12

K004

Sonnenbrille

2

50

L001

Ceylon-Tee

5

356

L002

China-Tee

5

42

L003

Naturreis

4

345

L004

Schokolade

2

2101

L005

Butterkekse

2

234

50

Relationenalgebra

Beispiel Relation, die alle Orte mit PLZ enthält, in denen Kunden wohnen. Hierbei tauchen Orte mit mehreren Kunden nur einmal auf. Proj[plz, ort] (Kunde) PLZ

Ort

23845

Oering

23863

Kayhude

23863

Nienwohld

24558

Ulzburg

24853

Eggebek

25813

Husum

Die Projektion besteht also darin, dass gewisse Spalten ausgeblendet werden; nur die angegebenen Spalten bleiben übrig. Dabei kann es als Folgeaktion notwendig werden, bei mehrfach auftretenden identischen Zeilen jeweils alle bis auf eine zu streichen, da in einer Relation kein Tupel doppelt auftreten kann (vgl. 2.1.4). Kunden_ Nr

Status

Name

Straße

PLZ

Ort

102

W

Berger, Uwe

Allee 12 b

25813

Husum

104

G

Andresen, Ute

Am Abhang

24558

Ulzburg



100

S

Voss, Hans

Kuhdamm 12

23863

Nienwohld



101

S

Stein, Peter

Moordamm 34

23863

Kayhude

28.04.2008



103

S

Randers, Nis

Am Seeufer 12

23845

Oering

15.05.2008



Tarper Weg

24853

Eggebek

12.05.2008

23863

Kayhude

105

S

Stuff, Werner

106

W

Staack, Hannes An der Alster 1

Letzte_ Bestellung

… …

… …

2.4.3 Umbenennung von Attribut-Bezeichnern Die Umbenennung von Attributen ist in folgenden Fällen notwendig: Änderung der Überschrift einer auszugebenden Tabelle Zwei Tabellen sollen miteinander verknüpft werden (wird in Kapitel 2.4.6 eingeführt). Dazu müssen die korrespondierenden Attribute in den beiden Relationen jeweils denselben Bezeichner haben.

51

2 Einführung in das relationale Datenmodell

Zwei Tabellen sollen miteinander verknüpft werden. Dazu müssen die nicht korrespondierenden Attribute in den beiden Relationen jeweils unterschiedliche Bezeichner haben. Wenn a ein Attribut einer Relation R ist und b ein Bezeichner, der nicht als AttributBezeichner von R auftritt, wird durch Ren[b ::= a] (R)

eine Relation definiert, in der das Attribut a jetzt b heißt. Falls mehrere Umbenennungen erfolgen, schreiben wir diese hintereinander in eckige Klammern, wie im folgenden Beispiel. Ren [KNr ::= kunden_nr, St ::= status, LBest ::= letzte_bestellung, LWerb ::= letzte_Werbung, ZArt := Zahlungsart] (Kunde)

ren KNr ::= kunden_nr, St ::= status, LBest ::= letzte_bestellung, LWerb ::= letzte_Werbung, ZArt := Zahlungsart

Kunde Abbildung 2.4: Umbenennung von Attribut-Bezeichnern KNr

St

Name

Straße

PLZ

Ort

102

W

Berger, Uwe

Allee 12 b

25813 Husum

LBest

LWerb

ZArt

01.12.2007

N

01.12.2007

N

01.12.2007

B

104

G

Andresen, Ute Am Abhang

100

S

Voss, Hans

Kuhdamm 12 23863 Nienwohld

101

S

Stein, Peter

Moordamm 34 23863 Kayhude

28.04.2008

103

S

Randers, Nis

Am Seeufer 12 23845 Oering

15.05.2008

B

105

S

Stuff, Werner Tarper Weg

12.05.2008

R

106

W

Staack, Hannes An der Alster 1 23863 Kayhude

52

24558 Ulzburg

24853 Eggebek

N

01.12.2007

N

Relationenalgebra

Da eine Umbenennung häufig im Zusammenhang mit einer Projektion erfolgt, schreiben wir in Zukunft als Kurzschreibweise die umbenannten Attribute in die Liste der Projektionsattribute mit hinein. Die umbenannten Attribute sind grundsätzlich mit in die Liste der zu projizierenden Attribute einzubeziehen (sonst bräuchten wir sie ja nicht erst umzubenennen). So wird Proj[ArtNr,Bezeichnung,Lager,Ist] ( Ren[ArtNr::=artikel_nr, lager::=Lagerplatz, ist::=bestand] (Artikel) )

zu: Proj[ArtNr::=artikel_nr, bezeichnung, lager::=lagerplatz, Ist::= bestand] (Artikel)

proj ArtNr, Bezeichnung, Lager, Ist

proj/ren ArtNr::=artikel_nr, Bezeichnung, Lager::=Lagerplatz, Ist::=bestand

ren ArtNr::=artikel_nr, Lager::=Lagerplatz, Ist::=bestand

Artikel

Artikel

Abbildung 2.5: Projektion und Umbenennung

ArtNr

Bezeichnung

Lager

Ist

G001

Whisky

7

397

G002

Portwein

7

473

G003

Bier

7

1250

K001

Schuhe

2

120

K002

Hose

2

62

K003

Damenhut

2

12

K004

Sonnenbrille

2

50

53

2 Einführung in das relationale Datenmodell

ArtNr

Bezeichnung

Lager

Ist

L001

Ceylon-Tee

5

356

L002

China-Tee

5

42

L003

Naturreis

4

345

L004

Schokolade

2

2101

L005

Butterkekse

2

234

Die Zusammenfassung von Umbenennung und Projektion macht auch aus einem anderen Grund Sinn:

2.4.4 Adjunktion virtueller Spalten Sei R eine Relation und sei a ein Ausdruck, der aus 왘 Attribut-Bezeichnern der Relationen und 왘 Operationen, die für die Domänen definiert sind, sowie 왘 Konstanten

gebildet ist, und s sei ein Bezeichner, der nicht als Attribut-Bezeichner in R auftritt; dann wird durch Adj[s ::= a] (R)

eine Relation definiert, die eine zusätzliche Spalte enthält, in der als Wert jeweils die Auswertung von a enthalten ist. Diese Spalte wird als virtuelle Spalte bezeichnet. Für ein Tupel t hängt also der Wert von s von den Werten der (echten) Attribute ab. Üblicherweise wird daher der Wert von s nicht mit in der Datenbank gespeichert, sondern bei jedem Aufruf neu berechnet. Auch hier übernehmen wir üblicherweise die adjungierten Spalten mit in die Liste der Projektionsattribute (vgl. hierzu das folgende Beispiel).

Beispiel Relation, die für jeden Artikel die angegebenen Attribute sowie das Produkt aus Bestand und Listenpreis enthält. Proj[artnr, bezeichnung, bestand, listenpreis, wert] ( Ren[artnr ::= artikel_nr] ( Adj[wert ::= bestand * listenpreis] (Artikel) ) )

oder kürzer:

54

Relationenalgebra

Proj[artNr ::= artikel_nr, bezeichnung, bestand, listenpreis, wert ::= bestand * listenpreis] (Artikel) Artikel_ Nr

Bezeichnung

G001

Whisky

G002

Portwein

G003

Bier

K001

Schuhe

K002

Hose

Bestand

Listenpreis

Wert

397

38,40

15.244,80

473

12,45

5.888,85

1250

5,20

6.500,00

120

98,50

11.820,00

62

112,80

6.993,60

K003

Damenhut

12

65,70

788,40

K004

Sonnenbrille

50

76,00

3.800,00

L001

Ceylon-Tee

356

6,35

2.260,60

L002

China-Tee

42

8,35

350,70

L003

Naturreis

345

1,78

614,10

L004

Schokolade

2101

0,98

2.058,98

L005

Butterkekse

234

1,72

402,48

2.4.5 Gruppierung von Daten Wie wir gesehen haben, werden bei der Projektion gegebenenfalls mehrere Tupel jeweils zusammengefasst, wenn sie in den Projektionsattributen identisch sind, sich aber in anderen Attributen noch unterscheiden. Hier bilden die Tupel, die in den Projektionsattributen identische Werte haben, jeweils eine Datengruppe, wobei die Werte in den nicht projizierten Attributen unterschiedlich sein können. Häufig möchten wir für diese Datengruppen weitere Werte haben, die sich aus den Tupeln der jeweiligen Gruppen berechnen lassen. Im einfachsten Fall möchten wir nur die Anzahl der Tupel der jeweiligen Gruppen haben. Des Weiteren möchten wir z. B. die Summen der Artikelbestände in den einzelnen Lagern wissen. In diesen Fällen verwenden wir statt der Projektion die Datengruppierung. Zusätzlich zu den zu projizierenden Attributen hat die neue Relation weitere adjungierte Attribute, die durch die Anwendung von Aggregatfunktionen auf die Attribute, die nicht in der Projektion auftreten, entstehen. Formal schreiben wir die Anwendung der Aggregatfunktionen hinter die Projektionsattribute durch einen senkrechten Strich getrennt. Grp[plz,ort| anzahl::=COUNT(*)] (Kunde)

55

2 Einführung in das relationale Datenmodell

grp plz, ort, -----------------------------anzahl ::= COUNT(*)

Kunde Abbildung 2.6: Gruppierung

PLZ

Ort

Anzahl

23845

Oering

1

23863

Kayhude

2

23863

Nienwohld

1

24558

Ulzburg

1

24853

Eggebek

1

25813

Husum

1

Die Aggregatfunktion COUNT(*) ergibt für jede Datengruppe die Anzahl ihrer Tupel. Die weiteren Aggregatfunktionen haben jeweils eine Spalte oder virtuelle Spalte als Argument. Als Aggregatfunktionen können beliebige Funktionen auftreten, die Mengen von Werten als Argumente annehmen. In SQL sind das neben COUNT(*) folgende Aggregatfunktionen, die als Argument jeweils ein Attribut annehmen (z. B. SUM(Bestand)): COUNT COUNT DISTINCT SUM AVG MIN MAX

Anzahl definierter Werte Anzahl verschiedener Werte Summe Average, Durchschnitt Minimum Maximum

Ab SQL:2003 sind weitere statistische Funktionen wie Standardabweichung, Varianz, Perzentil (z. B. Median), Rang in Abhängigkeit von einer vorgegebenen Sortierung definiert.

56

Relationenalgebra

Beispiel Die folgende Relation gibt für jeden Lagerplatz die Summe der Artikelbestände an. Grp[lagerplatz | gesamtbestand ::= SUM(bestand)] (Artikel) Lagerplatz

Gesamtbestand

2

2579

4

345

5

398

7

2120

Im nächsten Beispiel wird eine Selektion nach einer Gruppierung durchgeführt. Die Daten werden also zuerst gruppiert, und anschließend werden die Gruppen ausgesucht, die ein bestimmtes Kriterium erfüllen.

Beispiel Relation aller Lagerplätze, die einen Bestand von höchstens 1000 haben: Sel[gesamtbestand ≤ 1000] (Grp[lagerplatz| gesamtbestand::=SUM(bestand)] (Artikel)) Lagerplatz

Gesamtbestand

4

345

5

398

Bei diesem Beispiel ist zu beachten, dass hier die Selektion nach der Gruppierung durchzuführen ist und sich auf Gruppen bezieht. Eine Selektion vor der Gruppierung wäre hier nicht möglich, da ja dem einzelnen Satz nicht anzusehen ist, ob er in einer Gruppe auftritt, deren Gesamtbestand nicht größer als 1000 ist.

Beispiel Im folgenden Beispiel tritt sowohl eine Selektion auf Tupel-Ebene als auch eine Selektion auf Gruppenebene auf. Es werden nur die selektierten Tupel in die Gruppe aufgenommen und anschließend die relevanten Gruppen ausgewählt: Wir wollen eine Liste der Lager mit dem jeweiligen Gesamtbestand haben, wobei wir uns nur für Artikel interessieren, die für mindestens 5,00 € verkauft werden, und nur für Lager, in denen der Gesamtbestand dieser Artikel höchstens 1000 ist.

57

2 Einführung in das relationale Datenmodell

sel gesamtbestand

1000

grp lagerplatz -----------------------------gesamtbestand ::= SUM(bestand)

Artikel Abbildung 2.7: Selektion nach Gruppierung

Sel[gesamtbestand ≤ 1000]( Grp[lagerplatz | gesamtbestand ::= SUM(bestand)] ( Sel[listenpreis ≥ 5.00] (Artikel) ) ) sel gesamtbestand

1000

grp lagerplatz -----------------------------gesamtbestand ::= SUM(bestand)

sel listenpreis

5.00

Artikel

Abbildung 2.8: Selektion nach Gruppierung nach Selektion

58

Relationenalgebra

Lagerplatz

Gesamtbestand

2

244

5

398

Ein Sonderfall der Datengruppierung tritt auf, wenn kein Gruppierungsattribut angegeben wird, sondern nur Aggregatfunktionen. In diesem Fall werden die Aggregatfunktionen auf die gesamte Tabelle angewandt. So eine Relation hat naturgemäß nur ein Tupel. Grp[|

gesamtwert maxbestand minpreis sortiment

::= ::= ::= ::=

SUM(bestand * listenpreis), MAX(bestand), MIN(listenpreis), COUNT(artikel_nr)] (Artikel)

Gesamtwert

MaxBestand

MinPreis

Sortiment

56.722,51

2101

0,98

12

In diesem Fall können wir Varianten der Aggregatfunktionen definieren, die als Ergebnis keine Relation liefern, sondern einen Wert. So ist: SUM(bestand * listenpreis;Artikel) = 56722,51 MAX(bestand; artikel) = 2101 MIN(listenpreis; artikel) = 0,98 COUNT(artikel_nr; artikel) = 12

Ein weiterer Sonderfall ist gegeben, wenn keine Aggregatfunktion verwendet wird. In diesem Fall ist die Datengruppierung nichts anderes als die normale Projektion.6

2.4.6 Der Verbund (Join) Der Verbund ist eine sehr wichtige Operation im relationalen Datenbankmodell, da durch ihn die Möglichkeit gegeben ist, mehrere Relationen miteinander zu verknüpfen. In dieser abstrakten Darstellung des relationalen Datenmodells betrachten wir in erster Linie den sog. natürlichen Verbund (engl. natural join). Hieraus können wir den allgemeineren Gleichheitsverbund (equi join) erhalten, indem wir gegebenenfalls vorher die entsprechenden Attribute umbenennen. Den noch allgemeineren Verbund (?-Join im Sinne von Codd, wobei der griechische Buchstabe Theta für eine beliebige zweistellige Operation wie =, , ≥ steht) behandeln wir hier nicht weiter, da er als Selektion auf dem Kreuzprodukt dargestellt werden kann und außerdem in der Praxis nur eine geringe Rolle spielt. 6 So ist in dem relationalen Datenbanksystem Microsoft Access in einer Abfrage eine Projektion formal als Gruppierung zu beschreiben.

59

2 Einführung in das relationale Datenmodell

Wir merken hier an, dass der natürliche Verbund, wie wir ihn hier einführen, für die theoretische Definition der Relationenalgebra hervorragend geeignet ist, da er einfacher zu definieren ist als die anderen Verbundoperationen. In der Praxis hat der natürliche Verbund allerdings einen sehr großen Nachteil: Er ist empfindlich gegen nachträgliche Änderungen des Datenbankschemas. Da sich die Verknüpfung auf alle Attribut-Bezeichner bezieht, die (absichtlich oder zufälligerweise) in beiden Relationen auftreten, kann durch die Einführung eines neuen Attributs (z. B. mit der Bezeichnung Bemerkung), das eigentlich mit der geplanten Verknüpfung nichts zu tun hat, das Ergebnis völlig geändert werden: Es werden dann keine Tupel mehr verknüpft, da z. B. die Bemerkungen in der Relation Bestellung nicht mit den Bemerkungen in der Relation Kunden übereinstimmen. Folgende Voraussetzungen müssen gegeben sein, damit ein Verbund definiert werden kann: 1) R und S sind Relationen. 2) a1,… an sind genau die Attribut-Bezeichner, die in R und S vorkommen (also Durchschnittsmenge der Attribut-Bezeichner). 3) Für i = 1..n sind die Domänen von ai in R und in S jeweils widerspruchsfrei, d.h., sie sind identisch, oder es ist zumindest möglich, Werte dieser entsprechenden Domänen auf Gleichheit zu überprüfen. 4) Die übrigen Attribut-Bezeichner von R und S sind jeweils verschieden. Die Bedingungen 2 und 4 sind gegebenenfalls durch Umbenennungen von Attributen zu erzwingen. An dieser Stelle ist die Operation der Umbenennung essenziell.7 Es ist dann R Join[a1,…an] S

eine neue Relation, die wie folgt definiert ist: 1) Die Attribute von R Join[a1,… an] S sind genau die Attribute von R und S. 2) Die Verbindungsattribute a1,… an werden also genau einmal gezählt. 3) R Join[a1,… an]S enthält für alle Paare von Tupeln von r∈R und s∈S, die für alle Attribute von a1,… an definiert sind und jeweils identische Werte besitzen, jeweils das Tupel r+s.

7 In XBase fehlt diese Operation. Die JOIN-Anweisung enthält zwar die Möglichkeit, entsprechend Punkt 2 die Verbindungsbedingung zu formulieren, aber gleichnamige Feld-Bezeichner können nicht umbenannt werden. Somit sind die Operationen der Relationenalgebra in XBase nicht vollständig formulierbar (außer durch explizite Programmierung mit verschachtelten Programmschleifen – das können wir aber auch in jeder »normalen« Programmiersprache).

60

Relationenalgebra

4) Da nach der Voraussetzung für zu verknüpfende r und s alle Attribute von a1,… an identische Werte besitzen, sind r und s widerspruchsfrei8, und somit ist r+s definiert. Ein Tupel r∈R kann mit einem Tupel s∈S, mit mehreren Tupeln s∈S oder mit keinem Tupel s∈S verbunden werden.

Beispiel Bestellung Join[kunden_nr] Kunde join kunden_nr

Bestellung

Kunde

Abbildung 2.9: Natürlicher Verbund zweier Relationen Bestell_ BestellNr datum

Lieferdatum

Rechnungs- Kunden_ Status betrag Nr

Name

Straße

151

28.04.2008

02.05.2008

200,67 101

S

Stein, Peter



152

30.04.2008

02.05.2008

2.304,36 103

S

Randers, Nis …

153

12.05.2008

105

S

Stuff, Werner

154

15.05.2008

103

S

Randers, Nis …



Diese Relation enthält für jedes Tupel der Relation Bestellung die Angaben (bestell_nr, bestelldatum, lieferdatum, rechnungsbetrag) sowie alle Kundendaten für die entsprechenden Kunden. Falls allerdings eine in der Relation Bestellung angegebene Kundennummer in der Relation Kunde nicht enthalten ist, wird diese Bestellung – ohne Warnung – ignoriert. (Dies kann nicht auftreten, da wir als Konsistenzbedingung eine referenzielle Konsistenz angegeben haben – hier sehen Sie auch, warum diese referenzielle Konsistenz wichtig ist.) Grafisch können wir diese Verknüpfung auch in folgender Form darstellen, indem wir die zu verknüpfenden Attribute durch eine Linie verbinden:

8 Vgl. Abschnitt 2.1.3.

61

2 Einführung in das relationale Datenmodell

Bestell_ Nr

Kunden_ Nr

Bestelldatum

Lieferdatum

Rechnungsbetrag

151

101

28.04.2008

02.05.2008

200,67

152

103

30.04.2008

02.05.2008

2.304,36

153

105

12.05.2008

154

103

15.05.2008

= Kunden_ Nr

Status

Name

Straße

PLZ

Ort

Letzte_ Bestellung



102

W

Berger, Uwe

Allee 12 b

25813

Husum



104

G

Andresen, Ute

Am Abhang

24558

Ulzburg



100

S

Voss, Hans

Kuhdamm 12

23863

Nienwohld



101

S

Stein, Peter

Moordamm 34

23863

Kayhude

28.04.2008



103

S

Randers, Nis

Am Seeufer 12 23845

Oering

15.05.2008



105

S

Stuff, Werner Tarper Weg

24853

Eggebek

12.05.2008



106

W

Staack, Hannes

An der Alster 1 23863

Kayhude



Beispiel (Self Join) Verbindungen müssen nicht immer über Schlüsselattribute hergestellt werden. Durch entsprechende Umbenennung können auch Relationen mit sich selbst verknüpft werden. Hierzu sei die folgende Relation Kaiser gegeben: Kaiser Name

Beiname

Karl I

der Große

Ludwig I

der Fromme

Lothar I

Vater

Todesjahr 814

Karl I

840

Ludwig I

855

Ludwig II

der Deutsche

Ludwig I

876

Karl II

der Kahle

Ludwig I

877

Ludwig II

880

Ludwig II

882

Karlmann Ludwig III

62

der Jüngere

Relationenalgebra

Name

Beiname

Vater

Todesjahr

Karl III

Ludwig II

888

Arnulf

Karlmann

899

Arnulf

911

Ludwig IV

das Kind

Durch Proj[a_name::=name, vater] (Kaiser) Join[Vater] Proj[b_name::=name, vater] (Kaiser)

bekommen wir die Relation der Brüder – wobei hier allerdings noch jeder, der einen Vater hat, als sein eigener Bruder gilt, d.h., Tupel der Form [a_name='Ludwig I', b_name='Ludwig I', vater='Karl I']

sind zugelassen. Um diese auszuschließen, müssen wir anschließend eine Selektion vornehmen: Sel[a_Name ≠ b_Name] ( Proj[a_name::=name, vater] (Kaiser) Join[vater] Proj[b_name::=name, vater] (Kaiser) )

sel a_name b_name

join vater

proj a_name::=name, vater

Kaiser

proj b_name::=name, vater

Kaiser

Abbildung 2.10: Self Join

63

2 Einführung in das relationale Datenmodell

A_Name

Vater

B_Name

Karl II

Ludwig I

Ludwig II

Karl II

Ludwig I

Lothar I

Lothar I

Ludwig I

Karl II

Lothar I

Ludwig I

Ludwig II

Ludwig II

Ludwig I

Karl II

Ludwig II

Ludwig I

Lothar I

Karl III

Ludwig II

Ludwig III

Karl III

Ludwig II

Karlmann

Karlmann

Ludwig II

Karl III

Karlmann

Ludwig II

Ludwig III

Ludwig III

Ludwig II

Karl III

Ludwig III

Ludwig II

Karlmann

Die Verbundoperation ist kommutativ, d.h., es kommt nicht auf die Reihenfolge der Relationen an – beide Relationen in einem Verbund sind vollständig gleichberechtigt. Es gilt grundsätzlich: R Join[rs] S

= S Join[rs] R

join

join

rs

rs

=

R

S

S

R

Abbildung 2.11: Kommunikativität des Join

Die Verbundoperation ist assoziativ, d.h., für drei Relationen R, S, T mit den entsprechenden Mengen von Verbindungsattributen rs und st gilt stets: (R Join[rs] S) Join[st] T = R Join[rs] (S Join[st] T )

Wir können somit vollständig auf die Angabe von Klammern verzichten und den letzteren Ausdruck durch R Join[rs] S Join[st] T

darstellen.

64

Relationenalgebra

join

join

st

join

T

rs

R

rs

=

join

R

S

st

S

T

Abbildung 2.12: Assoziativität des Join

In der grafischen Darstellung auf Seite 68 bedeutet das, dass auch mehrere Relationen verknüpft werden können und es nur auf die Existenz der Linien ankommt – nicht auf eine irgendwie geartete Pfeilrichtung oder Nummerierung der Verbindungen. In der grafischen Darstellung der Funktionenverknüpfung können wir allerdings diese Gleichheit nicht unmittelbar durch eine »neutrale« Klammerung ersetzen. Die Gleichheit von Ausdrücken sagt, dass das Ergebnis unabhängig von der Reihenfolge der Ausführung ist. Andererseits stellen die Ausdrücke einen Ablauf dar, um auf das Ergebnis zu kommen. Die verschiedenen Grafiken sagen dann aus, dass es mehrere mögliche Abläufe gibt, um auf dasselbe Ergebnis zu kommen. Hier setzt der Query Optimizer eines SQL-Systems an: In SQL9 wird das gewünschte Ergebnis beschrieben; einen (möglichst optimalen) Ablaufplan, um zu diesem Ergebnis zu kommen, erzeugt der Query Optimizer. In vielen SQL-Systemen kann man sich den Ablaufplan für eine Abfrage ansehen. Sybase verwendet zur Darstellung dieser Ablaufpläne eine grafische Darstellung, die an die hier gezeigten Funktionsbäume angelehnt ist. Es stehen darüber hinaus weitere Informationen zur Verfügung, wenn man die entsprechenden Kästchen anklickt.

Beispiel Die Liste aller Artikel aus Lager 2, die von Kunden aus Kayhude geordert wurden, bekommen wir unter anderem durch die folgenden Ausdrücke der Relationenalgebra. Artikel_nr

Bezeichnung

K002

Hose

K003

Damenhut

9 Vgl. Kapitel 5.6

65

2 Einführung in das relationale Datenmodell

proj artikel_nr, bezeichung

sel lagerplatz = 2 ∧ ort = 'Kayhude'

join artikel_nr

join bestell_nr

join kunden_nr

Kunde

Artikel

Bestellposition

Bestellung

Abbildung 2.13: Alle von Kayhudern bestellten Artikel aus Lager 2 – Variante 1

Eine zweite Variante für den Ausdruck wird in der folgenden Abbildung dargestellt. Alle beiden Ausdrücke liefern dasselbe Ergebnis – allerdings ist der Rechenaufwand unterschiedlich. Am höchsten ist er für die erste Formulierung, da hier erst alle Tupel verknüpft werden und die Selektion am Schluss erfolgt. Es gibt weitere Ausdrücke, die dasselbe Ergebnis liefern, so könnte man die Reihenfolge vertauschen und bei den Artikeln statt bei den Kunden anfangen. Oder man könnte die Bestellpositionen mit den Artikeln verknüpfen, ebenfalls die Kunden mit den Bestellungen und dann diese beiden Ergebnisse verknüpfen.

66

Relationenalgebra

proj artikel_nr, bezeichung

join artikel_nr

join bestell_nr

join kunden_nr

sel ort ='Kayhude'

sel lagerplatz = 2

Bestellposition

Artikel

Bestellung

Kunde

Abbildung 2.14: Alle von Kayhudern bestellten Artikel aus Lager 2 – Variante 2

Dieses Beispiel zeigt auch, dass ein Datenbanksystem sinnvollerweise einen Abfrage-Optimierer benötigt. Dieser soll in der Lage sein, eine gegebene Abfrage so in eine äquivalente Abfrage (d. h. eine Abfrage mit derselben Ergebnismenge) umzuformen, dass der Rechenaufwand möglichst gering wird. Hierzu stehen dem Abfrage-Optimierer Informationen über die physikalische Speicherorganisation sowie über die Anzahl der Tupel in den beteiligten Relationen und gegebenenfalls Erfahrungen aus vergangenen Abfragen zur Verfügung. Meistens sind die automatisch erzeugten Abfragepläne relativ gut – bei komplexen Anfragen kann unter Umständen ein manuelles Tuning überlegen sein.

2.4.7 Das Kreuzprodukt In der Datenbankpraxis spielt das Kreuzprodukt nur eine geringe Rolle – es ist aber im ursprünglichen Codd'schen Modell vor dem Verbund eingeführt worden, da das Kreuzprodukt mathematisch einfacher ist als der Verbund. Der Verbund

67

2 Einführung in das relationale Datenmodell

kann als Selektion auf dem Kreuzprodukt definiert werden. Umgekehrt ist das Kreuzprodukt ein Spezialfall des natürlichen Verbundes: wenn nämlich die Menge der Verbindungsattribute leer ist, d. h.: R X S = R Join[ ] S.

Die Voraussetzung für das Kreuzprodukt ist also, dass die Attribut-Bezeichner der beteiligten Relationen jeweils verschieden sind – gegebenenfalls sind sie vorher umzubenennen.

Beispiel Es seien die beiden Relationen Werktag und Unterrichtsblock gegeben. Werktag TagNr

TagName

1

Montag

2

Dienstag

3

Mittwoch

4

Donnerstag

5

Freitag

Unterrichtsblock BlockNr

Anfang

Ende

1

08:00

09:30

2

09:45

11:15

3

11:30

13:00

4

13:45

15:15

5

15:30

17:00

6

17:15

18:45

Werktag X Unterrichtsblock TagNr

TagName

BlockNr

Anfang

Ende

1

Montag

1

08:00

09:30

1

Montag

2

09:45

11:15

1

Montag

3

11:30

13:00

1

Montag

4

13:45

15:15

68

Relationenalgebra

TagNr

TagName

BlockNr

Anfang

Ende

1

Montag

5

15:30

17:00

1

Montag

6

17:15

18:45

2

Dienstag

1

08:00

09:30

2

Dienstag

2

09:45

11:15

...

...

...

...

...

4

Donnerstag

5

15:30

17:00

4

Donnerstag

6

17:15

18:45

5

Freitag

1

08:00

09:30

5

Freitag

2

09:45

11:15

5

Freitag

3

11:30

13:00

5

Freitag

4

13:45

15:15

5

Freitag

5

15:30

17:00

5

Freitag

6

17:15

18:45

Diese Relation enthält für jeden Tag jeden Unterrichtsblock. Bei fünf Tagen und sechs Unterrichtsblöcken also 5 * 6 = 30 Tupel. Dieses sind genau die möglichen Kästchen auf einem Stundenplan für einen Schüler oder Dozenten. Wenn wir nur die Tupel jeweils durch ein Paar aus den ersten beiden Anfangsbuchstaben des Tages und der Nummer des Blocks beschreiben und für die beiden Ausgangsrelationen die horizontale bzw. vertikale Achse nehmen, bekommen wir ein Rechteck, das genau die Kombinationen der beiden Ausgangsrelationen enthält, wie in der folgenden abgewandelten Darstellung sichtbar wird:10 Mo

Di

Mi

Do

Fr

1

[Mo,1]

[Di,1]

[Mi,1]

[Do,1]

[Fr,1]

2

[Mo,2]

[Di,2]

[Mi,2]

[Do,2]

[Fr,2]

3

[Mo,3]

[Di,3]

[Mi,3]

[Do,3]

[Fr,3]

4

[Mo,4]

[Di,4]

[Mi,4]

[Do,4]

[Fr,4]

5

[Mo,5]

[Di,5]

[Mi,5]

[Do,5]

[Fr,5]

6

[Mo,6]

[Di,6]

[Mi,6]

[Do,6]

[Fr,6]

10 Eine solche sogenannte »Kreuztabelle« zu erstellen, ist nicht Aufgabe der Relationenalgebra, sondern von Report-Werkzeugen, mit denen wir uns hier nicht weiter beschäftigen.

69

2 Einführung in das relationale Datenmodell

Allgemeiner R1 und R2 seien Relationen; alle Attribut-Bezeichner von R1 und R2 seien verschieden. Die Relation R1 × R2 enthält alle Attribut-Bezeichner von R1 und R2. Für jedes Tupel t1∈ R1 und jedes t2 ∈ R2 ist t1+t2 ∈ R1 × R2. Insbesondere ist die Anzahl der Tupel von R1 × R2 das Produkt der Anzahl der Tupel von R1 und R2, d.h., die Zahl

kann sehr groß werden. Eine nicht erwünschte Rolle spielt das Produkt aus folgendem Grund: Vergisst ein Benutzer, die Verbindungsattribute anzugeben, erhält er statt einer Verbundoperation das Produkt. Leider waren im SQL-Standard vor 1992 die Verbundattribute im Zusammenhang mit den Selektionskriterien festzulegen, sodass diese leicht vergessen werden konnten. In SQL-92 ist dies nicht mehr so problematisch, da hier der Verbund als eigene Operation in der Abfrage-Anweisung auftritt. 11 Falls das Produkt einer Relation mit sich selbst gebildet werden soll, sind vorher mindestens in einer der beteiligten Relationen die Attribute umzubenennen.

2.4.8 Der äußere Verbund (Outer Join) Der Nachteil des natürlichen Verbundes ist, dass nur solche Tupel aufgenommen werden, für die explizit (mindestens) eine Verknüpfung existiert. Wenn wir in unserer Versandhandeldatenbank aber eine Liste aller Kunden haben wollen, jeweils verknüpft mit ihren Bestellungen, so bekommen wir für die Kunden, für die keine Bestellung eingetragen ist, keine Ausgabe. Dies ist in der folgenden Relation dargestellt.

Beispiel Proj[name, bestell_nr, bestelldatum]( Kunde Join[kunden_nr] Bestellung) Name

Bestell_Nr

Bestelldatum

Stein, Peter

151

28.04.2008

Randers, Nis

152

30.04.2008

Stuff, Werner

153

12.05.2008

Randers, Nis

154

15.05.2008

Nicht alle Kunden erscheinen in dieser Auflistung, sondern nur die, für die Bestellungen eingetragen sind. Wenn wir dagegen alle Kunden in der Aufstellung sehen wollen, müssen wir den äußeren Verbund benutzen.

11 Vgl. hierzu Kapitel 5.3.2.

70

Relationenalgebra

Es ist allerdings zu berücksichtigen, dass der äußere Verbund nur eingeführt werden kann, wenn ein Konzept von Nullmarken (vgl. Kapitel 2.5) eingeführt ist. Einige Attribute haben dann keine Werte, was durch sogenannte Nullmarken gekennzeichnet wird. Für die Relationen, deren Tupel auf jeden Fall erscheinen sollen, werden Sternchen an den eckigen Klammern nach dem Join-Operator angebracht. Somit gibt es drei verschiedene äußere Verbunde: Join*[…], Join[…]* und Join*[…]*. Diese werden als Left Outer Join, Right Outer Join und Full Outer Join bezeichnet. Häufig werden sie auch abgekürzt als Left Join, Right Join und Full Join bezeichnet; der oben eingeführte Verbund wird dann als Inner Join bezeichnet. Die obige Abfrage wird dann wie folgt dargestellt: Proj [name, bestell_nr, datum]

(Kunde Join*[kunden_nr] Bestellung)

Name

Bestell_Nr

Bestelldatum

Berger, Uwe

*

*

Andresen, Ute

*

*

Voss, Hans

*

*

Stein, Peter

151

28.04.2008

Randers, Nis

154

15.05.2008

Randers, Nis

152

30.04.2008

Stuff, Werner

153

12.05.2008

Staack, Hannes

*

*

Wenn der äußere Verbund mit dem natürlichen Verbund gemischt wird, dann kommt es gegebenenfalls auf die Reihenfolge der Operationen an. Wir können hier also nicht so sorglos auf Klammern verzichten wie bei dem natürlichen Verbund.

Beispiel Um die Liste aller Artikel zusammen mit einer Liste aller Kunden zu bekommen, die diese Artikel gekauft haben, können wir folgenden Verbund verwenden: Artikel Join*[artikel_nr] (Bestellposition Join[bestell_nr] Bestellung Join[kunden_nr] Kunde)

Wir stellen dabei zuerst den inneren Verbund zwischen Bestellposition, Bestellung und Kunde her. Damit haben wir alle »gültigen« Bestellpositionen mit den uns interessierenden Daten. Der anschließende äußere Verbund mit der Relation Artikel stellt sicher, dass alle Artikel in der Liste erscheinen.

71

2 Einführung in das relationale Datenmodell

Von diesem Verbund bilden wir die Projektion auf die interessierenden Attribute und erhalten: Proj[artikel_nr, bezeichnung, lieferdatum, kunden_name::=Name] ( Artikel Join*[artikel_nr] (Bestellposition Join[bestell_nr] Bestellung Join[kunden_nr] Kunde) ) Artikel_Nr

Bezeichnung

Lieferdatum

G001

Whisky

Stuff, Werner

G001

Whisky

Randers, Nis

G002

Portwein

Randers, Nis 02.05.2008

Kunden_Name

G002

Portwein

G003

Bier

Stein, Peter

G003

Bier

02.05.2008

Stein, Peter

K001

Schuhe

02.05.2008

Randers, Nis

K002

Hose

02.05.2008

Stein, Peter

K003

Damenhut

02.05.2008

Stein, Peter

K003

Damenhut

02.05.2008

Randers, Nis

K004

Sonnenbrille

02.05.2008

Randers, Nis

L001

Ceylon-Tee

L002

China-Tee

L002

China-Tee

L003

Naturreis

Stuff, Werner

L004

Schokolade

Stuff, Werner

L005

Butterkekse

Randers, Nis

Stuff, Werner 02.05.2008

Stein, Peter

Die Klammerung (Artikel Join*[artikel_nr] Bestellposition) Join[bestell_nr] Bestellung Join[kunden_nr] Kunde

würde für den Fall, dass irgendeine Bestellposition nicht zu einer Bestellung gehört (ungültige Bestellung in der Relation Bestellposition) oder dass eine ungültige Kundennummer in der Relation Bestellung enthalten ist, die entsprechenden Artikel nicht mit aufführen, da für Artikel ohne Bestellpositionen das Verbindungsattribut NULL ist.

72

Relationenalgebra

Hinweis: Wie wir eben gesehen haben, ist die Verwendung von mehreren verschiedenen (inneren und äußeren) Verbunden in einem relationalen Ausdruck problematisch. Voraussetzung dafür ist eine sehr genaue Analyse, und es sind dann Klammern zu setzen.

2.4.9 Mengenoperationen Seien R1 und R2 Relationen vom gleichen Typ (d.h., die Attribut-Bezeichner und ihre entsprechenden Domänen sind gleich, vgl. Kap. 2.1). Dann sind durch R1 ∪ R2für die Vereinigungsmenge der Tupel von R1 und R2, d. h. die Menge der Tupel, die in R1 oder in R2 oder in beiden Mengen enthalten sind, R1 ∩ R2für die Durchschnittsmenge, d. h. die Menge der Tupel, die sowohl in R1 und R2 enthalten sind, und R1 \ R2für die Differenzmenge, d. h. die Menge der Tupel von R1, die nicht in R2

enthalten sind, rein mengentheoretisch weitere Relationen definiert. 12

Beispiele Sel[lagerplatz=5](Artikel) ∪ Sel[lagerplatz=7](Artikel) Sel[verpackung='Karton'](Artikel) ∩ Sel[lagerplatz=7](Artikel) Sel[lagerplatz=7](Artikel) \ Sel[verpackung='Karton'](Artikel)

Diese Beispiele können auch jeweils durch eine einfache Selektion beschrieben werden: Sel[lagerplatz=5 ∨ lagerplatz=7] (Artikel) Sel[verpackung='Karton' ∧ lagerplatz=7] (Artikel) Sel[lagerplatz=7 ∧ verpackung≠'Karton'] (Artikel)

Wenn dagegen zwei Relationen verknüpft werden sollen, die sich nicht von einer gemeinsamen Oberrelation ableiten lassen, können die Mengenoperationen Vereinigung und Differenz nicht durch andere Operationen ersetzt werden. Der Durchschnitt zweier Mengen kann allerdings als natürlicher Verbund dargestellt werden, wobei alle Attribute als Verbindungsattribute zu nehmen sind (vgl. die Relation im folgenden Beispiel 2). 12 Wir können die Bedingung der Homogenität der Relationen bei der Vereinigungsbildung fallen lassen. Wir bekommen dann eine Relation, die als Attribute die Vereinigungsmenge der Attribute der zu vereinigenden Relationen und die Vereinigungsmenge der Tupel enthält – die jeweils nicht definierten Attribute enthalten dann Nullmarken. Dieses ist als OUTER UNION in SQL definiert. Für den Durchschnitt macht diese Erweiterung wenig Sinn, da dann immer eine leere Relation herauskommt – ebenso wenig macht sie bei der Differenz Sinn, da dann nichts abzuziehen ist.

73

2 Einführung in das relationale Datenmodell

Hierzu führen wir eine Relation Lieferant ein, die in etwa folgenden Aufbau hat: Lieferant (lief_nr, name, straße, plz, ort, telefon, …)

Wir können dann durch Verknüpfung der Relation Kunde mit der Relation Lieferant u. a. folgende Relationen bilden:

Beispiel 1 Proj[name,straße,plz,ort](Kunde) ∪ Proj[name,straße,plz,ort] (Lieferant)

ist die Relation der Geschäftspartner.

Beispiel 2 Proj[name,straße,plz,ort](Kunde) ∩ Proj[name,straße,plz,ort] (Lieferant)

ist die Relation der Lieferanten, die gleichzeitig Kunden sind. Dies ist äquivalent zu: Proj[name,straße,plz,ort] (Kunde) Join[name,straße,plz,ort] Proj[name,straße,plz,ort] (Lieferant)

Beispiel 3 Proj[name,straße,plz,ort] (Kunde) \ Proj[name,straße,plz,ort](Lieferant)

ist die Relation der Lieferanten, die keine Kunden sind.

2.4.10 Übungsaufgaben Die folgenden Aufgaben können gegebenenfalls auch mit einem DBMS gelöst werden, das die relationalen Operationen – wenigstens teilweise – über eine grafische Benutzeroberfläche für die Gestaltung von Abfragen anbietet. Beispiele sind Access, Query von Microsoft und der Discoverer von Oracle. Aufgabe 2.2 Beschreiben Sie folgende Fragen durch Verknüpfung der Relationen aus dem Anhang C. a) An welchen (verschiedenen) Lagerplätzen haben Sie Artikel? b) An welchen Orten mit PLZ wohnen Kunden? c) Bei welchen Bestellpositionen sind Artikel mit den Nummern 'G001', 'G002' oder 'G003' geliefert worden?

74

Nullmarken in Datenbanken

d) Bei welchen dieser Bestellpositionen ist die Liefermenge größer als 2? e) Bei welchen Bestellpositionen sind keine Artikel mit den oben genannten Nummern geliefert worden? f) Welche Kunden sind keine Stammkunden? Geben Sie die Kundennummer, den Namen, den Ort und den Status aus. g) Welche Kunden sind keine Stammkunden und nicht aus Husum? h) Welche Bestellungen sind vom Mai 2008? i) Es sollen die Bestellpositionen aufgelistet werden, in denen die Artikel 'K001' oder 'G003' vorkommen. j) Bei welchen Artikeln liegt der Listenpreis zwischen 6 und 15 €? k) Lassen Sie pro MwSt.-Satz die Anzahl der davon betroffenen Artikel anzeigen. l) Geben Sie jeweils die größte Kundennummer der Kunden mit demselben Status aus. m) Zeigen Sie je Lagerplatz den kleinsten, den größten und den durchschnittlichen Listenpreis der dort gelagerten Artikel an. n) Wie viele Kunden haben Sie jeweils mit gleicher Postleitzahl? o) Unter welcher Postleitzahl haben Sie mehr als einen Kunden? p) Zu welcher Zahlungsart haben Sie mehr als zwei Kunden? q) Zeigen Sie zu jedem Artikel den Mehrwertsteuersatz an. r) Zeigen Sie mithilfe des Mehrwertsteuersatzes den Bruttopreis je Artikel an. s) Zeigen Sie zu jeder Bestellung den Lieferort an. t) Zeigen Sie zu jeder Bestellposition die Bestellmenge, Bestellnummer und den Lieferort an. u) Zeigen Sie je Ort die Anzahl der bestellten Artikel an. v) Ermitteln Sie den billigsten Artikel (nicht nur den Preis des billigsten Artikels!). w) Welcher Kunde hat die letzte Bestellung aufgegeben? Zeigen Sie die Attribute kunden_nr, name und bestell_nr an. x) Welche Bestellungen sind nicht von Kunden aus Kayhude? y) Welche Artikel haben einen höheren Listenpreis als alle Artikel aus Lager 7?

2.5 Nullmarken in Datenbanken 2.5.1 Die Behandlung von fehlenden Attributwerten In relationalen Datenbanken ist es möglich, dass für ein Tupel ein Attribut fehlt. Wir sagen auch: »Das Attribut ist NULL.« (vgl. [Codd86]) Wir sagen hier bewusst nicht: »Das Attribut hat den Wert NULL.« NULL ist nicht identisch mit dem numerischen Wert 0 oder dem Leerzeichen.

75

2 Einführung in das relationale Datenmodell

Die Tatsache, dass kein Attribut vorhanden ist, wird durch eine Markierung in der Datenbank festgehalten. Diese Markierung kann abgefragt werden. Von daher ist es sinnvoll, von Nullmarken statt von Nullwerten zu sprechen. In der Literatur ist allerdings häufig noch von »Nullwerten« oder »null values« die Rede.

2.5.2 Verschiedene Arten von Nullmarken Nullmarken können unter anderem zweierlei Bedeutung haben: Sie können für Attribute stehen, die in der Realität einen Wert haben, der aber nicht bekannt ist (sog. A-Marken – für applicable). Sie können für Attribute stehen, für die in der Realität kein Wert existiert (sog. I-Marken – für inapplicable). In tabellarischen Darstellungen stellen wir A-Marken durch ein Fragezeichen und I-Marken durch ein Sternchen dar. Daneben sind weitere semantische Interpretationen von fehlenden Werten möglich.

Beispiele für I-Marken 왘 Abfahrtszeit eines Intercity-Zuges in einem Durchgangsbahnhof 왘 Telefonnummer für Personen ohne Telefon

I-Marken treten auch auf, wenn für ein Attribut noch kein Wert existiert – dieses Faktum an sich ist aber schon eine Aussage, die relevant sein kann, z. B.: 왘 das fehlende Todesdatum als Information, dass die entsprechende Person noch lebt 왘 das fehlende Lieferdatum als Information, dass ein Auftrag noch nicht erfüllt ist A-Marken treten in Bereichen auf, in denen Daten aus verschiedenen Quellen gesammelt werden. Beispiele dafür sind wissenschaftliche Datenbanken, wo Messergebnisse gespeichert werden. A-Marken können auch in Datensammlungen auftreten, die zur Entscheidungsunterstützung angelegt werden, z. B. bei der Kriminalpolizei oder in Datenbanken, die der Marktbeobachtung dienen. In operativen Datenbanken von Unternehmen dürften A-Marken dagegen seltener sein, da dort die Vertragsbedingungen und Produktionsabläufe jeweils klar definiert sein müssen. Leider wird die Unterscheidung von A-Marken und I-Marken bislang nicht durch existierende Datenbanksysteme, insbesondere nicht durch SQL, unterstützt. Bei Benutzung von Nullmarken ist immer besondere Vorsicht notwendig, da beide (und weitere) Semantiken nebeneinander und ohne besondere Unterscheidung benutzt werden.

76

Nullmarken in Datenbanken

Grundsätzlich müssen wir, wenn wir Nullmarken zulassen, festlegen, welche Semantik diese haben sollen. Beachten Sie, dass in der folgenden Tabelle die zweite Zeile die Bedeutung »Martin Venus hat kein Telefon« hat, während die vierte Zeile »Juppi Saturnus hat ein Telefon, aber die Nummer ist nicht bekannt« bedeutet. Person_1 PersNr

Name



Telefon

201

Adams, Eva

0471 / 12 34 56

202

Venus, Martin

*

203

Mondschein, Stella

0471 / 48 23 13

204

Saturnus, Juppi

?

Aussagen wie »Es ist nicht bekannt, ob Castor Pollux ein Telefon hat« können in der obigen Tabelle so nicht ausgedrückt werden. Hierzu wäre eine weitere Nullmarke einzuführen, beispielsweise ?*. Person_2 PersNr

Name

201

Adams, Eva



Telefon 0471 / 12 34 56

202

Venus, Martin

*

203

Mondschein, Stella

0471 / 48 23 13

204

Saturnus, Juppi

?

205

Pollux, Castor

?*

Falls diese Unterscheidung nicht zur Verfügung steht, können wir uns dadurch helfen, dass wir eine weitere Spalte einführen, etwa in folgender Form: Person_3 PersNr

Name

201



hat_Telefon

Telefon

Adams, Eva

Ja

0471 / 12 34 56

202

Venus, Martin

Nein

*

203

Mondschein, Stella

Ja

0471 / 48 23 13

204

Saturnus, Juppi

Ja

?

205

Pollux, Castor

?

Nach [Date90a S. 219 ff. und Date00 S. 585] gibt es beliebig viele unterschiedliche Semantiken für Nullmarken.

77

2 Einführung in das relationale Datenmodell

Die Behandlung von Nullmarken ist im ursprünglichen Codd'schen relationalen Datenmodell nicht vorgesehen. Da aber in vielen Anwendungen die Notwendigkeit auftrat, hierfür eine Behandlung zu haben, sind viele Ad-hoc-Erweiterungen in relationalen Systemen eingefügt worden, so auch in SQL. Der Ansatz in SQL führt zu verschiedenen Inkonsistenzen, auf die Date ab 1986 hingewiesen hat (vgl. z. B. [Date90a S. 339 ff.]). A-Marken und I-Marken spielen eine exotische Rolle in der Theorie der relationalen Datenbanken. Eine Erweiterung des relationalen Datenmodells um A-Marken und I-Marken wird z. B. in [Codd90] vorgeschlagen. Doch auch dieser Ansatz hat nicht die Konsistenz des relationalen Datenmodells ohne Nullmarken. Das liegt daran, dass aus pragmatischen Gründen eine einfache Behandlung dieser Marken sinnvoll ist – andererseits lassen sich viele Beispiele finden, in denen dieser Ansatz zu kurz greift. Eine Theorie dagegen, die alle vorhandenen semantischen Informationen im Zusammenhang mit Nullmarken auswertet, verliert die effiziente Berechenbarkeit des relationalen Modells, und eben diese ist eine der hervorragenden Eigenschaften dieses Datenmodells. Wir zeigen nun einige Probleme im Zusammenhang mit A-Marken und I-Marken auf. Als wichtigstes Ergebnis kommt dabei heraus, dass diese beiden Marken logisch unterschiedlich zu handhaben sind. Da aber auch die in [Codd90] vorgeschlagene vierwertige Logik nur ein Hilfsmittel ist, um eine große Menge von Anwendungen darstellen zu können, ohne Probleme bei der Berechenbarkeit zu bekommen, gilt nur eines: Jeder Anwender, der Abfragen formuliert, in denen gegebenenfalls Nullmarken miteinander verglichen werden können, muss sich genau darüber im Klaren sein, was herauskommen soll, wenn A-Marken oder IMarken in den Vergleich eingehen. Falls das nicht mit der vom Datenbanksystem unterstützten Logik übereinstimmt, müssen gegebenenfalls präzisere und differenzierte Datendarstellungen und Abfragen gefunden werden.

I-Marken in Relationen In Relationen mit I-Marken sind Tupel zugelassen, für die gegebenenfalls einige Attribute nicht auftreten. Grundsätzlich können Attribute, in denen I-Marken zugelassen sind, als äußerer Verbund von normalen Relationen dargestellt werden. Die Relation Bestellung, in der das Lieferdatum und der Rechnungsbetrag eine Nullmarke enthalten können, können wir als äußeren Verbund folgender zwei Tabellen aufbauen: Bestellung_2 Bestell_Nr

Kunde

Bestelldatum

151

101

28.04.2008

152

103

30.04.2008

153

105

12.05.2008

154

103

15.05.2008

78

Nullmarken in Datenbanken

und Lieferung Bestell_Nr

Lieferdatum

Rechnungsbetrag

151

02.05.2008

196,75

152

02.05.2008

2270,86

Die Relation Bestellung lässt sich darstellen als: Bestellung_2 Join*[bestell_nr] Lieferung

Bei der Selektion liefern Vergleiche mit I-Marken grundsätzlich nicht den Wert TRUE. So erhalten wir im Folgenden die Relation aller Lieferungen, die vor dem 8. Mai 2008 ausgeliefert worden sind: Sel[lieferdatum < DATE'2008-05-08'] (Bestellung) Bestell_Nr

Kunde

Bestelldatum

Lieferdatum

Rechnungsbetrag

151

101

28.04.2008

02.05.2008

196,75

152

103

30.04.2008

02.05.2008

2270,86

Auch die Selektion mit der komplementären Bedingung Sel[lieferdatum ≥ DATE'2008-05-08'] (Bestellung)

führt zu einer Ergebnisrelation, in der die Zeilen mit I-Marke beim Lieferdatum nicht enthalten sind. Codd führt für solche Fälle mit der Begründung, dass sich I-Marken jeglichem Vergleich entziehen, einen eigenen Wahrheitswert ein: »At first glance, it appears to make sense to handle equality between two inapplicable value marks just like equality between two actual values. Note, however, that I-marks are neither values nor placeholders for values. They mean unknowable rather than unknown. Thus, within the condition part of a relational-language statement, whenever an I-mark is equated to an actual value, an A-mark or an I-mark, the truth-value of such a condition is always taken to be MAYBE of type INAPPLICABLE.« [Codd90 Seite 180] Unterschiede in den verschiedenen Ansätzen (eigener Wahrheitswert oder FALSE für Vergleiche mit I-Marken) führen in Selektionen nur dann zu unterschiedlichen Ergebnissen, wenn wir eine Bedingung verwenden, welche die Negation enthält. Solange die Bedingungen nur AND und OR enthalten, sind diese Fragen irrelevant. In den Fällen, in denen eine Abfrage mit NOT verwendet wird und Attribute I-Marken enthalten können, muss der Benutzer sich jeweils besondere Gedanken machen, was die entsprechenden Aussagen bedeuten.

79

2 Einführung in das relationale Datenmodell

So ist die Menge der Züge von Bremerhaven nach Bremen, die nicht vor 12 Uhr in Wulsdorf halten, keineswegs identisch mit der Menge der Züge von Bremerhaven nach Bremen, die frühestens um 12 Uhr in Wulsdorf halten; denn sie enthält auch die Züge, die überhaupt nicht in Wulsdorf halten.

Prädikat isInapplicable Um feststellen zu können, ob ein Attribut eine I-Marke hat, führen wir das logische Prädikat isInapplicable ein. isInapplicable(x) hat den Wert TRUE, wenn das Attribut x eine I-Marke hat, und ist FALSE, wenn ein echter Wert vorhanden ist. So bekommen wir im Folgenden die Liste aller noch nicht abgearbeiteten Bestellungen: Proj[bestell_nr, kunde, bestelldatum] ( Sel[isInapplicable(Lieferdatum)] (Bestellung) ) Bestell_nr

Kunde

Bestelldatum

153

105

12.05.2008

154

103

15.05.2008

Verbund unter Verwendung von Nullmarken Beim Verbund werden Tupel nicht verknüpft, wenn eines der Verknüpfungsattribute eine I-Marke enthält. Vergleichen Sie hierzu das folgende Beispiel: Abteilung Abt_Nr

Leiter

Umsatz

A

Hansen, Hannes

B

Paulsen, Palle

50000,00

C

*

27500,00

D

Hansen, Hannes

41200,00

E

*

F

Petersen, Pidder

32000,00

234000,00 48600,00

Der folgende Verbund ergibt die Relation aller Abteilungen mit demselben Abteilungsleiter. Das Ergebnis enthält nicht die Abteilungen C und E, obwohl in der Tabelle diese beiden Abteilungen als Abteilungsleiter denselben Eintrag (nämlich keinen!) haben.

80

Nullmarken in Datenbanken

Sel[abt1 ≠ abt2] ( Proj[abt1 ::= abtNr, leiter] (Abteilung) Join[leiter] Proj[abt2 ::= abtNr, leiter] (Abteilung) ) Abt1

Leiter

Abt2

D

Hansen, Hannes

A

A

Hansen, Hannes

D

In den Aggregatfunktionen SUM, MAX, MIN, AVG werden Tupel mit einer I-Marke bei der Berechnung nicht berücksichtigt.

Prädikat isUnknown Das Auftreten einer A-Marke bezeichnen wir im Text durch UNKNOWN oder in Tabellen kürzer durch ein Fragezeichen. Entsprechend gibt es ein Prädikat isUnknown(attr), um abzufragen, ob ein Attribut einen unbekannten Wert hat. In arithmetischen Ausdrücken, in denen mindestens eine A-Marke als Argument auftritt, ist das Ergebnis eine A-Marke. Wenn wir im Folgenden A für eine A-Marke setzen, gilt also 8+A=A, ebenso 0*A=A (obwohl hier mit einem gewissen Recht 0 als Ergebnis angenommen werden könnte). Vergleichsoperationen mit einer A-Marke liefern die logische Konstante UNKNOWN. Das gilt auch für Vergleiche wie etwa A m2, sonst 0.

85

2 Einführung in das relationale Datenmodell

2.6.11 Anwendung der Tabellenoperationen auf Relationen Für diesen Abschnitt bezeichnen wir die Projektion im Sinne der Relationenalgebra als ProjR und die Projektion im Sinne der Tabellenalgebra entsprechend der Definition von Abschnitt 2.6.4 als ProjT. Für eine Relation R ist ProjT(R)

nicht immer eine Relation, sondern es kann eine Tabelle sein, die gewisse Tupel mehrfach enthält. Mit dem in Abschnitt 2.6.2 eingeführten Distinct-Operator erhalten wir die relationale Projektion Proj durch: Proj […](R) = Distinct(ProjT[…](R))

Aus diesem Grund gibt es in SQL ein Schlüsselwort DISTINCT, das angibt, dass eine durchzuführende Projektion eine Relation als Ergebnis haben soll. Ohne Angabe von DISTINCT wird die entsprechende Projektion der Tabellenalgebra ausgeführt. Bei der Bildung der Vereinigungsmenge von zwei Relationen können Tupel doppelt gezählt werden, sodass hier die relationale Vereinigung von der tabellarischen Vereinigung abweicht. Für die relationale und tabellarische Variante der Vereinigung gilt: T1 ∪ T2 = Distinct (T1 + T2)

Anders als bei der Projektion ist beim UNION-Operator von SQL allerdings die relationale Variante der Standardfall. Um die tabellarische Operation zu erhalten, ist hier UNION ALL anzugeben. Für alle anderen Operationen sind die relationalen Operatoren Spezialfälle der Tabellenoperatoren. Wenn also die Operanden Tabellen sind, die keine mehrfach auftretenden Tupel haben, haben auch die Ergebnisse keine mehrfach auftretenden Tupel. Hiermit können wir jetzt auch den vermeintlichen Widerspruch aufklären, wieso Datenbanksysteme, die nach dem tabellarischen Datenmodell (z. B. Access, SQLDatenbanksysteme) arbeiten, dennoch als relational bezeichnet werden: Wir können in diesen Datenbanksystemen rein relational arbeiten, wenn wir uns an folgende Richtlinien halten: 왘 Jede Basisrelation muss einen Primärschlüssel haben. 왘 Bei Projektionen ist sicherzustellen, dass die relationale Projektion benutzt wird (in SQL durch SELECT DISTINCT). 왘 Beim UNION-Operator ist die relationale Variante zu wählen (in SQL UNION ohne das Schlüsselwort ALL).

86

Indizes in relationalen Datenbanken

2.6.12 Übungsaufgaben Aufgabe 2.5 Welche der folgenden Abfragen ergeben Relationen? Vorausgesetzt ist, dass die Ausgangstabellen jeweils Relationen sind, aber jeweils die für Tabellen definierten Operatoren benutzt werden. Es sind hierbei die jeweiligen Schlüssel- und Fremdschlüsseleigenschaften der Relationen entsprechend Anhang C zu beachten. a) ProjT[kunden_nr, name, straße, plz, ort] (kunde) b) ProjT[plz, ort] (kunde) c) kunde join[kunden_nr] bestellung d) ProjT[bestell_nr] (kunde join[kunden_nr] bestellung) e) ProjT[kunden_nr] (kunde join[kunden_nr] bestellung)

2.7 Indizes in relationalen Datenbanken Während alle Informationen einer relationalen Datenbank in den Relationen enthalten sind und keine zusätzlichen Zugriffspfade existieren, die für die Semantik der Datenbank relevant sind, gibt es zur Beschleunigung einiger Operationen die sog. Indizes auf Relationen. Die Benutzung von Indizes gehört zum internen Schema einer relationalen Datenbank und ist daher nicht in den SQL-Standard aufgenommen worden. Die Indizes spielen jedoch eine große Rolle und sind in fast allen relationalen Datenbanksystemen zu finden. Ein Index ist durch die Angabe einer Tabelle und einer Menge von Attributen beschrieben, die den Index bilden. Daneben sind gegebenenfalls weitere Angaben möglich, z. B. zur Sortierung oder Speicherorganisation. Wenn ein Index vorhanden ist, können Operationen, in denen die entsprechenden Attribute in einer Bedingung auftreten (Auswahl oder Verbund; aber auch für Projektion und Mengenoperationen sinnvoll), und weitere Zugriffe schneller durchgeführt werden. Neben der Beschleunigung bei gewissen Zugriffen werden allerdings manche Änderungsoperationen durch einen Index langsamer. In der Regel ist es sinnvoll, auf jeden Schlüssel und auf jeden Fremdschlüssel einen Index zu legen. Dies sollte das Datenbank-Verwaltungssystem selbstständig erledigen. Schauen Sie bezüglich dieses Punktes in dem Handbuch Ihres Datenbanksystems nach. Im Folgenden wird der prinzipielle Aufbau eines Index auf der Relation »Artikel« auf dem Attribut »Lagerplatz« dargestellt. Zu diesem »Datenteil« des Index gehört dann noch ein B*-Baum oder Ähnliches, um die Zugriffe auf die entsprechenden Sätze weiter zu beschleunigen.

87

2 Einführung in das relationale Datenmodell

Hierzu wird die folgende Tabelle mit Angabe der Zugriffsadresse für die einzelnen Tupel verwendet (hier: die relative Satznummer – es kann auch andere sog. TupelIdentifikatoren geben, die z. B. die Lage in einem Block und die relative Nummer des Satzes in diesem Block beschreiben). Diese Zugriffsadresse ist nicht im Satz mit abgespeichert, kann aber vom Datenbank-Verwaltungssystem unmittelbar für den Datenzugriff verwendet werden. Ein Index muss sich nicht immer auf die relative Satznummer beziehen (wie es z. B. in XBase, Informix SE und – in modifizierter Form – in Adabas realisiert wird), sondern kann sich auf eine komplexere Satzadresse beziehen (z. B. auf sog. TupelIdentifikatoren aus Blocknummer und relativer Satznummer im Block wie in Oracle oder DB2). Satz- Artikel_ Nr Nr

MwSt._ Bezeichnung Satz

Listenpreis

Bestand Mindestbestand

Verpackung

Lagerplatz

0

G001

2

Whisky

38,40

397

50

0,7 l

7

1

G002

2

Portwein

12,45

473

100

0,5 l

7

2

G003

2

Bier

5,20

1250

250

6er Pack

7

Karton

2

3

K001

2

Schuhe

4

K002

2

Hose

98,50

120

25

112,80

62

25

5

K003

2

Damenhut

65,70

12

20

2 Karton

2

6

K004

1

Sonnenbrille

76,00

50

20

Karton

2

7

L001

1

Ceylon-Tee

6,35

356

100

125 g

5

8

L002

1

China-Tee

8,35

42

50

125 g

5

9

L003

1

Naturreis

1,78

345

0

1 kg

4

10

L004

2

Schokolade

0,98

2101

1000

Tafel

2

11

L005

2

Butterkekse

1,72

234

250

250 g

2

Zwei mögliche Formeln eines Index sind in den beiden folgenden Abbildungen kurz dargestellt. Die erste Form mit fester Satzlänge wird z. B. in XBase verwendet. Die zweite Implementierung ist im Zugriff schneller und weniger speicheraufwendig – setzt allerdings voraus, dass das System in der Lage ist, Sätze variabler Länge zu verwalten. Lagerplatz

Satz-Nr

Lagerplatz

Satz-Nr

2

3

2

3,4,5,6,10,11

2

4

4

9

2

5

5

7,8

2

6

7

0,1,2

88

Indizes in relationalen Datenbanken

Lagerplatz

Satz-Nr

2

10

2

11

4

9

5

7

5

8

7

0

7

1

7

2

Das Anlegen eines Index darf nur Auswirkungen auf die Effizienz haben – der Benutzer muss zur Abfrageformulierung grundsätzlich dieselben Möglichkeiten unabhängig davon haben, ob ein Index vorhanden ist oder nicht. In diesem Sinne sind z. B. Systeme wie XBase oder Paradox keine relationalen Datenbanksysteme, da dort für gewisse Verknüpfungen Indizes vorausgesetzt werden.

2.7.1 Übungsaufgaben Aufgabe 2.6 Finden Sie für das von Ihnen benutzte Datenbanksystem heraus, auf welche Weise dort Indizes anzulegen sind. Aufgabe 2.7 Finden Sie für das von Ihnen benutzte Datenbanksystem heraus, für welche Integritätsregeln (z. B. Primärschlüssel, weitere Schlüssel, Fremdschlüssel) automatisch Indizes angelegt werden. Aufgabe 2.8 Legen Sie bei einer großen Datenbank zusätzliche Indizes an, und vergleichen Sie die Verarbeitungszeiten mit und ohne Index.

89

Datenbankentwurf 3.1 Anwendungsentwicklung, Prozess- und Datenmodell Wenn die Idee entsteht, für ein bestimmtes Arbeitsgebiet EDV-Unterstützung zu schaffen, wird ein Prozess in Gang gesetzt, an dessen Ende möglicherweise eine fertige EDV-Lösung steht: ein Computersystem mit einer für die Erledigung der gestellten Aufgabe mehr oder weniger tauglichen Software. Die Lösung einer anspruchsvolleren Aufgabe – wie etwa die Automatisierung der Auftragsbearbeitung – ist von der Stange meist nicht zu haben. Sie muss erst erstellt werden, mit erheblichem Aufwand an Zeit und Arbeit. Dabei entsteht eine Reihe von Tätigkeiten und Zwischenresultaten, die aufgrund ihrer wechselseitigen logischen und zeitlichen Abhängigkeit allgemein verschiedenen Phasen zugeordnet werden (siehe Abbildung 3.1). Die Tätigkeiten werden in der Grafik in Form von Rechtecken dargestellt. Die Zwischenresultate, die sozusagen zur nächsten Phase weitergereicht werden, erscheinen als Pfeile. Jede Phase wird mit einem definierten und dokumentierten Resultat abgeschlossen. Man nennt dieses Modell auch Software-Life-Cycle, womit ausgedrückt werden soll, dass eine Anwendungslösung im Grunde nie richtig fertig ist. Das benutzbare Resultat, in unserem Bild das »Endprodukt«, lässt neue Bedürfnisse nach Erweiterung oder Verbesserung entstehen – der Benutzer »entdeckt« neue Anforderungen, sodass der Prozess unter Umständen mit einer modifizierten, erweiterten Problemstellung von Neuem beginnt. Dieses hier kurz skizzierte Modell hat heftige Diskussionen ausgelöst und etliche Korrekturen erfahren. Man hat festgestellt, dass es bei allen Bemühungen in der Praxis kaum ein Projekt gibt, das tatsächlich nach diesem linearen, immer nur vorwärts weisenden Muster abgewickelt wurde. Die Modifikationen, die am Modell vorgenommen wurden, reichen vom Hinzufügen rückwärts gerichteter Pfeile zwischen den Phasen (man kann jederzeit einen Schritt zurück tun) über »Spiralmodelle« bis hin zum »Prototyping«, wo möglichst schnell ein ablauffähiges Rumpfprogramm erstellt wird, an dem der Benutzer sehen kann, ob er seine Anforderungen richtig formuliert hat. Insofern markiert das in der Grafik vorgestellte Modell schon ein »Ideal« der Softwareproduktion. Wir beziehen uns hier wegen der Einfachheit der Darstellung weiter auf dieses Grundmodell.

91

3 Datenbankentwurf

Problem

Systemanalyse

Anforderungsdefinition

Systementwurf

Spezifikation

Implementierung

vorläufiges Produkt

Test und Korrektur

Endprodukt

Nutzung

neue Anforderungen

Abbildung 3.1: Klassisches Phasenmodell der Softwareentwicklung

Das Phasenmodell, wie es in Abbildung 3.1 wiedergegeben wird, geht von einem Programm als Endprodukt aus. Ein Programm oder Programmpaket ist eine mehr oder weniger umfangreiche, aber doch fest vorgegebene Ansammlung von Operationen und Datenstrukturen, die der Anwender benutzen, aber nicht selbst gestalten kann. Dies trifft für jedes Anwendungsprogramm zu, wie umfangreich auch immer seine Funktionen sein mögen. Ergeben sich nun aus der praktischen Arbeit neue, bisher nicht berücksichtigte Informationsbedürfnisse, so muss das Programm erweitert oder umgeschrieben werden, sofern das Grundkonzept die Erfüllung der neuen Anforderungen überhaupt zulässt. Sonst ist im schlimmsten Fall eine völlige Neuauflage des ganzen Zyklus notwendig.

92

Anwendungsentwicklung, Prozess- und Datenmodell

Mit einem Datenbankkonzept wird hingegen die Organisation und Speicherung der Daten unabhängig von jeder einzelnen Anwendung realisiert. Den Kern der betrieblichen Informationsverwaltung bildet damit die Datenbank. Die Verwaltung des betrieblichen Datenbestands wird zu einer zentralen Funktion. Anwendungsprogramme und unterschiedliche Sichtweisen auf die Daten, die ablauf- und aufbauorganisatorischen Erfordernissen Rechnung tragen, beziehen sich auf die gemeinsame Datenbasis. Daneben gibt es interaktive Zugriffsmöglichkeiten auf die Daten mithilfe einer Datenbanksprache wie SQL oder datenbankunabhängige Zugriffswerkzeuge, mit denen Benutzer ohne Kenntnis einer Sprache bequemen Zugriff auf Daten haben (sofern ihnen diese zugänglich sind). So können Informationen nach verschiedenen Kriterien abgefragt und Daten manipuliert werden, ohne dass in jedem Fall dazu Software entwickelt werden muss. Programme im herkömmlichen Sinn sind also nicht für jeden Datenzugriff notwendig, sondern nur dort, wo in einem Arbeitsgebiet jeweils bestimmte Folgen von aufeinanderfolgenden Operationen auszuführen sind, die sich für jeden gleichartigen Vorgang im Prinzip wiederholen. Man spricht in diesem Zusammenhang auch von »Geschäftsprozessen« und »Workflow«. Programme sind unter diesen Umständen nicht mehr identisch mit der EDV-Lösung, sondern nur ein Bestandteil von ihr. Die getrennte Betrachtung und Entwicklung von Datenstrukturen und automatisierten Verarbeitungsprozessen ist schließlich ein Hauptmerkmal der Client-ServerArchitektur, bei der Daten und Programme meist auf unterschiedlichen Rechnern implementiert werden. Der Gesamtprozess der Anwendungsentwicklung muss folgerichtig den Entwurf und die Realisierung der Datenbank als eigenständigen Zweig enthalten. Unser modifiziertes Phasenmodell stellt dies grafisch dar. Die erste Phase in Abbildung 3.2 ist die Systemanalyse, auch Anforderungsdefinition bzw. Requirements Definition genannt. Ihren Ausgang nimmt die Systemanalyse von den Benutzerwünschen. Ziel und Resultat ist eine exakte1, für Fachleute und Nichtfachleute gleichermaßen verständliche Formulierung der Forderungen an das zu erstellende Softwaresystem. Ist die Anforderungsdefinition erstellt, so teilt sich der Prozess. Der eine Zweig enthält den Entwurf und die Realisierung der Datenbank, der andere widmet sich den Anwendungsprogrammen. Der Datenbankentwurf resultiert in einem Datenbankschema, das direkt mittels der Datendefinitionssprache (DDL: Data Definition Language) in eine benutzbare Datenbank umgesetzt werden kann. Dieser Prozess wird in diesem Kapitel ausführlich beschrieben. Seine zunehmende Bedeutung für die Informationsverarbeitung entspringt dem Bedürfnis, über ein langfristig stabiles konzeptionelles Datenmodell als Ausgangsbasis für alle Anwendungen und Datenbanken zu verfügen. 1 Der Begriff »exakt« heißt hier »vollständig und konsistent«. Dabei bedeutet Vollständigkeit, dass alle vom Benutzer gewünschten Leistungen aufgeführt werden. Konsistenz bedeutet, dass keine Festlegung einer anderen widersprechen darf. [Somm87; S.16]

93

3 Datenbankentwurf

Problem

Systemanalyse

Anforderungsdefinition

Datenbankentwurf

Datenbankbeschreibung

Anwendungsentwurf

Datenbankrealisierung

Spezifikation

Datenbank

Anwendungsimplementierung

Test und Korrektur

vorläufiges Produkt

Endprodukt

Nutzung

neue Anforderungen

Abbildung 3.2: Modifiziertes Phasenmodell

Darin sind die Daten unternehmensweit vereinheitlicht und auf eine gemeinsame begriffliche und logische Basis gestellt. Eine gesamtheitliche Sicht des Unternehmens gilt als entscheidende Voraussetzung dafür, den Nutzen der Informatik im Unternehmen zu erhöhen. Die Betrachtung und Gestaltung der Informationsverarbeitung unter diesem Gesichtspunkt ist eine anspruchsvolle, langwierige Aufgabe, deren Nutzen gleichwohl den Aufwand rechtfertigt.2

2 Vgl. [Unte96]

94

Anwendungsentwicklung, Prozess- und Datenmodell

In der Phase Datenbankrealisierung werden die Datenbanktabellen erstellt, Zugriffsrechte vergeben und Benutzersichten auf Daten definiert. Das Resultat ist eine »leere« Datenbank, d. h. ein Gerüst, das für die Aufnahme von Daten vorbereitet ist. Daten sind in der Tat das Einzige, was dann für die Arbeit mit der Datenbank noch fehlt. In der Phase Anwendungsentwurf wird ein Modell des Softwaresystems entwickelt, das die in der Anforderungsdefinition festgelegten Prozesse zum Inhalt hat. Gegenstand sind hierbei komplexe Operationen auf der Datenbasis, die automatisiert werden sollen. Auch der Entwurf von Eingabe- und Editiermasken, Berichten sowie von Benutzermenüs gehört hierher. Das Modell ist so etwas wie die Architektur eines Anwendungsprogramms. Die Grundlage für den Entwurf von Prozessen sind die Daten, auf denen sie operieren sollen. Daher muss der Datenbankentwurf vor dieser Phase stattfinden. Die Datenbankbeschreibung geht in den Anwendungsentwurf ein, wie der Pfeil in Abbildung 3.2 andeutet. Während in den vorangegangenen Phasen noch keine einzige Programmzeile geschrieben wurde (höchstens als Pseudocode), geht es bei der Anwendungsimplementierung darum, aufgrund des »Bauplans« eine ablauffähige Lösung zu erstellen. Hier werden Menüs, Masken, Reports und verarbeitende Programme erstellt und zu einem Gesamtsystem zusammengefügt. Das resultierende »vorläufige Produkt« muss anschließend in der Testphase gründlich geprüft und korrigiert werden. In dieser Phase wird die Datenbank mit Testdaten gefüllt, und sämtliche Prozeduren werden auf ihre Richtigkeit hin überprüft. Sind die erforderlichen Korrekturen eingebracht, erfolgt die Übergabe des »Endprodukts« verbunden mit der Installation auf der Hardware und der Einweisung der Benutzer. In der vorläufigen Endphase, der Nutzung, muss das Produkt seine Alltagstauglichkeit unter Beweis stellen. Hier zeigt sich, ob es für den Zweck, für den das ganze Projekt ins Leben gerufen wurde, tauglich ist. Es entstehen dabei neue Bedürfnisse nach Erweiterungen und Verbesserungen, aber auch nach Korrektur festgestellter Unzulänglichkeiten. Daher schließt sich der Kreis mit neuen, aus der Praxis geborenen Anforderungen, die einen neuen Phasenzyklus veranlassen können. Den Fall, dass eine fertige Lösung nach kurzem Gebrauch auf dem »Müll« landet, wollen wir freilich der Vollständigkeit halber nicht verschweigen. Wir werden im Folgenden zwei Methoden kennenlernen, die im Datenbankentwurf eine Rolle spielen: den Datenbankentwurf nach dem Entity-RelationshipModell (kurz ERM) und die Normalisierung. Diese beiden Ansätze beruhen auf unterschiedlichen Voraussetzungen. Die Grundlage der Normalisierung ist ein relationales Datenmodell. Die Normalisierung setzt also auf einer Abstraktionsebene auf, die nahe an der Implementierung im Rechner liegt.

95

3 Datenbankentwurf

Das ERM geht von Entitäten (= Objekten) des abzubildenden Realitätsausschnittes aus. Im Prinzip ist das ERM unabhängig vom Datenmodell. Es wird jedoch heute fast ausschließlich zum Entwurf relationaler Datenbanken verwendet. Als das ERM entstand (1976), sind damit sehr wohl auch Datenbanken nach dem sog. Netzwerk-Datenmodell3 entwickelt worden. Für objektorientierte Datenbanken – die nächste Generation – brauchen wir dagegen mehr semantische Möglichkeiten. Hier hat sich in den letzten Jahren die UML (Unified Modeling Language) als anerkannter Standard etabliert. Die UML ist sehr viel mächtiger als das ERM; die Entity-Relationship-Diagramme lassen sich jedoch fast unverändert in der UML darstellen. Daneben enthält die UML einige Konstrukte, die auch bei der Entwicklung relationaler Datenbanken relevant sind; diese werden wir in Abschnitt 3.3 kennenlernen.

3.2 Grundbegriffe des EntityRelationship-Modells 3.2.1 Entität Eine Entität ist eine eigenständige Einheit, die im Rahmen des betrachteten Modells eindeutig identifiziert werden kann. Dieses Identifizierungsmerkmal wird als »Schlüssel« bezeichnet. Eine Entität kann z. B. Folgendes sein: 왘 ein Gegenstand

(z. B. ein bestimmtes Kfz) 왘 eine Kategorie von Gegenständen

(z. B. der Artikel 100 g Schokolade mit Lebertran, Marke Galaktika) 왘 eine Person 왘 ein Ereignis

(z. B. das Fußballspiel zwischen Werder Bremen und MSV Kayhude am 28.2.79) 왘 eine abstrakte Größe 왘 ein ausgefülltes Formular 왘 ein Dokument

3 Das »Netzwerk-Datenmodell« hat nichts mit den Computernetzwerken zu tun, sondern damit, dass die verschiedenen »Satzarten« des Datenmodells netzartig verknüpft werden konnten – im Gegensatz zum hierarchischen Datenmodell.

96

Grundbegriffe des Entity-Relationship-Modells

Merke! 왘 Eine Entität wird durch eine Menge von Eigenschaften (Attributen) beschrieben. 왘 Eine Eigenschaft hat einen Bezeichner und einen Wert. 왘 Eine Entität kann erzeugt werden. 왘 Die Eigenschaften einer Entität können geändert werden. 왘 Eine Entität kann gelöscht werden.

3.2.2 Abhängige Entität Eine Entität heißt abhängig, wenn die Existenz dieser Entität in dem betrachteten Modell von der Existenz einer anderen Entität abhängt und sie nicht durch eigene Attribute allein identifiziert werden kann. Abhängige Entitäten können erzeugt oder gelöscht und ihre Attribute können geändert werden. Die Entität, von der eine andere Entität abhängig ist, nennen wir Vaterentität. Beim Löschen der Vaterentität ist die abhängige Entität (automatisch) mit zu löschen. Beispiele für abhängige Entitäten sind: 왘 die Positionen einer Bestellung, die entfallen, wenn die Bestellung storniert

wird; 왘 Kinder von Arbeitnehmern (außer bei kleinen Firmen), da hier der Arbeitgeber

für die Zahlung des Kindergeldes zuständig ist. Beim Ausscheiden des Arbeitnehmers entfällt auch die Zahlung des Kindergeldes durch den Arbeitgeber. 왘 die Bankverbindung eines Kunden, der eine Einzugsermächtigung erteilt hat.

Beim Löschen des Kunden aus der Datenbank ist auch die Bankverbindung zu löschen.

3.2.3 Entitätentypen und Entitätenmengen Ein Entitätentyp ist eine abstrakte Zusammenfassung von Entitäten, die in dem betrachteten Modell durch dieselben Eigenschaften beschrieben werden können (z. B. Name, Länge in cm, Preis in Euro ...). Jede mögliche Entität in dem Modell kann durch den angegebenen Entitätentyp beschrieben werden. Neben den Entitätentypen verwenden wir den Begriff Entitätenmenge als Übersetzung des von Chen eingeführten Begriffs entity set. Unter einer Entitätenmenge verstehen wir eine konkrete Sammlung (z. B. die Menge aller zu einem festen Zeitpunkt in einem Unternehmen angestellten Mitarbeiter). Eine Abfrage an die Datenbank bezieht sich immer auf die Entitätenmenge, z. B.: »Suche alle Kunden mit dem Wohnort Bremerhaven, die im letzten Jahr für mehr als 100 € bestellt haben.« Die Anzahl der Elemente einer Entitätenmenge ist zu jedem Zeitpunkt durch die tatsächlich vorhandenen Entitäten gegeben – diese Menge kann sich zu jedem Zeitpunkt ändern.

97

3 Datenbankentwurf

3.2.4 Attribut Die Eigenschaften der Entitäten in einem Entitätentyp werden durch Attribute beschrieben. Ein Attribut besteht aus einem Attribut-Bezeichner und einer Domäne für mögliche Eigenschaften von konkreten Entitäten. Da Entitäten jeweils nur einen Ausschnitt der Wirklichkeit wiedergeben – nämlich den Ausschnitt, für den wir uns in einem bestimmten Anwendungsbereich interessieren –, ist es sehr wichtig festzulegen, durch welche Attribute wir die Entitäten beschreiben. So sind die Attribute, die eine Person als Patienten in einer Arztpraxis beschreiben, andere als die Attribute, die dieselbe Person als Mitarbeiter in einem Unternehmen beschreiben, und diese sind wiederum verschieden von den Attributen, die diese Person als Kunden einer Bank beschreiben. Daneben gibt es eine Unzahl von Attributen, für die sich glücklicherweise niemand interessiert (z. B. die exakte Anzahl der Haare).

3.2.5 Beziehung Mehrere Entitäten können in einer Beziehung (engl. Relationship) stehen. Beziehungen können zweistellig sein oder mehr als zwei Entitäten einbeziehen. Die Entitäten in einer Beziehung spielen jeweils eine Rolle – gegebenenfalls kann dieselbe Entität (auch in einer Beziehung) mehrere Rollen spielen, wenn dies nicht ausdrücklich ausgeschlossen wird. 왘 So kann ein Vertrag als eine vierstellige Beziehung mit den Rollen

Kontrahent_1, Kontrahent_2, Dokument, Notar aufgefasst werden: [Kontrahent_1 = 'Amann, Axel', Kontrahent_2 = 'Bemann, Bernd', Dokument = 'Kaufvertrag über das Grundstück, Gemarkung Hudekamp 63/17', Notar = 'Cefrau, Cäcilie'] 왘 Eine Bestellung ist eine dreistellige Beziehung mit den Rollen Besteller, Händ-

ler und Ware.

Beziehungen können auch durch Eigenschaften näher beschrieben werden. So kann in einer Bestellung das Bestelldatum eine relevante Eigenschaft sein. Falls der Preis nicht fest mit der Ware verbunden ist, sondern frei ausgehandelt wird, ist auch der Preis eine Eigenschaft der Bestellung.

3.2.6 Beziehungstyp (Relationship Type) Ein Beziehungstyp beschreibt eine Klasse von Beziehungen, die jeweils dieselben Rollen und Eigenschaften haben. Für die Entitäten der entsprechenden Rollen wird jeweils ein Entitätentyp vorgegeben. Auch Beziehungstypen können durch Attribute ergänzt werden, welche die Eigenschaften der Beziehung beschreiben.

98

Grundbegriffe des Entity-Relationship-Modells

3.2.7 Geschäftsregeln Neben der reinen Beschreibung von Daten, die einen bestimmten Unternehmensbereich widerspiegeln, enthält ein Datenmodell auch Geschäftsregeln. Das sind Vorgaben, die Einschränkungen an die möglichen Werte von Attributen und an die möglichen Beziehungen zwischen Entitätenmengen darstellen. Wichtige Geschäftsregeln sind z. B.: 왘 die Festlegung von eindeutigen Schlüsseln 왘 die Beschränkung von Beziehungen durch eine Angabe, wie viele Entitäten zu

einer anderen Entität in Beziehung stehen dürfen 왘 Einschränkungen der möglichen Wertebereiche von Attributen 왘 Abhängigkeiten zwischen Attributen wie Einkaufspreis < Verkaufspreis

Geschäftsregeln sind intentionale Bedingungen, d. h. Bedingungen, die aufgrund von Modellannahmen oder betrieblichen bzw. verwaltungsmäßigen Vereinbarungen zu gelten haben. Sie sind keine Bedingungen, die aufgrund zufälliger Umstände gerade gelten.

Beispiel (extensionale 1:1-Beziehung) Wenn in einem Unternehmen keine zwei Mitarbeiter dieselben Vor- und Nachnamen haben, ist damit extensional eine 1:1-Beziehung zwischen Mitarbeitern und Namen gegeben. Dies ist deswegen keine intentionale Beziehung, da nicht ausgeschlossen ist, dass gegebenenfalls später ein Mitarbeiter eingestellt wird, der denselben Namen hat wie ein schon vorhandener Mitarbeiter.

Beispiel (intentionale 1:1-Beziehung) Eine intentionale 1:1-Beziehung dagegen ist z. B. die Relation zwischen den Kraftfahrzeugen und ihren Zulassungskennzeichen, da hier eine Eindeutigkeit in beiden Richtungen (kein Fahrzeug mit zwei verschiedenen Kennzeichen/nicht dasselbe Kennzeichen für zwei verschiedene Fahrzeuge) vom System vorgegeben ist. Es kann hier »Systemfehler« geben (z. B. James Bond mit einem Aston Martin und mehreren wechselbaren Kennzeichen oder zwei verschiedene Fahrzeuge mit demselben Kennzeichen). Diese Systemfehler passieren aus organisatorischen Mängeln und/oder aus der Absicht heraus, das System für eigene Zwecke außer Kraft zu setzen. Die Verletzung von Regeln ist kein Argument dagegen, Regeln einzuführen, sondern ein Argument dafür, bessere Maßnahmen zur Durchsetzung der Regeln einzuführen. (Dieses gilt natürlich nur für sinnvolle Regeln – Regeln, die aus dem einzigen Grund »Das haben wir immer schon so gemacht« bestehen, sollten einer Überprüfung unterzogen werden.)

99

3 Datenbankentwurf

Merke: 왘 Extensionale Beziehungen können nachträglich als solche festgestellt werden.

Sie bestehen zufällig, oder ihre Gründe spielen für das Datenmodell keine Rolle. 왘 Intentionale Beziehungen sind nicht zufällig, sondern aus der Sache begründet.

Ihre Einhaltung muss mittels geeigneter Maßnahmen durch das DatenbankVerwaltungssystem sichergestellt werden. Für die Datenmodellierung spielen ausschließlich intentionale Beziehungen eine Rolle.

3.2.8 Schlüssel Jede Entität muss durch ihre Attribute eindeutig identifizierbar sein. Bei abhängigen Entitäten reichen Attribute, um jeweils die von einer Vaterentität abhängigen Entitäten zu unterscheiden. Dazu ist jeweils ein Schlüssel festzulegen, der aus einem Attribut oder aus einer Menge von Attributen besteht. Es besteht dann die Regel, dass es keine zwei Entitäten geben kann, die in allen Attributen des Schlüssels übereinstimmen. Für abhängige Entitäten bedeutet dies, dass es keine zwei Entitäten geben kann, die von derselben Vaterentität abhängen und in allen Attributen des Schlüssels übereinstimmen.

Beispiele von Identifikationsschlüsseln: 왘 Kfz-Kennzeichen 왘 Fahrgestellnummer eines Kfz 왘 Motornummer eines Kfz 왘 Signatur eines Buches in einer Bibliothek 왘 ISBN-Nummer eines Buches 왘 EAN (Europäische Artikelnummer) 왘 Gebäudenummer und Raumnummer für einen Raum auf einem Gelände

Notwendige Eigenschaften von Schlüsselsystemen Da vorhandene Identifizierungssysteme (z. B. Name, Vorname) nicht immer die strengen Anforderungen an einen identifizierenden Schlüssel erfüllen, müssen wir häufig ein künstliches Schlüsselsystem einführen. Nach [Zehn85] hat ein künstliches Identifikationssystem folgende Forderungen zu erfüllen: 왘 Eindeutigkeit: Für jede Entität gibt es genau einen Identifikationsschlüsselwert,

der anderweitig nie vorkommt. Der Schlüssel ist unveränderlich. 왘 Laufende Zuteilbarkeit: Eine neu auftretende Entität erhält ihren Identifikations-

schlüssel sofort.

100

Grundbegriffe des Entity-Relationship-Modells 왘 Kürze, Schreibbarkeit: Ein Identifikationsschlüssel soll (relativ) kurz sein und

leicht geschrieben werden können. Dieses gilt allerdings nur für Verfahren, in denen der Schlüssel auch manuell eingegeben werden muss. Die folgende Eigenschaft steht mit den vorstehenden im Widerspruch (insbesondere mit der Unveränderlichkeit) und sollte nur Beachtung finden, sofern die obigen Eigenschaften dadurch nicht beeinträchtigt werden: 왘 Sprechender Schlüssel: Aus dem Identifikationsschlüssel sollen gewisse

Eigenschaften der Entität ersichtlich sein.

Zur Problematik von sprechenden Schlüsseln Sprechenden Schlüsseln sieht man an, dass sie aus einem anderen Bereich stammen, nämlich einem Bereich, der ohne Unterstützung von Datenbanken konzipiert wurde. Verschiedene Informationen sind in dem Schlüssel untergebracht, um ohne DV-Unterstützung gewisse Eigenschaften schnell finden zu können. Zum Beispiel ist in Deutschland (wie auch in Frankreich und der Schweiz) auf den Kfz-Zeichen die Zulassungsstelle erkennbar – in der Regel durch eine Buchstabenkombination oder in einem Sonderfall durch eine Buchstabenkombination (»HB«) und die Anzahl der Ziffern (bis zu drei für die Kommune Bremen, vier für die Kommune Bremerhaven). In einigen Nachbarstaaten wie Dänemark und Schweden ist diese sprechende Eigenschaft der Schlüssel vor längerer Zeit abgeschafft worden. Es entfällt dort auch die Notwendigkeit, das Kennzeichen bei Umzug oder Verkauf des Pkw zu ändern. Bei Systemen, die DV-unterstützt auf einer Datenbank laufen, kann eine Entität durch die Angabe beliebiger Eigenschaften gefunden werden – diese Eigenschaften müssen nicht Bestandteil des Schlüssels sein. Falls diese Eigenschaften sich ändern, müsste der Schlüssel geändert werden. Schlüssel wie L-MUE85 für einen Lieferanten mit Namen Müller, der seit 1985 Geschäftspartner ist, sollten in einem DV-gestützten System nicht vergeben werden. In Systemen, die in erster Linie von menschlichen Lesern verarbeitet werden (wie dieses Buch), sind dagegen Schlüssel wie die hier verwendeten Kürzel für Literaturverweise, z. B. »[Zehn85]« für eine Schrift von Zehnder, die im Jahre 1985 erschienen ist, durchaus sinnvoll. Ein verwandtes Problem ist die Vergabe und Verwaltung von Schlüsseln in einer hierarchischen Organisation, in der eine zentrale Organisation mögliche Schlüsselbereiche an beteiligte Organisationen vergibt, die wiederum eigenverantwortlich diese Bereiche weiterverwalten. Dieses kann wegen der laufenden Zuteilbarkeit erforderlich werden. Beispiele hierfür sind die EAN (Europäische Artikelnummer), ISBN (internationale Standardbuchnummer) und die Internet-Adressen – die allerdings auch eine sprechende Komponente haben (wie hs-bremerhaven.de für die Hochschule Bremerhaven), um dem menschlichen Benutzer das »Speichern« der Adresse zu erleichtern. Bei der ISBN sind internationale Ziffern für die verschiedenen Länder vergeben worden, in jedem Land werden Nummern für die Verlage vergeben, und jeder Verlag nummeriert seine Bücher selbstständig durch.

101

3 Datenbankentwurf

3.2.9 ER-Diagramme In [Chen76] sind neben den Begriffen des Entity-Relationship-Modells (kurz ERM) auch Symbole für eine grafische Darstellung eingeführt worden, die später auf unterschiedliche Weise ergänzt und abgeändert wurden. In der Folge sind viele Variationen dieser Diagramme erschienen, da die ursprünglich von Chen eingeführten Diagramme für Systeme mit einer realistischen Größe nicht anwendbar sind. Wir verwenden die Variante, die in einigen Softwarewerkzeugen, z. B. dem PowerDesigner, Verwendung findet. Zum Einstieg stellen wir das konzeptionelle Datenmodell der Versand-Datenbank als ER-Diagramm dar. Auftraggeber

Bestellung

Kunde

für_Bankeinzug

Position

Artikel

Girokonto

MWSt_Satz

Abbildung 3.3: ER-Modell (Versandhandel)

Bedeutung: Die folgenden Bedeutungen ergeben sich aus dem Diagramm von »Girokonto« bis »MwSt_Satz« gegen den Uhrzeigersinn gelesen. 왘 Girokonto ist ein abhängiger Entitätentyp, d.h., dass ein Girokonto nur vorhan-

den sein kann, wenn ein entsprechender Kunde in der Datenbank vorhanden ist. Beim Löschen des Kunden ist das angegebene Girokonto automatisch zu löschen. 왘 Ein Kunde kann (maximal) ein Girokonto angeben (zum Bankeinzug). 왘 Eine Bestellung ist für höchstens einen Kunden bestimmt. 왘 Ein Kunde kann mehrere Bestellungen aufgeben. 왘 Eine Bestellung kann mehrere Bestellpositionen enthalten.

102

Grundbegriffe des Entity-Relationship-Modells 왘 Eine Bestellposition gehört zu genau einer Bestellung und ist von ihr abhängig,

왘 왘 왘 왘

d.h., eine Bestellposition kann nur im Rahmen einer Bestellung erzeugt werden. Wenn eine Bestellung storniert wird, wird automatisch die Bestellposition mit storniert. Eine Bestellposition enthält genau einen Artikel. (Über die Menge des Artikels ist damit noch nichts ausgesagt.) Ein Artikel kann in mehreren Bestellpositionen auftreten. Einem Artikel ist genau ein Mehrwertsteuersatz zugeordnet. Derselbe Mehrwertsteuersatz kann für verschiedene Artikel verwendet werden.

Beim »Lesen« eines Beziehungstyps ist es wichtig, jede Richtung einzeln zu betrachten. Das bedeutet, zu einer Beziehung gehören im Regelfall zwei Sätze, beispielsweise die letzten beiden in der obigen Aufzählung. Jeder Satz beschreibt, wie viele Exemplare des jeweils anderen Entitätstyps einem Exemplar des betrachteten Typs zugeordnet sind. Deswegen fangen, wie man am Beispiel nachvollziehen kann, diese Sätze üblichxerweise mit dem Wort »Ein« oder »Jede« oder »Derselbe« oder ähnlich an, niemals aber mit »Mehrere«. Falsch bzw. unvollständig wäre also die Kennzeichnung der Beziehung zwischen Artikel und Mehrwertsteuersatz mit den beiden Sätzen: »Ein Mehrwertsteuersatz kann für mehrere Artikel verwendet werden. Mehrere Artikel verwenden einen Mehrwertsteuersatz.« Beide Sätze machen nur ein und dieselbe Aussage mit veränderter Wortstellung.4

3.2.10 Kardinalitäten von zweistelligen Beziehungstypen Im Folgenden betrachten wir nur zweistellige Beziehungstypen. Grundsätzlich sind im Entity-Relationship-Modell auch Beziehungstypen mit mehr als zwei Rollen möglich. Die Behandlung von Beziehungstypen mit mehr als zwei Rollen betrachten wir weiter unten. Bei zweistelligen Beziehungstypen stellt sich die Frage, zu wie vielen Elementen der jeweils anderen Entitätenmenge ein Element in Beziehung stehen kann. Wenn Ent_1 und Ent_2 zwei Entitätenmengen sind, die in dem zweistelligen Beziehungstyp R stehen, interessieren wir uns dafür, wie viele Elemente aus Ent_2 jeweils zu einem Element aus Ent_1 in Beziehung stehen können (und umgekehrt). Als Mindestzahlen interessieren uns 0 oder 1 (obwohl es sicher auch Modelle gibt, wo die Mindestzahl 2 oder eine noch größere Zahl eine Rolle spielt). 0: optional, d.h., es kann Elemente aus Ent_1 geben, für die kein Element aus Ent_2 in der Relation R steht. 1: obligatorisch, d.h., für jedes Element aus Ent_1 muss (mindestens) ein Element aus Ent_2 in der Relation R stehen. 4 Genauso wie die beiden Sätze aus dem Struwwelpeter: »Ich esse meine Suppe nicht. Nein. meine Suppe ess‘ ich nicht.«

103

3 Datenbankentwurf

Wie wir in Abbildung 3.4 sehen, ist diese Unterscheidung in der ursprünglichen Notation von Chen nicht möglich. Als Höchstzahl unterscheiden wir nur zwischen »höchstens 1« und »keine Einschränkung« (obwohl es auch hier sicher andere relevante Beschränkungen gibt, z. B. »höchstens 2«). 1: eindeutig, d.h., für jedes Element aus Ent_1 kann nur höchstens ein Element aus Ent_2 in der Beziehung R stehen. *

mehrdeutig, d.h., es kann Elemente aus Ent_1 geben, für die mehr als ein Element aus Ent_2 in der Relation R stehen.

In der Verknüpfung gibt es somit vier Möglichkeiten in einer Richtung (vgl. die Darstellungen in Abbildung 3.4): 0..1, 0..*, 1..1, 1..*. Umgangssprachlich dagegen werden diese Beziehungen nur als 1:1-Beziehungen, n:1-Beziehungen oder n:m-Beziehungen bezeichnet. Es werden nur die maximalen Kardinalitäten betrachtet. Dieses hat auch seine Ursache darin, dass die Unterscheidung, ob mehrere Entitäten mit einer Entität in Beziehung stehen können oder nur maximal eine, Konsequenzen auf die Speicherstruktur hat – im ersteren Fall müssen wir in irgendeiner Weise eine Konstruktion vorsehen, die es ermöglicht, mehrere Elemente zu verwalten. Im letzteren Fall können wir das Element, das in Beziehung steht, direkt angeben. Dagegen ist die Frage, ob zu einer Entität notwendigerweise ein weiteres Element in Beziehung stehen muss, nicht von so weitreichender Konsequenz: Wir können die Nichtexistenz durch eine Nullmarke anzeigen.

(1)

(3)

(4)

0..1

1

c

0..*

N

mc

1..1

1

1

1..*

N

m

(2)

Abbildung 3.4: Verschiedene Beschreibungen von Kardinalitätsbedingungen

104

Grundbegriffe des Entity-Relationship-Modells

Legende: Beschreibung des Bereichs (* : keine obere Grenze) – wird u. a. in der UML benutzt Darstellung in ER-Diagrammen Darstellung nach [Chen76] Darstellung nach [Zehn85] Im Folgenden werden einige zweistellige Relationen beispielhaft dargestellt. Es ist zu beachten, dass die intendierten Beziehungen gegebenenfalls auch anders dargestellt werden können – es hängt jeweils vom festgelegten Kontext ab. So ist im ersten Beispiel in anderen Kulturkreisen auch eine Zuordnung von einem Mann zu mehreren Frauen, gegebenenfalls auch von einer Frau zu mehreren Männern möglich. Nach dem oben über intentionale Beziehungen Gesagten ist die Existenz von Bigamie kein Argument, dass die Ehe generell als m:n-Beziehung aufzufassen ist. Des Weiteren gilt die angegebene Bedingung im Rahmen der mitteleuropäischen Rechtsordnungen auch nur, wenn wir jeweils einen festen Zeitpunkt wählen – wenn dagegen eine historische Komponente enthalten ist, kann natürlich (juristisch einwandfrei) ein Mann zu verschiedenen Zeiten mit mehreren Frauen verheiratet sein – und vice versa. Ehefrau

Ehemann

Mann

Kfz-Kennzeichen

Motor

Reifen

Ehe

Zulassung

Einbau

Einbau

Frau

Kfz

Kfz

Kfz

105

3 Datenbankentwurf

Einwohner

Bundesbürger

Wohnsitz

Gemeinde in BRD

Anmeldung

Wahlberechtigter

erster Wohnsitz Gemeinde in BRD

Bundesbürger mind. 18 Jahre

Anmeldung

Student

Studiengang

E inschreibung

Auftra ggeber

Auftrag

Kunde

Abbildung 3.5: Beispiele für zweistellige Beziehungen

Es ist auch möglich, dass bei einer zweistelligen Relation die beteiligten Entitätentypen identisch sind, wie in folgenden Beispielen: Vorgesetzter

Unterstellter

Mitarbeiter

Mitarbeiter

Aggregat

Teil

Baugruppe

Teil

ist enthalten in

Abbildung 3.6: Zweistellige Beziehungen einer Entitätenmenge mit sich selbst

Hierfür wird in Diagrammen üblicherweise eine Entitätenmenge dargestellt und die Beziehung als Schleife von unten nach oben oder von rechts nach links. Vorgesetzter

Mitarbeiter

Aggregat

Unterstellter

Teil

Abbildung 3.7: Alternative Darstellung von Abbildung 3.6

106

Baugruppe

Grundbegriffe des Entity-Relationship-Modells

Es hat sich gezeigt, dass es aus mehreren Gründen sinnvoll ist, die Kardinalität einer Beziehung jeweils an genau der gegenüberliegenden Entitätenmenge anzuschreiben und nicht, wie sie von Chen vorgeschlagen wurde und von uns oben eingeführt worden ist. Gezählt wird dann nicht die Anzahl Exemplare von Ent_2, die mit Ent_1 in Beziehung stehen, sondern die Anzahl von Beziehungen, die von einem Element aus Ent_1 zu Elementen aus Ent_2 bestehen können. Dieses führt leider zu einer gewissen Verwirrung in der Literatur, da bei einigen deutschen Autoren diese alternative Symbolik bevorzugt wird, während im internationalen Bereich und auch in der objektorientierten Modellierungssprache UML5 die oben eingeführte Notation verwendet wird. Daher ist es unumgänglich, vor Verwendung von ER-Diagrammen die gewählte Nomenklatur zu erklären.

3.2.11 Domänen Auf der konzeptionellen Ebene, d. h. auf der Ebene des Entwurfs eines Datenmodells, sind für die Beschreibung von Wertebereichen die Attribute Domänen auf einem abstrakten, d. h. anwendungsnahen Niveau, zu verwenden, z. B. Postleitzahl, Familienstatus oder Uhrzeit.6 Diese Vorgehensweise hat zwei Gründe: 왘 Die Entwurfsdokumentation ist klarer, wenn die Bedeutung von möglichen

Attributwerten inhaltlich beschrieben wird. 왘 Eine Festlegung auf Datentypen sollte erst erfolgen, wenn das Datenbank-Ver-

waltungssystem feststeht, da in Datenbank-Verwaltungssystemen unterschiedliche Möglichkeiten zur Verfügung stehen (z. B. Datentyp Uhrzeit, Möglichkeit der Kodierung von Mengen mit festgelegten Werten, Datentyp für logische Werte). In der Regel können sinnvollerweise nur Werte einer Domäne mit Werten derselben Domäne verglichen werden. (Es macht keinen Sinn, eine Kundennummer mit einer Postleitzahl zu vergleichen.) Wenn in Ausnahmefällen etwas anderes beabsichtigt ist, ist es jeweils zu dokumentieren.

3.2.12 Fallbeispiel: Versandhandel als ER-Modell Im Folgenden stellen wir die in Kapitel 2 eingeführte Versand-Datenbank als ERDiagramm vor, wie es vom PowerDesigner erstellt wird. Der Unterschied zu dem relationalen Modell ist, dass es hier keine Fremdschlüssel gibt. Die Beziehungen zwischen verschiedenen Entitätentypen werden durch Beziehungen dargestellt.

5 Hierauf gehen wir in Kap. 3.3.1 ein. 6 Zum Domänenkonzept vgl. Kap. 2.1.

107

3 Datenbankentwurf

Bei dieser Darstellung werden die Attribute und ihre Datentypen mit angegeben. Schlüsselattribute sind unterstrichen. Somit kann man nach diesem Modell recht zügig eine Datenbankstruktur in einem DBMS entwickeln. Wenn die Modelle umfangreich werden, ist diese ausführliche Darstellung möglicherweise zu unübersichtlich. Deshalb gehört es zu den Forderungen an Entwurfswerkzeuge, auf Wunsch Details aus- oder einblenden zu können. Bei der Definition des Entitätentyps »Girokonto« existiert kein eigener Schlüssel, sondern der Schlüssel wird von »Kunde« geerbt. Da die Beziehung eindeutig ist, wird kein eigenes (zusätzliches) Schlüsselattribut benötigt. Bestellung bestell_nr bestelldatum lieferdatum rechnungsbetrag

BESTELL_KEY DATUM DATUM MONEY

Kunde Auftraggeber

kunden_nr status name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

KUNDEN_KEY KUNDEN_STATUS PERSONENNAME STRASSENNAME POSTLEITZAHL ORTSNAME DATUM DATUM ZAHLUNGSART

Position mwst_prozent bestellmenge liefermenge gesamtpreis

für Bankeinzug

PROZENTSATZ CARDINAL CARDINAL MONEY

Girokonto konto_inhaber blz konto_nr

Artikel artikel_Nr bezeichnung listenpreis bestand mindestbestand verpackung lagerplatz kann_wegfallen bestellvorschlag nachbestellung nachbestellmenge

ARTIKEL_KEY STRING MONEY CARDINAL CARDINAL STRING LAGER BOOLEAN ZEITPUNKT ZEITPUNKT CARDINAL

MWSt_Satz mwst prozent beschreibung

Abbildung 3.8: ER-Diagramm für Fallbeispiel »Versandhandel«

108

PERSONENNAME BANKLEITZAHL KONTONUMMER

CARDINAL PROZENTSATZ STRING

Grundbegriffe des Entity-Relationship-Modells

An dieser Stelle könnten wir noch diskutieren, ob einige dieser Attribute nicht eigenständige Entitätentypen beschreiben, z. B. »Lagerplatz«. Diese Frage kann nicht allgemein entschieden werden, sondern hängt von der jeweiligen Umgebung ab. Wenn die aufzubauende Datenbank auch eine Lagerverwaltung enthalten soll, ist es sicher sinnvoll, »Lagerplatz« mit Attributen wie Kapazität, Lage und klimatischen Bedingungen als eigenständigen Entitätentyp zu definieren. Solange aber keine Attribute für »Lagerplatz« benötigt werden, reicht es aus, »Lagerplatz« als Attribut von »Artikel« zu verwenden.

3.2.13 Umsetzung eines ER-Modells in ein relationales Datenmodell Im Folgenden diskutieren wir, wie aus dem ER-Diagramm ein Relationenschema abgeleitet wird. Am einfachsten ist es natürlich, die entsprechende Funktion des Design-Werkzeuges zu verwenden, das nicht nur das Modell grafisch darstellt (ggf. auch über mehrere Seiten), eine Beschreibung der Entitätsenypen und Beziehungstypen mit den Attributen, Beschreibungen und Anmerkungen gibt, sondern auch eine Umwandlung in ein relationales Datenmodell erzeugt (dort physikalisches Datenmodell genannt im Gegensatz zu dem konzeptionellen [ERM] und objektorientierten [UML]).

Aus Entitäten werden Relationen Der erste Schritt ist denkbar einfach: Für jeden Entitätentyp in dem Datenmodell wird eine Relation gebildet. Die Attribute dieser Relation sind zum einen die Attribute des Entitätentyps, ggf. kommen weitere Attribute für Fremdschlüssel hinzu. Der Schlüssel wird aus den Schlüsselattributen des Entitätentyps gebildet.

Funktionale Beziehungen werden durch Fremdschlüssel realisiert Funktionale Beziehungen, d. h. 1:1- oder n:1-Beziehungen, können in praktisch jedem Datenmodell dadurch dargestellt werden, dass in irgendeiner Weise ein Verweis auf die Speicherstruktur hergestellt wird, die den rechten Entitätentyp realisiert (Zeiger auf den Vatersatz beim Netzwerk-Datenbankmodell, Verweis über Schlüssel in irgendeiner Form bei fast allen Datenmodellen). Im relationalen Datenmodell wird dieser Verweis über sogenannte Fremdschlüssel realisiert (vgl. Kapitel 2.2). Bei n:1-Beziehungen nennen wir die Relation, die auf der Seite mit der Kardinalität 1..1 oder 0..1 steht, Vater-Relation, die andere SohnRelation. In die Sohn-Relation wird für jedes Schlüsselattribut der Vater-Relation ein Attribut eingebracht, das denselben Datentyp hat. Falls zwischen diesen beiden Relationen nur eine n:1-Beziehung existiert, können wir in der Regel die Bezeichner der Vater-Relation übernehmen. Die Beziehung zwischen Bestellung und Kunde kann also wie in Abbildung 3.9 aufgelöst werden.

109

3 Datenbankentwurf Kunde

Bestellung bestell_nr kunden_nr bestelldatum lieferdatum rechnungsbetrag

integer integer date date numeric(15,2)



kunden_nr = kunden_nr

kunden_nr status name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

integer

char(1) char(30) char(30) char(5) char(25) date date char(1)

Abbildung 3.9: Realisierung durch Fremdschlüssel

Falls mehrere solcher Beziehungen existieren wie in Abbildung 3.10, bietet es sich an, die Bezeichner der Rollen als Bezeichner für die Attribute zu übernehmen, wie in Abbildung 3.11.7 Dozent

Prüfung pruefungs_nr zeit raum

Prüfer

CARDINAL ZEITPUNKT RAUM_NR

Beisitzer

dozent_Nr name raum telefon

DOZENT_KEY PERSONENNAME RAUM_NR TELEFON_NR

Abbildung 3.10: ER-Diagramm mit zwei Beziehungstypen zwischen zwei Relationen

Pruefung pruefungs_Nr prüfer beisitzer zeit raum

integer char(3) char(3) timestamp char(5)



dozent_Nr = prüfer dozent_Nr = beisitzer

Dozent dozent_Nr name raum telefon

char(3) char(30) char(5) char(20)

Abbildung 3.11: Relation mit zwei Fremdschlüsseln

Der Unterschied zwischen der Kardinalität 0..1 und 1 besteht nur darin, dass im ersten Fall die Attribute des Fremdschlüssels Nullmarken zulassen, im zweiten Fall nicht.

7 Beim Betrachten der Diagramme fällt auf, dass als Datentypen manchmal benutzerdefinierte Domänen wie PERSONENNAME und manchmal systemspezifische Datentypen wie char(3) auftreten. Das liegt daran, dass das hier verwendete Werkzeug PowerDesigner beim Übergang auf das »physikalische Datenmodell« alle benutzerdefinierten Domänen in Standarddatentypen umwandelt und dies in der Grafik entsprechend darstellt.

110

Grundbegriffe des Entity-Relationship-Modells

Ein kleines Problem bekommen wir bei Anwendung dieser Methode bei 1:1-Beziehungen, wie z. B. in Abbildung 3.11. Es ist nicht klar, welche Relation die VaterRelation wird. Es gibt hier grundsätzlich drei Möglichkeiten: Motor

Fahrzeug fahrzeug_nr typ anzahl_zylinder

CARDINAL STRING CARDINAL

motor_nr hubraum anzahl_zylinder letzte_wartung

CARDINAL CARDINAL CARDINAL DATUM

Abbildung 3.12: 1:1-Beziehung

1) Wir erklären eine der beiden Relationen zur Vater-Relation und setzen den Fremdschlüssel entsprechend dem oben Gesagten in die Sohn-Relation ein. In dem ER-Diagramm in Abbildung 3.8 haben wir bei der Relation zwischen Girokonto und Kunde die Relation Kunde zur Vater-Relation erklärt. Dieses geschieht dort durch die Angabe »dominant role« für den Entitätentyp Kunde bei der Bearbeitung des Beziehungstyps zwischen den beiden Entitätentypen Kunde und Girokonto. 2) Wir verzichten darauf, eine der beiden Relationen zur Vater-Relation zu erklären, und setzen in jede der zwei Relationen einen Fremdschlüssel, der auf die jeweils andere Relation zeigt. Diese Lösung hat den Vorteil, dass ggf. schnellere Datenzugriffe in beide Richtungen möglich sind, aber den gravierenden Nachteil, dass sichergestellt werden muss, dass bei Änderung eines der beiden Fremdschlüssel der jeweils andere Fremdschlüssel auch mit aktualisiert werden muss. Dieses kann ggf. durch Einführung eines Triggers erfolgen (vgl. Kapitel 7.3.3). 3) Eine weitere Möglichkeit ergibt sich daraus, dass grundsätzlich zu überlegen ist, beide Entitätentypen durch eine gemeinsame Relation zu realisieren. Diese Relation erhält die Attribute von beiden Entitätenmengen (bei Namensgleichheit ist vorher ein Attribut umzubenennen), die für den nicht obligatorischen Entitätentyp in jedem Fall Nullmarken zulassen müssen. Dies ist allerdings bei einer 0..1:0..1-Beziehung in der Regel problematisch, da für Schlüsselattribute keine Nullmarken zulässig sind. Die Beziehung zwischen Girokonto und Kunde könnte dagegen ohne Probleme durch Übernahme der Attribute von Girokonto in Kunde realisiert werden. Motor

Fahrzeug fahrzeug_nr motor_nr typ anzahl_zylinder

integer

integer

varchar(64) integer

motor_nr = motor_nr

motor_nr hubraum anzahl_zylinder letzte_wartung

integer integer integer date

Abbildung 3.13: 1:1-Beziehung, realisiert durch einen Fremdschlüssel

111

3 Datenbankentwurf

Motor

Fahrzeug fahrzeug_nr motor_nr typ anzahl_sitze

motor_nr fahrzeug_nr hubraum anzahl_zylinder letzte_wartung

motor_nr = motor_nr

integer

integer

varchar(64) integer

fahrzeug_nr = fahrzeug_nr

integer integer integer integer date

Abbildung 3.14: 1:1-Beziehung, realisiert durch zwei Fremdschlüssel

Behandlung von abhängigen Entitäten Eine Sonderbehandlung erfahren n:1- oder 1:1-Beziehungen, bei denen die SohnRelation von der Vater-Relation abhängig ist. Da abhängige Entitäten nur im Rahmen der Vater-Entität existieren, erben sie auch den Schlüssel der Vater-Entität. Bei einer 1:1-Beziehung ist damit die abhängige Entität auch schon hinreichend identifiziert. Bei einer 1:n-Beziehung muss dagegen die abhängige Entität einen eigenen Schlüssel haben, der im Rahmen des Vatersatzes die Entität identifiziert. Es ist aber auch möglich, dass eine Entität von mehreren Vater-Entitäten abhängig ist, deren Schlüssel dann gemeinsam den Entitätenschlüssel bilden, wie in unserem Beispiel die Entität Position, die von Bestellung und Artikel abhängig ist. Bestellung

Kunde

integer integer date date numeric(15,2)

bestell_nr kunden_nr bestelldatum lieferdatum rechnungsbetrag



....

bestell_nr = bestell_nr

kunden_nr status name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

integer

char(1) char(30) char(30) char(5) char(25) date date char(1)

Position artikel_Nr bestell_nr mwst_prozent bestellmenge liefermenge gesamtpreis

char(4) integer decimal(3,1) integer integer numeric(15,2)



Girokonto kunden_nr konto_inhaber blz konto_nr

artikel_Nr = artikel_Nr

Artikel artikel_Nr mwst bezeichnung listenpreis bestand mindestbestand verpackung lagerplatz kann_wegfallen bestellvorschlag nachbestellung nachbestellmenge

char(4) integer varchar(64) numeric(15,2) integer integer varchar(64) integer smallint timestamp timestamp integer



Abbildung 3.15: Abhängige Entitäten

112

kunden_nr = kunden_nr

integer char(30) numeric(8) char(10)

Grundbegriffe des Entity-Relationship-Modells

Im Diagramm von Abbildung 3.8 haben wir hier zum einen die Beziehungen zwischen Kunde und Girokonto sowie zwischen Bestellung und Position und zwischen Artikel und Position dargestellt. In der Umsetzung in ein relationales Modell, wie ausschnittsweise in Abbildung 3.15 dargestellt, sehen wir, dass für die Relation Girokonto jetzt kunden_nr sowohl Primärschlüssel als auch Fremdschlüssel ist. In der Relation Position wird der Schlüssel zusammengesetzt aus den Fremdschlüsseln bestell_nr und artikel_nr. In Abbildung 3.16 wird eine abhängige Entität benutzt, die innerhalb der VaterEntität einen eigenen Schlüsselteil besitzt. Abbildung 3.17 zeigt das entsprechende relationale Modell. Mitarbeiter personal_nr name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

PERSONAL_KEY PERSONENNAME STRASSENNAME POSTLEITZAHL ORTSNAME DATUM DATUM ZAHLUNGSART

für Kindergeld

Kind kind_nr CARDINAL name PERSONENNAME geburtstag DATUM

Abbildung 3.16: Abhängige Entität mit eigenem Schlüsselteil Mitarbeiter personal_nr name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

PERSONAL_KEY PERSONENNAME STRASSENNAME POSTLEITZAHL ORTSNAME DATUM DATUM ZAHLUNGSART

personal_nr = personal_nr

Kind personal_nr kind_nr name geburtstag

PERSONAL_KEY

CARDINAL PERSONENNAME DATUM

Abbildung 3.17: Relationales Datenmodell für Abb. 3.16

113

3 Datenbankentwurf

Behandlung von n:m-Beziehungen Während – wie wir eben gesehen haben – n:1- und 1:1-Beziehungen sich durch Fremdschlüssel realisieren lassen, ist die Darstellung von m:n-Beziehungen in relationalen Datenbanken nicht unmittelbar möglich. Das hängt unter anderem mit der »ersten Normalform«8 zusammen, die wir in Abschnitt 3.4 über Normalformen besprechen. Um auf funktionale Beziehungen zu kommen, ersetzen wir die m:n-Beziehung durch die Einführung einer neuen (Entitäten-)Menge und zweier Funktionen. Diese Konstruktion ist immer möglich. Die neue Menge enthält für jedes Paar von Elementen, die in Beziehung stehen, einen Satz (bzw. Tupel im relationalen Modell). Die beiden Funktionen zeigen jeweils auf die entsprechenden Sätze (bzw. Tupel) der in Beziehung stehenden Entitätenmengen. Dieses sei an einem kleinen Beispiel dargestellt (vgl. Abbildung 3.18): Zwischen den Entitätenmengen Mitarbeiter = {Amann, Bemann, Cefrau, Demann, Efrau}

und Sprache = { Dänisch, Englisch, Finnisch, Französisch, Isländisch, Norwegisch, Schwedisch}

bestehe folgende zweistellige Beziehung: Fremdsprachenkenntnis = {[Amann, Englisch], [Amann, Spanisch], [Cefrau, Englisch], [Cefrau, Schwedisch], [Demann, Dänisch], [Efrau, Französisch], [Efrau, Dänisch], [Efrau, Isländisch]}

Wir können die zusammengehörenden Paare als Fakten (z. B. »Herr Amann spricht Englisch«, abgekürzt »A/En«) und somit als Entitäten auffassen. Damit wird Fremdsprachenkenntnis zu einer Entitätenmenge. Der Zusammenhang zwischen Mitarbeiter und Sprache wird durch die Projektionen auf die Komponenten der Paare ausgedrückt, d.h., es werden zwei Funktionen f : Fremdsprachenkenntnis → Mitarbeiter g : Fremdsprachenkenntnis → Sprache

und

definiert mit: f([A/En]) = Amann, f([A/Sp] = Amann, ..., f([E/Is]) = Efrau g([A/En]) = Englisch, g([A/Sp] = Spanisch, ..., g([E/Is]) = Isländisch

8 Wenn wir allerdings die Möglichkeiten der objektrelationalen Datenbanken, die in Kapitel 9 eingeführt werden, nutzen, können wir auf die erste Normalform verzichten. Es gibt dann auch weitere Möglichkeiten, Beziehungstypen zwischen Entitätentypen zu implementieren.

114

Grundbegriffe des Entity-Relationship-Modells

Mitarbeiter

Fremdsprachenkenntnis

Amann

Englisch

Amann

Spanisch

Cefrau

Englisch

Cefrau

Schwedisch

Demann

Finnisch

Efrau

Franz sisch

Efrau

D nisch

Efrau

Isl ndisch

Sprache

Abbildung 3.18: Darstellung einer zweistelligen Beziehung durch eine Relation

Mitarbeiter

FremdsprachenKenntnis

Sprache

Amann

A/En

D nisch

Bemann

A/Sp

Englisch

Cefrau

C/En

Finnisch

Demann

C/Se

Franz sisch

Efrau

D/Fi

Isl ndisch

E/Fr

Norwegisch

E/Dk

Schwedisch

E/Is

Spanisch

Abbildung 3.19: Darstellung einer zweistelligen Beziehung durch zwei Funktionen

Somit wird die n:m-Beziehung Fremdsprachenkenntnis im relationalen Datenmodell realisiert durch eine Relation Fremdsprachenkenntnis mit zwei Attributen, die zum einen jeweils Fremdschlüssel auf die beteiligten Relationen Mitarbeiter und Sprache sind, zum anderen gemeinsam den Primärschlüssel bilden. Bei n:1- und 1:1-Beziehungen haben wir unterschieden zwischen 0..1 und 1..1. Dieses ist relevant dafür, ob für den entsprechenden Fremdschlüssel eine Nullmarke zugelassen ist oder nicht. Bei n:m-Beziehungen müssen wir hier nicht unterscheiden, ob es sich um die Kardinalität 0..* oder 1..* handelt.

115

3 Datenbankentwurf

Mitarbeiter personal_nr name stra e plz ort gehalt

integer char(30) char(30) char(5) char(25) numeric(15,2)

personal_nr = personal_nr

Fremdsprachenkenntnis personal_nr sprach_id

integer char(2)

sprach_id = sprach_id

Sprache

sprach_id char(2) Sprache varchar(30)

Abbildung 3.20: Implementierung einer n:m-Beziehung im relationalen Datenmodell

3.2.14 Übungsaufgaben Aufgabe 3.1 Untersuchen Sie, welche Kardinalitäten in folgenden Beziehungen möglich sind. Geben Sie dafür gegebenenfalls Randbedingungen vor: Postleitzahlen ←→ Orte (in Deutschland) Steuernummer ←→ Steuerpflichtiger Kind ←→ Vater Kind ←→ Mutter Kind ←→ Elternteil Aufgabe 3.2 Erstellen Sie für die Beziehungen in Aufgabe 3.9 ER-Diagramme. Beachten Sie dabei, dass wir in c), d) und e) in der Aufgabenstellung nicht die Entitätentypen angegeben haben (die auf beiden Seiten jeweils »Person« sind), sondern Rollen.

116

Elemente der UML

Aufgabe 3.3 Erstellen Sie für die folgenden Angaben jeweils ein Entity-Relationship-Diagramm, und bestimmen Sie die Schlüsselattribute. a) Es gibt Personen, Bücher und Verlage. Jedes Buch hat einen oder mehrere Autoren. Jedes Buch kann zu gegebener Zeit von höchstens einem Ausleiher ausgeliehen werden. Ein Ausleiher kann mehrere Bücher leihen. Ein Buch ist von einem Verlag. b) Ein Auto ist von einem Hersteller. Es hat zum Zeitpunkt der Herstellung keinen Halter, danach höchstens einen Halter. Auf einen Halter können mehrere Autos eingetragen sein.

3.3 Elemente der UML Neben den Analyse- und Designmethoden, die auf dem reinen Datenmodell (wie ERM) und auf dem klassischen Programmierparadigma der imperativen Programmierung9 beruhen, haben sich Anfang der 90er-Jahre mehrere objektorientierte Analyse- und Designmethoden herausgebildet. Hierbei werden Objekte betrachtet, die nicht nur eine Existenz haben (wie Entitäten), sondern auch ein Verhalten. Das Verhalten wird durch Operationen beschrieben, die ein Objekt ausführen kann, wenn es eine entsprechende Nachricht (Auftrag) bekommt. Damit werden auch die Daten und die Algorithmen in einem einheitlichen Schema betrachtet. Ab Mitte der 90er-Jahre wurden verschiedene Analyse- und Designmethoden zu einer einheitlichen Methode zusammengefasst, die 1997 als Unified Modeling Language (UML) bei der Object Management Group (OMG) zur Standardisierung eingereicht und von dieser akzeptiert wurde (vgl. hierzu [Oest98] oder http://www. oose.de). In der UML gibt es mehrere Diagrammtypen, unter anderem: Anwendungsfalldiagramme, Klassendiagramme, Verhaltensdiagramme und Implementierungsdiagramme. Uns interessieren im Folgenden die Klassendiagramme. Aus folgenden Gründen ist die UML auch für die Zwecke des Entwurfs relationaler Datenbanken interessant: 왘 Die UML hat sich in sehr kurzer Zeit als anerkannter Standard etabliert. 왘 ER-Diagramme können auf relativ einfache Weise in Klassendiagramme

überführt werden.

9 Die Programmiersprache besteht in erster Linie aus Anweisungen, die auf der Basis von veränderbaren Variablen arbeiten. Beispiele sind FORTRAN, ALGOL, COBOL, PL/I, Pascal, Ada, C, Modula und andere. Daneben gibt es das funktionale Programmierparadigma, das schon sehr früh durch LISP unterstützt worden ist und in neuerer Zeit durch Miranda und andere Sprachen unterstützt wird. Mittlerweile hat sich das objektorientierte Programmierparadigma, das z. B. von C++, Smalltalk und Java unterstützt wird, gegen die anderen Programmierparadigmen durchgesetzt.

117

3 Datenbankentwurf 왘 Es gibt Konzepte, die nicht im ERM enthalten sind, die aber dennoch eine

große Rolle bei der Entwicklung relationaler Datenbanken spielen, nämlich die Konzepte Vererbung, Aggregation und Komposition. Wir werden daher im Folgenden zum einen diejenigen Konzepte der UML einführen, mit denen wir ER-Diagramme als Klassendiagramme darstellen können. Zum anderen werden wir die Konzepte Vererbung, Aggregation und Komposition einführen und zeigen, wie wir Modelle, die diese Konzepte benutzen, in relationale Datenbankschemata überführen können. Zu Beginn stellen wir die Notation der ERM und der UML gegenüber. Es ist allerdings zu beachten, dass die UML eine mächtigere Semantik hat und daher die entsprechenden Begriffe nicht äquivalent sind, sondern sich in diesem Zusammenhang nur entsprechen. So unterscheidet sich ein Objekt von einer Entität dadurch, dass es neben seiner Existenz auch ein eigenes Verhalten hat. Andererseits kann ein ERM in ein UML-Modell überführt werden – in diesem Fall wird aus einer Entität durch das Hinzufügen von Verhalten ein Objekt. Begriff der ERM

Begriff der UML

Entität

Objekt Exemplar (instance)

Entitätentyp

Klasse

Entitätenmenge

(extent)

Beziehung

Assoziation

Beziehungstyp

Assoziation

Attribut

Attribut

Tabelle 3.1: Vergleich von ERM- und UML-Notation

3.3.1 ER-Diagramme und UML-Klassendiagramme Entitätentypen werden in der UML zu Klassen. Sie werden wie im ERM in einem Kästchen dargestellt. Dieses Kästchen kann – wie in den oben eingeführten ERDiagrammen – auch die Attribute der Klasse enthalten. Danach können die Operationen der Klasse eingetragen werden – jeweils durch einen waagerechten Strich getrennt. Es sind weitere Angaben möglich; hier verweisen wir auf die entsprechende Literatur. Es stellt sich nun die Frage, wie es mit der Identifizierbarkeit von Objekten aussieht. Für Entitätenmengen haben wir verlangt, dass es eine Kombination von Attributen gibt, die jede Entität eindeutig identifizieren. In der Objektorientierung ist der Blickpunkt etwas geändert worden. Auch hier benötigen wir ein eindeutiges Identifizierungsmerkmal – allerdings ist dieses Merkmal fest mit dem Objekt verbunden und kann nie geändert werden. Auch nach dem Löschen eines Objekts darf dessen Schlüssel nicht ein weiteres Mal verwendet werden. Diese Objekt-

118

Elemente der UML

Identifizierer sollen darüber hinaus nicht nur innerhalb einer Klasse, sondern innerhalb eines Datenmodells einheitlich sein.10 Sie werden üblicherweise automatisch vergeben und sind nach außen nicht sichtbar. Daneben ist es natürlich nicht verboten, dass es Attributkombinationen gibt, welche die Schlüsseleigenschaft haben. Die Eigenschaft eines Attributes oder einer Attributkombination, innerhalb einer Entitätenmenge immer eindeutig zu sein, stellen wir als sog. Zusicherung in geschweiften Klammern mit dem Schlüsselwort »unique« dar. Bei zusammengesetzten Schlüsseln muss diese Zusicherung immer die Form {unique(attr1,attr2,... )} haben. Bei Schlüsseln, die aus einem Attribut bestehen (der häufigere Fall), schreiben wir die Zusicherung {unique} hinter das entsprechende Attribut. Bei der Umsetzung in ein Relationenschema müssen wir dann entweder eine eindeutige Attributkombination als Primärschlüssel auszeichnen oder einen künstlichen Schlüssel einfügen, der beispielsweise ein automatisch hochzuzählender numerischer Wert ist. Beziehungstypen im ERM werden in der UML zu Assoziationen. Wenn eine Assoziation zwei Rollen hat, wird sie durch eine Linie zwischen den beiden beteiligten Klassen bezeichnet. Die Kardinalität der Assoziation drückt ähnlich wie im ERM die Anzahl der Exemplare einer Klasse aus, die an der Beziehung beteiligt sind. Es treten unter anderem folgende Bezeichnungen auf: 1 für »genau 1«, * für »keiner, einer oder mehrere«, 0..1 für keiner oder einer, 1..* für »mindestens einer«. Es sind weitere Kombinationen möglich wie 2 für »genau zwei«, 4,6,10 für »vier, sechs oder zehn«, 12..20 für »zwischen 12 und 20«, 5..* für »mindestens 5« oder 0..5 für »höchstens fünf«. So wird das ER-Diagramm von Abbildung 3.8 zu dem Klassendiagramm in Abbildung 3.21.

3.3.2 Aggregation und Komposition in UML In dem Diagramm in Abbildung 3.21 haben wir die abhängigen Entitäten Position und Girokonto jeweils als Komponenten (ausgefüllte Raute) und Aggregate (offene Raute) dargestellt. Durch die Aggregation wird ausgedrückt, dass Objekte einer Klasse in Objekten einer anderen Klasse in gewisser Weise enthalten oder ihnen logisch zugeordnet sind. So ist ein Motor ein Aggregat eines Kraftfahrzeugs; andere Aggregate des Kraftfahrzeugs sind zum Beispiel die Räder, das Getriebe und die Bremsanlage. In unserem Beispiel ist die Bestellposition als Aggregat dem Artikel untergeordnet. Ein Aggregat kann auch gleichzeitig »mehrere Eltern« haben (vgl. [Balz00]).

10 Dies ist auch schon deshalb sinnvoll, da durch das weiter unten eingeführte Vererbungsprinzip ein Objekt gleichzeitig Element mehrerer Klassen sein kann.

119

3 Datenbankentwurf

Kunde

Bestellung bestell_nr bestelldatum lieferdatum rechnungsbetrag

Bestell_key Datum Datum Money

0..*

1..1

kunden_nr status 1..1 Auftraggeber name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

Kunden_key Kunden_status Personenname Straßenname Postleitzahl Ortsname Datum Datum Zahlungsart

1..* 1..1

Position mwst_prozent bestellmenge liefermenge gesamtpreis

Prozentsatz CARDINAL CARDINAL Money

1..1 für Bankeinzug

Girokonto

0..*

konto_inhaber Personenname blz Bankleitzahl konto_nr Kontonummer 1..1

Artikel artikel_Nr bezeichnung listenpreis bestand mindestbestand verpackung lagerplatz kann_wegfallen bestellvorschlag nachbestellung nachbestellmenge

Artikel_key String Money CARDINAL CARDINAL String Lager Boolean Zeitpunkt Zeitpunkt CARDINAL

MWSt_Satz 0..*

1..1

mwst CARDINAL prozent Prozentsatz beschreibung String

Abbildung 3.21: Klassendiagramm für Versandhandel Kfz

Gebäude

Motor

Raum

Abbildung 3.22: Aggregation und Komposition

120

Elemente der UML

Die Komposition ist ein Spezialfall der Aggregation mit der zusätzlichen Eigenschaft, dass die Komponenten nur im Zusammenhang mit genau einem Vaterobjekt existieren können. Konkrete Beispiele für die Komposition sind Gebäude mit ihren Teilen wie Zimmern, Dach, Eingängen, Fensterlöchern, Kabelschächten. Ein abstraktes Beispiel sind Bestellungen mit ihren Bestellpositionen. Ob es sich im konkreten Fall um eine Komposition oder um eine Aggregation handelt, hängt also von der Frage ab, ob ein Teilobjekt unabhängig von dem Vaterobjekt existieren kann. Betrachten wir zum Beispiel einen modular aufgebauten Lehrgang, der aus Kurseinheiten besteht. Wenn Kurseinheiten auch unabhängig von dem Lehrgang bestehen können, handelt es sich um eine Aggregation; wenn Kurseinheiten immer genau einem Lehrgang zugeordnet sind, handelt es sich um eine Komposition. Beispiele, die Aggregation und Komposition benutzen, sind in den Abbildungen 3.21 und 3.25 dargestellt. In Abbildung 3.21 stellen wir eine Bestellung als Komposition der Positionen dar. Ebenso kann ein Girokonto als Komponente des Kunden aufgefasst werden (ist dem Kunden untergeordnet; wird nur im Zusammenhang mit einem Kunden angelegt; wird gelöscht, wenn der Kunde gelöscht wird). In Abbildung 3.25 stellen wir ein Fahrzeug dar, das verschiedene Aggregate enthält. Ein Objekt in einer Komposition gehört immer genau einem Vaterobjekt an – ein Objekt einer Aggregation kann zu keinem, einem oder mehreren Vaterobjekten gehören. So kann ein Motor in einem Fahrzeug eingebaut sein oder auf Lager liegen (Kardinalität 0..1); eine Kurseinheit kann zu einem Lehrgang gehören oder zu mehreren Lehrgängen (Kardinalität 1..*); ein Mitarbeiter kann an keinem, einem oder mehreren Projekten beteiligt sein (Kardinalität *). Die Entsprechungen von Aggregation und Komposition im ER-Modell sind Beziehungstypen – eine Komponente stellt dabei jeweils eine abhängige Entität dar. Somit spielt bei der Komposition die Existenzabhängigkeit eine Rolle bei der Frage des Verhaltens beim Löschen eines Vatersatzes (Löschweitergabe, vgl. Kapitel 2.2.2).

3.3.3 Das Vererbungskonzept Ein wichtiges Konzept aus der Objektorientierung ist das Konzept der Vererbung. Der Begriff stammt aus der Biologie und dort speziell aus der Klassifizierung von Lebewesen. So ist jedes Säugetier ein Wirbeltier und »erbt« alle Eigenschaften, die allen Wirbeltieren gemein sind, z. B. wird die Stabilität des Körpers in erster Linie durch ein im Körper gelegenes System von Knochen oder Knorpeln gewährleistet; es hat einen geschlossenen Blutkreislauf, durch den die Körperzellen mit Nährstoffen und Sauerstoff versorgt werden, es hat einen Kopf mit einem Mund, durch den die Nahrung zugeführt wird, und zwei Linsenaugen. Des Weiteren ist jedes Raubtier ein Säugetier und erbt alle Eigenschaften der Säugetiere wie die Existenz von

121

3 Datenbankentwurf

vier Extremitäten11, der Nachwuchs wird lebend geboren und in den ersten Tagen des Lebens durch Milchdrüsen des Muttertieres versorgt. Jeder Löwe ist ein Raubtier und erbt damit alle Eigenschaften von Raubtieren. Für die Objektorientierung bedeutet das Vererbungsprinzip zum einen, dass es Unterklassen gibt, die weitere Attribute haben. Zum anderen wird das Verhalten der Oberklassen auf die Unterklassen vererbt – es kann aber auch überschrieben werden. Damit ist jedes Objekt einer Unterklasse auch gleichzeitig ein Objekt der entsprechenden Oberklasse (jedes Säugetier ist ein Wirbeltier), es hat aber in der Regel weitere Attribute, die es in der Oberklasse nicht gibt. So gibt es für Säugetiere Attribute wie Länge der Vorderextremitäten oder Volumen der Lunge, die nicht für alle Wirbeltiere sinnvoll sind, z. B. nicht für Fische. Üblicherweise können Klassen Objekte enthalten, die nicht in irgendeiner Unterklasse liegen. Daneben gibt es abstrakte Klassen. Das sind Klassen, die selbst keine eigenständigen Objekte enthalten können, sondern alle ihre Objekte sind Objekte irgendeiner Unterklasse. Dieser Begriff spielt in der Informatik eine größere Rolle als in der Modellierung der realen Welt. Die Struktur einer Klasse legt fest, wie die Objekte dieser Klasse repräsentiert werden. Wenn wir zum Beispiel eine Klasse »geometrische Figuren« haben, die Kreise, Rechtecke und Dreiecke enthält, gibt es für jede der Unterklassen Kreis, achsenparalleles Rechteck und Dreieck eine eigenständige Repräsentation12 – aber keine allgemeine Repräsentation für die Klasse der geometrischen Figuren. Bei abstrakten Klassen schreiben wir den Namen der Klasse kursiv oder das Wort »abstrakt« in geschweifte Klammern hinter den Namen der Klasse. Wenn die Unterklassen disjunkt sind, können wir in der Oberklasse ein Attribut angeben, das festlegt, zu welcher Oberklasse das Objekt jeweils gehört. Dieses Attribut wird Diskriminator genannt.

Beispiel Versandhandel, mit Vererbung In unserem Datenmodell Versandhandel können wir jetzt die Kunden mit Bankeinzug als Unterklasse der Klasse der Kunden darstellen. Diese haben alle Attribute von Kunden und zusätzlich die Attribute, die ihr Girokonto beschreiben. Um zu zeigen, dass eine Oberklasse üblicherweise mehr als eine Unterklasse hat, haben wir die Kunden, die mit Kreditkarte zahlen, als weitere Unterklasse dargestellt. Die Klasse der Kunden ist nicht abstrakt, da es Kunden gibt, die weder mit Kreditkarte

11 Wale sind auch Säugetiere, haben aber keine vier Extremitäten – dies zeigt die Grenzen dieses Modells in der Anwendung auf die Biologie. Auch sind Quadrate eine Spezialisierung von Rechtecken, haben aber weniger Attribute, nämlich nur eine Seitenlänge. Im objektorientierten Paradigma ist es dagegen nicht zulässig, dass Strukturen einer Oberklasse in einer Unterklasse verschwinden. Das Verhalten kann dagegen in einer Unterklasse neu definiert werden. 12 Z. B. Mittelpunkt und Radius für einen Kreis, linker unterer Punkt und Ausdehnung in x-Richtung und Ausdehnung in y-Richtung für ein achsenparalleles Rechteck, drei Punkte für ein Dreieck.

122

Elemente der UML

zahlen, noch am Bankeinzugsverfahren teilnehmen. In Abbildung 3.23 stellen wir den Teil des Diagramms, der die Kundendaten enthält, als Klassendiagramm mit Vererbung dar. Kunde + + + + + + + + +

kunden_nr status name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

Kunden_key Kunden_status Personenname Straßenname Postleitzahl Ortsname Datum Datum Zahlungsart

zahlung

Kunde mit Kreditkarte + herausgeber Personenname + karten_nr Bankleitzahl + gültig_bis Kontonummer

Kunde mit Girokonto + konto_inhaber + blz + konto_nr

Personenname Bankleitzahl Kontonummer

Abbildung 3.23: Klassendiagramm für Versandhandel, Version 2

Für die Umsetzung einer Vererbungshierarchie in ein Relationenschema gibt es vom Grundsatz her drei Möglichkeiten, eine davon in zwei Varianten: 왘 Jede Klasse wird zu einer eigenständigen Relation

Für jede Klasse wird eine Relation gebildet, welche die im Diagramm angegebenen Attribute enthält, d. h. für die Oberklasse die gemeinsamen Attribute und für die Unterklassen die jeweils zusätzlichen Attribute. Wenn es in der Vaterklasse eine eindeutige Attributkombination gibt, wird diese zum Schlüssel der Oberrelation. Dieser Schlüssel wird gleichzeitig zum Schlüssel der Unterrelationen und Fremdschlüssel, um die zusätzlichen Daten der Unterklassen mit den jeweiligen Daten der Oberklasse zu verbinden. Falls die Oberklasse abstrakt ist und nur für die Unterklassen eindeutige Schlüssel definiert sind, werden in einem ersten Schritt die Schlüssel auf einen gemeinsamen Datentyp gebracht, zum Beispiel Zeichenketten mit der größten Schlüssellänge der einzelnen Unterrelationen. Der Primärschlüssel der Oberrelation wird dann aus einer Kombination aus dem Diskriminator und diesem

123

3 Datenbankentwurf

Schlüssel gebildet.13 In diesem Falle kann es sinnvoll (wenn auch redundant) sein, den Diskriminator auch in den Primärschlüssel der Unterrelationen mit einzubeziehen.14 Diese Konstruktion ist sowohl bei abstrakten Oberklassen als auch bei normalen Oberklassen möglich. Einziger Nachteil: Die Daten, die zu einem Objekt gehören, sind jeweils auf zwei Relationen verteilt und müssen immer zur Laufzeit miteinander verknüpft werden. 왘 Jede nicht abstrakte Klasse wird zu einer Relation, disjunkte Variante

Hier werden die Attribute der Oberklasse in die Unterklassen hineingezogen. Somit wird für jede Klasse, die eigene Objekte hat, eine Relation eingeführt, welche die gesamten Daten der Objekte enthält. Die Oberklasse enthält dann nur die Objekte, die in keiner der Unterklassen enthalten sind. Schlüssel müssen hier nur für die jeweiligen Relationen eindeutig sein – sie müssen nicht eindeutig im Bereich der Oberklasse sein. Der Vorteil dieser Lösung besteht darin, dass die Daten, die zu einem Objekt gehören, zusammenhängend in einer Relation gespeichert sind. Ein Nachteil ist allerdings gegeben, wenn es Assoziationen zur Oberklasse gibt. Diese müssen nämlich gegebenenfalls für jede der Unterklassen getrennt verwaltet werden. 왘 Jede nicht abstrakte Klasse wird zu einer Relation, redundante Variante

Hier werden die Attribute der Oberklasse in die Unterklassen hineingezogen. Somit wird für jede Klasse, die eigene Objekte hat, eine Relation eingeführt, welche die gesamten Daten der Objekte enthält. Die Oberklasse enthält alle Objekte dieser Klasse, also redundanterweise auch die Objekte, die zusätzlich in einer der Unterklassen enthalten sind. Schlüssel müssen hier eindeutig im Bereich der Oberklasse sein. Die Vorteile dieser Lösung sind folgende: Die Daten, die zu einem Objekt gehören, sind zusammenhängend in einer Relation gespeichert. Es gibt keine Probleme mit Beziehungen zur Oberklasse, da alle Objekte, auf die sie sich beziehen, in der Oberklasse enthalten sind. Es ist allerdings eine Redundanz gegeben, die in der Datenbank sicher verwaltet werden muss (z. B. durch Trigger, vgl. Kapitel 7.3.3).

13 Der Schlüssel allein reicht üblicherweise nicht aus, da nicht sichergestellt werden kann, dass nicht in verschiedenen Unterklassen zufälligerweise identische Schlüssel auftreten. 14 Der Grund liegt darin, dass in SQL und in anderen Systemen ein Fremdschlüssel von der Struktur her mit dem Primärschlüssel der Vater-Relation identisch sein muss.

124

Elemente der UML 왘 Alle Daten werden in der Oberklasse gespeichert

Hier werden alle Attribute aller Unterklassen in die Oberklasse gezogen – alle Objekte werden in der Oberklasse gespeichert, wobei jeweils die Attribute, die nicht zu der entsprechenden Unterklasse gehören, mit einer Nullmarke zu versehen sind. Diese Lösung ist nur akzeptabel, wenn die Anzahl der eigenständigen Attribute der Unterklassen nicht zu groß ist. Die Umsetzung von Abbildung 3.23 in ein Relationenschema nach dem Grundsatz, dass jede Klasse zu einer eigenständigen Relation wird, ergibt das Schema in Abbildung 3.24. Kunde kunden_nr status name straße plz ort letzte_bestellung letzte_werbeaktion zahlung

Kunden_key Kunden_status Personenname Straßenname Postleitzahl Ortsname Datum Datum Zahlungsart

kunden_nr = kunden_nr

kunden_nr = kunden_nr

Kunde mit Kreditkarte kunden_nr herausgeber karten_nr gültig_bis

Kunden_key Personenname Bankleitzahl Kontonummer



Kunde mit Girokonto kunden_nr konto_inhaber blz konto_nr

Kunden_key Personenname Bankleitzahl Kontonummer

Abbildung 3.24: Relationenmodell für Klassen mit Vererbung

Beispiel Fahrzeugwartung Im Folgenden wird ein weiteres kleines Fallbeispiel eingeführt. Es geht um die Wartung und Inspektion von Fahrzeugen und Fahrzeugaggregaten in einem Verkehrsbetrieb. Das Modell ist durch folgende Aussagen beschrieben: 왘 Es gibt Fahrzeuge, Motoren, Bremsanlagen, Getriebe (und weitere Aggregate,

die hier nicht betrachtet werden). 왘 Jedes Fahrzeug kann ein Aggregat von einer Klasse enthalten. Ein Aggregat

kann auch fehlen, dann ist das Fahrzeug in der Werkstatt und nicht betriebsbereit. 왘 Jedes Fahrzeug und jedes Aggregat kann zu einer Variante gehören

(z. B. Dieselmotor, Automatikgetriebe).

125

3 Datenbankentwurf 왘 Es gibt eine Liste von vorgeschriebenen Wartungen und Inspektionen, die zeit-

lich und/oder nach Laufleistung seit der letzten Wartung/Inspektion festgelegt sind. Diese Wartungen bzw. Inspektionen sind von der Klasse und der Variante abhängig. 왘 Die durchgeführten Wartungen und Inspektionen sind zu speichern. 0..*

1

Gerät {abstract}

Wartung_Inspektion datum laufleistung bemerkung

variante

Gerät.geräteart = W&I-Vorgabe.für_gerät Gerät.variante = W&I-Vorgabe.für_variante

0..*

1 Geräteart W&I-Vorgabe

0..1 0..1 ist_eingebaut_in

Fahrzeug {geräteart = 'F' } Betankung(km_stand, liter) Einsatz(fahrer, ...) ...

Aggregat {geräteart 'F'} anfangsdatum laufleistung_bei_einbau kmstand_bei_einbau

bezeichnung für_gerät für_variante max_zeit max_km

{incomplete} Geräteart

Motor {geräteart = 'M'} motor_nr {unique} leistung hubraum anz_Zylinder anord_Zylinder anz_Ventile anz_Kerzen ...

Getriebe {geräteart = 'G'} getriebe_nr {unique} anz_gänge ...

Bremse {geräteart = 'B'} brems_nr {unique} bremskraftverstärker ...

Abbildung 3.25: Fahrzeugwartung

Die Attribute und Operationen sind aus Abbildung 3.19 zu entnehmen. Bei dieser Umsetzung werden die fahrzeug_nr, motor_nr, getriebe_nr und bremsen_nr allgemein als geräte_nr bezeichnet und gemeinsam mit dem Diskriminator geräteart zum Primärschlüssel für alle Geräteklassen. Für die Klassen

126

Elemente der UML

Wartung_Inspektion und W&I_Vorgabe wird jeweils ein Schlüssel vom System gene-

riert. Somit ergibt sich folgendes Schema: Gerät(geräteart, geräte_nr, variante) Fahrzeug(geräteart, geräte_nr, typ, erstzulassung, anz_räder, km_stand) FK: geräteart, geräte_nr -> Gerät Aggregat(geräteart, geräte_nr, typ, anfangsdatum, laufleistung_bei_einbau, km_leistung_bei_einbau) FK: geräteart, geräte_nr -> Gerät Motor(geräteart, geräte_nr, leistung, hubraum, anz_zylinder, anz_ventile, anz_kerzen) FK: geräteart, geräte_nr -> Gerät Getriebe(geräteart, geräte_nr, anz_gänge) FK: geräteart, geräte_nr -> Gerät Bremsanlage(geräteart, geräte_nr, bremskraftverstärker) FK: geräteart, geräte_nr -> Gerät W&I_Vorgabe(w&i_vorgabe_nr, bezeichnung, für_gerät, für_variante, max_zeit, max_km) Wartung_Inspektion(wartung_inspektion_nr, w&i_Vorgabe_Nr, geräteart, geräte_nr, datum, laufleistung, bemerkung) FK: geräteart, geräte_nr -> Gerät FK: W&I_Vorgabe_Nr -> W&I_Vorgabe

3.3.4 Übungsaufgaben Aufgabe 3.4 a) In einer Bibliothek gibt es »Buchtitel« und »Buchexemplare«. Für einen Buchtitel können mehrere Exemplare vorhanden sein, jedoch immer mindestens eins. Ausleiher leihen Buchexemplare. Ausleiher können Buchtitel vormerken lassen. Ausleiher und Autoren sind Personen. b) Personen sind Studenten oder Professoren. Jede Vorlesung wird von einem Professor gehalten. Ein Professor hält mehrere Vorlesungen. Ein Student besucht mehrere Vorlesungen. Eine Vorlesung wird von mehreren Studenten besucht, aber erst nach Semesterbeginn steht fest, von wem. Ein Professor empfiehlt für eine bestimmte Vorlesung ein Buch. Aufgabe 3.5 (Segeltörn) Eine Jachtagentur will die Törns (Touren) ihrer Segeljachten mit einer Datenbank verwalten. Dabei geht es darum, die Mitfahrer ebenso zu erfassen wie die im Lauf der Tour angelaufenen Häfen. Es gelten folgende Regeln: 왘 Eine Crew setzt sich aus mehreren Mitfahrern zusammen. Mitfahrer müssen an keiner Crew teilnehmen, können aber auch an mehreren Crews beteiligt sein. 왘 Eine Crew bezieht sich immer auf eine Tour. Während einer Tour kann aber die

Crew wechseln.

127

3 Datenbankentwurf 왘 Für jede Tour gibt es einen Kapitän. Ein Kapitän kann natürlich an mehreren

Touren teilnehmen. 왘 Kapitäne und Mitfahrer sind Personen. 왘 Eine Tour wird immer von einer Jacht gefahren. Meistens übersteht eine Jacht

die erste Fahrt. Dann kann sie an weiteren Touren teilnehmen. 왘 Während einer Tour läuft eine Jacht mehrere Häfen an.

Modellieren Sie grafisch die Entitätentypen und Beziehungstypen. Legen Sie die Schlüsselattribute und die wichtigsten anderen Attribute fest.

3.4 Normalformen in relationalen Datenbanken Wie wir in Kapitel 2 dargestellt haben, werden in relationalen Datenbanken alle Daten und auch alle Beziehungen in Relationen gespeichert. Sinnvollerweise werden für einen bestimmten Anwendungsbereich die erforderlichen Daten in mehrere Relationen aufgeteilt. Die Verknüpfung erfolgt über die gespeicherten Werte – so wird also beispielsweise in einer Bestellung die Kundennummer gespeichert, um daraus die Verknüpfung zu dem Kundensatz herzustellen. Es ist aber auch möglich, für jede Bestellung alle Kundendaten in einer Relation mit zu speichern. Dieses Vorgehen hat einige Nachteile, so unter anderem: 왘 Wenn für einen Kunden mehrere Bestellungen vorliegen, so müssen die Daten

mehrfach erfasst und gespeichert werden. Dabei ist darauf zu achten, dass die Kundendaten für denselben Kunden auch in verschiedenen Sätzen übereinstimmen. 왘 Wenn sich irgendwelche Kundendaten (z. B. die Telefonnummer) ändern,

muss diese Änderung in allen Bestelldaten für diesen Kunden vorgenommen werden. 왘 Wenn jeweils die Bestelldaten nur bis zur Abwicklung des Auftrags gespeichert

werden, werden nach Abwicklung des Auftrages auch die Kundendaten gelöscht. Es ist einleuchtend, dass solche Anomalien in einer gut organisierten Datenbank nicht auftreten. Sehr verkürzt gesagt verlangen wir, dass das Unternehmensdatenmodell so organisiert wird, dass keine redundanten Speicherungen auftreten. Doch was heißt »redundante Speicherungen«? Ist es überhaupt möglich, jegliche Redundanz in der Speicherung zu vermeiden? Eigentlich geht es darum, dass die Einhaltung gewisser Regeln, denen die Daten gehorchen müssen (z. B. dass für einen Kunden, der durch eine Kundennummer repräsentiert wird, jeweils dieselben Kundendaten gespeichert werden und nicht etwa in verschiedenen Aufträgen verschiedene Anschriften), möglichst ohne Zutun

128

Normalformen in relationalen Datenbanken

des Benutzers sichergestellt wird. Wie wir in Kapitel 1.3 festgestellt haben, gibt es eine Reihe von unterschiedlichen Regeln, denen eine Datenbank genügen muss. Im Grunde genommen können wir die Einhaltung aller dieser Regeln durch geeignete »Trigger« (vgl. Kapitel 7.3.3) gewährleisten. Es gibt jedoch eine Reihe von Geschäftsregeln, deren Einhaltung allein durch die Aufteilung der Daten auf die verschiedenen Relationen und die Festlegung von geeigneten Schlüsseln in diesen Relationen sichergestellt werden kann. Die zugrunde liegenden Abhängigkeiten sind sogenannte »funktionale Abhängigkeiten«. Diese Fragen sind schon zu Beginn der 70er-Jahre sehr intensiv untersucht worden. Es sind dabei spezielle Normalformen eingeführt worden, denen Relationen genügen sollen. Dazu gehört auch die Methode der Normalisierung, d. h. der systematischen Umwandlung eines Systems von Relationen, die Normalformen verletzen, in ein System von Relationen, welche die Normalformen erfüllen.

3.4.1 Funktionale Abhängigkeiten Um den Begriff der Normalformen einzuführen, müssen wir uns vorher mit dem Begriff der funktionalen Abhängigkeit befassen. Der Begriff der Funktion ist den meisten Lesern sicher aus der Schule bekannt. Üblicherweise verbindet man damit in irgendeiner Weise eine Rechenvorschrift (wie quadrieren, Wurzel ziehen, Winkelfunktionen sin, cos, tan und Ähnliches). Der Funktionenbegriff ist aber tatsächlich allgemeiner: Es kommt nicht darauf an, dass es eine Rechenvorschrift gibt, sondern nur, dass es möglich ist, zu einem Argument (»Urbild«) auf eindeutige Weise ein Resultat (»Bild«) zu bekommen. So ist die Anzahl der Stimmen, die in der letzten Wahl in einem Wahlbezirk für eine bestimmte Partei abgegeben worden ist, als Funktion aufzufassen: Wenn wir den Wahlbezirk und die Partei wissen, können wir die Anzahl der Stimmen feststellen: Wir müssen nur in der entsprechenden Datenbank mit den amtlich festgestellten Ergebnissen den entsprechenden Eintrag suchen.15 In diesem Sinne können wir natürlich das Suchen in einer Datenbank als »Rechenvorschrift« auffassen. Zwei weitere Eigenschaften unterscheiden unseren Begriff »funktional« vom üblichen Funktionenbegriff. 왘 Die abhängige Größe kann zu unterschiedlichen Zeiten unterschiedliche

Werte haben. So kann die funktionale Beziehung zwischen Mitarbeiter und Gehalt zu unterschiedlichen Zeiten ein unterschiedliches Gehalt liefern. 왘 Es muss nicht zu jedem Argument ein Ergebnis existieren. So liefert die

Beziehung zwischen Kfz-Kennzeichen und Fahrzeugtyp nicht zu jedem möglichen Kfz-Kennzeichen einen Fahrzeugtyp, sondern nur zu den ausgegebenen Kfz-Kennzeichen. 15 Eigentlich handelt es sich hierbei – und in den meisten anderen Anwendungen im Datenbankbereich – um eine partielle Funktion, das heißt, nicht für alle möglichen Kombinationen von Argumenten gibt es einen Eintrag – z. B. für eine Partei, die nicht kandidiert hat.

129

3 Datenbankentwurf

Nach der allgemeinen Vorrede kommen wir jetzt zur formalen Definition des Begriffs funktionale Abhängigkeit, der – wie oben gesagt – die Voraussetzung für die Untersuchung von Normalformen ist. Wir betrachten jetzt eine Relation, in der a und b Attribute seien. Das Attribut b ist von dem Attribut a funktional abhängig, wenn gilt: Für jeden bestimmten Wert von a gibt es – zu einem gegebenen Zeitpunkt – nur einen Wert von b. Vereinfacht ausgedrückt: »Wenn der Wert von a feststeht, steht auch der Wert von b fest. Auch wenn wir b (noch) nicht kennen, wissen wir, dass es nur einen Wert b gibt.« Oder über die logische Umkehrung definiert: Es kann zu keinem Zeitpunkt in der Datenbank zwei Tupel geben, die denselben Wert für das Attribut a haben, aber verschiedene Werte für b. Hierfür schreiben wir 16 {a} → {b}

Wir sagen auch »a determiniert b funktional« oder »a ist eine Determinante für b«.17

Beispiele Der Name eines Studierenden hängt funktional von der Matrikelnummer ab. {Matrikel_Nr} → {Name}

Ein Auftrag ist immer (höchstens) einem Kunden zugeordnet.18 {Auftrag_Nr} → {Kunden_Nr}

Funktionale Abhängigkeiten können auch zwischen Mengen von Attributen bestehen. So hängt – in einer entsprechenden Tabelle in einer Prüfungsdatenbank – das Prüfungsergebnis in Form der Note ab von der Matrikelnummer des Studierenden, der Nummer der Lehrveranstaltung und dem Datum der Prüfung (da es mehrere Prüfungsangebote gibt und die Wiederholung einer Prüfung unter bestimmten Bedingungen zulässig ist). Dieses ergibt folgende funktionale Abhängigkeit: {Matrikel_Nr, LV_Nr, Datum} → {Note} 16 Wie wir gleich sehen werden, können statt einzelner Attribute auch Mengen von Attributen genommen werden. Wegen einer einheitlichen Sprechweise fassen wir diese Attribute somit als Mengen auf, die jeweils genau ein Attribut enthalten. 17 Genauer ist eine Determinante bestimmt als minimale Attributmenge A, von der eine Attributmenge B funktional abhängig ist. Darauf kommen wir noch zurück. 18 Falls der wenig erfreuliche Fall auftritt, dass der Kunde vom Auftrag zurücktreten muss, haben wir einen Auftrag, dem kein Kunde zugeordnet ist. In Abhängigkeit vom Geschäftszweig des Unternehmens werden wir den Auftrag stornieren, falls die bisher erbrachten Leistungen für den Auftrag gering sind, oder wir werden für den Auftraggeber eine Nullmarke einsetzen und einen neuen Kunden suchen.

130

Normalformen in relationalen Datenbanken

Wenn A und B Mengen von Attributen einer Relation sind, bedeutet A → B: Wenn Tupel t1 und t2 in allen Attributen von A übereinstimmen, dann müssen sie auch in allen Attributen von B übereinstimmen. Weiter oben haben wir von Geschäftsregeln gesprochen, die einen intentionalen Charakter haben. Dieses gilt auch für funktionale Abhängigkeiten, die auf intentionalen Beziehungen beruhen, nicht auf extensionalen. So ist zum Beispiel in einer Studenten-Relation der Studiengang nicht abhängig vom Namen und Vornamen eines Studierenden, auch wenn rein zufällig keine zwei Studierenden mit gleichem Vor- und Nachnamen existieren – es ist ja nicht ausgeschlossen, dass irgendwann zwei Studierende mit gleichem Vor- und Nachnamen auftreten. Einige weitere kleine Beispiele für funktionale Abhängigkeiten sind im Folgenden gegeben: In einer Relation Student (matrikel_nr, name, vorname, studiengang, fachbereich, studienbeginn,...)

sei folgender Sachverhalt gegeben: Zwei Studierende, die denselben Studiengang studieren, gehören damit auch automatisch demselben Fachbereich an. Mit anderen Worten: Der Fachbereich ergibt sich aus dem Studiengang. Dieses ist eine funktionale Abhängigkeit: {Studiengang} → {Fachbereich}

Eine interessante Frage in diesem Zusammenhang ist zum Beispiel, ob es zwischen den Orten und den Postleitzahlen in Deutschland in irgendeiner Weise funktionale Abhängigkeiten gibt. Es können hier zwei Fragen gestellt werden: Gilt {Ort} → {PLZ} ? Gilt {PLZ} → {Ort} ? Um die erste Frage zu beantworten, müssen wir einen Experten befragen: »Kann es einen Ort geben, der mehrere Postleitzahlen hat?« Diese Frage ist mit »Ja« zu beantworten, wie viele Beispiele belegen (z. B. Bremerhaven mit den Postleitzahlen 27568, 27570 und anderen). Um die zweite Frage zu beantworten, müssen wir ebenfalls einen Experten fragen: »Kann es eine Postleitzahl geben, die für mehrere Orte gilt?« Hier muss der Experte gegebenenfalls schon etwas länger suchen – aber auch diese Frage ist mit »Ja« zu beantworten.19 In beiden Fällen haben wir hier herausgefunden, dass die oben untersuchten funktionalen Abhängigkeiten nicht gelten. 19 Zu belegen etwa durch die Gemeinden Kayhude im Kreis Segeberg und Bargfeld-Stegen im Kreis Stormarn, die beide die Postleitzahl 23863 haben. Es handelt sich hierbei nicht um Ortsteile derselben Gemeinde.

131

3 Datenbankentwurf

Im Folgenden zeigen wir, dass die Möglichkeit, dass auf der linken Seite mehrere Attribute stehen können, auch tatsächlich gebraucht wird. So ist bei einem Sommerhausvermittler in Dänemark (Häuser werden an einen Kunden grundsätzlich für eine oder mehrere Kalenderwochen von Sonnabend bis Sonnabend vermietet) eine funktionale Abhängigkeit {kalenderwoche, mietobjekt} → {kunden_nr}

gegeben. Es gibt aber weder eine funktionale Abhängigkeit {mietobjekt} → {kunden_nr},

(da dasselbe Mietobjekt – zu unterschiedlichen Zeiten – an verschiedene Kunden vermietet werden kann) noch eine funktionale Abhängigkeit {kalenderwoche} → {kunden_nr},

(da in einer Kalenderwoche verschiedene Mietobjekte vermietet werden können). Auf der rechten Seite einer funktionalen Abhängigkeit können auch mehrere Attribute stehen, wie in: {kunden_nr} → {name, vorname, straße, plz, ort}

Allerdings können wir uns hier auf einelementige rechte Seiten beschränken, da eine funktionale Abhängigkeit grundsätzlich äquivalent ist zu einer Menge von funktionalen Abhängigkeiten mit jeweils den einelementigen Untermengen der rechten Seite. Das heißt, dass z. B. die funktionale Abhängigkeit T → {a, b, c}

genau dann gilt, wenn die funktionalen Abhängigkeiten T → {a}, T → {b}, T → {c}

gelten.

Einige Eigenschaften von funktionalen Abhängigkeiten20 Wie wir oben gesehen haben, müssen wir bei der Festlegung von funktionalen Abhängigkeiten auf der linken Seite grundsätzlich auch Mengen mit mehr als einem Attribut zulassen. Auf der rechten Seite dagegen können wir uns grundsätzlich auf einelementige Mengen beschränken, denn aus der Definition der funktionalen Abhängigkeit folgt unmittelbar folgende Aussage: Eine funktionale Abhängigkeit A → B gilt genau dann, wenn für alle Attribute b ∈ B A → {b} gilt.

20 Die Ableitung weiterer funktionaler Abhängigkeiten aus bekannten funktionalen Abhängigkeiten wird durch sogenannte »Interferenzregeln« beschrieben. Eine ausführliche Darstellung findet sich in [ElNa05 S. 312 ff.].

132

Normalformen in relationalen Datenbanken

Eine weitere Regel lautet: Wenn A → B gilt, und A' eine Obermenge von A ist, dann gilt A' → B . Wenn A → B gilt und es keine echte Teilmenge von A‘‘ von A gibt, sodass A‘‘ → B, dann nennen wir A Determinante für B. Der zuvor eingeführte Begriff der Determinante wird hierdurch präzisiert und eingeschränkt auf den Fall, dass eine Attributmenge A, von der eine Attributmenge B funktional abhängig ist, minimal in dem Sinn zu sein hat, dass man kein Attribut weglassen kann.

Triviale funktionale Abhängigkeiten Wenn A eine Teilmenge von T ist, dann gilt immer die funktionale Abhängigkeit T → A. Solche funktionalen Abhängigkeiten nennen wir trivial. Für die Untersuchung von Normalformen spielen naturgemäß die trivialen funktionalen Abhängigkeiten keine Rolle.

3.4.2 Schlüssel Ein Spezialfall der funktionalen Abhängigkeit liegt vor, wenn es für jeden Wert einer Attributkombination jeweils nur ein Tupel in der Relation geben kann. So gibt es in der Studenten-Relation eine funktionale Abhängigkeit: {matrikel_nr} → {name,vorname,studiengang,fachbereich,studienbeginn}

Wir können jetzt den oben in Kapitel 2.2 und 3.2.8 eingeführten Begriff des Schlüssels für Entitätenmengen entsprechend für Relationen über funktionale Abhängigkeiten definieren. Das ist insofern wichtig, da in der Analyse der Normalformen sowohl der Begriff des Schlüssels als auch der Begriff der funktionalen Abhängigkeiten auftritt. Sei X die Menge der Attribute einer Relation R. Eine Menge A ⊆ X von Attributen ist ein Schlüsselkandidat für R, wenn gilt: 1) A → X 2) Keine echte Untermenge von A erfüllt 1. Eine Relation kann mehrere Schlüssel haben, vgl. das Beispiel in Tabelle 3.2, wo wir neben einer hochschulweiten Matrikelnummer die Studierenden in den einzelnen Jahrgängen mit laufenden Nummern versehen. Hier sind sowohl {matrikel_nr} als auch {Studiengang, Jahrgang, lfd_nr} Schlüssel.

133

3 Datenbankentwurf

Matrikel_Nr

Name

Studiengang

Jahrgang

lfd_Nr

01313-06

Aalbeck

Inf/WI

2001

2

01710-06

Mayer

Inf/WI

2000

2

01517-06

Mayer

Inf/WI

2001

4

01214-06

Peters

Inf/WI

2002

5

01422-06

Zyweck

Inf/WI

2001

5

Tabelle 3.2: Relation mit mehreren Schlüsselkandidaten

Einer der Schlüssel (ggf. der einzig mögliche Schüssel) kann als Primärschlüssel bezeichnet werden. Im Zusammenhang mit Schlüsseln müssen wir folgende Aussagen beachten (wobei die erste Aussage ein Spezialfall der zweiten Aussage ist): Wenn S ein Schlüssel für B ist, dann gilt S → B für jede beliebige Attributmenge B Wenn S' eine Obermenge eines Schlüssels ist, dann gilt S' → B für jede beliebige Attributmenge B Das bedeutet nichts anderes, als dass wir aus der Kenntnis der Schlüsselattribute die übrigen Attribute einer Relation feststellen können. So gilt für studentische Daten {matrikel_nr} → {name, strasse, plz, ort}

Wenn wir die Matrikelnummer kennen, können wir die übrigen Daten aus der Datenbank ableiten. Das gilt natürlich auch dann noch, wenn wir neben der Matrikelnummer zusätzliche Angaben wie z. B. den Studiengang kennen: {matrikel_nr, studiengang} → {name, strasse, plz, ort}

Folgende Sachverhalte können durch funktionale Abhängigkeiten beschrieben werden: 1) Die Relation stellt einen Entitätentyp dar. Die Schlüssel des Entitätentyps bilden den Primärschlüssel S der Relation. Wir haben eine funktionale Abhängigkeit S → X , wobei X die Menge aller Attribute der Relation ist. 2) Die Relation beschreibt einen Beziehungstyp; dann hängen die Attribute der Relation von den Schlüsselattributen der beteiligten Entitäten ab (z. B. hängt menge in der Relation Position von {liefer_nr, artikel_nr} ab). 3) Die Relation beschreibt einen Beziehungstyp zwischen Entitäten, die gewisse gegenseitige Ausschlusskriterien enthalten. Z. B. gibt es in der Relation »Stundenplan« die Bedingung, dass zu einem Zeitpunkt ein Raum nur für eine Veranstaltung reserviert sein darf.

134

Normalformen in relationalen Datenbanken

Im Fall (1) ist der Schlüssel ein sog. Identifikationsschlüssel, der in anderen Relationen vom Typ (2) die Entität vertreten kann. Ein Attribut in einer Beziehungs-Relation, das sich auf das Schlüsselattribut in einer Entitäten-Relation bezieht, ist der in Kapitel 2.2.2 eingeführte Fremdschlüssel. Im Fall (3) könnte man die Tatsache, dass z. B. die Kombination der Attribute {wochentag, raum, stundenblock} eindeutig sein muss, als Schlüssel modellieren. Es empfiehlt sich allerdings nicht, so eine Kombination als Primärschlüssel festzulegen, da dieser bei jeder Raumverlegung seinen Wert verliert. Sinnvoller ist es dann schon, ein (unveränderliches) künstliches Attribut als Primärschlüssel einzuführen. Relationen mit mehreren Schlüsselkandidaten, die jeweils aus mehreren Attributen bestehen, die sich teilweise überlappen, können Probleme aufwerfen, die sich nicht mit den geschilderten Normalformen lösen lassen. Der interessierte Leser sei z. B. an [Date00] und [Date92] verwiesen. Im nächsten Abschnitt zeigen wir eine Relation, in der es mehrere gegenseitige Ausschlussbedingungen gibt. Für solche Relationen können die im Folgenden dargestellten Begriffe sehr viel komplexer sein, als es aufgrund der angegebenen Beispiele erscheint. Andererseits treten solche komplexen Beispiele in der Praxis eher selten auf.

3.4.3 Normalformen, vereinfachte Version Für die Darstellung von unternehmensrelevanten Daten durch Relationen gibt es im Prinzip beliebig komplexe und geschachtelte Möglichkeiten. Ein Datenmodell ist aber nur dann vernünftig handhabbar, wenn es gewissen Kriterien genügt: Es muss den drei im Folgenden angegebenen Normalformen genügen. Es gibt darüber hinaus weitere Normalformen, die aber in diesem Rahmen nicht behandelt werden. Nach einem sauberen Entwurf gemäß dem Entity-Relationship-Modell aus Kapitel 3.2 sind die erzeugten Relationen häufig schon in der dritten Normalform. Da andererseits die Attribute im Entwurf nach dem ER-Modell nicht systematisch untersucht werden, kann es hier noch unerwünschte funktionale Abhängigkeiten geben, die durch die Normalisierung beseitigt werden. Wir erläutern zuerst die drei Normalformen und führen anschließend an einem Beispiel eine Normalisierung vollständig durch.

1. Normalform (1NF) Eine Relation ist in der ersten Normalform, wenn die Werte der Attribute elementar sind. Damit sind als Attribute z. B. Mengen, Folgen und Arrays ausgeschlossen. Einige Beispiele dafür werden am Ende dieses Abschnitts dargestellt.

135

3 Datenbankentwurf

Als elementar gelten aber auch komplexe (d. h. aus Komponenten zusammengesetzte) Datentypen wie Datum oder Uhrzeit, sofern in der Regel jeweils der gesamte Wert relevant ist und nicht die Komponenten wie Tag, Monat, Jahr oder Stunden und Minuten. Über diese Frage entbrennt in der Literatur immer wieder ein heftiger Streit – ist zum Beispiel ein Datum eine elementare Größe oder nicht? Wir legen hier als Kriterium für »elementar« an, dass unter dem Gesichtspunkt des Datenbanksystems die Größe jeweils als Ganzes zu sehen ist. Dem widerspricht nicht, dass über spezielle Funktionen auf diese Daten zugegriffen wird, z. B. durch »Gruppiere die Verkäufe nach Monaten«. Nun wird üblicherweise ein Datum als Aggregat von Tag, Monat und Jahr aufgefasst. Dies ist aber nur eine kulturell bedingte Auffassung, die außerdem noch in anderen Kulturkreisen (Arabien, China, Maya ...) anders festgelegt wird. Und was unterscheidet die obige Anweisung von »Gruppiere die Verkäufe nach Wochen«? Die Kalenderwoche ist sicherlich nicht Bestandteil des Datums, sondern kann durch eine (abgeleitete) Funktion (vgl. Kapitel 7.3.2) beschrieben werden. In der Praxis werden derzeit auch komplexe Objekte wie Bilddaten, Zeichnungen, Tonfolgen in relationalen Datenbanksystemen als elementar gespeichert. Dies wird damit begründet, dass diese Daten vom Datenbank-Verwaltungssystem nur verwaltet werden, Such- und Zugriffskriterien aber in anderen Attributen liegen. Es können daher auch keine Abfragen formuliert werden, die sich auf Inhalte dieser Attribute beziehen: So kann in einer Immobiliendatenbank, in der für jedes Objekt ein Foto enthalten ist, die Frage »Suche alle Objekte, die rote Dachziegel haben« nur beantwortet werden, wenn für die Farbe der Dachziegel ein spezielles Attribut enthalten ist – aus dem Foto kann die Datenbank diese Information (derzeit noch) nicht herausziehen. Erweiterungen von Datenbanken sind derzeit in der Entwicklung. Solche Systeme wird man dann allerdings eher zu den objektorientierten oder objektrelationalen Datenbanken rechnen. Nicht elementar ist ein Attribut einer Bücher-Relation, das für ein Buch mehrere Autoren enthält, wenn hinterher Abfragen der Form »Suche alle Bücher, deren Autoren in Schleswig-Holstein geboren wurden« oder »Zähle für jeden Autor die Anzahl der Werke« erfolgen. Wenn dagegen diese Autoren nur als bibliografische Information, nicht aber als Verarbeitungskriterium benutzt werden, kann dieses Attribut als elementar aufgefasst werden. Ohne so eine dialektische Festlegung des Begriffs »elementar« kämen wir unseres Erachtens nicht weiter, da sonst mit Recht jede Zeichenkette als nicht elementar anzusehen wäre, da sie aus einzelnen Zeichen besteht. Das würde sogar jede Zahl betreffen, die ja aus Ziffern besteht. Die 1NF ist eine Grundforderung für relationale Datenbanken. Relationale Datenbanksysteme können nur Relationen speichern und verarbeiten, die in der ersten Normalform sind. Insbesondere sind dort Mehrfachfelder durch Verknüpfung mehrerer Relationen zu erzeugen. In objektorientierten und in objektrelationalen

136

Normalformen in relationalen Datenbanken

Datenbanken ist diese Beschränkung aufgehoben – es gibt dort weitere Konstruktoren, welche die Modellierung von Mengen, Folgen, Arrays und Strukturen erlauben (vgl. Kapitel 9). Nicht elementar ist des Weiteren ein Array, das in zwölf Komponenten die Verkäufe eines Jahres festhält, oder ein Attribut, das für eine Buslinie die Folge aller Haltepunkte enthält. In diesen Fällen ist auch davon abzuraten, solche komplexen Datenstrukturen über spezielle Attribute in die Relation zu »schmuggeln« (z. B. verkauf_01, verkauf_02 ... verkauf_12 oder in Kenntnis, dass eine Buslinie höchstens 25 Stationen haben kann, stop_01 ... stop_25). Dieses ist zwar prinzipiell möglich – Programmierer, die gewohnt sind, solche Sachverhalte über Arrays auszudrücken, halten sicher solche Darstellungen auch für »natürlich«. Dennoch ist zu sagen, dass so eine Darstellung in der konkreten Anwendung zu großen Problemen führt. Eine Abfrage der Form »Welche Buslinien halten am Hauptbahnhof?« wäre zu formulieren als: »Für welche Buslinie ist stop_01 = ‘Hauptbahnhof’ oder stop_02 = ‘Hauptbahnhof’ oder ... stop_25 = ‘Hauptbahnhof ’?« (alle 25 stop_nn ausschreiben!). Die Abfrage »Gibt es eine Umsteigemöglichkeit von Linie 1 zu Linie 2?« führt sogar zu einer Abfrage mit 25 * 25 = 625 Termen: »Linie_1.stop_01 = Linie_2.stop_01 oder Linie_1.stop_01 = Linie_2.stop_02 ... Linie_1.stop_25 = Linie_2.stop_24 oder Linie_1.stop_25 = Linie_2.stop_25«. Eine Normalisierung führt hier etwa zu folgendem Relationenschema: Linie (linien_nr, gesellschaft, ...) Station (linien_nr, stop, km_von_start, ...)

Die Abfrage nach den Umsteigemöglichkeiten von Linie 1 auf Linie 2 ist in der Relationenalgebra (vgl. Kapitel 2.4) durch »Suche alle Paare von Tupeln [t1,t2] von Stationen mit t1.Linien_nr = 1 und t2.Linien_nr = 2 und t1.stop = t2.stop« lösbar – wenn diese Abfrage mindestens ein Ergebnis enthält, gibt es eine Umsteigemöglichkeit.

Hinweis Die weiteren Normalformen setzen grundsätzlich die erste Normalform voraus, ohne dass das im Folgenden jeweils explizit gesagt wird. Wir bringen jetzt vereinfachte Definitionen für die zweite und dritte Normalform. Wir können zeigen, dass diese Definitionen mit den später eingeführten exakten Definitionen nur dann nicht übereinstimmen, wenn die Relation mehrere verschiedene Schlüsselkandidaten mit mindestens einem gemeinsamen Attribut hat. Solche Relationen treten aber in der Praxis relativ selten auf, sodass für den Anfang die vereinfachten Definitionen ausreichen. Die exakten Definitionen bringen wir später und zeigen auch, weshalb sie eine weitere Bedingung enthalten müssen.

137

3 Datenbankentwurf

Wir gehen von folgenden Festlegungen aus: 왘 X ist die Menge aller Attribute. 왘 T und A sind beliebige Mengen von Attributen.

2. Normalform (2NF) (vereinfacht) Eine Relation ist in der zweiten Normalform, wenn es keine nicht triviale funktionale Abhängigkeit T → A gibt, wobei T eine echte Untermenge eines Schlüssels ist. »T ist eine echte Untermenge eines Schlüssels« heißt, dass der entsprechende Schlüssel mindestens ein Element enthält, das nicht in T enthalten ist. Somit wird die 2NF häufig in der Form »Alle Attribute21 sind vom Schlüssel voll abhängig« ausgedrückt. Die Betonung liegt hier auf »voll«, d.h., es gibt kein Attribut, das schon von einem Teil des Schlüssels funktional abhängt. Eine Verletzung der zweiten Normalform ist nur möglich, wenn der Schlüssel mehr als ein Attribut enthält.

3. Normalform (3NF) (vereinfacht) Eine Relation ist in der dritten Normalform, wenn für jede nicht triviale funktionale Abhängigkeit T → A T Obermenge eines Schlüssels ist. Ein Verstoß gegen die 3NF ist also gegeben, wenn wir eine nicht triviale funktionale Abhängigkeit T → A finden können, wobei T nicht alle Schlüsselattribute enthält. T kann auch Nichtschlüsselattribute enthalten. Wenn T keine echte Teilmenge eines Schlüssels ist, wenn also nicht bereits die 2NF verletzt ist, muss T ein Nichtschlüsselattribut enthalten, damit ein Verstoß gegen 3NF vorliegt. Im einfachsten Fall besteht T nur aus einem einzigen Attribut, einem Nichtschlüsselattribut. Die Bedingung »T nicht Obermenge eines Schlüssels« schließt auch aus, dass T selbst ein Schlüssel ist. Die Bedingung der 3NF wird häufig in der Form »Es gibt keine transitiven funktionalen Abhängigkeiten« ausgedrückt. Damit ist gemeint, dass sich für den Schlüssel S die funktionale Abhängigkeit S → A nach dem »Transitivitätsgesetz« aus S → T und T → A ableiten lässt. Es ist leicht zu zeigen, dass eine Relation in 3NF auch in 2NF ist, da eine Verletzung der 2NF auch eine Verletzung der 3NF ist: Wegen der Bedingung 2 (Minimalität) für Schlüssel kann eine echte Untermenge eines Schlüssels keine Obermenge eines (anderen) Schlüssels sein. Die folgenden beiden Grafiken stellen Verstöße gegen die zweite und dritte Normalform schematisch dar. Sn bedeutet darin ein Schlüsselattribut, An ein Nichtschlüsselattribut.

21 Genauer müssten wir sagen: »Alle Attribute, die nicht im Schlüssel liegen, sind vom Schlüssel voll abhängig.«

138

Normalformen in relationalen Datenbanken

Abbildung 3.26: Verstoß gegen 2NF schematisch

Abbildung 3.27: NF2 erfüllt, NF3 verletzt, schematisch

3.4.4 Beispiel für eine Normalisierung Der Begriff »Normalform« beinhaltet des Weiteren, dass jede Relation in eine Menge von Relationen zerlegt werden kann, die (in einer noch näher zu spezifizierenden Weise) äquivalent zu der Ursprungsrelation ist. Im Folgenden wird ein Beispiel für die Normalisierung einer Datenbank angegeben. In einem fiktiven Unternehmen der Branche Versandhandel sollen die Lieferscheine und Rechnungen entsprechend Abbildung 3.20 über eine Datenbank verwaltet werden. Wir können hier zwei mögliche Entwurfstechniken benutzen: den Entwurf nach dem ER-Modell sowie die Überführung in die dritte Normalform nach Codd. Dieses Beispiel ist angelehnt an das im Anhang C beschriebene Fallbeispiel, aber es enthält nur einen Ausschnitt und weicht an einigen Stellen von dem dort angegebenen Schema ab. Der Grund liegt darin, dass wir aus einer Analyse der Rechnun-

139

3 Datenbankentwurf

gen nur einen Teil der Datenstrukturen erhalten, die für die Auftragsverwaltung notwendig sind. Wir gehen davon aus, dass eine Rechnung immer auf einer Bestellung beruht, und sprechen daher von Bestelldaten statt von Rechnungsdaten.

Weser-Versand – seit 1827 immer für Sie bereit Postfach 12 34 56 # 27568 Bremerhaven # Tel. +49/471/482323 # Fax. +49/471/482328

Peter Stein Moordamm 34

RECHNUNG

23863 Kayhude

Kunden-Nr

Datum 29.04.2008

Seite 1 101

Rechnungs-Nr 151

Mehrwertsteuer 0=keine, 1=halbe, 2=volle ↓ Menge

Packung

Art-Nr

Artikel

Lager Einzelpreis Gesamtpreis

4

0,5 l

G002

Portwein

7

12,45

49,80

2

3

6er

G003

Bier

7

5,20

15,60

2

K002

Hose

2

112,80

338,40

2

3 1

Karton

K003

Damenhut

2

65,70

65,70

2

10

125 g

L002

China-Tee

5

8,35

83,50

1

Netto

MwSt.

Summe

halbe MwSt.

83,50

5,84

89,34

volle MwSt.

469,50

89,21

558,71

553,00

95,05

648,00

Artikel-Anzahl = 21 Ohne MwSt.

Abbildung 3.28: Rechnung als Ausgangsbasis für eine Datenanalyse

Sammlung der Daten, unnormalisiert Eine Zusammenfassung aller Daten ergibt die Tabelle 3.3. Dieses ist keine Relation, da die erste Normalform verletzt ist: Zu jeder Bestellung kann es mehrere Positionen geben. Formal könnten wir das etwa wie folgt beschreiben, wobei die Daten in den geschweiften Klammern jeweils wiederholt werden können.

140

Normalformen in relationalen Datenbanken

Versand (bestell-Nr, bestell-datum, kunden-nr, kunden-name, kunden-straße, kunden-plz, kunden-ort, {artikel-nr, artikel-packung, artikel-bezeichung, artikel-lager, artikel-preis, artikel-mwst, menge} )

Innerhalb einer Lieferung ist artikel_nr jeweils eindeutig, weshalb das Attribut in der geschweiften Klammer unterstrichen worden ist. Bestell

Kunden

Nr

Nr

Datum

Artikel

Name

Straße

PLZ

Ort

Nr

151 02.05.2008 101 Stein, Peter Moordamm 34 23863 Kayhude G002 0,5 l

152 02.05.2008 103 Randers, Nis Am Seeufer 12 23845 Oering

Menge

Packung Bezeichnung Lager Preis Portwein

MwSt.

7

12,45

2

4

G003 6er-Pack Bier

7

5,20

2

3

K002

Hose

2

112,80

2

3

K003 Karton

Damenhut

2

65,70

2

1

L002 125 g

China-Tee

5

8,35

1

5

K001 Karton

Schuhe

2

98,50

2

10

K003 Karton

Damenhut

2

65,70

2

2

K004 Karton

Sonnenbrille 2

76,00

1

12

Tabelle 3.3: Daten nicht in erster Normalform

Relationen in 1. Normalform Wir überführen diese nicht normalisierte Datensammlung22 in die erste Normalform, indem wir die Wiederholungsgruppen herausziehen. Somit erhalten wir die folgenden zwei Relationen. Die herausgezogene Relation benötigt zur vollen Identifizierbarkeit der Tupel neben den Attributen der Wiederholungsgruppe die Schlüsselattribute {bestell-nr} der Haupttabelle. Diese bilden zusammen mit dem Schlüssel {artikel-nr} der abhängigen Gruppen den Schlüssel {bestell-nr, artikel-nr}. Bestelldaten (bestell-nr, bestell-datum, kunden-nr, kunden-name, kunden-straße, kunden-plz, kunden-ort) Positionsdaten (bestell-nr, artikel-nr, artikel-packung, artikel-bezeichung, artikel-lager, artikel-preis, artikel-mwst, menge) Bestell- BestellNr Datum

Kunden- KundenNr Name

KundenStraße

KundenPLZ

KundenOrt

151

02.05.2008 101

Stein, Peter

Moordamm 34

23863

Kayhude

152

02.05.2008 103

Randers, Nis

Am Seeufer 12

23845

Oering

Tabelle 3.4: Bestelldaten

22 Diese Datensammlung wird auch als NF2-Relation bezeichnet (für NF2 = NFNF = Non

First Normal Form). Das NF2-Datenmodell ist ab etwa 1985 als mögliche Erweiterung des relationalen Datenmodells eingehend untersucht worden. Vgl. z. B. [HeSa95; S. 110 ff].

141

3 Datenbankentwurf BestellNr

ArtikelNr

ArtikelPackung

ArtikelBezeichnung

ArtikelLager

151

G002

0,5 l

Portwein

7

151

G003

6er Pack

151

K002

151

K003

151

L002

125 g

China-Tee

5

8,35 1

5

152

K001

Karton

Schuhe

2

98,50 2

10

Karton

Artikel- ArtikelPreis MwSt.

Menge

12,45 2

4

Bier

7

5,20 2

3

Hose

2

112,80 2

3

Damenhut

2

65,70 2

1

152

K003

Karton

Damenhut

2

65,70 2

2

152

K004

Karton

Sonnenbrille

2

76,00 1

12

Tabelle 3.5: Positionsdaten

Im Übrigen bekommen wir die Ausgangsdaten von Tabelle 3.3 in einer abgewandelten – aber relationalen – Form als Verbindung (vgl. Tabelle 3.6). Bestelldaten Join[bestell_nr] Positionsdaten Bestell

Kunden

Nr

Nr

Datum

Name

Artikel Straße

PLZ

Ort

Nr

Menge Packung Bezeichnung Portwein

Lager Preis

MwSt.

151 02.05.2008 101 Stein, Peter

Moordamm 34 23863 Kayhude G002 0,5 l

7

12,45

2

4

151 02.05.2008 101 Stein, Peter

Moordamm 34 23863 Kayhude G003 6er-Pack Bier

7

5,20

2

3

151 02.05.2008 101 Stein, Peter

Moordamm 34 23863 Kayhude K002

Hose

2

112,80

2

3

151 02.05.2008 101 Stein, Peter

Moordamm 34 23863 Kayhude K003 Karton

Damenhut 2

65,70

2

1

151 02.05.2008 101 Stein, Peter

Moordamm 34 23863 Kayhude L002 125 g

China-Tee 5

8,35

1

5

2

98,50

2

10

152 02.05.2008 103 Randers, Nis Am Seeufer 12 23845 Oering

K001 Karton

Schuhe

152 02.05.2008 103 Randers, Nis Am Seeufer 12 23845 Oering

K003 Karton

Damenhut 2

65,70

2

2

152 02.05.2008 103 Randers, Nis Am Seeufer 12 23845 Oering

K004 Karton

Sonnenbrille

76,00

1

12

2

Tabelle 3.6: Ursprungsdaten als Relation

Bestimmung der funktionalen Abhängigkeiten Es werden folgende funktionale Abhängigkeiten gefunden: Bestelldaten: {bestell-nr} → {bestell_datum, kunden-nr} {kunden-nr} → {kunden-name, kunden-straße, kunden-plz, kunden-ort } Positionsdaten: {artikel-nr} → {artikel-packung, artikel-bezeichung, artikel-lager, artikel-preis, artikel-mwst} {bestell-nr, artikel-nr} → { menge }

142

Normalformen in relationalen Datenbanken

In der Relation Positionsdaten ist der Schlüssel durch {bestell-nr, artikel-nr} gegeben. Die Artikelattribute hängen aber schon von einer Teilmenge davon ab, nämlich von {artikel-nr}. Somit ist die Relation nicht in der zweiten Normalform. Zur Normalisierung ziehen wir artikel-nr sowie alle davon abhängigen Attribute zu einer eigenen Relation mit dem Schlüssel {artikel-nr} zusammen und entfernen die von {artikel-nr} abhängigen Attribute aus der ursprünglichen Relation. Wir erhalten dann die beiden Relationen Artikel und Position. ArtikelNr

ArtikelPackung

ArtikelBezeichnung

ArtikelLager

G002

0,5 l

Portwein

7

12,45

2

G003

6er Pack

Bier

7

5,20

2

Hose

2

112,80

2

K002

ArtikelPreis

ArtikelMwSt.

K003

Karton

Damenhut

2

65,70

2

L002

125 g

China-Tee

5

8,35

1

K001

Karton

Schuhe

2

98,50

2

K004

Karton

Sonnenbrille

2

76,00

1

Tabelle 3.7: Relation »Artikel« in der zweiten Normalform

Bestell-Nr

Artikel-Nr

Menge

151

G002

4

151

G003

3

151

K002

3

151

K003

1

151

L002

5

152

K001

10

152

K003

2

152

K004

12

Tabelle 3.8: Relation »Position« in der zweiten Normalform

Diese neuen Relationen sind Projektionen der Relation Positionsdaten. Hierbei wird auch deutlich, dass bei der Projektion mehrfach auftretende Tupel auf eines reduziert werden (vgl. Kapitel 2.4.2). Der zweimal bestellte Artikel »Damenhut« taucht in der Relation Artikel nur einmal auf. Umgekehrt können wir die ursprüngliche Relation als Verbund der Relationen Artikel und Position wiedergewinnen:

143

3 Datenbankentwurf

Artikel = Proj [artikel-nr, artikel-packung, artikel-bezeichung, artikel-lager, artikel-preis, artikel-mwst] Positionsdaten Position = Proj [bestell-nr, artikel-nr, menge] Positionsdaten Positionsdaten = Artikel Join[artikel-nr] Position

Die Relation Position ist in der zweiten Normalform: Eine Verletzung dieser Normalform könnte nur eine Abhängigkeit des Attributs menge von {bestell-nr} oder von {artikel-nr} sein. Das Erstere ist nicht der Fall, da in derselben Bestellung verschiedene Artikel in unterschiedlicher Menge auftreten können; das Zweitere nicht, da ein Artikel in unterschiedlichen Bestellungen in unterschiedlicher Menge auftreten kann. Diese beiden Relationen sind auch in der dritten Normalform, da wir keine weiteren funktionalen Abhängigkeiten haben. Wir haben eben ganz locker gesagt: »da wir keine weiteren funktionalen Abhängigkeiten haben«. Dies ist eine Aussage, aber keine Analyse. Ist nicht vielleicht doch der Mehrwertsteuersatz von der Artikelbezeichnung abhängig? Es spricht sogar einiges dafür, dass Artikel mit derselben Bezeichnung auch denselben Mehrwertsteuersatz haben. Es geht hier aber nicht darum, ob vieles dafür spricht, sondern ob diese funktionale Abhängigkeit aufgrund irgendeiner Geschäftsregel notwendig ist. Wenn es keine Geschäftsregel gibt, die das erforderlich macht, gibt es hier auch keine funktionale Abhängigkeit. Dies erfordert aber – besonders bei größeren Datenmodellen – eine recht umfangreiche Analyse. Wir sehen hier auch eine Schwierigkeit der Normalisierung bzw. der Normalformen: Es ist nicht schwierig, aufgrund gegebener funktionaler Abhängigkeiten festzustellen, ob eine Relation in zweiter oder dritter Normalform ist, es ist aber ggf. schwierig, eine Liste von notwendigen funktionalen Abhängigkeiten aufzustellen. Die Relation Bestelldaten ist nicht in der dritten Normalform, da eine Reihe von Attributen von dem Nichtschlüsselattribut kunden-nr abhängt. Eine Zusammenfassung der von kunden-nr abhängigen Attribute mit kunden-nr als Schlüssel und das Herausziehen dieser Attribute ergibt die im Folgenden zwei dargestellten Relationen. Diese sind in der dritten Normalform, da keine weiteren nicht trivialen funktionalen Abhängigkeiten existieren. KundenNr

KundenName

KundenStraße

101

Stein, Peter

Moordamm 34

23863

Kayhude

103

Randers, Nis

Am Seeufer 12

23845

Oering

Tabelle 3.9: Relation »Kunde« in der dritten Normalform

144

KundenPLZ

KundenOrt

Normalformen in relationalen Datenbanken

BestellNr

BestellDatum

KundenNr

151

02.05.2008

101

152

02.05.2008

103

Tabelle 3.10: Relation »Bestellung« in der dritten Normalform

Es gilt hier: Kunde = Proj(kunden-nr, kunden-name, kunden-straße, kunden-plz, kunden-ort] Bestelldaten Bestellung = Proj(bestell-nr, bestell_datum, kunden-nr] Bestelldaten Bestelldaten = Kunde Join[kunden-nr] Bestellung

In den Abbildungen 3.26 bis 3.29 zeigen wir grafisch die Abhängigkeiten und ihre Beseitigung aus dem Modell, indem die Gesamtrelation in mehrere Relationen aufgeteilt wird. Die Verknüpfungen zwischen zwei Relationen erfolgen jeweils über einen Verweis eines Fremdschlüssels auf den Primärschlüssel einer anderen Relation. Nach dem letzten Schritt bleiben nur Abhängigkeiten von den jeweiligen Schlüsseln und Fremdschlüsseln übrig. Versand bestell_nr liefer_datum kunden_nr kunden_name kunden_stra e kunden_plz kunden_ort

...

artikel_nr artikel_nr artikel_nr artikel_bezeichnung artikel_bezeichnung artikel_bezeichnung artikel_lager artikel_lager artikel_lager artikel_preis artikel_preis artikel_preis artikel_mwst artikel_mwst artikel_mwst menge menge menge

Abbildung 3.29: Daten in nicht normalisierter Darstellung

145

3 Datenbankentwurf

Bestellung

Position

bestell_nr

bestell_nr

liefer_datum

artikel_nr

kunden_nr

artikel_bezeichnung

kunden_name

artikel_lager

kunden_stra e

artikel_preis

kunden_plz

artikel_mwst

kunden_ort

menge

Abbildung 3.30: Relationen in der ersten Normalform

Position

Bestellung bestell_nr

bestell_nr

liefer_datum

artikel_nr

kunden_nr

menge

kunden_name kunden_stra e

Artikel

kunden_plz artikel_nr kunden_ort artikel_bezeichnung artikel_lager artikel_preis artikel_mwst

Abbildung 3.31: Relationen in der zweiten Normalform

146

Normalformen in relationalen Datenbanken

Position

Bestellung bestell_nr

bestell_nr

liefer_datum

artikel_nr

kunden_nr

menge

Kunde

Artikel

kunden_nr

artikel_nr

kunden_name

artikel_bezeichnung

kunden_stra e

artikel_lager

kunden_plz

artikel_preis

kunden_ort

artikel_mwst

Abbildung 3.32: Relationen in der dritten Normalform

Wie wir in der Darstellung der 3NF sehen, sind alle Abhängigkeiten auf Abhängigkeiten von Schlüsseln bzw. auf Verknüpfungen über Fremdschlüssel reduziert. Für Relationen (in erster Normalform – sonst sind es ja keine Relationen) können wir jetzt formulieren, wann eine Relation R mit vorgegebenen funktionalen Abhängigkeiten äquivalent zu einer Menge von anderen Relationen R1 ... Rn ist. Dieses ist der Fall, wenn: 1) Jede der Relationen Ri ist eine Projektion von R. 2) R ist der natürliche Verbund der Relationen Ri. 3) Für jede zulässige Tupelmenge von R sind die entsprechenden Projektionen zulässige Tupelmengen für Ri. 4) Für alle zulässigen Tupelmengen für Ri ist der natürliche Verbund eine zulässige Tupelmenge für R. Als Anleitung für den Ablauf beim Normalisieren von Relationen fassen wir das am Beispiel vorgeführte Verfahren noch einmal als »Kochrezept« zusammen: 1) Herausfinden, was ein Tupel von R bedeutet. 2) Schlüssel bestimmen. (Ggf. mehrere! Alle müssen gefunden werden.) 3) Attribute suchen, die nicht atomar sind. Ausgliedern der Attribute in eine (mehrere) Extra-Relation(en) Schlüssel der neuen Relationen bestimmen.

147

3 Datenbankentwurf

4) Falls Schlüssel aus mehreren Attributen besteht: Attribute* suchen, die nur von einem Teil der Schlüsselattribute abhängig sind. Ausgliedern der abhängigen Attribute zusammen mit ihrer Determinante in eine Extrarelation. Deren Schlüssel ist durch die Determinante gegeben. Die Determinante bleibt als Fremdschlüssel in der Ausgangsrelation. 5) Suche nach funktional abhängigen Attributen*, deren Determinante ein Nichtschlüsselattribut ist oder eines enthält (transitive funktionale Abhängigkeiten). Ausgliedern ... (wie bei 4.) * nur funktional abhängige Attribute, die nicht Teil eines andern Schlüssels sind

3.4.5 Normalformen, exakte Version Wenn wir das Beispiel der Normalisierung analysieren, stellen wir fest, dass wir bei der Überführung in die zweite und dritte Normalform jeweils die abhängigen Attribute aus der Relation herausziehen, um zusammen mit den Determinanten eine neue Relation aufzubauen. Dieses Herausziehen der abhängigen Attribute ist aber im Sinne der äquivalenten Umformung nur zulässig, wenn dabei kein Schlüssel der Ausgangsrelation zerstört wird. Das heißt, wenn in den abhängigen Attributen kein Schlüsselattribut enthalten ist. Attribute von Schlüsselkandidaten nennen wir auch Primattribute. Ein Attribut a ist ein Primattribut, wenn a Element eines Schlüssels ist. Statt »a ist ein Primattribut« sagen wir auch »a ist prim«.

2. Normalform (2NF) Eine Relation ist in der zweiten Normalform, wenn es keine nicht triviale funktionale Abhängigkeit T → A gibt, wobei T eine echte Untermenge eines Schlüssels ist und A kein Primattribut enthält.

3. Normalform (3NF) Eine Relation ist in der dritten Normalform, wenn für jede nicht triviale funktionale Abhängigkeit T → A T Obermenge eines Schlüssels ist oder A (mindestens) ein Primattribut enthält. Wenn wir wiederum den Prozess der Normalisierung durchgehen, stellen wir fest, dass jede Relation in eine äquivalente Menge von Relationen in dritter Normalform überführt werden kann, da auf der rechten Seite der funktionalen Abhängigkeit ja keine Primattribute mehr auftreten dürfen.

148

Normalformen in relationalen Datenbanken

3.4.6 Übungsaufgaben Aufgabe 3.6 Betrachten Sie die Datensammlung in Tabelle 3.11. a) Welche Normalformen sind verletzt? b) Erzeugen Sie ein äquivalentes System in normalisierter Form. ISBN-Nr

Autoren

Titel

Jahr

Seiten

0-201-14192-2

Date, Ch.

The Relational Model for Database Management: Version 2

1990

538

Finkenzeller, H.

Systematischer Einsatz von SQL-Oracle

1989

494

Understanding the new SQL

1993

536

3-89319-117-8

Kracke, U. Unterstein, M. 1-55860-245-3

Melton, J. Simon, A.

Tabelle 3.11: Datensammlung »Buchtitel«

Aufgabe 3.7 Betrachten Sie die in Tabelle 3.12 dargestellte Relation. a) Welche Normalformen sind verletzt? b) Erzeugen Sie ein äquivalentes System in normalisierter Form. Matrikel- Student Nr

Kurs-Nr

Kurs-Titel

Note

30321

Meyer, J.

706S6

Datenbanksysteme

1,0

30321

Meyer, J.

715S4

Software-Engineering

1,7

30346

Ahrens, H.

715S4

Software-Engineering

3,0

30346

Ahrens, H.

706S6

Datenbanksysteme

2,0

30346

Ahrens, H.

713S5

Relationale u. funktionale Programmierung

1,7

30378

Knudsen, K.

706S6

Datenbanksysteme

2,0

Tabelle 3.12: Relation »Kursbelegung«

149

3 Datenbankentwurf

3.4.7 Die Boyce-Codd-Normalform (BCNF) Wenn wir bei der Formulierung die störende Zusatzbedingung mit dem Primattribut weglassen, erhalten wir die Boyce-Codd-Normalform, die also eine Verschärfung der dritten Normalform darstellt.

Boyce-Codd-Normalform (BCNF) Eine Relation ist in der Boyce-Codd-Normalform, wenn für jede nicht triviale funktionale Abhängigkeit T → A T Obermenge eines Schlüssels ist. Die BCNF ist eigentlich die einfachste Normalform: Es gibt nur funktionale Abhängigkeiten, die aufgrund der Schlüsseleigenschaften zu gelten haben. Es ist aber bei vorgegebener Menge von funktionalen Abhängigkeiten nicht möglich, für jede Relation eine Menge von Projektionen anzugeben, sodass die Ausgangsrelation sich als Verbund dieser Relationen darstellen lässt und genau die Werte für die Projektionen möglich sind, die als Verbund zulässige Werte für die Ausgangsrelation ergeben.

Beispiel für eine Relation in 3NF mit gegenseitigen Ausschlusskriterien So sei folgende Relation in der Hochschule im Rahmen des Vorlesungsplans gegeben (vgl. Tabelle 3.13): Stundenplan (tag, block, dozent, lehrveranstaltung, raum) Tag

Block

Dozent

Lehrveranstaltung

Raum

Montag

1

Otten

Datenbanksysteme

PC-Labor

Montag

2

Otten

Datenbanksysteme

PC-Labor

Montag

1

Hinz

Chemie

Chemie-Hörsaal

Montag

2

Hinz

Chemie

Chemie-Hörsaal

Dienstag

1

Otten

Java

PC-Labor

Tabelle 3.13: Relation »Stundenplan«

Hierbei sei Block die festgelegte Doppelstunde, in der nach vorgegebenem Raster Lehrveranstaltungen stattfinden können. Die darin steckende Vereinfachung nehmen wir hin.23

23 Der Tag ist hier in ein Raster von sechs Blöcken zu 90 Minuten mit dazwischenliegenden Pausen eingeteilt. Die verschiedenen Blöcke überschneiden sich nicht. Ohne die dogmatische Festlegung auf diese festen Blöcke haben wir dieselben Probleme bei der Verwaltung des Stundenplanes – wir könnten die gegenseitigen Ausschlusskriterien dann aber nicht durch funktionale Abhängigkeiten beschreiben, was wir für dieses Beispiel brauchen. Des Weiteren nehmen wir die Vereinfachung hin, dass hier nur ein Studiengang geplant wird. Würden wir mehrere Studiengänge zulassen, müssten die Schlüssel mindestens noch ein weiteres Attribut enthalten.

150

Normalformen in relationalen Datenbanken

Wir erhalten folgende funktionale Abhängigkeiten: 왘 {tag, block, raum} → {lehrveranstaltung},

Zu einem festen Zeitpunkt kann in einem Raum nur eine Veranstaltung stattfinden. 왘 {tag, block, raum} → {dozent},

Zu einem festen Zeitpunkt kann ein Raum nur für einen Dozenten reserviert sein. 왘 {tag, block, dozent} → {raum},

Zu einem festen Zeitpunkt kann ein Dozent nur in einem Raum eine Veranstaltung durchführen. 왘 {tag, block, dozent} → {lehrveranstaltung},

Zu einem festen Zeitpunkt kann ein Dozent nicht mehr als eine Lehrveranstaltung durchführen. 왘 {tag, block, lehrveranstaltung} → {dozent}

Zu einem festen Zeitpunkt kann nur eine Lehrveranstaltung stattfinden. 왘 {lehrveranstaltung} → {raum}

Einer Lehrveranstaltung ist ein fester Raum zugeordnet. Es gibt drei Schlüssel für diese Relation: {tag, block, dozent}, {tag, block, raum} und {tag, block, lehrveranstaltung}. Diese Relation ist in dritter Normalform, da alle Attribute prim sind und es daher keine funktionale Abhängigkeit gibt, an der ein Nichtprimattribut beteiligt ist. Dennoch gibt es einige Probleme beim Einfügen von neuen Tupeln und beim Ändern von Tupeln, da die ganzen gegenseitigen Ausschlusskriterien beachtet werden müssen. Die Relation ist aber nicht in Boyce-Codd-Normalform, da die Determinante der funktionalen Abhängigkeit {lehrveranstaltung} → {raum} keine Obermenge eines Schlüssels ist. Eine Zerlegung nach der Boyce-Codd-Normalform ergibt die beiden Relationen (vgl. Tabellen 3.14 und 3.15): Stundenplan_1 (tag, block, dozent, lehrveranstaltung) LV_Raum (lehrveranstaltung, raum) Tag

Block

Dozent

Lehrveranstaltung

Montag

1

Otten

Datenbanksysteme

Montag

2

Otten

Datenbanksysteme

Montag

1

Hinz

Chemie

Montag

2

Hinz

Chemie

Dienstag

1

Otten

Java

Tabelle 3.14: Relation »Stundenplan_1«

151

3 Datenbankentwurf

Lehrveranstaltung

Raum

Datenbanksysteme

PC-Labor

Chemie

Chemie-Hörsaal

Java

PC-Labor

Tabelle 3.15: Relation »Stundenplan_2«

Diese Relationen sind jeweils in BCNF. Sie sind Projektionen der Ursprungsrelation, und die Ursprungsrelation lässt sich als natürlicher Verbund wiederherstellen. Es geht jedoch u. a. die funktionale Abhängigkeit {tag, block, raum} → {lehrveranstaltung},

verloren, da die Menge dieser Attribute nicht mehr in einer Relation vorhanden ist. In Stundenplan_1 kann somit folgendes Tupel eingefügt werden: Tag

Block

Dozent

Lehrveranstaltung

Montag

1

Kunz

Java

Das ist nach den verbleibenden funktionalen Abhängigkeiten in Stundenplan_1 zulässig. Der natürliche Verbund der beiden Relationen würde jedoch die in Tabelle 3.16 dargestellte unzulässige Belegung von Stundenplan ergeben, in der jetzt das PC-Labor montags im ersten Block doppelt belegt ist. Tag

Block

Dozent

Lehrveranstaltung

Raum

Montag

1

Otten

Datenbanksysteme

PC-Labor

Montag

2

Otten

Datenbanksysteme

PC-Labor

Montag

1

Hinz

Chemie

Chemie-Hörsaal

Montag

2

Hinz

Chemie

Chemie-Hörsaal

Dienstag

1

Otten

Java

PC-Labor

Montag

1

Kunz

Java

PC-Labor

Tabelle 3.16: Relation »Stundenplan« mit inkonsistenten Eintragungen

3.4.8 Arten von funktionalen Abhängigkeiten Wir greifen hier noch einmal einige Argumente auf, die wir in Abschnitt 3.4.2 schon besprochen haben. Das eigentliche Problem sind nicht die Normalformen an sich, sondern die Arten der funktionalen Abhängigkeiten:

152

Normalformen in relationalen Datenbanken 왘 Die wichtigste Klasse der funktionalen Abhängigkeiten ergibt sich daraus,

dass Entitäten mit ihren Attributen durch eine eigenständige Relation implementiert werden. Wir erhalten damit die Abhängigkeiten vom Primärschlüssel (z. B. Relation der Kunden mit der Kundennummer als Primärschlüssel). 왘 Des Weiteren ergeben sich funktionale Abhängigkeiten dadurch, dass n:m-

Beziehungstypen durch Relationen implementiert werden: In diesen Relationen sind Attribute von der Vereinigung der Primärschlüssel der beteiligten Entitätentypen abhängig (z. B. die Relation, die sich aus dem Beziehungstyp »Bestellposition« ergibt). 왘 Daneben kann es funktionale Abhängigkeiten geben, weil in einer Relation die

Attribute eines weiteren Entitätentyps mit enthalten sind (vgl. im Folgenden die Bestellrelation, die neben der Kundennummer weitere Kundenattribute enthält). 왘 Abhängigkeiten, die durch gegenseitige Ausschlusskriterien entstehen, können

nicht durch Normalisierung gelöst werden. Die Einhaltung solcher Abhängigkeiten kann dagegen durch Trigger gelöst werden (vgl. Kapitel 7.3.3). Die damit zusammenhängenden Probleme des Datenbankentwurfs lassen sich am besten durch einen sauberen Entwurf im Entity-Relationship-Modell lösen, in dem die Normalisierung in einem zweiten Schritt zur Vollständigkeitskontrolle eingeführt wird.

3.4.9 Bewusste Abweichungen von Normalformen Häufig wird argumentiert, dass bei konsequenter Normalisierung die Antwortzeiten von Abfragen unzumutbar groß werden und dass man deswegen besser Verstöße gegen die zweite und dritte Normalform hinnehmen solle. Richtig ist, dass normalisierte Tabellen die Notwendigkeit implizieren, für viele Abfragen die zusammengehörigen Daten erst per Join über verschiedene Tabellen zusammenzusuchen. Joins sind in der Tat »teure«, d. h. aufwendige Operationen eines DBMS. Auf die Nachteile, die eine solche »Denormalisierung« hinsichtlich Änderungs-, Lösch- und Einfügeoperationen mit sich bringt, haben wir zu Anfang des Kapitels 3.4 hingewiesen. Wer eine Datenbank denormalisiert, muss Vorkehrungen zur Bewältigung der Redundanz treffen, sonst riskiert er inkonsistente Daten und damit die Zuverlässigkeit der Datenbank an sich. Bevor man im Übrigen leichtfertig aus Gründen der »Performanz« eine nicht oder nur teilweise normalisierte Datenbank implementiert, sollte man die Möglichkeiten der Beschleunigung von Verbundabfragen durch Indizes (Kapitel 4.3), Cluster und ähnliche Instrumente untersuchen. So manche Antwortzeit lässt sich um Größenordnungen verkürzen! Ein weiterer Grund, auf die vollständige Normalisierung zu verzichten, liegt in der Möglichkeit, berechnete Spalten zu definieren. Der SQL-Standard erlaubt die Angabe einer Berechnungsregel für Spalten in einer Tabellendefinition. Dies aller-

153

3 Datenbankentwurf

dings unter stark einschränkenden Bedingungen: So muss die Berechnungsvorschrift deterministisch sein und darf keine Zugriffe auf andere Tabellen, also auch keine Subquery, enthalten. Das DBMS kontrolliert dann die Redundanz, indem es die Berechnung bedarfsweise durchführt, wenn sich die Grunddaten geändert haben oder wenn die Daten abgerufen werden. Jedenfalls verhindert es, dass die Ausgangsdaten und der berechnete Wert sich widersprechen. Einen Join kann man damit nicht ersparen, aber evtl. eine kleine Berechnung als Bestandteil einer Abfrageanweisung. So könnte man beispielsweise zusätzlich zu den Seitenlängen eines Rechtecks die Fläche als berechnete Spalte in einer Tabelle ablegen. In jedem Fall setzt der bewusste Einsatz von Redundanz die Kenntnis der normalisierten Strukturen voraus. Also muss man beim Entwurf einer Datenbank stets bis zur dritten Normalform normalisieren. Nur dann ist man in der Lage, die Konsequenzen einer Abweichung zu übersehen und danach zu handeln.

3.5 Qualitätskriterien für den Datenbankentwurf Immer wieder kommt es in praktischen Projekten vor, dass beim Datenbankentwurf, um Zeit zu sparen, nicht alle Möglichkeiten der Konsistenzkontrolle ausgenutzt werden. Es wird beispielsweise häufig auf die Festlegung von Schlüsseln oder Fremdschlüsseln verzichtet, von den Möglichkeiten der Einschränkungen von Wertebereichen für Attribute wird selten Gebrauch gemacht. Der »Zeitgewinn« erweist sich schnell als nur vordergründig, denn die Kontrolle der Datenkonsistenz wird so in die Anwendungsprogrammierung verlegt oder dem Benutzer überlassen. Wir möchten dafür plädieren, sich dem Datenbankentwurf mit allergrößter Sorgfalt zu widmen, denn: Eine Datenbank »lebt« viele Jahre, länger als jedes Anwendungsprogramm. Sie kann langfristig eine stabile Quelle für Informationen aller Art sein, aber auch Quelle von Ärger, wenn man nur mit »Tricks« an die benötigten Daten kommt oder sich auf sie nicht verlassen kann. Man kann in jedem Fall davon ausgehen, dass ein sauberer Datenbankentwurf den Umgang mit der Datenbank bei Abfragen und sonstigen Datenmanipulationen ebenso wie bei der Anwendungsentwicklung erleichtert. Die folgenden Kriterien sollen Anhaltspunkte für einen »guten« Datenbankentwurf geben. Ein wichtiges Qualitätsmerkmal ist bereits die Unterscheidung in verschiedene Datenmodelle, die verschiedene Abstraktionsstufen darstellen. Es ist auf jeder Stufe angebracht, beim Modellieren der Zusammenhänge so genau wie möglich zu verfahren und sich nicht zu früh mit Einschränkungen des DBMS zu arrangieren, welche die Umsetzung von Konsistenzbedingungen teilweise gar nicht zulassen. Immerhin könnte die nächste Version solche Mängel beheben, oder man entscheidet sich doch noch für ein anderes Produkt. Die Regeln, die wir hier niedergelegt haben, sind als Empfehlungen zu verstehen und nicht alle gleichermaßen zwingend. Ein Datenbankentwurf, der nicht jeder Relation einen Schlüssel

154

Qualitätskriterien für den Datenbankentwurf

zuordnet, ist sicher als schlecht zu bezeichnen. Die Bezeichnung von Attributen kann hingegen aus verschiedenen Gründen von unseren Vorschlägen abweichen. Schließlich mag es in der einen oder anderen Firma noch weitergehende und vielleicht restriktivere Vorschriften für den Datenbankentwurf geben.

3.5.1 Konzeptuelles Modell 1) Das konzeptuelle Modell ist als Entity-Relationship-Modell oder Klassenmodell nach UML dokumentiert. 2) Für alle Beziehungen sind minimale und maximale Kardinalität in beiden Richtungen angegeben. 3) Falls zwischen zwei Entitätentypen bzw. Klassen A und B mehr als eine Beziehung existiert, werden alle Beziehungen zwischen A und B mit Namen qualifiziert. 4) Der Name einer Beziehung ist nicht identisch mit einer der Rollenbezeichnungen. 5) Werden Rollen zu einer Beziehung angegeben, sind diese in beiden Richtungen spezifiziert.

3.5.2 Logisches Datenmodell als Relationenschema 1) Domänen sind als eigenständige Datenbankobjekte dokumentiert. 2) Jedem Attribut ist die Domäne zugeordnet, die seine Wertemenge am genauesten beschreibt. 3) Alle Schlüssel sind dokumentiert (als PRIMARY KEY oder UNIQUE). 4) Alle Fremdschlüssel sind angegeben. Das Verhalten beim Ändern und Löschen von Vatertupeln ist mit Begründung festgelegt. 5) Alle Konsistenzbedingungen, die nicht mit Schlüsseln, Fremdschlüsseln oder Domänen zusammenfallen, sind dokumentiert. 6) Auch Konsistenzbedingungen, die mehr als eine Relation betreffen, sind dokumentiert.

3.5.3 Physikalisches Schema für die Zieldatenbank 1) Für jede Domäne ist ein Datentyp, ggf. mit CHECK-Bedingung, festzulegen, der durchgängige Konsistenz sicherstellt. 2) Jedem Attribut ist der Datentyp zugewiesen, der seine Wertemenge am genauesten beschreibt. Wertemengen werden z. B. durch CHECK-Klauseln oder andere Möglichkeiten der Werteinschränkung definiert. 3) Alle Konsistenzbedingungen (Constraints) werden durch entsprechende Konstrukte der Datendefinitionssprache umgesetzt.

155

3 Datenbankentwurf

4) Alle Schlüssel werden mithilfe der Datendefinitionssprache realisiert. 5) Alle Fremdschlüssel werden mithilfe der Datendefinitionssprache realisiert. Dabei werden die Verhaltensregeln beim Ändern und Löschen des Vatersatzes berücksichtigt. 6) Für alle Fremdschlüssel wird das Verhalten beim Löschen und Ändern der Vatertupel festgelegt. 7) Alle Konsistenzbedingungen, die nicht mit Schlüsseln oder Domänen zusammenfallen, werden umgesetzt, soweit das DBMS dies erlaubt. 8) Auch Konsistenzbedingungen, die mehr als eine Relation betreffen, werden umgesetzt, soweit das DBMS dies erlaubt. 9) Alle Konsistenzbedingungen (Constraints) haben Namen (falls das DBMS das unterstützt). 10)Nicht umsetzbare Konsistenzbedingungen (z. B. Assertions) werden in den Befehlen zur Datendefinition als Kommentare dokumentiert.

3.5.4 Normalformen 1) Alle Relationen sind in dritter Normalform. Abweichungen davon sind zu begründen. 2) Enthält das Datenmodell unauflösbare Redundanzen, sind diese und ein Verfahren zur möglichst automatisierten Konsistenzsicherung dokumentiert.

3.5.5 Namenskonventionen 1) Namen für Relationen folgen einer einheitlichen Regel. Sie bezeichnen die darin zu speichernde Objektart stets im Singular oder stets im Plural. Wir empfehlen, grundsätzlich den Singular zu verwenden. 2) Der Einsatz von Unterstrichen _ und Sonderzeichen in Bezeichnern folgt einer einheitlichen Regel. Wird beispielsweise ein Attribut als kunden_nr bezeichnet, heißt es ebenfalls artikel_nr und nicht artikelnummer oder artikelnr. 3) Bezeichner für Attribute sollten möglichst aussagefähig sein bezüglich der inhaltlichen Bedeutung. 4) Gleichbedeutende Attribute in verschiedenen Relationen haben möglichst denselben Bezeichner. 5) Constraints erhalten aussagefähige Namen. Die Art der Konsistenzbedingung kann durch Präfixe wie PKEY_, FKEY_, CHK oder Ähnliches gekennzeichnet werden.

156

Weitere Aufgaben bei der Einführung einer Datenbank

3.6 Weitere Aufgaben bei der Einführung einer Datenbank 3.6.1 Bestimmung eines Mengengerüsts für die Datenbank Nach dem Entwurf und vor der Implementierung muss festgestellt werden, welches Datenvolumen vorliegt. Dies ist zum einen wichtig, um abzuschätzen, ob ein vorgesehenes Datenbank-Verwaltungssystem überhaupt in der Lage ist, das Datenvolumen zu verwalten. Zum anderen müssen natürlich entsprechend große Datenträger zur Verfügung gestellt werden. Als Faustformel können wir von Folgendem ausgehen: Größe_einer_Relation = Anzahl_der_Tupel * (Größe_eines_Tupels + 1,2 * Summe(Länge_der_indizierten_Felder) + Platz_für_zukünftige_Erweiterungen.

Zu den indizierten Feldern sind – auch wenn der Benutzer nicht explizit einen Index anlegen muss – auch die Attribute der Schlüssel und der Fremdschlüssel zu rechnen. Die konkrete Größe hängt allerdings von Implementierungsdetails des konkreten Datenbanksystems ab, z. B. ob die einzelnen Sätze mit einer festen Satzlänge gespeichert werden, ob Leerzeichen komprimiert werden oder ob bei einer Speicherung in aufsteigender Reihenfolge des Primärschlüssels (clustered) Platz für spätere Einfügungen freigelassen wird. Hierbei ist besondere Sorgfalt auf die Berechnung der größeren Tabellen zu legen, während kleinere Tabellen sehr grob geschätzt werden können. Es kommt im Wesentlichen auf die Summe an. Entwurfswerkzeuge wie z. B. PowerDesigner nehmen die Berechnung automatisch vor, wenn bei den Entitätenmengen oder Tabellen jeweils die Anzahl der erwarteten Einträge angegeben wird.

3.6.2 Datenübernahme Üblicherweise sind für eine vorgesehene Anwendung schon Daten, gegebenenfalls in einem anderen Format, vorhanden. Diese Daten sind häufig unter anderen Gesichtspunkten geordnet. Hier sind Programme oder Werkzeuge bereitzustellen, die diese Daten übernehmen. Bei der Umstellung eines vorhandenen Verfahrens muss ein Konzept für den Parallelbetrieb entwickelt werden. Es können beide Verfahren mit echten Daten gefahren werden – was aber eine hohe Redundanz bei der Dateneingabe erfordert. Eine andere Möglichkeit besteht darin, ein Batch-Programm zu entwickeln, mit dem die Daten aus dem alten Verfahren in das neue übernommen werden. Dieses ist gegebenenfalls einige Male zu testen, um dann zu einem Zeitpunkt X endgültig das neue Verfahren einführen zu können.

157

3 Datenbankentwurf

Dabei tritt das Problem auf, dass die Daten in der Parallelphase möglicherweise doppelt eingegeben werden müssen. Dabei können zeitweise inkonsistente Zustände zwischen den Daten nach dem alten Verfahren und den Daten nach dem neuen Verfahren auftreten. Spätestens bei der Übernahme des neuen Verfahrens müssen die Daten nach dem neuen Verfahren in einem konsistenten Zustand sein. Außerdem muss ein Konzept für die Schulung des Personals auf das neue Verfahren entwickelt werden. Bei völlig neuer Entwicklung ist gegebenenfalls in der Anfangsphase ein größeres Datenvolumen zu erfassen. Dies kann durch zusätzliches Personal durchgeführt werden, oder es ist eine fremde Firma zu beauftragen.

3.6.3 Datenschutzkonzept Der Datenschutz ist ein Thema, das im Rahmen dieser Einführung nur kurz angesprochen werden kann. Es sind hier drei Bereiche zu beachten: 왘 Genügen der Aufbau der Datenbank und die organisatorischen Sicherungsmaßnahmen den Anforderungen des Bundesdatenschutzgesetzes bzw. des jeweiligen Landesdatenschutzgesetzes? Sind Beteiligungsrechte zu beachten, und ist die Speicherung zu melden? 왘 Ist sichergestellt, dass vertrauliche Firmendaten nicht von Unbefugten eingesehen oder kopiert werden können? 왘 Ist sichergestellt, dass die Daten nicht durch Unbefugte manipuliert (verändert oder zerstört) werden können?

3.6.4 Datensicherungskonzept Hier geht es in erster Linie um Maßnahmen, die sicherstellen sollen, dass bei nicht vorhersehbaren Ereignissen wie Überspannungsschäden, Feuerschäden, Wasserschäden, Diebstahl, Zerstörung, technischen Defekten oder Systemfehlern die notwendigen Daten wiederhergestellt werden können. Hier spielen zwei Verfahren eine größere Rolle: 왘 Totalsicherungen in bestimmten Abständen auf externe Datenträger

(z. B. Streamer-Tape, beschreibbare CD-ROM) oder über DFÜ auf einen entfernten Rechner. 왘 Sicherungen der Datenänderungen nach jeder Änderungstransaktion. Dies

sollte vom Datenbank-Verwaltungssystem unterstützt werden; es ist somit ein Auswahlkriterium für das verwendete Datenbank-Verwaltungssystem. Es muss grundsätzlich möglich sein, nach Zerstörung der aktiven Datenbank (z. B. physikalische Zerstörung der Platte oder Betriebssystemfehler) die Datenbank anhand der Sicherungen, Teilsicherungen und Protokolle wieder in den Zustand vor der Zerstörung zu versetzen.

158

Weitere Aufgaben bei der Einführung einer Datenbank

3.6.5 Verteilung der Daten und Verteilung der Verarbeitung Hier ist zu überlegen, ob die Daten auf mehrere Knoten zu verteilen sind. Wichtige Strukturdaten, deren Änderungshäufigkeit gering ist (z. B. Bankleitzahlen), sollten auf mehreren Knoten redundant gehalten werden. Wichtig ist ein Konzept zur automatischen Aktualisierung von Daten auf mehreren Knoten. Neben der Verteilung der Daten kann auch die Verarbeitung verteilt werden: Es muss ein Konzept für ein Client-Server-System entwickelt werden.

3.6.6 Historienführung In einer Datenbank werden in der Regel Geschäftsprozesse abgebildet, die zu einem bestimmten Punkt abgeschlossen sind. So folgt auf einen Auftrag die Erledigung des Auftrages. Spätestens mit der vollständigen Bezahlung der Rechnung nach der Erledigung des Auftrages sind diese Daten als operative Daten nicht mehr notwendig. Diese Daten können nach der operativen Phase als Grundlage für ein Unternehmensinformationssystem24 dienen. Hier ist zu bedenken, dass die Anforderungen an diese Daten andere sind als an die operativen Daten. Bei den operativen Daten ist die ständige Verfügbarkeit des jeweils aktuellen Stands notwendig. Die Ergebnisse müssen jeweils in sehr kurzer Zeit verfügbar sein (z. B. für Nachfragen bei Aufträgen oder für die Produktionssteuerung). In einem Unternehmensinformationssystem dagegen ist die Antwortzeit nicht so ein begrenzender Faktor – dafür sind hier sehr viel größere und komplexere Informationsmengen zu verarbeiten. Für den Datenbankentwurf stellen sich folgende Fragen: 왘 Wie lange bleiben Sätze in der aktuellen Datenbank? 왘 Zu welchen Zeitintervallen und wohin werden diese Sätze entsorgt? (Streamer-

Tape, beschreibbare CD oder Löschung, da Papierarchiv vorhanden) 왘 Wie werden Historiensätze geführt (d. h. Informationen über vergangene

Zustände, z. B. vergangene Buchungen)? 왘 Welche Daten sind als betriebliche Kennziffern wichtig, und wie können diese

Daten aggregiert werden?

24 In den 70er-Jahren war viel von Management Information Systems die Rede. Da die Versprechungen größer waren als die erbrachten Leistungen, hat man seit langer Zeit nichts mehr davon gehört. Heute werden spezielle Systeme unter dem Begriff Data Warehouse angeboten.

159

3 Datenbankentwurf

3.6.7 Auswahl eines Datenbank-Verwaltungssystems Häufig ist in der betrieblichen Umgebung ein Datenbank-Verwaltungssystem schon eingeführt. Es ist dann zu prüfen, ob dieses System die Anforderungen erfüllt. Eine Einführung eines neuen Systems für weitere Anwendungen muss schon sehr gut begründet werden. Bei der Auswahl sind die Aspekte aus den Abschnitten 3.6.1, 3.6.3, 3.6.4 und 3.6.5 zu berücksichtigen. Neben der Auswahl der Datenbankmaschine, d. h. des Datenbank-Verwaltungssystems auf dem Backend, ist das Programmentwicklungssystem auszuwählen. Genormte Schnittstellen wie z. B. ODBC von Microsoft oder JDBC erlauben den gleichzeitigen Einsatz unterschiedlicher Frontend-Systeme verschiedener Hersteller unabhängig vom eingesetzten Datenbank-Verwaltungssystem.

160

Datendefinition in SQL SQL hat sich als Standardabfragesprache für relationale Datenbanken etabliert. SQL steht ursprünglich für »Structured Query Language«. Die ersten Versuche dazu wurden in den IBM-Labors vorgenommen, und daraus ist die Vorläufersprache SEQUEL entstanden. Im strengen Sinne sind SQL-Datenbanken nicht relational, sondern die Daten werden in sog. »Tabellen« dargestellt. In einer Tabelle ist es grundsätzlich zugelassen, dass identische Tupel mehrfach vorkommen. Durch das Konzept des Primärschlüssels ist es jedoch möglich sicherzustellen, dass keine identischen Tupel mehrfach vorkommen. Des Weiteren können alle Operationen der Relationenalgebra, die wir in Kapitel 3 eingeführt haben, mit entsprechenden SQL-Konstrukten erzeugt werden. Mit SQL können wir die Tabellenstruktur einer Datenbank definieren; hierzu gehören auch die in Kapitel 3 eingeführten Konsistenzregeln wie Schlüssel und Fremdschlüssel. Des Weiteren stellt SQL die Schnittstelle zwischen der relationalen Datenbank und dem Anwendungsprogramm dar. Die Sprache ist in erster Linie nicht für Endanwender gedacht, sondern für Systementwickler.

4.1 SQL und SQL-Standard 4.1.1 Ein kleiner Überblick über die Historie von SQL 1974 D. Chamberlain et al. definieren SEQUEL. 1977 Revision: SEQUEL/2; IBM-Prototyp System R. 1977 Oracle beginnt mit der Implementierung von SEQUEL auf Mainframes. 1979 Oracle liefert ein erstes relationales Datenbanksystem mit SQL aus. 1981 IBM liefert SQL/DS aus. 1983 IBM stellt DB2 vor. 1985 Ingres stellt auf SQL um. Informix stellt auf SQL um. 1986 Sybase wird ausgeliefert.

161

4 Datendefinition in SQL

1986 X/OPEN entscheidet sich für SQL. 1987 Der Standard ISO 9075 Database Language SQL (SQL-86) wird veröffentlicht. 1988 dBASE IV erhält SQL als Abfragesprache. Adabas erhält SQL. 1989 ISO 9075 Database Language SQL with Integrity Enhancement (SQL-89) wird veröffentlicht. SESAM und UDS erhalten SQL. 1992 ISO 9075 Database Language SQL (SQL2 oder SQL-92) wird veröffentlicht. 1993 RDA-Standard (Remote Data Access) der Database Access Group (z. B. Microsoft ODBC) wird veröffentlicht. 1996 SQL/PSM (Persistent Stored Modules); Datenbankprozeduren werden normiert (vgl. Kapitel 7.3). 1999 SQL:1999 wird veröffentlicht und löst damit SQL-92 ab. 2003 Eine abermals überarbeitete und teilweise neu strukturierte Version SQL:2003 löst SQL:1999 ab. Zwischen dem SQL-Standard und den in kommerziell verfügbaren Datenbanksystemen angebotenen SQL-Implementierungen gibt es Differenzen in beiden Richtungen. Teilweise übersteigt der verfügbare Sprachumfang die Forderungen der Norm, teilweise werden Forderungen der SQL-Norm nicht erfüllt. Es ist aber grundsätzlich zu erkennen, dass die Hersteller sich mehr und mehr um die Einhaltung des SQL-Standards bemühen. Die Standardisierung von SQL ist aus folgenden Gründen wichtig: 왘 Verschiedene RDBMs können nebeneinander im Betrieb existieren. Die

Anwendungsentwicklung und Datenbankadministration sollten möglichst wenig voneinander abweichen. 왘 Anwendungen sollen portabel sein, damit man beispielsweise auf ein leis-

tungsfähigeres DBMS umsteigen kann oder damit die Anwendungsentwicklung in einer anderen Umgebung erfolgen kann als der produktive Einsatz. 왘 Herstellerunabhängigkeit ist gefordert. 왘 Die Verfügbarkeit von »Frontend-Systemen«, also Programmen, die herstelle-

runabhängig auf Datenbanken zugreifen können, nimmt zu. 왘 In Client-Server-Umgebungen kommuniziert die Anwendersoftware über

Schnittstellen mit dem DBMS. 왘 Bei der Anwendungsentwicklung mit CASE-Tools erfolgt die Festlegung von

Daten- und Programmstrukturen über weite Strecken unabhängig vom eingesetzten DBMS.

162

SQL und SQL-Standard

4.1.2 Elemente von SQL Die Sprachelemente von SQL lassen sich in zwei Kategorien unterteilen: die DDL (Data Definition Language), die wir in diesem Kapitel einführen, und die DML (Data Manipulation Language), die im folgenden Kapitel eingeführt wird.

Zur DDL gehören u. a.: 왘 Anweisungen zur Anlage und Verwaltung von Schemata (Zusammenfassung

von Tabellen, Datensichten etc.), für die ein bestimmter Benutzer zuständig ist 왘 Anweisungen zur Definition von Domänen 왘 Anweisungen zur Definition von Relationen einschließlich der dazugehörigen

Konsistenzbedingungen 왘 Anweisungen zur Anlage von Datensichten (Views) 왘 Die Verwaltung von Benutzern und deren Datenzugriffsrechten

Weiterhin gehören zum Sprachumfang verfügbarer relationaler Datenbanksysteme Anweisungen, welche die interne Organisation der Datenbanken steuern. Sie sind syntaktisch meist an SQL-Anweisungen angelehnt, unterliegen aber nicht der Norm, da diese sich nicht auf das interne Schema einer Datenbank bezieht. Dazu gehören: 왘 Anweisungen zur Definition von Indizes 왘 Erweiterungen der CREATE TABLE-Anweisung im Hinblick auf Varianten der

Ablage auf dem Speichermedium (beispielsweise in »Clustern«)1 Wir beziehen uns im Wesentlichen auf SQL im Sinne des Standards SQL:2003. An einigen Stellen weisen wir auf Abweichungen und Besonderheiten bei existierenden DBMS (Sybase, Oracle etc.) hin.

Wie benutzen wir SQL? Die in diesem und im folgenden Kapitel vorgestellten Beispiele sowie die Übungsaufgaben haben wir mit den Datenbanksystemen Sybase SQL Anywhere, Oracle, Firebird, MySQL, PostgreSQL, HSQLDB getestet. Abweichende Skriptdateien sind ggf. auf der Begleit-CD zu finden. Wir können die DDL-Anweisungen unter Verwendung eines entsprechenden Programms (»Administrator-Tool«) direkt hinschreiben (meist steht auch ein integrierter Editor zur Verfügung). Die Anweisungen können wir dann an die Datenbankmaschine senden, die sie interpretiert und – sofern die Anweisung syntaktisch richtig geschrieben worden ist und keine logischen Fehler existieren – aus-

1 Das beispielsweise bei Oracle vorhandene Cluster-Konzept sieht die Speicherung von Tupeln verschiedener Tabellen, die häufig miteinander verbunden werden, physikalisch benachbart auf den Plattensektoren vor, um damit die Anzahl der Zugriffe zu reduzieren.

163

4 Datendefinition in SQL

führt. Wir bekommen anschließend eine Statusmeldung zurück (»Anweisung erfolgreich ausgeführt« oder »Fehler ...«).

Abbildung 4.1: Administrator-Tool mit SQL

Eine andere Möglichkeit stellen Administrator-Tools dar, welche die Datenbankobjekte grafisch darstellen und mit denen man direkt die Datenbankobjekte manipulieren kann. Sybase SQL Anywhere liefert hier das Programm Sybase Central. Auch dieses Administrator-Tool arbeitet nicht direkt auf der Datenbank, sondern holt sich die Information über die Datenbankstruktur (mittels SQL!) aus der Datenbank, stellt sie dar und erzeugt nach Änderungen der Struktur SQL-Anweisungen, die es an die Datenbankmaschine sendet, und wertet die zurückgemeldeten Statusmeldungen aus.

164

Schemadefinition

Wichtig ist jedoch die Möglichkeit, dass das Administrator-Tool die SQL-Anweisungen für die Erzeugung der Datenbankobjekte ausgeben kann. Damit lassen sich in der Testphase auf einfache Art Entwurfsalternativen erzeugen und austesten.

Abbildung 4.2: Grafisches Administrator-Tool

4.2 Schemadefinition Ein SQL-Schema enthält Datenbankobjekte2 eines einzelnen Benutzers3 innerhalb einer Datenbank. Dazu gehören unter anderem von ihm erzeugte Basistabellen, Datensichten mit ihren Integritätsbedingungen und Datenbankprozeduren. Jedes Datenbankobjekt gehört zu genau einem Schema, und jedes Schema gehört genau einem Benutzer. Ein Benutzer kann aber Eigentümer mehrerer Schemata sein. Datenbankobjekte müssen innerhalb eines Schemas eindeutige Namen haben. Das bedeutet, dass in einer Datenbank mehrere Schemata gleichzeitig existieren und dass gleichnamige Datenbankobjekte in verschiedenen Schemata auftreten können.

2 Der Begriff »Datenbankobjekt« darf nicht mit dem Objektbegriff der objektorientierten Datenbankmodelle verwechselt werden (vgl. Kapitel 9). 3 Unter einem Benutzer verstehen wir einen in der Datenbank namentlich eingetragenen, mit bestimmten Zugriffsrechten ausgestatteten Anwender der Datenbank. Benutzer und Person sind nicht unbedingt identisch. Einer Person können je nach Anwendungszusammenhang verschiedene Benutzernamen zugeordnet sein, beispielsweise einer für die Rolle als Datenbankadministrator und ein anderer für die normale Nutzung der Datenbank. Umgekehrt kann es manchmal organisatorisch sinnvoll sein, einen Benutzernamen (z. B. GAST) einzurichten, unter dem sich verschiedene Personen anmelden können, um allgemein zugängliche Informationen abzufragen (Auskunftssysteme).

165

4 Datendefinition in SQL

Die Anweisung zur Anlage eines Schemas lautet in etwas vereinfachter Form: CREATE SCHEMA schema AUTHORIZATION benutzer

Es folgen dann die Definitionsanweisungen zur Erzeugung beliebig vieler Datenbankobjekte innerhalb des neuen Schemas, das sind beispielsweise CREATE DOMAIN, CREATE TABLE, CREATE VIEW in beliebiger Anzahl und Reihenfolge. Diese Anweisungen werden in den folgenden Abschnitten beschrieben. Zur Schemadefinition muss gesagt werden, dass zurzeit die meisten Datenbanksysteme diese Anweisung nicht zur Verfügung stellen. Oft wird gar nicht logisch zwischen Datenbank und Schema unterschieden, und die Anlage einer Datenbank erfolgt nicht über eine SQL-Anweisung, sondern über spezielle Hilfsprogramme, mit denen dann auch gleich noch Details der internen Ebene wie Name und Größe der Plattendatei festgelegt werden. Bei der Benutzung der Datenbankobjekte sind wir nicht an ein bestimmtes Schema gebunden, sondern können Objekte aus verschiedenen Schemata verwenden (falls wir die entsprechenden Zugriffsrechte haben). Wir müssen vor den Namen des Datenbankobjektes den Namen des Schemas schreiben. In der Regel gibt es für eine Datenbanksitzung ein »Standardschema«; für Datenbankobjekte, die diesem Standardschema angehören, müssen wir keine Schemaangabe machen. Bei Sybase SQL Anywhere ist der Bezeichner des Standardschemas mit dem Bezeichner des Benutzers der Sitzung identisch.

4.3 DDL: Datendefinition mit SQL Eine zentrale Forderung an eine relationale Datendefinitionssprache ist die Unterstützung von Integritätsbedingungen. SQL unterstützt Schlüssel (Primär- und Kandidatenschlüssel), Fremdschlüssel und sogenannte »Geschäftsregeln« – das sind Integritätsregeln, die sich durch logische Bezüge von Daten auf andere Daten ergeben. Für die Zuverlässigkeit der Daten ist beispielsweise die Eineindeutigkeit aller Primär- und Kandidatenschlüsselwerte unverzichtbar – wie sollen wir sonst einen bestimmten Kunden identifizieren? Wir stellen die entsprechenden Sprachelemente in diesem Kapitel vor. Seit 1992 wird eine rudimentäre Form von benutzerdefinierten Datentypen (»Domänen«4) unterstützt. SQL:2003 enthält auch objektorientierte Elemente, welche die sogenannten »objektrelationalen Datenbanken« charakterisieren sollen. Dazu gehört die Möglichkeit, »echte« benutzerdefinierte Datentypen inklusive Zugriffsmethoden und Vergleichsoperatoren zu erzeugen. Wir kommen auf diese Erweiterungen in Kapitel 9 zurück.

4 Sie erfüllen aber nur einen Teil des in Abschnitt 2.1.2 eingeführten Domänenkonzeptes.

166

DDL: Datendefinition mit SQL

Allgemein gilt, dass die Erzeugung neuer Datenbankobjekte mit dem Schlüsselwort CREATE eingeleitet wird. Anweisungen zur Entfernung von Datenobjekten beginnen mit dem Schlüsselwort DROP5.

4.3.1 Datentypen und Domänen Eine Domäne ist vom theoretischen Konzept her die Menge zulässiger Werte, die ein Attribut in einer Relation annehmen kann, verbunden mit einer Menge an Operationen, die innerhalb der Wertemenge oder in Verbindung mit anderen Wertemengen ausgeführt werden kann.6 CREATE DOMAIN gehört zum Datendefinitionsteil von SQL. Die Syntax lautet in der

Grundform:7 CREATE DOMAIN domänenname [AS] datentyp [default-klausel] [check-klausel]

Die Domänendefinition wird im Systemkatalog8 abgelegt. Jede Domäne muss einen eindeutigen Namen erhalten. Der Domänenname kann anschließend in einer CREATE TABLE-Anweisung an Stelle des dort definierten Datentyps verwendet werden. Da das Domänenkonzept von SQL-92 relativ schwach ist und die objektrelationalen Konzepte von SQL:1999 das weitergehende Konzept der abstrakten Datentypen eingeführt haben (vgl. Kapitel 9), gilt CREATE DOMAIN seit SQL:1999 als veraltet (»obsolete«), d.h., es kann in späteren Versionen des Standards entfernt werden. In unseren Beispielen verwenden wir CREATE DOMAIN an einer Stelle: Ab SQL:1999 gibt es einen Datentyp BOOLEAN in SQL, der aber noch nicht in allen Implementierungen realisiert ist. In Datenbanksystemen, die BOOLEAN noch nicht implementiert haben9, führen wir es ein durch: CREATE DOMAIN boolean AS SMALLINT CHECK (VALUE IN (0,1))

Es steht dann 0 für FALSE, 1 für TRUE und NULL für UNKNOWN.10

5 6 7 8 9 10

Wird in 4.3.8 kurz behandelt. Vergleiche Abschnitt 2.1.2. Die formale Syntaxbeschreibung ist in Anhang A beschrieben. Der Systemkatalog wird in Kapitel 8 eingeführt. Z. B. in Sybase SQL Anywhere Vgl. hierzu die Definition der Tabelle Artikel im Anhang C.

167

4 Datendefinition in SQL

Seit SQL:1999 gibt es darüber hinaus den »DISTINCT TYPE«, der vorher schon von DB2 eingeführt worden war. Die Anweisung CREATE DISTINCT TYPE erreicht, dass Attribute nur dann direkt vergleichbar sind, wenn sie demselben DISTINCT TYPE angehören. Beispielsweise ist ein Vergleich von Kundennummern mit Bestellnummern dann nicht ohne Weiteres möglich. Dies macht ja auch in der Regel keinen Sinn und ist meistens das Ergebnis einer Verwechslung von AttributBezeichnern. Die Definition eines DISTINCT TYPE schließt aber wiederum keine Einschränkung eines Wertebereichs ein. Beispiel: CREATE DISTINCT TYPE typ_kunden_nr AS INTEGER; CREATE DISTINCT TYPE typ_bestell_nr AS INTEGER;

Falls doch ausnahmsweise so ein Vergleich vorgenommen werden soll, ist das durch explizite Konvertierung auf den Ursprungstyp zu formulieren, was mit der CAST-Funktion11 geschehen kann. Beispiel: SELECT * FROM Bestellung WHERE CAST(kunden_nr AS INTEGER) = CAST(bestell_nr AS INTEGER)

4.3.2 Datentypen in SQL Es gibt die im Folgenden angegebenen Datentypen in SQL. Die Skalierungsgrößen in der Klammer hinter dem Datentyp können in der Regel weggelassen werden. Es werden dann Standardwerte eingesetzt, die bei numerischen Datentypen implementierungsabhängig sind, bei Zeichen- und Bitketten jeweils 1, bei TIME 0 (d. h. keine Unterteilung der Sekunde) und bei TIMESTAMP 6 (d. h. Genauigkeit auf Mikrosekunden).

Exakt numerisch INTEGER

üblicherweise vier Byte

SMALLINT

üblicherweise zwei Byte

BIGINT

mindestens so groß wie INTEGER

NUMERIC(p,q)

Dezimalzahlen mit genau p Stellen, davon q hinter dem Dezimalpunkt

DECIMAL(p,q)

Dezimalzahlen mit mindestens p Stellen, davon q hinter dem Dezimalpunkt

11 Vgl. Kapitel 5.2

168

DDL: Datendefinition mit SQL

Angenähert numerisch REAL

Gleitpunktzahlen, einfache Genauigkeit

DOUBLE PRECISION

Gleitpunktzahlen, doppelte Genauigkeit

FLOAT(p)

Gleitpunktzahlen, mindestens p Stellen Genauigkeit

Zeichenketten CHARACTER(n)

Zeichenketten mit genau n Zeichen

CHARACTER VARYING(n)

Zeichenketten mit höchstens n Zeichen

VARCHAR(n) NATIONAL CHARACTER(n)

Zeichenketten mit genau n Zeichen mit nationalen Besonderheiten wie z. B. Zeichensatz, Sortierreihenfolge

NATIONAL CHARACTER VARYING(n)

Zeichenketten mit höchstens n Zeichen mit nationalen Besonderheiten

CHARACTER LARGE OBJECT (n)

Große Textobjekte. Die maximal mögliche Länge ist implementierungsabhängig. Zur Spezifikation großer Längen können die Abkürzungen K (Kilo), M (Mega), G (Giga) verwendet werden.

CLOB (n)

Jede Zeichenkette hat eine bestimmte Länge, wobei diese Länge nicht immer die Anzahl der Bytes sein muss. Bei nationalen Zeichensätzen tritt eine Reihe von Problemen auf, zum Beispiel: 왘 Im Deutschen bilden ä, ö, ü jeweils einen Buchstaben, der aber bei der Sortie-

rung in Namensverzeichnissen jeweils wie ae, oe, ue zu behandeln ist. 왘 Im Spanischen gelten ch und ll (wie auch ñ) jeweils als ein Zeichen, das nach c, l

(bzw. n) einzusortieren ist.

Bitketten Bitketten dienen in erster Linie als Behälter für Objekte, die nicht vom DBMS interpretiert werden (wie Bilddaten, Zeichnungselemente, Font-Daten). Die Typen BIT und BIT VARYING sind aus dem Standard entfernt worden. Es gibt nur mehr einen Typ für große Bitmuster wie beispielsweise Bilder, Töne, Zeichnungen, deren Bedeutung dem DBMS nicht bekannt ist, die es also als »unstrukturierte Daten« behandelt.

169

4 Datendefinition in SQL

BINARY LARGE OBJECT (n)

Binäre Daten mit einer maximalen Länge entsprechend n Zeichen

BLOB(n)

Datum und Uhrzeit: DATE

Kalenderdaten vom Jahr 1 bis zum Jahr 9999

TIME(p)

Uhrzeit in Stunden, Minuten, Sekunden, wobei die Sekunden noch p Stellen nach dem Komma haben

TIMESTAMP(p)

Datum und Uhrzeit

TIME(p) WITH TIME ZONE

Die Angabe einer Zeitzone ist die Abweichung von der UCT (Zeitzone von London, früher »GMT« genannt). Wegen der Sommerzeit kann sie zwischen –11:59 und +13:00 liegen. MEZ ist UCT +1:00, MESZ = UCT + 2:00.

TIMESTAMP(p) WITH TIME ZONE

Datum und Uhrzeit mit Zeitzone

INTERVAL YEAR

Datumsdifferenz in Jahren

INTERVAL YEAR TO MONTH

Datumsdifferenz in Jahren und Monaten

INTERVAL DAY

Zeitdifferenz in Tagen

INTERVAL DAY TO HOUR

Zeitdifferenz in Tagen und Stunden

INTERVAL DAY TO MINUTE

Zeitdifferenz in Tagen, Stunden, Minuten

INTERVAL MINUTE TO SECONDS (6)

Zeitdifferenz in Minuten und Mikrosekunden

Logischer Datentyp In der Praxis sind boolesche Variablen unverzichtbar. Jedes Prädikat (WHEREKlausel) ist schließlich ein boolescher Wert. In Programmiersprachen werden boolesche Ausdrücke benutzt, um Verzweigungen zu bearbeiten (IF ... THEN ... ELSE). Einige, aber längst nicht alle Datenbanksysteme bieten einen solchen Typ für logische Daten bereits an. Umso erstaunlicher ist es, dass es bis 1999 gedauert hat, bis der Datentyp BOOLEAN in SQL Einzug gehalten hat. BOOLEAN

170

Wahrheitswerte TRUE und FALSE sowie UNKNOWN

DDL: Datendefinition mit SQL

Leider fehlt bei den Datenbanksystemen häufig der boolesche Datentyp. In diesem Fall empfehlen wir die Benutzung einer Domäne BOOLEAN (vgl. 4.3.1).

4.3.3 Operationen mit Datentypen Mit den Datentypen sind bestimmte erlaubte und sinnvolle Operationen und Funktionen verbunden. Wir stellen dies hier nur beispielhaft dar und verzichten auf eine vollständige Aufzählung.

Operationen mit Zahlen Für numerische Datentypen sind dies zunächst einmal die arithmetischen Operatoren: +

Addition

-

Subtraktion

*

Multiplikation

/

Division

Weitere mathematische Operationen wie Potenzierung können mit speziellen Funktionen ermöglicht werden oder sind selbst zu definieren (vgl. Kapitel 7.2). Bei arithmetischen Operationen gelten die üblichen Hierarchieregeln: 왘 Punktrechnung geht vor Strichrechnung. 왘 Bei gleicher Hierarchiestufe wird von links nach rechts gerechnet. 왘 Um davon abzuweichen, sind Klammern zu setzen.

Operationen mit Zeichenketten Eine ganze Ansammlung von Operatoren und Funktionen für Zeichenketten steht ebenfalls zur Verfügung. Dazu gehören Funktionen zur Ermittlung der Länge einer Zeichenkette, zur Extraktion bestimmter Teile, zum Entfernen von Leerzeichen am Anfang und Ende, zur Konvertierung in Großbuchstaben etc. Wir kommen auf diese Funktionen in Kapitel 5 zurück. Der Operator für die Zeichenverkettung ist:12 ||

12 Zwei senkrechte Striche, im ASCII-Code durch den Wert 124 wiedergegeben.

171

4 Datendefinition in SQL

Durch Verkettung von Zeichenketten können zwei oder mehr Spalten virtuell wie eine ausgegeben werden. Beispielsweise können wir Vornamen und Nachnamen (unterstellt, diese wären in verschiedenen Spalten enthalten) für ein Adressetikett so zusammenfügen: vorname ||' '|| nachname

Operationen mit Datums- und Zeitdaten Eine Anzahl von Operationen, die sich mit Datums- und Zeitwerten befassen, ist im Standard definiert. Dazu gehören Differenzen zwischen Datums- und Zeitwerten mit dem Ergebnis eines Intervalls (Tage, Stunden etc.). Auf der anderen Seite können durch Addition oder Subtraktion von Intervallwerten zu Werten vom Typ TIMESTAMP neue Werte vom Typ TIMESTAMP gewonnen werden.

Operationen mit booleschen Werten Für Wahrheitswerte gibt es die üblichen Operatoren AND, OR und NOT. Zum Vergleich von Ausdrücken mit Wahrheitswerten benutzt man nicht das Gleichheitszeichen, sondern den Operator IS. Das Ergebnis des Vergleichs ist wieder ein boolescher Wert, der aber nur die Werte TRUE oder FALSE annehmen kann. Wenn wir bei der Artikeltabelle das Attribut kann_wegfallen mit dem Datentyp BOOLEAN definieren, könnte eine Abfrage beispielsweise die Bedingung enthalten: WHERE kann_wegfallen IS TRUE

oder auch nur: WHERE kann_wegfallen

In beiden Fällen steht hinter WHERE ein Wahrheitswert. Im ersten Fall kann nur TRUE oder FALSE herauskommen, der zweite Ausdruck kann die Werte TRUE, FALSE oder UNKNOWN annehmen, falls für die Spalte kann_wegfallen Nullmarken erlaubt sind.

Vergleiche von Daten Die folgende Tabelle listet die Operatoren für Vergleiche auf. =

gleich

ungleich. In einigen DBMS wird stattdessen der Operator != verwendet.

>

größer als

>=

größer/gleich


10;

211

5 Datenmanipulation in SQL

artikel_nr

mwst

bestand

G001

2

397

G002

2

473

G003

2

1250

K001

2

120

K002

2

62

K003

2

12

K004

1

50

L001

1

356

L002

1

42

L003

1

345

L004

2

2101

L005

2

234

Liste die Artikel mit mwst ungleich 1 oder bestand > 10. Das NOT bezieht sich nur auf die Bedingung, vor der es steht: SELECT artikel_nr, mwst, bestand FROM artikel WHERE NOT mwst = 1 OR bestand > 10; artikel_nr

mwst

bestand

G001

2

397

G002

2

473

G003

2

1250

K001

2

120

K002

2

62

K003

2

12

K004

1

50

L001

1

356

L002

1

42

212

SELECT-Anweisung mit einer Tabelle

artikel_nr

mwst

bestand

L003

1

345

L004

2

2101

L005

2

234

Zeige die Artikel, für die nicht gilt: mwst = 1 oder bestand > 10. Das bedeutet, dass weder mwst = 1 noch bestand > 10 gilt. Das NOT bezieht sich bei der folgenden Anweisung auf beide Bedingungen, da diese durch die Klammer zusammengefasst werden. Die Ergebnismenge ist leer; es gibt keine Tupel, für welche die Gesamtbedingung erfüllt ist. SELECT artikel_nr, mwst, bestand FROM ARTIKEL WHERE NOT (mwst = 1 OR bestand > 10); ARTIKEL_NR MWST BESTAND % **Warning: No records selected

Aufgabe 5.10 Welche Artikel lagern auf Lagerplatz 7? Geben Sie die Artikelnummer, die Bezeichnung, den Lagerplatz und den Bestand aus. Aufgabe 5.11 Bei welchen dieser Artikel ist der Bestand größer oder gleich 400? Aufgabe 5.12 Welche Kunden sind noch nie mit einer Werbeaktion belästigt worden? Aufgabe 5.13 Bei welchen Positionen sind Artikel mit den Nummern 'G001', 'G002' oder 'G003' geliefert worden? Aufgabe 5.14 Bei welchen dieser Positionen ist die Liefermenge größer als 2? Aufgabe 5.15 Bei welchen Positionen sind keine Artikel mit den oben genannten Nummern geliefert worden? Aufgabe 5.16 Welche Positionen sind schon bearbeitet worden (ein Wert für Liefermenge ist eingetragen)? Aufgabe 5.17 Welche Kunden sind keine Stammkunden und nicht aus Husum?

213

5 Datenmanipulation in SQL

5.2.5 SELECT mit BETWEEN, IN, LIKE, SIMILAR Die im Folgenden dargestellten Operatoren erlauben Vergleiche, bei denen Wertebereiche anstelle von Einzelwerten den Maßstab bilden.

BETWEEN Um Wertebereiche mit definierter Unter- und Obergrenze als Selektionskriterium anzuwenden, steht der Operator BETWEEN zur Verfügung. Bedingungen mit BETWEEN werden so formuliert: ausdruck BETWEEN vergleichswert1 AND vergleichswert2

Die Wirkung ist dieselbe wie bei zwei Bedingungen, die mit AND verbunden sind, nämlich: ausdruck >= vergleichswert1 AND ausdruck = '2008-04-01' bestelldatum 0 THEN +1 wert = 0 THEN -1 wert < 0 THEN 0 NULL

Was liefert in der Versand-Datenbank folgende Abfrage? Testen Sie das Ergebnis, und kritisieren Sie die Abfrage! SELECT bestell_nr, artikel_nr, liefermenge, CASE WHEN liefermenge = 0 THEN 'gar nichts' WHEN liefermenge < 5 THEN 'wenig' ELSE 'viel' END AS wieviel FROM bestellposition

Syntax für Variante 2: CASE wert WHEN wert_1 THEN ergebnis_1 WHEN wert_2 THEN ergebnis_2 ... WHEN wert_n THEN ergebnis_n ELSE ergebnis_x END

222

SELECT-Anweisung mit einer Tabelle

Dieses ist eine Kurzform für: CASE WHEN WHEN ... WHEN ELSE END

wert=wert_1 THEN ergebnis_1 wert=wert_2 THEN ergebnis_2 wert=wert_n THEN ergebnis_n ergebnis_x

Die folgende Abfrage liefert für alle Kunden ihren Kundenstatus: SELECT name, CASE status WHEN 'S' THEN 'Stammkunde' WHEN 'G' THEN 'gelegentlich' WHEN 'W' THEN 'Werbung' END AS status FROM kunde Name

Status

Voss, Hans

Stammkunde

Stein, Peter

Stammkunde

Berger, Uwe

Werbung

Randers, Nis

Stammkunde

Andresen, Ute

gelegentlich

Stuff, Klaus

Stammkunde

Staack, Hannes

Werbung

Weitere Funktionen werden zur Verfügung gestellt, um mit Nullmarken umzugehen. Im Prinzip können wir das alles mit der CASE-Funktion; aber da Nullmarken häufig behandelt werden müssen, gibt es hier zwei weitere Funktionen, die gegensätzlich sind: COALESCE ersetzt eine Nullmarke durch einen Wert, während NULLIF einen Wert durch eine Nullmarke ersetzt.

COALESCE Bei diversen Auswertungen haben wir das Problem, dass für bestimmte Attribute eine Nullmarke existieren kann, aber in der Auswertung dort etwas stehen soll. Oder das entsprechende Attribut kommt in einem Ausdruck vor. Eine Nullmarke führt dann dazu, dass der ganze Ausdruck als Ergebnis eine Nullmarke hat.

223

5 Datenmanipulation in SQL

Nehmen wir an, dass in einer Personendatenbank eine Tabelle existiert, die folgende Struktur hat (und weitere – hier nicht interessierende – Attribute): CREATE TABLE Person ( pers_id INTEGER NOT NULL, name VARCHAR(20) NOT NULL, vorname VARCHAR(20) NOT NULL, titel VARCHAR(10), ... PRIMARY KEY (pers_id) );

Wir wollen – z. B. für einen Serienbrief – aus den Feldern Name, Vorname und Titel eine berechnete Spalte mit Titel, Vorname und Name machen. Die folgende Abfrage -- unvollständiges Beispiel SELECT titel || ' ' || vorname || ' ' || name FROM Person

liefert für Personen ohne Titel eine Nullmarke. Wir möchten aber für Personen ohne Titel nur den Namen und Vornamen haben. Dieses erreichen wir mit der Funktion COALESCE: SELECT COALESCE(titel || ' ','') || vorname || ' ' || name FROM Person

COALESCE kann mit beliebig vielen Argumenten aufgerufen werden. Wenn alle Argumente eine NULL sind, ist das Ergebnis NULL. Andernfalls ist das Ergebnis das erste Argument, das nicht NULL ist. In dem o. a. Beispiel ist für den Fall, dass titel eine Nullmarke hat, titel || ' ' auch NULL. Dann wird als Ergebnis eine leere Zeichenkette zurückgegeben, deren Verknüpfung mit vorname || ' ' || name den Vornamen und Namen ohne ein Leerzeichen davor ergibt. Wenn titel nicht NULL ist, wird der Titel, gefolgt von einem Leerzeichen, vor den Vornamen gesetzt. In unserer Versand-Datenbank wollen wir eine Liste aller Bestellungen haben mit dem Lieferdatum – falls noch nicht geliefert, soll der Text 'noch nicht geliefert' erscheinen. SELECT bestell_nr, bestelldatum, COALESCE(lieferdatum,'noch nicht geliefert') AS lieferdatum FROM bestellung bestell_nr

224

bestelldatum

lieferdatum

151

28.04.2008

02.05.2008

152

30.04.2008

02.05.2008

SELECT-Anweisung mit einer Tabelle

bestell_nr

bestelldatum

lieferdatum

153

12.05.2008

noch nicht geliefert

154

15.05.2008

noch nicht geliefert

Beim genauen Betrachten des Ergebnisses stellt sich eine Frage: Welchen Datentyp hat die Spalte lieferdatum ? Datum oder Zeichenfolge? Hier ist eine implizite Typanpassung vorgenommen worden, die auch an anderer Stelle auftreten kann: wenn in einem Attribut unterschiedliche Datentypen auftreten können – hier DATE und CHAR(20) – dann wird der kleinste gemeinsame Datentyp genommen. Die Regeln für die Bestimmung dieses kleinsten gemeinsamen Datentyps sind etwas umfangreicher – für uns reichen erst einmal folgende zwei Regeln: 왘 Wenn alle verknüpften Datentypen numerisch sind, ist das Resultat auch

numerisch, wobei die Anzahl der Nachkommastellen das Maximum der beteiligten Nachkommastellen ist. 왘 Wenn einer der verknüpften Datentypen ein Zeichenfolgen-Datentyp ist, dann

ist das Resultat auch ein Zeichenfolgen-Datentyp. Die Länge ist so gewählt, dass alle beteiligten Argumente sicher dargestellt werden können (z. B. zehn Zeichen für einen Datumswert 'tt-mm-jjjj') . 왘 Diese Regeln gelten auch z. B. für die CASE-Funktion oder für den weiter hin-

ten stehenden UNION-Operator. Wir können generell COALESCE (arg_1, arg_2, ..., arg_n)

darstellen durch CASE WHEN WHEN ... WHEN ELSE END

arg_1 IS NOT NULL THEN arg_1 arg_2 IS NOT NULL THEN arg_2 arg_n IS NOT NULL THEN arg_n NULL

NULLIF NULLIF ist gewissermaßen eine Umkehrung von COALESCE. Ein vorgegebener Wert in der Datenbank wird in einer Auswertung als Nullmarke interpretiert, während die anderen Werte ungeändert bleiben.

225

5 Datenmanipulation in SQL

Syntax: NULLIF(attribut, wert)

Dieses ist äquivalent zu: CASE WHEN attribut = wert THEN NULL ELSE attribut END

Dieses kann z. B. verwendet werden, wenn in einer Datenbank zu Lehrveranstaltungen für das Feld Dozent entweder der Name des Dozenten steht oder 'NN' für »nicht nominiert«. Wenn wir für einen nicht angegebenen Dozenten eine Nullmarke haben wollen, erreichen wir das durch NULLIF (dozent,'NN')

Numerische Funktionen Numerische Funktionen liefern eine Zahl als Ergebnis zurück. In SQL sind nur zwei numerische Funktionen mit numerischen Argumenten definiert: ABS und MOD.

ABS ABS liefert den Absolutbetrag einer Zahl. SELECT ABS(x-y) AS abstand

MOD MOD liefert den Rest bei einer ganzzahligen Division. SELECT MOD(21,5)

Antwort: 1

Numerische Funktionen mit String-Argumenten Einige andere Funktionen hätten wir lieber als String-Funktionen bezeichnet, weil sie Strings verarbeiten. Dennoch fallen Sie nach SQL:2003 unter »numerisch«. Beispielsweise lässt sich mit CHARACTER_LENGTH oder dem Synonym CHAR_LENGTH ermitteln, wie lang eine Zeichenkette ist. name

CHAR_LENGTH(kunde.name)

Voss, Hans

10

Stein, Peter

12

Berger, Uwe

11

226

SELECT-Anweisung mit einer Tabelle

name

CHAR_LENGTH(kunde.name)

Randers, Nis

12

Andresen, Ute

13

Stuff, Klaus

12

Staack, Hannes

14

Die Funktion POSITION gibt an, an welcher Stelle einer Zeichenkette eine gesuchte Zeichenfolge vorkommt. Damit finden wir beispielsweise die Position des Kommas im Kundennamen. SELECT name, POSITION(',' IN name) AS pos FROM kunde;

Antwort: name

pos

Voss, Hans

5

Stein, Peter

6

Berger, Uwe

7

Randers, Nis

8

Andresen, Ute

9

Stuff, Klaus

6

Staack, Hannes

7

String-Funktionen String-Funktionen haben als Rückgabewert Zeichenketten. Wir können beispielsweise mit UPPER alle Buchstaben in Großbuchstaben umwandeln. Das Gegenstück dazu heißt LOWER. upper(name) VOSS, HANS STEIN, PETER BERGER, UWE RANDERS, NIS

227

5 Datenmanipulation in SQL

upper(name) ANDRESEN, UTE STUFF, KLAUS STAACK, HANNES

Zum Extrahieren von Teil-Strings gibt es die Funktion SUBSTRING. Die Parameter geben an, ab dem wievielten Zeichen und wie viele Zeichen zu extrahieren sind. Im Beispiel kombinieren wir die SUBSTRING-Funktion mit der POSITION-Funktion, um anhand der Position des Kommas den Nachnamen des Kunden zu extrahieren. SELECT name, SUBSTRING(name FROM 1 FOR POSITION(',' IN name)-1) AS Nachname FROM kunde;

Antwort: name

Nachname

Voss, Hans

Voss

Stein, Peter

Stein

Berger, Uwe

Berger

Randers, Nis

Randers

Andresen, Ute

Andresen

Stuff, Klaus

Stuff

Staack, Hannes

Staack

Behandlung von Datumswerten Für praktische Anwendungen ist es häufig wichtig, Datumswerte in Strings umzuwandeln und umgekehrt. Wir können hierfür den Operator CAST verwenden, den wir oben eingeführt haben. Dieser stellt das Datum entsprechend der ISO-Norm in der Form 'yyyy-mm-dd' dar wie in folgendem Beispiel. Die Umwandlung in eine andere Darstellung (z. B. der üblichen deutschen mit 'dd.mm.yyyy') ist in der SQLNorm nicht vorgesehen. Das ist dann Aufgabe des Anwendungsprogramms. SELECT kunden_nr, CAST (letzte_werbeaktion AS VARCHAR) FROM kunde;

228

SELECT-Anweisung mit einer Tabelle

kunden_nr

letzte_werbeaktion

100

2007-12-01

101

2007-12-01

102

2007-12-01

103 104 105 106

2007-12-01

Wir können aber die einzelnen Komponenten von Datums- oder Uhrzeitangaben durch die Funktion EXTRACT erhalten, die in folgender Weise aufgerufen wird: EXTRACT (feld FROM wert)

Hierbei ist feld einer der Werte YEAR, MONTH, DAY, HOUR, MINUTE, SECOND und gibt an, welches Feld des Datums auszugeben ist, der wert muss vom Typ DATE, TIME, TIMESTAMP oder INTERVAL sein. SELECT bestell_nr, bestelldatum, extract(DAY FROM bestelldatum) AS Tag, extract(MONTH FROM bestelldatum) AS Monat, extract(YEAR FROM bestelldatum) AS Jahr from bestellung bestell_nr bestelldatum Tag Monat Jahr 151 28.04.2008 28 4 2008 152 30.04.2008 30 4 2008 153 12.05.2008 12 5 2008 154 15.05.2008 15 5 2008

Eine Funktion für den Wochentag ist im Standard auch leider nicht vorgesehen. Wir können uns durch folgenden Aufruf behelfen: MOD (datum - CAST('1900-01-01' AS DATE) ,7) + 1 SELECT bestell_nr, bestelldatum, extract(DAY FROM bestelldatum) AS Tag,

20

20 Erläuterung hierzu: Der 1. Januar 1900 war ein Montag. Die Differenz zu dem gesuchten Datum ist die Anzahl der Tage seit diesem Montag, der Divisionsrest durch 7 somit die Anzahl der Tage seit dem letzten Montag, also 0 für Montag, 1 für Dienstag, …, 6 für Sonntag. Da aber nach der ISO-Norm der Montag den Wert 1 hat, Dienstag den Wert 2 etc. muss noch 1 dazu addiert werden. Es empfiehlt sich, bei häufigerer Benutzung hierfür eine Funktion zu definieren (vgl. Kapitel 7.3.2)

229

5 Datenmanipulation in SQL

extract(MONTH FROM bestelldatum) AS Monat, extract(YEAR FROM bestelldatum) AS Jahr, MOD (datum - CAST('1900-01-01' AS DATE) ,7) + 1 AS Wochentag FROM bestellung bestell_nr bestelldatum Tag Monat Jahr Wochentag 151 28.04.2008 28 4 2008 1 152 30.04.2008 30 4 2008 3 153 12.05.2008 12 5 2008 1 154 15.05.2008 15 5 2008 4

In der Norm ist auch keine Möglichkeit vorgesehen, einen Datumswert aus Tag, Monat und Jahr zu erhalten. Hier können wir aus den Komponenten einen String machen, der anschließend mit der CAST-Funktion in einen Datumswert umgewandelt wird. CAST (jahr || '-' || monat || '-' || tag AS DATE)

Übungsaufgaben Da die meisten der in diesem Abschnitt vorgestellten Funktionen vom Hersteller des DBMS abhängen und nicht normiert sind, empfehlen wir, die jeweilige Dokumentation zu durchsuchen und an selbst gewählten Beispielen einige Funktionen auszuprobieren. Anregungen: 왘 Testen Sie numerische Funktionen an artikel.lagerbestand, bestellposition.

bestellmenge, bestellposition.liefermenge. 왘 Testen Sie Funktionen für Zeichenketten an kunde.name, kunde.strasse,

kunde.ort, artikel.bezeichnung, artikel.verpackung. 왘 Splitten Sie die Kundennamen auf in die zwei virtuellen Attribute vorname,

nachname. 왘 Testen Sie Funktionen zur Umwandlung von Datumswerten in Zeichenketten

und umgekehrt an artikel.nachbestellung, kunde.letzte_bestellung, bestellung.bestelldatum, bestellung.lieferdatum. Sie können beispielsweise artikel.nachbestellung mit einem UPDATE-Befehl auf ein ausgewähltes Datum setzen. Wandeln Sie die in der Datenbank enthaltenen Datumswerte für die Ausgabe in verschiedene Formate um, z. B. '12. May 2008', '2008/05/12', '05-12-2008'.

5.2.8 SELECT mit Gruppenbildung Während die in den beiden vorigen Abschnitten behandelten virtuellen Spalten so zustande kommen, dass für jede Zeile einzeln aus vorhandenen Attributwerten neue berechnet werden, fasst die Gruppenbildung Werte einer Spalte für verschiedene Zeilen der Tabelle zusammen.21 21 Vgl. Abschnitt 2.4.5.

230

SELECT-Anweisung mit einer Tabelle

COUNT Wie viele Kunden gibt es? Wir zählen die Zeilen der Kundentabelle. SELECT COUNT(*) FROM kunde

Die Funktion COUNT(*), die hier angewandt wird, ist eine Aggregatfunktion. Sie zählt in diesem Fall die Anzahl aufgefundener Zeilen. Wenn wir statt * den Bezeichner einer Spalte bei COUNT angeben, wird die Anzahl der vorhandenen Werte ohne Nullmarken gezählt. Daher erhalten wir bei der Tabelle bestellung verschiedene Zahlen, wenn wir die Werte in bestelldatum und lieferdatum zählen lassen.22 SELECT COUNT(bestelldatum), COUNT(lieferdatum) FROM bestellung; COUNT(bestelldatum)

COUNT(lieferdatum)

4

2

Auffällig sind erstens die verschiedenen Werte, zweitens die Tatsache, dass die Ergebnistabelle nur eine einzige Zeile aufweist. Die Funktion COUNT berechnet also aus allen Zeilen einer Tabelle genau einen Wert. Schließlich kann mit der COUNT-Funktion auch die Anzahl verschiedener Werte in einer Spalte ermittelt werden. Beispielsweise lassen wir mit folgender Anweisung die Anzahl verschiedener Postleitzahlen bei unseren Kunden anzeigen: SELECT COUNT(DISTINCT plz) FROM kunde

Zusammengefasst stellen wir fest, dass die Aggregatfunktion COUNT drei verschiedene Formen hat: COUNT(*) COUNT(spalte) COUNT(DISTINCT spalte)

22 SYBASE weist auf die Eliminierung von Nullmarken mit einer Fehlermeldung hin. Wir halten dies nicht für angebracht, da es sich hier nicht um einen Fehler handelt. Die Ergebnisse werden aber korrekt ermittelt und angezeigt.

231

5 Datenmanipulation in SQL

Weitere Aggregatfunktionen In SQL sind ursprünglich fünf Aggregatfunktionen definiert, die dort als set functions bezeichnet werden: COUNT

Anzahl von Tupeln, Werten oder verschiedenen Werten

SUM

Summe der Werte

AVG

Durchschnitt der Werte

MAX

Größter Wert

MIN

Kleinster Wert

Im Übrigen kann – wie bei COUNT – jeweils das Schlüsselwort DISTINCT vor der Spaltenbezeichnung stehen; allerdings ist uns bei SUM und AVG keine sinnvolle Anwendung bekannt, die mögliche (zufällige?) Dubletten nur einmal in die Berechnung aufnimmt; bei MAX und MIN ändert sich eh nichts am Ergebnis. Alle Funktionen lassen sich in einer einzigen SELECT-Anweisung unterbringen, wie das folgende Beispiel zeigt. Wir fragen nach der Anzahl aller Artikel, der Summe und dem Durchschnitt ihrer Einzelpreise sowie nach dem größten und kleinsten Einzelpreis. SELECT COUNT(artikel_nr)AS SUM(listenpreis) AS AVG(listenpreis) AS MIN(listenpreis) AS MAX(listenpreis) AS FROM artikel;

ANZAHL, PREISSUMME, DURCHSCHN, KLEINSTER, GROESSTER

ANZAHL

PREISSUMME

DURCHSCHN

KLEINSTER

GROESSTER

12

428,33

35,6941667

0,98

112,8

Ab SQL:2003 sind weitere statistische Funktionen wie Standardabweichung, Varianz, Perzentil (z. B. Median), Rang in Abhängigkeit von einer vorgegebenen Sortierung definiert.

Einschränkungen der Projektion bei Anwendung von Aggregatfunktionen Die Aggregatfunktionen in den bisher vorgestellten Beispielen fassen eine ganze Tabelle in einer Reihe zusammen. Aus diesem Grund ist es nicht möglich, zusätzlich noch einzelne Attributwerte auszugeben, denn dann müsste die Ergebnistabelle

232

SELECT-Anweisung mit einer Tabelle

mindestens so viele Zeilen enthalten, wie es verschiedene Werte in der gewünschten Spalte gibt. Das heißt: Wenn in der SELECT-Klausel mindestens eine Aggregatfunktion auftritt, dann dürfen keine elementaren Spalten mehr auftreten.23 Aufgabe 5.26 Warum liefert im obigen Fall die Funktion COUNT(artikel_nr) mit Sicherheit dasselbe Ergebnis wie COUNT(*)? Was würde bei Anwendung von COUNT(DISTINCT artikel_nr) herauskommen?

Nullmarken bei Aggregatfunktionen Nullmarken erfahren bei Anwendung der Aggregatfunktionen keine Berücksichtigung. Dieses führt bei der Summenbildung zu demselben Resultat, als wenn NULL die Zahl 0 repräsentierte. Bei der Minimum- und Maximumbildung und bei der Durchschnittsbildung fallen Nullmarken ganz heraus. Bei leeren Ergebnismengen sind MIN, AVG, MAX nicht definiert. Die COUNT-Funktion, auf eine Spalte angewandt, zählt die tatsächlich vorhandenen Werte. Bei Spalten, für die Nullmarken zulässig sind, kann deshalb das Ergebnis COUNT(spalte) kleiner sein als COUNT(*). Dieses Verfahren scheint deswegen berechtigt, weil wir einen Wert, der gar nicht da ist, auch nicht in einen Durchschnitt einbeziehen können. Aber wie sieht es aus, wenn der Wert zwar existiert, aber zurzeit nicht bekannt ist, also durch eine AMarke repräsentiert ist? Dann wird der über AVG(spalte) berechnete Durchschnitt falsch und der über die Formel SUM(spalte)/COUNT(spalte) berechnete ebenfalls. Richtig wäre dann die Antwort NULL, was hieße: »Ich kann den Durchschnitt nicht ausrechnen, weil ein Wert in ihn eingeht, den ich nicht kenne.« Dazu müsste das DBMS aber zwei Sorten von Nullmarken (I- und A-Marken, vgl. Kapitel 2.5) unterscheiden können, was in der Regel nicht der Fall ist. Wenn wir die Berechnung des Durchschnitts nicht über die Funktion AVG, sondern über die Summenbildung und Zählung der Werte durchführen wollen, müssen wir beachten, dass wir SUM(spalte)/COUNT(spalte)

formulieren. SUM(spalte)/COUNT(*)

liefert bei Vorhandensein von Nullmarken in spalte ein falsches Resultat. Also Achtung bei Aggregatfunktionen auf Spalten mit Nullmarken! Das Ergebnis muss sorgfältig interpretiert werden!

23 Außer Spalten aus der GROUP BY-Klausel, wenn diese verwendet wird. Vgl. 5.2.9. Eine weitere Möglichkeit ist die in SQL:2003 eingeführte PARTITION-Klausel (vgl. Kapitel 5.2.11).

233

5 Datenmanipulation in SQL

5.2.9 SELECT mit GROUP BY Die oben diskutierten Aggregatfunktionen erlauben weitergehende Auswertungen, indem bestimmte Teilmengen der Tupel einer Relation zu Gruppen zusammengefasst werden. Das Zählen, die Durchschnitts- und Summenbildung, die Ermittlung von Minimal- und Maximalwerten kann beispielsweise so bezogen werden: 왘 jeweils auf alle Bestellungen eines Kunden, 왘 jeweils auf alle Positionen einer Bestellung, 왘 auf alle Positionen, in denen ein Artikel bestellt oder berechnet wird, 왘 auf alle Kunden einer Stadt.

Entscheidend für die Gruppenbildung sind gleiche Werte in einer bestimmten Spalte. Für Bestellungen desselben Kunden ist der Wert der Spalte kunden_nr in bestellung immer gleich. Bei Positionen, die denselben Artikel betreffen, ist der Wert der Spalte artikel_nr in der Tabelle bestellposition immer derselbe. Eine Abfrage-Anweisung mit GROUP BY hat folgende Struktur: SELECT gruppenausdrucksliste FROM tabelle GROUP BY spaltenliste gruppenausdruck ::= MIN (ausdruck1) | MAX (ausdruck1) | AVG (ausdruck1) | SUM (ausdruck1) | COUNT (ausdruck1)| COUNT (DISTINCT ausdruck1)| COUNT (*) | gruppenspalte | ausdruck2

Dabei ist gruppenspalte eine Spalte, die in der GROUP BY-Klausel auftritt. ausdruck1 ist ein beliebiger Ausdruck24, ausdruck2 ist ein Ausdruck, der nur Aggregatfunktionen oder Gruppenspalten verwenden darf. Die hinter GROUP BY angegebene Spaltenliste wird als Gruppierungskriterium benutzt, d.h., alle Zeilen der Tabelle, die in diesen Spalten denselben Wert haben, werden zu einer Gruppe zusammengefasst. Im einfachsten Fall besteht die Gruppierspaltenliste aus einer einzigen Spalte.

24 Zu beachten ist allerdings, dass SUM und AVG nur auf numerische Ausdrücke angewandt werden dürfen.

234

SELECT-Anweisung mit einer Tabelle

Beispiele Wie oft hat jeder Kunde bestellt? SELECT kunden_nr, count (*) FROM bestellung GROUP BY kunden_nr; kunden_nr

count(*)

101

1

103

2

105

1

Für jeden Artikel ist die Anzahl aller Bestellungen zu ermitteln. SELECT artikel_nr, SUM (bestellmenge) FROM bestellposition GROUP BY artikel_nr; artikel_nr

SUM(bestellmenge)

K003

3

L002

16

K002

3

G001

6

L004

5

K004

12

G003

4

G002

16

K001

10

L003

25

Wann war die jeweils letzte Bestellung der Kunden? SELECT kunden_nr, MAX (bestelldatum) FROM bestellung GROUP BY kunden_nr;

235

5 Datenmanipulation in SQL

kunden_nr

MAX(bestelldatum)

103

2008-05-15

101

2008-04-28

105

2008-05-12

Auch auf virtuelle Spalten können Gruppierungsfunktionen angewandt werden. Das folgende Beispiel berechnet für jeden Lagerplatz die durchschnittliche Differenz zwischen Lagerbestand und Mindestbestand aller Artikel. SELECT lagerplatz, AVG(bestand – mindestbestand) AS abweichung FROM artikel GROUP BY lagerplatz; lagerplatz

abweichung

2

206.50000000000

4

345.00000000000

5

124.00000000000

7

573.33333333333

Die Ergebnistabellen sind echte Relationen, die nur verschiedene Zeilen enthalten. Es ist klar, dass die Werte des Gruppierungsattributes jeweils eindeutig in der Ergebnistabelle sind. Somit können insbesondere keine Zeilen mehrfach auftreten. Es ist sinnvoll, alle Gruppierungsattribute in der SELECT-Klausel anzugeben – sonst könnte wir die übrigen Informationen ja gar nicht zuordnen. Die Gruppierung fasst in der Regel mehrere Tupel der Ausgangsrelation zu einem einzigen Tupel der Ergebnisrelation zusammen. Daraus ergibt sich, dass die Attribute, die innerhalb einer Gruppe verschiedene Werte aufweisen, nicht mehr einzeln angezeigt werden können. Es kann pro Gruppe und pro Attribut nur noch einen Wert geben, der eben durch Anwendung der Aggregatfunktionen erzeugt wird. Wenn also, um auf das letzte Beispiel zurückzukommen, die letzte Bestellung eines Kunden gefragt ist, können nicht alle einzelnen Bestelldaten angezeigt werden. Ein solcher Versuch wird vielmehr mit einer Fehlermeldung quittiert, wie im folgenden Beispiel: -- Illegale Anweisung SELECT kunden_nr, bestelldatum FROM bestellung GROUP BY kunden_nr

236

SELECT-Anweisung mit einer Tabelle

Die Reaktion des Datenbanksystems ist eine Meldung der Art: »Column ‘bestelldatum’ cannot be used unless it is in a GROUP BY«. Neben der Datengruppierung, wie sie uns hier in der SELECT-Anweisung begegnet, gibt es die Möglichkeit, Daten in Berichten zu gruppieren. Hierzu steht eine Reihe von Werkzeugen zur Verfügung. In einem Punkt unterscheiden sich jedoch diese beiden Konzepte: Nach der Gruppierung in der SELECT-Anweisung, wie wir sie oben eingeführt haben, können wir keine individuellen Sätze der Gruppen sehen, sondern nur die Gruppierungsattribute sowie aggregierte Werte. In Berichten ist es jedoch möglich, die Daten nach den Gruppierungsattributen zu sortieren und am Ende jeder Gruppe gewisse Aggregatfunktionen anzuwenden. Die folgende Ausgabe erlaubt sowohl die Feststellung, wann Kunden im Einzelnen bestellt haben, wie oft sie bestellt haben und wann die letzte Bestellung war. Diese Daten müssen aber durch Interpretation aus der Liste gewonnen werden, sie werden (bis auf die Einzelwerte) nicht direkt angezeigt. SELECT Kunden_nr, bestelldatum FROM bestellung ORDER BY kunden_nr, bestelldatum; Kunden_nr

bestelldatum

101

2008-04-28

103

2008-04-30

103

2008-05-15

105

2008-05-12

In einem Bericht könnte daraus folgende Darstellung gewonnen werden: Bestellungen von Kunde 101 28.04.2008 Anzahl: 1; letzte Bestellung: 28.04.2008 Bestellungen von Kunde 103 30.04.2008 15.05.2008 Anzahl: 2; letzte Bestellung: 15.05.2008 Bestellungen von Kunde 105 12.05.2008 Anzahl: 1; letzte Bestellung: 12.05.2008

237

5 Datenmanipulation in SQL

Regel für die Projektion bei Gruppenbildung Im Zusammenhang mit einer GROUP BY-Klausel dürfen wir auf folgende Attribute projizieren: 왘 die Gruppierungsattribute selbst 왘 Aggregatfunktionen, angewendet auf Attribute der Tabelle 왘 Aggregatfunktionen, angewendet auf Spaltenausdrücke (virtuelle Spalten) 왘 Spaltenausdrücke, die auf den Gruppierungsspalten beruhen 왘 Konstanten

Nullmarken in den Gruppierungsspalten Die Gruppierung auf eine Spalte mit Nullmarken macht aus allen Nullmarken eine Gruppe. Allgemeiner gesagt bildet bei einer Gruppierung mit mehreren Spalten jede Kombination – auch unter Einbeziehung der Nullmarken – eine Gruppe. Als Beispiel wird die Frage gestellt, wie viele Kunden bei jeweils einer Werbeaktion betreut wurden. Auch die Gruppen, die noch nie bei einer Werbeaktion angesprochen wurden, bilden dann eine Gruppe. SELECT letzte_werbeaktion, COUNT(*) FROM kunde GROUP BY letzte_werbeaktion; letzte_werbeaktion

COUNT(*)

(NULL)

3

2007-12-01

4

Aufgabe 5.27 Lassen Sie pro MwSt.-Satz die Anzahl der davon betroffenen Artikel anzeigen. Aufgabe 5.28 Geben Sie jeweils die größte Kundennummer der Kunden mit demselben Status aus. Aufgabe 5.29 Zeigen Sie je Lagerplatz den kleinsten, den größten und den durchschnittlichen Listenpreis der dort gelagerten Artikel an.

238

SELECT-Anweisung mit einer Tabelle

5.2.10 SELECT mit GROUP BY und HAVING Die Gruppierung erzeugt eine Relation, in der Attribute auftreten, die für jede Gruppe einen Wert haben. Bis auf die Gruppierungsspalten selbst, deren Werte sich in der Originaltabelle befinden, werden die Werte erst zur Laufzeit der Abfrage berechnet. Mit der zusätzlichen HAVING-Klausel kann nun eine Selektion nach der Gruppenbildung vorgenommen werden. Das bedeutet, es erfolgt eine Selektion unter den Gruppen. Für jeden Artikel hatten wir die Anzahl aller Bestellungen so ermittelt: SELECT artikel_nr, SUM (bestellmenge) FROM bestellposition GROUP BY artikel_nr; artikel_nr

SUM(bestellmenge)

K003

3

L002

16

K002

3

G001

6

L004

5

K004

12

G003

4

G002

16

K001

10

L003

25

Die Auswertung zeigt, dass es Artikel gibt, die mehr als zehnmal bestellt wurden, und solche, die weniger oft geordert wurden. Wollen wir nur Artikel mit Bestellmengen von über zehn berücksichtigen, so muss die obige SQL-Anweisung um eine HAVING-Klausel ergänzt werden, nämlich: ... HAVING SUM(bestellmenge) > 10

Das Ergebnis: SELECT artikel_nr, SUM (bestellmenge) FROM bestellposition GROUP BY artikel_nr HAVING SUM(bestellmenge) > 10;

239

5 Datenmanipulation in SQL

artikel_nr

SUM(bestellmenge)

G002

16

K004

12

L002

16

L003

25

Die Grundform der HAVING-Klausel in Verbindung mit einer GROUP BY-Klausel lautet: GROUP BY spaltenliste HAVING bedingung

Da die HAVING-Klausel die Ergebnisse der Gruppierung selektiert, enthält sie meist selbst eine Aggregatfunktion. Beispiele: ... HAVING COUNT(*) > 1 ... HAVING SUM(LIEFERMENGE) < SUM(BESTELLMENGE)

Die WHERE-Klausel und die HAVING-Klausel können auch in Kombination eingesetzt werden. Dabei gilt die syntaktische Reihenfolge: WHERE GROUP BY HAVING

Dies entspricht der logischen Abfolge: Zunächst wird mit der WHERE-Klausel eine Selektion auf der Basistabelle ausgeführt. Die verbleibenden Tupel werden gruppiert. Zum Schluss werden die Gruppen selektiert. Die Kombination von WHERE und HAVING zeigt das folgende Beispiel, in dem die Anzahl der Bestellpositionen ermittelt wird, in denen ein Artikel mehr als einmal auftritt. Vor der Gruppierung werden aber die Tupel selektiert, bei denen die Artikelnummer mit 'K' anfängt. SELECT artikel_nr, COUNT(*) FROM bestellposition WHERE artikel_nr LIKE 'K%' GROUP BY artikel_nr HAVING COUNT(*) > 1;

240

SELECT-Anweisung mit einer Tabelle

artikel_nr K003

COUNT(*) 2

Aufgabe 5.30 Wie viele Ihrer Kunden haben jeweils die gleiche Postleitzahl? Aufgabe 5.31 Unter welcher Postleitzahl haben Sie mehr als einen Kunden? Aufgabe 5.32 Auf welchen Lagerplätzen ist der Gesamtbestand aller Artikel kleiner als 1000?

5.2.11 Erweiterte Gruppierungsmöglichkeiten in SQL/OLAP Dieser Abschnitt kann von Lesern, die sich erstmals mit SQL beschäftigen, ohne Schaden übersprungen werden. Der Standard ab Version 2003 enthält eine Erweiterung unter dem Namen SQL/ OLAP. Hierbei geht es um die Bereitstellung von Auswertungsmöglichkeiten, wie man sie in anspruchsvollen Reports oder in Data Warehouse-Anwendungen benötigt. OLAP steht dabei für »Online Analytical Processing« und bezeichnet eine Herangehensweise, die sich nicht für einzelne Werte und einzelne Vorgänge, sondern für nach allen möglichen Kriterien aggregierte, d. h. zusammengefasste Daten interessiert. In einer operativen, d. h. dem Alltagsgeschäft zugewandten, Sicht interessiert man sich für eine einzelne Bestellung, die abzuwickeln, d.h., zu liefern und später in Rechnung zu stellen ist. Eine OLAP-Auswertung hingegen betrachtet beispielsweise Umsätze, die nach Quartalen, nach Kundengruppen (z. B. alle Kunden mit demselben Status), nach Orten oder Warengruppen oder nach mehreren dieser Kriterien zugleich aufsummiert worden sind. Dahinter stecken, wenn man solche Abfragen in SQL realisieren will, häufig mehrere sehr komplizierte SELECT-Anweisungen mit GROUP BY-Klauseln. Die Realisierung solcher Auswertungen ist daher bisher die Domäne spezieller Anwendungsprogramme bzw. Berichtgeneratoren gewesen, sogenannter OLAP-Werkzeuge. Nunmehr sollen sie in die Funktionalität der Datenbankmaschine integriert sein. Die Beschäftigung mit der Thematik Data Warehouse und OLAP würde den Rahmen dieses Buchs sprengen. Wir gehen aber auf einige ausgewählte Punkte kurz ein, da wir in Abschnitt 5.2.8 (**??**) auf Restriktionen im Zusammenhang mit Gruppierungsoperationen hingewiesen haben, die mit den neuen Möglichkeiten teilweise gelockert werden.25 25 Eine ausführlichere, aber insgesamt recht knappe Darstellung dieses Teils im SQL-Standard mit Beispielen zum Nachvollziehen findet man in [Petk07].

241

5 Datenmanipulation in SQL

Gruppierung von Tupeln mit Partitionen Die Bildung von Partitionen ähnelt dem Gruppieren von Daten. Wie eine Gruppe ist eine Partition definiert durch die Menge der Tupel, die in extra angegebenen Gruppierungsattributen denselben Wert haben. Beispielsweise können wir so auf Basis der Artikeltabelle Gruppen bilden, indem wir die Artikel zusammenfassen, bei denen das Attribut Lagerplatz denselben Wert hat. Auf diese Weise konnten wir bereits mithilfe der Gruppenfunktionen aggregierte Daten erzeugen, beispielsweise die Summe der Bestände je Lager. SELECT lagerplatz, SUM(bestand) AS gesamtlager FROM artikel GROUP BY lagerplatz; lagerplatz

gesamtlager

7

2120

5

398

4

345

2

2579

Hier können wir keine Einzelwerte über die Artikel, die sich auf diesen Lagerplätzen befinden, anzeigen: Weder die Artikel_nr noch die Bezeichnung, noch den jeweiligen Einzelbestand, wir erhalten sonst eine Fehlermeldung. Mit dem Einsatz der PARTITION-Klausel wird solches aber möglich: SELECT lagerplatz, artikel_nr, bezeichnung, bestand, SUM(bestand) OVER (PARTITION BY lagerplatz) AS GesamtLager FROM artikel lagerplatz

artikel_nr

bezeichnung

bestand

GesamtLager

7

G001

Whisky

397

2120

7

G002

Portwein

473

2120

7

G003

Bier

1250

2120

5

L001

Ceylon-Tee

356

398

5

L002

China-Tee

42

398

4

L003

Naturreis

345

345

2

K001

Schuhe

120

2579

242

SELECT-Anweisung mit einer Tabelle

lagerplatz

artikel_nr

bezeichnung

bestand

GesamtLager

2

K002

Hose

62

2579

2

K003

Damenhut

12

2579

2

K004

Sonnenbrille

50

2579

2

L004

Schokolade

2101

2579

2

L005

Butterkekse

234

2579

Es wäre auch ohne Benutzung der PARTITION-Klausel möglich, aber sehr viel umständlicher, diese Auswertung zu erzeugen. Die Artikeltabelle wird hier sozusagen zweimal durchsucht: einmal, um die Summe der Bestände je Lagerplatz zu ermitteln, und dann noch einmal, um die Einzelwerte je Artikel anzuzeigen. Beide Zwischenergebnisse wären dann über das Attribut Lagerplatz per Natural Join zu verbinden. Die folgende Anweisung erzeugt dieselbe Liste wie das Beispiel mit PARTITION: SELECT a1.lagerplatz, a1.artikel_nr, a1.bezeichnung, a1.bestand, SUM(a2.bestand) AS GesamtLager FROM artikel a1 JOIN artikel a2 ON a1.lagerplatz = a2.lagerplatz GROUP BY a1.lagerplatz, a1.artikel_nr, a1.bezeichnung, a1.bestand ORDER BY a1.lagerplatz DESC;

Man kann die PARTITION-Klausel, die in obigem Beispiel »inline«, als Bestandteil der Spaltenangabe auftritt, explizit als WINDOW-Definition einsetzen. SELECT lagerplatz, artikel_nr, bezeichnung, bestand, SUM(bestand) OVER w AS gesamtlager FROM artikel WINDOW w AS (PARTITION BY lagerplatz);

Von Vorteil ist dies dann, wenn man mehr als eine Aggregatfunktion auf die Partitionen anwenden will, da man die PARTITION-Klausel dann nur einmal angeben muss. In einer Abfrage können auch mehrere solcher Fenster geöffnet werden. Wir ermitteln im folgenden Beispiel die Gesamtbestände nach lagerplatz und nach dem Wert von mwst. SELECT lagerplatz AS lager, artikel_nr, mwst, bestand, SUM(bestand) OVER w1 AS gesamtprolager, SUM(bestand) OVER w2 AS gesamtpromwst FROM artikel WINDOW w1 AS (PARTITION BY lagerplatz), w2 AS (PARTITION BY mwst);

243

5 Datenmanipulation in SQL

lager

artikel_nr

mwst

bestand

gesamtprolager

gesamtpromwst

7

G001

2

397

2120

4649

7

G002

2

473

2120

4649

7

G003

2

1250

2120

4649

2

K001

2

120

2579

4649

2

K002

2

62

2579

4649

2

K003

2

12

2579

4649

2

L004

2

2101

2579

4649

2

L005

2

234

2579

4649

5

L001

1

356

398

793

5

L002

1

42

398

793

4

L003

1

345

345

793

2

K004

1

50

2579

793

Das Ergebnis enthält nun mehrere Aggregationen nach mehreren Kriterien.26 Es gibt eine Reihe von Möglichkeiten, Daten innerhalb von Partitionen zu sortieren und einzuschränken. Letzteres wird mit der im Standard so bezeichneten Window frame clause erreicht. Wir gehen hier nicht weiter darauf ein und verweisen auf die Handbücher der Hersteller.

Erweiterungen der GROUP BY-Klausel Nach SQL:2003 ist es erstmalig auf verschiedene Weise möglich, in einer Abfrage nach mehreren Kriterien Daten zu gruppieren. Außerdem sind einige Gruppenfunktionen für statistische Auswertungen neu hinzugekommen. Wir erläutern Ersteres an einigen Beispielen, auf die neuen Gruppenfunktionen gehen wir hier nicht ein und verweisen auf die Literatur27 bzw. auf die Handbücher der Hersteller. Um die Summe der Artikelbestände je Lagerplatz und die Summe der Artikelbestände überhaupt (»grand total«) zu erfahren, müssen zwei Aggregationen angefordert werden: die eine nach dem Gruppierungskriterium lagerplatz und die andere ohne Gruppierungskriterium, da für eine Gesamtsumme über alles die 26 Auch diese Abfrage kann man ohne Partitionen erzeugen, es gehört aber schon eine gute Portion Erfahrung und womöglich Experimentierfreude dazu. Wir haben drei Instanzen der Artikeltabelle benötigt, wobei wir eine Subquery in der SELECT-Klausel benutzt haben, um das Aggregat nach mwst zu erzeugen. Subqueries werden in Abschnitt 5.4 und 5.5 behandelt. 27 Einige Hinweise findet man in [Petk07].

244

SELECT-Anweisung mit einer Tabelle

ganze Tabelle eine einzige Gruppe bildet. Nach dem SQL-Standard bis 1999 waren dafür zwei Abfragen notwendig. Nach SQL:2003 kann man dies mit einer einzigen Abfrage erledigen, wenn man die Zusatzklausel ROLLUP verwendet. SELECT lagerplatz, SUM(bestand) FROM artikel GROUP BY ROLLUP(lagerplatz); lagerplatz null

SUM(artikel.bestand) 5442

5

398

4

345

2

2579

7

2120

Das Ergebnis enthält je Lagerplatz die Summe wie bei einer »normalen« Abfrage mit GROUP BY (zum Vergleich siehe den Anfang dieses Unterkapitels). Für jeden verschiedenen Wert des Gruppierungskriteriums gibt es eine Zeile im Ergebnis. Hier taucht zusätzlich eine Zeile mit einer Nullmarke in der Gruppierungsspalte auf. Diese enthält die Gesamtsumme über alle Gruppen, das sogenannte »grand total«. Ein anderer Ausdruck dafür wäre »Superaggregat«, insofern die zuvor ermittelten Aggregate, nämlich hier die Gruppensummen, nochmals aggregiert werden. Falls in den Daten selbst Nullmarken auftreten (hier für Artikel, für die noch kein Lagerplatz feststeht), werden diese auch zu einer Gruppe zusammengefasst, sodass man an der Nullmarke am Ergebnis nicht sehen kann, ob sie für ein Superaggregat steht oder für eine Gruppe. Zur Unterscheidbarkeit dient die Funktion GROUPING, deren Argument eine Gruppierungsspalte ist und die mit einem Wert ungleich 0 anzeigt, dass es nicht um »normale« Daten geht, sondern um eine Zusammenfassung über alle Gruppen hinweg. Eine Alternative wäre die Umwandlung von Nullmarken in den Ausgangsdaten mithilfe der Funktion IFNULL oder COALESCE in echte Werte, z. B. den Lagerplatz 99, der in der Realität nicht auftritt. Für das folgende Beispiel haben wir einen Artikel mit lagerplatz NULL in die Artikeltabelle eingefügt und nach der Abfrage wieder entfernt. SELECT lagerplatz, SUM(bestand), GROUPING(lagerplatz) FROM artikel GROUP BY ROLLUP (lagerplatz) ;

245

5 Datenmanipulation in SQL

lagerplatz

SUM(artikel.bestand)

GROUPING(artikel.lagerplatz)

5839

1

5

398

0

4

345

0

NULL

397

0

2

2579

0

7

2120

0

NULL

Wir sehen hier sehr deutlich, dass die Gruppierung nach mehreren Kriterien in einer Abfrage auch Probleme mit sich bringt, was die Interpretierbarkeit der Ergebnistabelle angeht. Die Nullmarke bei lagerplatz reicht schon nicht mehr aus, man braucht noch eine virtuelle Spalte dazu, die mit einer Zahl darauf hinweist, wie das Tupel zu interpretieren ist. Solche Konstruktionen sind in der Welt der Anwendungsprogrammierung nichts Ungewöhnliches. Häufig muss für die Erstellung mehrstufig aggregierter Berichte zu solchen Hilfsmitteln gegriffen werden. Sie stehen aber im Gegensatz zu einer relationalen Sichtweise auf die Daten, bei der für die Interpretation nur die originären Attributwerte zur Verfügung stehen und dafür aber auch ausreichen! Die Bildung von Aggregaten mit ROLLUP kann auch nach mehreren Kriterien vorgenommen werden, wie das folgende Beispiel zeigt. SELECT lagerplatz, mwst, SUM(bestand) AS ges_bestand FROM artikel GROUP BY ROLLUP(lagerplatz, mwst); lagerplatz

mwst

ges_bestand

NULL

NULL

5442

5

NULL

398

4

NULL

345

2

NULL

2579

7

NULL

2120

2

1

50

7

2

2120

5

1

398

4

1

345

2

2

2529

246

SELECT-Anweisung mit einer Tabelle

Das Superaggregat ist hier mehrstufig. Die Zeile mit Nullmarken für lagerplatz und mwst ist die oberste Stufe und zeigt die Gesamtsumme über alle Lagerplätze und Mehrwertsteuerkennzeichen. Die Zeilen mit Nullmarke nur bei mwst zeigen die Bestandssumme über alle Mehrwertsteuerkennzeichen bezogen auf den jeweiligen Lagerplatz. Die Zeilen mit Werten für lagerplatz und mwst zeigen die Summe für die Gruppe, die durch das entsprechende Wertepaar gebildet wird. Was nicht dargestellt wird, ist eine Gesamtsumme über alle Lagerplätze je Mehrwertsteuerkennzeichen. Insofern ist die Reihenfolge der Spalten in der GROUP BY ROLLUPKlausel nicht beliebig. Will man alle Superaggregate sehen, nutzt man die Klausel GROUP BY CUBE: SELECT lagerplatz, mwst, SUM(bestand) AS ges_bestand FROM artikel GROUP BY CUBE(mwst, lagerplatz); lagerplatz

mwst

ges_bestand

2

1

50

4

1

345

5

1

398

NULL

1

793

2

2

2529

7

2

2120

NULL

2

4649

NULL

NULL

5442

2

NULL

2579

4

NULL

345

5

NULL

398

7

NULL

2120

Das Ergebnis enthält zwei Zeilen mehr, nämlich die, wo lagerplatz eine Nullmarke und mwst einen Wert hat. Dies sind die in der vorigen Abfrage nicht gezeigten Summen der Bestände je Mehrwertsteuerkennzeichen über alle Lagerplätze. Eine differenzierte Angabe der darzustellenden Aggregate wird durch GROUPING SETS ermöglicht. Hier werden alle gewünschten Gruppierungen einzeln aufgezählt. Das folgende Beispiel gruppiert einzeln sowohl nach lagerplatz als auch nach mwst, verzichtet aber auf eine Gesamtsumme und gruppiert auch nicht nach verschiedenen Paaren von lagerplatz und mwst:

247

5 Datenmanipulation in SQL

SELECT lagerplatz, mwst, SUM(bestand) AS ges_bestand FROM artikel GROUP BY GROUPING SETS ((mwst), (lagerplatz)); lagerplatz

mwst

ges_bestand

NULL

1

793

NULL

2

4649

2

NULL

2579

4

NULL

345

5

NULL

398

7

NULL

2120

Dasselbe Resultat wie beim Einsatz der GROUP BY CUBE-Klausel kann man durch einzelne Angabe der Gruppierungsebenen erreichen: SELECT lagerplatz, mwst, SUM(bestand) FROM artikel GROUP BY GROUPING SETS ((mwst,lagerplatz),(mwst), (lagerplatz),());

Alle in diesem Unterkapitel mit den erweiterten GROUP BY-Klauseln gezeigten Abfragen hätte man auch mit der »konventionellen« Gruppenbildung erstellen können. Es wäre dazu notwendig, verschiedene einzelne Abfrage-Anweisungen zu formulieren und mit UNION28 zusammenzufügen.

5.2.12 Reihenfolge der Komponenten der SELECT-Anweisung Da nunmehr alle Klauseln der SELECT-Anweisung vorgestellt worden sind, kann ein Resümee gezogen werden. Syntaktisch gesehen sind die einzelnen Klauseln der SELECT-Anweisung zwingend in folgender Reihenfolge anzuordnen: SELECT FROM WHERE GROUP BY HAVING ORDER BY

Jede andere Reihenfolge führt zu einer Fehlermeldung. 28 UNION gehört zu den Mengenoperationen, die wir am Ende von Kapitel 5 besprechen.

248

Datenabfrage mit mehreren Relationen

Von der logischen Bearbeitung her gilt allerdings etwas anderes: Die FROM-Klausel wird zuerst ausgewertet. Damit stehen die Tabellen fest, auf die sich die Abfrage richtet. Als Nächstes folgt die Selektionsklausel WHERE. Es schließt sich die Gruppierung aufgrund der GROUP BY-Klausel an, gefolgt von der Gruppenselektion mit HAVING. Zum Schluss werden die Spalten der Ergebnisrelation mit der SELECT-Klausel festgelegt. Etwaige Berechnungen von virtuellen Spalten werden also auch erst dann vorgenommen, wenn feststeht, welche Zeilen die Ergebnisrelation umfasst. Die technische Abfolge in der Bearbeitung einer SELECT-Anweisung kann durchaus noch von der hier dargestellten logischen abweichen.

5.3 Datenabfrage mit mehreren Relationen Um Redundanz in einer Datenbank zu vermeiden, bedarf es der Normalisierung von Relationen (siehe Kapitel 2). Sachlich zusammengehörige Informationen werden dadurch in mehrere Basistabellen zerlegt. Um diese aber wieder ordnungsgemäß etwa in Listen zusammenzusetzen, sind oft Abfragen nötig, die gleichzeitig mehrere Tabellen betreffen. Bei einem Verbund von Relationen, englisch Join, verbinden wir Tupel verschiedener Relationen zu einem zusammenhängenden Tupel. Die Ergebnisrelation umfasst Attribute aus den beteiligten Basistabellen. In Kapitel 3 haben wir den Verbund als eine der Operationen der Relationenalgebra erklärt. Dort sind auch die Spezialfälle und die äußeren Verbunde definiert. Neben den Verbundoperationen sind in SQL noch die Mengenoperationen definiert. Es sind seit SQL-92 folgende Verknüpfungen von mehreren Tabellen definiert: SQL-Bezeichnung

Relationenalgebra

INNER JOIN(vgl. )

Verbund

(vgl. 2.4.6)

LEFT OUTER JOIN (vgl. )

äußerer Verbund

(vgl. 2.4.8)

RIGHT OUTER JOIN(vgl. )

äußerer Verbund

(vgl. 2.4.8)

FULL OUTER JOIN(vgl. )

äußerer Verbund

(vgl. 2.4.8)

UNION(vgl. 5.5)



(vgl. 2.4.9)

UNION JOIN(hier nicht behandelt)

(hier nicht behandelt)

INTERSECT(vgl. 5.5)



(vgl. 2.4.9)

MINUS(vgl. 5.5)

\

(vgl. 2.4.9)

249

5 Datenmanipulation in SQL

5.3.1 Tabellen und Tabellenalias Wenn in einer Abfrage mehrere Tabellen auftreten oder eine Tabelle mehrfach, was auch möglich ist (vgl. 5.3.3), ist es erforderlich, die benutzten Spalten den entsprechenden Tabellen zuzuordnen. Dazu kann grundsätzlich bei der Einführung einer Tabelle in der FROM-Klausel hinter die Tabelle ein Tabellenalias geschrieben werden. Die Syntax ist: tabellenreferenz ::= tabelle [[AS] alias]

Hierbei darf für Tabelle nicht nur der Bezeichner einer realen Tabelle stehen, die mit einer CREATE TABLE-Anweisung eingerichtet wurde, sondern auch der Bezeichner einer Datensicht29 oder sogar eine eingeklammerte Abfrage-Anweisung. Den letzteren Fall betrachten wir näher in Kapitel 5.4.7. Beispiele: kunde kunde AS k kunde k

Falls kein Alias angegeben wird, ist standardmäßig der Name der Tabelle der Alias – das geht allerdings nicht, wenn eine Tabelle in einer Abfrage mehrfach verwendet wird. Ein Bezug auf eine Spalte der entsprechenden Tabelle erfolgt durch: [alias.]spalte

Der Alias darf nur dann entfallen, wenn der Spaltenname in genau einer der benutzten Tabellen auftritt. Wir empfehlen, bei komplexen Abfragen mit mehreren Tabellen grundsätzlich alle Spalten mit Alias zu benutzen, da das auch der Übersichtlichkeit dient – wir können dann sofort erkennen, zu welcher Tabelle eine entsprechende Spalte gehört.

5.3.2 Der innere Verbund Wir haben folgende Möglichkeiten, Tabellen zu verknüpfen: Cross Join Natural Join Condition Join Column name Join

29 Vgl. Kapitel 6.

250

Datenabfrage mit mehreren Relationen

Die syntaktische Notation ist für alle Verbundoperationen gleich – es gibt lediglich die Unterscheidung, ob für den Verbund eine Spezifikation der zu verbindenden Spalten notwendig ist (beim Condition Join und beim Column Name Join) oder ob diese sich aus dem Zusammenhang ergibt (beim Produkt und dem natürlichen Verbund). In der folgenden syntaktischen Beschreibung werden diese beiden Verbundoperationen als join_op1 und join_op2 beschrieben. from-klausel ::= tabellenreferenz {join_tabelle} jointabelle ::= join_op2 tabelle verknüpfung | join_op1 tabelle join_op1 ::= NATURAL JOIN | CROSS JOIN join_op2 ::= [INNER] JOIN | LEFT OUTER JOIN | RIGHT OUTER JOIN | FULL OUTER JOIN verknüpfung ::= ON bedingung | USING (spaltenliste)

Der Verbund ist nicht nur zwischen zwei Tabellen, sondern zwischen beliebig vielen Tabellen möglich. Dies ist durch die Wiederholung der syntaktischen Konstruktion join_tabelle dargestellt. Nur bei den Varianten des Outer Join spielt die Reihenfolge, in der die Tabellen bei der FROM-Klausel aufgezählt werden, eine Rolle. Des Weiteren sind in der Regel Klammern zu setzen, wenn verschiedene Arten des Joins in einer Anweisung gemeinsam auftreten. Durch die Angabe einer Spaltenliste hinter der SELECT-Klausel wird die Verbundoperation mit einer Projektion kombiniert. In den folgenden Beispielen nutzen wir das aus, um die Ergebnislisten übersichtlicher zu gestalten.

Cross Join (cartesisches Produkt) Es werden alle Zeilen der einen Tabelle mit allen Zeilen der zweiten Tabelle verbunden.30 Beispiel (vgl. 2.4.7) SELECT t.tagname, b.anfang, b.ende FROM werktag AS t CROSS JOIN Unterrichtsblock AS b

30 Vgl. Kapitel 2.4.7.

251

5 Datenmanipulation in SQL

Condition Join Bei dieser Variante wird eine Bedingung angegeben, die in einem Wertvergleich zwischen Spalten der einen und Spalten der anderen Tabelle besteht. Die Spaltennamen sind dabei beliebig. Der Vergleichsoperator ist im Prinzip auch beliebig. In der Mehrzahl der Fälle wird aber die Gleichheit geprüft. Das folgende Beispiel, das Bestelldaten mit den Namen und Wohnorten der Kunden enthält, werden wir in äquivalenter Form in den folgenden Abschnitten wiederfinden. SELECT k.kunden_nr, k.name, b.bestelldatum FROM bestellung AS b JOIN kunde AS k ON k.kunden_nr = b.kunden_nr; kunden_nr

name

bestelldatum

101

Stein, Peter

2008-04-28

103

Randers, Nis

2008-05-15

103

Randers, Nis

2008-04-30

105

Stuff, Klaus

2008-05-12

Der Condition Join bietet die flexibelste Möglichkeit, Tabellen zu verbinden. Bis zum Standard SQL-89 gab es keine eigenständige JOIN-Klausel, sondern es waren alle zu verknüpfenden Tabellen (in beliebiger Reihenfolge) in der FROMKlausel anzugeben, gegebenenfalls zusätzlich mit Alias. Die Verknüpfung erfolgte dann mit denselben Ausdrücken wie in der ON-Klausel, aber sie war in der WHERE-Klausel mit unterzubringen, eventuell durch AND mit anderen Join-Verknüpfungen und Selektionskriterien gemischt. Diese Art des Verbundes ist noch zulässig und wird als »Old Style Join« bezeichnet. Das oben angegebene Beispiel sieht mit dem Old Style Join wie folgt aus: SELECT k.kunden_nr, k.name, b.bestelldatum FROM bestellung b, kunde k WHERE k.kunden_nr = b.kunden_nr;

Column Name Join Diese Form der Darstellung ist eine Kurzform für den sehr häufig auftretenden Fall, dass die Verbundattribute in den beiden Relationen dieselben Bezeichner haben und dass die Attributwerte auf Gleichheit geprüft werden.31

31 Diese Variante steht bei Sybase und Oracle nicht zur Verfügung.

252

Datenabfrage mit mehreren Relationen

SELECT kunden_nr, k.name, b.bestelldatum FROM bestellung AS b JOIN kunde AS k USING (kunden_nr)

Als Besonderheit ist zu beachten, dass die nach USING angegebenen Spalten nicht den zugrunde liegenden Tabellen zugeordnet werden, sondern der Ergebnisrelation. Von daher ist – im Gegensatz zum Condition Join – kein Alias bei diesen Spalten anzugeben.

Natural Join (natürlicher Verbund) Hier werden die Zeilen aus Tabelle 1 mit den Zeilen aus Tabelle 2 verbunden, bei denen die gemeinsamen Attribute beider Tabellen denselben Wert haben. Dabei kommt es darauf an, dass die Namen der Attribute übereinstimmen. SELECT k.kunden_nr, k.name, b.bestelldatum FROM bestellung AS b NATURAL JOIN kunde AS k

Anders als beim Column Name Join ist bei kunden_nr bei Sybase und einigen anderen DBMS die Angabe eines Tabellenalias notwendig. Das widerspricht allerdings dem Prinzip, da beim natürlichen Verbund jede in beiden Tabellen vorhandene Spalte eigentlich nur einmal vorkommt. Das Konzept des natürlichen Verbundes haben wir in Kapitel 2.4.6 eingeführt. In der theoretischen Darstellung der Relationenalgebra spielt der natürliche Verbund eine große Rolle, da er mit wenigen Begriffen auskommt – insbesondere können die Spalten der Ergebnisrelation auf natürliche Weise jeweils die Bezeichner der Ursprungsrelationen übernehmen. Wenn wir dagegen Abfragen mit SQL formulieren, die häufig in irgendwelchen Programmquellen eingebettet sind, müssen wir berücksichtigen, dass die zugrunde liegenden Tabellen im Laufe der Zeit geändert werden können. Wenn wir z. B. eines Tages für die Tabelle bestellung eine Spalte mit der Bezeichnung status einrichten (in der Kundentabelle gibt es eine entsprechend bezeichnete Spalte schon), werden Bestellungen mit ihren Kunden nur noch aufgenommen, wenn auch diese Spalte in der Bestellung denselben Wert hat wie die Spalte in der Kundentabelle. Es werden dann also sehr viele (oder sogar alle) Bestellungen aus der Ergebnisrelation herausfallen. Die Suche nach dem Fehler dürfte sicher einige Zeit in Anspruch nehmen, da das Programm, das ja nicht geändert worden ist, vorher immer einwandfrei lief.

Key Join Eine besondere Variante des Tabellenverbundes wird bei Sybase SQL Anywhere angeboten: der »Key Join«. Hierbei wird die Fremdschlüsselsemantik ausgenutzt, die unabhängig von Spaltennamen Beziehungen zwischen Tabellen festschreibt. Die per DDL definierten Beziehungen zwischen Tabellen (FOREIGN KEY-Klausel

253

5 Datenmanipulation in SQL

in der CREATE TABLE-Anweisung) sind in einem Datenwörterbuch hinterlegt und können zur Bildung von Verbunden herangezogen werden. Beispiel: SELECT k.kunden_nr, k.name, b.bestelldatum FROM bestellung AS b KEY JOIN kunde AS k

Allerdings gibt es beim Key Join einige Probleme: So ist beim Vorhandensein mehrerer FOREIGN KEY-Verknüpfungen zwischen den beteiligten Tabellen eine Mehrdeutigkeit vorhanden. Des Weiteren erlaubt Sybase SQL Anywhere beim Key Join die zusätzliche Angabe weiterer Bedingungen, die wie üblich nach dem Schlüsselwort ON anzugeben sind. Problematisch ist, dass beim Weglassen einer Verknüpfungsbedingung aus einem »normalen« Join ein Key Join wird. Hier wäre es sinnvoller, den Benutzer darauf hinzuweisen, wenn er die ON-Klausel vergessen hat – er kann ja dann das Schlüsselwort KEY vor JOIN angeben. Selbsttätige Aktionen des Rechners haben nicht immer das erwünschte Ergebnis zur Folge. Wir verwenden im Folgenden den Key Join nicht, da er nicht herstellerunabhängig verfügbar ist.

Verbund von mehr als zwei Tabellen Den Verbund von mehr als zwei Tabellen demonstrieren wir anhand der Tabellen bestellposition, bestellung und kunde. Zu allen Positionen sollen die Kundennamen angezeigt werden. SELECT k.kunden_nr, k.name, b.bestelldatum, p.artikel_nr FROM kunde AS k JOIN bestellung AS b JOIN bestellposition AS p ON b.kunden_nr = k.kunden_nr AND p.bestell_nr = b.bestell_nr ORDER BY k.kunden_nr, bestelldatum;

Wir erhalten folgende Liste: kunden_nr

254

name

bestelldatum

artikel_nr

101

Stein, Peter

2008-04-28

G002

101

Stein, Peter

2008-04-28

G003

101

Stein, Peter

2008-04-28

K002

101

Stein, Peter

2008-04-28

K003

101

Stein, Peter

2008-04-28

L002

Datenabfrage mit mehreren Relationen

kunden_nr

name

bestelldatum

artikel_nr

103

Randers, Nis

2008-04-30

K001

103

Randers, Nis

2008-04-30

K003

103

Randers, Nis

2008-04-30

K004

103

Randers, Nis

2008-05-15

G001

103

Randers, Nis

2008-05-15

G002

103

Randers, Nis

2008-05-15

G003

105

Stuff, Klaus

2008-05-12

G001

105

Stuff, Klaus

2008-05-12

L002

105

Stuff, Klaus

2008-05-12

L003

105

Stuff, Klaus

2008-05-12

L004

Verbund und andere Operationen der SELECT-Anweisung Alle übrigen Klauseln der SELECT-Anweisung sind auf Tabellenverbunde genauso wie auf Basistabellen anwendbar. Für die Projektion haben wir dies bereits an den letzten SQL-Anweisungen demonstriert. Natürlich steht auch die Selektion weiterhin zur Verfügung. Im folgenden Beispiel selektieren wir die Zeilen aus dem Verbund zwischen kunde und bestellung, bei denen die kunden_nr größer als 101 ist: SELECT k.kunden_nr, k.name, b.bestelldatum FROM kunde AS k JOIN bestellung AS b ON b.kunden_nr = k.kunden_nr WHERE k.kunden_nr > 101 ORDER BY k.kunden_nr, bestelldatum; kunden_nr

name

bestelldatum

103

Randers, Nis

2008-04-30

103

Randers, Nis

2008-05-15

105

Stuff, Klaus

2008-05-12

Das in Kapitel 2.4.6 vorgestellte Beispiel können wir nun in SQL formulieren. Die Liste aller Artikel aus Lager 2, die von Kunden aus Kayhude geordert wurden, bekommen wir durch die folgende Anweisung, in der vier Tabellen verbunden werden müssen.

255

5 Datenmanipulation in SQL

SELECT a.artikel_nr, a. bezeichnung, k.name, k.ort FROM kunde AS k JOIN bestellung AS b ON k.kunden_nr = b.kunden_nr JOIN bestellposition AS p ON p.bestell_nr = b.bestell_nr JOIN artikel AS a ON a.artikel_nr = p.artikel_nr WHERE a.lagerplatz = 2 AND k.ort = 'Kayhude'; artikel_nr

bezeichnung

name

ort

K002

Hose

Stein, Peter

Kayhude

K003

Damenhut

Stein, Peter

Kayhude

5.3.3 Verbindung einer Tabelle mit sich selbst Der in Kapitel 3.3.6 eingeführte Verbund einer Tabelle mit sich selbst kann auch in SQL formuliert werden. Das dort eingeführte Beispiel der Brüder der fränkischen Kaiser erhalten wir durch folgende Abfrage: SELECT k1.name, vater, k2.name FROM kaiser AS k1 JOIN kaiser AS k2 USING (Vater) WHERE k1.name k2.name;

Aufgabe 5.33 Zeigen Sie zu jedem Artikel den Mehrwertsteuersatz an. Sehen Sie im Handbuch nach, welche Join-Varianten Ihr DBMS unterstützt, und probieren Sie alle aus. Aufgabe 5.34 Zeigen Sie mithilfe des Mehrwertsteuersatzes den Bruttopreis je Artikel an. Aufgabe 5.35 Zeigen Sie zu jeder Bestellung den Wohnort des Kunden an. Aufgabe 5.36 Zeigen Sie zu jeder Bestellposition die Bestellmenge, Bestellnummer und den Wohnort des Kunden an. Aufgabe 5.37 Zeigen Sie je Ort die Anzahl der bestellten Artikel an (modifizieren Sie dazu die Anweisung aus Aufgabe 5.33).

256

Datenabfrage mit mehreren Relationen

5.3.4 Outer Join In Kapitel 2.4.8 ist der äußere Verbund als Operation der relationalen Algebra eingeführt worden. Wir benötigen den äußeren Verbund, um auch solche Tupel erscheinen zu lassen, die mit keinem Tupel der anderen Relation verknüpft sind, beispielsweise Kunden ohne Bestellung. Verglichen werden bei einem äußeren wie bei einem inneren Verbund jeweils die Werte in den angegebenen Verbindungsspalten der beteiligten Tabellen.32 Wir gehen der Einfachheit und der praktischen Bedeutung halber von einer Gleichheitsbedingung aus. Während es bei einem inneren Verbund zweier Tabellen keine Rolle spielt, in welcher Reihenfolge diese in der FROM-Klausel aufgezählt werden, sind bei einem äußeren Verbund mehrere Varianten möglich:33 1) In der zuerst genannten – der linken – Tabelle sollen Tupel ohne Entsprechung in der anderen Tabelle übernommen werden; sog. Left Outer Join. 2) In der als zweiten genannten – der rechten – Tabelle sollen Tupel ohne Entsprechung in der anderen Tabelle übernommen werden; sog. Right Outer Join. 3) Sowohl in der linken als auch in der rechten Tabelle sollen Tupel ohne Entsprechung in der anderen Tabelle übernommen werden; sog. Full Outer Join.

Anwendungsbeispiele Die Aufzählung von Kunden und zugehörigen Bestellungen unter Einschluss der Kunden, von denen noch keine Bestellung eingegangen ist, erhalten wir mit folgender Anweisung: SELECT k.name, b.bestell_nr, b.bestelldatum FROM kunde AS k LEFT OUTER JOIN bestellung AS b ON k.kunden_nr = b.kunden_nr;

Dabei werden die beiden Spalten bestell_nr und bestelldatum für Kunden ohne Bestellung mit Nullmarken aufgefüllt.

32 Dies können, wie beim inneren Join, natürlich mehrere Spalten sein. 33 Außerdem ist zu berücksichtigen, dass bei einer Verknüpfung von mehr als zwei Tabellen unter Verwendung verschiedener (äußerer und innerer) Verbunde eventuell Klammern zu setzen sind, um die richtige Verarbeitung sicherzustellen. Das kann sehr schnell unübersichtlich werden. Einen Verbund von mehr als zwei Tabellen können wir so darstellen, dass wir zuerst zwei Tabellen verbinden, mit dem Resultat dann die dritte usw. Wir haben dies am Beispiel in Kapitel 2.4.8 erläutert.

257

5 Datenmanipulation in SQL

name Voss, Hans Stein, Peter Berger, Uwe

bestell_nr (NULL) 151 (NULL)

bestelldatum (NULL) 2008-04-28 (NULL)

Randers, Nis

152

2008-04-30

Randers, Nis

154

2008-05-15

Andresen, Ute Stuff, Klaus Staack, Hannes

(NULL) 153 (NULL)

(NULL) 2008-05-12 (NULL)

Wenden wir auf dasselbe Tabellenpaar den RIGHT OUTER Join an, sieht das Resultat anders aus: SELECT name, bestell_nr, bestelldatum FROM kunde RIGHT OUTER JOIN bestellung ON kunde.kunden_nr = bestellung.kunden_nr; name

bestell_nr

bestelldatum

Stein, Peter

151

2008-04-28

Randers, Nis

152

2008-04-30

Stuff, Klaus

153

2008-05-12

Randers, Nis

154

2008-05-15

Es erscheinen nur die vorhandenen Bestellungen mit den zugehörigen Kunden. Die rechte Variante des äußeren Verbundes erbringt in diesem Fall also dasselbe Resultat wie der innere Verbund. Das liegt daran, dass es wegen der referenziellen Integritätsbedingung »FOREIGN KEY kunden_nr REFERENCES kunde«, die wir beim Erzeugen der Tabelle bestellung angewandt hatten, keine Bestellungen ohne zugehörigen Kunden geben kann. Der Right Outer Join macht Sinn, wenn wir in der FROM-Klausel die Reihenfolge der Tabellennamen ändern. SELECT name, bestell_nr, bestelldatum FROM bestellung RIGHT OUTER JOIN kunde ON kunde.kunden_nr = bestellung.kunden_nr;

258

Datenabfrage mit mehreren Relationen

name

bestell_nr

Voss, Hans

(NULL)

Stein, Peter Berger, Uwe

151 (NULL)

bestelldatum (NULL) 2008-04-28 (NULL)

Randers, Nis

152

2008-04-30

Randers, Nis

154

2008-05-15

Andresen, Ute Stuff, Klaus Staack, Hannes

(NULL) 153 (NULL)

(NULL) 2008-05-12 (NULL)

Wir erhalten dasselbe Resultat wie oben bei der linken Variante mit der anderen Reihenfolge der Tabellennamen. Es gibt Fragen, deren Beantwortung eine Kombination mehrerer Outer Joins erfordert: Wir benötigen beispielsweise eine Liste, in der zu allen Kunden die zugehörigen Bestellungen und die Bankverbindung aufgeführt werden. Es gibt aber Kunden, die noch nichts bestellt haben, und solche, von denen keine Bankverbindung bekannt ist. Die Lösung: SELECT k.kunden_nr, g.blz, b.bestell_nr FROM kunde k LEFT OUTER JOIN girokonto AS g ON k.kunden_nr = g.kunden_nr LEFT OUTER JOIN bestellung AS b ON k.kunden_nr = b.kunden_nr; kunden_nr

blz

bestell_nr

100

(NULL)

(NULL)

101

23410022

151

102

(NULL)

(NULL)

103

23410112

154

103

23410112

152

104

(NULL)

(NULL)

105

(NULL)

153

106

(NULL)

(NULL)

259

5 Datenmanipulation in SQL

Die Variante Full Outer Join ist anzuwenden, wenn sowohl bei der linken als auch bei der rechten Tabelle auch Zeilen ohne Bezüge zu der jeweils anderen Tabelle angezeigt werden sollen. Das setzt voraus, dass zwischen beiden Entitätentypen ein konditioneller Beziehungstyp besteht, sodass jeder Entität aus der einen Menge (E1) eine Entität aus der anderen (E2) zugeordnet sein kann, aber nicht muss, und dass dies auch in der umgekehrten Richtung der Beziehung gilt (siehe Kapitel 3). Ein solches Beispiel findet sich nicht in unserer Beispieldatenbank und ist auch in der Praxis nur sehr selten anzutreffen. Es gibt einige Systeme, die den Left Outer Join und den Right Outer Join besitzen, aber keinen Full Outer Join (z. B. Oracle, MS Access). Aufgabe 5.38 Listen Sie alle Artikel zusammen mit der jeweiligen Bestellmenge auf, unter Einschluss der Artikel, die noch in keiner Bestellposition auftreten. Aufgabe 5.39 Listen Sie alle Kunden mit der zugehörigen Bankverbindung auf. Auch Kunden ohne Bankverbindung sollen in der Liste erscheinen.

5.4 Unterabfragen In diesem Unterabschnitt erweitern wir die bisher behandelten Möglichkeiten der DML-Anweisungen in SQL durch die Möglichkeit, Ergebnisse von Abfragen direkt in anderen Anweisungen zu verwenden. Wir werden folgende Varianten kennenlernen: 왘 Unterabfragen, die einen Wert liefern 왘 ALL-, ANY- und IN-Unterabfragen 왘 EXISTS-Unterabfragen 왘 Unterabfragen, die eine Tabelle liefern

5.4.1 Unterabfragen, die einen Wert liefern Wir benötigen Unterabfragen, um Anweisungen mit Werten zu formulieren, die selbst erst über eine Datenbankabfrage ermittelt werden. Ein Beispiel: »Zeige die Kunden, die in derselben Stadt wohnen wie der Kunde mit der Kundennummer 106.« Bei der Formulierung der Abfrage ist der Ort nicht bekannt. Um ihn zu ermitteln, könnten wir zunächst die Abfrage formulieren: SELECT ort FROM kunde WHERE kunden_nr = 106

260

Unterabfragen

Den Ortsnamen könnten wir dann als Konstante in der Abfrage einsetzen, welche die eigentlich erwünschten Ergebnisse liefert: SELECT kunden_nr, name FROM kunde WHERE ort = 'Kayhude'

An jeder Stelle in einer SQL-Anweisung, an der ein Ausdruck stehen kann, der einen Wert berechnet, kann stattdessen eine spezielle Unterabfrage stehen, die als Ergebnis einen Wert hat. Eine Unterabfrage, die für einen Wert stehen darf, ist hierbei eine in Klammern gesetzte Abfrage-Anweisung, die zwei Bedingungen zu erfüllen hat: 1) Die SELECT-Klausel muss genau eine Spalte oder virtuelle Spalte enthalten. 2) Das Ergebnis der SELECT-Klausel darf nicht mehr als ein Tupel enthalten. Somit können wir unser obiges Problem formulieren als: SELECT kunden_nr, name, ort FROM kunde WHERE ort = (SELECT ort FROM kunde WHERE kunden_nr = 106);

Falls das Ergebnis der Unterabfrage kein Tupel enthält, wird NULL als Ergebnis zurückgeliefert, sonst der Wert der Spalte dieses Tupels. Die Gültigkeit der ersten Bedingung ist sofort syntaktisch nachprüfbar. Die Gültigkeit der zweiten Bedingung kann in einigen Fällen auch schon vor der Auswertung festgestellt werden, so z.B. 왘 wenn die virtuelle Spalte der SELECT-Klausel eine Aggregatfunktion enthält

und keine GROUP BY-Klausel enthalten ist oder 왘 wenn die Bedingung in der WHERE-Klausel der Abfrage-Anweisung den Pri-

märschlüssel mit einem Wert vergleicht. Beispiele für diese beiden Bedingungen sind die folgenden zwei Abfrage-Anweisungen: SELECT AVG(Listenpreis) FROM artikel SELECT listenpreis FROM Artikel WHERE artikel_Nr = 'G001'

In den anderen Fällen wird die Anweisung ausgeführt, und es gibt einen Laufzeitfehler, wenn die Unterabfrage eine Tabelle mit mehr als einem Tupel ergibt. Unterabfragen, die einen Wert liefern, können prinzipiell dort benutzt werden, wo ein Ausdruck stehen muss, unter anderem: 왘 in der SELECT-Klausel einer SELECT-Anweisung 왘 in der WHERE-Klausel einer SELECT-Anweisung 왘 in der WHERE-Klausel einer DELETE-Anweisung

261

5 Datenmanipulation in SQL 왘 in der WHERE-Klausel einer UPDATE-Anweisung 왘 in der SET-Klausel einer UPDATE-Anweisung 왘 in der VALUES-Klausel einer INSERT-Anweisung

-- zeige die Abweichungen vom durchschnittlichen Listenpreis SELECT artikel_nr, listenpreis, listenpreis (SELECT AVG(listenpreis) FROM artikel) AS Abweichung FROM artikel; artikel_nr

listenpreis

Abweichung

G001

38,50

2,81

G002

12,45

-23,24

G003

5,20

-30,49

K001

98,50

62,81

K002

112,80

77,11

K003

65,70

30,01

K004

76,00

40,31

L001

6,35

-29,34

L002

8,35

-27,34

L003

1,78

-33,91

L004

0,98

-34,71

L005

1,72

-33,97

Eine Unterabfrage kann auch in der WHERE-Klausel der Abfrage stehen, was wir schon in den vorigen Abschnitten besprochen haben. Hier noch einmal ein Beispiel: -- Zeige alle Artikel, die teurer sind als der Durchschnitt SELECT artikel_nr, listenpreis FROM Artikel WHERE listenpreis > (SELECT AVG(listenpreis) FROM artikel);

Das folgende Beispiel enthält eine Subquery in der WHERE-Klausel der UPDATEAnweisung. Es verbilligt alle Artikel, die teurer sind als der Durchschnitt, um 10 %.

262

Unterabfragen

UPDATE artikel SET listenpreis = 0.9*listenpreis WHERE listenpreis > (SELECT AVG(listenpreis) FROM artikel);

In der SET-Klausel der UPDATE-Anweisung kann eine Unterabfrage dazu benutzt werden, die geänderten Werte aus einer anderen oder derselben Tabelle zu ermitteln, statt sie als Konstanten zu übergeben. Die folgende Anweisung ändert die Adresse von Herrn Berger auf die Adresse von Herrn Stein. UPDATE kunde SET strasse = (SELECT strasse FROM kunde WHERE name LIKE 'Stein%'), plz = (SELECT plz FROM kunde WHERE name LIKE 'Stein%'), ort = (SELECT ort FROM kunde WHERE name LIKE 'Stein%') WHERE name LIKE 'Berger%';

In der Values-Klausel der INSERT-Anweisung können wir einen Wert indirekt durch Unterabfrage aus der Datenbank ermitteln. Wir wissen beispielsweise nicht die Kundennummer von Herrn Berger, für den wir eine Bestellung erfassen wollen. -- Füge eine Bestellung mit der Nummer 176 für den Kunden Berger ein INSERT INTO bestellung (bestell_nr, kunden_nr) VALUES ( 176, (SELECT kunden_nr FROM kunde WHERE NAME LIKE 'Berger%') );

Herr Berger hat glücklicherweise keinen Namensvetter, sonst wäre das Ergebnis der Unterabfrage nicht ein Wert, sondern eine Relation mit mehreren Tupeln, und wir erhielten eine Fehlermeldung, wie im folgenden Beispiel. -- Diese Abfrage führt zu einem Laufzeitfehler, da die -- Unterabfrage mehr als ein Tupel liefert. INSERT INTO bestellung (bestell_nr, kunden_nr) VALUES ( 176, (SELECT kunden_nr FROM kunde WHERE NAME LIKE 'St%') );

Als Erweiterung dieses Konzeptes erlaubt die SQL-Norm auch Unterabfragen, die mehr als eine Spalte enthalten. Diese Tupel können dann u. a. mit Tupeln verglichen werden. Ein Beispiel34:

34 Diese Variante wird nur von sehr wenigen DBMS unterstützt.

263

5 Datenmanipulation in SQL

SELECT kunden_nr, name, plz, ort FROM kunde WHERE (plz,ort) = (SELECT plz,ort FROM kunde WHERE name = 'Stein, Peter')

Aufgabe 5.40 Ermitteln Sie den billigsten Artikel. Aufgabe 5.41 Welcher Kunde hat die letzte Bestellung aufgegeben? Zeigen Sie die Attribute kunden_nr, name und bestell_nr an. Hinweis: Zur Lösung benötigen Sie in der Hauptabfrage einen geeigneten Verbund von kunde und bestellung.

5.4.2 Unterabfragen, die eine Menge liefern Liefert die Unterabfrage eine Ergebnistabelle mit mehr als einer Zeile, so ist der Einsatz von Mengenoperatoren in der WHERE-Klausel der Hauptabfrage erforderlich. Nach der SQL-Norm (schon seit SQL-92!) kann die Anzahl der Ausdrücke nach SELECT größer als 1 sein – in dem Fall ist eine Folge mit mehreren Komponenten in dem Ergebnis zu suchen. Da aber keines der von uns benutzten Systeme diese Möglichkeit unterstützt, setzen wir für das Folgende immer voraus, dass nur ein Ausdruck hinter SELECT steht. Folgende Operatoren werden zur Formulierung von Bedingungen angeboten, wobei θ für einen der Vergleichsoperatoren (=, , >, >=, ANY (SELECT listenpreis FROM artikel WHERE lagerplatz = 2);

266

Unterabfragen

artikel_nr

listenpreis

G001

38,5

G002

12,45

G003

5,2

L001

6,35

L002

8,35

L003

1,78

Zum besseren Verständnis hier das Ergebnis der Unterabfrage: SELECT listenpreis FROM artikel WHERE lagerplatz = 2; listenpreis 98,5 112,8 65,7 76 0,98 1,72

Die Bedingung der Hauptabfrage ist für alle Artikel erfüllt, die nicht im Lagerplatz 2 liegen und teurer sind als 98 Cent, denn dieses ist der kleinste Listenpreis bezogen auf Lagerplatz 2. Es reicht also aus, wenn die Bedingung der Hauptabfrage für irgendeine Zeile der Unterabfrage den Wahrheitswert TRUE liefert.

ALL Der ALL-Operator wird eingesetzt, wenn die Bedingung für alle Zeilen der Unterabfrage erfüllt sein muss. Wir suchen die Artikel, deren Listenpreis größer ist als die Listenpreise aller Artikel aus Lager 5. SELECT artikel_nr, listenpreis FROM artikel WHERE listenpreis > ALL

267

5 Datenmanipulation in SQL

(SELECT listenpreis FROM artikel WHERE lagerplatz = 5); artikel_nr

listenpreis

G001

38,50

G002

12,45

K001

98,50

K002

112,80

K003

65,70

K004

76,00

Abfragen mit ALL können durch Umkehrung der logischen Bedingung immer auch mit ANY formuliert werden und umgekehrt. Beispielsweise ist die Aussage vergleichswert > ALL (Unterabfrage)

logisch äquivalent zu der Aussage: NOT vergleichswert (SELECT MAX(listenpreis) FROM artikel WHERE lagerplatz = 5)

Abfragen unter Verwendung von Unterabfragen können also meist auf verschiedene Weise formuliert werden. Im Allgemeinen wird man die kürzere Formulierung bevorzugen, die meist auch die verständlichere ist. Unterschiede ergeben sich aber auch im Antwortverhalten. Bedingungen, die erfüllt sind, wenn die Unterabfrage bereits eine einzige Zeile erbringt, werden in der Regel schneller bearbeitet als Bedingungen, für die jeweils die Ergebnistabelle der Unterabfrage geprüft werden muss. Im obigen Beispiel mit der WHERE-Klausel WHERE listenpreis > ALL (SELECT listenpreis FROM artikel WHERE lagerplatz = 5)

268

Unterabfragen

müssen in der Unterabfrage alle Artikel mit lagerplatz = 5 gelesen werden, denn der in der Hauptabfrage geprüfte Artikel muss teurer sein als alle Artikel aus Lager 5. Im nächsten Beispiel reicht es aus, wenn die Unterabfrage einen Artikel liefert, dessen Listenpreis kleiner oder gleich ist als der betrachtete. WHERE listenpreis > ANY (SELECT listenpreis FROM artikel WHERE lagerplatz = 2)

Im günstigsten Fall ist schon bei der ersten Zeile der Unterabfrage die Bedingung erfüllt.

5.4.3 EXISTS Der EXISTS-Operator prüft, ob in der Ergebnismenge der Unterabfrage mindestens eine Zeile enthalten ist. Wir greifen noch einmal eine bereits behandelte Abfrage auf: »Gesucht sind die Bestellungen, in denen der Artikel ‘K003’ enthalten ist.« Mit dem EXISTS-Operator kann dieselbe Frage beantwortet werden, wenn wir sie etwas anders formulieren: »Gesucht sind die Bestellungen, für die eine Bestellposition existiert, in der die artikel_nr den Wert ‘K003’ hat.« SELECT bestell_nr, bestelldatum FROM bestellung b WHERE EXISTS (SELECT * FROM bestellposition p WHERE p.artikel_nr = 'K003' AND p.bestell_nr = b.bestell_nr)

Um diese Aufgabe mit dem IN-Operator zu lösen, hatten wir in der Unterabfrage die Bestellnummern der Positionen ermittelt, bei denen artikel_nr den Wert 'K003' hat. In der Hauptabfrage wurden die Bestellungen selektiert, deren Bestellnummern in der Ergebnismenge enthalten waren. Die Unterabfrage war unabhängig von der Hauptabfrage formulierbar. Im Unterschied dazu muss bei der Verwendung von EXISTS in der Unterabfrage eine Bedingung formuliert werden, die ein bestimmtes in der Hauptabfrage behandeltes Tupel mit einbezieht. Anders ausgedrückt: Die Frage EXISTS (SELECT * FROM bestellposition WHERE artikel_nr = 'K003')

269

5 Datenmanipulation in SQL

liefert immer dann TRUE, wenn der Artikel ‘K003’ in irgendeiner Position auftritt. Also müssen wir die Untersuchung in der Unterabfrage auf die Positionen beschränken, die zu einer gerade in der Hauptabfrage behandelten Bestellung gehören. Die Bedingung lautet daher: WHERE AND

artikel_nr = 'K003' p.bestell_nr = b.bestell_nr

5.4.4 Korrelierte Unterabfrage Eine Unterabfrage wie im letzten Beispiel wird korreliert genannt, da sie nicht unabhängig von der Hauptabfrage bearbeitet werden kann. Die Bedingung in der Unterabfrage, welche die Korrelation herstellt, ist einer Verbundbedingung sehr ähnlich. Sie bezieht sich aber auf eine Tabelle, die in der FROM-Klausel der Hauptabfrage angesprochen wird und nicht in der FROMKlausel der Unterabfrage. Dies kann nur funktionieren, wenn die Attributwerte der Hauptabfrage in der Unterabfrage bekannt sind. Bei SQL gilt die Regel, dass in einer Unterabfrage sämtliche Attribute der sie umfassenden Oberabfragen mit Namen und Wert referenzierbar sind. Da eine Unterabfrage wiederum Unterabfragen enthalten kann, umfasst die Reichweite eines Attributs eventuell mehrere Stufen. Taucht ein Attribut in der Ober- und Unterabfrage gleichermaßen auf, so muss es mit vorangestelltem Tabellenalias qualifiziert werden, wie in obigem Beispiel zu erkennen ist. Korrelierte Unterabfragen können auch im Zusammenhang mit IN, ANY und ALL auftreten. Des Weiteren können sie nicht nur in der WHERE-Klausel der SELECTAnweisung auftreten, sondern auch z. B. in der SET-Klausel der UPDATE-Anweisung. So wird mit folgender Anweisung jedem Kunden das Datum seiner letzten Bestellung zugewiesen: UPDATE kunde AS k SET letzte_bestellung = (SELECT MAX(bestelldatum) FROM bestellung AS b WHERE k.kunden_nr = b.kunden_nr);

Die logische Abarbeitung einer DML-Anweisung mit einer korrelierten Unterabfrage sieht so aus, dass für jedes in der DML-Anweisung behandelte Tupel die Unterabfrage gestartet und evaluiert wird. Das entspricht einer zweifachen Iteration. Ob das DBMS tatsächlich in der geschilderten Weise verfährt, hängt vom Hersteller und der Version ab. Eventuell kann der Abfrageoptimierer die Abfragen in eine äquivalente Formulierung übersetzen, die er dann schneller abarbeitet. In einem weiteren Beispiel suchen wir alle Bestellungen, die Artikel aus Lager 2 enthalten. Hierzu muss in der Unterabfrage ein Verbund der Tabellen artikel und position hergestellt werden.

270

Unterabfragen

SELECT * FROM bestellung AS b WHERE EXISTS (SELECT * FROM bestellposition p JOIN artikel a ON a.artikel_Nr = p.artikel_Nr WHERE b.bestell_nr = p.bestell_nr AND a.lagerplatz = 2)

Wir können nun mithilfe des EXISTS-Operators die Kunden ermitteln, die noch keine Bestellung aufgegeben haben: SELECT kunden_nr, name FROM kunde k WHERE NOT EXISTS (SELECT * FROM bestellung b WHERE b.kunden_nr = k.kunden_nr)

Aufgabe 5.42 Welche Bestellungen sind nicht von Kunden aus Kayhude? Aufgabe 5.43 Welche Artikel haben einen höheren Listenpreis als alle Artikel, deren artikel_nr mit 'G' anfängt? Hinweis: Verwenden Sie in der Bedingung der Unterabfrage den Operator LIKE. Aufgabe 5.44 Welche Artikel sind schon irgendwann einmal bestellt worden? Welche sind noch nie bestellt worden? Geben Sie jeweils die Artikel_nr und die Bezeichnung an. Aufgabe 5.45 Welche Kunden haben kein Girokonto?

5.4.5 EXISTS versus IN Abfragen mit EXISTS lassen sich in vielen Fällen in Abfragen mit IN umwandeln. Vorsicht ist aber geboten, da Bedingungen mit EXISTS grundsätzlich nur die Wahrheitswerte TRUE und FALSE ergeben, Bedingungen mit IN können auch UNKNOWN zurückgeben. Das spielt dann eine Rolle, wenn Bedingungen mit NOT verneint werden, da NOT FALSE = TRUE und NOT UNKNOWN = UNKNOWN ist. Ein Tupel wird aber dann in die Ergebnisrelation übernommen, wenn die Bedingung auf TRUE evaluiert, bei UNKNOWN und FALSE fällt es heraus.36 36 Eine ausführliche Darstellung dieses Problems findet sich in [Date90a S. 339ff].

271

5 Datenmanipulation in SQL

An folgendem Beispiel erläutern wir den Unterschied: Gesucht sind die Bestellungen, bei denen keine Position mit der Liefermenge 11 auftritt. SELECT * FROM bestellung AS b WHERE NOT 11 in (SELECT liefermenge FROM bestellposition AS p WHERE b.bestell_nr = p.bestell_nr); bestell_nr

kunden_nr

bestelldatum

lieferdatum

151 152

rechnungsbetrag

101

2008-04-28

2008-05-02

200,67

103

2008-04-30

2008-05-02

2304,36

Bei den Positionen, in denen die Liefermenge noch gar keinen Wert enthält, ergibt die Bedingung »11 IN (SELECT liefermenge ...« den Wahrheitswert UNKNOWN. Daran ändert auch die Verneinung mit NOT nichts. Anders bei der nächsten Abfrage. Dort wird die Bedingung »EXISTS (SELECT liefermenge ... WHERE ... liefermenge = 11)« für die Zeilen mit einer Nullmarke in der Liefermenge mit FALSE evaluiert. Die logische Verneinung mit NOT liefert dann TRUE. Daher tauchen jetzt die Bestellungen 153 und 154 in der Liste auf, bei deren Positionen die Liefermenge noch eine Nullmarke enthält. SELECT * FROM bestellung AS b WHERE NOT EXISTS ( SELECT * FROM bestellposition AS p WHERE b.bestell_nr = p.bestell_nr AND liefermenge = 11); bestell_nr

kunden_nr

bestelldatum

lieferdatum

151

rechnungsbetrag

101

2008-04-28

2008-05-02

200,67

152

103

2008-04-30

2008-05-02

2304,36

153

105

2008-05-12

154

103

2008-05-15

5.4.6 Unterabfragen, die eine Tabelle liefern 왘 Eine Unterabfrage liefert natürlich grundsätzlich eine Tabelle – aber in den vor-

herigen Abschnitten haben wir gezeigt, dass in SQL diese Tabelle auch als Wert, als Menge von Werten (oder Tupeln) oder im Rahmen eines EXISTS-Prädikats verwendet werden kann. 왘 Als Letztes zeigen wir, wie das Ergebnis einer Unterabfrage als Tabelle in der FROM-Klausel verwendet werden kann.

272

Unterabfragen

Beispiele Die folgenden Beispiele sind nicht mit jedem DBMS nachvollziehbar, da der Einsatz von Unterabfragen in verschiedenen Klauseln der SELECT-, UPDATE-, INSERT-Anweisung nicht von allen Herstellern gleichermaßen unterstützt wird. Die Anzeige des Einzelpreises jedes Artikels und des Durchschnittspreises aller Artikel in derselben Abfrage scheitert normalerweise daran, dass bei Gruppenbildung einzelne Attributwerte der Tabelle nicht abgefragt werden können.37 Das Problem lässt sich jedoch elegant lösen, wenn wir den aggregierten Wert in einer Subquery ermitteln und diesen dann in der SELECT-Klausel mit angeben. Das Ergebnis der Unterabfrage in der FROM-Klausel einer SELECT-Anweisung ist ein Tabellenausdruck. Diese Tabelle – die beispielsweise nur einen Teil der Spalten und Tupel einer Basistabelle oder aggregierte Daten enthält – kann dann weiter untersucht werden. Manche komplexe Probleme können dadurch in einer (verschachtelten) Abfrage formuliert werden. Die folgende Anweisung zeigt als Beispiel, das wir zugegebenermaßen auch einfacher hätten erzeugen können38, die Summe der Gesamtpreise der einzelnen Positionen pro Bestellung: SELECT * FROM (SELECT bestell_nr, SUM(gesamtpreis) AS summe FROM bestellposition GROUP BY bestell_nr) AS bb;

Wichtig hierbei ist die Angabe eines Alias für diese Tabelle. Nun können wir aber eine neue Auswertung erzeugen, die zu jeder Bestellposition den Anteil anzeigt, den diese am gesamten Wert der Bestellung hat, indem wir die eben getestete Unterabfrage in einen Join einbinden. Diese Auswertung ist schon recht anspruchsvoll, da sie Einzeldaten mit aggregierten Daten verbindet, übrigens ein typischer Fall für Data-Warehouse-Anwendungen. SELECT p.bestell_nr, p.artikel_nr, p.gesamtpreis, bb.summe, p.gesamtpreis/bb.summe AS Anteil FROM ( SELECT bestell_nr, SUM(gesamtpreis) AS summe FROM bestellposition GROUP BY bestell_nr ) AS bb JOIN bestellposition p ON p.bestell_nr = bb.bestell_nr;

37 Vgl. Abschnitt 5.2.9 und 5.2.10. Diese Einschränkung lässt sich durch Anwendung der PARTITION-Klausel umgehen, vgl. Abschnitt 5.2.11. 38 Das Resultat erhielte man einfacher, wenn man die äußere SQL-Anweisung SELECT * einfach wegließe. Aber hier wollen wir zeigen, dass eine SELECT-Anweisung in der FROM-Klausel möglich ist.

273

5 Datenmanipulation in SQL

bestell_nr

artikel_nr

gesamtpreis

summe

Anteil

151

L002

41,75

172,85

0,241538907

151

K003

65,70

172,85

0,380098351

151

K002

0,00

172,85

0,000000000

151

G003

15,60

172,85

0,090251663

151

G002

49,80

172,85

0,288111079

152

K004

912,00

2028,40

0,449615460

152

K003

131,40

2028,40

0,064780122

152

K001

985,00

2028,40

0,485604417

153

L004

153

L003

153

L002

153

G001

154

G003

154

G002

154

G001

Aufgabe 5.46 Entwickeln Sie in mehreren Schritten folgende Auswertung:39 1) den Bestellumsatz je Artikel anhand der Werte von artikel.listenpreis und des Werts der Spalte bestellposition.bestellmenge. Sie müssen dafür einen Join von artikel mit bestellposition erzeugen. 2) die Einzeldaten artikel_nr, bezeichnung, listenpreis pro Artikel zusammen mit dem Bestellumsatz des jeweiligen Artikels in einer Zeile. Hierzu benötigen Sie einen Join der Artikeltabelle mit der Tabelle, die Sie in Teil 1 erzeugen.

39 Diese Aufgabe ist mit SYBASE nicht lösbar.

274

Unterabfragen

3) Das Ergebnis sollte etwa so aussehen: Artikel_nr

Bezeichnung

Listenpreis

Bestellumsatz

G002

Portwein

12,45

199,20

K003

Damenhut

65,70

197,10

K002

Hose

112,80

338,40

K001

Schuhe

98,50

985,00

L003

Naturreis

1,78

44,50

K004

Sonnenbrille

76,00

912,00

L004

Schokolade

0,98

4,90

G003

Bier

5,20

20,80

L002

China-Tee

8,35

133,60

G001

Whisky

28,78

172,68

5.4.7 Benannte Unterabfragen Wie wir gesehen haben, kann man mithilfe von Abfragen Tabellenausdrücke definieren, die man in einer FROM-Klausel verwenden kann wie eine reguläre Tabelle. Da dies an mehreren Stellen innerhalb einer Abfrage möglich ist, kann die gesamte Anweisung damit doch etwas unübersichtlich werden. Das folgende Beispiel ermittelt die Bestellung mit den meisten Positionen. SELECT bestell_nr, anzahl1 FROM ( SELECT bestell_nr, COUNT(*) AS anzahl1 FROM bestellposition GROUP BY bestell_nr ) AS p WHERE p.anzahl1= ( SELECT MAX(anzahl2) FROM ( SELECT bestell_nr, COUNT(*) AS anzahl2 FROM bestellposition GROUP BY bestell_nr ) AS pp); bestell_nr

anzahl1

151

5

275

5 Datenmanipulation in SQL

Wir verwenden dabei an zwei Stellen denselben Tabellenausdruck: (SELECT bestell_nr, COUNT(*) AS anzahl1 FROM bestellposition GROUP BY bestell_nr)

Die WITH-Klausel erlaubt es nun, einem Tabellenausdruck einen innerhalb der Abfrage gültigen Namen zu geben, sodass er mehrfach referenziert werden kann und nicht mehrfach als Unteranweisung formuliert werden muss. Wir nennen den Ausdruck im Folgenden anz_positionen und gestalten damit die obige Abfrage kürzer: WITH anz_positionen (bestell_nr, anzahl) AS (SELECT bestell_nr, COUNT(*) AS anzahl1 FROM bestellposition GROUP BY bestell_nr) SELECT * FROM anz_positionen ap1 WHERE ap1.anzahl = (SELECT MAX(anzahl) FROM anz_positionen AS ap2);

Seit SQL:2003 können Unterabfragen auch rekursiv sein. Wir verzichten hier auf eine eingehende Darstellung.40

5.5 Mengenoperationen In Kapitel 3.2.9 wurden die Mengenoperationen Vereinigung, Durchschnitt und Differenz als Bestandteil der relationalen Algebra eingeführt. Wir zeigen nun, wie diese Operationen in SQL realisiert werden. Syntaktisch werden damit zwei Abfrage-Anweisungen verbunden. abfrage1 mengenoperator abfrage2

40 Mit nichtrekursiven SQL-Abfragen können wir alles das ausdrücken, was in der Prädikatenlogik erster Ordnung möglich ist – aber nicht alles, was berechenbar ist. Wenn wir eine Datenbank haben, in der für Personen jeweils – falls bekannt – ein Fremdschlüssel für den Vater und ein Fremdschlüssel für die Mutter eingetragen ist, können wir ohne Probleme Relationen wie »a ist Großvater von b«, »a ist Urgroßvater von b«, aber auch »a ist Onkel von b« ausdrücken. Wir können auch noch »a ist Ururgroßvater von b« oder »a ist Urururgroßvater von b« ausdrücken (für jedes »ur« wird der Join um eine Relation erweitert). Wir können aber nicht darstellen »a ist vorfahre von b«, obwohl das (effizient) berechenbar ist. Da hier die Anzahl der »ur« nicht beschränkt ist, kann auch die Anzahl der Relationen in dem Join nicht beschränkt sein – unendliche Abfragen aber gibt es nicht. Durch die Einführung von Rekursion können wir das auch ausdrücken. Umgangssprachlich ausgedrückt ist ein Ahn entweder ein Elternteil oder ein Ahn eines Elternteils.

276

Mengenoperationen

Dabei ist mengenoperator ::= UNION | UNION ALL | INTERSECT | MINUS

Die Festlegung der korrespondierenden Attribute erfolgt – anders als bei der Relationenalgebra – nicht durch die Namen der Spalten, sondern durch die Reihenfolge in der SELECT-Klausel der Abfrage-Anweisung. Korrespondierende Spalten müssen kompatible Datentypen haben. Dagegen können die Bezeichner unterschiedlich sein – es werden jeweils die Bezeichner der ersten Abfrage-Anweisung übernommen.

UNION Der UNION-Operator bildet die Vereinigung zweier Relationen. Doppelte Tupel werden dabei automatisch zusammengefasst.41 Es werden dabei nicht nur Dubletten zusammengefasst, die jeweils in beiden Abfrage-Anweisungen auftreten, sondern auch Dubletten, die in einer der Abfrage-Anweisungen mehrfach auftreten, aber in der anderen nicht. Somit erhalten wird durch SELECT plz, ort FROM kunde UNION SELECT '00000', 'Dummy' FROM kunde WHERE 1=0;

dasselbe Ergebnis wie durch SELECT DISTINCT plz, ort FROM kunde;

da in beiden Fällen nach der Auswertung der Tupel eine Projektion durchgeführt wird. Im ersten Beispiel liefert die Abfrage nach UNION mit Sicherheit kein Tupel. Wir ermitteln die Menge aller Artikel mit lagerplatz = 5 und vereinigen diese mit der Menge aller Artikel mit lagerplatz = 7: SELECT artikel_nr, bezeichnung, lagerplatz FROM artikel WHERE lagerplatz = 5 UNION SELECT artikel_nr, bezeichnung, lagerplatz FROM artikel WHERE lagerplatz = 7; artikel_nr bezeichnung lagerplatz G001 Whisky 7

41 Zur Erinnerung: Bei der Projektion werden Dubletten unter SQL nicht automatisch entfernt, dies muss vielmehr mit dem DISTINCT-Operator extra angefordert werden (vgl. 4.4.1).

277

5 Datenmanipulation in SQL

G002 G003 L001 L002

Portwein Bier Ceylon-Tee China-Tee

7 7 5 5

Wir hätten diese Abfrage natürlich auch etwas einfacher mithilfe eines logischen Kalküls formulieren können: SELECT artikel_nr, bezeichnung, lagerplatz FROM artikel WHERE lagerplatz = 5 OR lagerplatz = 7

Diese Variante funktioniert aber nur, wenn die zu vereinigenden Mengen auf derselben Basistabelle beruhen (vgl. hierzu das Beispiel in Kapitel 2.4.9). Die folgende Anweisung zeigt, wie wir mithilfe von UNION und einer Unterabfrage einen Outer Join formulieren können. SELECT k1.kunden_nr, name, bestell_nr FROM kunde k1, bestellung b1 WHERE k1.kunden_nr = b1.kunden_nr UNION SELECT kunden_nr, name, NULL FROM kunde k2 WHERE kunden_nr NOT IN (SELECT kunden_nr FROM bestellung) kunden_nr name bestell_nr 100 Voss, Hans (NULL) 101 Stein, Peter 151 102 Berger, Uwe (NULL) 103 Randers, Nis 152 103 Randers, Nis 154 105 Stuff, Werner 153 106 Staack, Hannes (NULL)

Der UNION-Operator kann nicht durch andere Operationen nachgebildet werden. Daher war er schon in den ersten SQL-Systemen vorhanden und in SQL-86 enthalten.

UNION ALL Während beim UNION-Operator das Ergebnis immer eine Relation ist, kann durch Angabe des Schlüsselwortes ALL verhindert werden, dass nach der Bestimmung der Ergebnisrelation identische Tupel zusammengefasst werden.

278

Verarbeitung einer Abfrage

INTERSECT Für die Durchschnittsbildung von Relationen ist in SQL der Operator INTERSECT vorgesehen. Syntaktisch ist er genauso zu verwenden wie der UNION-Operator. Wie wir in Kapitel 2.4.9, Beispiel 2, gezeigt haben, kann der Durchschnitt grundsätzlich durch einen natürlichen Verbund realisiert werden. Aus diesem Grunde bestand keine große Notwendigkeit für den INTERSECT-Operator. So ist er nicht in SQL-89 enthalten und auch in vielen Systemen nicht verfügbar.

MINUS Für die Differenzbildung ist in SQL der Operator MINUS vorgesehen. Die Mengendifferenz kann grundsätzlich auch durch eine Konstruktion mit NOT EXISTS (...) realisiert werden, sodass der MINUS-Operator nicht unbedingt erforderlich ist. Aufgabe 5.47 Bilden Sie bezüglich der Kunden mit status = 'S' und der Kunden mit ort = 'Kayhude' die mengenmäßige Vereinigung, den Durchschnitt und die Differenz.

5.6 Verarbeitung einer Abfrage Die Bearbeitung einer SQL-Abfrage, ob sie nun von einem menschlichen Benutzer interaktiv eingegeben oder von einem Anwendungsprogramm angestoßen wird, durchläuft mehrere Stationen, die wir anhand der folgenden Grafik erläutern. Das Quadrat links stellt den Initiator der Abfrage dar, an den auch sämtliche Fehlermeldungen des DBMS und das Ergebnis einer ausgeführten Abfrage adressiert werden. Die Kreise symbolisieren Funktionseinheiten, die das DBMS enthält. Die Pfeile stellen Datenflüsse dar, die jeweils einen spezifischen Aufbau haben. Im Falle des SQL-Befehls ist es Text, die symbolische Repräsentation und der Ausführungsplan folgen internen Formaten, die für den Benutzer nicht sichtbar und nicht lesbar sind. Die Fehlermeldung und das Abfrageergebnis können als normaler Text oder in irgendeiner Weise formatiert erscheinen und sind für den Benutzer oder das Anwendungsprogramm lesbar.

Anwender/ Programm

SQL-Befehl

Parsen

Symbolische Repräsentation

Anfrageoptimierung

Ausführungsplan

Abfrageausführung

Fehlermeldung Abfrageergebnis

Abbildung 5.1: An der Verarbeitung einer Abfrage beteiligte Funktionseinheiten des DBMS

279

5 Datenmanipulation in SQL

Parsen Der eingegebene Befehl wird zunächst von einer als »Parser« bezeichneten Funktionseinheit des DBMS auf formale Richtigkeit geprüft. Dazu gehört die Syntax, also die richtige Schreibweise und Anordnung der Befehlswörter wie SELECT, GROUP BY, das Vorhandensein von Kommas oder Leerzeichen an den richtigen Stellen, die konsistente Verwendung von Klammern und anderes mehr. Das Parsen umfasst ebenfalls eine Prüfung daraufhin, ob die Bezeichner für Tabellen und deren Spalten sowie ggf. die verwendeten Funktionen korrekt sind: Hierzu muss das System im Systemkatalog »nachsehen«, wo die Beschreibungen aller Datenbankobjekte wie Tabellen, Spalten, Integritätsregeln, benutzerdefinierte Funktionen etc. abgelegt sind.42 Der Parser gibt entweder eine Fehlermeldung an den Autor des SQLBefehls, oder er gibt diesen in einer symbolischen Repräsentation, also in einer für das DBMS direkt verarbeitbaren Ausdrucksweise, die nicht SQL ist, weiter.

Abfrageoptimierung Die Abfrage wird hinsichtlich möglicher Änderungen im Hinblick auf die effiziente Verarbeitung untersucht und optimiert. Dies geschieht in mehreren Schritten. Der erste ist eine algebraische Optimierung. Nach den Regeln der Relationenalgebra können Abfragen mithilfe gewisser »Rechenregeln« in äquivalente Ausdrücke umgewandelt werden. Äquivalenz bedeutet dabei, dass die Ergebnismenge für jede gegebene Datenbank unabhängig von den jeweils aktuellen Dateninhalten identisch ist. Beispielsweise kann in einem Befehl, der einen Join und eine Selektion enthält, zuerst der Join und dann die Selektion ausgeführt werden oder umgekehrt. Letzteres ist in den meisten Fällen effizienter, wenn damit nämlich die Anzahl der im Join zu verbindenden Tupel vermindert, also die Anzahl der nötigen Speicherzugriffe reduziert wird.43 Auch gibt es bei komplexeren Befehlen oftmals Möglichkeiten, Joins durch Unterabfragen zu ersetzen oder umgekehrt. Auch die Formulierung von Unterabfragen kann beispielsweise durch Verwendung des komplementären Operators (z. B. ANY statt ALL oder EXISTS statt IN) mit entsprechend modifizierter Bedingung unterschiedlich gestaltet werden. Die Optimierung bezieht sich weiterhin auf verschiedene Zugriffsmöglichkeiten, die sich beispielsweise durch das Vorhandensein von Indizes und Clustern ergeben. Schließlich können Statistiken über die Anzahl von Datensätzen in den einzelnen Tabellen und die Selektivität der Auswahlbedingungen (wie viel Prozent der Tupel werden aussortiert) hinzugezogen werden, was man als »kostenbasierte« Optimierung bezeichnet.44 Nach dem Optimierungsprozess liegt ein »Ausführungsplan« in einer für das DBMS direkt interpretierbaren Form vor.

42 Der Systemkatalog wird in Kapitel 8 behandelt. 43 In Kapitel 2.4. haben wir Beispiele für verschiedene äquivalente Formulierungen von Abfragen in einer grafischen Notation angegeben. 44 Eine ausführliche Schilderung des recht komplexen Optimierungsprozesses einschließlich einiger Regeln der algebraischen Optimierung findet sich in [TüSa06 S. 396 ff].

280

Datenänderungen mit mehreren Tabellen

5.7 Datenänderungen mit mehreren Tabellen Wir haben gesehen, dass wir bei der Selektion von Daten mehrere Tabellen in einer Abfrage-Anweisung benutzen können. In ähnlicher Form können wir auch bei Datenänderungen mehrere Tabellen verwenden. So haben wir auf Seite UPDATE gesehen, dass eine Unterabfrage in der SET-Klausel der UPDATE-Anweisung auftreten kann. Ebenso können Unterabfragen in der WHERE-Klausel der UPDATE- oder DELETE-Anweisung auftreten. Wenn wir alle Kunden löschen wollen, die nicht in der Relation Bestellung auftreten, können wir das durch DELETE FROM kunde WHERE kunden_nr NOT IN ( SELECT kunden_nr FROM Bstellung )

erreichen.

5.7.1 Datenübertragung mit INSERT-Anweisung Eine Variante der INSERT-Anweisung erlaubt das Übertragen von Daten aus einer oder mehreren Tabellen, die als Abfrage formuliert sind. Sie folgt der Syntax: INSERT INTO tabelle [(spaltenliste)] abfrage

Diese Form wird beispielsweise benötigt, um Kopien (»Snapshots«) von Tabellen zu erzeugen. Auch bei der Datenübernahme aus anderen Systemen spielt diese Form des Einfügens eine Rolle. Es kann zweckmäßig sein, beispielsweise erst einmal alle Daten in eine unnormalisierte, temporäre Tabelle zu übernehmen und aus dieser dann per Abfrage auf die normalisierten Tabellen zu verteilen. Stellen wir uns hierzu vor, dass die Kundendaten für unsere Versanddatenbank zusammen mit den Bestellungen in einer unnormalisierten Form in einer Datei vorliegen, die folgenden Aufbau hat: kun_bestell (k_nr, name, vorname, strasse, plz, ort, b_nr, bestelldatum, art_nr_1, menge_1, art_nr_2, menge_2, … art_nr_10, menge_10)

281

5 Datenmanipulation in SQL

art_nr1, menge_1 ist immer angegeben, die übrigen art_nr_i und menge_i können auch leer sein. Mehr als zehn Artikel pro Bestellung sind in diesem nicht normalisierten Datenmodell nicht vorgesehen – es werden dann eben entsprechend mehrere Bestellungen erfasst.

In einem ersten Schritt erstellen wir hier eine Tabelle mit dem entsprechenden Aufbau und bringen die Daten irgendwie mit einem Werkzeug oder einem kleinen Programm in die Datenbank. Anschließend können wir die Daten dann in die normalisierten Tabellen bringen durch die folgenden Anweisungen: INSERT INTO kunde (kunden_nr, name, strasse, plz, ort) SELECT DISTINCT k_nr, name || ', ' || vorname, strasse, plz, ort FROM kund_bestell; INSERT INTO bestellung (bestell_nr, kunden_nr, bestelldatum) SELECT DISTINCT b_nr, k_nr, bestelldatum FROM kund_bestell; INSERT INTO bestellposition (bestell_nr, artikel_nr, bestellmenge) SELECT DISTINCT b_nr, art_nr_1, menge_1 FROM kund_bestell; INSERT INTO bestellposition (bestell_nr, artikel_nr, bestellmenge) SELECT DISTINCT b_nr, art_nr_2, menge_2 FROM kund_bestell WHERE art_nr_2 IS NOT NULL; … INSERT INTO bestellposition (bestell_nr, artikel_nr, bestellmenge) SELECT DISTINCT b_nr, art_nr_10, menge_10 FROM kund_bestell WHERE art_nr_10 IS NOT NULL;

Ein kleines Problem bekommen wir bei diesem Ansatz, wenn ein Kunde inzwischen umgezogen und mit zwei verschiedenen Adressen in der Datenbank vorhanden ist. Dieses führt zu einer Verletzung der PRIMARY KEY-Bedingung der Tabelle Kunde, da dann die Unterabfrage denselben Schlüssel mehrfach liefert. Eine weitere Eingabe desselben Schlüssels müsste dann nicht ein INSERT, sondern ein UPDATE bewirken. Diesen Effekt bewirkt die in SQL:2003 eingeführte MERGE-Anweisung, in der eine »Stammtabelle« mit einer »Bewegungstabelle« verknüpft und in Abhängigkeit von zu betrachtenden Schlüsselfeldern entweder ein neuer Satz eingefügt oder ein vorhandener Satz modifiziert wird.

282

Datenänderungen mit mehreren Tabellen

5.7.2 DIE MERGE-Anweisung Die MERGE-Anweisung hat folgende Syntax MERGE INTO stammtabelle [AS alias] [(spaltenliste)] USING bewegungstabelle [AS alias] ON (verbindungsbedingung) [ WHEN MATCHED THEN UPDATE-Anweisung ] [ WHEN NOT MATCHED THEN INSERT-Anweisung]

Mindestens eine der beiden Anweisungen muss vorhanden sein (UPDATE oder INSERT) oder beide. Stammtabelle und Bewegungstabelle kann jeweils eine Tabelle, Datensicht oder aber auch eine eingebettete SELECT-Anweisung45 sein. Unser obiges Problem können wir jetzt wie folgt lösen, indem wir für Kundennummern, die schon eingetragen sind, Daten durch die angegebenen Werte ersetzen. MERGE INTO kunde AS ku USING kun_bestell AS kb ON (ku.kunden_nr = kb.k_nr) WHEN MATCHED THEN UPDATE kunde SET name = kb.name || ', ' || kb.vorname, strasse = kb.strasse, plz = kb.plz, ort = kb.ort WHEN NOT MATCHED THEN INSERT INTO kunde (kunden_nr, name, strasse, plz, ort) VALUES(kb.k_nr, kb.name || ', ' || kb.vorname, kb.strasse, kb.plz, kb.ort)

Mit der MERGE-Anweisung müssen nicht unbedingt die eingegebenen Werte übernommen werden, sie können auch vorhandene Werte modifizieren. Als letztes Beispiel gehen wir davon aus, dass es eine Tabelle Wareneingang gibt mit folgender Spezifikation: Wareneingang (art_nr, bezeichnung, menge)

In diesem Wareneingang sollen Waren, die noch nicht existieren, neu aufgenommen werden, bei den anderen soll der Bestand entsprechend addiert werden.

45 Vgl. hierzu Abschnitt 5.4.

283

5 Datenmanipulation in SQL

MERGE INTO artikel AS ar USING wareneingang AS we ON (ar.artikel_nr = we.art_nr) WHEN MATCHED THEN UPDATE artikel SET bestand = bestand + menge WHEN NOT MATCHED THEN INSERT INTO artikel(artikel_nr, bezeichnung, bestand) VALUES(art_nr, art.bezeichnung, menge)

284

Datensichten in SQL Wir haben in Kapitel 2 die Relationenalgebra eingeführt und in Kapitel 5 die Möglichkeiten von SQL kennengelernt, aus vorhandenen Relationen bzw. Tabellen eine neue Relation bzw. Tabelle zu beschreiben. Eine Datensicht ist eine »abgeleitete Tabelle«. Wie in einem »Ausschnitt-Fenster« können wir darin Informationen aus einer oder mehreren Tabellen sehen und ggf. bearbeiten. In SQL brauchen wir hierfür eine Abfrage-Anweisung, also eine SELECT-Anweisung ohne ORDER BYKlausel. Solche abgeleiteten Tabellen können in SQL auf unterschiedliche Weise für Benutzer zur Verfügung gestellt werden: 1) Virtuelle Datensicht: Bereitstellung der Abfrage-Anweisung zur Ausführung auf den jeweils aktuellen Daten. Dieses ist die »klassische Datensicht«, die es seit Beginn von SQL gibt. Die beiden folgenden Varianten gibt es erst seit Kürzerem, und sie sind noch nicht in jedem SQL-System verfügbar. 2) Snapshot: Ausführung der Abfrageanweisung und Speichern des Ergebnisses in einer neuen Tabelle, die auf dem derzeitigen Datenbestand beruht. Änderungen an den zugrunde liegenden Daten wirken sich nicht mehr auf die Datensicht aus. Snapshots sind neu in SQL:2003. 3) Materialisierte Datensicht: Dieses ist eine Kombination aus 1. und 2. Es wird die Abfrageanweisung zur Verfügung gestellt, die nach Bedarf ausgeführt wird und dann einen Snapshot erzeugt, der in einer Tabelle gespeichert wird. Dieser Snapshot kann beliebig häufig ausgeführt werden. Diese Möglichkeit gibt es seit einigen Jahren in einigen Datenbanksystemen wie Oracle und seit Neuerem in Sybase SQL Anywhere, in anderen Systemen z. T. in abgewandelter Form. Datensichten gehören dem externen Schema des Datenbankkonzepts an. Sie sind Objekte der Datenbank. Insofern sind Datensichtdefinitionen Bestandteil der Datendefinitionssprache (DDL) auf »höherer« Ebene. Sie sorgen für ein großes Stück logischer Datenunabhängigkeit. Der Benutzer braucht die logische Struktur der Datenbank nicht zu kennen, um sinnvolle Datenmanipulationen daran vorzunehmen. Das betrifft insbesondere die genaue Kenntnis der Primär- und Fremdschlüsselrelationen, mit deren Hilfe die Informationen redundanzfrei auf die verschiedenen Tabellen aufgeteilt worden sind. Zusammengehörige Daten, wie der Name und die Anschrift des Kunden, der einen bestimmten Auftrag erteilt hat, lassen sich in Datensichten wieder zusammenführen, sodass es nur für den Benutzer so aussieht, als seien sie zusammen gespeichert.

285

6 Datensichten in SQL

Auch Resultate von Berechnungen werden aus Gründen des logischen Datenbankentwurfs nicht in die Basisrelationen aufgenommen. Die Tupel einer Basistabelle enthalten im Allgemeinen weder Rechenergebnisse, die aus den anderen Attributwerten desselben Tupels ermittelt werden können (wie zum Beispiel durch Multiplikation zweier Spalten), noch kumulative Resultate, die mithilfe von Aggregatfunktionen aus einer Tabelle mit Detailinformationen abgeleitet werden können1. Auch diese Form der redundanten Information in einer Datensicht kann vom Benutzer weiterverwendet werden, sodass sie zum Ausgangspunkt weiterer Abfragen und Datensichten gemacht werden kann.

6.1 Verhalten von Datensichten bei Abfragen Eine Datensicht verhält sich in der Abfrage-Anweisung wie eine reelle Tabelle, auch wenn es nur eine virtuelle Tabelle ist. Es kann sowohl eine Basistabelle als auch eine andere Datensicht zur Grundlage der Datensichtdefinition herangezogen werden. Abfragen an eine Datensicht können genauso formuliert werden wie Abfragen an Basistabellen. Es sind also auch Verbunde mit anderen Tabellen oder Datensichten möglich. Eine Datensicht wird in SQL als View bezeichnet. Betrachten wir zunächst einfache Datensichten, die auf einer einzigen Basistabelle beruhen und nur reelle Spalten daraus enthalten. Solche Datensichten liefern einen echten Ausschnitt aus der Basistabelle, der nur durch Projektion und Selektion zustande kommt. Um eine Datensicht zu definieren, die nur die Adressdaten zeigt, brauchen wir lediglich auf die entsprechenden Spalten in der Tabelle kunde zu projizieren: CREATE VIEW kundadr AS SELECT kunden_nr, name, strasse, plz, ort FROM kunde;

Die Datensicht kundadr verhält sich nun wie eine reelle Tabelle. Die Anweisung SELECT * FROM kundadr ORDER BY kunden_nr

führt dazu, dass die Tupel aus der Kundentabelle so angezeigt werden, als hätten wir die in der Datensichtdefinition angegebene SELECT-Klausel auf die Kundentabelle selbst angewandt.

1 Seit SQL:2003 gibt es jedoch auch die Möglichkeit, abgeleitete Ausdrücke in der Tabellendefinition mit zu berücksichtigen.

286

Verhalten von Datensichten bei Abfragen kunden_nr 100 101 102 103 104 105 106

name Voss, Hans Stein, Peter Berger, Uwe Randers, Nis Andresen, Ute Stuff, Werner Staack, Hannes

strasse Kuhdamm 12 Moordamm 34 Allee 12 b Am Seeufer 12 Am Abhang Tarper Weg An der Alster 1

plz 23863 23863 25813 23845 24558 24853 23863

ort Nienwohld Kayhude Husum Oering Ulzburg Eggebek Kayhude

Datensichten können jedoch auch sehr komplexe Sichten darstellen. So wird durch die folgende Datensicht eine Sicht auf die Rechnungen der abgearbeiteten Lieferungen erzeugt: CREATE VIEW rechnung AS SELECT be.bestell_nr, be.lieferdatum AS datum, po.bestellmenge, po.liefermenge AS menge, po.mwst, po.gesamtpreis AS preis, ku.kunden_nr, ku.name, ku.strasse, ku.plz, ku.ort, ar.artikel_nr, ar.verpackung AS packung, ar.bezeichnung, ar.lagerplatz AS lager FROM bestellung be JOIN kunde ku ON be.kunden_Nr = ku.kunden_nr JOIN position po ON be.bestell_Nr = po.bestell_nr JOIN artikel ar ON po.artikel_Nr = ar.artikel_nr WHERE lieferdatum IS NOT NULL;

Diese Datensicht liefert alle Informationen, die notwendig sind, um eine Rechnung zu drucken. Wir können diese Datensicht in beliebiger Weise auswerten, z. B. durch: SELECT bestell_nr, datum, name, menge, artikel_nr, preis FROM rechnung WHERE ort = 'Kayhude'; bestell_nr 151 151 151 151 151

datum 2008-05-02 2008-05-02 2008-05-02 2008-05-02 2008-05-02

name Stein, Stein, Stein, Stein, Stein,

Peter Peter Peter Peter Peter

menge 4 3 0 1 5

artikel_nr G002 G003 K002 K003 L002

preis 49.80 15.60 0.00 65.70 41.75

Es können natürlich auch Datengruppierungen verwendet werden wie in: SELECT bestell_nr AS b_nr, datum, kunden_nr AS K_Nr, name, strasse, plz, ort, SUM (preis) AS R_Betrag FROM rechnung GROUP BY bestell_nr,datum,kunden_nr,name,strasse,plz,ort; b_nr datum k_nr name strasse plz ort R_Betrag 151 2008-05-02 101 Stein, Peter Moordamm 34 23863 Kayhude 200.67 152 2008-05-02 103 Randers, Nis Am Seeufer 12 23845 Oering 2304.36

287

6 Datensichten in SQL

6.2 Virtuelle Datensicht »Virtuell« bedeutet hier, dass die Datensicht nicht physikalisch als Tabelle, sondern nur für den Benutzer existiert. Die Tupel, die darin erscheinen, sind aus Basistabellen abgeleitet. Diese Ableitung geschieht mithilfe einer geeigneten AbfrageAnweisung, die SQL jedes Mal ausführt, wenn eine Datensicht benutzt wird. Veränderungen in den Werten der Basistabellen spiegeln sich also in den Datensichten unmittelbar wider. Auf diese Weise lassen sich die Informationen so zusammenstellen, wie der Anwender sie für seine Arbeitsaufgabe braucht. Verschiedene Benutzer können vollständig unterschiedliche Sichtweisen auf dieselben Daten haben und gleichzeitig damit arbeiten. Der Datenbestand der Basistabelle kann in einer Datensicht nicht nur passiv genutzt werden. Es ist ebenso möglich, über eine Datensicht unmittelbar die Werte in der Basistabelle zu verändern.

6.2.1 CREATE VIEW Die Syntax der Anweisung für die Datensichtdefinition lautet im SQL-Standard: CREATE VIEW viewname [(spaltenliste)] AS abfrage [WITH [CASCADED | LOCAL] CHECK OPTION]

Dabei ist abfrage eine beliebige Datenabfrage; die ORDER BY-Klausel ist darin nicht zulässig. Die Angabe von Spaltennamen kann entfallen, wenn die Spaltennamen der zugrunde liegenden Abfrage übernommen werden sollen. Die vollständige CREATE VIEW-Anweisung enthält einen optionalen Zusatz WITH CHECK OPTION. Diese Angabe soll für jede Datensicht, auf die Update-Operationen angewendet werden können, sicherstellen, dass das Ergebnis einer solchen Operation nicht der Datensichtdefinition widerspricht. Die Definition einer Datensicht wird in den Systemtabellen gespeichert. Anstelle der Spalten und ihrer Spezifikation wird dort die Abfrage festgehalten, mit der diese Datensicht gebildet wird.

6.2.2 Nutzen von virtuellen Datensichten Es bleibt noch die Frage: »Kann ich mit Datensichten etwas erreichen, was ohne deren Verwendung nicht möglich wäre?« In einem Anwendungsprogramm, das einen SQL-Zugriff auf die Datenbank enthält, kann ja auch schließlich eine beliebig komplexe SQL-Abfrage enthalten sein.

288

Virtuelle Datensicht

Eine sehr wichtige Begründung von Datensichten ist die Möglichkeit, auf Datensichten Zugriffsrechte zu erteilen (vgl. hierzu Kap. 7.2). Wie können einem Benutzer das Leserecht auf eine Datensicht geben, ohne dass Leserechte auf die zugrunde liegenden Tabellen vorhanden sein müssen. So kann mit der Datensicht CREATE VIEW kundenstatistik (plz, ort, anz_kunden, anz_bestellung) AS SELECT k.plz,k.ort, COUNT(DISTINCT k.kunden_nr), COUNT(*) FROM kunde AS k JOIN bestellung AS b ON k.kunden_nr = b.kunden_nr

eine Kunden- und Bestellstatistik definiert werden, die für jede PLZ-Ort-Kombination in der Datenbank die Anzahl der dort lebenden Kunden sowie die Anzahl der Bestellungen auflistet. Wenn ein Mitarbeiter aus der Abteilung »Public Relations« ein Zugriffsrecht auf diese Datensicht bekommt, kann er diese Abfrage jederzeit mit den aktuellen Daten ausführen. Er kann aber nicht auf die einzelnen Kundendaten oder Bestelldaten zugreifen. Ein weiterer Grund für Datensichten ist die Möglichkeit der Datenbank, einen möglichst optimalen Zugriffspfad für die Datensicht in der Datenbank zu speichern. Hierzu können nicht nur statistische Daten vor der Ausführung der Datensicht verwendet werden (Sind indexierte Felder betroffen? Wie viele Sätze haben die beteiligten Tabellen? Wie viele verschiedene Werte haben die Fremdschlüsselattribute, über welche die Verknüpfung erfolgt?). Es können auch die tatsächlichen Zugriffszeiten für den benutzten Ausführungsplan mit den geschätzten Zeiten verglichen und daraus verbesserte Zugriffspläne gespeichert werden. Diese Optimierungen sind nicht möglich, wenn die Abfrage – außerhalb der Datenbank – in einem Anwendungsprogramm gespeichert ist und nur sporadisch als Abfrage an die Datenbank gestellt wird.

6.2.3 Datensichten mit virtuellen Spalten Aus Gründen der Normalisierung werden beim logischen Datenbankentwurf meist keine Spalten in die Basisrelationen aufgenommen, die durch Berechnungen aus vorhandenen Attributwerten einzelner Tupel abzuleiten sind. Dieses ist z. B. der Fall bei dem Wert des Lagerbestands je Artikel. Dieser ergibt sich rechnerisch aus der Multiplikation des Listenpreises mit dem Bestand, hängt also funktional von diesen beiden Werten ab. Dies widerspricht den Forderungen der dritten Normalform. Wenn wir eine Spalte Bestandswert in die Relation artikel aufgenommen hätten, wäre z. B. das praktische Problem entstanden, dass darin Eintragungen möglich sind, die nicht mit dem Ergebnis der Berechnung übereinstimmen. Für die Anwendung stellt der Bestandswert jedoch eine wichtige Information dar. Es mag sogar Anwendungsfälle geben, in denen die abgeleitete Information wichtiger ist als die einzelnen Faktoren. Seit SQL:2003 gibt es die GENERATED BYKlausel (vgl. Kapitel 4.3.5). Mit einer Datensicht lässt sich das gewünschte Ergebnis leicht realisieren; es ist stets reproduzierbar und kann zur Grundlage weiterer Datenmanipulationen gemacht werden.

289

6 Datensichten in SQL

CREATE VIEW artikel_bestandswert AS SELECT artikel_nr, listenpreis, bestand, listenpreis * bestand AS bestandswert FROM artikel

Diese Datensicht enthält jetzt, da sie ohne WHERE-Klausel definiert wurde, alle Zeilen der Basistabelle. Die Spalte bestandswert erscheint wie eine reelle Spalte. Sie kann auch jederzeit über diesen Aliasnamen angesprochen werden. SELECT * FROM artikel_bestandswert; artikel_nr listenpreis bestand G001 38.50 397 G002 12.45 473 G003 5.20 1250 K001 98.50 120 K002 112.80 62 K003 65.70 12 K004 76.00 50 L001 6.35 356 L002 8.35 42 L003 1.78 345 L004 .98 2101 L005 1.72 234

bestandswert 15284.50 5888.85 6500.00 11820.00 6993.60 788.40 3800.00 2260.60 350.70 614.10 2058.98 402.48

6.2.4 Datenmanipulationen an virtuellen Datensichten An Datensichten dürfen auch Datenmanipulationen vorgenommen werden, die eine Veränderung der Werte bewirken. Es gibt jedoch Einschränkungen für die Datenmanipulation. Sie beziehen sich auf die Erlaubnis, Änderungen über eine Datensicht durchzuführen. Es gibt in SQL Restriktionen, für welche Datensichten Änderungsoperationen erlaubt sind. Über die Datensicht kundadr, die oben eingeführt wurde, dürfen grundsätzlich alle Datenmanipulationen durchgeführt werden. Es können Adressen geändert und auch Kunden gelöscht werden. Grundsätzlich kann auch die INSERT-Anweisung benutzt werden wie in: INSERT INTO kundadr (kunden_nr, name, strasse, plz, ort) VALUES (201,'Turvenpää, P.','Moorweg 2','12345','Kayhude');

Allerdings gibt es hier das Problem, dass über diese Datensicht die Spalte status, die obligatorisch ist, keinen Wert bekommen kann. Dies führt zu einem Laufzeitfehler mit einer Meldung wie »column 'status' in 'kunde' cannot be NULL. « Die folgende Datensicht enthält neben der Projektion auch eine Selektion. Der Kundenstamm aus Kayhude soll separat verwaltet werden. Die Datensichtdefinition lautet dann:

290

Virtuelle Datensicht

CREATE VIEW kunden_kayhude AS SELECT kunden_nr, name, strasse, plz, ort FROM kunde WHERE ort = 'Kayhude';

Ebenso wie an der nur projizierenden Datensicht kundadr sind bei kunden_kayhude mit der zusätzlichen Selektion alle SQL-Anweisungen erlaubt, also auch alle Wertveränderungen. Es kann dabei die Situation eintreten, dass Werte in Spalten geändert werden sollen, die in den Selektionskriterien auftreten. Beispielsweise zieht der Kunde mit der Kundennummer 101 nach Bremerhaven um. Die entsprechende UPDATE-Anweisung lautet: UPDATE SET

kunden_kayhude strasse = 'An der Karlstadt 8', plz = '27568', ort = 'Bremerhaven' WHERE kunden_nr=101;

Diese UPDATE-Anweisung bewirkt, dass der Kunde mit der Kundennummer 101 nicht länger in der Datensicht kunden_kayhude sichtbar ist. Die Änderung des Orts widerspricht dem Selektionskriterium, das der Datensicht zugrunde liegt. Bei dem Nutzer einer solchen Datensicht, der nicht weiß und nicht zu wissen braucht, dass seiner Sicht eine anders geartete Datenbanktabelle zugrunde liegt, kann dergleichen zu Verwirrung führen: Die Änderung eines Datensatzes hat denselben Effekt wie eine Löschung! Es kommt sehr auf den Anwendungsfall an, ob so eine UPDATE-Anweisung zulässig sein soll.

CHECK OPTION SQL macht es möglich, Änderungen an einer Datensicht auszuschließen, die einen Widerspruch zur Selektion in der Datensichtdefinition darstellen. Die CREATE VIEW-Anweisung wird dafür nach der Abfrage mit dem Zusatz WITH CHECK OPTION versehen. Um Updates zu verhindern, die der Selektion widersprechen, die in der Datensicht durchgeführt wird, erzeugen wir eine neue Datensicht mit dieser Kontrolloption: CREATE VIEW kunden_kayhude_check AS SELECT kunden_nr, name, strasse, plz, ort FROM kunde WHERE ort = 'Kayhude' WITH CHECK OPTION;

Der Versuch, ein Update wie das oben geschilderte zu veranlassen, wird jetzt von SQL mit einem Hinweis wie dem folgenden zurückgewiesen: »invalid value for column 'ORT' in table 'KUNDE'« oder mit der für den Entwickler etwas deutlicheren Meldung von ORACLE: »view WITH CHECK OPTION where-clause violation«.

291

6 Datensichten in SQL

Der Wirkungskreis der CHECK OPTION bezieht auch die INSERT-Anweisung mit ein. Ein Versuch, eine Zeile einzufügen, die dem Selektionskriterium der Datensichtdefinition widerspricht, wird zurückgewiesen.

6.2.5 Änderbarkeit von Daten über Datensichten Wir betrachten im Folgenden etwas näher die Problematik der Änderungen von Daten über Datensichten. Als Erstes betrachten wir die oben eingeführte Datensicht mit virtuellen Spalten: CREATE VIEW artikel_bestandswert AS SELECT artikel_nr, listenpreis, bestand, listenpreis * bestand AS bestandswert FROM artikel

Wie steht es nun mit der Aktualisierbarkeit dieser Datensicht? Bei der Spalte bestandswert dürfen sicherlich weder UPDATE noch INSERT zugelassen werden. Beim Versuch, eine Zeile zu ändern, gibt das DBMS eine Fehlermeldung aus: »cannot modify column ‘bestandswert’ in table ‘artikel_bestandswert’.« Es wäre ein logischer Widerspruch, wenn in einer Spalte, deren Werte Berechnungsresultate enthalten, Veränderungen vorgenommen werden könnten. Es kann auch kein Tupel mit einem solchen Wert erfasst werden, weil die zugrunde liegende Tabelle diese Spalte ja gar nicht enthält. Gegen ein DELETE spricht aus logischer Sicht allerdings gar nichts. Diese Datensicht enthält die Primärschlüsselspalte artikel_nr, sodass die Tupel in der Basistabelle eindeutig identifiziert werden können. Der SQL-Standard war hier früher recht restriktiv.2 Die Datensicht musste auf einer Tabelle basieren und durfte praktisch nur aus einer einfachen Auswahl und Projektion bestehen, die keine virtuellen Spalten erzeugt. Mittlerweile sind die Kriterien für die Aktualisierbarkeit erheblich erweitert und differenziert worden. Beispielsweise ist es nicht mehr grundsätzlich ausgeschlossen, Daten über Views zu ändern, die auf einem Verbund mehrerer Tabellen beruhen. Wenn die Query einen Schlüssel einer der unterliegenden Basisrelationen enthält und jedem Tupel der Datensicht genau ein Tupel in jener Basisrelation entspricht und wenn gewisse Spalten des View unmittelbar Spalten der Basistabelle entsprechen, ist es möglich, die Werte eben dieser Spalten über die Datensicht zu ändern. Im Ganzen sind die Kriterien recht kompliziert – sie müssen aber insgesamt die logische Bedingung erfüllen, dass die Änderungen in der virtuellen Tabelle auf eindeutige Weise in eine Änderung einer oder mehrerer Basistabellen überführt werden können.

2 Vgl. [DaDa 93; S. 174].

292

Virtuelle Datensicht

INSERT Bei der INSERT-Anweisung ist eine Besonderheit zu beachten. Auch für aktualisierbare Datensichten kann es unmöglich sein, einen neuen Satz einzufügen. Eine Einfügeoperation über eine Datensicht muss auch dann zurückgewiesen werden, wenn die Sicht nicht alle Spalten enthält, die bei der Tabellendefinition als NOT NULL deklariert wurden und für die kein expliziter DEFAULT angegeben wurde (vgl. hierzu das Beispiel auf Seite INSERT_in_View).

DELETE Die Verwendung der DELETE-Anweisung ist bei dieser Art Datensichten sowohl logisch als auch praktisch unproblematisch. Die betreffenden Tupel können über ihren Primärschlüssel eindeutig identifiziert werden. Und bei der Löschung verschwinden die Spalten, die der virtuellen Spalte zugrunde liegen, ebenfalls.

Probleme für Nutzer Je umfassender die Klasse der änderbaren Datensichten ist, desto besser für die logische Datenunabhängigkeit (Kapitel 1), die ja fordert, dass der Nutzer auf der externen Sicht der Datenbank (auf Datensichten) arbeitet und die konzeptionelle Sicht (Basistabellen) nicht zu kennen braucht. Ein Problem liegt bei INSERT und UPDATE auf Datensichten mit virtuellen Spalten allerdings darin, dass der Benutzer unter Umständen gar nicht weiß, warum seine Angaben zu Fehlern führen. Damit sind Datensichten einerseits die Voraussetzung und andererseits zugleich das Hindernis für eine konsequente Trennung der externen Datensicht von der logischen. Lösbar ist die Problematik der Änderungsbeschränkungen auf dem jetzigen Stand von SQL nur durch organisatorische Konventionen für die Arbeit mit der Datenbank. Hilfreich ist dabei auch eine Aufklärung der Nutzer über das logische Schema der Datenbank, sodass sie sich über die Grundlage ihrer Datensicht im Klaren sind und sich von daher manches angebliche »Fehlverhalten« der Datenbank bei der Datenmanipulation erklären können. Zum Abschluss ist zu sagen, dass Datensichten eine geeignete Methode darstellen, um die lesenden Zugriffe auf eine Datenbank – im Zusammenhang mit entsprechenden Zugriffsrechten3 – zu regeln. Die Möglichkeiten dazu sind sehr weitgehend, und die Leserechte können sehr detailliert, bis hin zur Tupel-Ebene, geregelt werden. Dagegen ist der Versuch, die ändernden Zugriffe über Datensichten zu regeln, zum Scheitern verurteilt. Dies geht nur zu einem kleinen Teil. Wer sich mit abstrakten Datentypen befasst hat, weiß, dass für ein sauberes Zugriffsrechtssystem Methoden bzw. Prozeduren zu definieren sind, über welche die Datenbankobjekte manipuliert werden können. Dies behandeln wir in Kapitel 7.3.

3 Zugriffsrechte werden in Kapitel 7.2 behandelt.

293

6 Datensichten in SQL

6.3 Snapshots Bei der Entwicklung einer Datenbank oder beim Portieren von Daten aus einer vorhandenen Datenbank in eine neue stehen wir häufig vor der Aufgabe, vorhandene Daten zu verknüpfen und in eine neue Tabelle zu speichern. Dieses ist schon immer möglich gewesen: Zuerst legen wir die neue Tabelle an, und anschließend füllen wir die Tabelle mit einer INSERT-Anweisung nach folgendem Schema: CREATE TABLE neue_tabelle ( ... ); INSERT INTO neue_tabelle abfrageanweisung

Hier haben wir das Problem, dass wir die neue Tabelle erst vollständig erstellen müssen, bevor wir sie mit Daten füllen. Es wäre ja schön, wenn die Bezeichner und Datentypen der vorhandenen Tabellen bzw. der Abfrageanweisung übernommen werden. Diese Möglichkeit ist seit SQL:2003 in folgender Form gegeben: CREATE TABLE tabellenname [(spaltenliste)] AS ( abfrageanweisung ) [WITH DATA]

Die Angabe WITH DATA gibt an, dass die angelegte Tabelle auch mit den abgeleiteten Daten gefüllt werden soll. Die Daten der Tabelle werden zum Zeitpunkt der Erzeugung übernommen. Spätere Änderungen an den Ursprungsdaten haben keinen Einfluss auf die Daten der neuen Tabelle.

Beispiel: CREATE TABLE Kunde_aus_Kayhude AS SELECT * FROM Kunde WHERE ort = 'Kayhude'

In Sybase ist derselbe Effekt mit einer Erweiterung der SELECT-Anweisung zu erreichen: nach der SELECT-Klausel ist eine INTO-Klausel einzufügen, in welcher der Name der neuen Tabelle anzugeben ist.4

4 Dieses ist etwas unglücklich, da die INTO-Klausel in Embedded SQL oder in Datenbankprozeduren eine etwas andere Bedeutung hat.

294

Materialisierte Datensichten

Der Anwendungsbereich dieser Anweisung liegt bei der Entwicklung oder Portierung einer Datenbank. Im laufenden Betrieb ist diese Anweisung in der Regel nicht so sinnvoll, da wir dort bei geänderten Originaldaten auch die Änderungen an den abgeleiteten Daten sehen wollen. Hierfür benötigen wir die im Folgenden dargestellten materialisierten Datensichten.

6.4 Materialisierte Datensichten Das Konzept der virtuellen Datensichten gibt es schon aus den Anfangstagen der relationalen Datenbanken und von Anbeginn in SQL. Es realisiert schließlich eine Form des externen Schemas der Datenbankarchitektur nach der ANSI-Sparc (vgl. Kap.1.2), die vor der Einführung der relationalen Datenbanken aufgestellt wurde. Eigentlich können wir alle gewünschten Aufgaben mit virtuellen Datensichten lösen. Nun gibt es aber Situationen, wo es aus Gründen der Performanz sinnvoll erscheint, Datensichten in periodischen Abständen zu berechnen und in berechneter Form den Benutzern zur Verfügung zu stellen. Diese Methode wird schon seit längerer Zeit bei sog. Data Warehouses erfolgreich durchgeführt. Die Daten einer materialisierten Datensicht können durch INSERT, UPDATE und DELETE geändert werden – es werden aber dadurch die Ursprungsdaten nicht geändert. Hierbei handelt es sich um sehr große Datenbanken und um Abfragen, die sich auf sehr große Datenmengen beziehen, aber nur relativ kleine Ergebnismengen liefern. Dieses könnte z. B. eine Abfrage sein, die sich auf (fast) alle Sätze bezieht und durch GROUP BY eine Gruppierung auf diesen Sätzen durchführt. Dann entfällt die mehrfache Berechnung der Datensicht. Bei verteilten Datenbanken können materialisierte Datensichten auf einem Server in der Nähe des Clients gespeichert werden, wodurch das Netzwerk entlastet wird. Materialisierte Datensichten waren ursprünglich für SQL:2003 vorgesehen und werden wohl im folgenden Standard erscheinen. Das Anlegen einer materialisierten Datensicht erfolgt praktisch wie ein virtueller View mit der Anweisung (z. B. Sybase SQL Anywhere): CREATE MATERIALIZED VIEW viewname [(spaltenliste)] AS abfrage

Die materialisierte Datensicht wird durch eine weitere Anweisung mit Daten gefüllt: REFRESH MATERIALIZED VIEW viewname [ WITH { ISOLATION LEVEL Isolationsstufe | EXCLUSIVE MODE } ] [ FORCE BUILD ]

295

6 Datensichten in SQL

Isolation-Level werden in Kapitel 7.1.5 eingeführt. Standardmäßig wird vor dem Füllen mit Daten geprüft, ob sich die Daten geändert haben, und im Falle keiner Änderungen werden standardmäßig die Daten nicht neu geladen. Durch Angabe von FORCE BUILD werden die Daten in jedem Fall geladen (z. B. da Sie in der Zwischenzeit möglicherweise die Daten der Datensicht geändert haben). In Oracle gibt es einige weitere Möglichkeiten, sodass dort eine materialisierte Datensicht angelegt werden kann, die alle zwei Stunden aktualisiert wird: CREATE MATERIALIZED VIEW artikelstatistik REFRESH FAST START WITH SYSDATE NEXT SYSDATE + 1/24 AS SELECT artikel_nr, SUM(anzahl) FROM verkauf GROUP BY artikel_nr

Daneben gibt es eine REFRESH-Anweisung, die noch mehr Parameter hat als die oben angegebene. Auch in DB2 gibt es materialisierte Datensichten, dort stellen sie syntaktisch eine Erweiterung der oben eingeführten Snapshots dar.

296

Konsistenz und Mehrbenutzerbetrieb In den bisherigen Kapiteln haben wir das relationale Datenmodell und die entsprechenden Implementierungen jeweils unabhängig von den Bedingungen im Mehrbenutzerbetrieb betrachtet. Das bisher Gesagte reicht im Wesentlichen aus, wenn wir eine Datenbank auf einem Einzelplatzrechner betreiben. In betrieblichen Anwendungen ist jedoch davon auszugehen, dass die Daten unternehmensweit – mindestens jedoch abteilungsweit – organisiert sind und die Nutzer den Zugriff über ein lokales Netzwerk haben. Hierbei ergibt sich das Problem, dass verschiedene Nutzer gleichzeitig auf dieselben Daten zugreifen und sie sogar gleichzeitig ändern. Das Datenbanksystem muss diese Zugriffe so organisieren, dass durch gleichzeitige Zugriffe nicht irgendwelche Tupel in unerwünschter Weise geändert werden. Natürlich gehört zu einer Datenbank im Mehrbenutzerbetrieb eine Benutzerverwaltung. Nicht jeder Benutzer darf alles lesen – das Ändern von Daten ist noch stärkeren Einschränkungen unterworfen. Neben der Gewährleistung der Vertraulichkeit und des Datenschutzes ist in noch stärkerem Maße das Prinzip der Datenkonsistenz wichtig. Hierzu sind Zugriffsrechte und Rollen zu vergeben. Selbst jemand, der Zugriff auf bestimmte Daten (z. B. ein Geldkonto) hat, darf nicht einfach in beliebiger Weise die Daten (z. B. den Kontostand) ändern, sondern muss sich festgelegter Verfahren bedienen (Durchführung einer Überweisung). Hierbei sind gegebenenfalls mehrere Datenänderungen für einen Vorgang nötig (z. B. Speicherung des Buchungsbelegs und Durchführung der Buchung). Dies kann durch Datenbankprozeduren und Trigger erfüllt werden, die in den Kapiteln 7.3.1 und 7.3.3 vorgestellt werden. Zum Abschluss dieses Kapitels fassen wir die verschiedenen Möglichkeiten zusammen, mit denen die Konsistenz einer Datenbank sichergestellt werden kann.

7.1 Transaktionen in Datenbanken Als Transaktionen werden Folgen von Datenmanipulationen bezeichnet, die eine logische Einheit bilden (englisch: logical unit of work). Jede einzelne Operation könnte, für sich betrachtet, die Konsistenz der Datenbank zerstören. Werden sie aber alle ausgeführt, geht die Datenbank wieder in einen konsistenten Zustand über.

297

7 Konsistenz und Mehrbenutzerbetrieb

7.1.1 Eigenschaften von Transaktionen Folgende Merkmale werden mit dem Begriff der Transaktion verbunden: 왘 Atomarität:

Eine Transaktion wird entweder vollständig oder gar nicht, keinesfalls aber nur teilweise ausgeführt. 왘 Konsistenz:

Eine Transaktion überführt eine Datenbank von einem konsistenten Zustand in einen anderen konsistenten Zustand. 왘 Isoliertheit:

Andere gleichzeitig ablaufende Transaktionen haben keinen Einfluss auf das Ergebnis einer aktiven Transaktion. Diese Forderung, die für einen reibungslosen Mehrbenutzerbetrieb entscheidend ist, wird durch die Serialisierbarkeit erreicht. Dies bedeutet, dass mehrere gleichzeitig ablaufende Transaktionen dasselbe Resultat bewirken, als hätten sie nacheinander stattgefunden. 왘 Dauerhaftigkeit:

Nach Beendigung einer Transaktion können die durch sie bewirkten Änderungen in der Datenbank nicht mehr verloren gehen. In Anlehnung an die englischen Begriffe atomicity, consistency, isolation und durability fasst man diese Eigenschaften unter dem Kürzel »ACID« zusammen.

Beispiele Aus irgendeinem Grund soll der Wert von kunden_nr in der Tabelle kunde geändert werden. Wenn zu diesem Kunden Bestellungen existieren, muss konsequenterweise auch in der Tabelle bestellung die Änderung der kunden_nr nachvollzogen werden. Sonst verlieren alle Bestellungen, in denen dieser Wert von kunden_nr als Fremdschlüssel erscheint, ihre Zuordnung. Die Datenbank wäre dann inkonsistent. Durch eine einzige SQL-Anweisung wird nun eine Folge von Datenbankoperationen ausgelöst: UPDATE kunde SET kunden_nr .... ...

Dass hieraus mehrere Update-Aktionen folgen, liegt an der Deklaration der kunden_nr als Fremdschlüssel in bestellung, wobei die Weitergabe aller Wertänderungen des Primärschlüssels an den Fremdschlüssel verlangt wird: FOREIGN KEY (kunden_nr) REFERENCES kunde ON UPDATE CASCADE

Im nächsten Beispiel müssen zwei SQL-Anweisungen ausgeführt werden: Vom Konto 11 werden 100 Euro auf das Konto 22 überwiesen.

298

Transaktionen in Datenbanken

UPDATE konto SET stand = stand + 100 WHERE konto_nr =22

und UPDATE konto SET stand = stand - 100 WHERE konto_nr = 11

Die Transaktionsmerkmale stellen sich an diesem Beispiel wie folgt dar: 왘 Konsistenz: Zwischen der ersten und der zweiten Anweisung ist die Datenbank

in einem inkonsistenten Zustand. Die Bank hat in ihrem Gesamtsaldo 100 Euro zu viel. Nach der Ausführung der gesamten Transaktion ist die Datenbank wieder konsistent. 왘 Atomarität: Die beiden Buchungen werden komplett oder gar nicht ausgeführt. 왘 Dauerhaftigkeit: Nach der zweiten Buchung werden die neuen Kontostände

gespeichert. Die Änderungen können nun nicht mehr verloren gehen. 왘 Isoliertheit/Serialisierbarkeit: Wenn während dieser Transaktion ein zweiter

Buchungsvorgang beginnt, der das Konto 22 betrifft, muss dessen Stand am Ende denselben Wert haben, als wenn die Buchungen nacheinander stattgefunden hätten (in diesem Fall wäre die Reihenfolge beliebig).

Gefahren für die Konsistenz Ursachen für die unvollständige Bearbeitung von Transaktionen können sein: 왘 Programmfehler in Applikationen, die z. B. zu Endlosschleifen mit Überlauf

des Stacks führen 왘 Betriebssystembedingte Fehler, z. B. unerlaubt hoher Verbrauch an Ressourcen

wie Hauptspeicher, Plattenspeicher, Puffer 왘 Zusammenbruch des Datenbanksystems 왘 Hardware-Fehler, z. B. Zerstörung eines Datenträgers 왘 Stromausfall 왘 Der gleichzeitige Zugriff mehrerer Benutzer auf dieselben Daten

Das in solchen Fällen stets entstehende Problem, dass Daten unbrauchbar werden, kann in zwei Varianten auftreten: 왘 Die gesamte Datenbank ist durch einen Hardware-Fehler oder aufgrund eines

nicht reversiblen Betriebssystemfehlers physisch zerstört. In dieser Situation ist nichts anderes möglich als die Wiederherstellung aus einer – hoffentlich nicht weit zurückliegenden – Datensicherung. 왘 Die Datenbank ist nicht physisch zerstört, aber ihr Inhalt ist inkonsistent

geworden, weil eine Reihe zusammenhängender Operationen nicht vollständig ausgeführt wurde.

299

7 Konsistenz und Mehrbenutzerbetrieb

Für Fehler der ersten Kategorie ist die regelmäßige Datensicherung auf einem anderen Medium (z. B. Band) die sicherste Vorsorge. Auf Basis einer solchen Sicherung kann das Transaktionsmanagement eines DBMS unter Umständen einen relativ aktuellen Stand der Datenbank wiederherstellen. Dazu wird ein sogenanntes »After-Image«-Protokoll verwaltet, das die nochmalige Ausführung abgeschlossener Transaktionen ermöglicht. Fehler der zweiten Art können beseitigt werden, indem der Zustand der Daten wiederhergestellt wird, den diese vor Beginn der Transaktion hatten. Hierzu werden »Before-Image«-Protokolle verwendet.

Transaktionsmanagement Die Aufgabe des Transaktionsmanagements eines DBMS ist es, die ordnungsgemäße Beendigung von Transaktionen zu kontrollieren. Dabei muss es Situationen erkennen und bewältigen, welche die Konsistenz der Datenbank gefährden. Falls der erfolgreiche Abschluss einer Transaktion nicht möglich ist, muss es die Datenbank auf einen bekanntermaßen konsistenten Zustand zurückführen, d.h., alle seit Beginn der Transaktion vorgenommenen Einzeländerungen rückgängig machen.

7.1.2 Transaktionen in SQL Der Beginn einer Transaktion wird nach dem Standard auf folgende Weise definiert: 왘 durch die Anweisung

START TRANSACTION optionsliste 1 왘 durch die Beendigung einer zuvor aktiven Transaktion mit einer der Anwei-

sungen COMMIT AND CHAIN ROLLBACK AND CHAIN

durch eine CREATE-Anweisung Transaktionen können nicht »verschachtelt« sein, es kann also innerhalb einer laufenden Transaktion keine weitere Transaktion (etwa wie ein Unterprogramm) gestartet werden. Die Beendigung einer Transaktion ist auf verschiedene Weise möglich. Im Allgemeinen wird ein Transaktionsende durch den Benutzer selbst (respektive durch ein Anwendungsprogramm) explizit angefordert. Dazu stehen die beiden SQLAnweisungen COMMIT und ROLLBACK zur Verfügung. Daneben können Transaktionen implizit beendet werden, worauf wir weiter unten zurückkommen.

1 Die Optionen dieser Anweisung werden in Kapitel 7.1.5 eingeführt.

300

Transaktionen in Datenbanken

Ist der Benutzer sicher, dass alle Operationen richtig ausgeführt wurden, muss er dem DBMS mitteilen, dass die Änderungen in der Datenbank dauerhaft niedergelegt werden sollen. Dies geschieht mit der Anweisung: COMMIT [WORK]

Erst ab diesem Moment sind die geänderten Daten für andere Benutzer sichtbar. Solange eine Transaktion noch nicht abgeschlossen ist, sind alle Änderungen sozusagen »privat«, das heißt nur für den Prozess existent, der sie durchgeführt hat. Dies gilt im Übrigen auch, wenn solche zusammenhängenden Änderungen mit Datenbankprozeduren ausgeführt werden (vgl. Kapitel 7.3.1). Dort kann durch die Angabe BEGIN ATOMIC festgesetzt werden, dass alle innerhalb der Prozedur ausgeführten Schreiboperationen ganz oder gar nicht wirksam werden. Die Anweisung ROLLBACK [WORK]

macht die Änderungen seit Beginn der Transaktion rückgängig. Wurden beispielsweise irrtümlich für alle Artikel die Verkaufspreise um 10 % erhöht, so kann die Wirkung einer solchen Anweisung, z. B.: UPDATE artikel SET listenpreis = listenpreis * 1.1

mit ROLLBACK aufgehoben werden. Die Artikeltabelle enthält dann dieselben Werte in der Spalte listenpreis wie vor der Änderung. Technisch liegt dieser Möglichkeit eine zeitweise Duplizierung der Daten zugrunde. Bei ORACLE beispielsweise werden alle Tupel vor der Änderung in die bereits erwähnte Before-Image-Datei kopiert. Aus dieser werden sie mit ROLLBACK in die Datenbank zurückkopiert. Verschiedene DBMS können über Systemvariablen so konfiguriert werden, dass sie selbst nach jeder UPDATE-, INSERT- oder DELETE-Anweisung automatisch ein COMMIT durchführen. Dies ist in vielen Fällen sinnvoll, wenn nämlich alle Änderungen mit einer einzigen Anweisung erledigt werden. Es hat zudem den Vorteil, dass andere Benutzer möglichst bald mit dem aktualisierten Datenbestand arbeiten können. Sollen aber mehrere Änderungen, Einfügungen oder Löschungen im Zusammenhang betrachtet werden, muss dieses automatische COMMIT unterbunden werden. Die zugehörigen Systemvariablen sind nicht standardisiert. Bei ORACLE heißt die Systemvariable AUTOCOMMIT und wird durch die Anweisung SET AUTOCOMMIT ON | OFF

eingestellt. Bei Sybase heißt sie AUTO_COMMIT, die entsprechende Anweisung sieht wie folgt aus: SET OPTION AUTO_COMMIT ON | OFF

301

7 Konsistenz und Mehrbenutzerbetrieb

Implizites Ende von Transaktionen Wenn einer der oben aufgezählten Fälle von Systemfehlern eintritt, ist keine ordnungsgemäße Beendigung der Transaktion mehr möglich. Wegen der Eigenschaft Atomarität muss das DBMS dann beim nächsten Laden automatisch die Datenbank in den letzten konsistenten Zustand zurückversetzen. Die Aufgabe der Anwender ist es dann, die unvollständig gebliebenen Änderungen noch einmal vorzunehmen.

Transaktionsabschnitte Mit sogenannten »Sicherungspunkten« (englisch »Savepoint«) wird in SQL ein Konzept realisiert, das es erlaubt, im Fehlerfall möglichst viele der bereits erfolgreich ausgeführten Eingaben und Änderungen zu erhalten. Eine Transaktion kann damit in einzelne Abschnitte aufgeteilt werden – mit dem Effekt, dass bei einem Zurücksetzen nicht alle Änderungen seit Beginn der Transaktion rückgängig gemacht werden, sondern nur die, die seit dem letzten oder einem anderen ausgewählten Sicherungspunkt erfolgten. Beispielsweise kann bei einer zeitaufwendigen Änderung über mehrere Tabellen nach jeder UPDATE-Anweisung ein solcher Abschnittspunkt gesetzt werden, um im Fehlerfall nur den Teil der Datenmanipulationen wiederholen zu müssen, bei dem der Fehler aufgetreten ist. Es scheint so, als würde damit die Forderung nach Atomarität verletzt; es ist aber nur ein teilweises Zurücksetzen einer Transaktion, kein teilweises COMMIT möglich. Auch verhindert das Zurücksetzen auf einen Sicherungspunkt nicht, dass eine Transaktion vollständig ungeschehen gemacht wird, wenn sie insgesamt nicht vollständig abgeschlossen werden kann. Mit der Anweisung SAVEPOINT name

wird ein Transaktionsabschnitt definiert. Soll nach einem Fehler die unterbrochene Transaktion an dieser Stelle fortgesetzt werden, so fordert man dies mit der Anweisung ROLLBACK TO SAVEPOINT name

an. Man kann einen zuvor gesetzten Sicherungspunkt auch ohne Rollback aufheben, und zwar mit RELEASE SAVEPOINT name

Die Entscheidung über Zeitpunkte, an denen die Datenbank konsistent ist, ist nicht trivial. Wenn im interaktiven Modus mit SQL oder einer anderen Zugriffsmethode wie Query by Example gearbeitet wird, muss sich der Benutzer darüber klar sein, wann eine zusammenhängende Folge von Manipulationen die Konsistenz seiner Daten gefährdet, und er muss mit SET AUTOCOMMIT OFF, COMMIT und ROLLBACK sowie etwaigen über SAVEPOINT gesetzten Transaktionsabschnitten zweckentsprechend umgehen. Bei interaktiven Applikationen obliegt es dem Designer oder Pro-

302

Transaktionen in Datenbanken

grammierer, durch die explizite Angabe des Beginns und Endes der Transaktionen im Programmcode für die ständige Wiederherstellbarkeit von Konsistenz und Integrität der Datenbank Sorge zu tragen. Dazu gehört auch die Einrichtung von Fehlerbehandlungsroutinen, die ein differenziertes Fortsetzen, Abbrechen oder Wiederholen einer ganz oder teilweise gescheiterten Transaktion vorsehen.

7.1.3 Parallele Ausführung von Transaktionen Wenn durch konkurrierende Zugriffe auf eine Datenbasis parallele Änderungen von Daten durch verschiedene Benutzer stattfinden, ist es Aufgabe des Transaktionsmanagements, mögliche Inkonsistenzen zuverlässig zu verhindern. Dies können unter anderem Sperrkonzepte leisten, welche die gleichzeitige Verfügbarkeit der Daten für alle Benutzer einschränken. Hierzu gehören auch »Nur-Lese-Transaktionen« und das in SQL vorgesehene Konzept der »Isolation-Level«. Die Nebenläufigkeit von Prozessen im Mehrbenutzerbetrieb (»Concurrency«) und die Erhaltung der Datenkonsistenz (»Consistency«) stellen widersprüchliche Anforderungen an ein DBMS. Einerseits ist es eine der Hauptaufgaben, einen gemeinsamen Datenbestand für die Verarbeitung an verschiedenen Orten und durch verschiedene Personen zugleich verfügbar zu machen. Andererseits können durch die gleichzeitige oder fast gleichzeitige Änderung von Daten Konflikte entstehen, welche die Datenbasis inkonsistent und damit unbrauchbar werden lassen. Die Extrempunkte machen den Widerspruch deutlich: Der Standpunkt der Konsistenz würde in radikaler Form realisiert, wenn die gesamte Datenbank jeweils zu einem bestimmten Zeitpunkt nur einem Benutzer zur Verfügung steht. Schrankenlose Konkurrenz würde bedeuten, dass alle Daten zu jedem Zeitpunkt allen Benutzern für Lese- und Schreibzugriffe zur Verfügung stehen. Damit wäre jegliche Kontrolle über gleichzeitige Änderungen unmöglich. Die Aufgabe der »Concurrency Control« ist es, diesen Widerspruch so zu regeln, dass beiden Anforderungen Genüge getan wird. Ein Prinzip der Lösung besteht darin, zu verändernde Daten zeitweise der allgemeinen Verfügbarkeit zu entziehen. Die auf dem Markt angebotenen Datenbanksysteme unterscheiden sich teilweise erheblich darin, wie fein und differenziert die zu sperrende Datenmenge und die Zeitdauer der Sperre festgelegt werden können und welche konkurrierenden, aber nicht die Konsistenz gefährdenden Zugriffe durch andere Benutzer noch möglich sind. Beispielsweise kann sich die Sperre auf einen physikalischen Speicherbereich, eine ganze Tabelle oder auf eine einzelne Zeile beziehen. Die Zeitdauer einer Sperre kann sich auf eine einzelne Schreib- oder Leseoperation, aber auch auf eine längere Transaktion beziehen. Anderen Benutzern kann unter Umständen erlaubt werden, die gesperrten Daten zu lesen. »Optimistische Methoden« gehen hingegen von der Annahme aus, dass gleichzeitige Zugriffe auf Daten recht selten vorkommen, sodass nicht im Vorhinein, sondern erst nachträglich, im Falle eines tatsächlich eingetretenen Konflikts, reagiert werden muss. Zwei Beispiele sollen die Problematik illustrieren.

303

7 Konsistenz und Mehrbenutzerbetrieb

Situation 1: »Lost Update« Wir nehmen an, eine Tabelle kunde enthalte die Spalte umsatz_kumuliert, die nach jeder Rechnungserstellung zu aktualisieren ist. Es ist dafür nötig, den alten Wert von umsatz_kumuliert zu lesen, im Hauptspeicher den neuen Nettobetrag der Rechnung zu addieren und das Resultat in die Kundentabelle zurückzuschreiben. Prozess 1 erhöhe den Ausgangsbetrag von 10.000 auf diese Weise um 2.000. Nachdem der Prozess den alten Betrag gelesen hat (aber bevor er den geänderten Betrag von 12.000 in die Datenbank schreibt), liest der Prozess 2 für dieselbe Zeile der Kundentabelle ebenfalls den Wert 10000 aus umsatz_kumuliert, um den Betrag um 3.000 zu erhöhen. Prozess 1 führt inzwischen die Änderung mit 12.000 durch. Prozess 2 berechnet neu: 10.000 + 3.000 = 13.000 und schreibt diesen Wert in die Datenbank zurück; damit überschreibt er die Änderung von Prozess 1. Resultat: umsatz_kumuliert enthält den Wert 13.000, obwohl zwei Umsätze verbucht wurden und damit der richtige Wert 15.000 hieße. Abbildung 7.1 stellt die beiden Transaktionen und einen möglichen Zeitablauf ohne Transaktionsmanagement dar.

Prozess 1 Prozess 2 Lies Kunde 101 (Umsatz = 10000)

Addiere 2000 zu Umsatz (Umsatz = 12000)

Schreibe Kunde 101 mit Umsatz = 12000

Lies Kunde 101 (Umsatz = 10000)

Addiere 3000 zu Umsatz (Umsatz = 13000)

Schreibe Kunde 101 mit Umsatz = 13000

Abbildung 7.1: Lost Update

Situation 2: Inkonsistente Analyse Wir nehmen an, Benutzer A will die Bestandswerte der Lagerbestände auf Basis der Tabelle artikel auswerten, und zwar für jedes Lager einzeln. Dazu gibt er folgende Anweisung:

304

Transaktionen in Datenbanken

SELECT lagerplatz, sum(listenpreis * bestand) FROM artikel GROUP BY lagerplatz

Während das Datenbanksystem den Wert für Lager 2 bereits ermittelt hat und nun die Berechnungen für das Lager 4 vornimmt, möchte ein zweiter Benutzer einen Artikel aus Lager 2 nach Lager 4 verlegen. Hierzu gibt er folgende Anweisung UPDATE artikel SET lagerplatz = 4 WHERE artikel_nr = 'L005'

ein. In der Gesamtsumme ist nun der Bestandswert des Artikels mit der Artikelnummer L005 zweimal enthalten, da er einmal als Teil von Lager 2 und danach noch einmal als Teil von Lager 4 behandelt wurde.

7.1.4 Sperrmechanismen Die einfachste Lösung bestünde darin, die zu verändernde Tabelle vollständig für andere Benutzer zu sperren und erst nach der Beendigung der Arbeit wieder freizugeben. Dieses Verfahren ist sehr radikal – es läuft auf eine zeitweise Aussetzung des Mehrbenutzerbetriebs hinaus – und würde zu völlig unakzeptablen Wartezeiten bei der gemeinsamen Nutzung einer Datenbank führen. Es ist aber für die Gewährleistung der Konsistenz lediglich erforderlich, dass kollidierende Zugriffe auf die gerade zu verändernden Objekte verhindert werden. In Situation 1 wäre es sinnvoll, dass Prozess 1 die zu aktualisierende Zeile in der Tabelle kunde mit einer Sperre belegt, sodass sie ihm für die Zeitdauer seiner Transaktion exklusiv zur Verfügung steht. Prozess 2 müsste dann so lange warten, bis Prozess 1 sein COMMIT zur Beendigung der Transaktion ausgeführt hat, und könnte dann erst die – nun schon aktualisierten – Daten lesen und verändern. Prozess 2 könnte aber inzwischen einen anderen Kunden bearbeiten, ohne dabei in Konflikt mit Prozess 1 zu geraten. Eine Sperre für Situation 2 müsste wie folgt wirken: Während A seine Salden bildet, müsste jegliche Veränderung an den Werten der saldierten Spalte für die gesamte Tabelle artikel verhindert werden. Welche Operationen wären ohne Schaden von anderen Benutzern ausführbar? Unproblematisch ist das gleichzeitige Lesen der Tabelle. Auch Veränderungen an Werten (Updates) könnten vorgenommen werden, sofern die Spalten, die für die Auswertung von A gerade relevant sind, nicht verändert werden. Verhindert werden müssen daher nur Änderungsoperationen, bei denen die Spalten lagerplatz, listenpreis und bestand im Wert verändert werden. Die Änderung der Artikelbezeichnung wäre hingegen unproblematisch. Die dargestellten Sperrmöglichkeiten ergeben sich aus der logischen Sicht der Dinge. Je feiner die »Granularität« der Sperrung, das heißt, je begrenzter die Anzahl der von der Sperrung betroffenen Datenbankobjekte ist, desto geringer

305

7 Konsistenz und Mehrbenutzerbetrieb

sind die Einschränkungen konkurrierender Zugriffe. Umso höher ist aber der Verwaltungsaufwand, den das DBMS zu leisten hat. Das bedeutet sowohl Ressourcenverbrauch (Hauptspeicher und Plattenzugriffe) als auch merkbare Einbußen der Performanz, sodass alle praktisch vorhandenen Lösungen auf Kompromisse hinauslaufen. Dabei muss aber zuverlässig gewährleistet werden, dass die Granularität der Sperrung mindestens so groß ist, dass sie alle von einer Transaktion benötigten Datenobjekte umschließt.

Sperrbare Objekte Bei einer relationalen Datenbank sind Relationen und Tupel die Objekte von Datenmanipulationen, also auch Kandidaten für Sperrungen. Theoretisch sinnvoll wäre auch noch die nächstfeinere Granularität, nämlich die Sperrung lediglich einer Spalte eines Tupels. Da sich die Sperrung von Daten auf der physikalischen Schicht eines Datenbanksystems abspielt, ist aber nicht jede Granularität mit vertretbarem Aufwand realisierbar. In der Realität finden sich außer Sperrmöglichkeiten für Tabellen oder Tupel auch noch Zwischenformen, bei denen größere Speichereinheiten (Seiten) im Ganzen dem allgemeinen Zugriff zeitweise entzogen werden. Davon sind dann alle Tupel betroffen, die physikalisch auf derselben Seite abgelegt sind.

Typen von Sperren Zwei Haupttypen von Sperren (englisch »locks«) sind zu unterscheiden (wir gebrauchen hier die englischen Begriffe): 왘 »exclusive locks« (X-Lock) 왘 »shared locks« (S-Lock)

S-Locks erlauben die gleichzeitige Bearbeitung desselben Objekts durch eine Transaktion, die ebenfalls ein S-Lock anfordert. X-Locks entziehen das Objekt dem Zugriff jeglicher anderen Transaktion. Objekte können Zeilen einer Tabelle oder ganze Tabellen sein. Das Setzen von Sperren sollte für den Benutzer transparent sein und ohne sein Zutun erfolgen, wenn die Konsistenz der Datenbank dies bei Schreib- oder Lesevorgängen erfordert. Das DBMS setzt also implizite Sperren unmittelbar vor jedem Zugriff auf ein Datenobjekt. Der Zugriff kann erst dann erfolgen, wenn die Sperre erfolgreich gesetzt werden konnte. 왘 Jede lesende Transaktion (SELECT) beantragt ein S-Lock auf die betreffenden

Objekte. 왘 Jede schreibende Transaktion (UPDATE, INSERT, DELETE) beantragt ein X-Lock auf

die betreffenden Objekte. Es sind folgende Fälle zu unterscheiden, wobei wir von einer Transaktion (A) ausgehen, die eine Sperre angefordert hat, während eine zweite Transaktion (B) versucht, ebenfalls eine Sperre für dasselbe Objekt anzufordern:

306

Transaktionen in Datenbanken

1) A hat S-Lock gesetzt: • B kann ebenfalls ein S-Lock setzen und den Zugriff ausführen. • B kann kein X-Lock setzen. Der beabsichtigte Zugriff kann nicht ausgeführt werden. 2) A hat ein X-Lock gesetzt: • B kann weder ein S-Lock noch ein X-Lock auf dasselbe Objekt setzen. Beide Typen von Sperren werden jeweils am Ende der Transaktion, in der sie angefordert worden sind, gelöscht – im Allgemeinen also durch COMMIT oder ROLLBACK. Aber auch jede andere Art der Beendigung (LOGOUT, EXIT, Systemabsturz etc.) von Transaktionen sollte gesetzte Sperren aufheben, wenngleich dies nicht bei allen SQL-Implementierungen der Fall ist.

Die Lösung schafft neue Probleme Betrachten wir die oben geschilderten Problemfälle noch einmal. Im ersten Beispiel löst das Lesen eines Tupels der Tabelle kunde durch Prozess 1 ein S-Lock aus. Prozess 2 kann ebenfalls das Tupel lesen und setzt gleichfalls ein S-Lock. Nun will Prozess 1 den geänderten Satz zurückschreiben, fordert also ein X-Lock an. Dies kann nicht gewährt werden, da Prozess 2 ein S-Lock hält, Prozess 1 muss also warten. In derselben Lage befindet sich aber auch Prozess 2. Auch hier ist kein X-Lock mehr möglich, da Prozess 1 das S-Lock hält. Beide Transaktionen warten also wechselseitig aufeinander, keine kann mit COMMIT beendet werden. Zwar ist nun das LostUpdate-Problem umgangen, aber ein neues Problem geschaffen: das sogenannte »Deadlock«. Dazu im Folgenden mehr. Auch im zweiten Fall reicht das implizite Setzen eines S-Locks durch die lesende Transaktion A nicht unbedingt aus. Zwar kann während des Lesevorgangs keine Änderung durch B erfolgen, denn diese würde ein X-Lock erfordern. Aber in der Pause zwischen den beiden Abfragen ist das S-Lock von A aufgehoben, und B kann aktiv werden. Hier dürfte aber zwischen zwei Lesevorgängen keine Veränderung der Daten erfolgen, damit die Analyse konsistent bleibt. Außerdem erfordert dieser Fall, dass die Sperrung auf die gesamte Tabelle wirkt und nicht nur auf einzelne Tupel, die gerade bearbeitet werden.

Zusätzliche explizite Sperrmöglichkeiten sind notwendig Es bedarf also in der Praxis zusätzlicher Möglichkeiten, um auf die Sperrung Einfluss zu nehmen. In Situation 1 (»lost update«) müsste jede der beteiligten Transaktionen bereits beim Lesen vorsorglich eine exklusive Sperre für die betreffende Zeile anfordern. B würde dann schon vor dem Lesevorgang in den Wartezustand gesetzt und könnte erst dann fortfahren, wenn A mit COMMIT die Transaktion beendet hat. Diese vorsorgliche Sperre muss im Allgemeinen aber explizit durch den Benutzer oder das Anwendungsprogramm erfolgen, da das DBMS bei einem Lesevorgang innerhalb

307

7 Konsistenz und Mehrbenutzerbetrieb

einer Dialoganwendung ja noch nicht »wissen« kann, ob diesem ein Schreibvorgang folgt. In unserer obigen Formulierung könnte allerdings die Notwendigkeit eines X-Locks vom DBMS selbst noch erkannt werden, da mit der SQL-Anweisung UPDATE immer Lese- und Schreibvorgänge verbunden sind. Situation 2 erfordert die explizite Anforderung einer exklusiven Sperre durch A, auch wenn es sich »nur« um eine lesende Transaktion handelt.

Optimistische Sperrmethoden Bei optimistischen Kontrollmethoden wird davon ausgegangen, dass die Wahrscheinlichkeit auftretender Konflikte relativ gering ist, sodass auf die vorsorgliche Sperre ganzer Tabellen oder einzelner Reihen verzichtet wird. Erst im Konfliktfall werden dann Maßnahmen ergriffen, welche die Konsistenzerhaltung gewährleisten. Dabei geht es dann im Wesentlichen um ein kontrolliertes Zurücksetzen von Änderungen, verbunden mit einer automatischen Wiederholung der Operationen zu einer anderen Zeit.

7.1.5 Isolation-Level in Transaktionen Der Standard definiert zur Steuerung von Transaktionen sogenannte »IsolationLevel«. Dabei wird dem Datenbanksystem mitgeteilt, welche möglichen Wirkungen nebenläufiger Transaktionen zulässig sind bzw. ausgeschlossen werden sollen. Es wird dem Benutzer von SQL also keine »technische« Spezifikation der erforderlichen Sperre unter Benutzung der Begriffe S-Lock und X-Lock abverlangt – das DBMS muss selbst den Typ von Sperre ermitteln, der die Benutzeranforderungen realisiert. Folgende Anweisung stellt SQL zur Verfügung, um vor einer Transaktion deren Eigenschaften festzulegen: SET TRANSACTION optionsliste

Dieselbe Optionsliste kann auch als Parameter der Anweisung START TRANSACTION

übergeben werden. Die Optionsliste umfasst einen Zugangsmodus (»access mode«) und den IsolationLevel. Der Zugangsmodus wird mit READ ONLY oder READ WRITE angegeben und legt fest, ob die Transaktion nur Lesevorgänge enthält oder auch Schreibvorgänge. Im ersten Fall sind Änderungen auf temporäre Tabellen beschränkt. Für den Isolation-Level können folgende Werte eingetragen werden: isolation level ::=

308

READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE

Transaktionen in Datenbanken

Die strengste Anforderung wird mit SERIALIZABLE spezifiziert. Nur sie gewährleistet die volle Erfüllung aller Eigenschaften einer Transaktion, die mit dem ACIDPrinzip umschrieben werden. Die anderen Werte definieren, welche Abweichungen davon zulässig sind. Wir bezeichnen mit A und B zwei nebenläufige Transaktionen. 왘 READ UNCOMMITTED

Hier kann A Tupel lesen, die von einer zweiten Transaktion B geändert werden, auch wenn B diese Änderungen noch nicht durch COMMIT dauerhaft an die Datenbank übergeben hat oder sie sogar mit ROLLBACK zurücknimmt. Dies wird auch mit dem Begriff »dirty read« umschrieben. 왘 READ COMMITTED

Bei dieser Variante ist das dirty read ausgeschlossen. A sieht nur solche Daten, die von B per COMMIT an die Datenbank übergeben werden. Es ist dann aber möglich, dass zwei identische Lesevorgänge innerhalb von A für dasselbe Tupel zu unterschiedlichen Ergebnissen führen. Der Lesevorgang innerhalb von A ist also nicht beliebig wiederholbar (»non repeatable read«). 왘 REPEATABLE READ

Letzteres ist bei diesem Isolation-Level ausgeschlossen. Es kann aber noch der Fall eintreten, dass B ein neues Tupel in die von A gelesene Tabelle einfügt, sodass ein zweiter Lesevorgang innerhalb A ein Tupel findet, das vorher nicht vorhanden war (»phantom read«). Eine Übersicht über die Isolation-Level und die von ihnen erlaubten Verstöße gegen das ACID-Prinzip gibt Tabelle 7.1 wieder:2 Isolation-Level

dirty read

nonrepeatable read

Phantom

READ UNCOMMITTED

J

J

J

READ COMMITTED

N

J

J

REPEATABLE READ

N

N

J

SERIALIZABLE

N

N

N

Tabelle 7.1: Definition der Isolation-Level

Das Lost-Update-Problem wäre bereits mit dem Isolation-Level REPEATABLE READ gelöst. Zur Vermeidung der inkonsistenten Analyse muss der Isolation-Level SERIALIZABLE angefordert werden. Dabei kann aber der Zugriffsmodus mit READ ONLY beschränkt werden, sodass andere, nur lesende Transaktionen zugleich stattfinden können, ohne die Serialisierbarkeit zu gefährden.

2 Vgl. [DaDa90 S. 56].

309

7 Konsistenz und Mehrbenutzerbetrieb

Bei Adaptive Server Anywhere werden die Isolation-Level nahe an der Standardvorgabe mit der Anweisung SET OPTION ISOLATION_LEVEL = zahl

eingestellt, wobei für zahl die Werte 0 bis 3 für die Einstellungen READ UNCOMMITTED bis SERIALIZABLE anzugeben sind. Bei ORACLE muss der Benutzer selbst dafür sorgen, dass die entsprechenden Resultate erreicht werden, indem er mit der Anweisung LOCK TABLE explizit Sperren setzt.

7.1.6 Explizite Sperrung mit LOCK TABLE Die explizite Anforderung einer Sperre mit der Anweisung LOCK TABLE ist im Standard nicht vorgesehen. Es gibt sie aber beispielsweise bei ORACLE. Die Anweisung erfordert einen direkten Bezug des Benutzers auf die Sperrtypen S bzw. X und das zu sperrende Objekt (Granularität). Die Syntax lautet in etwas vereinfachter Form: LOCK TABLE tabellenliste IN lockmodus MODE

Folgende Lockmodi werden angeboten: 왘 EXCLUSIVE

erlaubt nur Abfragen an die Tabelle durch andere Benutzer. Jegliche andere Aktivität (UDPATE, DELETE, INSERT) wird unterbunden. Es können auch keine anderen Sperren gleichzeitig existieren. 왘 SHARE

erlaubt Abfragen und andere S-Locks, aber keine Datenänderungen und exklusiven Sperren. Die Daten in der Tabelle können nicht verändert werden. Die Wirkung einer Sperre kann auf einzelne Tupel eingeschränkt werden, wenn der Zusatz ROW angebracht wird. 왘 ROW SHARE (oder SHARE UPDATE):

erlaubt den konkurrierenden Zugriff und die gleichzeitige Änderung anderer Zeilen durch andere Benutzer. Es verhindert jegliche exklusive Sperre auf die gesamte Tabelle. Mehrere Sperren dieses Typs können zugleich existieren. 왘 ROW EXCLUSIVE:

arbeitet wie ROW SHARE, verbietet aber auch gleichzeitige Sperren auf die gesamte Tabelle im Share-Modus. Mehrere Sperren dieses Typs können gleichzeitig und neben ROW SHARE bestehen. 왘 SHARE ROW EXCLUSIVE:

Hier sind gleichzeitig lediglich Sperren im Modus ROW SHARE erlaubt. Der Modus ROW SHARE ist im Hinblick auf eine möglichst geringe Behinderung anderer Transaktionen der effektivste. Wir hatten für die Bewältigung der Situation 1 (lost update) erwähnt, dass eine vorsorgliche Anmeldung einer Exklusivsperre bereits beim Aufsuchen der betreffenden Zeile aus der Datenbank erfor-

310

Transaktionen in Datenbanken

derlich wäre. Dies ist durch eine besondere Anweisung möglich, welche die zu ändernden Datensätze reserviert. Sie sieht wie folgt aus: SELECT spaltenliste FROM tabellenausdruck [WHERE bedingung] FOR UPDATE OF spaltenliste

Das Phänomen dirty read wird übrigens bei ORACLE dadurch umgangen, dass alle Lesevorgänge auf die Before-Image-Datei umgeleitet werden, soweit sie Daten betreffen, die von gerade laufenden Transaktionen verändert werden.

7.1.7 Deadlock Wenn mehrere Transaktionen wechselseitig auf die Freigabe von Ressourcen warten, spricht man von einer Verklemmung oder einem Deadlock. Wir haben bereits oben ein Beispiel für ein Deadlock erwähnt. Es kommt dadurch zustande, dass (mindestens zwei) Transaktionen zyklisch jeweils auf die Freigabe einer Ressource durch eine andere warten, um selbst mit COMMIT abschließen zu können. Da aber erst mit COMMIT oder ROLLBACK die gesetzten Sperren aufgehoben werden, liegt ein Widerspruch vor, der nur durch Abbruch einer der beteiligten Transaktionen aufgehoben werden kann. Eine ebenso typische Situation zeigt folgendes Szenario, bei dem zwei Tabellen betroffen sind: Prozess A

Prozess B

LOCK TABLE kunde IN EXCLUSIVE MODE;

LOCK TABLE auftrag IN EXCLUSIVE MODE;

...

...

LOCK TABLE auftrag

LOCK TABLE kunde

Prozess A muss auf die Freigabe von auftrag warten, während B auf die Freigabe von kunde durch A warten muss. Beide Transaktionen versperren sich gegenseitig den Zugriff auf Ressourcen.

Automatische Deadlock-Behandlung Es gehört zu den Aufgaben eines DBMS, solche Deadlock-Situationen zu erkennen und einige Sperren zu löschen, damit wenigstens eine der Transaktionen abgeschlossen werden kann. Das Mindeste ist ein automatisches Rollback gegen eine der beteiligten Transaktionen und eine entsprechende Mitteilung an den Benutzer bzw. das Anwendungsprogramm. Von diesem Rollback sollte nach Möglichkeit diejenige Transaktion betroffen sein, die noch am wenigsten fortgeschritten, also am leichtesten zu wiederholen ist.

311

7 Konsistenz und Mehrbenutzerbetrieb

7.2 Zugriffsrechte und Rollen SQL regelt den Zugang zu einer Datenbank, zu einzelnen Tabellen und sogar zu einzelnen Spalten sehr restriktiv. Im Prinzip gilt: Niemand darf Daten ansehen oder verändern, wenn er nicht ausdrücklich dazu befugt ist. Der Zugriff auf die Datenbank überhaupt und auf die in ihr enthaltenen Daten gilt als »Privileg« und wird bei SQL auch so bezeichnet. Zunächst muss jeder, der eine SQL-Datenbank nutzen will, dieser namentlich »bekannt« sein. In einer Anmeldeprozedur wird jeweils eine Benutzeridentität abgefragt und mit den Einträgen in speziellen Systemtabellen verglichen. Danach entscheidet sich dann, welche Objekte dem Benutzer zur Verfügung stehen und welche Operationen er darauf ausführen darf. Die Benutzeridentität ist ein Bezeichner; sie kann identisch oder nicht identisch mit der Benutzeridentität für das Betriebssystem sein. Eine Person kann durchaus über verschiedene Bezeichner verfügen, mit denen sie sich beim System anmeldet. Umgekehrt kann eine Benutzeridentität von mehreren Personen verwendet werden. Wenn wir im Folgenden vom Benutzer sprechen, ist damit eine dem System bekannte Benutzeridentität gemeint. Privilegien sind auf zwei verschiedenen Ebenen angesiedelt: 왘 Der Zugang zur Datenbank überhaupt ist durch allgemeine Zugangsprivile-

gien geregelt. 왘 Die Rechte zur Manipulation einzelner Tabellen und anderer Objekte werden

für jeden Benutzer mit objektbezogenen Privilegien verwaltet.

7.2.1 Allgemeine Zugangsprivilegien Die allgemeinen Zugangsprivilegien sind herstellerabhängig realisiert und nicht Gegenstand des SQL-Standards. Der Standard geht davon aus, dass jeder Benutzer der Datenbank mit einem Benutzernamen und Passwort ausgestattet ist und dass das Datenbanksystem Benutzern, die keine derartige Autorisierung vorweisen können, jeglichen Zugriff auf alle seine Funktionen verweigert. Unter Umständen sind die allgemeinen Zugangsrechte feiner differenziert, beispielsweise in das Recht, die Datenbank abzufragen, und das Recht, eigene Tabellen, Datensichten und andere Objekte anzulegen. Meist gibt es eine Unterscheidung zwischen normalen Benutzern und Datenbankadministratoren.

312

Zugriffsrechte und Rollen

Datenbankadministrator Der Datenbankadministrator darf beispielsweise:3 왘 die von allen anderen Benutzern eingerichteten Datenbankobjekte konsultieren

und jede beliebige SQL-Anweisung darauf anwenden 왘 Benutzerprivilegien erteilen und widerrufen 왘 allgemeine Synonyme (PUBLIC SYNONYM) vergeben 왘 in die physikalische Speicherorganisation eingreifen, beispielsweise Partitionen

einrichten und ändern 왘 den Zugang zur Datenbank und zu allen Tabellen jederzeit kontrollieren 왘 die gesamte Datenbank zu Sicherungs- oder Übertragungszwecken als

Betriebssystemdatei exportieren Die genannten Privilegien sind notwendig, um eine Datenbank einzurichten, zu optimieren und den laufenden Betrieb zu betreuen. Die damit gegebene Freiheit ist aber für die normale, alltägliche Datenverwaltung nicht erwünscht. Deshalb ist es üblich, das DBA-Privileg einer einzelnen Person oder – in größeren Organisationen – wenigen Personen zu erteilen, die zudem meist zu größter Geheimhaltung verpflichtet sind, da sie alle in der Datenbank gespeicherten Informationen einsehen können. Unter Umständen kann das Privileg auch geteilt werden, sodass keiner der Datenbankadministratoren die gesamte Datenbank überblicken kann.

7.2.2 Objektbezogene Privilegien Jedes Datenbankobjekt – dazu gehören Tabellen und Datensichten und ihre Spalten, Domänen und Integritätsbedingungen, Zeichensätze und Übersetzungstabellen, Typen, Trigger und Prozeduren – hat einen Eigentümer. Das ist prinzipiell der Benutzer, der es angelegt hat, beziehungsweise der Eigentümer des Schemas, in welches das Objekt eingebettet ist.4 Da SQL grundsätzlich jedermann den Zugang zu allen Objekten verbietet, kann zunächst nur der Eigentümer selbst mit seinen Objekten arbeiten. Will er anderen das Lesen oder Verändern seiner Tabellen erlauben, muss er dies explizit tun. Es gibt zwei Formen der GRANT-Anweisung: GRANT privilegienliste | ALL ON TABLE tabelle TO userliste | PUBLIC [ WITH GRANT OPTION]

und 3 So bei ORACLE. 4 Ein Benutzer kann mehrere Schemata besitzen, aber jedes Schema hat genau einen Eigentümer. Erläuterungen zu CREATE SCHEMA finden Sie in Kapitel 4.2.

313

7 Konsistenz und Mehrbenutzerbetrieb

GRANT USAGE ON objekt TO userliste | PUBLIC [ WITH GRANT OPTION]

Die Objektart TABLE umfasst gleichermaßen Basistabellen und Datensichten. Die Objekte, für die Privilegien erteilt werden, werden wie folgt spezifiziert:5 objekt ::= [ TABLE ] tabelle | DOMAIN domäne | COLLATION collation | CHARACTER SET character set name| TRANSLATION translation | NULL CLASS null class name | DATA TYPE benutzer-datentyp | DATA TYPE distinct-typ | MODULE modul | TYPE TEMPLATE type-template

Folgende Privilegien für Tabellen und Datensichten sind möglich: Privileg

Bedeutung

SELECT[ (spaltenliste) ]

Lesen aller Spalten einer Tabelle oder, wenn angegeben, bestimmter Spalten.

INSERT [ (spaltenliste) ]

Einfügen von neuen Zeilen in eine Tabelle, die Spalten können dabei durch die Spaltenliste eingeschränkt werden. Nicht zugängliche Spalten werden mit dem Default-Wert gefüllt.

UPDATE [ (spaltenliste) ]

Ändern der Daten in einer Tabelle, gegebenenfalls eingeschränkt auf bestimmte Spalten.

DELETE

Löschen von Zeilen aus einer Tabelle.

REFERENCES [(spaltenliste) ]

Benutzung von Spalten der Tabelle in einer FOREIGN KEY-Klausel in einer anderen CREATE TABLE-Anweisung.

Tabelle 7.2: Privilegien in SQL

Für Objekte, die keine Datensichten oder Tabellen sind, können andere, typspezifische Privilegien zugeteilt werden, z. B. USAGE für die Nutzung eines Zeichensatzes, UNDER für die Nutzung eines benutzerdefinierten Typs zur Tabellendefinition (vgl.

5 Die Objektart COLLATION bezieht sich auf eine Zeichensatzordnung. TRANSLATION bewirkt den Austausch bestimmter Zeichen nach einem namentlich definierten Muster. Beide Konzepte können im Rahmen internationaler, verteilter Datenbanken von Bedeutung sein.

314

Zugriffsrechte und Rollen

Kapitel 9), TRIGGER für das Anlegen von Triggern, die mit Tabellen verbunden sind (Kapitel 7.3), EXECUTE für den Aufruf gespeicherter Prozeduren oder benutzerdefinierter Funktionen. Das Privileg bezeichnet jeweils den Typ von SQL-Operationen, der dem Benutzer erlaubt wird. Mit ALL werden alle Privilegien für ein Objekt auf einmal erteilt. Hat Christa, die Besitzerin der Tabelle kunde, beispielsweise dem Benutzer hans den Zugriff auf die Tabelle mit folgender Anweisung gestattet: GRANT SELECT, UPDATE, INSERT ON kunde TO hans

so darf hans alle vorhandenen Informationen aus Christas Kundentabelle mit SELECT abrufen. Dabei muss er dem Tabellennamen den Namen des Eigners voranstellen, in diesem Fall zum Beispiel: SELECT * FROM christa.kunde

Er darf außerdem neue Kunden mit INSERT erfassen und vorhandene Daten, z. B. eine Adresse, mit UPDATE ändern. Er darf aber nicht Kunden löschen (DELETE) oder die Kundentabelle in ihrer Struktur verändern6. Hätte man statt des Benutzers hans alle Benutzer zugelassen (PUBLIC), so dürfte jeder, der Zugang zur Datenbank hat, die Anweisungen SELECT, UPDATE, INSERT auf die Tabelle kunde anwenden. Die DROP-Anweisung taucht in der obigen Liste nicht auf. Grundsätzlich ist es nur dem Eigentümer der Tabelle selbst bzw. dem DBA erlaubt, eine komplette Tabellenstruktur zu löschen. Die GRANT-Option gestattet es einem privilegierten Benutzer, seine eigenen Rechte (und nur diese) an einem Datenbankobjekt weiterzugeben.

7.2.3 Rücknahme von Privilegien Die Rücknahme von Privilegien geschieht mit der REVOKE-Anweisung. Die Syntax lautet in diesem Fall: REVOKE [GRANT OPTION FOR] privilegienliste ON objekt FROM userliste | PUBLIC. [ RESTRICT | CASCADE ]

6 Hierfür gibt es in einigen Datenbanksystemen, z. B. in ORACLE, die Möglichkeit, das ALTERPrivileg zum Ändern der Tabellendefinition und das INDEX-Privileg zum Anlegen von Indizes zu vergeben.

315

7 Konsistenz und Mehrbenutzerbetrieb

Beispiel: REVOKE ON FROM

ALL kunde hans

Wird nur die GRANT-Option zurückgenommen, so kann der Benutzer weiterhin seine bisherigen Rechte auf die Tabelle ausüben, lediglich die Weitergabe an andere Benutzer wird künftig unterbunden. Der optionale Zusatz RESTRICT bedeutet, dass die REVOKE-Anweisung nicht durchgeführt wird, wenn der betreffende Nutzer seine Privilegien an andere weitergegeben hat. Durch Angabe von CASCADE werden auch alle weitergegebenen Privilegien zurückgenommen.

7.2.4 Rollen und Gruppen Die Verwaltung von Benutzerrechten kann in größeren Organisationen recht aufwendig sein. Unter Umständen müssen ganzen Gruppen von Benutzern identische Privilegien für eine Vielzahl von Tabellen zugeteilt werden. Um dies zu erleichtern, hat man in den Standard das Rollenkonzept eingeführt; in Sybase, DB2 und anderen Systemen gibt es hierfür Gruppen (von Benutzern). Privilegien können statt an einzelne Benutzer an Rollen bzw. an Gruppen vergeben werden. Die Möglichkeiten, die sich durch die Benutzung von Rollen oder Gruppen ergeben, sind gleich. Allerdings ist die Philosophie, die hinter diesen beiden Begriffen steckt, unterschiedlich: Während Rollen Zusammenfassungen von Privilegien darstellen, sind Gruppen Zusammenfassungen von Benutzern. Die Rolle personalbearbeitung könnte alle Privilegien enthalten, die für die Bearbeitung von Personaldaten notwendig sind. Diese Rolle kann dann einzelnen Benutzern gewährt werden. Die Gruppe personalabteilung (in Sybase u. a.) enthält dagegen die Benutzer, welche die Rechte für die Personalbearbeitung benötigen. Hieraus ergeben sich unterschiedliche Anweisungen – die Effekte bei beiden Konzepten sind identisch. Eine gewisse Anzahl von Rollen ist in einem ORACLE-System vordefiniert. Dazu gehören die »alten« (und in früheren Versionen einzigen) Rollen CONNECT (Zugang zur Datenbank überhaupt), RESOURCE (Recht zur Anlage eigener Datenbankobjekte), DBA (unbeschränkter Zugriff auf alle Datenbankobjekte, Export der gesamten Datenbank etc.). Um das Rollenkonzept produktiv einzusetzen, sind zwei Schritte erforderlich: 1) die Zuweisung von Rechten an Rollen 2) die Zuweisung von Rollen an Benutzer Beim Gruppenkonzept werden die Gruppen ähnlich wie Benutzer dem Datenbanksystem bekannt gegeben. Rechte können an Gruppen wie an Benutzer vergeben werden, und alle Benutzer, die das Recht zur Teilnahme an einer Gruppe haben, erben die Rechte der Gruppe.

316

Prozedurale Konzepte in SQL

7.3 Prozedurale Konzepte in SQL Über Datensichten ist es möglich, auf einfache Weise dem Benutzer komplexe Zusammenhänge darzustellen. Des Weiteren kann der lesende Datenzugriff über Datensichten und Zugriffsrechte recht detailliert geregelt werden. Es ist ziemlich einfach, einem Benutzer das Recht zu geben, abgeleitete Daten zu sehen (z. B. das Durchschnittsgehalt pro Abteilung), ohne einen Zugriff auf die zugrunde liegenden Daten zuzulassen. Dagegen ist die Möglichkeit, Daten über Datensichten zu aktualisieren, recht eingeschränkt. Hier brauchen wir weitere Möglichkeiten, um beispielsweise für den Benutzer gekoppelte Datenänderungen zuzulassen (z. B. Buchung und Gegenbuchung), ohne ihm das Recht zu geben, die entsprechenden Daten direkt zu manipulieren. Dazu sind Datenbankprozeduren geeignet. Neben den Datenbankprozeduren können Trigger eingeführt werden, um die Konsistenz der Datenbank sicherzustellen. Während Datenbankprozeduren vom Endanwender (sofern er das entsprechende Zugriffsrecht hat) aufgerufen werden können, werden Trigger automatisch aktiviert, wenn jeweils ein entsprechendes Ereignis eintritt, d. h. das Einfügen, Ändern oder Löschen eines Tupels. Ein Benutzer kann die Aktion eines Triggers nicht ausschalten. Ganz im Gegenteil kann ein Trigger dazu benutzt werden, um Benutzer daran zu hindern, gewisse Datenmanipulationen vorzunehmen.

7.3.1 Datenbankprozeduren Als Erstes führen wir die Datenbankprozeduren oder Stored Procedures ein. Hierbei handelt es sich um Prozeduren, die im Stil der imperativen Programmierung gewisse Abläufe in der Datenbank beschreiben. Datenbankprozeduren sind im Standard ISO/IEC 9075-4:1996 Information technology – Database languages – SQL – Part 4: Persistent Stored Modules kurz SQL/PSM normiert worden. So gut wie alle Datenbanksysteme für Server enthalten diese Konzepte. Die im Folgenden verwendeten Beispiele sind unter Adaptive Server Anywhere entwickelt worden und weitgehend konform zum Standard; auf Abweichungen von der Norm weisen wir in den Beispielen durch einen Kommentar hin. Bei anderen Systemen sind die Abweichungen stärker. Der einzige Punkt, in dem in unseren Beispielen eine Abweichung vom Standard vorliegt, ergibt sich daraus, dass der Standard fordert, dass im Kopf einer Prozedur anzugeben ist, ob SQLDaten verändert werden, dieses Angabe erfolgt bei Sybase nicht. In diesem Abschnitt geben wir eine grundsätzliche Einführung in die Bedeutung der Datenbankprozeduren – wir geben keine Einführung in den Standard SQL/ PSM. Wer sich hier weiter informieren will, den verweisen wir auf [Melt98]. Insbesondere ist es im Standard auch möglich – und bei einigen Systemen wie DB2 derzeit auch notwendig –, dass die Rümpfe der Prozeduren in einer Programmiersprache (wie Ada, C, Cobol, Pascal) geschrieben werden. Daneben findet derzeit eine starke Integration von Java mit SQL statt, die auch im Bereich der Datenbankprozeduren eine größere Rolle spielt.

317

7 Konsistenz und Mehrbenutzerbetrieb

Datenbankprozeduren können von einem Nutzer angelegt werden, der die entsprechenden Privilegien an den Datenbankobjekten (insbesondere an den Tabellen) besitzt, die in der Prozedur benötigt werden. Der Ersteller kann das Privileg zur Ausführung (EXECUTE) einer Prozedur an jeden beliebigen Nutzer weitergeben. Der Nutzer mit dem EXECUTE-Privileg für eine Datenbankprozedur kann die Prozedur dann aufrufen, ohne dass er die Privilegien für die Datenbankobjekte haben muss, die in der Prozedur manipuliert werden.

Bedeutung von Datenbankprozeduren Es gibt mehrere Gründe, warum man Datenbankprozeduren anlegen sollte. 왘 Unterstützung der Datenbankkonsistenz

Hier ist zum Beispiel daran zu denken, dass bestimmte Änderungsoperationen an Tabellen für alle Anwender verboten sind und nur der Datenbankadministrator formal das Recht hat, diese Änderungen zuzulassen. Er kann aber komplexe, d. h. aus mehreren Elementaroperationen zusammengesetzte, Operationen zur Verfügung stellen, über die der Anwender die Datenbanktabellen manipulieren kann, ohne dass die Konsistenz dadurch gefährdet ist. So ist es beispielsweise denkbar, in einem Unternehmen, in dem verschiedene Geldkonten existieren, die absolute Änderung eines Kontostands zu verbieten. Stattdessen werden Datenbankprozeduren zur Verfügung gestellt, die Umbuchungen, Einnahmen oder Ausgaben ermöglichen. Zur zusätzlichen Sicherheit können die Prozeduren erzwingen, dass für jeden Aufruf Datum, Uhrzeit und Benutzeridentifizierung in einer weiteren Tabelle gespeichert werden, für die niemand (außer dem Datenbankadministrator) irgendwelche Änderungsrechte besitzt. 왘 Effizientere Ausführung komplexer Operationen

Operationen, die zur Ausführung größere Datenmengen durchsuchen müssen, aber nur kleine Datenmengen als Ergebnis zurückliefern, laufen im Server effizienter ab. 왘 Erweiterung der Möglichkeiten des relationalen Datenmodells

Es ist möglich, Prozeduren zu definieren, die in der DML von SQL nicht formulierbar sind.

Aufbau von Datenbankprozeduren In der Standardform haben Datenbankprozeduren skalare Werte als Argumente: 왘 IN

Eingabeargument

왘 OUT

Ausgabeargument

왘 INOUT

transientes Argument

Leider ist es in SQL/PSM nicht möglich, die Datentypen der Argumente durch Domänen zu beschreiben; bietet doch gerade das Konzept von Datenbankproze-

318

Prozedurale Konzepte in SQL

duren und -funktionen die Möglichkeit, Domänen zu abstrakten Datentypen zu erweitern und damit das Verhalten sehr viel einfacher und genauer zu überwachen. In der Prozedur können weitere lokale Variablen definiert werden. Für die Formulierung stehen die Kontrollstrukturen der imperativen strukturierten Programmierung (also ähnlich wie in Pascal, C, PL/I) zur Verfügung, d. h.: 왘 Variablendeklaration 왘 Wertzuweisung 왘 Prozeduraufruf 왘 sequenzielle Folge von Anweisungen 왘 Fallunterscheidung 왘 Wiederholungsschleife

Beispiele für Datenbankprozeduren Die folgende Datenbankprozedur benennt systematisch Paare von [plz, ort] um. Dies kann zum Beispiel bei Eingemeindungen benutzt werden: CREATE PROCEDURE umbenenne( IN plz_alt CHAR(5), IN ort_alt CHAR(25), IN plz_neu CHAR(5), IN ort_neu CHAR(25)) -- SQL/PSM: MODIFIES SQL-DATA BEGIN UPDATE kunde SET plz=plz_neu, ort=ort_neu WHERE plz=plz_alt AND ort=ort_alt END;

Ein Aufruf könnte wie folgt aussehen: CALL umbenenne ('23863', 'Heidkrug', '23863', 'Kayhude');

Die folgende Prozedur ist eine Erweiterung der eben eingeführten: Hier kann der Anwender anschließend auswerten, wie viele Umbenennungen erfolgt sind. CREATE PROCEDURE umbenenn2( IN plz_alt CHAR(5), IN ort_alt CHAR(25), IN plz_neu CHAR(5), IN ort_neu CHAR(25), OUT anzahl INTEGER) -- SQL/PSM: MODIFIES SQL-DATA

319

7 Konsistenz und Mehrbenutzerbetrieb

BEGIN SELECT COUNT(*) INTO anzahl FROM kunde WHERE plz=plz_alt AND ort=ort_alt; UPDATE kunde SET plz=plz_neu, ort=ort_neu WHERE plz=plz_alt AND ort=ort_alt END;

An diesem Beispiel erkennen wir eine besondere Variante der SELECT-Anweisung. Hinter der Spaltenangabe steht die INTO-Klausel, gefolgt von einer Liste lokaler Variablen, in welche die Spaltenwerte der Antworttabellen übertragen werden. Diese Art der Abfrage ist aber nur möglich, wenn die Abfrage nur eine einzige Zeile als Ergebnis hat.7 Die Variablenliste muss genauso viele Variablen enthalten wie die Spaltenliste. Die Zuordnung von einer Spalte zu einer Variablen erfolgt über die Reihenfolge. Der Datentyp der Variablen muss jeweils mit dem Datentyp der Spalte kompatibel sein. Die Spaltenliste kann neben Tabellenspalten natürlich auch Ausdrücke enthalten. Ein Aufruf kann wie folgt aussehen: CREATE VARIABLE umbenannt INTEGER; CALL umbenenn2 ('23863', 'Heidkrug', '23863', 'Kayhude', umbenannt); SELECT umbenannt FROM dummy;

Durch die erste Anweisung wird eine Hilfsvariable namens umbenannt eingeführt. In der zweiten Anweisung werden alle Tupel mit [plz='23863',ort='Heidkrug',…]

umbenannt in [plz='23863',ort='Kayhude',…]

Die Anzeige der Variablen umbenannt ist Aufgabe eines steuernden Anwendungsprogramms, das die Prozedur aufgerufen hat. In diesem Beispiel zeigen wir sie über einen formalen Spaltenausdruck in einer SELECT-Anweisung an. dummy8 ist in Adpative Server Anywhere eine Tabelle, die in jeder Datenbank enthalten ist. Diese Tabelle hat genau einen Satz, und der einzige Zweck dieser Tabelle ist es, interaktiv Datenbankvariablen auszugeben. Im folgenden Beispiel wird durch die Einführung von zwei Datenbankprozeduren und einer Protokolltabelle die Konsistenz des Attributs bestand in der Relation artikel sichergestellt. Wir gehen davon aus, dass eine direkte Änderung dieses 7 Um die Ergebnisse einer möglicherweise mehrzeiligen Antworttabelle zur Weiterverarbeitung in lokale Variablen zu übernehmen, muss ein sog. Cursor definiert werden. Wir beschreiben dieses Konzept in Kapitel 7.3.4. 8 Die Angabe FROM DUMMY könnten wir auch weglassen. In Oracle gibt es eine Tabelle mit ähnlicher Funktion, die dort »dual« genannt wird.

320

Prozedurale Konzepte in SQL

Attributs ausgeschlossen sein soll. Beim Einfügen eines neuen Tupels wird automatisch der Default-Wert 0 eingetragen. Der Wert kann nur durch eine Lieferung (PROCEDURE Lieferung), durch Lagerzugang (PROCEDURE Lagerzugang – hier nicht beschrieben) und durch eine protokollierte Bestandskorrektur (PROCEDURE Bestandskorrektur) geändert werden. Ein direktes Ändern mit der UPDATEAnweisung wird ausgeschlossen, indem kein UPDATE-Privileg weitergegeben wird. Der Autor der oben genannten Prozeduren hat dieses Recht und somit indirekt der Aufrufer der Prozeduren. Als Erstes beschreiben wir den Aufbau der Protokolldatei: CREATE TABLE protokoll ( benutzer CHAR(10) NOT NULL DEFAULT CURRENT USER, zeitpunkt TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, artikel_Nr CHAR(4) NOT NULL, bestand_alt INTEGER, bestand_neu INTEGER, FOREIGN KEY (artikel_Nr) REFERENCES artikel ON UPDATE CASCADE ON DELETE CASCADE );

Durch die Default-Werte für Benutzer und Zeitpunkt werden automatisch die Benutzeridentifikation sowie der Zeitpunkt der Änderung eingetragen. Bei Angabe einer falschen Artikelnummer erfolgt keine Aktion – auch keine Protokollierung. -- Prozedur, die aus einer vorhandenen Bestellung eine Lieferung abbucht. -- Argumente: -IN best_nr: Bestell_Nr von vorhandener Bestellung -IN arti_nr: Artikel_Nr in vorhandener Bestellung -IN anzahl: Anzahl der zu versendenden Artikel -kann kleiner sein als Bestellmenge -OUT fehler: 0 falls alles in Ordnung -1 falls Bestellposition nicht vorhanden -2 falls Liefermenge schon festgelegt -- Auf die Überprüfung von Konsistenzbedingungen, die in der -- Artikel-Relation angelegt sind, wird hier verzichtet. CREATE PROCEDURE lieferung( IN best_nr INTEGER, IN arti_nr CHAR(4), IN anzahl INTEGER, OUT fehler INTEGER) -- SQL/PSM: MODIFIES SQL-DATA

321

7 Konsistenz und Mehrbenutzerbetrieb

BEGIN ATOMIC DECLARE nicht_vorhanden EXCEPTION FOR SQLSTATE VALUE '02000'; DECLARE geliefert INTEGER; SELECT liefermenge INTO geliefert FROM position WHERE artikel_nr=arti_nr AND bestell_nr=best_nr; IF sqlstate=nicht_vorhanden THEN SET fehler=1 ELSEIF geliefert IS NOT NULL THEN SET fehler=2 ELSE SET fehler=0; UPDATE position SET liefermenge=anzahl, gesamtpreis=gesamtpreis*anzahl/bestellmenge WHERE artikel_Nr=arti_nr AND bestell_nr=best_nr; UPDATE artikel SET bestand=bestand-anzahl WHERE artikel_nr=arti_nr END IF END;

Durch BEGIN ATOMIC ... END wird eine Aktion beschrieben, die alle Eigenschaften einer Transaktion9 hat, d.h., sie wird entweder vollständig ausgeführt oder gar nicht. Sie ist nur in dem Sinne keine Transaktion, als dass der Programmaufruf in eine Transaktion eingebettet sein kann – ein Ende der Prozedur stellt nicht automatisch ein Ende der Transaktion dar. Wenn wir dagegen den Block der Prozedur in BEGIN ... END einbetten (ohne ATOMIC), gilt diese spezielle Eigenschaft nicht. Eine Datenbankprozedur kann über die übliche SQL-Schnittstelle mit der CALLAnweisung aufgerufen werden. Diese SQL-Schnittstelle kann überall verwendet werden, wo SQL-Anweisungen formuliert werden können, also z. B.: 왘 in einer interaktiven SQL-Umgebung (ISQL) 왘 über Embedded SQL (ESQL) in einer Wirtssprache 왘 über ein Call-Level-Interface (CLI) aus einer Programmiersprache 왘 über ODBC

9 Das Transaktionskonzept von SQL haben wir in Abschnitt 7.1 eingeführt.

322

Prozedurale Konzepte in SQL

Ein Aufruf kann wie folgt aussehen: CREATE VARIABLE fehler INTEGER; CALL lieferung (960153, 'G001', 1, fehler); SELECT fehler FROM dummy;

Dieser Aufruf führt also zuerst die notwendigen Kontrollen durch (gibt es überhaupt einen entsprechenden Satz in der Positionstabelle?). Anschließend erledigt der Aufruf (auf dem Server!) alle notwendigen Datenänderungen, die eine Auslieferung nach sich ziehen, nämlich die Eintragung der gelieferten Menge in der Positionsdatei und gegebenenfalls die Neuberechnung des Gesamtpreises proportional zur bestellten Menge sowie die Abbuchung in der Artikeltabelle. Das Verfahren ist DV-mäßig jetzt so sicher, dass jederzeit aus der Datenbank der aktuelle Lagerbestand ermittelt werden kann. Da aber im Unternehmen Menschen arbeiten und nicht nur Computer und Roboter, stellt sich spätestens bei der Inventur heraus, dass es doch Abweichungen gibt. Diese müssen dann als Bestandskorrektur in die Datenbank eingegeben werden. Damit aber diese Funktion nicht als einfache Möglichkeit benutzt werden kann, Schwund zu erzeugen und anschließend problemlos abzubuchen, sollte zum einen die Berechtigung für die Benutzung dieser Prozedur auf einen kleinen Personenkreis eingeschränkt werden. Zum anderen wird grundsätzlich eine Protokolldatei geführt, die den Aufruf dieser Prozedur mit Angabe des Benutzers und des Zeitpunkts des Aufrufs protokolliert. -- Prozedur, die eine Bestandskorrektur vornimmt. –- Die Bestandskorrektur wird in der Tabelle Protokoll protokolliert. -- Argumente: -IN artikel: Artikel_Nr des zu korrigierenden Artikels -IN neubestand: zu setzender Bestand CREATE PROCEDURE bestandskorrektur( IN art_nr CHAR(4), IN neubestand INTEGER) -- SQL/PSM: MODIFIES SQL-DATA BEGIN ATOMIC DECLARE nicht_vorhanden EXCEPTION FOR SQLSTATE VALUE '02000'; DECLARE altbestand INTEGER; SELECT bestand INTO altbestand FROM artikel WHERE artikel_nr=art_nr; IF SQLSTATE nicht_vorhanden THEN UPDATE artikel SET bestand = neubestand WHERE artikel_nr = art_nr; INSERT INTO protokoll(artikel_nr,bestand_alt,bestand_neu) VALUES(art_nr,altbestand,neubestand) END IF END;

323

7 Konsistenz und Mehrbenutzerbetrieb

Ein Aufruf könnte wie folgt aussehen: CALL bestandskorrektur ('G002', 468);

7.3.2 Funktionen in Datenbanken Funktionen haben einen ähnlichen Aufbau wie Prozeduren, sind aber zum Teil einfacher, da nur Eingabeargumente übergeben werden können und ein Ergebniswert zurückgegeben werden muss. Funktionen können an jeder Stelle aufgerufen werden, wo Ausdrücke auftreten können, also insbesondere auch in der SELECTKlausel, in der WHERE-Klausel und in der SET-Klausel der UPDATE-Anweisung. Die Zugriffsrechte werden wie bei Datenbankprozeduren durch GRANT EXECUTE vergeben. Die folgende Funktion addiert einen Prozentbetrag zu einem Geldbetrag. -- Berechnung des Bruttobetrages aus Betrag und Prozentsatz CREATE FUNCTION brutto (betrag DECIMAL(15,2), proz DECIMAL(3,1)) RETURNS DECIMAL(15,2) BEGIN RETURN (betrag + betrag*proz*.01) END;

Dies kann zum Beispiel in einer SELECT-Anweisung auf folgende Weise aufgerufen werden: SELECT ar.artikel_nr, ar.bezeichnung, ar.listenpreis, brutto(ar.listenpreis, mw.prozent) AS bruttopreis FROM artikel AS ar JOIN mwstsatz AS mw ON ar.mwst = mw.mwst;

Als weitere Möglichkeiten definieren wir im Folgenden eine Funktion, die es in Ad-hoc-Auswertungen auf einfache Weise ermöglicht, die Zahlungsart von Kunden darzustellen. Es wird die gespeicherte Information (ein Buchstabe) durch eine Zeichenkette in Klartext dargestellt. -- Ausgabe der Zahlungsart als Zeichenkette CREATE FUNCTION zahl_string (kurz CHAR) RETURNS CHAR(12) BEGIN CASE kurz WHEN 'R' THEN RETURN('Rechnung') WHEN 'B' THEN RETURN('Bankeinzug') WHEN 'N' THEN RETURN('Nachnahme') WHEN 'V' THEN RETURN('Vorkasse') WHEN 'K' THEN RETURN('Kreditkarte') ELSE RETURN('') END CASE END;

324

Prozedurale Konzepte in SQL

Ein Aufruf kann z. B. in folgender Form erfolgen: SELECT kunden_nr, name, strasse, plz, ort, zahl_string(zahlung) AS Zahlungsart FROM kunde;

Auf ähnliche Weise können wir die deutsche Bezeichnung von Wochentagen oder Monaten erzeugen, falls das Datenbanksystem diese Ausgabe nicht unterstützt. Als abschließendes Beispiel erweitern wir die Datumsfunktionen durch eine Funktion, die für ein gegebenes Jahr im Bereich 1900–2099 das Osterdatum bestimmt. -- Berechnung des Ostertages im 20. und 21. Jahrhundert -- Grundlagen für den Algorithmus: -Fischer Lexikon Astronomie (1957), S. 52 und -Heinz Zemanek; Kalender und Chronologie; -4. Auflage, Oldenbourg Verlag (1987) CREATE FUNCTION ostern (jahr SMALLINT) RETURNS DATE BEGIN DECLARE a SMALLINT; -- Nummer des Jahres im -- 19-Jahre-Zyklus DECLARE newroz DATE; -- Frühlingsanfang DECLARE vollmond DATE; -- Vollmond ab Frühlingsanfang DECLARE ostern_x DATE; -- Sonntag nach Vollmond -- Auswertung der Formeln nach Fischer Lexikon Astronomie SET a = MOD(jahr,19); SET newroz = YMD(jahr,3,21); SET vollmond = newroz + MOD(19*a + 24, 30); SET ostern_x = vollmond + 8-DOW(vollmond); -- DOW : Wochentag; 1 = Sonntag, ..., 7 = Samstag IF ostern_x = YMD(jahr,4,26) OR -- kein 26. April ostern_x = YMD(jahr,4,25) AND a>10 THEN -- 25. April nur in erster Hälfte des Zyklus RETURN ostern_x - 7 ELSE RETURN ostern_x END IF END;

Dies kann zu einer Funktion erweitert werden, die feststellt, ob ein gegebener Tag ein Feiertag ist – eine Funktion, die für viele Geschäftsvorgänge von Bedeutung ist (allerdings ist wegen der Internationalisierung des Handels z. B. zwischen Preußen und Bayern diese Funktion um ein weiteres Argument zu ergänzen, das den jeweiligen Gültigkeitsbereich angibt).

325

7 Konsistenz und Mehrbenutzerbetrieb

-- Bestimmung, ob ein gegebenes Datum ein Feiertag ist. -- Derzeitige norddeutsche Version -- Andere dürfen einige zusätzliche Eintragungen vornehmen. -- Achtung! Sensibel gegen Gesetzesänderungen -- Rückgabe: 0 -> kein Feiertag -1 -> Feiertag CREATE FUNCTION feiertag (dat DATE) RETURNS SMALLINT BEGIN DECLARE diff SMALLINT; -- Differenz zu Ostern DECLARE tag SMALLINT; -- Tag des Datums DECLARE monat SMALLINT; -- Monat des Datums SET diff = dat - ostern(YEAR(dat)); SET tag = DAY(dat); SET monat = MONTH(dat); IF tag = 1 AND monat = 1 -- Neujahr OR tag = 1 AND monat = 5 -- 1. Mai OR tag = 3 AND monat = 10 -- Tag der Deutschen Einheit OR tag = 25 AND monat = 12 -- 1. Weihnachtstag OR tag = 26 AND monat = 12 -- 2. Weihnachtstag OR diff = -2 -- Karfreitag OR diff = 0 -- Ostersonntag OR diff = 1 -- Ostermontag OR diff = 39 -- Himmelfahrtstag OR diff = 49 -- Pfingstsonntag OR diff = 50 -- Pfingstmontag THEN RETURN 1 ELSE RETURN 0 END IF END;

Hieraus lässt sich jetzt die Funktion ableiten, die für einen Tag bestimmt, welcher Tag in n Werktagen (ohne Samstag) sein wird. -- Bestimmung des Tages, der n Werktage (Montag bis -- Freitag, kein Feiertag) nach dem gegebenen Datum liegt. -- Argumente: -dat Anfangsdatum ; muss kein Werktag sein -n Anzahl Tage ; muss >= 0 sein. CREATE FUNCTION add_werktage (dat DATE, n SMALLINT) RETURNS DATE BEGIN DECLARE neudat DATE; DECLARE rest SMALLINT; SET neudat = dat; SET rest = n; WHILE rest > 0 LOOP

326

Prozedurale Konzepte in SQL

IF DOW(neudat) BETWEEN 2 AND 6 Feiertag(neudat)=0 THEN SET rest = rest-1 END IF; SET neudat = neudat+1 END LOOP; RETURN neudat; END;

AND

-- Montag .. Freitag -- kein Feiertag

Ein Aufruf kann z. B. wie folgt aussehen: SELECT bestell_nr, bestelldatum, lieferdatum FROM bestellung WHERE lieferdatum > add_werktage(Bestelldatum,3)

Ein weiteres wichtiges Anwendungsgebiet für Datenbankfunktionen ist die automatische Erzeugung einer Nummerierung. In der SQL-Norm gibt es zwar mittlerweile die Möglichkeit, mit CREATE SEQUENCE10 einen Nummerngenerator zu definieren, aber nicht jedes DBMS bietet das auch an. Die im Folgenden dargestellte Möglichkeit zeichnet sich außerdem dadurch aus, dass in der automatischen Nummerierung der Nummerierungswert per Hand auf einen anderen Wert gesetzt werden kann (z. B. wenn die laufenden Rechnungsnummern aus neun Ziffern bestehen, wovon die ersten beiden die Jahreszahl sind). Die Implementierung enthält eine Relation mit genau einem Tupel, in dem die zuletzt vergebene Nummer gespeichert wird. -- ********************************************************** -- ** Nummernserver als abstrakter Datentyp ** -- ********************************************************** -------------------------------------------------------------- TABLE num_table -------------------------------------------------------------- Tabelle, die die letzte vergebene Nummer enthält CREATE TABLE num_table (next_num INTEGER NOT NULL); INSERT INTO num_table (next_num) VALUES(0); -------------------------------------------------------------- PROCEDURE num_set -------------------------------------------------------------- Setzen des Nummernservers auf einen vordefinierten Wert. -- Beim nächsten Aufruf wird wert+1 zurückgegeben. CREATE PROCEDURE num_set (IN wert INTEGER) -- SQL/PSM: MODIFIES SQL-DATA BEGIN UPDATE num_table SET next_num = wert END; -- num_set 10 Dies wird in Kapitel 4.3.5 beschrieben.

327

7 Konsistenz und Mehrbenutzerbetrieb

-------------------------------------------------------------- FUNCTION num_get -------------------------------------------------------------- Holen der nächsten freien Nummer CREATE FUNCTION num_get () RETURNS INTEGER -- SQL/PSM: MODIFIES SQL-DATA BEGIN ATOMIC DECLARE wert INTEGER; UPDATE num_table SET next_num = next_num+1; SELECT next_num INTO wert FROM num_table; RETURN wert END;

7.3.3 Trigger in Datenbanken Während Datenbankprozeduren bewusst vom Anwender (oder Anwendungsprogrammierer) aufgerufen werden, werden Trigger durch Ereignisse zwangsweise vom Datenbanksystem aufgerufen. Trigger sind zur gleichen Zeit wie Datenbankprozeduren, also etwa 1992, in SQLDatenbanksystemen eingeführt worden. Eine Normierung ist im Standard SQL:1999 erfolgt. Die Beispiele sind in Adaptive Server Anywhere getestet worden. Sie genügen auch dem neuen Standard. Das Auslösen eines Triggers wird durch mehrere Parameter beschrieben: trigger_zeitpunkt ::= BEFORE | AFTER | INSTEAD OF

Ein Trigger kann vor oder nach einem Ereignis aktiviert werden. Er kann auch anstelle eines vorgesehenen Ereignisses durchgeführt werden. Dadurch ist es zum Beispiel möglich, dass für Zeilen, die in einer DELETE-Anweisung gelöscht werden sollen, stattdessen ein Attribut mit der Bedeutung »Löschmarkierung« auf TRUE gesetzt wird. trigger_ereignis ::= DELETE | INSERT | UPDATE [OF spaltenliste]

Das auslösende Ereignis kann das Löschen eines Tupels, das Einfügen eines Tupels oder das Ändern eines Tupels sein. trigger_level ::= ROW | STATEMENT

Hiermit werden unterschiedliche Aktionen beschrieben, wenn eine Anweisung mehrere Tupel betrifft, z. B. beim Löschen oder Ändern mehrerer Zeilen. Ein Trigger auf Zeilenebene wird für jede Zeile einzeln aufgerufen und führt eine Aktion durch, die auf einzelne Zeilen bezogen ist. Dagegen wird ein Trigger auf Anweisungsebene nur einmal aufgerufen – ihm stehen dafür jeweils eine Ta-

328

Prozedurale Konzepte in SQL

belle der gelöschten bzw. überschriebenen Tupel und eine Tabelle der eingefügten Tupel (bzw. der Tupel nach der Änderung) zur Verfügung, die er auswerten kann. Mit diesen Angaben sieht ein Trigger im Wesentlichen11 wie folgt aus: CREATE TRIGGER trigger_name trigger_zeitpunkt trigger_ereignis [ ORDER integer ] ON tabelle [ REFERENCES [ OLD AS alt_name ] [ NEW AS neu_name] ] FOR EACH trigger_level [ WHEN (suchbedingung) ] block_anweisung

Der Name des Triggers hat weiter keine Bedeutung. Er wird nur benötigt, um den Trigger gegebenenfalls später wieder mit einer DROP TRIGGER-Anweisung zu löschen. Die Zahl nach dem Schlüsselwort ORDER ist wichtig, wenn für ein Ereignis mehrere Trigger aktiviert werden können. Die angegebene Zahl legt die Reihenfolge fest. Durch alt_name kann bei DELETE- und UPDATE-Triggern der Wert der Spalten vor der Änderung abgefragt werden, durch neu_name entsprechend bei INSERTund UPDATE-Triggern der Wert der Spalten nach der Änderung. Trigger dienen zum einen dazu, die Konsistenz der Datenbank sicherzustellen. Wir können sehr komplexe Konsistenzbedingungen durch Trigger überprüfen lassen. Im Gegensatz zur CHECK-Klausel der CREATE TABLE-Anweisung haben wir in Triggern dieselben Möglichkeiten der imperativen Programmierung wie in Datenbankprozeduren. Zum anderen ist es durch die Verwendung von Triggern möglich, komplexe Betriebsabläufe in die Datenbank abzubilden. Beim Vorliegen bestimmter Datenbankzustände oder bestimmter Datenbankübergänge können automatisch Ereignisse ausgelöst werden. Diese Ereignisse sind Datenbankmanipulationen (z. B. Einfügen eines Tupels mit einem bestimmten Informationsgehalt). Durch Einbindung von C-Programmen ist es allerdings auch möglich, dass Trigger unmittelbar eine Datenfernübertragung oder einen Notruf veranlassen.

Beispiele für Datenbank-Trigger Über einen Trigger können wir erreichen, dass bei einer Verringerung des Bestands unter den Mindestbestand (unter der Voraussetzung, dass noch kein Bestellvorschlag erfolgt ist) das Attribut bestellvorschlag auf den Zeitpunkt der Unter11 Wir lassen hier die zusätzliche Möglichkeit weg, dass in einem Trigger gegebenenfalls verschiedene Aktionen für das Ändern unterschiedlicher Spalten angegeben werden können.

329

7 Konsistenz und Mehrbenutzerbetrieb

schreitung gesetzt wird. Zu einem späteren Zeitpunkt kann dann ein Programm alle Tupel heraussuchen, für die das Attribut bestellvorschlag einen definierten Wert und nachbestellung keinen definierten Wert hat. Daraus können Nachbestellungen erzeugt werden. Beim Eintreffen der entsprechenden Lieferung sind dann die Attribute bestellvorschlag und nachbestellung wieder auf NULL zu setzen.12 CREATE TRIGGER Bestellvorschlag AFTER UPDATE OF Bestand ON Artikel REFERENCING NEW AS neu OLD AS alt FOR EACH ROW WHEN (neu.Bestand < neu.Mindestbestand AND alt.Bestellvorschlag IS NULL) BEGIN UPDATE Artikel SET Bestellvorschlag = CURRENT TIMESTAMP WHERE Artikel_Nr = neu.Artikel_Nr END;

In dem folgenden Beispiel wird die Konsistenzbedingung, dass für einen Kunden mit Bankeinzug ein Girokonto vorhanden sein muss, durch Trigger erreicht. Wir benötigen hierfür drei Trigger,13 deren Namen sich jeweils durch zwei Buchstaben unterscheiden: 왘 vor dem Einfügen eines Kunden (IK) 왘 vor der Änderung der Zahlungsart eines Kunden (UK) 왘 vor dem Löschen eines Girokontos (DG)

Die Änderung der Kundennummer eines Girokontos können wir dadurch verhindern, dass wir für die Spalte kunden_nr kein UPDATE-Recht vergeben. -- Neues Konto mit Bankeinzug nicht zulässig CREATE TRIGGER ExistGiroKontoIK BEFORE INSERT ON Kunde REFERENCING NEW AS neu FOR EACH ROW WHEN (neu.Zahlungsart = 'B') BEGIN DECLARE Anwender_Fehler EXCEPTION FOR SQLSTATE '99999'; SIGNAL Anwender_Fehler END;

12 Dieses Beispiel führt unter ORACLE zu einer Fehlermeldung. Der Grund dafür ist, dass der Trigger das Ereignis (UPDATE eines Tupels) herbeiführt, das ihn selbst ausgelöst hat. In diesem Fall ist die Programmierung viel umständlicher und muss über eine temporäre Tabelle erfolgen. 13 Es wäre natürlich einfacher, an dieser Stelle stattdessen die Möglichkeiten der CREATE ASSERTION-Anweisung zu nutzen, vgl. Kapitel 4.3.6.

330

Prozedurale Konzepte in SQL

-- Änderung der Kundendaten auf Bankeinzug: -- Konto muss schon existieren CREATE TRIGGER ExistGiroKontoUK BEFORE UPDATE OF Zahlung ON Kunde REFERENCING NEW AS neu FOR EACH ROW WHEN (neu.Zahlung = 'B') BEGIN DECLARE Anwender_Fehler EXCEPTION FOR SQLSTATE '99999'; IF NOT EXISTS(SELECT * FROM Girokonto AS gi WHERE gi.kunden_nr = neu.kunden_Nr) THEN SIGNAL Anwender_Fehler END IF END; -- Löschen eines Girokontos: -- Es darf keine entsprechende Kundennummer mit Bankeinzug existieren CREATE TRIGGER ExistGiroKontoDG BEFORE DELETE ON Girokonto REFERENCING OLD AS alt FOR EACH ROW BEGIN DECLARE Anwender_Fehler EXCEPTION FOR SQLSTATE '99999'; IF EXISTS(SELECT * FROM kunde ku WHERE ku.kunden_nr = alt.kunden_nr AND ku.zahlungsart = 'B') THEN SIGNAL Anwender_Fehler END IF END; SQLSTATE '99999' kann in Triggern benutzt werden, um unter bestimmten Bedingungen einen Abbruch zu erzwingen. Die Aktion, die den Trigger ausgelöst hat – hier also der Versuch, einen Kunden mit zahlungsart = 'B' einzufügen, ohne dass ein Girokonto existiert –, wird dann vom Trigger abgebrochen, d.h., das entsprechende Tupel wird nicht eingefügt.

7.3.4 Das Cursor-Konzept In verkürzter Form stellen wir den relationalen Ansatz der Datengewinnung dem der imperativen Programmierung gegenüber.

Relational – eine Relation auf einmal Verknüpfe Relationen, um neue Relationen zu erhalten.

331

7 Konsistenz und Mehrbenutzerbetrieb

Imperativ – einen Satz nach dem anderen Öffne eine Datei; lies den ersten Satz; solange der letzte Datenzugriff erfolgreich war, tue Folgendes: verarbeite den Satz; lies den nächsten Satz Ende tue Dieser Bruch zwischen den beiden Paradigmen relational und imperativ wird in der Literatur als Impedance Mismatch bezeichnet. Wenn wir in einer imperativen Programmiersprache eine Relation satzweise verarbeiten wollen, müssen wir einen Weg finden, wie wir dieser Programmiersprache eine Relation satzweise zur Verfügung stellen. Hierbei kommt es nicht infrage, einfach eine Relation wie eine Datei zu behandeln und alle Verknüpfungen in die Programmierung zu legen. Wir wollen natürlich die Vorteile des relationalen Ansatzes mit in die Schnittstelle übernehmen. In einer Client-Server-Verbindung wäre es auch geradezu töricht, wenn eine imperative Sprache über eine DFÜ-Verbindung satzweise mehrere Relationen verknüpfen und dabei jeden zu untersuchenden Satz über die DFÜ-Verbindung »schaufeln« würde. Die Lösung ist der sogenannte Cursor. Ein Cursor ist hier aber nicht, wie vom Monitor her bekannt, ein blinkender Bildpunkt. Ein Cursor beschreibt eine beliebig komplexe SELECT-Anweisung, deren Tupel satzweise im Anwendungsprogramm verarbeitet werden können. Somit stellt ein Cursor ein Analogon zu einer Datei in einer imperativen Programmiersprache dar. Diese Analogie kann sogar weitergeführt werden: Der Cursor entspricht nicht einer physikalischen Datei, die ja außerhalb des Programms existiert, sondern einer logischen Verbindung zur Datei. In Programmiersprachen wird einer Datei üblicherweise programmintern ein Name zugeordnet, über den diese Datei angesprochen wird. Dieser Name muss nicht identisch mit dem Dateinamen auf dem Datenträger sein – in vielen Fällen kann er gar nicht identisch mit ihm sein, da die Syntax für Bezeichner in Programmiersprachen von der Syntax für Dateibezeichner in Betriebssystemen abweicht. Dementsprechend hat ein Cursor einen Namen, über den die SELECT-Anweisung angesprochen wird. SQL-Anweisung

Bedeutung

DECLARE cname CURSOR FOR ...

Logische Beschreibung eines Cursors. Diese Beschreibung bewirkt noch keine Aktion der Datenbankmaschine.

OPEN cname

Die Verbindung zur Abfrage wird hergestellt. Die Vorbereitungen in der Datenbankmaschine werden getroffen.

FETCH cname

Der nächste (oder auf andere Weise positionierte) Satz wird in die entsprechenden Variablen des Wirtsprogramms geladen.

CLOSE cname

Schließen des Cursors – die entsprechende Verbindung zur Datenbank wird aufgehoben.

Tabelle 7.3: Das Cursor-Konzept in SQL

332

Prozedurale Konzepte in SQL

In dem folgenden Beispiel werden alle Kunden gesucht, die nach einem vorgegebenen Datum einen bestimmten Artikel bestellt haben. Ein Aufruf kann in der Weise erfolgen: CALL Werbeliste('G002', '1996-04-30');

Das folgende Beispiel sollte sinnvollerweise in einer imperativen Programmiersprache (wie Ada, C, Cobol, Pascal oder auch Java) eingebettet sein. Da wir hier aber keine Kenntnisse einer spezifischen Sprache voraussetzen, haben wir es als Datenbankprozedur in SQL/PSM formuliert. Es gibt aber in SQL/PSM keine Anweisung, um Daten auf dem Bildschirm oder Drucker auszugeben. Ersatzweise haben wir hier – die nicht im Standard enthaltene – MESSAGE-Anweisung von Adaptive Server Anywhere verwandt, die Daten im Meldungsfenster der Datenbankmaschine anzeigt. -------------------------------------------------------------- PROCEDURE Werbeliste -------------------------------------------------------------- Es soll eine Liste aller Kunden ausgegeben werden, die den -- Artikel mit der angegebenen Artikelnummer ab dem -- angegebenen Datum bestellt haben. -- Die Ausgabe soll gruppenweise nach plz | ort erfolgen. CREATE PROCEDURE werbeliste (IN art_nr CHAR(4), IN dat DATE) BEGIN DECLARE kname CHAR(30); DECLARE kstrasse CHAR(30); DECLARE kplz CHAR(05); DECLARE aplz CHAR(05); DECLARE kort CHAR(25); DECLARE aort CHAR(25); DECLARE bdatum DATE; DECLARE bmenge INTEGER; DECLARE nicht_vorhanden EXCEPTION FOR SQLSTATE VALUE '02000'; DECLARE kliste CURSOR FOR SELECT DISTINCT k.name, k.strasse, k.plz, k.ort, b.bestelldatum, p.bestellmenge FROM kunde AS k JOIN bestellung AS b ON b.kunden_nr = k.kunden_nr JOIN position AS p ON p.bestell_nr = b.bestell_nr WHERE p.artikel_nr=art_nr AND b.bestelldatum >= dat ORDER BY plz, ort, name FOR READ ONLY; -- Festlegung der ersten Gruppe, damit zu Anfang ein

333

7 Konsistenz und Mehrbenutzerbetrieb

-- Gruppenwechsel erfolgt SET aplz = '00000'; -- Satzweise Verarbeitung der Abfrage OPEN kliste; leseschleife: LOOP FETCH NEXT kliste INTO kname, kstrasse, kplz, kort, bdatum, bmenge; IF SQLSTATE=nicht_vorhanden THEN LEAVE leseschleife END IF; -- Gruppenwechsel IF (kplz aplz) OR (kort aort) THEN SET aplz = kplz; SET aort = kort; MESSAGE STRING('Kunden aus ', aplz, ' ', aort) END IF; MESSAGE STRING('==> ', kname, ' am ', bdatum, ' ', bmenge, ' Stück'); END LOOP leseschleife; MESSAGE 'FERTIG !'; CLOSE kliste END;

Daneben können die UPDATE- und die DELETE-Anweisung benutzt werden, indem in der WHERE-Klausel CURRENT OF cname angegeben wird. Die entsprechende Datenänderung bezieht sich dann auf das aktuelle Tupel des Cursors. In dem folgenden Beispiel wird jedem Artikel ein neues Attribut num zugewiesen, das den Rang nach der Preishöhe enthält, d.h., der teuerste Artikel bekommt die Nummer 1, der nächste die Nummer 2 etc. Bei Artikeln mit gleichem Preis ist nach der Artikelnummer zu sortieren. -------------------------------------------------------------- PROCEDURE Neu_Num -------------------------------------------------------------- Es sollen alle Artikel nach dem Preis absteigend sortiert -- und in der Reihenfolge nummeriert werden -- bei gleichem Preis ist nach der Artikelnummer zu sortieren -- Dazu ist zuvor eine neue Spalte einzuführen ALTER TABLE artikel ADD num INTEGER; CREATE PROCEDURE neu_num () BEGIN DECLARE lfd_nr INTEGER; DECLARE preis DECIMAL(15,2); DECLARE art_nr CHAR(4);

334

Strategien zur Konsistenzsicherung

DECLARE nicht_vorhanden EXCEPTION FOR SQLSTATE VALUE '02000'; DECLARE aliste CURSOR FOR SELECT listenpreis, artikel_nr FROM artikel ORDER BY listenpreis DESC, artikel_nr ASC FOR UPDATE; -- Satzweise Verarbeitung der Abfrage SET lfd_Nr = 0; OPEN aliste; leseschleife: LOOP FETCH NEXT aliste INTO preis, art_nr; IF SQLSTATE=nicht_vorhanden THEN LEAVE leseschleife END IF; SET lfd_nr = lfd_nr + 1; UPDATE artikel SET num = lfd_nr WHERE CURRENT OF aliste; END LOOP leseschleife; MESSAGE STRING ('Es wurden ', lfd_nr, ' Artikel nummeriert.'); CLOSE aliste END;

7.4 Strategien zur Konsistenzsicherung Wir haben in den vergangenen Kapiteln verschiedene Methoden und Gesichtspunkte des Datenbankentwurfs kennengelernt: vom konzeptionellen Entwurf über den Entwurf eines relationalen Schemas bis hin zur Implementierung unter SQL. In diesem Kapitel haben wir verschiedene Möglichkeiten kennengelernt, wie wir die Konsistenz einer Datenbank sicherstellen können. Zum Abschluss werden wir in diesem Abschnitt die verschiedenen Techniken der Konsistenzsicherung zusammenfassen (vgl. hierzu auch [Lipe92]). Die Aufgaben der Konsistenzsicherung sind im Wesentlichen dadurch bestimmt, dass: 왘 die Datenbank die abzubildende Realität korrekt wiedergibt, 왘 dabei vorgegebene Geschäftsregeln eingehalten werden und 왘 verhindert wird, dass Nutzer Datenbanktransaktionen auslösen, die nicht

zulässig sind.

335

7 Konsistenz und Mehrbenutzerbetrieb

Die erste Forderung kann durch ein Datenbanksystem nur in sehr eingeschränktem Maße unterstützt werden. Wenn ein Herr Schulze als »Schultze« gespeichert wird, kann das nicht vom Datenbanksystem bemerkt werden. Wenn dagegen eine Rechnung über 0,00 Euro geschrieben wird oder eine negative Anzahl von Artikeln auf dem Lieferschein erscheint, kann das vom Datenbanksystem erkannt werden (Rückgaben sollten als Rückgaben und nicht als negative Lieferungen behandelt werden). Die beiden weiteren Bedingungen können in heute verfügbaren Datenbanksystemen weitgehend unterstützt werden. Geschäftsregeln sind Festlegungen, wie bestimmte Geschäftsvorgänge zu behandeln sind. Dazu gehören zum Beispiel: 왘 Festlegung von Domänen für bestimmte Attribute 왘 Festlegung von Bedingungen, die für einzelne Tupel zu gelten haben 왘 Festlegung von Eindeutigkeitsbedingungen 왘 Festlegung von existenziellen Abhängigkeiten von Werten in derselben Rela-

tion oder in einer anderen Relation 왘 Festlegung von allgemeinen Beziehungen, in die auch Summen, Durchschnitts-

werte, Maxima oder Minima verschiedener Tabellen eingehen

7.4.1 Statische Geschäftsregeln Die hier angegebenen Geschäftsregeln sind in dem Sinne statisch, als sie festlegen, welche Bedingungen die Werte in der Datenbank jeweils erfüllen müssen. Es gibt hierbei keine Abhängigkeiten zu vergangenen Zuständen.

Domänen Zu den Geschäftsregeln zählen wir auch die Festlegung von Domänen für die Attribute. In unserem Beispiel haben wir für den Kundenstatus eine Domäne, die aus genau drei Werten {Stammkunde, Gelegenheitskunde, Werbemaßnahme} besteht. In SQL müssen wir solche Attribute durch einen der in Kapitel 4.3.2 eingeführten Datentypen kodieren.14 Die Einschränkung auf die zulässigen Werte ist durch eine CHECK-Klausel sicherzustellen. Als sinnvolle Lösungen kommen u. a. die folgenden Möglichkeiten infrage:

14 In Pascal und anderen höheren Programmiersprachen gibt es dagegen Datentypen, in denen alle möglichen Werte aufgezählt werden, ohne dass wir uns um die Implementierung hierfür kümmern müssen, z. B.: TYPE kundenstatus = (werbemassnahme, gelegenheitskunde, stammkunde)

336

Strategien zur Konsistenzsicherung

-- Variante 115 CREATE DOMAIN kunden_status SMALLINT CHECK (VALUE BETWEEN 0 AND 2); -- 0: Werbemaßnahme, -- 1: Gelegenheitskunde -- 2: Stammkunde -- Variante 2 CREATE DOMAIN kunden_status CHAR(1) CHECK (VALUE IN ('W', 'G', 'S'); -- 'W': Werbemaßnahme, -- 'G': Gelegenheitskunde -- 'S': Stammkunde -- Variante 3 CREATE DOMAIN kunden_status CHAR(17) CHECK (VALUE IN ( 'Werbemaßnahme','Gelegenheitskunde','Stammkunde'));

Eine weitere Möglichkeit ist die Benutzung einer zusätzlichen Tabelle, welche die Kategorien als Volltext enthält, wie in folgendem Beispiel dargestellt: -- Variante 4, mit Nachschlagetabelle CREATE TABLE kunden_status ( status_key SMALLINT NOT NULL, status_text CHAR(30) NOT NULL, PRIMARY KEY (status_key) ); CREATE TABLE kunde ( kunden_nr kunden_key NOT NULL, ... status SMALLINT NOT NULL, REFERENCES kunden_status ON UPDATE CASCADE ON DELETE RESTRICT, ... );

Diese Variante hat darüber hinaus weitere Vorteile: 왘 Wir kommen an die Volltexte heran, ohne dass wir dafür Funktionen (wie in

Kapitel 7.3.2 an Beispielen dargestellt) erstellen müssen. 왘 Wir können ohne Änderung der Datenbankdefinition die möglichen Werte

ändern. Das kann jeder, der die entsprechenden Änderungsrechte an der Tabelle kunden_status hat. 15 Die Umsetzung vom Kürzel in eine lesbare Form können wir durch Implementierung einer entsprechenden Funktion erreichen (vgl. Kapitel 7.3.2), die auch in SELECT-Anweisungen benutzt werden kann.

337

7 Konsistenz und Mehrbenutzerbetrieb

Häufig wird eine Domäne für natürliche Zahlen benötigt: Wenn es um die Anzahl realer Objekte geht, sind negative Zahlen nicht zulässig. Hier können wir eine entsprechende Domäne in folgender Weise definieren: CREATE DOMAIN cardinal INTEGER CHECK (VALUE >= 0);

Bedingungen für einzelne Tupel Als Beispiel hierfür betrachten wir die Bedingung, dass für eine Bestellung die Anzahl der auszuliefernden Artikel nicht größer sein kann als die Anzahl der bestellten Artikel. Sie kann bei Lieferengpässen gegebenenfalls kleiner sein. Dieses können wir durch eine CHECK-Klausel sicherstellen:16 CHECK (liefermenge = Einkaufspreis*1.2 OR sonderkondition = 1) );

Ein Spezialfall hiervon sind Festlegungen, dass für Attribute jeweils Werte eingetragen sein müssen, dass sie also keine Nullmarken enthalten dürfen. Sie sind durch die NOT NULL-Klausel anzugeben. Es können jedoch auch komplexere Bedingungen gelten, z. B. dass nur unter bestimmten Zusatzbedingungen ein Wert erforderlich ist. Dies ist dann explizit in einer CHECK-Klausel unter Verwendung des Prädikats IS NULL anzugeben, wie im folgenden Beispiel, wo für die Vertragsart 'Express' eine Telefaxnummer vorhanden sein muss:

16 Beachten Sie, dass die Bedingung der CHECK-Klausel nicht TRUE ergeben muss, sondern auch UNKNOWN zulässig ist (vgl. Kapitel 4.3.5, CHECK-Klausel). In diesem Beispiel ist es also keine Verletzung der CHECK-Klausel, wenn liefermenge eine Nullmarke enthält. Die in der Literatur zum Teil anzutreffende Formulierung CHECK (liefermenge IS NULL OR liefermenge < bestellmenge)

ist also nicht notwendig.

338

Strategien zur Konsistenzsicherung

... vertragsart CHAR(10), telefax CHAR(15), ... CHECK (vertragsart 'Express' OR telefax IS NOT NULL),

Eindeutigkeitsbedingungen Eindeutigkeitsbedingungen sind in der Regel Bedingungen an künstlich vergebene Schlüssel wie Kundennummer oder Artikelnummer. Sie sind durch eine PRIMARY KEY-Klausel festzulegen, bei eventuell weiteren Schlüsseln durch eine UNIQUE-Klausel (vgl. Kapitel 4.3.5). Eindeutigkeitsbedingungen können aber auch bei zeitlichen Festlegungen von Ressourcen auftreten, z. B. bei der Vermietung von Sommerhäusern. Falls die zeitlichen Festlegungen jeweils durch eine kleine Anzahl vorgegebener Intervalle beschrieben werden können (z. B. die Wochen von Samstag bis Samstag), kann die Bedingung, dass ein Sommerhaus nicht gleichzeitig an mehrere Kunden vermietet werden darf, durch eine UNIQUE-Bedingung festgelegt werden; vgl. hierzu folgendes Beispiel: CREATE TABLE vermietung ( vertrag CHAR(10) NOT NULL, objekt CHAR(10) NOT NULL REFERENCES haus, mieter CHAR(10) NOT NULL REFERENCES kunde, jahr SMALLINT NOT NULL, woche SMALLINT NOT NULL CHECK (WOCHE BETWEEN 1 AND 53), PRIMARY KEY (vertrag, jahr, woche), UNIQUE (objekt, jahr, woche) );

In diesem Beispiel ist eine Vermietung für drei Wochen durch drei Einträge zu beschreiben. Wenn wir dagegen die Mietdauer durch Anfangs- und Endtag festlegen, ist das Überschneidungsproblem nicht durch eine UNIQUE-Klausel zu lösen, da z. B. Vermietungen für die Intervalle [1.7.–15.7] und [8.7.–20.7] verschiedene Anfangs- und Endzeiten haben, aber sich dennoch überschneiden. Dieses Problem können wir durch eine Assertion lösen (vgl. Kapitel 4.3.6), über Trigger (für Einfügen und Ändern von Vermietungen) oder in folgender Weise durch eine Datensicht mit CHECK OPTION. Die Überschneidungsbedingung ist abstrakt dadurch ausgedrückt, dass es für eine Vermietung v1 keine Vermietung v2 geben darf mit v1.objekt=v2.objekt, sodass

339

7 Konsistenz und Mehrbenutzerbetrieb

sich die Intervalle [v1.beginn,v1.ende] und [v2.beginn,v2.ende] überlappen. Die Überschneidung kann in SQL durch das Prädikat (v1.beginn,v1.ende) OVERLAPS (v2.beginn,v2.ende)

abgefragt werden und, wenn das nicht zur Verfügung steht, durch: v2.beginn < v1.ende AND v1.beginn < v2.ende

Wir können das Überschneidungsproblem vollständig im Datenbankschema definieren, wenn wir im Folgenden für die Tabelle vermietung_x keine Zugriffsrechte zulassen, sondern nur für die Datensicht vermietung: CREATE TABLE vermietung_x ( vertrag CHAR(10) NOT NULL, objekt CHAR(10) NOT NULL REFERENCES haus, mieter CHAR(10) NOT NULL REFERENCES kunde, beginn DATE NOT NULL, ende DATE NOT NULL, PRIMARY KEY (vertrag) ); CREATE VIEW vermietung AS SELECT * FROM vermietung_x AS v1 WHERE NOT EXISTS ( SELECT * FROM vermietung_x AS v2 WHERE v1.objekt = v2.objekt AND v2.beginn < v1.ende AND v1.beginn < v2.ende) WITH CHECK OPTION;

Diese Datensicht vermietung ist entsprechend dem in Kapitel 6.4 Gesagten aktualisierbar. Somit ist es über diese Datensicht möglich, alle gewünschten Änderungen durchzuführen; es ist aber nicht möglich, ein Objekt für einen Zeitraum doppelt zu vermieten.

Existenzielle Abhängigkeiten In den meisten Fällen können existenzielle Abhängigkeiten über eine Fremdschlüsselbeziehung gelöst werden. Die existenzielle Abhängigkeit kann aber gewisse andere Bedingungen als Voraussetzung haben. In diesen Fällen können wir keine Fremdschlüssel verwenden. Als Beispiel betrachten wir folgende Bedingung: »Für jeden Kunden, der die Waren über Bankeinzug erhält, muss ein Girokonto existieren.« Dies können wir durch eine Assertion beschreiben (die leider noch nicht in vielen Datenbanksystemen verfügbar ist), oder wir können Trigger dazu verwenden (die noch nicht zum Standard gehören und außerdem den Nachteil haben, dass sie pro-

340

Strategien zur Konsistenzsicherung

zedural und nicht deklarativ sind). Wir zeigen im Folgenden, dass solche komplexen Bedingungen auch ohne Trigger und ohne Assertions über Datensichten mit einer CHECK-Klausel gelöst werden können. Hierzu definieren wir die Kundentabelle als kunde_x, auf die kein Benutzer Zugriffsrechte bekommt. Darüber legen wir eine Datensicht kunde, für welche die entsprechenden Zugriffsrechte vergeben werden: CREATE TABLE kunde_x( kunden_nr Kunden_Key NOT NULL, status Kunden_status NOT NULL, name PersonenName NOT NULL, strasse Strassenname NOT NULL, plz Postleitzahl NOT NULL, ort ortsname NOT NULL, letzte_bestellung DATE, letzte_werbeaktion DATE, zahlung Zahlungsart NOT NULL, PRIMARY KEY (kunden_nr) ); CREATE VIEW kunde AS SELECT * FROM kunde_x AS k WHERE zahlungsart 'B' OR EXISTS ( SELECT * FROM girokonto g WHERE k.kunden_nr = g.kunden_nr) WITH CHECK OPTION;

Die Datensicht kunde ist wiederum aktualisierbar. Somit können alle gewünschten Änderungen durchgeführt werden. Es ist aber nicht möglich, einen Kunden zu erfassen oder dessen Daten so zu ändern, dass er das Attribut zahlungsart = 'B' hat, aber kein entsprechendes Girokonto.

Beziehungen mit Aggregatfunktionen In diese Kategorie fallen Bedingungen wie die folgende: »Der Wert der bestellten Artikel in einer Bestellung darf den Betrag 1.000,- Euro nicht übersteigen«. Eine solche Bedingung kann ähnlich wie die beiden letzten Beispiele durch eine Assertion, durch Trigger oder durch eine Datensicht sichergestellt werden. Wir formulieren hier eine Assertion: CREATE ASSERTION nicht_mehr_als_1000 CHECK (1000 >= ALL (SELECT SUM(gesamtpreis) FROM position GROUP BY bestell_nr) );

341

7 Konsistenz und Mehrbenutzerbetrieb

7.4.2 Dynamische Geschäftsregeln Neben den statischen Geschäftsregeln können wir Regeln festlegen, welche Bedingungen Änderungsoperationen erfüllen müssen. Hierbei stellen wir Regeln auf, die nicht nur die zulässigen Datenbankzustände einbeziehen, sondern die Bedingungen beziehen sich auf den Datenbankzustand vor einer Änderung und den Zustand nach einer Änderung. Dynamische Geschäftsregeln treten u. a. in folgenden Fällen auf: 왘 Attribute, die Zustände von Entitäten beschreiben, dürfen nicht in beliebiger

Reihenfolge auftreten. 왘 Attribute dürfen nicht geändert werden – allerdings dürfen Nullmarken durch

einen Wert ersetzt werden. 왘 Bestandsänderungen (z. B. Kontostand, Warenbestand) dürfen nur durch vor-

her festgelegte Änderungsoperationen (z. B. Buchung, Warenentnahme) verändert werden, wobei die Änderungsoperationen zu protokollieren sind.

Zustandsübergänge Als Beispiel hierfür kann der Kundenstatus gelten, wenn wir vereinbaren, dass nur folgende Übergänge möglich sind: werbemassnahme -> gelegenheitskunde gelegenheitskunde -> stammkunde, stammkunde -> gelegenheitskunde

Zur Kontrolle dieser Regeln können wir entweder einen Trigger benutzen, der vor der Änderung des Attributs status aktiviert wird und andere Übergänge verhindert, oder wir verwenden eine Datenbankprozedur für die Änderung eines Zustandes. Für das Attribut status darf dann kein Änderungsrecht existieren.

Änderungsrecht für Nullmarken Hier haben wir eine Art von »Einfügen« neuer Daten (Wert für ein Attribut, das vorher eine Nullmarke hatte). Allerdings ist dieses Einfügen mit der UPDATEAnweisung durchzuführen. Neben den Möglichkeiten, die im vorigen Abschnitt genannt wurden, können wir diese Bedingung durch eine Datensicht festlegen. Wir gehen im Folgenden davon aus, dass es unzulässig ist, bei einem Artikel die Verpackung zu ändern, es sei denn, es ist keine Verpackung eingetragen. In diesem Fall wird für die Verpackung kein Änderungsrecht vergeben – es wird aber zusätzlich eine Datensicht erzeugt, welche die Artikel ohne Verpackung enthält. Hierfür werden Änderungsrechte vergeben: CREATE VIEW artikel_ohne_verpackung AS SELECT artikel_nr, verpackung FROM artikel WHERE verpackung IS NULL;

342

Strategien zur Konsistenzsicherung

GRANT INSERT(artikel_nr,bezeichnung,verpackung,...), UPDATE(artikel_nr,bezeichnung,...), SELECT, DELETE ON artikel TO lager; GRANT UPDATE (verpackung) ON artikel_ohne_verpackung TO lager;

Protokollierte Bestandsänderungen Als Beispiele betrachten wir folgende Geschäftsregeln: 왘 Für jeden Zahlungsausgang ist zu verbuchen, wer diese Zahlung wann veran-

lasst hat. 왘 Für jede Änderung des Artikelbestandes muss ein Änderungsbeleg vorhanden

sein. Eine solche Bestandsänderung kann z. B. sein: eine Position in einer Auslieferung, eine Warenentnahme für betriebsinterne Zwecke, eine Entnahme von beschädigter, defekter oder verdorbener Ware, ein Wareneingang vom Lieferanten, die Aufnahme eines eigenen Produkts, die Rücknahme aus einer Lieferung und als Letztes eine Bestandskorrektur, wenn bei einer Inventur eine Abweichung des Istbestands vom Sollbestand nach Buchführung herauskommt. Die erste Aufgabenstellung können wir dadurch lösen, dass wir eine Tabelle ueberweisung mit den Feldern veranlasser und zeitpunkt anlegen , auf die keine

Einfüge- und Änderungsrechte vergeben werden, aber stattdessen Standardwerte durch DEFAULT-Klauseln (unveränderlich) festgelegt werden17, wie im Folgenden dargestellt: CREATE TABLE ueberweisung ( empfaenger CHAR(30) NOT NULL, blz DECIMAL(8) NOT NULL, konto_nr CHAR(10) NOT NULL, betrag MONEY, grund CHAR(120), veranlasser CHAR(12) NOT NULL DEFAULT CURRENT USER, zeitpunkt TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP ); GRANT INSERT (empfaenger, blz, konto_nr, betrag, grund) TO haushalt;

17 Falls – wie in Sybase SQL Anywhere – für das INSERT-Privileg keine Spalten angegeben werden können, müssen wir auch hier den Umweg über eine Datensicht nehmen, welche die beiden Attribute veranlasser und zeitpunkt nicht enthält.

343

7 Konsistenz und Mehrbenutzerbetrieb

Die zweite Aufgabe können wir mithilfe zweier zusätzlicher Tabellen18 für die Warenzugänge und Warenabgänge lösen, in welche die Bestandsänderungen mit Datum, Uhrzeit, gegebenenfalls Veranlasser und Art der Änderung einzutragen sind. Für diese Tabellen darf es kein DELETE-Privileg und kein UPDATE-Privileg geben: Buchungen dürfen nicht gelöscht und nicht nachträglich verändert werden; eine falsche Buchung muss durch eine weitere Korrekturbuchung mit umgekehrtem Vorzeichen rückgängig gemacht werden. Des Weiteren darf auf der Spalte bestand der Tabelle artikel kein UPDATE- oder INSERT-Privileg existieren. Es ist dann sicherzustellen, dass für jede Bestandsänderung die entsprechende Tabelle gleichzeitig mit aktualisiert wird. Dies lässt sich auf zwei Weisen erledigen: 왘 Es wird ein Satz in die Buchungstabelle geschrieben. Dafür muss ein INSERT-

Privileg vorhanden sein. Die entsprechenden Änderungen im Bestand werden durch Trigger veranlasst. 왘 Es wird kein Recht auf die Buchungstabellen vergeben. Änderungen erfolgen

jetzt über Datenbankprozeduren (vgl. Kapitel 7.3.1, dort finden Sie auch die Beispiele). Die berechtigten Benutzer bekommen dafür ein EXECUTE-Privileg für die entsprechenden Prozeduren. Die Benutzung von Datenbankprozeduren ist die direktere Lösung dieses Problems. Sie entspricht dem aus der Programmierung bekannten Verfahren der abstrakten Datentypen und der Datenkapselung bei der objektorientierten Programmierung. Die Verwendung von Triggern hat aber einen Vorteil, der für eine Reihe von mittleren Anwendungen ins Gewicht fallen kann: Falls für die Entwicklung des Frontend-Systems ein über ODBC angeschlossenes PC-Datenbanksystem wie z. B. Access verwendet wird, können wir die Oberfläche unabhängig vom Backend entwickeln. Die Daten werden in die Buchungstabellen eingetragen, und die Trigger sorgen dafür, dass die Bestandsänderung veranlasst wird. Es müssen keine ODBCProzeduren für den Aufruf der Datenbankprozeduren erstellt werden.

18 Es reicht im Prinzip eine Tabelle, in der die Zugänge durch positive Zahlen, die Abgänge durch negative Zahlen einzutragen sind. Die Formulierung der Konsistenzregeln und die Regelung der Zugriffsrechte sind dann eventuell etwas umständlicher. Es können auch mehr als zwei Tabellen genommen werden – für jede Art der Bestandsänderung eine eigene Tabelle. Dies führt aber schnell zu einer unübersichtlichen Menge von Tabellen.

344

Der Systemkatalog Der Systemkatalog besteht aus Tabellen und Datensichten, die Informationen über die diversen Datenbankobjekte wie Tabellen, Datensichten, Benutzer sowie Zugriffsrechte und Indizes enthalten. Er erfüllt bei SQL eine doppelte Funktion: Einerseits kann ein Benutzer daraus Namen und Struktur von Datenbankobjekten entnehmen, die er für seine Arbeit mit SQL braucht. Andererseits benötigt das System selbst solche Informationen, um über die Datenbank Buch zu führen und Datenmanipulationen auszuführen. Beispielsweise benutzt das DBMS den Katalog, um zu registrieren, welche Tabellen es gibt und welche Spalten sie enthalten. Ändert sich die Struktur einer Tabelle, wird eine neue Tabelle erstellt oder eine alte gelöscht, so spiegelt sich dies im Datenkatalog wider. SQL prüft mittels des Katalogs, ob ein Benutzer Zugang zur Datenbank erhält und weiter dazu berechtigt ist, eine bestimmte Tabelle zu bearbeiten (siehe Kapitel 7). Wenn der DBA einen neuen Benutzer anmeldet, so werden Benutzername und Passwort in einer Datenkatalogtabelle niedergelegt. Der Query-Optimizer ermittelt bei Abfragen die zu der betroffenen Tabelle gehörigen Indizes und die jeweiligen Indexspalten aus dem Katalog. Jedes DBMS muss im Prinzip einen Systemkatalog enthalten. Jedoch ist der Zugriff darauf unter Umständen nur über spezielle Programme und für einen Benutzer unter Umständen gar nicht ohne Weiteres möglich. Die Besonderheit des SQLDatenkatalogs ist, dass die Tabellen und Datensichten, aus denen er sich zusammensetzt, mit dem SQL-Befehl SELECT erreichbar sind wie jede andere Tabelle. Die unerlässliche Dokumentation jeder Datenbank wird damit automatisch geleistet, ist jederzeit abrufbar und stets auf dem neuesten Stand.

8.1 Der Systemkatalog im SQL-Standard In SQL wird ein INFORMATION_SCHEMA gefordert, das aus einer Sammlung von Datensichten besteht. Die Basistabellen, auf denen diese Datensichten beruhen, werden ebenfalls im Standard aufgeführt. Das zugehörige Schema wird DEFINITION_SCHEMA genannt.1 Es ist aber nicht so gemeint, dass ein standardkonformes DBMS genau diese Basistabellen und Datensichten bereitstellen muss. Das INFORMATION_SCHEMA kann und darf durch Datenbankhersteller jederzeit erweitert werden, wenn dies für die Funktion des DBMS erforderlich ist.

1 Vgl. [MeSi02 S. 715f].

345

8 Der Systemkatalog

Ein Benutzer hat auf Systemtabellen keinen direkten schreibenden Zugriff. Keiner der Befehle INSERT, UPDATE oder DELETE kann angewandt werden. Implizit werden jedoch bei allen DDL-Befehlen in Systemtabellen Zeilen eingefügt, verändert oder gelöscht. So zieht beispielsweise die Ausführung eines CREATE TABLEBefehls die Eintragung etlicher neuer Zeilen mit Daten über die Tabelle, ihre Spalten und Konsistenzbedingungen nach sich. DROP TABLE hingegen führt zu Löschoperationen in den Systemtabellen. Zu Informationszwecken soll das INFORMATION_SCHEMA allen Benutzern zur Verfügung stehen, wenn auch in unterschiedlicher Detaillierung. Daher ist einheitlich für alle Datensichten der Benutzergruppe PUBLIC das SELECT-Privileg zugewiesen. Um benutzerspezifische Details wiederzugeben und nichtöffentliche Informationen zu verbergen, enthalten die Datensichten meist eine dynamische Selektionsklausel, sodass jeder Benutzer nur Daten über seine eigenen und die ihm zugänglichen fremden Datenbankobjekte erhält. Das Selektionsattribut (z. B. OWNER), das den Eigentümer oder den berechtigten Benutzer (z. B. GRANTEE) eines Datenbankobjekts enthält, wird dazu mit der Pseudospalte CURRENT_ USER verglichen, in welcher der Name des angemeldeten Benutzers zur Verfügung steht. Einige der wichtigsten Datensichten des INFORMATION_SCHEMA stellen wir kurz vor.2 Unter »Benutzer« verstehen wir den in der Datenbank niedergelegten Namen des jeweils für die aktuelle Sitzung beim DBMS angemeldeten Benutzers. Die Begriffe »zugänglich« und »eigen« beziehen sich jeweils auf den Benutzer.3 Name der Datensicht

Erläuterung

SCHEMATA

zeigt alle Schemata, die dem Benutzer gehören.

DOMAINS

zeigt die zugänglichen Domänen.

TABLES

zeigt die zugänglichen Tabellen.

VIEWS

zeigt die zugänglichen Datensichten.

COLUMNS

zeigt die Spalten der zugänglichen Tabellen.

TABLE_CONSTRAINTS

zeigt die für die eigenen Tabellen definierten Konsistenzbedingungen.

REFERENTIAL_CONSTRAINTS

zeigt die eigenen referenziellen Integritätsbedingungen, also die, die sich auf die eigenen Tabellen beziehen.

ASSERTIONS

zeigt die vom Benutzer eingerichteten Assertions.3

COLUMN_DOMAIN_USAGE

zeigt die eigenen Tabellenspalten in Verbindung mit der bei ihrer Definition verwendeten Domäne.

Tabelle 8.1: Datensichten des INFORMATION_SCHEMA in SQL 2 Eine vollständige Übersicht findet man in [MeSi02 Kap. 22]. 3 Assertions sind Integritätsbedingungen, die sich auf Werte aus mehreren Zeilen unterschiedlicher Tabellen beziehen können (siehe Kapitel 4.3.6).

346

Systemtabellen in SQL-Implementationen

Name der Datensicht

Erläuterung

ABSTRACT_DATA_TYPES

zeigt die dem Benutzer zugänglichen abstrakten Datentypen.

COLUMN_ABSTRACT_DATA_TYPE_USAGE

zeigt die von einem abstrakten Datentyp abhängigen Tabellenspalten, soweit dem aktuellen Nutzer zugänglich.

Tabelle 8.1: Datensichten des INFORMATION_SCHEMA in SQL (Forts.)

8.2 Systemtabellen in SQLImplementationen Wir zeigen in diesem Abschnitt den Gebrauch einiger Systemtabellen anhand der Produkte Sybase Adaptive Server Anywhere und ORACLE. Bei manchen DBMS beginnen die Namen aller Katalogtabellen mit »SYS« – so auch bei Sybase Adaptive Server Anywhere. ORACLE stellt sinnigerweise jeweils das Präfix »USER_«, »DBA_« beziehungsweise »ALL_« voran. Damit ist durchgängig eine Auswahl der aufzulistenden Objekte impliziert, und die im Standard anzutreffende Vermischung von eigenen und zugänglichen Objekten wird vermieden. USER_objektkategorie

zeigt nur die eigenen Objekte des angemeldeten Benutzers.

z.B. USER_TABLES

ALL_objektkategorie

zeigt die dem Nutzer zugänglichen Objekte.

z.B. ALL_TABLES

DBA_objektkategorie

zeigt die einem Systemverwalter zugänglichen Objekte.

z.B. DBA_TABLES

Tabelle 8.2: Namenskonventionen für Systemtabellen bei ORACLE

Informationen über Tabellen SYSTABLE heißt bei Sybase die Katalogtabelle4, aus der man Informationen über die verfügbaren Tabellen erhält. Sie wird bei der Anlage einer neuen Datenbank vom System aus automatisch erzeugt. Ein entsprechender CREATE TABLE-Befehl sähe so aus:5 4 Ob es sich bei den im Folgenden vorgestellten Tabellen um Basistabellen oder Datensichten handelt, ist ohne Belang. Wir benutzen in jedem Fall den Begriff Tabelle. 5 Der Befehl läuft so aber nicht ab, denn dann müsste für SYSTABLE bereits bei der Anlage eine Zeile in SYSTABLE eingetragen werden.

347

8 Der Systemkatalog CREATE TABLE SYS.SYSTABLE ( table_id SMALLINT NOT NULL, file_id SMALLINT NOT NULL, count INTEGER NOT NULL, first_page INT NOT NULL, last_page INT NOT NULL, primary_root INT NOT NULL, creator SMALLINT NOT NULL, table_name CHAR(128) NOT NULL, table_type CHAR(10) NOT NULL, view_def LONG VARCHAR, remarks LONG VARCHAR, replicate CHAR(1) NOT NULL, PRIMARY KEY UNIQUE FOREIGN KEY FOREIGN KEY

( table_id ), ( table_name, creator ), ( creator ) REFERENCES SYS.SYSUSERPERM ( user_id ), REFERENCES SYS.SYSFILE

)

Jede Zeile in der Tabelle SYSTABLE beschreibt eine Basistabelle oder eine Datensicht. Man erkennt beispielsweise an der PRIMARY KEY-Klausel, dass es sich bei SYSTABLE in diesem DBMS selbst um eine Basistabelle und nicht um eine Datensicht handelt. Attribute wie file_id und first_page beziehen sich auf spezielle Formen der Speicherorganisation und sind produktspezifisch. Die im Standard vorgesehene Einschränkung auf Tabellen, die dem Nutzer zugänglich sind, ist hier nicht realisiert. Die Tabelle SYSTABLE zeigt alle Tabellen, und erst der Versuch, in einem SELECT-Befehl auf eine nicht verfügbare Tabelle zuzugreifen, führt zu einer Fehlermeldung. Es ist also erforderlich, eine Selektionsbedingung einzugeben, falls man nur die eigenen Tabellen sehen möchte, wie folgendes Beispiel zeigt: SELECT table_id, table_name, view_def, creator FROM systable WHERE creator > 3 table_id ======== 161 162 163 164 165 166

table_name ================ kunde mwstsatz bestellung artikel position girokonto

view_def creator ============================== ======= (NULL) 4 (NULL) 4 (NULL) 4 (NULL) 4 check(liefermenge 3 table_id ======== 161 162 163 164 165 166

table_name ============ kunde mwstsatz bestellung artikel position girokonto

view_def creator name ============================== ======= ============= (NULL) 4 chef2000 (NULL) 4 chef2000 (NULL) 4 chef2000 (NULL) 4 chef2000 check(liefermenge=0) BOOLEAN 2 check(@VALUE between 0 and 1) Prozentsatz 3 (NULL) ... Kunden_key 4 check(@VALUE>100) Kunden_status 1 check(@VALUE in('W','G','S')) Zahlungsart 1 check(@VALUE in('R','B','N','V','K') Artikel_key 4 (NULL) ...

Informationen über Integritätsbedingungen Wie bereits erwähnt, sind Integritätsbedingungen eigenständige Objekte. Sie können im Allgemeinen nicht einer bestimmten Tabelle zugeordnet werden, da sie sich auf mehrere Tabellen beziehen können. In unserem Anwendungsfall darf das Attribut zahlung in der Tabelle kunde nur dann den Wert 'B' haben, wenn in der Tabelle girokonto ein dem nämlichen Kunden per Fremdschlüssel zugeordnetes Tupel existiert. Sie können erst recht nicht einer einzelnen Spalte zugeordnet werden. Im Anwendungsbeispiel haben wir die Bedingung bestelldatum = 0 MINDESTBESTAND >= 0 NACHBESTELLMENGE is null or (NACHBESTELLMENGE >= 0)

PK_MWST

NO ACTION

PK_KUNDE

NO ACTION

ARTIKEL_NR IS NOT NULL MWST IS NOT NULL BEZEICHNUNG IS NOT NULL LISTENPREIS IS NOT NULL BESTAND IS NOT NULL BESTELL_NR IS NOT NULL KUNDEN_NR IS NOT NULL BESTELLDATUM IS NOT NULL

Übungsaufgaben Aufgabe 8.1 Ermitteln Sie die Systemtabellen des von Ihnen genutzten DBMS, die Informationen über 왘 Tabellen 왘 Spalten 왘 Benutzer 왘 Integritätsbedingungen

enthalten. Mit welchen Abfragen erhalten Sie die Daten 왘 Ihrer eigenen Datenobjekte? 왘 der Ihnen zugänglichen Datenobjekte?

353

8 Der Systemkatalog

Aufgabe 8.2 Welche Spalten kommen in mehreren Tabellen oder Datensichten vor? Aufgabe 8.3 Welche Spalten kommen in mehreren Basistabellen (ohne Datensichten) vor? Aufgabe 8.4 Ermitteln Sie alle zu Ihren Datenobjekten definierten Primär- und Fremdschlüssel.

354

Objektorientierung und SQL Nach der breiten Durchsetzung von relationalen Datenbanksystemen zu Beginn der 80er-Jahre wurde bald klar, dass für eine Reihe von Anwendungsgebieten das relationale Datenmodell keine adäquate Grundlage darstellt. Zu diesen »NichtStandard«-Anwendungen gehören u. a.: 왘 Kartografie und Katasterwesen 왘 Entwurf integrierter Schaltungen 왘 CAD-Datenbanken 왘 Datenbanken für Büro-Umgebungen, z. B. Dokumentenverwaltung 왘 Multimedia-Anwendungen

Zuerst erschien eine Reihe von Arbeiten, die das relationale Modell erweiterten (z. B. NF²-Modell: »non first normal form«) und dabei bewährte Konzepte der relationalen Datenbanken übernahmen. Später (ab etwa 1985) erschienen Arbeiten über objektorientierte Datenbanksysteme, die ein neues Datenbankmodell forderten als Ergänzung der bereits existierenden objektorientierten Programmiersprachen wie C++ und Smalltalk um die Möglichkeit, persistente Objekte zu schaffen und zu bearbeiten und in ihrer komplexen Objektstruktur in einer Datenbank zu speichern. Ein wichtiger Hinweis: Eine Datenbank ist noch nicht objektorientiert, wenn das Entwicklungssystem, mit dem die Anwendungen erstellt werden, objektorientiert ist (wie zum Beispiel über JDBC mit Java), sondern die Eigenschaften der Objektorientierung beziehen sich auf das Datenmodell und die Datenspeicherung.

Entwicklungstendenzen der Objektorientierung im Datenbankbereich Diese Diskussionen führten zuerst im Forschungsbereich zur Entwicklung von Prototypen von objektorientierten Datenbanken, die zum Ende der 80er-Jahre auch auf den Markt drängten. Um die Entwicklungstendenzen in eine bestimmte Richtung zu lenken, haben im Jahre 1989 sechs Autoren aus mehreren Ländern auf der DOOD-Konferenz in Kyoto das Object-Oriented Database Manifesto vorgestellt, das einige Anforderungen an objektorientierte Datenbanksysteme stellt (nachzulesen in [Atki89]). Bei der Entwicklung objektorientierter Datenbanksysteme waren von Anfang an zwei Linien erkennbar. Die Vertreter der einen Linie waren der Ansicht, dass man objektorientierte Datenbanken völlig neu entwickeln müsse, um eine adäquate Technik zu schaffen, die das objektorientierte Paradigma möglichst effektiv realisiert. Die bisherige Datenbanktechnologie hielten sie für unbrauchbar, weil auf

355

9 Objektorientierung und SQL

einer völlig anderen theoretischen Grundlage entstanden. Die andere Linie propagierte den Ansatz, auf der bewährten relationalen Technologie aufbauend, objektorientierte Konzepte zu verwirklichen.

ODMG und OMG Um frühzeitig einen Standard für OO-Datenbanken zu definieren, haben sich 1993 mehrere Personen aus verschiedenen Softwarehäusern zusammengefunden. Diese Gruppe hat sich den Namen ODMG (Object Database Management Group) gegeben. Es findet eine Zusammenarbeit mit der OMG (Object Management Group) statt, die Standards für objektorientierte Methoden für Betriebssysteme und Netze entwickeln will. Das Ergebnis ist 1993 zuerst veröffentlicht worden. Die derzeit aktuelle Version 3.0 aus dem Jahr 2000 ist in [Catt00] verfügbar. Dieser Standard ist noch kein Industriestandard oder gar eine Norm – er sollte aber verhindern, dass eine Vielzahl unterschiedlicher, nicht miteinander kompatibler Systeme auf dem Markt erscheint. Gegenstände der Standardisierung sind unter anderem ein objektorientiertes Datenmodell mit einem Typenkonzept, das Konstruktoren für komplexe Datenstrukturen wie set, bag, list umfasst sowie die Bildung von Typhierarchien mit Mehrfachvererbung festschreibt. Es werden eine Objektdefinitionssprache ODL und eine Sprache zur Datenmanipulation OQL (Object Query Language) definiert. Ferner wird die Sprachanbindung an C++, Smalltalk und Java behandelt. Die neuere Entwicklung ist, dass die OMG eine »Object Database Technology Working Group (ODBT WG)« ins Leben gerufen hat, welche die OMGNorm in Richtung eines »4th Generation Standard« fortentwickeln will.1

Objektrelationale Datenbanken Dagegen argumentierten die Vertreter der anderen Linie, dass die Entwicklung der relationalen Datenbanktechnologie bis zum heutigen Reifegrad Millionen von Entwicklungsstunden gekostet habe, die man nicht einfach auf den Müllhaufen der Geschichte werfen solle. Schließlich geht es dabei nicht nur um das Speichern und Wiederfinden von Daten, sondern auch um Konzepte der Datensicherheit, Nebenläufigkeit, Datenverteilung, unterbrechungsfreien Betrieb 7*24 Stunden lang und vieles mehr. Hinzu kommt das Argument der »sanften Migration«: Wenn auf der ganzen Welt Millionen von Anwendungen in Wirtschaft und Verwaltung produktiv genutzt werden, wird es eine neue Technologie, in der die alten Systeme keinen Bestand mehr hätten, schwer haben, sich durchzusetzen. Aus diesen Erwägungen ergab sich der Ansatz, vorhandene relationale Datenbanksysteme um objektorientierte Konzepte zu erweitern; alte Systeme laufen unverändert weiter. Dieser Ansatz wird »objektrelational« genannt. Er hat in den SQL-Standard Einzug gehalten und wird von etablierten Anbietern relationaler DBMS vorangetrieben, allerdings in höchst uneinheitlicher Weise.

1 Details sind zu finden unter www.odbms.org.

356

Das objektorientierte Datenbankmodell

Wir stellen in Abschnitt 9.1 zunächst in Kurzform das objektorientierte Datenbankmodell vor2. In Abschnitt 9.2 behandeln wir die objektorientierten Konzepte, die in den SQL-Standard aufgenommen wurden. Kapitel 9.3 stellt eine objektrelationale Fassung unserer Versand-Datenbank vor, wie man sie mit Oracle realisieren kann. Wir kommen nicht umhin, auch auf die eine oder andere theoretische Ungereimtheit hinzuweisen. Der Begriff »Objektorientierung« existiert zwar schon seit einer gehörigen Anzahl von Jahren, enthält aber durchaus widersprüchliche Momente, die immer wieder Anlass für allerlei Diskussionen unter den Experten und Stoff für diverse Publikationen liefern.3

9.1 Das objektorientierte Datenbankmodell Wir beschreiben nun einige wichtige Eigenschaften des objektorientierten Datenmodells.4 Die zunächst recht abstrakten Begriffe, die wir gleich vorstellen, werden in den folgenden Abschnitten an Beispielen erläutert. In den dann folgenden Konkretisierungen – objektrelationale Erweiterungen ab SQL:1999 und objektorientierte Datenbanken nach ODMG – sind jeweils nur einige der hier angesprochenen Möglichkeiten konkret ausgebildet und dann in einer jeweils unterschiedlichen Syntax.

9.1.1 Grundbausteine Basiselemente des objektorientierten Datenbankmodells sind Objekte und Literale. Jedes Objekt hat eine eindeutige Identität. Ein Objekt kann grundsätzlich im Laufe seiner Existenz seinen Zustand ändern. Ein Literal hat keine Identität; es hat auch keinen änderbaren Zustand, sondern beschreibt einen Wert. So ist ein Mitarbeiter oder ein Artikel ein Objekt – eine Zahl, eine Zeichenkette oder eine feste Uhrzeit ist ein Literal. Objekte und Literale werden durch Typen kategorisiert. Alle Elemente eines gegebenen Typs haben dieselbe Menge an abstrakten Eigenschaften (Attribute) und dasselbe Verhalten. Ein Objekt wird als Instanz seines Typs bezeichnet. Der Zustand eines Objekts wird durch die Werte beschrieben, die seine Eigenschaften aufweisen, und durch die Beziehungen, die es zu anderen Objekten hat.

2 Einige Bemerkungen zum objektorientierten Datenmodell finden sich auch in Kapitel 3.3, in dem wir die objektorientierte »Entwurfssprache« UML behandeln. 3 Beispiele dafür: [DaDa00], [Fran03], [JäHe02]. 4 Vgl. [Atki89]. Siehe auch [HeSa95 S 114ff].

357

9 Objektorientierung und SQL

Das Verhalten eines Objekts wird durch die Menge an Operationen (Methoden) beschrieben, die das Objekt ausführen kann. Operationen können eine Liste von Ein- und Ausgabeparametern haben, die einem bestimmten Typ angehören. Eine Operation kann ein typisiertes Ergebnis zurückgeben. Eine objektorientierte Datenbank speichert Objekte. Sie basiert auf einem Schema und enthält Instanzen, die auf den im Schema definierten Typen basieren.

9.1.2 Typen Typen dienen der abstrakten Beschreibung von Objekten hinsichtlich ihrer Zustände und ihres Verhaltens. Sie werden durch eine externe Spezifikation und eine oder mehrere Implementierungen beschrieben. Die externe Spezifikation beschreibt die Eigenschaften und Operationen, die für Benutzer sichtbar sind. Eine Implementierung besteht aus Datenstrukturen und Methoden, welche die Attribute und Operationen realisieren und die in einer konkreten Programmiersprache erstellt worden sind. Beispielsweise kann die externe Spezifikation eines Typs Person durch seine Attribute Name, Adresse, Familienstand und durch eine Operation heiraten beschrieben werden. Diese sind für den Nutzer sichtbar. Verborgen hingegen sind die interne Repräsentation der Attribute (so könnte z. B. das Attribut Familienstand = verheiratet durch die Zahl 3 dargestellt werden) und der Algorithmus, mit dem der Familienstand beim Aufruf der Operation heiraten geändert wird. Typen können von Benutzern definiert werden. Ausgehend von primitiven Datentypen (wie numerische Datentypen, Zeichenketten, logische Werte, Datum, Uhrzeit, Aufzählungstypen) können durch Konstruktoren5 orthogonal6 neue Datentypen zusammengesetzt werden.

Tupel Einen Datentyp, der aus mehreren Komponenten (unterschiedlicher) Datentypen zusammengesetzt ist, erhalten wir durch den Tupel-Konstruktor. In Pascal und Modula entspricht dieses dem RECORD, in C, und C++ dem struct, in C++ und Java einer Klasse, die nur public-Attribute, aber keine Operationen besitzt. Ein Beispiel dafür ist die Zusammensetzung einer Adresse aus den Attributen Straße, Hausnummer, PLZ und Ort. Es kann ein Typ typ_adresse definiert werden, der intern

5 Der Begriff Konstruktor wird in unterschiedlichen Zusammenhängen mit unterschiedlicher Bedeutung verwendet: Wir meinen hier ein (sprachliches) Mittel, um komplexe (= zusammengesetzte) Datentypen zu konstruieren. Wenn wir dann auf diese Weise zu einer Klasse gekommen sind, gibt es in der Klasse jeweils mindestens einen Konstruktor (jetzt andere Bedeutung!); damit wird beschrieben, was beim Anlegen eines neuen Objektes in der Klasse durchzuführen ist. 6 Der Begriff »orthogonal« wird auf der nächsten Seite eingeführt.

358

Das objektorientierte Datenbankmodell

die genannten Attribute hat und bei der Definition einer Klasse als Ganzes für den Wertebereich eines strukturierten Attributs verwendet wird. Dieses könnte in etwa wie folgt aussehen: TYPE typ_adresse = RECORD strasse: STRING; hausnummer: INTEGER; plz: STRING; ort: STRING; END;

In SQL heißt dieser Konstruktor »ROW«.

Kollektionstypen Daneben gibt es die sogenannten Kollektionstypen (auch »Sammlungen« genannt), die wir im Folgenden erläutern. Diese Konstruktoren können auf beliebige – auch strukturierte – Typen angewandt werden.

Set Der SET-Konstruktor dient zur Konstruktion von Mengen im mathematischen Sinne. Eine Menge ist eine Zusammenfassung von Elementen desselben Datentyps. Ein Element kann in einer Menge entweder nicht oder (einmal) enthalten sein. Es gibt also keine Dubletten, und die Elemente sind ungeordnet. Die Anzahl der Elemente einer Menge ist nicht a priori begrenzt.

Bag oder Multiset Ein Bag oder eine Multimenge enthält ebenfalls Elemente desselben Datentyps; im Gegensatz zu Mengen sind hier Dubletten erlaubt. Eine Multimenge ist ebenfalls ungeordnet und hat beliebig viele Elemente.

List Auch eine Liste besteht aus Elementen desselben Datentyps. Jede Liste hat eine Länge, wobei die Elemente der Liste angeordnet sind: Wenn die Liste nicht leer ist, gibt es ein erstes Element; jedes Element der Liste – bis auf das letzte Element – hat genau ein Nachfolgeelement. Dasselbe Element kann in der Liste mehrfach vorkommen. Es können nachträglich Elemente vor das erste Element, zwischen zwei Elemente oder hinter das letzte Element eingefügt werden, außerdem können Elemente aus einer Liste entfernt werden.

359

9 Objektorientierung und SQL

Array Ein Array ist eine Kollektion, die eine vorher festgelegte Anzahl von Elementen enthält. Die Elemente eines Arrays sind indiziert (nummeriert), der Zugriff auf ein einzelnes Element erfolgt über seinen Index. Somit können auch keine weiteren Elemente eingefügt werden, sondern nur die Elemente an den festgelegten Positionen eingefügt, abgefragt oder geändert werden. Array-Elemente können allerdings Nullmarken enthalten.

Vararray Ein dynamisches Array hat sehr viele gemeinsame Eigenschaften mit einem Array. Die Elemente eines Array sind indiziert (nummeriert), der Zugriff auf ein einzelnes Element erfolgt über seinen Index. Ein dynamisches Array kann jedoch am Ende um jeweils ein Element verlängert und es kann an einer bestimmten Stelle abgeschnitten werden.

Dictionary Dictionary-Typen bestehen aus einer ungeordneten Menge von Paaren von eindeutigen Schlüsselwerten und zugeordneten Werten. Für einen Schlüssel k kann es jeweils nur maximal eine Eintragung [k,v] geben. Der Schlüssel dient also als Zugriffskriterium zum Zugriff auf die Werte. Eine Relation mit zwei Attributen s und w, wobei s der Primärschlüssel ist, ist in diesem Sinne ein Dictionary. Eine beliebige Relation mit einem Primärschlüssel k kann als Dictionary aufgefasst werden, wenn wir als Schlüssel die Zusammenfassung der Primärschlüsselattribute nehmen und als Daten die Zusammenfassung der übrigen Attribute. Diese Konstruktoren können orthogonal angewandt werden, was bedeutet, dass es für diese Konstruktionen keine spezifischen Einschränkungen gibt. Insbesondere können Datentypen auch durch mehrfache Anwendung der Konstruktoren erzeugt werden, also z.B. SET OF LIST OF RECORD a1: INTEGER; a2: STRING END

und dergleichen (die konkrete Syntax in ODMG und SQL ist hiervon jeweils unterschieden).

9.1.3 Vererbung Die Bildung von Hierarchien ist ein genuiner Bestandteil des objektorientierten Modells. Hier können Strukturen wie die Spezialisierung und Generalisierung unmittelbar umgesetzt werden, die in einem relationalen Datenmodell nicht ohne semantische Verluste abgebildet werden können. Zum Beispiel sind für einen Typ Person verschiedene Spezialisierungen in Hochschulangehörige und freie Dozenten denkbar, die ihrerseits weiter differenziert sein können. Dabei gilt für Studie-

360

Das objektorientierte Datenbankmodell

rende wie für Professoren bei allen spezifischen Unterschieden allgemein, dass sie Personen sind und damit alle Merkmale von Personen aufweisen.

Person

HochschulAngehöriger

Studierender

Dozent

sonstiger Bediensteter

Professor

Lehrbeauftragter

Abbildung 9.1: Typenhierarchie von Personen im Hochschulbereich

Im Bereich der Geografie – speziell der Kartografie – haben wir es mit Linienzügen zu tun, die Verschiedenes darstellen können, etwa Küstenlinien, Wasserwege, darunter Kanäle etc. Abgesehen von speziellen Eigenschaften wie Farben haben sie viele gemeinsame Eigenschaften, wie z. B. das Verhalten bei Veränderungen des Abbildungsmaßstabes.

Linienzug

Küstenlinie

Wasserweg

Verkehrsweg

Grenze

Kanal

Fluss

Straße

Schiene

Abbildung 9.2: Typenhierarchie von Linienzügen im Bereich Geografie

361

9 Objektorientierung und SQL

Die Hierarchiebildung wird dabei durch das Vererbungskonzept unterstützt, das zwei Ausprägungen hat: Strukturvererbung und Verhaltensvererbung. Die Strukturvererbung bedeutet, dass jeder Subtyp alle Attribute des Obertyps enthält. Er kann aber weitere Attribute enthalten oder auch Attribute des Obertyps in modifizierter Form übernehmen.7 Beispielsweise hat jede Person eine Adresse, bestehend aus Straße, Hausnummer, Name, PLZ und Ort. Bei Hochschulangehörigen könnte zusätzlich ein Datum des Ersteintritts verwaltet werden. Professoren und sonstige Bedienstete haben eine Gehaltsstufe, Studenten aber nicht; bei Studenten wird wegen häufigen Umzugs eine zusätzliche Heimatadresse verwaltet. Die Verhaltensvererbung erläutern wir im nächsten Abschnitt näher. Das ODMB-Objektmodell unterstützt die Mehrfachvererbung. In dem obigen Beispiel ist der Subtyp Professor zugleich eine Spezialisierung von Dozent und von HS-Angehöriger und erbt daher von beiden die Attribute und Methoden.

9.1.4 Verhaltensvererbung und Polymorphismus Die Verhaltensvererbung bedeutet, dass jeder Subtyp automatisch die Methoden des Obertyps übernimmt. Er kann jedoch zusätzliche Methoden anbieten oder ererbte Methoden redefinieren.8 Dies wird mit dem Begriff Polymorphismus gekennzeichnet. Beispielsweise können alle Hochschulangehörigen heiraten, aber nur bei Professoren und sonstigen Bediensteten ändert sich dann automatisch die Gehaltshöhe. Welche spezielle Ausprägung einer Methode für ein konkretes Objekt anzuwenden ist, muss das System anhand der Zuordnung zu einer Subklasse entscheiden. Beispiel: Für geometrische Figuren sei grundsätzlich die Methode Fläche definiert. Jede Figur ist ein Ellipsenelement, Kreissegment, Kreis, Polygonzug, Viereck, Dreieck oder achsenparalleles Rechteck. Für jede dieser Figuren ist eine Formel für Fläche definiert. Für eine beliebige Figur f kann erst zur Laufzeit bestimmt werden, welche Formel zu wählen ist. Die Benutzung des einheitlichen Operators Fläche, der für die verschiedenen Subklassen abweichend von der Oberklasse implementiert ist, wird als Überschreiben bezeichnet, das Laden des entsprechenden Programmcodes Überladung. Den Vorgang, dass ein Programm zur Laufzeit aufgrund der Subklassenzugehörigkeit eines Objekts den passenden Algorithmus für Fläche(f) auswählt, nennen wir spätes Binden.

7 Dabei muss der Typ des modifizierten Attributs eine Spezialisierung des geerbten Attributs sein. 8 Dabei muss die modifizierte Methode »aufrufkompatibel« zur ererbten sein. Das betrifft im Wesentlichen die Anzahl und die Typen der Ein- und Ausgabeparameter.

362

Das objektorientierte Datenbankmodell

9.1.5 Klassen und Extents Eine Klasse ist laut Definition der ODMG ein Typ, der unmittelbar Objekte aufnehmen kann. Objekte einer Klasse werden (in Übersetzung des englischen Begriffs Instance) auch als Instanzen bezeichnet. Zum einen hat eine Klasse also eine Funktion als Behälter. Daneben beschreibt eine Klasse durch ihre öffentlichen Operationen aber auch eine Schnittstelle, über die mit den Objekten der Klasse kommuniziert werden kann. Neben den durch die Klassen festgelegten Schnittstellen gibt es auch Interfaces, die eine Schnittstelle beschreiben, ohne damit die Struktur einer Klasse zu beschreiben. Beispielsweise kann es sein, dass in unserer objektorientierten Hochschulverwaltungs-Datenbank eine Bibliotheksverwaltung integriert ist. Kunden der Bibliothek können Hochschulangehörige sowie externe Kunden sein – externe Kunden bilden eine eigene Klasse –, und es gibt eine Schnittstelle, die das Verhalten der Hochschulangehörigen und der externen Kunden gegenüber der Bibliothek definiert. Ein Interface kann durch verschiedene Klassen implementiert werden. Klassen sind nach dem Verständnis der ODMG Vorschriften zur Bildung von konkreten Instanzen, also Objekten. Dagegen sind Schnittstellen abstrakte Beschreibungen des Verhaltens von Objekten. Für die Sammlung der Objekte einer Klasse sind Extents zuständig. Ein Extent bezeichnet die Menge aller Instanzen eines Typs. Extents werden auch als Behälter bezeichnet. Im Zusammenhang mit der Typen- und Klassenhierarchie gilt dabei: wenn A der Extent des Typs a_typ ist und b_typ ein Subtyp von a_typ, dann ist der Extent B von b_typ eine Teilmenge von A. Ein Objekt, das einer Unterklasse angehört, kann auch als Argument einer Operation der Oberklasse zugewiesen werden. Das Umgekehrte gilt nicht. Damit scheint das Vererbungskonzept in sich stimmig zu sein. Jedoch gibt es zwischen den Betrachtungsweisen auf Klassen als Typ mit Attributen und Operationen (intensionaler Klassenbegriff) einerseits und Klassen als Sammlung von Objekten Extent (extensionaler Klassenbegriff) Widersprüche.9 So erscheint das Konzept der Mehrfachvererbung beim intensionalen Klassenbegriff kritisch und führt zu Uneindeutigkeiten, wenn die mehreren Oberklassen, von denen eine Unterklasse erbt, gleichnamige Attribute oder Operationen aufweisen. Welche Variante soll dann übernommen werden? Beim extensionalen Klassenbegriff erscheint es hingegen ganz natürlich, dass eine Teilmenge von Objekten in mehreren überschneidenden Obermengen enthalten sein kann. Aber auch bei einfachen Vererbungsbeziehungen gibt es Phänomene, die mit dem außerinformatischen Begriff von Spezialisierung nicht übereinstimmen. So ist, mathematisch betrachtet, 9 Eine Darstellung der Problematik findet sich in [Fran03]. Auch Date und Darwen nehmen zu dem Problem ausführlich Stellung in [DaDa00].

363

9 Objektorientierung und SQL

ein Quadrat ein Rechteck mit der Spezialität, dass alle Seiten gleich lang sind. Ein Quadrat hat alle Eigenschaften eines Rechtecks: Alle Winkel sind rechte, gegenüberliegende Seiten sind gleich lang. In der Welt der objektorientierten Programmierung sind aber Rechtecke Quadrate, da sie alle Attribute von Quadraten (Seitenlänge a) enthalten, sich darüber hinaus aber durch weitere Attribute (Seitenlänge b) unterscheiden. In der objektorientierten Programmierung (im Gegensatz zu objektorientierten Datenbanken) wird in der Regel nicht zwischen Klasse und Extent unterschieden, da Objekte in Programmen erzeugt werden und über einen Bezeichner oder eine Referenz auf sie zugegriffen werden kann.10 In objektorientierten Programmiersprachen mit automatischer Speicherverwaltung (sog. Garbage Collection) werden nicht erreichbare Objekte automatisch aus dem Speicher entfernt (z. B. bei Smalltalk und Java). In Datenbanken wollen wir dagegen auch in Programmläufen auf Objekte zugreifen, die in anderen Programmläufen erzeugt wurden. Daher benötigen wir hier für viele Anwendungen Extents von Klassen, um alle Objekte einer Klasse verarbeiten zu können.

Abbildung 9.3: Begriffe im Zusammenhang mit objektorientierten Datenbanken

10 Auch in UML-Klassendiagrammen wird nicht zwischen Klassen als Typ und Extent unterschieden. Das führt zu dem Problem, dass man Typen, die »nur« dazu dienen, als Domäne eines Attributs verwendet zu werden (vgl. Adresse in Abschnitt 9.3), mit demselben Symbol darstellt wie Typen, deren Instanzen persistent gespeichert werden sollen, die also über einen Extent verfügen müssen. Mehr schlecht als recht behilft man sich mit Stereotypen, die lediglich als Texthinweis, beispielsweise , auf die weitere Bedeutung der Klasse hinweisen.

364

Das objektorientierte Datenbankmodell

9.1.6 Objektidentität und Gleichheit Ein Objekt muss systemweit identifizierbar sein. Die Identität (OID) wird vom System selbst gepflegt. Sie ist nicht von der Schemadefinition abhängig – es muss kein Attribut dafür definiert werden. Somit gibt es keine Wertzuweisungen, wie das bei Schlüsselwerten im relationalen Datenmodell der Fall ist. Die Objektidentität ist von allen Attributwerten unabhängig und kann durch keine Operation geändert werden. Nach Löschen eines Objektes darf seine OID auch nicht an ein anderes Objekt übergehen. Die Objektidentität ist nicht für Benutzer als Wert erkennbar, sie ist ein rein systeminternes Merkmal. Hier ist ein gravierender Unterschied zum relationalen Modell vorhanden, das Bezüge auf der Basis von Wertegleichheit herstellt und nicht auf der Basis von Objektidentität. Daher muss für Objekte der Begriff der Gleichheit von dem Begriff der Identität unterschieden werden. Gleichheit beruht auf dem Vergleich der Attributwerte von zwei Objekten, sodass zwei nicht identische Objekte in diesem Sinne »gleich« sein können.11 Die Rolle des Primärschlüssels, der bei relationalen Systemen Tupel identifiziert und die Verschiedenheit aller Tupel gewährleistet, wird damit entwertet. Gleichwohl kann es ein-eindeutige Attribute wie Kundennummern etc. geben, mit denen der Benutzer ein Objekt identifiziert.

9.1.7 Kapselung Objekte können nach außen hin »gekapselt« werden. Das bedeutet, dass ein direkter Zugriff von Benutzern respektive Anwendungsprogrammen auf die Attributwerte eines Objekts verwehrt ist. Kommuniziert wird mit Objekten ausschließlich über ihre Operationen (Methoden). Um den Namen der Dozenten von Seminaren zu erfahren, muss das Objekt Dozent eine Methode anbieten, die beispielsweise unter der Bezeichnung get_dozent aufgerufen werden kann und den geforderten Attributwert zurückliefert. Somit können Objekte zur Implementierung von abstrakten Datentypen verwendet werden. Es ist aber möglich, spezielle Komponenten bei der Deklaration der Objektklasse als PUBLIC festzulegen. Der Zugriff auf solche Attribute kann dann in einer SQLähnlichen Sprache formuliert werden, sodass die Kapselung vom Benutzer gar nicht bemerkt wird.

9.1.8 Lebenszeit von Objekten Zwei Lebenszeiten sind im Objektmodell vorgesehen, die bei der Anlage von Objekten angegeben werden müssen. Transiente Objekte sind an die Laufzeit des Programms gebunden, in dem sie erstellt werden. Sie verschwinden mit der Beendigung des Programms. Persistente Objekte werden durch das Objektdatenbank11 Die Alltagssprache unterscheidet auch zwischen »dasselbe« (Identität) und »das Gleiche« (Gleichheit).

365

9 Objektorientierung und SQL

Dozent SELECT * FROM dozent WHERE kurs_nr = 1234

einstelle_dozent() versetze_dozent() get_dozent() ... Methoden-Aufruf

get_dozent( )

Ergebnis

Petersen, Paul Matzen, Matthias Mäkinen, Tuula

Abbildung 9.4: Kapselung

Managementsystem verwaltet und existieren unabhängig von dem Programm, das sie ins Leben gerufen hat. Dieses sind die eigentlichen Datenbankobjekte.

9.1.9 Beziehungen Beziehungstypen12 werden zwischen Klassen definiert. Das ODMGObjektmodell unterstützt nur Beziehungen zwischen zwei Klassen. Im Entity-RelationshipModell und in der UML sind auch mehrstellige Beziehungen möglich. Kardinalitäten13 werden ebenso festgehalten wie im Entity-Relationship-Modell (vgl. Kapitel 3). Eine Beziehung ist selbst kein Objekt und hat keine Identität, sie dient lediglich dazu, einen Navigationspfad zwischen Objekten herzustellen und ermöglicht es Anwendungsprogrammen, die logischen Verbindungen zwischen Objekten verschiedener Klassen zu verfolgen. Die referenzielle Integrität wird durch das ODBMS gewährleistet. Die Definition von Beziehungen in Objektdatenbanken ist auf verschiedene Weise möglich. Wir zeigen in den Abschnitten 9.3 und 9.4 Beispiele dafür. 12 Wir unterscheiden Beziehungen, die sich jeweils auf konkrete Objekte (Kunde Peter Stein und Bestellung Nr. 151) beziehen, und Beziehungstypen, die auf einer höheren Abstraktionsebene Aussagen darüber treffen, ob und wie viele Objekte einer Klasse prinzipiell eine Beziehung zu Objekten einer anderen Klasse haben können. Wenn klar ist, was gemeint ist, kann man den Begriff »Beziehung« auch verwenden, wenn eigentlich von einem Beziehungstyp die Rede ist. 13 Kardinalitäten sind quantitative Aussagen über Beziehungen zwischen Objekten (Instanzen) der beteiligten Klassen. Hier werden die Klassen als Extent, Behälter für Objekte, angesehen.

366

Objektorientierung im SQL-Standard

9.2 Objektorientierung im SQL-Standard Im SQL-Standard wird Objektorientierung nicht verstanden als ein neues Paradigma, das den alten, relationalen Ansatz ablöst, sondern als eine Erweiterung des Konzepts, das damit sozusagen »aufwärtskompatibel« zum relationalen Modell herkömmlicher Prägung ist. Dieser Ansatz wird als »objektrelational« bezeichnet. So erlaubt SQL die Konstruktion von Datentypen beliebig komplexer Struktur einschließlich der Definition von Methoden, also »abstrakte Datentypen«. Diese Datentypen können Attributen zugewiesen werden, oder es können Objekttypen mit einer modifizierten Form der CREATE TABLE-Anweisung auf Basis eines Datentyps definiert werden. Die Implementierung eines Objekttyps ist immer eine Tabelle, wenngleich diese sich durch Attribute mit komplexer Struktur erheblich von einer »flachen« Tabelle, welche die erste Normalform erfüllt, unterscheiden kann. Die »klassischen Tabellen«, wie wir sie in den übrigen Kapiteln des Buchs behandelt haben, sind nur mehr ein Spezialfall des erweiterten Konzepts.

9.2.1 Definition von Typen und Tabellen SQL unterstützt die Definition benutzerdefinierter Datentypen mit folgenden Eigenschaften: 왘 Ein Typ ist zusammengesetzt aus einem oder mehreren Attributen. 왘 Jedes Attribut hat einen Datentyp, wobei Standarddatentypen wie INTEGER

ebenso verwendet werden können wie nutzerdefinierte Datentypen. 왘 Das Verhalten eines Datentyps wird durch Methoden (Funktionen oder

Prozeduren) bestimmt. 왘 Die Komponenten (Attribute und Methoden) sind gekapselt. Der Zugriff

erfolgt über systemgenerierte Funktionen (»get and set functions« [EiMe99, S. 131]). 왘 Um Instanzen eines Typs zu erzeugen, muss der systemgenerierte Konstruktor

benutzt werden. Der Konstruktor hat denselben Namen wie der Typ. 왘 Wertvergleiche und eine Sortierung der Exemplare eines Typs erfolgen anhand

benutzerdefinierter Funktionen für Vergleiche und Sortierung. 왘 Typen können hierarchisch aufgebaut sein. Alle Subtypen erben die Attribute

und Methoden der Supertypen und können diese umbenennen, umdefinieren und durch eigene Komponenten erweitern. Es ist keine Mehrfach-Vererbung vorgesehen. Ein Subtyp kann in der Regel dort verwendet werden, wo sein Supertyp erwartet wird.

367

9 Objektorientierung und SQL

Die SQL-Anweisung zum Anlegen eines abstrakten Datentyps lautet in vereinfachter Form: CREATE value_or_object TYPE typname [UNDER supertyp] [AS (attributliste)] [methodendeklarationsliste] value_oder_objekt ::= VALUE | OBJECT

Durch VALUE bzw. OBJECT wird festgelegt, ob der Typ instanziierbar ist, d.h., ob auf seiner Basis Objekttabellen erzeugt werden können. Ansonsten ist nur die Verwendung als Datentyp von Attributen möglich. Wenn ein Supertyp angegeben ist, erbt der aktuelle Datentyp dessen Attribute und Methoden. Zu jedem Element der Attributliste werden Name und Datentyp angegeben. Der Datentyp eines Attributs kann selbst wieder ein nutzerdefinierter Datentyp sein. Die Methodendeklaration enthält nur die Signatur, d. h. den Namen der Methode, die Eingabeparameter und den Datentyp des Rückgabewerts. Ein kleines Beispiel14 möge zunächst die Syntax erläutern. In Abschnitt 9.3 stellen wir ein ausführlicheres Beispiel vor, das dort allerdings vom Standard abweicht, da wir es unter Oracle erstellt haben. CREATE OBJECT TYPE otyp_angestellter UNDER otyp_person AS ( angest_nr INTEGER, gehalt DECIMAL(7,2)), INSTANCE METHOD gehaltserhoehung ( abs_oder_prozent BOOLEAN, betrag DECIMAL (7,2) RETURNS DECIMAL (7,2);

Hier wird ein Typ für Angestellte als Subtyp von otyp_person eingerichtet, der seinerseits zuvor deklariert worden sein muss und den wir hier einfach unterstellen. Die Attribute angest_nr und gehalt ergänzen die Attribute des Typs otyp_person. Die Methode gehaltserhoehung kann von außen aufgerufen werden. Von dieser Methode sehen wir hier nur die Signatur, aus der hervorgeht, dass man einen booleschen Wert zu übergeben hat, der festlegt, ob die Gehaltserhöhung um einen absoluten Geldbetrag oder prozentual erfolgt. Der Rückgabewert ist eine Zahl vom Typ DECIMAL(7,2), das neue Gehalt. Die Implementierung der Methode ist hier nicht sichtbar. Sie erfolgt mit prozeduralen Sprachelementen von SQL.15

14 Angelehnt an [EiMe99, S. 134]. 15 Vgl. Kapitel 7.3

368

Objektorientierung im SQL-Standard

Tabellendefinition auf Typbasis Benutzerdefinierte Typen, die mit dem Zusatz OBJECT angelegt wurden, können zur Definition von Tabellen verwendet werden. Der entsprechende Befehl lautet: CREATE TABLE tabelle OF typ

Dabei übernimmt die Tabelle alle Attribute und Methoden des Typs. Jedes Typattribut wird in eine Tabellenspalte übernommen. Da der Datentyp eines Attributs selbst wieder ein komplexer Typ sein kann, ist die Vorstellung von einer Tabelle als zweidimensionale Struktur von Zeilen und Spalten, wobei jede Zelle am Schnittpunkt von Zeile und Spalte einen skalaren Wert aufweist, allerdings überholt – er kann auch mehrwertig sein. Die erste Normalform ist somit nicht mehr notwendig. Dennoch werden die Begriffe Zeile, Spalte und Tabelle beibehalten. Jede Zeile in der typisierten Tabelle stellt eine Instanz des Objekttyps dar. Sie erhält automatisch eine systemgenerierte OID (»Object Identifier«), der von den Attributwerten unabhängig ist und vom Nutzer nicht geändert werden kann.

9.2.2 Konstruktoren für komplexe Typen Spezielle Konstruktoren sind für die Erzeugung von »Collection Types« vorgesehen (vgl. Abschnitt 9.1.2). SQL sieht die Konstruktoren ARRAY und MULTISET vor. MULTISET haben wir in 9.1.2 als bag eingeführt.

Beispiel: Eine Rundreise, die über bis zu zwölf Orte geht und mehrere Teilnehmer hat, kann folgende Attribute enthalten: teilnehmer otyp_person MULTISET, stationen otyp_ort ARRAY[12]

Die Schachtelung der Konstruktoren wie beispielsweise INTEGER ARRAY[6] MULTISET ist erlaubt. Als Datentypen kommen auch nutzerdefinierte in Frage. Wir können daher beispielsweise einen Objekttyp abteilung_typ definieren, der seine Angestellten enthält, indem wir folgende Attributspezifikation vornehmen: CREATE OBJECT abt_nr abt_name angestellte ... )

TYPE otyp_abteilung AS INTEGER, VARCHAR(20), otyp_angestellter MULTISET,

Ausprägungen der drei Kollektionstypen können auch leer sein. Ein weiterer Konstruktor, ROW, dient der Definition von Tupeln als Datentyp. Demnach hat eine herkömmliche SQL-Tabelle den Typ: ROW(attributliste) MULTISET.

369

9 Objektorientierung und SQL

Diese Strukturen umgehen Einschränkungen des relationalen Modells, die sich aus der Verpflichtung auf die erste Normalform ergeben (Kapitel 3.4). Im Resultat führt dort die Notwendigkeit zur Ausgliederung von Wiederholungsgruppen zur Verteilung zusammengehöriger Daten auf mehrere Tabellen. Im objektorientierten Modell können wir eine Menge von Tupeln als Einzelattribut in einen Objekttyp einbinden.

9.2.3 Referenzen Nachdem im objektorientierten Konzept die Identifizierung von Objekten über die systemverwaltete OID erfolgt und nicht über dessen (veränderliche) Attributwerte, hat konsequenterweise der Fremdschlüssel für den Bezug auf ein fremdes Objekt ausgedient. Bezüge werden über die OID hergestellt. Die Einbindung einer »fremden« OID in ein Objekt geschieht sozusagen als Referenz auf das fremde Objekt. Das Attribut, das diese Referenz enthält, wird folgendermaßen definiert: attribut REF (fremder_objekt_typ)

Beispielsweise kann in einer Bibliotheksverwaltung der Ausleiher eines Buchs als Bestandteil der Definition eines Buchtyps folgendermaßen festgehalten werden: CREATE OBJECT TYPE otyp_buch AS (signatur VARCHAR(12), ausleiher REF otyp_person, ....

Wenn nun eine Objekttabelle CREATE TABLE Buch OF otyp_buch

angelegt wird, enthält diese als Attribut die Referenz auf ein in einer anderen Tabelle gespeichertes Objekt vom Typ person_typ. Der Zugriff auf die Attribute des eingebetteten Typs erfolgt nunmehr im Vergleich zum Join bei der relationalen Modellierung über die »Punktnotation«. Wenn wir von obigem Beispiel ausgehen, erfahren wir den Namen des Ausleihers über den Ausdruck: buch.ausleiher.name

Diese Art des Zugriffs nennt man »navigierend«. Im UML-Klassendiagramm wird dies durch einen Pfeil an der Beziehungslinie kenntlich gemacht. Ein Beispiel dazu folgt im nächsten Abschnitt.

370

Objektrelationales Modell der Versand-Datenbank

Die Aufnahme eines Referenztyps in eine Tabelle setzt die selbstständige Existenz des referenzierten Typs mit eigenem Extent16 (Objekttabelle) voraus. Eine Wertzuweisung kann auch nur dann erfolgen, wenn die gewünschte Instanz des referenzierten Typs in der Datenbank vorhanden ist. So wird in der objektorientierten Welt die referenzielle Integrität realisiert. Im Unterschied dazu ist die Einbettung von Tupeln in eine Objekttabelle über die Typzuweisung freunde otyp_person MULTISET

so zu verstehen, dass die Personen, die in dieses Attribut eingetragen werden, nur als Attributwerte des Tupels existieren. Dies entspricht dem Konzept des abhängigen Entitätentyps, das wir in Kapitel 3.2 vorgestellt haben. Sie werden auch nicht getrennt erfasst, sondern bei der Instanziierung des sie enthaltenden Typs zusammen mit den anderen Attributwerten angegeben.

9.3 Objektrelationales Modell der VersandDatenbank Es folgen nun Beispiele für die im vorigen Abschnitt vorgestellten Modellelemente. Wir realisieren das in folgendem UML-Diagramm wiedergegebene objektorientierte Modell der Versand-Datenbank.17 Die Umsetzung der Beispiele folgt nicht dem Standard, wir haben sie mit einem Oracle-DBMS erstellt und getestet. Zur Erläuterung: Die Pfeile an den Beziehungslinien geben an, dass man von einem Objekt zum anderen navigieren kann. Beispielsweise können wir von Artikel zu mwst navigierend zugreifen, und zwar mit der »Punktnotation«, etwa in der Art: artikel.mwst.prozent. Beispiele dafür folgen in Abschnitt 9.3.5. Den Typ Adresse benutzen wir lediglich als benutzerdefinierten Datentypen für das Attribut Adresse unserer Klasse Kunde. Adressen werden selbst nicht gespeichert. Wir haben dies durch den Stereotyp kenntlich gemacht, die einzige Art, die UML zur Verfügung stellt, um solche Unterschiede deutlich zu machen. Sehr erhellend ist das aber nicht, da alle anderen Klassen natürlich auch Typen darstellen. Außerdem haben wir die Beziehung zwischen Bestellung und Position als Komposition dargestellt: Position ist ein Teil von Bestellung, und eine Position kann ohne Bestellung nicht existieren. Auch die Beziehung zwischen Kunde und Adresse kann als Komposition aufgefasst werden, da eine Adresse Teil eines Kun-

16 Ein Extent für einen Typ ist an eine Objekttabelle gebunden. Typen ohne Extent sind auch möglich; dieses ist z. B. sinnvoll für einen Typ für Adressen. 17 UML wird in Kapitel 3 beschrieben.

371

9 Objektorientierung und SQL

Abbildung 9.5: UML-Diagramm der Versand-Datenbank – objektorientiert

den ist und nicht ohne diesen existieren kann.18 Das Attribut telefonliste der Klasse Kunde ist für die Aufnahme mehrerer Werte vorgesehen. Kenntlich ist dies an den eckigen Klammern, die implementierungstechnisch für ein ARRAY stehen. Dazu gleich mehr. Kunde wird spezialisiert zu Kunde_mit_Bankeinzug. Nur Objekte der Unterklasse haben eine Bankverbindung. Es handelt sich um eine Teil-Ganzes18 Aber wehe, es gäbe noch eine weitere Klasse, die auch ein Attribut vom Typ Adresse enthält. Dann wäre die Adresse Teil von zwei Ganzen, ohne die sie nicht existieren könnte. Damit hätte die Komposition als Beziehungstyp keine Berechtigung mehr. Hinsichtlich des Gebrauchs von Kompositionen sieht man vielerlei unterschiedliche und durchaus gegensätzliche Varianten in Klassendiagrammen. Es gibt keine vereinheitlichte, verbindliche und vor allem exakte Definition, an der eindeutig zu entscheiden wäre, wohin diese Art von Beziehung gehört und wohin nicht, da der Begriff »Teil-Ganzes« ja auch unscharf ist. Wir raten dazu, Aggregationen und Kompositionen eher sparsam einzusetzen und in jedem Einzelfall zu prüfen, ob eine Teil-Ganzes-Beziehung vorliegt, die als solche kenntlich gemacht werden soll.

372

Objektrelationales Modell der Versand-Datenbank

Beziehung mit Existenzabhängigkeit, also eine Komposition. Bankverbindungen werden hier als Teil der Kunden verwaltet, da der Versandhandel keinen Wert darauf legt, einfach irgendwelche Daten über Konten zu sammeln. Es gibt eine Reihe von Abweichungen zwischen Oracle und dem Standard19; deren bedeutendste sind: 왘 Die Kollektionstypen, die zurzeit unterstützt werden, sind TABLE (entspricht

MULTISET(ROW))und VARRAY. Ein VARRAY ist im Wesentlichen ein Array mit einer jeweils vorgegebenen Maximalzahl von Komponenten; es kann aber auch weniger Komponenten enthalten. 왘 Oracle bietet zur Formulierung von Methoden eine eigene Programmier-

sprache an, deren Syntax von der SQL/PSM-Sprache abweicht. Sie ist als prozedurale Erweiterung von SQL entwickelt worden und heißt PL/SQL. Daneben können Methoden auch mit Java entwickelt werden. Daneben fällt auf, dass die unterstützten Möglichkeiten objektorientierter Datenmanipulation mittels SQL nicht orthogonal realisiert sind. Das bedeutet: Nicht überall, wo es theoretisch sinnvoll ist, auf einen Typen und seine Methoden zuzugreifen, ist es auch möglich.

Namenskonventionen Die erweiterten Möglichkeiten, die wir durch die Definition von Datentypen erhalten, bedingen gleichzeitig eine gesteigerte Vielfalt an Namen. Und weil es häufig notwendig ist, den Typkonstruktor beim Zugriff auf Attribute anzuwenden, kommt einer sauberen Unterscheidung von Typnamen, Attributbezeichnern und Objektnamen Bedeutung zu. Wir wenden in diesem Kapitel folgende Notationsrichtlinien an: Datenstruktur Name vorangestellt --------------------------------------------Object Type Names otyp_ Varray Names varr_ Nested Table Types ntyp_ Object Tables otab_ Relational Tables rtab_ Nested Table ntab_ Object View ov_ Local Variables lvar_ Input Parameters ip_ Output Parameters op_ Input-Output Parameters iop_

19 Siehe auch [CHRS98, S. 420ff.].

373

9 Objektorientierung und SQL

9.3.1 Abstrakte Datentypen als Wertebereich für Attribute In diesem Abschnitt geht es zunächst um Datentypen, die als Basis bei der Deklaration von Attributen und Variablen verwandt werden. Der SQL-Standard stellt dafür beispielsweise INTEGER, REAL und VARCHAR zur Verfügung. In Kapitel 4 haben wir behandelt, wie man mit dem Befehl CREATE DOMAIN eigene Datentypen als Domänen definieren kann. Die so erzeugten Domänen stellen aber im Grunde nichts anderes als einen Standard-Datentyp mit einer zusätzlichen Einschränkung des Wertebereichs dar. Dabei fehlt bisher eine Möglichkeit, die Funktionalität eines solchen Typs zu definieren. Zu einem abstrakten Datentyp gehört aber auch die Festlegung seines Verhaltens, seiner Vergleichbarkeit und der sonstigen Operationen, die mit ihm ausgeführt werden können.20 Wir erzeugen zunächst einen Basistyp mit Struktur. In jeder Adresse kommen immer wieder die Angaben strasse, plz, ort vor. Warum nicht einfach einen Typ Adresse definieren, der diese Attribute als Komponenten enthält? Im vorigen Unterkapitel haben wir dies im Klassendiagramm bereits so dargestellt: CREATE OR REPLACE TYPE otyp_adresse0 AS OBJECT ( strasse varchar2(30), plz varchar2(5), ort varchar2(30));

Damit wäre bereits ein benutzerdefinierter Typ angelegt, allerdings ohne eigene Funktionalität. Wollen wir neben Attributen auch Methoden definieren, so geschieht dies in zwei Schritten: Im ersten Schritt wird der Typ mit seinen Attributen und Methodensignaturen definiert, im zweiten Schritt werden die Methoden dann implementiert. Wir sehen für jedes Attribut eine get-Methode vor, die einfach den Attributwert zurückgibt. Diese Methoden müssen und können nur für ein bestimmtes Objekt ausgeführt werden. Deswegen werden sie mit dem Zusatz MEMBER definiert. Außerdem definieren wir einen Konstruktor für ein Objekt. Hierbei handelt es sich um eine »Klassenmethode«, eine Methode, die nicht an ein bestimmtes Objekt gebunden ist – wie auch, das Objekt soll ja gerade erst mit der Methode erzeugt werden. Eine Klassenmethode wird als STATIC definiert.21

20 Die hier vorgestellten Befehle sind als SQL-Skripte auf der CD im Verzeichnis \...\OracleOO zu finden. Hier geht es gerade um das Skript crykunde08. 21 Ein paar Hinweise zur Syntax: Die Methoden werden hier mit der proprietären Sprache PL/ SQL formuliert, die dem Standard ähnelt, aber auch einige Abweichungen aufweist. Jeder Befehl wird mit Semikolon abgeschlossen. Der Schrägstrich ganz am Ende signalisiert dem Befehlseditor SQL_Plus, dass der darüber stehende Programmcode auszuführen ist. Eine Kurzeinführung in PL/SQL, die sich auch gut als Referenz eignet, findet sich in [FePD05].

374

Objektrelationales Modell der Versand-Datenbank

CREATE OR REPLACE TYPE otyp_adresse AS OBJECT ( strasse varchar2(30), plz varchar2(5), ort varchar2(30), -----

Konstruktorfunktion statt des Standardkonstruktors Standardkonstruktor ist nicht ueberschreibbar In Klammern die Aufrufparameter hinter dem Codewort RETURN der Typ des Rueckgabewerts

STATIC FUNCTION construct_adresse(ip_strasse VARCHAR2, ip_plz VARCHAR2, ip_ort VARCHAR2) RETURN otyp_adresse, -- Datentypen in Parameterliste ohne Laengenangabe MEMBER FUNCTION get_strasse RETURN VARCHAR2, MEMBER FUNCTION get_plz RETURN VARCHAR2, MEMBER FUNCTION get_ort RETURN VARCHAR2 ); /

Mit dem obigen Befehl ist die Schnittstelle definiert. Ein Benutzer, der mit diesem Typ arbeiten will, muss lediglich diese Schnittstelle kennen, aber nicht die Implementierung. Diese wird nun vorgenommen: CREATE OR REPLACE TYPE BODY otyp_adresse IS -- hier wird ein selbst definierter Konstruktor implementiert -- mit einer einfachen Pruefung des Wertebereichs der PLZ STATIC FUNCTION construct_adresse(ip_strasse VARCHAR2, ip_plz VARCHAR2, ip_ort VARCHAR2) RETURN otyp_adresse IS BEGIN IF NOT ip_plz BETWEEN '00001' AND '99999' THEN -- besser mit regular expressions pruefen, ob 5 Ziffern -- IF NOT REGEXP_LIKE (ip_plz,'^\d{5}$') THEN raise_application_error(-1,'Wertfehler bei plz',FALSE); ELSE RETURN otyp_adresse(ip_strasse,ip_plz,ip_ort); END IF; END; -- STATIC FUNCTION construct_adresse MEMBER FUNCTION get_strasse RETURN VARCHAR2 IS BEGIN -- self ist der Verweis auf das Objekt selbst, -- das diese Methode ausfuehrt (bei Java: this)

375

9 Objektorientierung und SQL

RETURN self.strasse; END; MEMBER FUNCTION get_plz RETURN VARCHAR2 IS BEGIN RETURN self.plz; END; MEMBER FUNCTION get_ort RETURN VARCHAR2 IS BEGIN RETURN self.ort; END;

END; -- CREATE TYPE BODY otyp_adresse /

Der nächste Typ wird als VARRAY (VARYING ARRAY) deklariert, eine Struktur, die eine von vornherein beschränkte Anzahl gleichartiger Werte aufnehmen kann. Angesichts der um sich greifenden Kommunikationstechnik könnten fünf Telefonnummern pro Person nützlich sein. CREATE TYPE varr_telefonliste AS VARRAY (5) OF VARCHAR2(20);

Bei diesem Typ brauchen wir uns nicht weiter mit der Definition von Methoden zu beschäftigen. Es reicht aus, dass wir Exemplare erzeugen und darstellen können. Dazu genügen die Standardmethoden, die SQL gleich mitliefert. Beide Typen sind im Übrigen ein Affront gegen die erste Normalform für Relationen (siehe Kapitel 3)!

9.3.2 Komplexe Objekttypen und Objekttabellen Wir zeigen nun, wie die oben definierten abstrakten Datentypen (ADT) bei der Einrichtung eines komplexen Objekttyps verwendet werden können. Unsere bekannte Kundenrelation haben wird in Kapitel 4 mit dem Befehl CREATE TABLE angelegt. Hier wird nun zunächst ein Objekttyp erzeugt, der dann als Basis für eine Objekttabelle dient. CREATE OR REPLACE TYPE otyp_kunde AS OBJECT ( -- Zuvor definierte Typen werden benutzt. Keine Constraints moeglich kunden_nr number (4,0), status varchar2(1), name varchar2(30), telefonliste varr_telefonliste, adresse otyp_adresse, letzte_bestellung date,

376

Objektrelationales Modell der Versand-Datenbank

letzte_werbeaktion zahlungsart

date, char(1)

) NOT FINAL; /

Wir benutzen hier die im vorigen Abschnitt definierten ADTs als Typangabe für die Attribute kunden_nr, telefonliste, adresse. An dieser Stelle können keine Integritätsbedingungen angegeben werden. UNIQUE-Klauseln und Ähnliches sind auf Typebene nicht gestattet. Wir müssen trotzdem nicht ganz auf sie verzichten, da wir beim Einrichten einer Objekttabelle darauf zurückkommen können. Hierauf gehen wir später ein. Die Klausel NOT FINAL am Ende ist notwendig, damit wir später Subtypen definieren können. Die Struktur des Objekttyps otyp_kunde ist nun definiert. Hätten wir Methoden mit angegeben, so müssten diese anschließend implementiert werden. Um den Typen zu instanziieren, benötigen wir einen Behälter (Extent), der die Objekte aufnehmen kann. Hier liegt der wesentliche Unterschied zwischen objektorientierten und objektrelationalen Datenbanken: Der Behälter für Objekttypen ist eine Tabelle. Die CREATE TABLE-Anweisung kommt in neuer Form daher, um solche typisierten Tabellen zu erstellen: CREATE TABLE otab_kunde OF otyp_kunde SUBSTITUTABLE AT ALL LEVELS -- damit auch Objekte von Unterklassen in dieser Tabelle -- gespeichert werden können ( CONSTRAINT pk_kunde PRIMARY KEY (kunden_nr), status NOT NULL, CONSTRAINT chk_status CHECK (status IN ('S','W','G')), adresse NOT NULL, zahlungsart NOT NULL, CONSTRAINT chk_zahlart CHECK (zahlungsart in ('R','B','N','V','K')) );

Die Klausel SUBSTITUTABLE AT ALL LEVELS wird hier in weiser Voraussicht eingesetzt, um diesen Extent auch für Objekte des später zu erstellenden Typs Kunde mit Bankeinzug nutzen zu können. Anschließend werden, wie man sieht, die altbekannten Integritätsbedingungen (vgl. Kapitel 4) formuliert.22 Nicht auf der Ebene der Typdefinition werden diese festgelegt, sondern auf der Ebene der Defi-

22 Es werden aber keine Einschränkungen außer NOT NULL für Spalten, deren Typ ein Objekt, eine eingebettete Tabelle, VARRAY, REF oder LOB ist, akzeptiert [ORA01a, S 11–73].

377

9 Objektorientierung und SQL

nition von Extents.23 Das führt zu einem Konflikt. Legen wir bei der Kundennummer Wert darauf, dass sie die Fähigkeit hat, selbst fortlaufende Nummern zu erzeugen und nicht mit Artikelnummern vergleichbar zu sein? Dann hätten wir für Kundennummern einen eigenen abstrakten Datentyp definieren müssen. Oder legen wir Wert darauf, dass die Primärschlüsseleigenschaft festgehalten wird? Letztere brauchen wir zwar in objektorientierten Datenbanken nicht wegen der Entitätsintegrität – die OID ist ein besserer Ersatz für den Primärschlüssel. Als Anwender wollen wir aber vielleicht doch nicht die identifizierende Funktion der Kundennummer missen. Beides zugleich geht jedenfalls nicht so ohne Weiteres. Die PRIMARY KEY-Klausel ist zwar für Objekttabellen erlaubt, aber nicht für Attribute, deren Basis ein ADT ist!24 Dies ist ein Beispiel für die derzeit mangelnde Orthogonalität dieser objektrelationalen SQL-Implementierung. Wenn wir auf die Deklaration eines Schlüssels nicht verzichten wollen, müssen wir wohl oder übel als Wertebereich für Kunden_nr den Standarddatentyp NUMBER(4,0) einsetzen und können dann die Anweisung CREATE TABLE otab_kunde um die Klausel CONSTRAINT pk_kunde PRIMARY KEY (kunden_nr),

ergänzen.25 Die Verwendung von ADTs als Wertebereich für Attribute erfordert, wie wir bereits oben (Seite subkap_adt) gezeigt haben, Sorgfalt im Hinblick auf die Verarbeitung ihrer Werte. Wir können beim Einfügen eines Kundentupels in die neue Tabelle nicht einfach drei Strings als Adresse übergeben, sondern müssen als Attributwert jeweils ein Objekt vom Typ otyp_adresse konstruieren und dieses komplett verarbeiten. Gleiches gilt für die Telefonliste. 26 INSERT INTO otab_kunde /* (kunden_nr, status, name, telefonliste, adresse, letzte_bestellung, letzte_werbeaktion, zahlungsart) */ -- wuerde auch ohne den Konstruktor funktionieren VALUES (otyp_kunde(100, 'S','Voss, Hans', varr_telefonliste('04511349483','0179-3423708'), otyp_adresse.construct_adresse('Kuhdamm 12','23863',

23 Wir halten es für einen Mangel des objektorientierten Datenmodells, dass Integritätsbedingungen nicht deklarativ auf Typebene formulierbar sind. Es müssen dafür jeweils Methoden definiert werden, die beim Einfügen oder Ändern einer Objektinstanz die Einhaltung von Bedingungen überprüfen. Eine recht umständliche Angelegenheit! Darüber hinaus ist das Redefinieren eines Typ-Konstruktors vielfach im objektrelationalen Modell nicht möglich. 24 Vgl. [Ora01a, S. 11–81]. 25 Dieses Beispiel ist auf der CD in dem Skript crykunde08.sql enthalten. Wenn man es komplett ausführen lässt, werden auch die Typen für Bankverbindung und für Kunden mit Girokonto angelegt. 26 Dieses Beispiel ist in dem Skript inskunde08.sql enthalten. Die Skriptdatei enthält aber weitere Einfügebefehle, die erst funktionieren, wenn wir die Subklasse für Kunden mit Girokonto erzeugt haben.

378

Objektrelationales Modell der Versand-Datenbank

'Nienwohld'),NULL, TO_DATE('01-12-2007','DD-MM-YYYY'),'N') ) /

Wir haben im relationalen Modell unserer Versand-Datenbank (vgl. Kapitel 3) die Einrichtung einer eigenen Tabelle für die Mehrwertsteuersätze als zweckmäßig erachtet. Diese Sätze ändern sich nicht sehr häufig und kommen immer wieder bei jedem Verkauf eines Artikels zur Anwendung. Aus demselben Grund ist auch die Einrichtung einer eigenen Objekttabelle für die Mehrwertsteuer im objektrelationalen Modell sinnvoll. Wir zeigen an dieser Variante einer Typdefinition die Möglichkeit, eine eigene Methode für die Sortierung einzurichten (wobei dieses Beispiel nicht sehr kompliziert ist, anders z. B. bei Multimedia- oder GeografieDaten). Weiter enthält dieses Beispiel einen Konstruktor, um ein neues Objekt zu erzeugen und dabei auf die Einhaltung des Wertebereichs für den Schlüssel zu achten. Leider kann der Standardkonstruktor, der den Namen des Typs hat, nicht überschrieben werden, sodass wir hier einen eigenen Namen vergeben müssen. Der Konstruktor ist eine STATIC-Funktion, da er nur einmal für die ganze Klasse existiert, die Sortierfunktion wird hingegen als MEMBER-Funktion eingerichtet, da sie für jedes Objekt einmal ausgeführt werden muss. CREATE OR REPLACE TYPE otyp_mwstsatz AS OBJECT ( mwst integer, prozent number (3,3), beschreibung varchar2(10), -- Mit der MAP-Funktion wird eine Sortierung ermoeglicht MAP MEMBER FUNCTION mwst_order RETURN REAL, PRAGMA RESTRICT_REFERENCES (mwst_order, RNDS, WNDS, RNPS, WNPS), STATIC FUNCTION construct_mwst (in_mwst IN INTEGER, in_prozent IN NUMBER, in_beschreib IN VARCHAR2) RETURN otyp_mwstsatz -In der Parameterliste von Funktionen haben Variablen -bei PL/SQL keine Laengenangabe );

Die Implementierung der Methoden erfolgt separat. CREATE OR REPLACE TYPE BODY otyp_mwstsatz AS MAP MEMBER FUNCTION mwst_order RETURN REAL IS BEGIN RETURN prozent; -- Attribute und Methoden ohne explizite Objektangabe -- beziehen sich auf das Objekt "self" (bei Java "this") END mwst_order;

379

9 Objektorientierung und SQL

STATIC FUNCTION construct_mwst (in_mwst IN INTEGER, in_prozent IN NUMBER, in_beschreib IN VARCHAR2) RETURN otyp_mwstsatz IS BEGIN -- Constraints implementieren IF in_mwst < 0 THEN DBMS_OUTPUT.PUT_LINE ('Mwst-Schluessel muss >=0 sein'); raise_application_error (-1,'Wertfehler bei mwst',FALSE); ELSE RETURN otyp_mwstsatz(in_mwst,in_prozent,in_beschreib); END IF; END construct_mwst; END;

Die MAP-Funktion bildet ein Objekt vom Typ otyp_mwstsatz auf eine Zahl ab. Die Abbildungsvorschrift wird mit der Implementierung festgelegt. Wir haben hier einfach den Prozentsatz genommen. Es könnte aber jeder beliebige numerische Ausdruck sein, der sich beispielsweise bei einer geometrischen Figur aus einer mehr oder weniger komplizierten Flächenberechnung ergäbe. Die Definition dieser Methode eröffnet uns die Möglichkeit, bei der Ausgabe der Tupel mit der ORDER BY VALUE-Klausel eine Standardsortierung anzufordern. SELECT * FROM otab_mwstsatz m ORDER BY VALUE(m);

Die Angabe PRAGMA RESTRICT_REFERENCES (mwst_order, RNDS, WNDS, RNPS, WNPS),

ist eine »Compiler-Direktive«. Sie ist notwendig, um »Seiteneffekte« zu kontrollieren, und besagt, dass weder Datenbank-Zustände gelesen oder verändert werden noch Prozedurvariablen. Wir beschäftigen uns mit diesen mehr technischen Dingen hier nicht weiter. Der Bezeichner self zeigt zur Laufzeit auf das Objekt (Exemplar), bei dessen Bearbeitung die Methode aufgerufen wurde. Wir erzeugen eine Objekttabelle auf Basis des Typen: CREATE TABLE otab_mwstsatz OF otyp_mwstsatz ( mwst NOT NULL, prozent NOT NULL, CONSTRAINT pk_mwstsatz PRIMARY KEY (mwst) )

380

Objektrelationales Modell der Versand-Datenbank

In diese können wir nun Tupel mithilfe des Konstruktors einfügen, z. B.: INSERT INTO otab_mwstsatz VALUES (otyp_mwstsatz.construct_mwst(1,.07,'halbe'))27

9.3.3 Vererbung Das UML-Diagramm am Anfang des Abschnitts 9.3 enthält eine Spezialisierungsbeziehung zwischen Kunde und Kunde_mit_Bankeinzug. Der Subtyp enthält zusätzliche Attribute. Da eine Bankverbindung immer dieselben Attribute hat, können wir verfahren wie bei der Adresse und dafür einen eigenen Typen einführen. CREATE OR REPLACE TYPE otyp_girokonto AS OBJECT ( konto_inhaber varchar2(30), blz varchar2(8), kontonr varchar2(10) );

Der Typ für Kunden mit Bankeinzug wird nun als Subtyp erzeugt, indem man mit der Klausel UNDER den Obertypen28 referenziert und ansonsten nur die zusätzlichen Attribute (und gegebenenfalls Methoden) spezifiziert. CREATE OR REPLACE TYPE otyp_kunde_mit_bankeinzug UNDER otyp_kunde ( bankverbindung otyp_girokonto );

Um einen Extent zu erzeugen, der Kunden und Kunden mit Bankverbindung als Objekte speichern kann, lässt sich ausnutzen, dass Objekte einer Unterklasse immer zugleich Objekte der Oberklasse sind. Das bedeutet, dass es nicht notwendig ist, eine eigene Extent-Tabelle für Kunden mit Bankverbindung anzulegen.29 Der Extent für Kunden wird angelegt, wobei wir mit der Klausel SUBSTITUTABLE AT 27 Dieses Beispiel ist mit in den Skriptdateien cryartikel.sql und insartikel.sql auf der CD enthalten. Beide befinden sich im Unterverzeichnis \...\OracleOO. 28 Wir haben hier den Obertypen otyp_kunde angegeben. Dieser hat gegenüber dem Beispiel otyp_kunde0, das wir im vorigen Abschnitt besprochen haben, ein skalares Attribut als Kundennummer. Bei der Anlage der Objekttabelle otab_kunde kann man dann deklarativ einen Primärschlüssel erzeugen: CREATE OR REPLACE TYPE otyp_kunde AS OBJECT (kunden_nr number (4,0), ...)

Auf der CD befindet sich dieses Beispiel im Skript crykunde.sql. 29 Es wäre zwar möglich, dies zu tun, dann würde der Extent von otyp_kunde aber nicht die Objekte von otyp_kunde_mit_Bankverbindung enthalten, im Widerspruch zum Prinzip, dass Objekte der Unterklasse Objekte der Oberklasse sind.

381

9 Objektorientierung und SQL

ALL LEVELS verlangen, dass immer dann ein Subtyp von otyp_kunde verwendet werden darf, wo ein Kunde gefragt ist. Wir brauchen hier also weiter nichts zu tun, da wir oben die Kundentabelle bereits so angelegt haben: CREATE TABLE otab_kunde OF otyp_kunde SUBSTITUTABLE AT ALL LEVELS ( ... );

Wir fügen nun zwei Tupel in die Objekttabelle otab_kunde ein, eines ohne und eines mit Bankverbindung. Beim zweiten Mal müssen wir unbedingt den Konstruktor für otyp_kunde benutzen, da dieser prinzipiell auch als Methode an die Unterklasse vererbt wird. Sonst würde eine Fehlermeldung erzeugt, die angibt, dass die VALUES-Klausel des INSERT-Befehls mehr Spalten enthält als die Tabelle. INSERT INTO otab_kunde -- wuerde auch ohne den Konstruktor funktionieren VALUES (otyp_kunde(100, 'S','Voss, Hans', varr_telefonliste('04511349483','0179-3423708'), otyp_adresse.construct_adresse('Kuhdamm 12','23863', 'Nienwohld'), NULL, TO_DATE('01-12-2007','DD-MM-YYYY'),'N') ) /

INSERT INTO otab_kunde /* (kunden_nr, status, name, adresse, letzte_bestellung, letzte_werbeaktion, zahlungsart, bankverbindung) */ -- funktioniert nicht ohne Konstruktor, da sonst zu viele Spalten VALUES (otyp_kunde_mit_bankeinzug(101,'S','Stein, Peter', varr_telefonliste('04523-3456'), otyp_adresse.construct_adresse('Moordamm 34', '23863', 'Kayhude'), TO_DATE('28-04-2008','DD-MM-YYYY'), TO_DATE('01-12-2007','DD-MM-YYYY'),'B', otyp_girokonto('Dagmar Stein', '23410022','123456789')) ) /

Abfragen von Daten aus einer Objekttabelle behandeln wir wenig später in Abschnitt 9.3.5.

382

Objektrelationales Modell der Versand-Datenbank

9.3.4 Referenzen Die Beziehung zu einem Objekt eines eigenständigen Objekttyps wird durch eine »Referenz« hergestellt.30 Implizit wird dafür die OID genutzt, der Nutzer hat jedoch damit nichts zu tun. Voraussetzung für die Referenzierbarkeit eines Objekttyps ist, dass ein Extent in Form einer Objekttabelle erzeugt worden ist. Die Einbindung einer Referenz in eine Typdefinition geschieht über ein Attribut (»Referenzattribut«). Dessen Typ wird auf folgende Weise angegeben: referenzattribut

REF objekttyp

Wir nutzen dieses Konstrukt, um einen Objekttyp für Artikel zu erzeugen, der eine Referenz auf den zugehörigen MwSt.-Satz enthält.31 CREATE OR REPLACE TYPE otyp_artikel AS OBJECT ( artikel_nr varchar2(4), mwst REF otyp_mwstsatz, bezeichnung varchar2(15), listenpreis number(8,2), bestand number(5,0), mindestbestand number (5,0), verpackung varchar2(15), lagerplatz number(2,0), kann_wegfallen number(1,0), bestellvorschlag date, nachbestellung date, nachbestellmenge number(5,0), MEMBER FUNCTION get_mwst RETURN REAL, PRAGMA RESTRICT_REFERENCES (get_mwst, WNDS, WNPS) -- moegl. Methoden: -init_nachbestellung, entferne_aus_lager, -neue_artikel_nr, preiserhoehung );

Wir haben eine Methode angegeben, die den MwSt.-Satz eines Artikels zurückgibt. Die Implementierung überlassen wir als Übung dem Leser.32 Wie in den vorherigen Fällen richten wir als Extent eine Tabelle ein, zu der dann auch die Integritätsbedingungen so formuliert werden, wie wir es bei der relationalen Fassung auch getan haben.

30 In imperativen Programmiersprachen wie Pascal, C, C++ entspricht das im Wesentlichen einem Pointer bzw. einer Adresse. 31 Dieses und alle anderen Beispiele sind in vollständiger Ausführung auf der CD im Unterverzeichnis \...\OracleOO enthalten. 32 In der Datei cryartikel auf der CD findet sich die Lösung.

383

9 Objektorientierung und SQL

CREATE TABLE otab_artikel OF otyp_artikel ( CONSTRAINT pk_artikel PRIMARY KEY (artikel_nr), CONSTRAINT nn_mwst mwst NOT NULL, CONSTRAINT nn_bezeichnung bezeichnung NOT NULL, CONSTRAINT nn_listenpreis listenpreis NOT NULL, CONSTRAINT nn_bestand bestand NOT NULL, CONSTRAINT chk_bestand CHECK (bestand >= 0), CONSTRAINT nn_mindestbestand mindestbestand NOT NULL, CONSTRAINT chk_mindestbestand CHECK (mindestbestand >= 0), CONSTRAINT chk_nachbestell CHECK (nachbestellmenge >= 0) -- FOREIGN KEY entfaellt wegen Einbindung mwst als REF-Typ );

Beim Einfügen von Tupeln muss, wie wir bereits gesehen haben, der Typ der Attribute respektiert werden, was bei ADTs den Einsatz des Konstruktors erfordert. Der Konstruktor für den MwSt.-Satz heißt in unserem Fall REF und muss sich auf ein konkretes Objekt vom Typ otyp_mwstsatz beziehen. Dies macht eine Abfrage erforderlich, weil wir auf andere Weise keine Referenz auf ein MwSt.-Objekt erzeugen können. Alle anderen Attribute müssen dabei als Konstante mit angegeben werden. INSERT INTO otab_artikel (Artikel_Nr, Verpackung, Bezeichnung, Lagerplatz, Bestand, Mindestbestand, Listenpreis, MWST) SELECT 'G001','0,7 l','Whisky',7,397,50,38.50, REF(m) FROM otab_mwstsatz m WHERE m.mwst = 2

Mit der obigen Einfügeabfrage wird für das Attribut mwst die OID des Objekts mit dem Schlüsselwert mwst = 2 eingetragen. Etwas logischer erscheint vielleicht die folgende Variante, bei der die SELECTAnweisung, welche die Referenz auf das MwSt.-Objekt liefert, einzeln innerhalb der Werteliste auftritt. Diese Variante ist jedenfalls dann zu empfehlen, wenn mehr als eine Referenz übergeben werden soll, sonst wird die gesamte Anweisung recht unübersichtlich. INSERT INTO otab_artikel (Artikel_Nr, Verpackung, Bezeichnung, Lagerplatz, Bestand, Mindestbestand, Listenpreis, MWST) VALUES ('G001','0,7 l','Whisky',7,397,50,38.50, (SELECT REF(m) FROM otab_mwstsatz m WHERE m.mwst = 2) ) /

384

Objektrelationales Modell der Versand-Datenbank

9.3.5 Abfragen Bevor wir Abfragen an einzelnen Beispielen erläutern, ein Hinweis: In vielen Fällen, und immer dann, wenn objektorientierte Möglichkeiten genutzt werden, muss in der FROM-Klausel ein Tabellenalias angegeben werden. Der Grund dafür ist, dass jedes zurückgegebene Tupel zunächst in ein Objekt verwandelt werden muss, damit beispielsweise dessen Methoden zur Verfügung stehen. Für die Abfrage einer Objekttabelle steht zunächst einmal der SELECT-Befehl in seiner bekannten Form zur Verfügung. Allerdings sind die Resultate nicht unbedingt sehr leserlich, wenn man in der Projektionsangabe den Namen eines nichtskalaren Attributs angibt. Zum Beispiel liefert die Abfrage des Attributs mwst der Tabelle otab_artikel eine Ausgabe der folgenden Art: 0000220208A456CA777B2811D3A9080000E867DD32A456CA437B2811D3A

Abgesehen davon, dass man die OID eigentlich als Nutzer nicht sehen sollte, ist das natürlich keine Aussage von Wert. Man muss daher, um die Mehrwertsteuerangaben eines Artikels im Klartext zu sehen, die Referenz auflösen, sozusagen ersetzen durch das konkrete Objekt, auf das sie zeigt. Dafür gibt es den Operator DEREF SELECT artikel_nr, verpackung, bezeichnung, DEREF(mwst) FROM otab_artikel;

In der Antwort werden nicht einfach die Werte des entsprechenden Tupels der Mehrwertsteuer-Tabelle ausgegeben, sondern dies geschieht mit vorangestellten Typbezeichnern in Klammern. Auch hier sieht man, dass das objektrelationale Modell sehr genau Typen und Werte unterscheidet. ARTIKEL_NR VERPACKUNG BEZEICHNUNG ---------- --------------- --------------DEREF(MWST)(MWST, PROZENT, BESCHREIBUNG) ------------------------------------------------------------------------------G001 0,7 l Whisky OTYP_MWSTSATZ(2, .19, 'volle') G002 0,5 l Portwein OTYP_MWSTSATZ(2, .19, 'volle') G003 6er Pack Bier OTYP_MWSTSATZ(2, .19, 'volle') K001 Karton Schuhe OTYP_MWSTSATZ(2, .19, 'volle') ...

385

9 Objektorientierung und SQL

Interessant ist aber die Möglichkeit, die wir im Folgenden vorstellen. Die Einbettung eines Verweises auf ein Tupel der Mehrwertsteuer-Tabelle über eine Referenz macht den Zugriff auf seine Attribute quasi als Eigenschaft eines Artikels möglich. Dazu dient die sogenannte »Punktnotation«.33 SELECT a.artikel_nr, a.mwst.prozent FROM otab_artikel a WHERE a.mwst.prozent = 0.19; ARTIKEL_NR MWST.PROZENT ---------- -----------G001 ,19 G002 ,19 G003 ,19 K001 ,19 K002 ,19 K003 ,19 L004 ,19 L005 ,19

Im Ausdruck a.artikel_nr wird – wie von der relationalen Datenmanipulation her bekannt – kundgetan, dass artikel_nr ein Attribut der Tabelle otab_artikel mit dem Alias a ist. Der Ausdruck a.mwst.prozent gibt an, dass prozent ein Attribut von mwst und mwst ein Attribut von a ist. Gegenüber der oft recht aufwendigen Formulierung eines Joins ist der Zugriff hier eindeutig erleichtert. Man bezeichnet diese Art auch als »navigierenden Zugriff«, da man über ein Objekt »einsteigt« und über die von ihm ausgehenden Referenzen zu anderen Objekten hin »navigiert«. Die Objekte von Kunden mit Bankverbindung waren in Abschnitt 9.3.3 in die Objekttabelle für Kunden eingefügt worden, die ja per Definition eigentlich nicht über die Attribute der Bankverbindung verfügt. Da wir aber den Typ für Kunden mit Girokonto als Subtyp von otyp_kunde eingeführt hatten, lassen sich dessen Objekte wie normale Kunden behandeln. Nun muss sich zeigen, dass die Zusatzangaben auch bei Abfragen wieder zur Verfügung stehen. Dazu ist es erforderlich, die Behandlung der zusätzlichen Attribute explizit anzufordern, indem man verlangt, dass alle Kunden wie Exemplare des Subtyps behandelt werden. Damit werden die zusätzlichen Attribute des spezialisierten Typs in die Projektionsliste übernommen, und für normale Kunden erhalten sie den Wert NULL:

33 Dass im Befehl die Wertangabe mit Dezimalpunkt erscheint und in der Ausgabe mit Dezimalkomma, ist kein Druckfehler. Die Ausgabe ist auf deutsches Zahlenformat eingestellt. In Anweisungen werden Zahlenwerte dagegen stets mit Dezimalpunkt angegeben. Dies gilt auch für fast alle Programmiersprachen, da diese normalerweise auf der englischen Sprache basieren.

386

Objektrelationales Modell der Versand-Datenbank

SELECT kunden_nr, name, TREAT (VALUE(k) AS otyp_kunde_mit_bankeinzug). bankverbindung.konto_inhaber AS konto_inhaber, TREAT (VALUE(k) AS otyp_kunde_mit_bankeinzug). bankverbindung.kontonr AS kontonr, TREAT(VALUE(k) AS otyp_kunde_mit_bankeinzug). bankverbindung.blz AS blz FROM otab_kunde k; KUNDEN_NR NAME KONTO_INHABER ---------- ------------------------------ ----------------------KONTONR BLZ ---------- -------100 Voss, Hans

101 Stein, Peter 123456789 23410022

Dagmar Stein

103 Randers, Nis 20001234 23410112

Tetsche Wind

...

9.3.6 Eingebettete Objekttypen In Abschnitt 9.2.2 haben wir die Kollektionstypen vorgestellt, die der Standard fordert: MULTISET und ARRAY34. Oracle unterstützt zurzeit zwei Kollektionstypen: VARRAY (siehe Seite varray) und die eingebettete Tabelle (nested table). Mit eingebetteten Tabellen als Typ können Attribute definiert werden, die eine Multimenge von Tupeln enthalten. Wenn eine solche Tabelle nur ein Attribut enthält, handelt es sich eben um eine Multimenge von Einzelwerten. Die Einbettung einer Tabelle, die selbst auf einem Objekttyp beruht, in einen anderen Objekttyp stellt eine sehr enge Verbindung dar. Wir haben sie in Kapitel 3.3 als Komposition eingeführt. In unserer Fallstudie kann die Verbindung zwischen einer Bestellung und ihren Positionen als Komposition aufgefasst werden. Im Entity-Relationship-Modell würde man einen eingebetteten Objekttyp als abhängigen Entitätentyp modellieren. Um diese Idee umzusetzen, müssen wir erst einen Objekttyp für Positionen definieren, den wir dann in einen Typ für Bestellungen einbetten. Langsam werden unsere Objekttypen auch komplexer, denn eine Position enthält einen Verweis auf 34 Vgl. 9.2.2.

387

9 Objektorientierung und SQL

einen Artikel. Wir verwalten Artikel unabhängig von Bestellpositionen und sollten deshalb hierfür eine Referenz einsetzen. Da der Typ otyp_artikel selbst wieder ein Attribut vom Typ REF otyp_mwsatz enthält, haben wir dann gleich eine mehrstufige Referenz. Wir zeigen hierbei auch noch einmal ein Beispiel für eine Methode mit dem Namen get_artikel_nr, welche die Anzeige der Artikelnummer einer Bestellposition etwas einfacher gestaltet. CREATE OR REPLACE TYPE otyp_position AS OBJECT ( pos_artikel REF otyp_artikel, mwst number (4,3), -- tatsaechlich angewandter MWST-Satz als Dezimalzahl bestellmenge number (5,0), liefermenge number(5,0), gesamtpreis number(10,2), MEMBER FUNCTION get_artikel_nr RETURN VARCHAR2, PRAGMA RESTRICT_REFERENCES (get_artikel_nr, WNDS, WNPS) ); CREATE OR REPLACE TYPE BODY otyp_position AS MEMBER FUNCTION get_artikel_nr RETURN VARCHAR2 IS lvar_artnr VARCHAR2(4); BEGIN SELECT artikel_nr INTO lvar_artnr FROM otab_artikel a WHERE VALUE(a) = DEREF(pos_artikel); RETURN lvar_artnr; END get_artikel_nr; ;

Wir haben nunmehr den Typ einer einzelnen Position beschrieben. Wenn wir eine ganze Tabelle davon in eine Bestellung einbetten wollen, müssen wir einen Kollektionstyp definieren. CREATE OR REPLACE TYPE ntyp_position AS TABLE OF otyp_position;

Jetzt kann otyp_bestellung erstellt werden. CREATE OR REPLACE TYPE otyp_bestellung AS OBJECT ( bestell_nr number(6,0), bestellkunde REF otyp_kunde, bestelldatum date, lieferdatum date, positionen ntyp_position, rechnungsbetrag number(10,2) );

Wir erzeugen anschließend einen Extent für diesen Typ mit der Besonderheit, dass wir auch für die eingebettete Tabelle einen Namen vergeben können.

388

Objektrelationales Modell der Versand-Datenbank

CREATE TABLE otab_bestellung OF otyp_bestellung NESTED TABLE positionen STORE AS ntab_position;

Das Einfügen eines neuen Tupels in die Bestellung wird nun wegen der relativ komplizierten Struktur schon etwas aufwendig. Es lässt sich aber vermeiden, dass man mit der Bestellung gleich alle Positionen übergibt. Mit dem Ausdruck ntyp_position ()

wird die Sammlung instanziiert, bleibt aber leer. Die Tupel, die ihn mit Inhalt füllen, können später separat an die Datenbank übermittelt werden. INSERT INTO otab_bestellung (bestell_nr, Bestelldatum, bestellkunde, Lieferdatum, Rechnungsbetrag, Positionen) SELECT 151,TO_DATE('28-04-2008','DD-MM-YYYY'),REF(k), TO_DATE('02-05-2008','DD-MM-YYYY'), 200.67, ntyp_position () /* der Positionen-Sammler wird hier nur instanziiert, aber nicht mit Objekten gefuellt [Kähl99 S. 230]. Wichtig hier die Angabe des Nested-Tabellen-Typs. */ FROM otab_kunde k WHERE k.kunden_nr = 101

Die Tatsache, dass ein Attribut nicht einen Wert, sondern eine Tabelle enthalten kann, hat weitreichende Konsequenzen. Wir haben in Kapitel 5.4 gesehen, dass in einer Unterabfrage (SELECT ausdruck FROM tabelle ...)

mit dem Wert des Ausdrucks in dem Ergebnistupel identifiziert wird (vorausgesetzt, die Abfrage liefert eine Relation mit genau einem Tupel). Diese Unterabfrage kann an jeder Stelle erscheinen, wo ein Wert stehen kann. Wir haben jetzt eine ähnliche Situation, wenn ausdruck als Ergebnis eine Tabelle liefert, TABLE (SELECT ausdruck FROM tabelle ...)

liefert uns eine Tabelle, die an Stellen, in denen Tabellen auftreten können, eingesetzt werden kann. Das sind neben der SELECT-Anweisung auch die INSERT-, UPDATE- und DELETE-Anweisung.35 Damit wir das folgende Tupel der richtigen Bestellung zuordnen können, wird die Zieltabelle für den folgenden INSERT-Befehl durch eine Abfrage erzeugt. Die Referenz auf einen Artikel lässt sich wieder nur über eine SELECT-Anweisung darstellen und in das neue Tupel einfügen.

35 In der Version 8 war das Schlüsselwort THE statt TABLE.

389

9 Objektorientierung und SQL

INSERT INTO TABLE ( SELECT positionen FROM otab_bestellung WHERE bestell_nr = 151) SELECT REF(a1),a1.get_mwst(),4,4,49.80 FROM otab_artikel a1 WHERE artikel_nr = 'G001' /

Die Anzeige von Bestelldaten mit dem Namen des Kunden kommt wieder ohne Join aus. select bestell_nr, bestelldatum, b.bestellkunde.kunden_nr, b.bestellkunde.name from otab_bestellung b; BESTELL_NR ---------151 152

BESTELLDATUM BESTELLKUNDE.KUNDEN_NR BESTELLKUNDE.NAME ------------ ---------------------- -----------------28.04.08 101 Stein, Peter 30.04.08 103 Randers, Nis

Die Positionsdaten einer Bestellung aus der eingebetteten Tabelle anzuzeigen, erfordert noch ein syntaktisches Konstrukt, die CURSOR-Klausel. Der Cursor liefert einen sequenziellen Zugriff auf die Tupel der Untertabelle, um dort, wo sonst ein einzelner Attributwert steht, eine Menge von Tupeln als Tabelle anzuzeigen.36 Bei der ersten Variante erscheint wieder die unleserliche OID, welche die Referenz auf den MwSt.-Satz des Artikels darstellt. In der zweiten Variante projizieren wir, entlang der Referenzen navigierend, auf Einzelattribute. 1. Variante: SELECT b.bestell_nr, CURSOR (SELECT DEREF(pos_artikel), bestellmenge FROM TABLE (b.positionen) ) FROM otab_bestellung b ; BESTELL_NR CURSOR(SELECTDEREF(P ---------- -------------------151 CURSOR STATEMENT : 2

36 In imperativen Programmiersprachen dient ein gleichnamiges Konzept dazu, die mengenmäßig strukturierte Ausgabe einer SQL-Abfrage in eine konventionelle Satzstruktur abzubilden, die dann beispielsweise in Schleifen Satz für Satz bearbeitet werden kann. Mehr dazu findet sich in Kapitel 7.3.4.

390

Objektrelationales Modell der Versand-Datenbank

CURSOR STATEMENT : 2 DEREF(POS_ARTIKEL)(ARTIKEL_NR, MWST, BEZEICHNUNG, LISTENPREIS, BESTAND, MINDESTB ----------------------------------------------------------------BESTELLMENGE -----------OTYP_ARTIKEL('K002', 0000220208F6C875DF3D4C4067AB8323CBFBC3B3889E1EB69AB2DC4B139 A4B6698472A1C6C, 'Hose', 112,8, 62, 25, NULL, 2, NULL, NULL, NULL, NULL) 3 OTYP_ARTIKEL('G001', 0000220208F6C875DF3D4C4067AB8323CBFBC3B3889E1EB69AB2DC4B139 A4B6698472A1C6C, 'Whisky', 38,5, 397, 50, '0,7 l', 7, NULL, NULL, NULL, NULL) 4 ...

2. Variante SELECT bestell_nr, bestelldatum, b.bestellkunde.kunden_nr, b.bestellkunde.name, CURSOR (SELECT p.pos_artikel.artikel_nr AS artikel_nr, p.pos_artikel.bezeichnung AS bezeichnung, p.bestellmenge AS bestellmenge FROM TABLE (b.positionen) p) FROM otab_bestellung b; BESTELL_NR BESTELLDATUM BESTELLKUNDE.KUNDEN_NR BESTELLKUNDE.NAME ---------- ------------ ---------------------- -----------------CURSOR(SELECTP.POS_A -------------------151 28.04.08 101 Stein, Peter CURSOR STATEMENT : 5 CURSOR STATEMENT : 5 ARTIKEL_NR ---------K002 G001 G003 K003 L002

BEZEICHNUNG BESTELLMENGE --------------- -----------Hose 3 Whisky 4 Bier 3 Damenhut 1 China-Tee 10

391

9 Objektorientierung und SQL

9.3.7 Schreiboperationen in Objekttabellen Wir haben bereits vorgeführt, wie man Daten in eine Objekttabelle einfügt. Weitere Schreiboperationen sind Änderungen an vorhandenen Objekten und Löschungen. Das Löschen von Daten aus Objekttabellen geht genauso vor sich wie bei der relationalen Variante. Der folgende Befehl löscht die Bestellung 151: DELETE FROM otab_bestellung WHERE bestell_nr = 151;

Damit werden gleichzeitig die Positionsdaten in der eingebetteten Tabelle gelöscht. Eine Besonderheit gibt es bei eingebetteten Tabellen. Hier muss eine Subquery mit dem Typkonstruktor TABLE verwendet werden. Der folgende Befehl löscht alle Positionen der Bestellung 151: DELETE FROM TABLE (SELECT b.positionen FROM otab_bestellung b WHERE b.bestell_nr =151);

Das Ändern von Daten mit dem UPDATE-Befehl unterliegt einigen Besonderheiten, wenn es sich nicht um skalare Attribute handelt, sondern um Attribute, die auf einem benutzerdefinierten Typ beruhen. Im Falle der Kundenadresse haben wir ein Attribut auf Basis des Typs otyp_adresse mit innerer Struktur definiert. Der Zugriff auf eine einzelne Komponente erfolgt mit der »Punktnotation«: UPDATE otab_kunde k SET k.adresse.strasse = 'neue Strasse 3' WHERE kunden_nr = 103;

Der Tabellenalias k darf dabei nicht fehlen. Erlaubt ist auch die Anwendung des UPDATE-Befehls auf eine konstruierte Tabelle, die Resultat einer Abfrage ist. UPDATE (SELECT kunden_nr, k.adresse.strasse strasse, k.adresse.plz FROM otab_kunde k WHERE kunden_nr = 103) SET strasse = 'neue Strasse 4';

Hier haben wir die Struktur der Adresse aufgelöst, sodass eine »flache« Tabelle vorliegt.

Schreiboperationen in eingebettete Tabellen Um Attributwerte in einer eingebetteten Tabelle zu aktualisieren, ist es erforderlich, diese sozusagen in eine »normale« Tabelle umzuwandeln. Dazu dient der Typkonstruktor TABLE, den wir in Abschnitt 9.3.6 eingeführt haben. Möglich ist dies nur für jeweils eine Bestellung, da für jedes Bestellung-Objekt die eingebetteten Positionen eine eigene Tabelle darstellen: Der Datentyp des Attributs positionen ist ntyp_position, Letzterer wurde als TABLE OF otyp_position definiert.

392

Objektrelationales Modell der Versand-Datenbank

Ändern wir nun für die Bestellung 151 die Bestellmenge für Whisky auf 1000: UPDATE TABLE (SELECT b.positionen from otab_bestellung b WHERE bestell_nr = 151) p SET p.bestellmenge=1000 WHERE p.pos_artikel.artikel_nr = 'G001';

Der Befehl UPDATE bezieht sich auf das Attribut in otab_bestellung, das die eingebettete Tabelle enthält. Mit dem Konstruktor TABLE wird dieses in eine selbstständige Tabelle umgewandelt. Dieser Tabelle wird der Aliasname p gegeben. Somit kann auf alle Attribute in der WHERE- und SET-Klausel zugegriffen werden. Auch mehrere Tupel dieser eingebetteten Tabelle können zugleich mit einer UPDATE-Anweisung geändert werden, solange es sich um dieselbe Bestellung handelt.

9.3.8 Object Views Ein View ist eine virtuelle Tabelle, erzeugt durch eine Abfrage. Wir haben dies in Kapitel 6 beschrieben. Ein Object View ist das Gleiche als Objekttabelle. Jede Zeile in einem Object View ist ein Objekt mit Attributen und möglicherweise Methoden. Ein interessanter Aspekt an dieser Form der Datensichten besteht darin, dass es möglich ist, Object Views auf relationalen Basistabellen aufzubauen. Damit gewinnt man Vorteile bei der objektorientierten Programmierung von Anwendungen.37 Die Zugriffe auf Daten können beispielsweise über die Object Views erfolgen, so als wären die Datenstrukturen objektorientiert und nicht relational.38 Ein weiterer Vorteil besteht darin, dass die Navigation zu den Werten komplexer, auf abstrakten Datentypen basierender Attribute einfacher sein kann39 als ein komplizierter Join über 2 oder mehr Tabellen. Wir zeigen ein Beispiel, in dem wir Object Views über den Basistabellen unserer relationalen Versand-Datenbank anlegen. Im Unterschied zu relationalen Datensichten sind jeweils zwei Schritte erforderlich. Zunächst erzeugen wir einen Datentyp für Kundenobjekte. Dabei wollen wir die Adresse als benutzerdefinierten Datentyp verwalten.

37 Vgl. [Ora01b, S. 5–2], 38 Schreibende Zugriffe auf Views sind nicht immer zulässig, wie wir in Kapitel 6 ausgeführt haben. Es gibt aber die Möglichkeit, über INSTEAD OF-Trigger das Verhalten eines UPDATE-Befehls so umzudefinieren, dass er die gewünschten Operationen auf Basistabellen ausführt. Vgl. [Ora01b, S. 5–14]. 39 Darüber, was im Endeffekt einfacher ist, können sich nun Anhänger der Objektorientierung und Verfechter des »klassischen« relationalen Ansatzes endlos streiten.

393

9 Objektorientierung und SQL

CREATE TYPE otyp_KUNDE4view AS OBJECT ( kunden_nr number (4,0) , status varchar2(1) , name varchar2(30) , adresse otyp_adresse, letzte_bestellung date, letzte_werbeaktion date, zahlungsart char(1) )

Im zweiten Schritt erzeugen wir den View über eine Abfrage, die natürlich objektspezifische Eigenarten enthält. Die eine ist die optionale Festlegung, dass die OID auf Basis des Primärschlüssels erzeugt werden soll. Weiterhin müssen wir den Standardkonstruktor für eine Adresse benutzen, um mit den Werten von Straße, Postleitzahl und Ort ein Objekt vom Typ otyp_adresse zu erzeugen. CREATE VIEW ov_kunde OF otyp_KUNDE4view WITH OBJECT IDENTIFIER (kunden_nr) AS SELECT k.kunden_nr, status, name, otyp_adresse(strasse, plz, ort), letzte_bestellung, letzte_werbeaktion, zahlungsart FROM kunde k

Der Objekttyp für Bestellungen enthält nun eine Referenz auf den Object View für Kunden. CREATE TYPE otyp_BESTELLUNG4view AS OBJECT ( bestell_nr number(6,0), ref_kunde REF otyp_KUNDE4view, bestelldatum date, lieferdatum date, rechnungsbetrag number(10,2) ) CREATE VIEW ov_bestellung OF otyp_BESTELLUNG4view WITH OBJECT IDENTIFIER (bestell_nr) AS SELECT bestell_nr, REF (k), bestelldatum, lieferdatum, rechnungsbetrag FROM bestellung b, ov_kunde k WHERE b.kunden_nr = k.kunden_nr

394

Objektrelationales Modell der Versand-Datenbank

Eine Abfrage auf diese Datensicht zeigt uns nunmehr die in relationalen Tabellen gespeicherten Tupel wie Objekte: SELECT bestell_nr, bestelldatum, DEREF (ref_kunde) FROM ov_bestellung; BESTELL_NR BESTELLDATUM ---------- -------DEREF(REF_KUNDE)(KUNDEN_NR, STATUS, NAME, ADRESSE(STRASSE, PLZ, ORT), LETZTE_BESTELLUNG, LETZTE_WERBEAKTION, ZAHLUNGSART) ----------------------------------------------------------------151 28.04.08 OTYP_KUNDE4VIEW(101, 'S', 'Stein, Peter', OTYP_ADRESSE('Moordamm 34', '23863', 'Kayhude'), '28.04.08', '01.12.07', 'B')

9.3.9 Fazit Objektrelationale Datenbanken sind der Versuch, mit relationaler Datenbanktechnik objektorientierte Konzepte abzubilden. Der Standard wie auch die gezeigte Version von Oracle gehen dabei Kompromisse ein, die sowohl vom Standpunkt der relationalen Welt als auch vom Standpunkt des objektorientierten Paradigmas erhebliche Abstriche von beiden Konzepten bedeuten. Der Umgang mit objektrelationalen Datenbanken, was die Datendefinition angeht, aber in fast noch größerem Maß was die Manipulation (Abfrage, Änderung etc.) angeht, bringt einige Umständlichkeiten mit sich. Immerhin ist es mit strukturierten Datentypen beispielsweise möglich, XML-Datenstrukturen, die ja Hierarchien und Listen etc. enthalten können, gut abzubilden. Für die Einbindung relationaler Datenstrukturen in objektorientierte Programme hat sich mittlerweile die Technik des »Mappings« verbreitet: Es wird zu jeder Tabelle eine Klasse definiert, die mit den diesbezüglichen Methoden die Datenzugriffe regelt. Die »Persistenzschicht«, d. h. die Manipulation der Daten in den Datenbanktabellen, bleibt dabei dem Anwendungsprogrammierer verborgen. Wir haben entschieden, im Rahmen dieses Buchs auf dieses Thema nicht weiter einzugehen.

395

Ausgewählte Lösungen zu den Aufgaben Kapitel 2 Aufgabe 2.1 a) SET NULL: Wenn der Abteilungsleiter ausscheidet, gibt es für eine gewisse Zeit keinen Abteilungsleiter. b) CASCADE: Wenn die Studentendaten nicht mehr benötigt werden, dann auch nicht mehr ihre Prüfungsdaten. c) CASCADE: Wenn die Bestellung storniert wird, dann auch ihre Positionen. d) RESTRICT: Solange nicht bezahlt ist, müssen die Kundendaten ermittelbar sein.

Aufgabe 2.2 a) Proj[lagerplatz] (artikel) b) Proj[plz, ort] (kunde) c) Sel[ (artikel_nr = 'G001' ∨ artikel_nr = 'G002' ∨ artikel_nr = 'G003') ∧ liefermenge > 0 ] (bestellposition) d) Sel[ (artikel_nr = 'G001' ∨ artikel_nr = 'G002' ∨ artikel_nr = 'G003' ) ∧ liefermenge > 2 ] (bestellposition) e) Sel[artikel_nr≠'G001' ∧ artikel_nr≠'G002' ∧ artikel_nr≠ 'G003'] (bestellposition)

f) Proj[kunden_nr, name, ort, status] (Sel[status≠'S'] (kunde)) g) Sel[status ≠ 'S' ∧ ort ≠ 'Husum'] (kunde) h) Sel[bestelldatum ≥ '2008-05-01' ∧ bestelldatum ≤ '2008-05-31'] (bestellung)

i) Sel[artikel_nr = 'K001' ∨ artikel_nr = 'G003'] (bestellposition) j) Sel[listenpreis ≥ 6.00 ∧ listenpreis ≤ 15.00] (artikel) k) Grp[mwstsatz | anzahl ::= COUNT(*)] (artikel) l) Grp[status | max_kunden_nr ::= MAX(kunden_nr)] (kunde) m) Grp [lagerplatz | min_preis ::= MIN(Listenpreis), max_preis ::= MAX(Listenpreis), avg_preis ::= AVG(Listenpreis)] (artikel)

397

A Ausgewählte Lösungen zu den Aufgaben

n) Grp [plz | anzahl ::= COUNT(*)] (kunde) o) Proj[plz] (Sel[anzahl > 1] (Grp[plz | anzahl ::= COUNT(*)] (kunde))) p) Proj[zahlung] (Sel[anzahl > 2] (Grp[zahlung | anzahl ::= COUNT(*)] (kunde)))

q) Proj[artikel_nr, bezeichnung, prozent] (artikel Join[mwst] mwstsatz) r) Proj[artikel_nr, bezeichnung, brutto ::= listenpreis + listenpreis * prozent ] (artikel Join[mwst] mwstsatz)

s) Proj[bestell_nr, ort] (bestellung Join[kunden_nr] kunde) t) Proj[bestellmenge, bestell_nr, ort] (bestellposition Join[bestell_nr] bestellung Join[kunden_nr] kunde )

u) Grp [ort | anzahl ::= SUM(bestellmenge)] (bestellposition Join[bestell_nr] bestellung Join[kunden_nr] kunde )

v) Sel[ | listenpreis = MIN(listenpreis; artikel)] (artikel) w) Proj[kunden_nr, name, bestell_nr] (Sel[bestelldatum = MIN(bestelldatum; bestellung)] (bestellung) Join[kunden_nr] (kunde))

x) Proj[bestell_nr, bestelldatum]

(bestellung Join[kunden_nr] Sel [ort ≠ 'Kayhude'] (kunde)

y) Sel[listenpreis > MAX(listenpreis; Sel[lagerplatz=7](artikel))] (artikel)

Aufgabe 2.3 Adj[späteste_lieferung ::= bestelldatum + 14] (Sel[isNull(Lieferdatum)] (bestellung))

Aufgabe 2.4 a) Proj[artikel_nr, bezeichnung, bestellmenge] (artikel Join*[artikel_nr] (bestellposition Join[bestell_nr] bestellung))

b) kunde Join*[kunden_nr] girokonto c) (Proj[kunden_nr](kunde) \ Proj[kunden_nr] (girokonto)) Join[kunden_nr] (kunde)

Aufgabe 2.5 a) b) c) d)

Relation, da der Primärschlüssel in der Projektion enthalten ist. Keine Relation, da [plz,ort] jeweils mehrfach auftreten können. Relation, da der Verbund von Relationen immer eine Relation ist. Relation, da bestell_nr Primärschlüssel der Bestell-Relation ist und zu jeder bestell_nr nur ein Kundentupel gehört. e) Keine Relation, da ein Kunde mehrere Bestellungen aufgegeben haben kann.

398

Kapitel 3

Kapitel 3 Aufgabe 3.1 왘 n:m-Beziehung:

Es gibt Orte mit mehreren Postleitzahlen (z. B. Bremen), jeder Ort hat mindestens eine Postleitzahl. Es gibt Postleitzahlen, die für mehrere Orte gelten (z. B. 23863 für Kayhude und Bargfeld-Stegen). Jede Postleitzahl gilt für mindestens einen Ort – dies gilt allerdings nicht für jede Kombination von fünf Ziffern. 왘 1:1-Beziehung: Zu jeder Steuernummer gibt es genau einen Steuerpflichtigen.

Jeder Steuerpflichtige hat genau eine Steuernummer. 왘 n:1-Beziehung 왘 n:1-Beziehung 왘 n:2-Beziehung

Aufgabe 3.2

PLZ

Steuernummer

Person

〈 hat

ist ausgegeben für 〉 〈 hat

Kind



Ort

Steuerpflichtiger

Person Vater

Person

Person

Kind Mutter

Kind Elternteil

Person

Person

399

A Ausgewählte Lösungen zu den Aufgaben

Aufgabe 3.3 Autor

a) Buch

Ausleihe

Person

verlegt bei

Verlag

b) Auto

Hersteller

Produktionsbetrieb

Halter

Person

Aufgabe 3.4 a) Wir stellen hier zwei Lösungen vor, die sich in der Behandlung der Person unterscheiden. In der ersten Lösung gehen wir von einer eigenen Klasse »Person« aus, Ausleiher und Autor sind jeweils Unterklassen. In der zweiten Lösung werden »Ausleiher« und »Autor« jeweils als Rolle gesehen – es werden dafür keine eigenen Klassen angelegt. Die Auswahl zwischen diesen beiden Lösungen hängt davon ab, wie stark sich Autoren und Ausleiher in ihren Attributen und ihrer Verarbeitung unterscheiden.

400

Kapitel 3

Person

Autor

Kunde

Ausleihe

Autorenschaft Vormerkung

Buchtitel

Zuordnung

Buchexemplar

Person

Autor

Ausleihe

Vormerkung

Buchtitel

Zuordnung

Buchexemplar

401

A Ausgewählte Lösungen zu den Aufgaben

b) Person

Buchtitel

Professor

Student

Empfehlung

Vorlesung

Aufgabe 3.5 (Segeltörn) Wir zeigen zwei Lösungen, die erste als Entity-Relationship-Modell, die zweite wendet objektorientierte Konstrukte an. a) Mitfahrer

Person

Crew

Kapitän

Yacht Tour Hafen

402

Kapitel 3

b) 1..* Mitfahrer Person

0..*

Kapitän

Crew

1..1

1..* Yacht

1..1 1..1

0..*

0..* Tour Hafen

0..*

1..*

Aufgabe 3.6 a) Die erste Normalform ist verletzt, da das Feld Autoren für ein Tupel mehrere Werte hat. b) Es sind zwei Relationen in folgender Weise zu erzeugen: ISBN-Nr

Titel

Jahr

Seiten

0-201-14192-2

The Relational Model for Database Management: Version 2

1990

538

3-89319-117-8

Systematischer Einsatz von SQL-Oracle

1989

494

1-55860-245-3

Understanding the new SQL

1993

536

ISBN-Nr

Autor

0-201-14192-2

Date, Ch.

3-89319-117-8

Finkenzeller, H.

3-89319-117-8

Kracke, U.

3-89319-117-8

Unterstein, M.

1-55860-245-3

Melton, J.

1-55860-245-3

Simon, A.

403

A Ausgewählte Lösungen zu den Aufgaben

Aufgabe 3.7 a) Der Schlüssel ist aus matrikel_nr und kurs_nr zusammengesetzt. Da es funktionale Abhängigkeiten {matrikel_nr} → {student} und {kurs_nr} → {kurs_titel} gibt, ist die zweite Normalform an zwei Stellen verletzt. b) Ein äquivalentes System in normalisierter Form erhalten wir, indem wir aus den Attributen der rechten funktionalen Abhängigkeiten eine neue Relation erstellen und die rechten Seiten der funktionalen Abhängigkeiten aus der Relation herausziehen: Matrikel

Student

30321

Meyer, J.

30346

Ahrens, H.

30378

Knudsen, K.

Kurs-Nr

Kurs-Titel

715S4

Software-Engineering

713S5

Relationale u. funktionale Programmierung

706S6

Datenbanksysteme

Matrikel

Kurs-Nr

Note

30321

706S6

1,0

30321

715S4

1,7

30346

715S4

3,0

30346

706S6

2,0

30346

713S5

1,7

30378

706S6

2,0

Kapitel 4 Aufgabe 4.1 a) In der Tabelle angestellter werden alle Zeilen geändert, in denen die geänderte Abteilungsnummer als Fremdschlüssel auftritt. Dort wird die neue Abteilungsnummer eingetragen. b) Es wird dort jetzt für die Abteilungsnummer eine Nullmarke eingetragen. c) In der Tabelle abteilung wird der Primärschlüssel nur geändert, wenn kein Tupel der Relation angestellter diese Abteilungsnummer eingetragen hat.

404

Kapitel 4

Aufgabe 4.2 a) In der Tabelle seminar ist die dritte Normalform verletzt, weil es eine transitive Abhängigkeit dozname von doznr gibt. b) In seminarteilnehmer ist die zweite Normalform verletzt, weil das Attribut tnname von einem Teil des Schlüssels, nämlich tnnr, abhängt. c) Es genügt, die abhängigen Attribute aus den Relationen zu entfernen, da die entsprechenden Relationen (dozent und teilnehmer) schon vorhanden sind. d) Da die Aufgabenstellung hinsichtlich der Wertemengen keine Vorgaben enthält, stellt die folgende Lösung eine von mehreren Möglichkeiten dar. CREATE CREATE CREATE CREATE CREATE CREATE CREATE CREATE

DOMAIN DOMAIN DOMAIN DOMAIN DOMAIN DOMAIN DOMAIN DOMAIN

D_doznr AS INTEGER CHECK (VALUE > 10); D_personenname AS VARCHAR (30); D_tnnr AS INTEGER CHECK (VALUE > 100); D_semnr AS INTEGER CHECK (VALUE > 1000); D_thema AS VARCHAR (30); D_datum AS DATE CHECK (VALUE >= DATE '1996-01-01'); D_preis AS DECIMAL (6,2) CHECK (VALUE > 0); D_bezahlt AS CHAR (1) CHECK (VALUE IN ('J','N'));

e) Da in der Aufgabenstellung hinsichtlich der Verfahrensregeln bei Löschung und Änderung keine Vorgaben enthalten sind, sind auch andere Varianten als die hier vorgeschlagenen denkbar. CREATE TABLE dozent (doznr D_doznr NOT NULL, dozname D_personenname NOT NULL, PRIMARY KEY (doznr)); CREATE TABLE teilnehmer (tnnr D_tnnr NOT NULL, tnname D_personenname NOT NULL, PRIMARY KEY (tnnr)); CREATE TABLE SEMINAR (semnr D_semnr NOT NULL, thema D_thema NOT NULL, datum D_datum, preis D_preis, doznr D_doznr, PRIMARY KEY (semnr), FOREIGN KEY (doznr) REFERENCES dozent ON DELETE SET NULL ON UPDATE CASCADE); CREATE TABLE SEMINARTEILNEHMER (semnr D_semnr NOT NULL, tnnr D_tnnr NOT NULL, bezahlt D_bezahlt NOT NULL,

405

A Ausgewählte Lösungen zu den Aufgaben

PRIMARY KEY (semnr, tnnr), FOREIGN KEY (semnr) REFERENCES SEMINAR ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (tnnr) REFERENCES TEILNEHMER ON DELETE CASCADE ON UPDATE CASCADE)

Kapitel 5 Für die Aufgaben aus Kapitel 5 existieren je nach DBMS leicht modifizierte Lösungen. Die hier abgedruckten sind mit SYBASE getestet. Die Versionen für Oracle, Firefox, HSQL, MySQL, MS-SQL, PostgreSQL befinden sich auf der CD .

Aufgabe 5.1 INSERT INTO kunde (kunden_nr, status, name, strasse, plz, ort, letzte_bestellung, letzte_werbeaktion, zahlungsart) VALUES (198,'W','Holgerson, Nils', 'Gänseweg 10','28209','Bremen', DATE ('2008-11-01'), DATE ('2007-06-29'),'N')

Aufgabe 5.2 Es gibt eine Fehlermeldung, da die Kundennummer 198 als Primärschlüssel schon existiert.

Aufgabe 5.3 SELECT * FROM kunde; UPDATE kunde SET name = 'Berger-Steiger, Uwe' WHERE kunden_nr = 102; SELECT * FROM kunde; ROLLBACK;

Aufgabe 5.4 SELECT * FROM artikel; UPDATE artikel SET mindestbestand = mindestbestand + 5; SELECT * FROM artikel;

406

Kapitel 5

Aufgabe 5.5 UPDATE artikel SET listenpreis = listenpreis + 1, bestand = bestand + 10 WHERE artikel_nr = 'K003'

Aufgabe 5.6 INSERT INTO artikel (artikel_nr, mwst, bezeichnung, listenpreis, bestand, mindestbestand, verpackung, lagerplatz) VALUES ('X001', 2, 'Dudelsack', 1000, 1, 1, 'Karton', 7); SELECT * FROM artikel; DELETE FROM artikel WHERE artikel_nr = 'X001'; SELECT * FROM artikel;

Aufgabe 5.7 SELECT bestell_nr, artikel_nr FROM bestellposition ORDER BY bestell_nr, artikel_nr;

Aufgabe 5.8 SELECT DISTINCT lagerplatz FROM artikel;

Aufgabe 5.9 SELECT DISTINCT plz, ort FROM kunde;

Aufgabe 5.10 SELECT artikel_nr, bezeichnung, lagerplatz, bestand FROM artikel WHERE lagerplatz = 7 ;

Aufgabe 5.11 SELECT artikel_nr, bezeichnung, lagerplatz, bestand FROM artikel WHERE lagerplatz = 7 AND bestand > 400 ;

407

A Ausgewählte Lösungen zu den Aufgaben

Aufgabe 5.12 SELECT kunden_nr, name, letzte_werbeaktion FROM kunde WHERE letzte_werbeaktion IS NULL;

Aufgabe 5.13 SELECT bestell_nr, artikel_nr FROM bestellposition WHERE (artikel_nr = 'G001' OR artikel_nr = 'G002' OR artikel_nr = 'G003') AND liefermenge > 0;

Aufgabe 5.14 SELECT FROM WHERE AND OR OR

bestell_nr, artikel_nr bestellposition liefermenge > 2 (artikel_nr = 'G001' artikel_nr = 'G002' artikel_nr = 'G003') AND liefermenge > 2;

Aufgabe 5.15 SELECT bestell_nr, artikel_nr FROM bestellposition WHERE NOT (artikel_nr = 'G001' OR artikel_nr = 'G002' OR artikel_nr = 'G003'

oder SELECT bestell_nr, artikel_nr FROM bestellposition WHERE artikel_nr 'G001' AND artikel_nr 'G002' AND artikel_nr 'G003')

Aufgabe 5.16 SELECT * FROM bestellposition WHERE liefermenge IS NOT NULL

408

Kapitel 5

Aufgabe 5.17 SELECT FROM WHERE AND

kunden_nr, name, ort, status kunde status 'S' ort 'Husum';

Aufgabe 5.18 SELECT bestell_Nr, bestelldatum FROM bestellung WHERE bestelldatum BETWEEN '2008-05-01' AND '2008-05-31';

Aufgabe 5.19 SELECT artikel_nr, bezeichnung FROM artikel WHERE bezeichnung LIKE '%kekse%'

Aufgabe 5.20 SELECT artikel_nr, bezeichnung FROM artikel WHERE artikel_nr LIKE 'G%'

Aufgabe 5.21 SELECT artikel_nr, bestell_nr FROM bestellposition WHERE artikel_nr IN ('K001', 'G003')

Aufgabe 5.22 SELECT artikel_nr, bezeichnung, listenpreis FROM artikel WHERE listenpreis BETWEEN 6.00 AND 15.00

Aufgabe 5.23 SELECT artikel_nr, bestand, mindestbestand, bestand-mindestbestand AS differenz FROM artikel ORDER BY artikel_nr

409

A Ausgewählte Lösungen zu den Aufgaben

Aufgabe 5.24 SELECT bestell_nr || ' vom ' || bestelldatum AS "offene Bestellungen" FROM bestellung WHERE lieferdatum IS NULL

Aufgabe 5.25 SELECT bestell_nr || ' vom ' || bestelldatum AS "offene Bestellungen", bestelldatum + 14 AS "späteste Lieferung" FROM bestellung WHERE lieferdatum IS NULL

Aufgabe 5.26 artikel_nr ist der Primärschlüssel der Artikeltabelle und hat deshalb für jede Zeile einen Wert. Wegen der Eindeutigkeit wäre das Ergebnis von COUNT(DISTINCT artikel_nr) ebenfalls identisch.

Aufgabe 5.27 SELECT mwst, COUNT(*) FROM artikel GROUP BY mwst

Aufgabe 5.28 SELECT MAX (kunden_nr) FROM kunde GROUP BY status

Aufgabe 5.29 SELECT lagerplatz, MIN(listenpreis), MAX (listenpreis), AVG(listenpreis) FROM artikel GROUP BY lagerplatz

Aufgabe 5.30 SELECT plz, COUNT(kunden_nr) FROM kunde GROUP BY plz

410

Kapitel 5

Aufgabe 5.31 SELECT plz FROM kunde GROUP BY plz HAVING COUNT(*) > 1

Aufgabe 5.32 SELECT lagerplatz, SUM(bestand) FROM artikel GROUP BY lagerplatz HAVING SUM(bestand) < 1000;

Aufgabe 5.33 SELECT artikel_nr, artikel.mwst, prozent FROM artikel KEY JOIN mwstsatz

Diese Lösung unterstützt nur Sybase, ansonsten: SELECT artikel_nr, artikel.mwst, prozent FROM artikel JOIN mwstsatz ON artikel.mwst = mwstsatz.mwst

Aufgabe 5.34 SELECT artikel_nr, artikel.mwst, prozent, listenpreis * (1 + prozent) AS bruttopreis FROM artikel JOIN mwstsatz ON artikel.mwst = mwstsatz.mwst

Aufgabe 5.35 SELECT bestell_nr, bestelldatum, ort FROM bestellung JOIN kunde USING(kunden_nr);

Es sind auch andere Arten der Verknüpfung möglich, z. B.: SELECT bestell_nr, bestelldatum, ort FROM bestellung NATURAL JOIN kunde ;

Aufgabe 5.36 SELECT FROM JOIN JOIN

b.bestell_nr, p.artikel_nr, p.bestellmenge, k.ort bestellposition AS p bestellung AS b ON b.bestell_nr = p.bestell_nr kunde AS k ON k.kunden_nr = b.kunden_nr

Es sind auch andere Arten der Verknüpfung möglich.

411

A Ausgewählte Lösungen zu den Aufgaben

Aufgabe 5.37 SELECT k.ort, SUM(p.bestellmenge) FROM bestellposition AS p JOIN bestellung AS b ON b.bestell_nr = p.bestell_nr JOIN kunde AS k ON k.kunden_nr = b.kunden_nr GROUP BY k.ort

Es sind auch andere Arten der Verknüpfung möglich.

Aufgabe 5.38 SELECT a.artikel_nr, p.bestellmenge FROM artikel AS a LEFT OUTER JOIN bestellposition AS p ON a.artikel_nr = p.artikel_nr

Aufgabe 5.39 SELECT k.kunden_nr, k.name, g.blz, g.kontonr FROM kunde AS k LEFT OUTER JOIN girokonto AS g ON k.kunden_nr = g.kunden_nr

Aufgabe 5.40 SELECT artikel_nr, bezeichnung, listenpreis FROM artikel WHERE listenpreis = (SELECT MIN(listenpreis) FROM artikel)

Aufgabe 5.41 SELECT k.kunden_nr, k.name, b.bestell_nr FROM kunde AS k JOIN bestellung AS b ON k.kunden_nr = b.kunden_nr WHERE bestell_nr = (SELECT MAX(bestell_nr) FROM bestellung)

Aufgabe 5.42 SELECT bestell_nr FROM bestellung WHERE kunden_nr IN ( SELECT kunden_nr FROM kunde WHERE ort 'Kayhude');

oder SELECT bestell_nr FROM bestellung WHERE kunden_nr NOT IN ( SELECT kunden_nr FROM kunde WHERE ort = 'Kayhude');

oder

412

Kapitel 5

SELECT FROM JOIN WHERE

DISTINCT b.bestell_nr bestellung AS b kunde AS k ON k.kunden_nr = b.kunden_nr k.ort 'Kayhude';

Aufgabe 5.43 SELECT artikel_nr, listenpreis FROM artikel WHERE listenpreis > ALL ( SELECT listenpreis FROM artikel WHERE artikel_nr LIKE 'G%')

Aufgabe 5.44 SELECT artikel_nr, bezeichnung FROM artikel AS a WHERE EXISTS (SELECT * from bestellposition AS p WHERE a.artikel_nr = p.artikel_nr)

beziehungsweise SELECT artikel_nr, bezeichnung FROM artikel AS a WHERE NOT EXISTS (SELECT * from bestellposition AS p WHERE a.artikel_nr = p.artikel_nr)

Aufgabe 5.45 SELECT k.kunden_nr, k.name FROM kunde AS k WHERE NOT EXISTS ( SELECT * FROM girokonto AS g WHERE g.kunden_nr = k.kunden_nr);

Aufgabe 5.46 -- erster Teil SELECT p.artikel_nr, sum(listenpreis*bestellmenge) AS bestellumsatz FROM bestellposition p JOIN artikel a ON p.artikel_nr = a.artikel_nr GROUP BY p.artikel_nr; -- Zweiter Teil SELECT a1.artikel_nr, a1.bezeichnung, a1.listenpreis, pp.bestellumsatz

413

A Ausgewählte Lösungen zu den Aufgaben

FROM artikel a1 JOIN (SELECT p.artikel_nr, sum(listenpreis*bestellmenge) AS bestellumsatz FROM bestellposition p JOIN artikel a ON p.artikel_nr = a.artikel_nr GROUP BY p.artikel_nr) pp ON a1.artikel_nr = pp.artikel_nr;

Aufgabe 5.47 Vereinigung: SELECT kunden_nr, name, status, ort FROM kunde WHERE status = 'S' UNION SELECT kunden_nr, name, status, ort FROM kunde WHERE ort = 'Kayhude'

Für Durchschnitt bzw. Differenz ersetze UNION durch INTERSECT bzw. MINUS.

Kapitel 8 Zu den Aufgaben aus Kapitel 8 werden keine Lösungen dokumentiert, da diese stark vom verwendeten DBMS abhängen.

414

Syntaxnotation Als formale Beschreibung für die Syntax der SQL-Anweisungen werden folgende Konventionen benutzt: Schreibweise

Beispiel

Bedeutung

Großbuchstaben

SELECT

Schlüsselwort – ist so hinzuschreiben (beim Aufschreiben des Schlüsselwortes sind allerdings auch Buchstaben in Kleinschreibung zulässig).

Kleinbuchstaben

Tabelle

Dieser Text ist durch eine entsprechende syntaktische Konstruktion zu ersetzen.

[ ]

DELETE FROM tabelle [WHERE bedingung]

Der Teil in eckigen Klammern kann fehlen.

|

DISTINCT | ALL

Genau eine der angegebenen Alternativen ist zu nehmen. Es können auch mehr als zwei Alternativen angegeben werden.

{ }

UPDATE tabelle SET spalte = wert {, spalte = wert}

Der Teil in geschweiften Klammern kann fehlen, einmal benutzt oder mehrfach wiederholt werden.

xyz-liste

spaltenliste

Ein Element von xyz oder mehrere Elemente von xyz; Elemente der Liste werden jeweils durch ein Komma getrennt.

Die Konstruktion xyz-liste ist eine Kurzschreibweise für xyz {, xyz}.

415

Beispieldatenbank C.1 Definition der Domänen und Tabellen --------------------------------------------------------------- Datenbank : VERSANDHANDEL -- Version: 2008 --------------------------------------------------------------- getestet für diverse Datenbanksysteme -- Firebird Version 2.0 -Ersetze jeweils "RESTRICT" durch "NO ACTION" -In Version < 2.0 muss noch in den Relationen -Artikel und Bestellposition in den CHECK-Klauseln, wo -das zu prüfende Attribut eine Nullmarke haben kann, die -Möglichkeit einer Nullmarke explizit zugelassen werden. -- HSQLDB -CREATE DOMAIN boolean entfernen -- PostgreSQL Version 8.2 -CREATE DOMAIN boolean entfernen -- Oracle -angepasste Version auf der CD enthalten -- Sybase SQL Anywhere 10 -DOMAIN boolean: ersetze "VALUE" durch "@VALUE" -TABLE bestellung: ersetze "CURRENT_DATE" durch "CURRENT DATE" --------------------------------------------------------------- Domain : BOOLEAN --------------------------------------------------------------CREATE DOMAIN boolean AS SMALLINT CHECK (VALUE BETWEEN 0 AND 1); COMMIT;

417

C Beispieldatenbank

--------------------------------------------------------------- Table : KUNDE --------------------------------------------------------------CREATE TABLE kunde ( kunden_nr INTEGER NOT NULL, status CHAR(1) NOT NULL, name VARCHAR(30) NOT NULL, strasse VARCHAR(30) NOT NULL, plz CHAR( 5) NOT NULL, ort VARCHAR(25) NOT NULL, letzte_bestellung DATE, letzte_werbeaktion DATE, zahlungsart CHAR(1) NOT NULL, PRIMARY KEY (kunden_nr), CHECK (status IN ('W','G','S')), -- Werbemaßnahme, Gelegenheitskunde, Stammkunde CHECK (zahlungsart IN ('R','B','N','V','K')) -- Rechnung, Bankeinzug, Nachnahme, Vorkasse, Kreditkarte ); ----------------------------------------------------------------- Table : MWStSatz ---------------------------------------------------------------CREATE TABLE mwstsatz ( mwst SMALLINT NOT NULL, prozent DECIMAL(3,2) NOT NULL, beschreibung VARCHAR(10) NOT NULL, PRIMARY KEY (mwst) ); ----------------------------------------------------------------- Table : Bestellung ---------------------------------------------------------------CREATE TABLE bestellung ( bestell_nr INTEGER NOT NULL, kunden_nr INTEGER NOT NULL, bestelldatum DATE DEFAULT CURRENT_DATE NOT NULL, lieferdatum DATE, rechnungsbetrag DECIMAL(15,2), PRIMARY KEY (bestell_nr), FOREIGN KEY (kunden_Nr) REFERENCES kunde(kunden_nr) ON DELETE RESTRICT ON UPDATE CASCADE );

418

Definition der Domänen und Tabellen

----------------------------------------------------------------- Table : Artikel ---------------------------------------------------------------CREATE TABLE artikel ( artikel_nr CHAR(4) NOT NULL, mwst SMALLINT NOT NULL, bezeichnung VARCHAR(20) NOT NULL, listenpreis DECIMAL(15,2) NOT NULL, bestand INTEGER NOT NULL, mindestbestand INTEGER NOT NULL, verpackung VARCHAR(10), lagerplatz SMALLINT, kann_wegfallen BOOLEAN, bestellvorschlag TIMESTAMP, nachbestellung TIMESTAMP, nachbestellmenge INTEGER, PRIMARY KEY (artikel_nr), FOREIGN KEY (mwst) REFERENCES mwstsatz(mwst) ON UPDATE CASCADE ON DELETE RESTRICT, CHECK ( -nachbestellmenge IS NULL OR nachbestellmenge > 0) ); ----------------------------------------------------------------- Table : Bestellposition ---------------------------------------------------------------CREATE TABLE bestellposition ( bestell_nr INTEGER NOT NULL, artikel_nr CHAR(4) NOT NULL, mwst DECIMAL(3,3), -- der tatsaechlich angewandte Prozentsatz -bestellmenge INTEGER NOT NULL, liefermenge INTEGER, gesamtpreis DECIMAL(15,2), PRIMARY KEY (bestell_nr, artikel_nr), FOREIGN KEY (bestell_nr) REFERENCES bestellung(bestell_nr) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (artikel_nr) REFERENCES artikel(artikel_nr), CHECK (bestellmenge >= 1), CHECK ( -liefermenge IS NULL OR liefermenge BETWEEN 0 AND bestellmenge) );

419

C Beispieldatenbank

----------------------------------------------------------------- Table : Girokonto ---------------------------------------------------------------CREATE TABLE girokonto( kunden_nr INTEGER NOT NULL, kontoinhaber VARCHAR(30) NOT NULL, blz CHAR( 8) NOT NULL, kontonr CHAR(10) NOT NULL, PRIMARY KEY (kunden_nr), FOREIGN KEY (kunden_nr) REFERENCES kunde(kunden_nr) ON UPDATE CASCADE ON DELETE CASCADE ); COMMIT; ------------------------------------------------------------------

C.2 Tabellarische Darstellung der Relationen Artikel artikel_ mwst bezeichnr nung

listenpreis

bestand mindest- verpabestand ckung

lager- kann_weg platz fallen

G001

2 Whisky

38,50 €

397

50 0,7 l

7 Nein

G002

2 Portwein

12,45 €

473

100 0,5 l

7 Nein

G003

2 Bier

5,20 €

1250

250 6er-Pack

7 Nein

98,50 €

120

25 Karton

2 Nein

112,80 €

62

25

2 Nein

2 Damenhut

65,70 €

12

20 Karton

2 Nein

K004

1 Sonnenbrille

76,00 €

50

20 Karton

2 Nein

L001

1 Ceylon-Tee

6,35 €

356

100 125 g

L002

1 China-Tee

8,35 €

42

50 125 g

5 Nein

L003

1 Naturreis

1,78 €

345

0 1 kg

4 Nein

L004

2 Schokolade

0,98 €

2101

1000 Tafel

2 Nein

L005

2 Butterkekse

1,72 €

234

K001

2 Schuhe

K002

2 Hose

K003

420

250 250 g

5 Nein

2 Nein

Tabellarische Darstellung der Relationen

MwSt.-Satz mwst

prozent

beschreibung

0

0,0

ohne

1

0,07

halbe

2

0,19

volle

Kunde – Teil 1 kunden_nr

status

name

strasse

plz

ort

100

S

Voss, Hans

Kuhdamm 12

23863

Nienwohld

101

S

Stein, Peter

Moordamm 34

23863

Kayhude

102

W

Berger, Uwe

Allee 12 b

25813

Husum

103

S

Randers, Nis

Am Seeufer 12

23845

Oering

104

G

Andresen, Ute

Am Abhang

24558

Ulzburg

105

S

Stuff, Werner

Tarper Weg

24853

Eggebek

106

W

Staack, Hannes

An der Alster 1

23863

Kayhude

Kunde – Teil 2 kunden_nr

letzte_bestellung

100 101

28.04.2008

102 103

letzte_werbeaktion

zahlung

01.12.2007

N

01.12.2007

B

01.12.2007

N

15.05.2008

B

104 105

N 12.05.2008

106

R 01.12.2007

N

Girokonto kunden_nr

konto_inhaber

blz

kontonr

101

Dagmar Stein

23410022

12346789

103

Tetsche Wind

23410112

20001234

421

C Beispieldatenbank

Bestellung bestell_nr

kunden_nr

bestelldatum lieferdatum

rechnungsbetrag

151

101

28.04.2008

02.05.2008

200,67 €

152

103

30.04.2008

02.05.2008

2304,36 €

153

105

12.05.2008

154

103

15.05.2008

Bestellposition bestell_nr

artikel_nr

mwst

bestellmenge

liefermenge

gesamtpreis

151

G002

0.190

4

4

49.80 €

151

G003

0.190

3

3

15.60 €

151

K002

0.190

3

0

0.00 €

151

K003

0.190

1

1

65.70 €

151

L002

0.070

10

5

41.75 €

152

K001

0.190

10

10

985.00 €

152

K003

0.190

2

2

131.40 €

152

K004

0.070

12

12

912.00 €

153

G001

153

L002

6

153

L003

25

153

L004

5

154

G001

4

154

G002

12

154

G003

1

422

2

Literaturverzeichnis Atki89

Atkinson et al.: The Object-Oriented Database System Manifesto. In: DOOD 89 Kyoto, 1989

Balz95

Balzert, H.: Methoden der objektorientierten Systemanalyse. BI Wissenschaftsverlag 1995

Balz00

Balzert, Helmut: Lehrbuch der Software-Technik, 2. Auflage, Spektrum Akademischer Verlag 2000

Bart98

Bartels, J.: OR-Konstrukte in Oracle 8: OO auf stabilem Fundament. In: Objekt Fokus 9/10 1998, IT-Verlag

BrMy86

Brodie, Michael L., Mylopoulos, John (Hg.): On Knowledge Base Management Systems. Springer Verlag 1986

Burk97

Burkhard, R.: UML – Unified Modeling Language. Addison-Wesley 1997

Catt00

Cattell, Rick, et al.: The Object Database Standard: ODMG 3.0. Morgan Kaufmann, 2000

Chen76

Chen, Peter Pin-Shan: The Entity-Relationship Model – Toward a Unified View of Data. In: ACM Transactions on Database Systems, Bd. 1, Nr. 1, März 1976, S. 9-36

CHRS98

Christiansen, A., Höding, M., Rautenstrauch, C., Saake, G.: Oracle 8 effizient einsetzen, Addison Wesley Longman 1998

ChKn91

Chen, Peter Pin-Shan, Knöll, Heinz-Dieter: Der Entity-RelationshipAnsatz zum logischen Systementwurf. BI-Wissenschaftsverlag 1991

Codd81

Codd, E. F.: Relational Database: A Practical Foundation for Productivity. In: ACM Turing Award Lectures. ACM Press 1987, S. 391–410

Codd86

Codd, E. F.: Missing Information (Applicable and Inapplicable) in Relational Databases. In: ACM SIGMOD RECORD, Bd. 15, (Dez. 1986), S. 53–78

Codd87

Codd, E. F.: More Commentary on Missing Information in Relational Databases (Applicable and Inapplicable). In: ACM SIGMOD RECORD, Bd. 16, März 1987

Codd90

Codd, E. F.: The relational model for database management: version 2. Addison-Wesley 1990

423

D Literaturverzeichnis

CoYo94

Coad, Yourdon: Objektorientierte Analyse. Prentice Hall 1994

DaDa93

Date; Darwen: A Guide to the SQL-Standard. Addison-Wesley 1993

DaDa98

Date, C.J.; Darwen, H.: SQL – Der Standard: SQL/92 mit den Erweiterungen CLI und PSM. Addison-Wesley 1998

DaDa00

Date, C.J.; Darwen, H.: Foundation für Future Database Systems: The Third Manifesto ; Addison Wesley Longman, 2000

Date86a

Date, C. J.: An Introduction to Database Systems. Band I, 4. Aufl.; Addison-Wesley 1986

Date86b

Date, C. J.: Relational Database: Selected Writings. Addison-Wesley 1986

Date87

Date, C. J.: A Guide to the SQL Standard. Addison-Wesley 1987

Date90a

Date, C. J.: Relational Database: Selected Writings 1985–1989. Addison-Wesley 1990

Date90b

Date, C. J.: An Introduction to Database Systems, Band II, 5. Aufl.; Addison-Wesley 1990

Date92

Date, C. J.: Relational Database Writings 1989-1991. Addison-Wesley 1992

Date95

Date, C. J.: Relational Database Writings 1991-1994. Addison-Wesley 1995

Date00

Date, C. J.: An Introduction to Database Systems, 7. Auflage. Addison-Wesley 2000.

Dene91

Denert, E.: Software-Engineering. Springer Verlag 1991

Ditt91a

Dittrich, K. R.: Object-Oriented Database Systems: The Notion and the Issues. In: Dittrich, Klaus R., Dayal, Umeshwar, Buchman, Alejandro P.: On Object-Oriented Database Systems. Springer Verlag 1991, S. 3–10

EiMe99

Eisenberg, A., Melton, J.: SQL:1999 formerly known als SQL3. SIGMOD Record, Vol 28, No. 1, March 1999

ElNa05

Elmasri, R., Navathe, S.: Grundlagen von Datenbanksystemen. 3. Auflage. Pearson Studium 2005

FePD05

Feuerstein, S.; Pribyl, B; Dawes, C.: Oracle PL/SQL – kurz & gut. O'Reilly 2005

FiKU89a

Finkenzeller, H., Kracke, U., Unterstein, M.: Systematischer Einsatz von SQL-Oracle. Addison-Wesley 1989

424

Fran03

Frank, U.: Ebenen der Abstraktion und ihre Abbildung auf konzeptionelle Modelle. In: EMISA FORUM – Mitteilungen der GI-Fachgruppe 'Entwicklungsmethoden für Informationssysteme und deren Anwendung' 2003/2

HeSa95

Heuer, A., Saake, G.: Datenbanken: Konzepte und Sprachen. Internat. Thomson Publ. 1995

Heue97

Heuer, A.: Objektorientierte Datenbanken, 2. Auflage; Addison-Wesley 1997

ISO02

ISO-ANSI Working Draft: SQL/Foundation. WG3:DRS-013 H2-2002-358. Über http://sqlx.org/5wd-14-xml-2002-08.pdf

JäHe02

Jähnichen, S.; Herrmann, S.: Was, bitte, bedeutet Objektorientierung?; in: Informatik-Spektrum; Springer-Verlag, 2002/4;

Kähl90

Kähler, W.: SQL: Bearbeitung relationaler Datenbanken. Vieweg Verlag 1990

Kähl99

Kähler, W.: Relationales und objektrelationales SQL. Vieweg Verlag 1999

LaLo95

Lang, Stefan M., Lockemann, Peter C.: Datenbankeinsatz. Springer Verlag 1995

Lans90

Lans, R.: SQL: Der ISO-Standard. Hanser Verlag 1990

Lipe92

Lipeck, U.: Integritätszentrierter Datenbank-Entwurf. In: EMISAForum – Mitteilungen der GI-Fachgruppe »Entwicklungsmethoden für Informationssysteme und deren Anwendung«, 1992, Heft 2

LoSc87

Lockemann, P.C., Schmidt, J. W. (Hrsg): Datenbank-Handbuch. Springer Verlag 1987

MaDL87

Mayr, H. C., Dittrich, K. R., Lockemann, P. C.: Datenbankentwurf. In: [LoSc87]. S. 481-557

Melt98

Melton, J.: Understanding SQL’s Stored procedures: a complete guide to SQL/PSM. Morgan Kaufmann Publishers 1998

Melt03

Melton, J.: Advanced SQL:1999 – Understanding Object-Relational and Other Advanced Features. Morgan Kaufmann Publishers 2003.

Mesc71

Meschkowski: Mathematisches Begriffswörterbuch. Bibliographisches Institut, Mannheim 1971.

MeSi02

Melton, J., Simon, A. R.: SQL:1999 Understandig Relational Language Components. Morgan Kaufmann Publishers 2002

MeWü00

Meier, A., Wüst, T.: Objektorientierte und objektrelationale Datenbanken, 2. Auflage, dpunkt-Verlag 2000.

425

D Literaturverzeichnis

Oest98

Oestereich, B.: Objektorientierte Software-Entwicklung: Analyse und Design mit der Unified Modeling Language. Oldenbourg Verlag 1998

Ora01a

Oracle 9i SQL Reference. Part No. A90125-01, Oracle 2001

Ora01b

Oracle 9i Application Developer's Guide – Object Relational Features. Part No. A88878-01. Oracle 2001

Petk07

Petkovic, Dusan: Die Unterstützung von SQL/OLAP im SQL Standard und in relationalen Datenbanksystemen, Kapitel 1 und 2 in: Datenbank Spektrum, Heft 22/2007, dpunkt.verlag 2007

Raas91

Raasch, J.: Systementwicklung mit strukturierten Methoden. 3. Auflage; Hanser Verlag 1993

RaVo03

Rahm, E., Vossen, G. (Hg.): Web & Datenbanken. Konzepte, Architekturen, Anwendungen. dpunkt.verlag 2003

Reit84

Reiter, R.: Towards a Logical Reconstruction of Relational Database Theory. In: Brodie, Michael L., Mylopoulos, John, Schmidt, J.: On Conceptual Modelling. Springer Verlag 1984, S. 191-238

Salz86

Salzberg, B.: Third Normal Form Made Easy. In: ACM SIGMOD RECORD, Bd. 15, (Dez. 1986); S. 2-18

Schw92

Schwinn, H.: Relationale Datenbanksysteme. Hanser Verlag 1992

ScSc94

Schoop, Schinzer: Konzeption von Client-Server-Applikationen am Beispiel zweier prototypischer Projekte. In: Wirtschaftsinformatik, Bd. 36 (1994), S. 546–556

Stic91

Stickel, E.: Datenbank Design: Methoden und Übungen. Gabler Verlag 1991

StMo99

Stonebraker, M., Moore, D.: Objektrelationale Datenbanken. Hanser 1999

Türk02

Türker, Can: SQL:2003 – Was dürfen wir erwarten? Datenbank Spektrum Heft 4/2002.

TüSa06

Türker, Can, Saake, G.: Objektrelationale Datenbanken. dpunkt.verlag 2006.

Unte94

Unterstein, M.: Kriterien für die Anlage eines Index. In: Kracke (Hrsg.) Datenbank Management. Grundwerk 1994, Teil 9/5.2; Weka Verlag (Interest Verlag)

Unte96

Unterstein, M.: Unternehmensübergreifende Modellierung von Datenstrukturen. Deutscher Universitäts Verlag 1996

VaGa89

Valduriez, P., Gardarin, G.: Analysis and Comparison of Relational Database Systems. Addison-Wesley 1989

426

Vett86

Vetter, M.: Aufbau betrieblicher Informationssysteme mittels konzeptioneller Datenmodellierung. 7. Auflage. Teubner Verlag 1989

Voss87

Vossen, G.: Datenmodelle, Datenbanksprachen und DatenbankManagement-Systeme. Addison-Wesley 1987

VoWi91

Vossen, G.; Witt, K.-U.: Entwicklungstendenzen bei Datenbanksystemen. Oldenbourg Verlag 1991

Weik88

Weikum, G.: Transaktionen in Datenbank-Systemen. Addison-Wesley 1988

Zehn85

Zehnder, C. A.: Informationssysteme und Datenbanken. 4. Auflage. Teubner Verlag 1987

427

Stichwortverzeichnis Symbols != 172 %, Platzhalter 216 *, Platzhalter 203 Numerics 1:1-Beziehung 111 1NF -> s. Normalform, erste 2NF -> s. Normalform, zweite 3NF -> s. Normalform, dritte A Abfrage-Anweisung 201, 261 Abfrage-Optimierer 67 abhängig funktional 130 Abhängigkeit funktionale 129, 134, 142 transitive funktionale 138 triviale funktionale 133 abstrakt -> s. Klasse, abstrakt Abstrakter Datentyp 377 ACID-Prinzip 298 Adj 45, 54 Adjunktion 54, 84 Administrator-Tool 163 Adresse als Datentyp 374 ADT 377 After-Image 300 Aggregat 119 Funktion 81, 233 Aggregation 118, 119 Alias Spalten 218 ALL 203, 267 ALTER TABLE 188 A-Marke 76, 81 AND 79 änderungsaktion 182 Anforderungsdefinition 93 ANSI/X3/SPARC 23 Anweisung UPDATE 302 Anwendungsentwurf 95

428

Anwendungsimplementierung 95 ANY 266 ARRAY 376 AS, Spaltenalias 218 Assertion 340, 341 Assoziation 118, 119, 124 Atomarität 298 ATOMIC 322 Attribut 32, 98, 118, 123, 134, 357 Beziehungs- 134 Entitäten- 134 prim 148 Attribut-Bezeichner 98 Ausdruck arithmetischer 81 logischer 46 Ausschlusskriterium 134 Auswahl 84 AVG 56, 81 B Back-End 28 Backend 344 Before-Image 300, 301, 311 Behälter 363 Benutzeridentität 312 Berechnungsregel für Spalten 153 Bezeichner 32 Attribut- 98 Beziehung 41, 98, 118, 357 in Objektdatenbanken 366 Beziehungstyp 98, 103, 118, 134 lesen 103 BOOLEAN 171 Boyce-Codd-Normalform 150 C CALL 319, 322 Call Level Interface 322 CASCADE 184 cascade 43

CAST 168, 228 CHAR_LENGTH 226 CHARACTER_LENGTH 226 CHECK Klausel 329, 341 CHECK OPTION 291, 339, 341 CLI -> s. Call Level Interface Client 28 Client-Server-Architektur 93 clustered 157 COALESCE 245 Compiler-Direktive 380 CONNECT 316 CONSTRAINT 188 COUNT 56 COUNT DISTINCT 56 COUNT(*) 56 CREATE ASSERTION 330 CREATE SEQUENCE 177 CREATE TABLE 44, 329 CREATE TRIGGER 329 CUBE 247 CURRENT OF cursor 334 Cursor 331, 332, 390 D Data Dictionary 345 Data Warehouse 159, 241, 295 Datei 32 Daten operative 159 Datenbank objektorientierte 136 objektrelationale 136 relationale 40 Vorteile 22 Datenbankadministrator 22, 312, 313 Datenbankentwurf 93 Datenbankkatalog -> s. Systemkatalog 345 Datenbankkonzept 93 Datenbankprozedur 31, 317, 344

Stichwortverzeichnis Datenbankrealisierung 95 Datenbankschema 40 Datenbankserver 29 Datenbanksystem 21 DatenbankVerwaltungssystem 160 Datengruppe 55 Datenmodell 91 konzeptionell 109 objektorientiert 109 physikalisch 109 Datensatz 32 Datenschutz 158 Datensicherung 158 Datensicht 45, 317, 341 aktualisierbare 292, 340, 341 Schreiboperation 290 UPDATE 291 Datentyp elementarer 136 komplexer 136 Datentyp-Konstruktor 358 Datenübernahme 157, 281 Datenunabhängigkeit 25 logische 25 physische 25 Datumswerte umwandeln 228 Dauerhaftigkeit 298 DBA 312, 313, 316 DBMS 21 DBMS -> s. DatenbankVerwaltungssystem Deadlock 307, 311 Behandlung 311 DECLARE CURSOR 332 DEFAULT 43, 185, 343 DEFERRABLE 188 DELETE Anweisung 334 aus Objekttabelle 392 eingebettete Tabelle 392 Privileg 314 Denormalisierung 153 DEREF 385 Determinante 133 Dictionary 360 Differenzmenge 45, 73, 85 Diskriminator 123 DISTINCT 203 Distinct 84, 86

DISTINCT TYPE 168 DOMAIN 167 Domäne 37, 98, 107, 171, 318, 336 im Systemkatalog 351 dreiwertige 179 Logik 81, 209 dual 220 dummy 220 Durchschnittsmenge 45, 73, 85 E Eigenschaft 98, 357 Einfügen Tupel in Objekttabelle 378 Eingebettete Tabelle 387 elementar -> s. Datentyp Entität 41, 96, 118 abhängige 97, 100, 112, 121, 387 Vater- 97 Entitätenmenge 97, 118 Entitätentyp 97, 118 abhängiger 371 Entitätsintegrität 41 Entity-RelationshipDiagramm 102, 107 Entity-RelationshipModell 102, 139 ER-Diagramm-> s. EntityRelationship-Diagramm Ereignis 328 ERM -> s. Entity-RelationshipModell ESQL 322 EXCLUSIVE 310 EXECUTE 318, 324 Exemplar 118 EXISTS 269 versus IN 271 extensional 99, 100 Extent 363 extent 118 externe Spezifikation 358 F FALSE 79, 81 Feld 32 FETCH 332 Fremdschlüssel 26, 42, 44, 87, 113, 123, 145, 157

FROM 203 FROM-Klausel 251 Front-End 28 Frontend 344 Funktion 324 Datenbank- 324 siehe Aggregatfunktion 230 funktional abhängig 130 Funktionen 220 G GENERATED 177 Geschäftsprozess 93 Geschäftsregel 26, 99, 342 statische 336 Gleichheit 365 GRANT 313 GRANT OPTION 315 Granularität von Sperren 305 GROUP BY 234 Projektionsregel 238 GROUPING 245 GROUPING SETS 247 Grp 45, 55 Gruppenbildung zulässige Attribute 232, 238 Gruppierung 45, 55, 84 H Hierarchie 360 Historiensätze 159 I Identifikationsschlüssel 100, 135 Identifizierbarkeit 118, 141 Identität 357, 365 IFNULL 245 I-Marke 76, 78, 79 Impedance Mismatch 332 Implementierung 375 IN 265, 318 INDEX 190 Index 87, 157 Informationssystem 159 Ingres 31 Inkonsistente Analyse 304 INSERT 195 mit SELECT 281 Privileg 314

429

Stichwortverzeichnis instance 118 Instanz 357, 363 Integrität Aktionsregeln 184 Entitäts- 41 Foreign 182 referenzielle 42 Integritätsbedingung 26, 27 bei Objekt 377 Integritätsregel 26 Namen für 176 intentional 99, 100, 131 Interface 363 INTERSECT 279 INTO Klausel 320 isInapplicable isNull 80 isNull 82 isNullisInapplicable 80 Isolation-Level 308 Isoliertheit 298 ISQL 322 isUnknown 81 J JOIN 45, 59, 60 Column name 250, 252 Condition 250, 252 Cross 250, 251 FULL OUTER 251 Full Outer 71 INNER 251 Inner 71 KEY 253 LEFT OUTER 251 Left Outer 71 natural 250, 253 old style 252 Outer 70 RIGHT OUTER 251 Right Outer 71 Self 62 K Kapselung 365 Kardinalität 103, 107, 119 Katalog -> s. Systemkatalog 345 Kayhude 131, 140 Kennziffer betriebliche 159 Klasse 118, 123 abstrakt 122, 123

430

Klassendiagramm 117 Klassenmethode 374 Kollektionstyp 359, 388 Kollektionstyp -> s. Sammlung kompatibel 38 Komponente 119 Komposition 118, 119, 121, 387 Konkurrenz im Datenzugriff 303 Konsistenz 22, 303, 317, 318 referenzielle 61 Konsistenzbedingung 40 Konsistenzproblem Inkonsistente Analyse 304 Lost Update 304 Konsistenzüberprüfung 22 Konstruktor 358, 367 korrelierte Subquery 270 Kreuzprodukt 45, 67, 85 L LIKE 216 Liste 83 Literal 357 Lock -> s. Sperre LOCK TABLE 310 Logik dreiwertige 81 vierwertige 78, 82 logischer Operator 210 Löschen von Tupeln 200 Lost Update 304 LOWER 227 M Management Information System 159 mandatory -> s. obligatorisch MATCH 182 MATCH-Klausel 183 MAX 56, 81 MAYBE, TYPE APPLICABLE 82 MAYBE, TYPE INAPPLICABLE 82 Mehrfachfeld 136 Mehrfachvererbung 362 Menge 40, 83 Mengengerüst 157 Mengenoperation 73 Mengenoperationen in SQL 276

MERGE 283 Metadaten -> s. Systemkatalog 345 Methode 358, 365 Methodensignatur 374 MIN 56, 81 MINUS 279 MIS -> s. Management Information System Multimenge 83, 85, 359, 387 Multi-Tier-Architektur 29 N Nachricht 117 navigierender Zugriff 370, 386 nested table 387 NEXT VALUE 177 NO ACTION 184 Normalform 128, 135, 137, 376 Boyce-Codd 150 dritte 138, 144, 148 erste 135, 136, 141 zweite 138, 144, 148 Normalisierung 128, 129, 139 NOT 79, 82 NOT FINAL 377 Notationsrichtlinien fürobjektrelationaleDatens trukturen 373 NULL 43, 75, 175, 185 bei Gruppenfunktion 233 im Fremdschlüssel 183 Nullmarke 37, 40, 75 in Gruppierungsspalte 238 Nullwert 76 Numerische Funktionen 226 Nummerierung 327 Nutzung 95 O Objekt 117, 357 komplexes 136 Operation 117 Objekt-Identifizierer 119 Objektidentität 365 objektorientiert 136 Objektorientierte Datenbank 355, 358 Objektorientierung 355 objektrelational 136, 356, 367, 371 Objekttabelle 368, 369

Stichwortverzeichnis obligatorisch 103 ODBC 160, 322, 344 ODL 356 ODMG 356 OID 365, 369, 370 OLAP 241 OPEN 332 Operation 358 - auf Domänen 35 Operation -> s. Objekt Operator BETWEEN 214 für Vergleiche 172 IN 215 logischer 46 Teilstring 216 Vergleichs- 46 Optimierung Abfrage 280 Optimistische Konkurrenzkontrolle 308 Sperrmethode 303 optional 103 OQL 356 OR 79 ORDER BY 204 orthogonal 360 Orthogonalität 378 OUTER JOIN 278 Outer Join 257 OUTER UNION 73 OVERLAPS 340 P Parallelbetrieb 157 Persistent 365 Platzhalter % 216 * 203 Polymorphismus 362 POSITION 227 PowerDesigner 107 PRAGMA 380 Primärschlüssel 26, 41, 42, 44, 86, 113, 145 PRIMARY KEY 339 Primattribut 148 Privileg Objekt- 314 Zugangs- 312 Programmiersprache 322 imperative 319, 332 Proj 45

Projektion 31, 45, 49, 53, 55, 83, 84, 86 Prozedur -> s. Datenbankprozedur Prozess 95 Prozessmodell 91 Punktnotation 370, 386 Q QUEL 31 R Recovery 300 Redundanz 22 REF 370 REFERENCES Privileg 314 Referenz 370, 383, 388 Referenzattribut 383 Referenzielle Integrität 366 bei UPDATE 199 referenzielle Integrität 371 Reichweite von Attributen 270 Relation 31, 32, 40, 83, 86, 123 abhängige 42 in Mathematik 38 relational 31, 161 Relationenalgebra 32, 44, 161 Relationenschema 39, 40, 109 formale Definition 37 Relationentheorie 31 Ren 45, 52 RESOURCE 316 RESTRICTED 184 restricted 43 REVOKE 315 Rolle 98, 119, 316 ROW 369 ROW EXCLUSIVE 310 ROW SHARE 310 S Sammlung 359 Savepoint 302 SCHEMA 166 Schema 166 externes 25 internes 25 konzeptionelles 24, 27 objektorientierte Datenbank 358

Schlüssel 26, 87, 100, 133, 145, 157 Entitäts- 100 Identifikations- 100, 135 sprechender 101 Schlüsselfeld 40 Schlüsselkandidat 133 Schlüsselsystem 100 Schnittstelle 375 Seiteneffekt 380 Sel 45, 46, 81 SELECT Anweisung 320, 332 Klausel 324 mit GROUP BY und HAVING 239 mit Projektion und Sortieren 202 mit Selektion und Projektion 205 Privileg 314 SELECT * 197 Selektion 31, 45, 46, 57, 73, 79, 81 SEQUEL 31 SEQUENCE 177 Serialisierbarkeit 298 Server 28 Service -> s. Methode SET 359 Klausel 324 SHARE ROW EXCLUSIVE 310 S-Lock 306 Snapshot 44, 281 Sohn-Relation 109 Spalte 32 im Systemkatalog 350 virtuelle 45, 54, 84, 218 spätes Binden 362 Sperre Deadlock 307 Explizite Anforderung 310 Typ S 306 Typ X 306 Sperrmechanismus 305 Granularität 305 Objekte 306 SQL 31, 76, 78, 82, 87 embedded 322 SQL/PSM 317 SQL-92 70

431

Stichwortverzeichnis SQL-Server 31 SQLSTATE 331 stehen 98 stored procedure -> s. Datenbankprozedur String-Funktionen 227 Strukturvererbung 362 Subquery 261 mit mehreren Vergleichswerten 264 SUBSTITUTABLE bei Objekttabelle 377 SUBSTRING 228 Subtyp 363, 367 SUM 56, 81 Superaggregat 245 Supertyp 367 Systemanalyse 93 Systemkatalog 345 T Tabelle 32, 83, 161 eingebettete 387 globale temporäre 186 im Systemkatalog 347 typisierte 377 Tabellenalias 250, 392 in objektorientierten Abfragen 385 Tabellenausdruck 273 TABLE 174 in SELECTAnweisung 389 in UPDATEAnweisung 392 Testphase 95 Theta-Join 59 Tool -> s. Werkzeug Totalsicherung 158 Transaktion 297, 322 Abschnitt 302 ACID-Prinzip 298 Isolation-Level 308 Transaktionsmanagement 300

432

Transient 365 transitiv -> s. Abhängigkeit TREAT 387 Trigger 31, 111, 129, 317, 328, 344 trivial -> s. Abhängigkeit TRUE 46, 81 Tupel 32, 36, 40, 83 widersprüchlich 37 Typ 357 Typenkompatibilität 363

Vereinigungsmenge 45, 73, 85 Vererbung 118, 121, 360 Vergleich 79 Vergleichsoperatoren 173 Verhalten 117, 357, 358 Verhaltensvererbung 362 Verteilung 159 View 285 CREATE VIEW 288 virtuell 45 Vorgabewert 176

U Übereinstimmungstyp 182 Überladen 362 Umbenennung 45, 51, 62, 84 UML 105, 117 UNDER 381 UNION 86, 277 UNIQUE 339 unique 119 UNKNOWN 81 unnormalisiert 140 Unterabfrage weitere Verwendungen in SELECT, UPDATE, INSERT 261 Unterklasse 122 Untertyp 360 UPDATE 198 Anweisung 321, 324, 334 Privileg 314 UPPER 227

W Werkzeug 93 WHERE 207 bei DELETE 200 bei UPDATE 198 Klausel 324, 334 mit Unterabfrage 260, 261 widersprüchlich (Tupel) 36 widerspruchsfrei (Tupel) 37 Wiederherstellung der Konsistenz 300 Wiederholungsgruppe 141 WINDOW 243 Window frame clause 244 Wirtssprache 322 WITH 276 Workflow 93

V VALUE 380, 387 VARRAY 376 Vater-Relation 109, 111 Vaterrelation 42, 44 Verbund 31, 59, 80 äußerer 45, 70, 78, 85 natürlicher 45, 59, 85 Vereinigung 86

X XBase 32, 60 X-Lock 306 Z Zeile 32 Zugriffsrecht 317, 318, 324, 341 Zusicherung 119, 187 Zustand 357

Copyright Daten, Texte, Design und Grafiken dieses eBooks, sowie die eventuell angebotenen eBook-Zusatzdaten sind urheberrechtlich geschützt. Dieses eBook stellen wir lediglich als persönliche Einzelplatz-Lizenz zur Verfügung! Jede andere Verwendung dieses eBooks oder zugehöriger Materialien und Informationen, einschliesslich •

der Reproduktion,



der Weitergabe,



des Weitervertriebs,



der Platzierung im Internet, in Intranets, in Extranets,



der Veränderung,



des Weiterverkaufs



und der Veröffentlichung

bedarf der schriftlichen Genehmigung des Verlags. Insbesondere ist die Entfernung oder Änderung des vom Verlag vergebenen Passwortschutzes ausdrücklich untersagt! Bei Fragen zu diesem Thema wenden Sie sich bitte an: [email protected] Zusatzdaten Möglicherweise liegt dem gedruckten Buch eine CD-ROM mit Zusatzdaten bei. Die Zurverfügungstellung dieser Daten auf unseren Websites ist eine freiwillige Leistung des Verlags. Der Rechtsweg ist ausgeschlossen. Hinweis Dieses und viele weitere eBooks können Sie rund um die Uhr und legal auf unserer Website

http://www.informit.de herunterladen