Dusan Petkovic
INGRES Das relationale Datenbanksystem mit Knowledge-Base und Object-Base ISBN: 3893193855 Gebundene Aus...
53 downloads
926 Views
841KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Dusan Petkovic
INGRES Das relationale Datenbanksystem mit Knowledge-Base und Object-Base ISBN: 3893193855 Gebundene Ausgabe - Addison-Wesley, München Erscheinungsdatum: 1992
Inhalt Inhalt ....................................................................................................................................................................................................1 Abstract...............................................................................................................................................................................................8 Einleitung..........................................................................................................................................................................................13 1
Einleitung...............................................................................................................................................................................13
1.1 Datenbanken - allgemein .......................................................................................... 17 1.1.1 Logische Datenunabhängigkeit ........................................................................ 18 1.1.2 Physikalische Datenunabhängigkeit ................................................................. 18 1.1.3 Prozedurale und nichtprozedurale Schnittstellen ............................................. 18 1.1.4 Effiziente Abarbeitung der Datenbankoperationen.......................................... 19 1.1.5 Minimale Datenredundanz ............................................................................... 19 1.1.6 Datenintegrität .................................................................................................. 19 1.1.7 Konkurrierender Datenzugriff .......................................................................... 19 1.1.8 Datensicherheit ................................................................................................. 20 1.1.9 Datenschutz ...................................................................................................... 20 1.2 Relationale Datenbanken.......................................................................................... 20 1.3 Datenbankdesign ...................................................................................................... 23 1.3.1 Allgemeine Hinweise zur Normalisierung ....................................................... 24 1.3.2 Erste Normalform............................................................................................. 24 1.3.3 Zweite Normalform.......................................................................................... 24 1.3.4 Dritte Normalform............................................................................................ 25 1.3.5 Vierte Normalform........................................................................................... 26 1.4 INGRES-Datenbanksprachen................................................................................... 26 1.4.1 Die Datenbanksprache SQL............................................................................. 27 1.5 Notation.................................................................................................................... 28 Aufgaben .............................................................................................................................. 29 SQL-Komponenten .........................................................................................................................................................................30 SQL-Komponenten .........................................................................................................................................................................30
Grundelemente der SQL-Sprache ......................................................................................... 30 Literale.............................................................................................................................. 31 Beispiel 2.1 ........................................................................................................................... 31 Beispiel 2.2 ........................................................................................................................... 31 Beispiel 2.3 ........................................................................................................................... 31 Begrenzer.......................................................................................................................... 31 Namen............................................................................................................................... 32 Schlüsselwörter................................................................................................................. 32 Datentypen............................................................................................................................ 32 Numerische Datentypen .................................................................................................. 32 Alphanumerischer Datentyp ............................................................................................. 33 Abstrakte Datentypen....................................................................................................... 34 Beispiel 2.4 ........................................................................................................................... 34 Beispiel 2.5 ........................................................................................................................... 34 Prädikate ............................................................................................................................... 35 Aggregatfunktionen.............................................................................................................. 35 Skalare Funktionen............................................................................................................... 36 Datumsfunktionen ............................................................................................................ 37 Zeichenkettenfunktionen.................................................................................................. 37 Spezielle Konstanten und die DBMSINFO-Systemfunktion........................................... 39
Arithmetische und Boolesche Operatoren............................................................................ 40 Beispiel 2.6 ........................................................................................................................... 40 NULL-Werte ........................................................................................................................ 40 Klassifizierung der SQL-Anweisungen................................................................................ 41 Aufgaben. ............................................................................................................................. 42 INGRES-frames...............................................................................................................................................................................43 2
INGRES-frames ....................................................................................................................................................................43
2.1 INGRES/MENU....................................................................................................... 43 2.2 Erstellung von Tabellen mit Hilfe von INGRES/MENU......................................... 45 Aufgaben .............................................................................................................................. 48 INGRES/Forms, JoinDefs und INGRES/QBF...........................................................................................................................49 3
INGRES/Forms, JoinDefs und INGRES/QBF................................................................................................................49
3.1 Einfache Masken...................................................................................................... 49 3.2 JoinDefs .................................................................................................................... 53 3.3 Komplexe Masken.................................................................................................... 58 Aufgabe. ............................................................................................................................... 59 INGRES/RBF, Report-Writer und INGRES/VIGRAPH .........................................................................................................60 4
INGRES/RBF, Report-Writer und INGRES/VIGRAPH...............................................................................................60
4.1 Einleitung ................................................................................................................. 60 4.2 INGRES/RBF ........................................................................................................... 61 4.3 Report-Writer ........................................................................................................... 66 Beispiel 5.1 ........................................................................................................................... 66 4.4 INGRES/GRAPHS ................................................................................................... 71 Aufgaben. ............................................................................................................................. 73 Datendefinition ................................................................................................................................................................................75 5
Datendefinition......................................................................................................................................................................75
5.1 Erstellen der Objekte ................................................................................................ 75 Beispiel 6.1 ........................................................................................................................... 77 5.2 Löschen der Objekte ................................................................................................. 78 Aufgaben .............................................................................................................................. 78 Einfache Abfragen...........................................................................................................................................................................80 6
Einfache Abfragen................................................................................................................................................................80
6.1 Grundform der SELECT-Anweisung....................................................................... 80 6.2 Die WHERE-Klausel................................................................................................ 82 6.2.1 Boolesche Funktionen...................................................................................... 83 6.2.2 Die Operatoren IN und BETWEEN ................................................................. 86 6.2.3 Der NULL-Operator ......................................................................................... 88 6.2.4 Der Operator LIKE........................................................................................... 89 6.3 Einfache Unterabfragen............................................................................................ 91 6.3.1 Unterabfrage und Vergleichsoperatoren........................................................... 91 6.3.2 Unterabfragen und IN-Operator ....................................................................... 92 6.3.3 Die Operatoren ANY und ALL........................................................................ 93 6.3.4 Der Operator EXISTS ...................................................................................... 95 6.4 Die GROUP BY-Klausel.......................................................................................... 95 6.5 Aggregatfunktionen.................................................................................................. 96 6.5.1 Die Funktionen MIN und MAX....................................................................... 97 6.5.2 Die Funktion SUM ........................................................................................... 98 6.5.3 Die Funktion AVG ........................................................................................... 99 6.5.4 Die Funktion COUNT ...................................................................................... 99
6.6 Die HAVING-Klausel............................................................................................ 100 6.7 Die ORDER BY-Klausel........................................................................................ 101 6.8 Der Mengenoperator UNION................................................................................. 103 Aufgaben ............................................................................................................................ 106 Komplexe Abfragen......................................................................................................................................................................107 7
Komplexe Abfragen ...........................................................................................................................................................107
7.1 Verknüpfen zweier oder me hrerer Tabellen........................................................... 107 7.1.1 Der Equijoin ................................................................................................... 108 Beispiel 8.1 ......................................................................................................................... 108 Beispiel 8.2 ......................................................................................................................... 110 7.1.2 Das Kartesische Produkt................................................................................. 110 Beispiel 8.3 ......................................................................................................................... 110 Beispiel 8.4 ......................................................................................................................... 111 7.1.3 Der Natürliche Join......................................................................................... 111 Beispiel 8.5 ......................................................................................................................... 112 7.1.4 Der Thetajoin.................................................................................................. 113 Beispiel 8.7 ......................................................................................................................... 113 Beispiel 8.8 ......................................................................................................................... 113 7.1.5 Verknüpfung von mehr als zwei Tabellen...................................................... 114 Beispiel 8.9 ......................................................................................................................... 114 7.1.6 Eine Tabelle mit sich selbst verknüpfen......................................................... 115 Beispiel 8.12 ....................................................................................................................... 116 7.1.7 Der Outer Join ................................................................................................ 116 Beispiel 8.13 ....................................................................................................................... 117 Beispiel 8.14 ....................................................................................................................... 117 7.2 Korrelierte Unterabfragen ...................................................................................... 118 Beispiel 8.15 ....................................................................................................................... 118 Beispiel 8.16 ....................................................................................................................... 119 Beispiel 8.18 ....................................................................................................................... 120 7.3 Der EXISTS-Operator in Unterabfragen................................................................ 121 Beispiel 8.19 ....................................................................................................................... 121 Beispiel 8.20 ....................................................................................................................... 122 Beispiel 8.22 ....................................................................................................................... 123 Beispiel 8.23 ....................................................................................................................... 124 Beispiel 8.24 ....................................................................................................................... 124 Aufgaben ............................................................................................................................ 124 Änderung der Tabellen .................................................................................................................................................................126 8
Änderung der Tabellen ......................................................................................................................................................126
8.1 Die INSERT-Anweisung........................................................................................ 126 8.1.1 Einfügen einer Reihe ...................................................................................... 127 Beispiel 9.1 ......................................................................................................................... 127 Beispiel 9.2 ......................................................................................................................... 127 Beispiel 9.3 ......................................................................................................................... 127 Beispiel 9.5 ......................................................................................................................... 128 Beispiel 9.6 ......................................................................................................................... 128 Beispiel 9.7 ......................................................................................................................... 128 8.1.2 Einfügen mehrerer Reihen.............................................................................. 128 Beispiel 9.8 ......................................................................................................................... 128 Beispiel 9.9 ......................................................................................................................... 129 Beispiel 9.10 ....................................................................................................................... 130
8.2 Die UPDATE-Anweisung ...................................................................................... 130 Beispiel 9.11 ....................................................................................................................... 131 Beispiel 9.12 ....................................................................................................................... 131 Beispiel 9.13 ....................................................................................................................... 131 8.3 Die DELETE-Anweisung....................................................................................... 132 Beispiel 9.14 ....................................................................................................................... 132 Beispiel 9.15 ....................................................................................................................... 132 Beispiel 9.16 ....................................................................................................................... 132 Aufgaben ............................................................................................................................ 132 Views ...............................................................................................................................................................................................134 9
Views ....................................................................................................................................................................................134
9.1 Datendefinitionsanweisungen und Views .............................................................. 134 9.1.1 Erstellen von Views........................................................................................ 135 9.1.2 Views löschen................................................................................................. 138 9.2 Abfragen in Views.................................................................................................. 139 9.3 Ändern eines Views................................................................................................ 139 9.3.1 INSERT-Anweisung und View...................................................................... 140 9.3.2 UPDATE-Anweisung und View .................................................................... 142 9.3.3 DELETE-Anweisung und View ..................................................................... 143 Aufgaben ............................................................................................................................ 144 INGRES-Data Dictionary und Systemkatalog .........................................................................................................................145 10
INGRES-Data Dictionary und Systemkatalog...............................................................................................................145
10.1 10.2 10.3
INGRES-Master-Datenbank ................................................................................... 145 INGRES-Systemkatalog......................................................................................... 146 Abfragen auf Systemtabellen ................................................................................. 149
Speicherstrukturen und Indexe ....................................................................................................................................................151 11
Speicherstrukturen und Indexe .........................................................................................................................................151
11.1 Speicherstrukturen.................................................................................................. 151 11.1.1 Stapel.............................................................................................................. 152 11.1.2 Hash................................................................................................................ 152 11.1.3 ISAM .............................................................................................................. 153 11.1.4 BTREE ........................................................................................................... 153 11.2 Indexe ..................................................................................................................... 154 11.2.1 SQL-Anweisungen in Bezug auf Indexe ........................................................ 155 11.2.2 Indexe und Schlüssel...................................................................................... 159 11.2.3 Kriterien zur Erstellung eines Index............................................................... 160 11.3 Allgemeine Kriterien zur Verbesserung der Effizienz ........................................... 162 11.3.1 Join statt korrelierter Unterabfrage ................................................................. 162 11.3.2 Unvollständige Anweisungen......................................................................... 163 11.3.3 LIKE-Operator ............................................................................................... 163 11.4 Optimieren von Suchanweisungen......................................................................... 164 Aufgaben ............................................................................................................................ 165 Transaktionen und Restaurierung...............................................................................................................................................166 12
Transaktionen und Restaurierung ....................................................................................................................................166
12.1 Transaktionen......................................................................................................... 166 12.2 Restaurierung.......................................................................................................... 168 12.2.1 Fehlerquellen.................................................................................................. 168 12.2.2 Log- und Journal-Datei................................................................................... 169 12.3 Sperren.................................................................................................................... 171
12.3.1 12.3.2
Objekte sperren............................................................................................... 171 Die SET LOCKMODE-Anweisung............................................................... 172
INGRES-Sicherheitskonzept und Integrität..............................................................................................................................175 13
INGRES-Sicherheitskonzept und Integrität...................................................................................................................175
13.1 Das INGRES-Sicherheitskonzept........................................................................... 175 13.1.1 Die Anweisungen GRANT und DROP PERMIT .......................................... 177 13.1.2 Einschränkung des Datenzugriffs mit Views ................................................. 178 13.2 Integritätsvorschriften............................................................................................. 179 Aufgaben. ........................................................................................................................... 181 Systemumgebung ..........................................................................................................................................................................183 14
Systemumgebung................................................................................................................................................................183
14.1 Umgebungsvariablen.............................................................................................. 183 14.2 Betriebssystemkommandos .................................................................................... 186 14.2.1 ingmenu-Kommando ...................................................................................... 186 14.2.2 Kommando qbf............................................................................................... 186 14.2.3 Kommando vifred........................................................................................... 187 14.2.4 Kommandos rbf, sreport und report ............................................................... 187 14.2.5 Kommando vigraph........................................................................................ 188 14.2.6 Kommando sql................................................................................................ 188 14.3 INGRES-Dienstprogramme ................................................................................... 189 14.3.1 Das Dienstprogramm accessdb....................................................................... 189 14.3.2 Das Dienstprogramm catalogdb ..................................................................... 192 14.3.3 Dienstprogramme createdb und destroydb ..................................................... 192 14.3.4 Dienstprogramme ckpdb und rollforwarddb .................................................. 193 14.3.5 Das Dienstprogramm unloaddb ...................................................................... 194 14.3.6 Dienstprogramm auditdb ................................................................................ 195 Die eingebettete SQL-Sprache ....................................................................................................................................................196 15
Die eingebettete SQL-Sprache .........................................................................................................................................196
15.1 Einleitung ............................................................................................................... 196 15.1.1 Kennzeichnen der eingebetteten SQL-Anweisungen..................................... 198 15.1.2 Variablen ........................................................................................................ 198 15.1.3 Fehlerbehandlung ........................................................................................... 201 15.2 Eingebettete SQL-Anweisungen ohne Cursor........................................................ 204 15.3 Verwendung des Cursors........................................................................................ 214 15.4 Übersetzung eines ESQL/C- bzw. ESQL/COBOL-Programms ............................ 222 15.5 Eingebettete Anweisungen und Programmierstil ................................................... 222 15.5.1 Optimierung der Datenbankzugriffe............................................................... 223 15.5.2 Explizite Abfrage bei der Änderung von Reihen........................................... 227 15.5.3 Explizite Angabe aller Reihen in einer SELECT- bzw. ................................. 227 INSERT-Anweisung....................................................................................................... 227 Aufgaben ............................................................................................................................ 228 Dynamisch formulierte Anweisungen.......................................................................................................................................229 16
Dynamisch formulierte Anweisungen.............................................................................................................................229
16.1 Einführung.............................................................................................................. 229 16.2 Vorbereitung und Ausführung einer SQL-Anweisung........................................... 230 16.3 Dynamisch formulierte SELECT-Anweisungen in ESQL/C ................................. 235 16.4 Dynamisch formulierte SQL-Anweisungen und Optimierung............................... 235 Aufgaben ............................................................................................................................ 239 Datenbank-Prozeduren .................................................................................................................................................................241
17
Datenbank-Prozeduren.......................................................................................................................................................241
17.1 17.2 17.3
Einführung.............................................................................................................. 241 SQL-Anweisungen in Bezug auf DB-Prozeduren.................................................. 242 Fehlerbehandlung und Meldungen......................................................................... 244
Object Management ......................................................................................................................................................................247 18
Object Management ...........................................................................................................................................................247
18.1 18.2 18.3 18.4
Einleitung ............................................................................................................... 247 Benutzerdefinierte Datentypen............................................................................... 248 Benutzerdefinierte Funktionen............................................................................... 250 Funktionsinstanzen................................................................................................. 251
Knowledge Management..............................................................................................................................................................253 19
Knowledge Management...................................................................................................................................................253
19.1 Benutzerdefinierte Regel........................................................................................ 253 19.1.1 Referentielle Integrität .................................................................................... 255 19.2 Erweiterter Autorisierungsmechanismus ................................................................ 258 19.2.1 Gruppen- und Prozedur-Zugriffsrechte .......................................................... 258 19.2.2 Die erweiterte GRANT-Anweisung und die REVOKE-Anweisung ............. 260 Verteilte Datenbanken bei INGRES ..........................................................................................................................................264 20
Verteilte Datenbanken bei INGRES ................................................................................................................................264
20.1 Einleitung ............................................................................................................... 264 20.2 INGRES/NET ......................................................................................................... 265 20.2.1 GCA................................................................................................................ 265 20.2.2 Zugriff auf eine ferne INGRES-Datenbank mit INGRES/NET ..................... 266 20.3 INGRES/STAR ...................................................................................................... 267 20.3.1 Zwei-Phasen-Commit..................................................................................... 269 20.4 Gateways ................................................................................................................ 270
Abstract This paper describes the conception for a book about INGRES. INGRES is a relational database system(DBS), developed and marketed by Relational Technology Inc. (RTI). RTI has been amongst the first vendors to realize the tremendous potential for relational database systems in the UNIX world. Combining the
advantage of
being one of a firstcomers with
a series of state-of-the-art products, RTI has the most versatile and the most efficient product line in the UNIX DBS market. As well as under UNIX, INGRES runs in many other different environments, including VMS, VM/CMS, MVS/XA and DOS. INGRES database system belongs to the Open Desktop Software from Santa Cruz Operation, which gives INGRES a great potential in the near future. This book describes the latest version of INGRES – Version 6.3 and is practically written. The emphasis has been placed not on theory, but on practice. The book is intended to serve as a supplement to the INGRES-Manuals for end users and application programmers. In every chapter there are numerous examples. All these examples are based upon a small database, which is extremely simple, but is adequate to illustrate almost all of the points made in this book. The book has 20 chapters and aproximately 400 pages: 1
Introduction 1.1 Database 1.2 Relational Databases 1.3 Database Design 1.4 Database Languages SQL and QUEL
2
SQL Components 2.1 Basic Objects 2.2 Data Types 2.3 Scalar and Set Functions 2.4 Arithmetic and Boolean Operators
3
INGRES/MENU 3.1 Creating a Table using INGRES/MENU 3.2 Examining a Table using INGRES/MENU
4
INGRES/FORMS 4.1 Creating a Form using VIFRED 4.2 Editing a Table using INGRES/FORMS
5
INGRES/REPORTS 5.1 Creating a Basic Report 5.2 Report Writer
6
INGRES/QUERY 6.1 Querying a Table using SQL 6.2 Querying a Table using QBF 6.3 Join Definitions -8-
7
Data Definition Statements 7.1 Creating Database Objects 7.2 Destroying Database Objects
8
Simple Queries 8.1 SELECT Statement - Basic Form 8.2 SELECT Statement - WHERE Clause 8.3 Simple Subqueries 8.4 SELECT Statement - GROUP BY Clause 8.5 Set Functions in SELECT Statement
9
Complex Queries 9.1 Joining Tables 9.2 Correlated Subqueries 9.3 EXISTS Operator
10 Updating a Table 10.1 INSERT Statement 10.2 UPDATE Statement 10.3 DELETE Statement 11 System Catalog 11.1 Basic Catalog 11.2 Extended Catalog 11.3 Global Catalog 11.4 Querying the System Catalog 12 Views 12.1 Data Definition Statements and Views 12.2 Querying a View 12.3 Updating a View 13 Storage Structure 13.1 Introduction 13.2 MODIFY Statement 13.3 Heap, Hash and Isam 13.4 Indexes 14 Authorisation and Integrity 14.1 GRANT Statement 14.2 REVOKE Statement 14.3 Data Access and Views 14.4 Integrity Statements 15 Transactions and Backups 15.1 Introduction 15.2 Savepoints 15.3 Database Backups 16 INGRES Command Line Interface -9-
16.1 System Commands 16.2 Environment Variables 17 Embedded SQL 17.1 Introduction 17.2 Embedded SQL Statements Without Cursor 17.3 Embedded SQL Statements With Cursor 18 Distrubuted Databases 18.1 Introduction 18.2 INGRES/NET 18.3 INGRES/STAR 19 Gateways 20 INGRES/Windows 4GL 20.1 Introduction 20.2 Implementing an Application using Windows 4GL
Appendix A. QUEL Statements Appendix B. Referential Integrity The first chapter discusses databases in general and relational databases in particular. The notion of normal forms and the demonstration database are also defined in this chapter. Finally, both INGRES database languages - SQL and QUEL - are explained in general terms. Chapter 2 discusses all components of the SQL language. First, we introduce the basic objects and existing data types. All functions and operators are also described in this chapter.
In the third chapter we present INGRES/MENU subsystem, and use this INGRES subsystem to create and examine the tables of the demonstration database.
The fourth chapter explains the use of INGRES/FORMS. We use the utility VIFRED to create different forms for tables of the demonstration database. The functions of INGRES/FORMS(editing trims and fields etc.) are also described in this chapter.
Chapter 5 has two parts. In the first part of this chapter we create basic reports. The second part explains the use of the Report Writer to create more sophisticated reports.
Chapter 6 explains the use of Query-by-Forms introduction to the usage of SQL for querying tables.
-10-
and
gives
an
With Chapter 7 we start a new section of the book. We describe the data definition language (DLL) with all SQL statements belonging to it. The creation of tables using SQL statements is also shown in this chapter.
Chapters 8 and 9 discuss the most important SQL statement - SELECT. These chapters consider the introduction of the SELECT statement and describe the use of simple and complex queries. Each clause concerning SELECT is separately defined and explained with reference to the demonstration database.
In Chapter 10 we discuss the three SQL statements for the updating of data - INSERT, UPDATE and DELETE. Each of these three statements is explained using numerous examples.
Chapter 11 discusses the different parts of the INGRES system catalog. All system tables belonging to the Basic Catalog are explained and examples concerning querying these tables are given. The other two parts of system catalog - the Extended Catalog and the Global Catalog are described in general terms.
In Chapter 12 we introduce views and describe all SQL statements concerning them. At the end of this chapter we discuss the existing restrictions to update a view. INGRES supports many different possibilities to store database objects in memory. All these possibilities are discussed in Chapter 13. The MODIFY statement, which converts the storage structure of a table or index is also explained in this chapter. In Chapter 14 we discuss two important issues that must be addressed when implementing a database system. The first – database authorization - includes two SQL statements - GRANT and REVOKE which provide the security of portions of a database against unauthorized access. The use of a view for the same purpose is also explained. The second issue integrity explains the possibility of keeping INGRES databases in a consistent state with respect to the constraints specified in the database. Chapter 15 describes the concept of a transaction and the SQL statements, which control the transaction: SET AUTOCOMMIT, COMMIT, ROLLBACK and SAVEPOINT. In the second part of this chapter we discuss the INGRES utilities used in the recovery from failures. Chapter 16 first introduces the operating system commands which can be used to start INGRES Terminal Monitors, database utilities and other subsystems. In the second part of this chapter we describe numerous environment variables, which are used by INGRES to influence the behaviour of different INGRES products.
-11-
In Chapter 17 we discuss Embedded SQL (ESQL) and explain the difference between Interactive and Embedded SQL. As a host language we use the programming language C. All examples in this chapter are programmed with reference to the demonstration database. Chapter 18 introduces the distributed databases and two INGRES products concerning them - INGRES/NET and INGRES/STAR. We show in this chapter the architecture and the functionality of both of these products. Chapter 19 is continuation of the previous chapter. It describes another INGRES product concerning distributed databases - Gateway. We explain the concept of Gateway and the access to it. The last chapter, Chapter 20, introduces the newest product from INGRES product line - INGRES/Windows 4GL. Based on an application program we explain the use and functions of Windows 4GL. As far the author knows, two other books exist in English concerning INGRES: - Stonebraker, M. (ed.) - The INGRES Papers, Addison Wesley, 1986. - Date, C.J. – A Guide to INGRES, Addison Wesley, 1987. The emphasis of the book "The INGRES Papers" is on theory, and not on practice. This book describes the design and construction of INGRES and is therefore intended to serve as a textbook for database researchers. The main drawback of the second book "A Guide to INGRES" is that it concentrate on the database language QUEL. In the last 5 years (the book was written in 1986) SQL has become the most important database language, but this language is handled only marginally in the book.
-12-
Einleitung
Einleitung .......................................................................................................................................13 1
Einleitung.................................................................................................................................13 1.1 Datenbanken - allgemein 17 1.1.1 Logische Datenunabhängigkeit 18 1.1.2 Physikalische Datenunabhängigkeit 18 1.1.3 Prozedurale und nichtprozedurale Schnittstellen 1.1.4 Effiziente Abarbeitung der Datenbankoperationen 1.1.5 Minimale Datenredundanz 19 1.1.6 Datenintegrität 19 1.1.7 Konkurrierender Datenzugriff 19 1.1.8 Datensicherheit 20 1.1.9 Datenschutz 20 1.2 Relationale Datenbanken 20 1.3 Datenbankdesign 23 1.3.1 Allgemeine Hinweise zur Normalisierung 24 1.3.2 Erste Normalform 24 1.3.3 Zweite Normalform 24 1.3.4 Dritte Normalform 25 1.3.5 Vierte Normalform 26 1.4 INGRES-Datenbanksprachen 26 1.4.1 Die Datenbanksprache SQL 27 1.5 Notation 28 Aufgaben 29
18 19
1 Einleitung Das Datenbanksystem INGRES ("INteractive Graphics and REtrieval System") hat seinen Ursprung in einem Projekt, das von Michael Stonebraker und Eugene Wong an der Universität von Kalifornien in Berkeley in den Jahren 1973-1975 durchgeführt wurde. Das Ziel dieses Projektes war, ein relationales Datenbanksystem zu implementieren. Im Winterund Sommersemester 1973/74 wurden zwei Projektgruppen gebildet, die die Erstellung einer Datenbanksprache und die Erzeugung einer Schnittstelle für die Datenzugriffe zur Aufgabe hatten. Als Ergebnis der Arbeit der ersten Gruppe entstand die Datenbanksprache QUEL ("QUEry Language"), während die zweite Projektgruppe fünf verschiedene Zugriffsmethoden und den Systemkatalog implementierte. -13-
Für beide Projekte wurde das Betriebssystem UNIX verwendet. Im Laufe der Zeit zeigte sich, daß die Einbettung der Sprache QUEL in eine Programmiersprache der dritten Generation für die weitere Entwicklung des Systems unbedingt notwendig war. Als Ergebnis dieser Überlegungen entstand EQUEL ("Embedded QUEL") - ein Vorübersetzer für die Einbettung der QUEL-Anweisungen in die Programmiersprache C. Nachdem die anfänglichen Ziele des Projektes erreicht waren und ein Prototyp erstellt war, wurde in der zweiten Hälfte des Jahres 1975 damit begonnen, das ganze System leistungsfähiger und zuverlässiger zu gestalten. Aus diesem Grund wurde der Schwerpunkt auf den konkurrierenden Datenzugriff und die Datensicherheit gelegt. Im Jahre 1979 entschieden die beiden Projektleiter, den existierenden Prototyp zu vermarkten. Deswegen wurde die Firma RTI (Relational Technology Incorporated) gegründet und die Codekonvertierung von UNIX auf VMS, das Standardbetriebssystem der DEC-Rechner, durchgeführt. Das erste kommerzielle INGRES-Produkt enthielt zusätzlich einen Listenprogrammgenerator sowie einen EQUEL-Vorübersetzer für die Sprachen BASIC, COBOL, Fortran und Pascal. Dies waren aus der Sicht der Benutzer gleichzeitig die wichtigsten Einschränkungen des INGRES-Prototyps. In den Jahren 1981 - 1983 wurde zuerst das Dienstprogramm QBF ("Query-by-Forms") implementiert, das den Benutzern eine einfache Schnittstelle zur Datenmanipulation bietet. In dieser Zeit wurden auch ein anderer Optimierer (JOINOP) und zwei weitere Benutzerschittstellen GBF ("Graph-by-Forms") und RBF ("Report-by-Forms"), implementiert. GBF ermöglicht den Benutzern, die Daten in Grafikform auszugeben, während RBF einen visuellen Editor für die Definition von Listenprogrammen darstellt. Als wichtige Neuigkeit in diesem Zeitraum wurde ABF ("Application-by-Forms") implementiert. ABF stellt eine Entwicklungsumgebung dar, die für die Datenmanipulation das Dienstprogramm QBF und für die Datenausgabe RBF und GBF verwendet. Damit stellt ABF ein interaktives, formatorientiertes Entwicklungssystem dar. Ab Mitte 1983 bis Mitte 1984 wurde der Schwerpunkt der Firmenaktivitäten auf ein besseres Marketing- und Verkaufsprogramm gelegt. In diesem Zeitraum wurden auch einige technische Verbesserungen durchgeführt wie die Implementierung eines Subsystems (INGRES/CS) für Kleinrechner und die Darstellung von Masken für Mehrtabellen auf dem Bildschirm. Im Bereich der verteilten Datenverarbeitung hatte INGRES von Anfang an einen technologischen Vorsprung gegenüber anderen relationalen Datenbanksystemen. Im Jahre 1983 wurde mit INGRES/NET das erste Produkt freigegeben, das einen verteilten Zugriff auf Daten ermöglicht. Damit war RTI die erste Firma überhaupt, die ein Produkt zur Unterstützung des -14-
Server/Clients-Prinzips auf den Markt gebracht hat. Im Jahre 1986 wurde INGRES/STAR freigegeben, das als erstes Produkt die Verteilung der Daten im Netz unterstützte. 1987 wurde INGRES/PC eingeführt, das eine Integration von Personal Computern in die Datenverarbeitung eines Unternehmens ermöglichte. Im Jahr darauf erweiterte RTI die INGRES-Produktpalette für die verteilte Datenverarbeitung um Gateways, was einen verteilten Zugriff auf Datenbanken unterschiedlicher Hersteller ermöglichte. 1989 brachte RTI die Multiserver-Architektur auf den Markt. Damit war INGRES das erste Datenbanksystem, das eine dynamische Zuweisung zwischen den unterschiedlichen Datenbank-Anwendungsprogrammen ("Front-Ends") einerseits und verschiedenen Datenbank-Servern ("Back-Ends") andererseits ermöglichte. Anfang 1990 wurde das INGRES-Datenbanksystem erweitert. Neben dem Datenbank-Management, das das bisherige Datenbanksystem enthielt, wurden "Knowledge-Management" und "Objekt-Management" auf den Markt gebracht. "Knowledge-Management" ermöglicht den Aufbau regelbasierter Systeme, während "Objekt-Management" die objektorientierte Schnittstelle zum Datenbanksystem unterstützt. Die zuletzt freigegebene INGRES-Version, Version 6.3, die im vierten Quartal 1989 auf den Markt gebracht wurde, bietet eine neue Entwicklungsumgebung INGRES/Windows-4GL. Dieses Produkt ermöglicht die Erstellung von Datenbankanwendungen mit einer grafischen Benutzerschnittstelle. Die mit INGRES/Windows-4GL erstellten Anwendungen können ohne Programmodifikation unter verschiedenen grafischen Schnittstellen wie Open Look, OSF/Motif usw. laufen. Alle INGRES-Produkte sind für eine Vielzahl von Betriebssystemen implementiert. Der wichtigste Bereich, in dem sie angeboten werden, ist der Rechnerbereich mit dem Betriebssystem UNIX. Die UNIX-Version von INGRES existiert für die meisten UNIX-Derivate, die entweder auf Intel-, Motorola- oder anderen Prozessoren laufen. Im Großrechnerbereich werden INGRES-Produkte u.a. für DEC VAX und Micro VAX (mit dem Betriebssystem VMS) angeboten. In der letzten Zeit erweist sich für INGRES-Produkte der Personal Computer-Bereich mit dem Betriebssystem DOS als immer wichtigerer Markt. Die INGRES-Software unter DOS enthält nur Datenbank-Anwendungen (front ends). Trotzdem sind die Unterschiede zwischen INGRES-Produkten für verschiedene Betriebssysteme minimal und können deswegen ignoriert werden. In diesem Buch wird Bezug auf INGRES unter dem Betriebssystem UNIX genommen. Das INGRES-Datenbanksystem enthält, wie alle anderen relationalen Datenbanksysteme, zwei Komponenten - Datenbank-Anwendung (front end) und - Datenbank-Server (back end) . Die Datenbank-Anwendungen sind alle INGRES-Subsysteme -15-
wie INGRES/FORMS, ESQL/C-Komponente usw. Aufgabe des Datenbank-Servers ist es, alle in einer Anwendung programmierten Abfragen bzw. Änderungen auf die Daten einer Datenbank durchzuführen. Die Kommunikation zwischen den Datenbank-Anwendungen einerseits und dem Datenbank-Server andererseits hat bei INGRES im Laufe der Zeit unterschiedliche Formen gehabt. Bei der ersten Form kommunizierte jede Anwendung mit einem Datenbank-Server, wie in Abbildung 1-1 dargestellt ist: Anwendung_ 1
Anwendung_ 2
Anwendung_ 3
Server_1
Server_2
Server_3
Abb. 1-1 1:1 Architektur Der Nachteil dieser Kommunikationsform besteht darin, daß Datenbank für jede Datenbank-Anwendung ein eigener Datenbank-Server gestartet werden muß. Damit werden für jeden Benutzer eines Systems je 2 Prozesse gestartet, was bei einer großen Benutzeranzahl Engpässe im Arbeitsspeicher verursachen kann. Eine der nachfolgendenen IN GRES-Versionen hatte eine andere Kommunikationsform zwischen den Datenbank-Anwendungen und dem Datenbank-Server. In dieser Version wurde immer ein einziger Datenbank-Server als Prozeß gestartet, der dann mit allen Datenbank-Anwendungen, die in Bezug zu einer INGRES-Datenbank standen, kommunizierte. Diese Kommunikationsform wird oft n:1-Architektur genannt. Sie ist in Abbildung 1-2 dargestellt. Anwendung_ 1
Anwendung_ 2
Anwendung_ 3
Server Abb. 1-2 n:1-Architektur Der Vorteil dieser Kommunikationsform liegt darin, daß Datenbank nur n+1-Prozesse gestartet werden, wobei n die Anzahl der Benutzer einer INGRES-Datenbank darstellt. Bei einer großen Anzahl der Benutzer, die auf eine Datenbank zugreifen, kann damit der befürchtete Arbeitsspeicherengpaß vermieden werden. Diesem Vorteil steht folgender Nachteil gegenüber: Die Verwendung eines einzigen Datenbank-Servers für eine große Anzahl von Datenbank-Anwendungen kann zum Engpaß beim Prozessor führen, an dem der Datenbank-Server läuft. INGRES Version 6 hat eine dritte Kommunikationsform -16-
zwischen den Datenbank-Anwendungen und den Datenbank-Servern hervorgebracht, die beide Engpässe beseitigt. Diese Kommunikationsform (auch Multi-Server-Architektur genannt) ist gleichzeitig die fortschrittlichste von allen existierenden Kommunikationsformen. Die Abbildung 1-3 stellt die Multi-Server-Architektur dar.
Anwendung 1
Anwendung 2
Anwendung 3
Anwendung 4
Server1 Server2 Abb. 1-3 m:n - Architektur
Anwendung 5
Server3
Bei der Multi-Server-Architektur wird die Kommunikation zwischen den Datenbank-Anwendungen und mehreren Datenban Datenbank-Servern, von welchen jeder einem Prozessor zugeordnet ist, frei gewählt.k (Deswegen wird diese Kommunikationsform auch m:n-Archtitekur genannt.) Die Anzahl der Prozesse bei der m:n-Architektur liegt bei m+n, wobei m die Anzahl der Datenbank-Anwendungen und n die Anzahl der Datenbank-Server ist.
1.1
Datenbanken - allgemein Bevor wir uns mit relationalen Datenbanken befassen, soll der Begriff der Datenbank allgemein erläutert werden. Eine Datenbank kann aus verschiedenen Blickwinkeln betrachtet werden, abhängig davon, in welchem Zusammenha ng sie gesehen wird. Aus der Sicht eines Managers ist die Datenbank eine Sammlung logisch zusammenhängender Daten, die ein Modell der Aktivitäten seines Unternehmens darstellen. Aus der Sicht eines Datenbanksystems ist die Datenbank eine Sammlung von physikalischen Daten. Obwohl beide Betrachtungsweisen diametral verschieden sind, haben sie etwas gemeinsam: Sie erfordern Funktionen, mit denen eine Datenbank abgefragt und modifiziert werden kann sowie Schnittstellen, die maschinenunabhängig sind. All diese Funktionen und Schnittstellen soll ein Datenbanksystem bieten. Zusammenfassend soll ein Datenbanksystem folgendes gewährleisten: - logische Datenunabhängigkeit; - physikalische Datenunabhängigkeit; - prozedurale und nichtprozedurale Schnittstellen; - effiziente Abarbeitung von Datenbankoperationen; - minimale Datenredundanz; - Datenintegrität; -17-
- konkurrierender Datenzugriff; - Datensicherheit und - Datenschutz. Jede dieser Grundeigenschaften eines Datenbanksystems soll nachfolgend erläutert werden. 1.1.1 Logische Datenunabhängigkeit Die logische Datenunabhängigkeit bezieht sich auf unterschiedliche logische Sichten einer Datenbank. Einerseits existiert die logische Struktur einer Datenbank mit allen dazugehörigen Objekten und Zusammenhängen, die zwischen diesen Objekten bestehen. Andererseits sieht jeder Benutzer, der eine Datenbankanwendung programmiert oder benutzt, nur den für ihn relevanten Ausschnitt der gesamten logischen Struktur. Die logische Unabhängigkeit bedeutet, daß jeder Benutzer seine Sicht der Datenbank erzeugen und modifizieren kann, ohne daß die logische Gesamtstruktur geändert werden müßte. 1.1.2 Physikalische Datenunabhängigkeit Unter der physikalischen Datenunabhängigkeit versteht man die Unabhängigkeit zwischen logischer und physikalischer Struktur einer Datenbank. Die physikalische Datenunabhängigkeit garantiert, daß die physikalische Struktur einer Datenbank beliebig geändert werden kann, ohne daß dadurch die logische Struktur berührt wird. 1.1.3 Prozedurale und nichtprozedurale Schnittstellen Bei Datenbanksystemen existieren u.a. zwei Arten von Benutzern, nämlich der Programmierer und der Endbenutzer. Die Aufgabe eines Programmierers ist es, Programme zu schreiben, mit denen eine Datenbank abgefragt oder modifiziert werden kann. Endbenutzer sind in der Regel keine DV-Fachleute. Sie greifen auf die Datenbank über eine leicht erlernbare Kommandosprache zu. Falls auf der Ebene dieser Kommandosprache Kontrollstrukturen angeboten werden, wie z.B. IF bedingung THEN wahr_zweig ELSE falsch_zweig spricht man von einer prozeduralen, ansonsten von einer nichtprozeduralen Schnittstelle. Ein Programmierer hat in den meisten Fällen weitaus komplexere Aufgabenstellungen zu erledigen als ein Endbenutzer und benötigt daher nahezu immer Programmiersprachen mit einem umfangreichen Spektrum an Kontrollstrukturen. Dementsprechend sind alle bekannteren -18-
Datenbankschnittstellen für Schnittstellen.
Programmierer
prozedurale
1.1.4 Effiziente Abarbeitung der Datenbankoperationen Mit der Verwendung mächtiger Datenbankprogrammiersprachen wird die Entwicklungszeit von Datenbankanwendungen in der Regel reduziert, oft allerdings auf Kosten von zusätzlichen E/A-Operationen und längerer Verarbeitungszeit. Das Datenbanksystem sollte daher für die Abarbeitung der jeweiligen Datenbankoperation eine optimale Strategie entwickeln können. 1.1.5 Minimale Datenredundanz In dateiorientierten Anwendungen, bei denen jeder Benutzer seine Dateien unabhängig von den anderen Benutzern verarbeitet, existiert zwangsläufig eine große Menge redundanter Daten. Durch diese wird unnötig viel Speicherplatz verbraucht. Daneben werfen redundante Daten im Falle einer Änderung erhebliche Probleme auf, weil es in der Regel nicht möglich ist, redundante Daten synchronisiert zu modifizieren. Eine wesentliche Aufgabe jedes Datenbanksystems ist es daher, die Datenredundanz zu minimieren. Die später in diesem Kapitel beschriebenen Normalformen ermöglichen die Minimierung der Datenredundanz. 1.1.6 Datenintegrität Ein Datenbanksystem sollte offensichtlich unsinnige Daten erkennen und abweisen können. (Das Datum 30. Februar oder die Uhrzeit 17:77:00 sind typische Beispiele.) Daneben ist es wünschenswert, gegenüber dem Datenbanksystem Begrenzungen oder Formatangaben zu Eingabedaten deklarieren zu können, z.B.: Jahrgang > 1949 Ausgereifte Datenbanksysteme stellen außerordentlich komplexe Mechanismen zur Plausibilitätsprüfung von Eingabedaten zur Verfügung. Dazu gehört insbesondere die Fähigkeit, bereits in der Datenbank vorhandene Daten in die Plausibilitätsprüfung mit einzubeziehen. 1.1.7 Konkurrierender Datenzugriff Der Umstand, daß im Regelfall viele Benutzer gleichzeitig auf eine Datenbank zugreifen, wirft eine Reihe von Problemen auf. Ein besonders gravierendes Problem wird durch das folgende Beispiel erläutert: 1) Auf dem Konto 4711 der Bank X befinden sich 10.000DM. 2) Die Kunden A und B gehen in zwei verschiedene Filialen der Bank X und heben gleichzeitig 10.000DM vom Konto 4711 ab. -19-
3) Die Kassierer in beiden Bankfilialen bekommen vom Datenbanksystem den Kontostand 10.000DM gezeigt. 4) Beide Kassierer zahlen jeweils 10.000DM aus und ändern das Konto 4711 mit dem Wert 10.000 DM minus 10.000 DM gleich 0 DM. 5) Es ist nun unerheblich, in welcher Reihenfolge diese beiden Änderungen ausgeführt werden; das Konto 4711 steht auf 0 DM statt auf -10.000 DM. Der ganze Problemkreis des konkurrierenden Datenzugriffs, der hier aufgezeigt wurde, muß von einem Datenbanksystem natürlich mit völliger Korrektheit abgehandelt werden. Bezogen auf das vorangehende Beispiel bedeutet dies, daß ein Datenbanksystem den Kontostand von -10.000 DM garantieren muß, nachdem beiden Kunden je 10.000 DM ausgezahlt wurden. 1.1.8 Datensicherheit Der Begriff der Datensicherheit bezieht sich auf den Ausfall von Hardware und/oder Software. Ein Datenbanksystem sollte in der Lage sein, nach denkbaren Ausfällen die betroffenen Datenbanken automatisch in den letzten konsistenten Stand zu überführen. 1.1.9 Datenschutz Eine Datenbank sollte gegen unerlaubten Zugriff geschützt werden können. Entsprechende Möglichkeiten, wie Vergabe und Entzug der Zugriffsrechte, sollte jedes Datenbanksystem unterstützen.
1.2
Relationale Datenbanken Der Begriff der relationalen Datenbanken wurde 1970 von E.F.Codd eingeführt. In dem Artikel "A Relational Model of Data for Large Shared Data Banks" wurde die theoretische Grundlage für relationale Datenbanken festgelegt: Das sogenannte relationale Datenmodell. Im Unterschied zu anderen Datenbanksystemen (netzwerkartigen bzw. hierarchischen Systemen), basiert das relationale Modell völlig auf den mathematischen Grundlagen der relationalen Algebra. Eine Erklärung der relationalen Algebra liegt außerhalb der Ziele dieses Buches. Wir werden die wichtigsten Eigenschaften des relationalen Modells mit Hilfe einer Beispieldatenbank erklären. Weiter dient die Beispieldatenbank als Grundlage für alle praktischen Beispiele innerhalb dieses Buches. Das Grundelement einer relationalen Datenbank ist die Tabelle. Aus der Benutzersicht besteht jede relationale Datenbank nur aus Tabellen. Eine Tabelle setzt sich aus Reihen und Spalten zusammen, d.h. sie beinhaltet keine, eine oder mehrere Reihen und eine oder mehrere Spalten. Das -20-
Objekt, das genau zu einer Reihe und einer Spalte gehört, heißt Datenwert oder Datum. Die Beispieldatenbank enthält die Datenwerte einer Firma, die in mehrere Abteilungen unterteilt ist. Jeder Mitarbeiter der Firma gehört zu einer der existierenden Abteilungen an. Die Eigenschaft unserer Firma ist, daß die Mitarbeiter ihre Tätigkeiten in verschiedenen Projekten ausüben. Jeder Mitarbeiter kann in verschiedenen Projekten arbeiten und dabei unterschiedliche Aufgaben wahrnehmen. Die Datenbank besteht bei uns aus vier Tabellen: abteilung mitarbeiter projekt arbeiten . Die Tabelle abteilung stellt alle Abteilungen der Firma dar. Jede Abteilung ist auf folgende Weise beschrieben: abteilung (abt_nr, abt_name, stadt) abt_nr ist die für jede Abteilung der Firma eindeutige Abteilungsnummer. abt_name steht für den Namen der Abteilung; stadt für die Stadt, in der sich diese Abteilung befindet. Die Tabelle mitarbeiter beinhaltet alle Mitarbeiter der Firma. Jeder Mitarbeiter ist auf folgende Weise beschrieben: mitarbeiter (m_nr, m_name, m_vorname, abt_nr) m_nr kennzeichnet die für jeden Mitarbeiter eindeutige Personalnummer. m_name und m_vorname kennzeichnen Namen und Vornamen des Mitarbeiters, während abt_nr die Nummer der Abteilung benennt, welcher der Mitarbeiter angehört. Die Tabelle projekt stellt alle Projekte der Firma dar. Jedes Projekt ist dabei auf folgende Weise beschrieben: projekt (pr_nr, pr_name, mittel) pr_nr bezeichnet die innerhalb der Firma eindeutige Nummer des Projektes. pr_name und mittel kennzeichnen den Namen des Projektes bzw. die Geldmittel, die für das Projekt zur Verfügung stehen. Die Geldmittel sind in DM angegeben. Die Tabelle arbeiten beschreibt die Beziehung zwischen den Mitarbeitern und den Projekten. Diese Tabelle ist auf folgende Weise beschrieben: arbeiten (m_nr, pr_nr, aufgabe, einst_dat) m_nr zeigt die Personalnummer des Mitarbeiters und pr_nr die Nummer des Projektes, in dem der Mitarbeiter arbeitet, an. Die Kombination aus m_nr und pr_nr ist innerhalb der Firma eindeutig. aufgabe beschreibt die Funktion des Mitarbeiters (mit der Personalnummer m_nr) innerhalb des Projektes (mit der Nummer pr_nr). einst_dat kennzeichnet das Eintrittsdatum des Mitarbeiters in das Projekt. Die relationale Datenbank für das soeben beschriebene Schema ist in Abbildung 1-4a bis 1-4d dargestellt. (Die Primärschlüssel aller Tabellen sind im Unterschied zu den anderen Spalten invers dargestellt.) abt_nr abt_name
stadt -21-
a1 a2 a3
Beratung Diagnose Freigabe
Muenchen Muenchen Stuttgart
Abb. 1-4 a) Tabelle abteilung m_nr 25348 10102 18316 29346 9031 2581 28559
m_name Keller Huber Mueller Probst Meier Kaufmann Mozer
m_vorname Hans Petra Gabriele Andreas Rainer Brigitte Sibille
abt_nr a3 a3 a1 a2 a2 a2 a1
Abb. 1-4 b) Tabelle mitarbeiter pr_n r p1 p2 p3
pr_name Apollo Gemini Merkur
Mittel 120000 95000 186500
Abb. 1-4 c) Tabelle projekt m_nr 10102 10102 25348
pr_nr p1 p3 p2
aufgabe Projektleiter Gruppenleiter Sachbearbeite r
einst_dat 01-oct-1988 01-jan-1989 15-feb-1988
18316 29346 2581 9031 28559 28559
p2 p2 p3 p1 p1 p2
9031
p3
29346
p1
01-jun-1989 15-dec-1987 Projektleiter 15-oct-1989 Gruppenleiter 15-mar-1989 01-aug-1988 Sachbearbeite 01-feb-1989 r Sachbearbeite 15-nov-1988 r Sachbearbeite 01-apr-1989 r
Abb. 1-4 d) Tabelle arbeiten Mit Hilfe unseres Beispiels können wir jetzt einige wichtige Eigenschaften des relationalen Modells erklären: - die Reihen innerhalb einer Tabelle können beliebige Reihenfolge haben; - die Spalten innerhalb einer Tabelle können beliebige Reihefolge haben; -22-
- alle Datenwerte einer Spalte haben genau denselben Datentyp; - jede Spalte hat einen eindeutigen Namen innerhalb einer Tabelle. Spalten, die verschiedenen Tabellen angehören, können durchaus denselben Namen haben. (Beispiel: Die Spalte m_nr in der Tabelle arbeiten und die Spalte m_nr in der Tabelle mitarbeiter.); - jeder einzelne Datenwert innerhalb einer Tabelle ist durch einen einzigen Wert dargestellt. Das heißt: In einer Reihe und innerhalb einer Spalte können sich nie mehrere Werte gleichzeitig befinden. - in jeder Tabelle einer relationalen Datenbank existiert ein (oder mehrere) Bezeichner, der jede Reihe der Tabelle eindeutig definiert. Dieser Bezeichner kann entweder aus einer Spalte oder aus einer Kombination mehrerer Spalten bestehen. Im relationalen Modell heißt dieser Bezeichner Primärschlüssel. Die Spalte abt_nr ist der Primärschlüssel in der Tabelle abteilung; m_nr ist der Primärschlüssel in der Tabelle mitarbeiter; pr_nr ist der Primärschlüssel in der Tabelle projekt und die Kombination der Spalten (m_nr, pr_nr) ist der Primärschlüssel in der Tabelle arbeiten. - in einer Tabelle existieren nie zwei identische Reihen; (Diese Eigenschaft wird von INGRES und allen anderen relationalen Datenbanksystemen nicht unterstützt.) Hinweis In der Terminologie relationaler Datenbanken existieren mehrere analoge Begriffe. So entsprechen die mathematischen Begriffe Relation, Tupel und Attribut in der Praxis jeweils den Be griffen Tabelle, Reihe und Spalte. Zusätzlich existieren in der Praxis weitere Begriffe wie Satz oder Record (für Reihe), Feld (für Spalte) usw. In diesem Buch werden nur die Begriffe benutzt, die im SQL-Standard verwendet wurden, also Tabelle, Reihe und Spalte.
1.3
Datenbankdesign Das Datenbankdesign ist eine sehr wichtige Phase, die der Erstellung einer Datenbank vorangeht. Falls das Datenbankdesign intuitiv und ohne sorgfältige Analysephase entworfen wird, ist die daraus resultierende Datenbank in den meisten Fällen nicht optimal an die Aufgabe, zu deren Lösung sie aufgebaut wurde, angepaßt. Die daraus resultierende Folge kann überflüssige Datenredundanz, mit damit verbundenen Nachteilen für Speicherverbrauch und Datenkonsistenz sein. Die Normalisierung der Daten stellt ein Verfahren dar, in dem die Datenredundanz stufenweise reduziert werden kann. Mit der Normalisierung der Daten wird ein weiteres Ziel - die logische Unabhängigkeit der Daten - verfolgt. Insgesamt existieren fünf Normalformen, von welchen wir die ersten vier erläutern werden. Die fünfte Normalform hat keine -23-
bzw. sehr geringe praktische Bedeutung. Jede Normalform ist in der nachfolgenden enthalten. 1.3.1 Allgemeine Hinweise zur Normalisierung Der Prozeß der Normalisierung einer Datenbank sollte immer mit der ersten Normalform beginnen. Nachdem die Datenbank die erste Normalform erfüllt, sollten die Tabellen der Datenbank so zerlegt werden, daß sie die zweite Normalform erfüllen usw. Für die meisten Datenbanken genügt die Normalisierung bis einschließlich der dritten Normalform. Die vierte und insbesondere die fünfte Normalform finden in der Praxis selten Anwendung. Die im Zusammenhang mit der Normalisierung entscheidende Frage ist, wieviel Datenredundanz sinnvoll ist. Diese Frage kann nur für jede Datenbank separat beantwortet werden. Das wichtigste Kriterium für diese Entscheidung ist, ob die Datenbank wenigen oder vielen Änderungen unterworfen ist. Die Datenbanken, die wenigen oder keinen Änderungen unterworfen sind, können problemlos mehr Datenredundanz enthalten. Demgegenüber sollten die Datenbanken, die häufig geändert werden, möglichst wenig redundante Daten haben, weil das Ziel, die redundanten Daten konsistent zu halten, i.a. nur mit hohem Aufwand zu erreichen ist. 1.3.2 Erste Normalform Eine Tabelle ist in der ersten Normalform, falls in jeder Reihe und für jede Spalte nur atomare Werte existieren. Wie aus dem vorherigen Abschnitt ersichtlich, beinhaltet das relationale Modell an sich schon diese Eigenschaft. Die erste Normalform werden wir anhand eines Ausschnitts aus der Tabelle arbeiten der Beispieldatenbank darstellen: 10102 10102 .....
p1 p3 ....
..... ..... ......
Falls diese zwei Reihen folgendermaßen geschrieben würden: 10102 .....
(p1, p3) ..... ........ ......
würde die Tabelle arbeiten nicht in der ersten Normalform sein. (Dies ist im relationalen Modell nicht möglich.) 1.3.3 Zweite Normalform Eine Tabelle befindet sich in der zweiten Normalform, wenn jede Spalte dieser Tabelle, die den Primärschlüssel -24-
nicht bildet, voll funktional abhängig von jedem Teil des Primärschlüssels ist. Nehmen wir an, die Tabelle arbeiten der Beispieldatenbank enthält folgende Spalten: m_nr pr_n r 10102 p1 10102 p3 25348 p2 18316 p2
aufgabe
einst_dat
Projektleiter 01-oct-1988 Gruppenleiter 01-jan-1989 Sachbearbeiter 15-feb-1988 01-jun-1989
abt_nr a3 a3 a3 a1
Den Primärschlüssel dieser Tabelle bildet die Kombination der Spalten m_nr und pr_nr. Die Spalte abt_nr ist nicht voll funktional vom kompletten Primärschlüssel, sondern schon von einem Teil(m_nr) abhängig. Deswegen befindet sich die oben abgebildete Tabelle nicht in der zweiten Normalform. (Die Tabelle arbeiten der Beispieldatenbank befindet sich in der zweiten Normalform.) 1.3.4 Dritte Normalform Die dritte Normalform besagt, daß zwischen den Spalten einer Tabelle, die nicht den Primärschlüssel bilden, keine Abhängigkeiten existieren dürfen. Ausgegangen wird dabei immer von einer Tabelle, die sich bereits in der zweiten Normalform befindet. Nehmen wir an, die Tabelle mitarbeiter enthält eine zusätzliche Spalte mit dem Namen der Abteilung: m_nr 25348 10102 18316 29346
m_name Keller Huber Mueller Probst
m_vorname Hans Petra Gabriele Andreas
abt_nr a3 a3 a1 a2
abt_name Freigabe Freigabe Beratung Diagnose
Der Primärschlüssel dieser Tabelle ist die Spalte m_nr. Weil die Spalten abt_nr und abt_name voneinander abhängig sind und keine vo n beiden Teil des Primärschlüssels ist, befindet sich die oben abgebildete Tabelle nicht in der dritten Normalform. (Die Tabelle mitarbeiter, genauso wie alle anderen Tabellen der Beispieldatenbank, befindet sich in der dritten Normalform.) Datenbanken, die die dritte Normalform erfüllen, enthalten weitgehend nicht redundante Daten.
-25-
1.3.5 Vierte Normalform Die vierte Normalform beseitigt die mehrwertigen Abhängigkeiten in den Tabellen einer Datenbank. Als praktisches Beispiel betrachten wir die Tabelle verkauf, mit welcher der Verkauf diverser Artikel in verschiedenen Läden abgewickelt wird. art_nr laden_nr farbe art_1 laden_1 schwar z art_1 laden_1 weiß art_2 laden_1 rot art_2 laden_1 schwar z art_2 laden_2 rot art_2 laden_2 schwar z art_3 laden_2 weiß
Die Tabelle verkauf erfüllt die dritte Normalform, weil der einzige Primärschlüssel die Kombination aller drei Spalten art_nr, laden_nr und farbe ist. Trotzdem sind die Datenwerte dieser Tabelle redundant. Der Grund für die Redundanz liegt darin, daß jeder Artikel sowohl in mehreren Läden verkauft wird als auch mehrere möglichen Farben hat. Der Artikel art_2 z.B. wird in zwei Läden verkauft: laden_1 und laden_2 und in zwei verschiedenen Farben schwarz und rot. Deswegen existieren in der Tabelle verkauf mehrwertige Abhängigkeiten, die mit der Trennung in zwei Tabellen beseitigt werden können.
1.4
INGRES-Datenbanksprachen INGRES unterstützt zwei Datenbanksprachen - QUEL und SQL. QUEL ist die Sprache, die von der INGRES-Manschaft implementiert wurde und ursprünglich als Teil dieses Datenbanksystems gedacht war. Die Sprache SQL wurde erst später portiert, nachdem sich abgezeichnet hatte, daß diese Sprache die Standardsprache für relationale Datenbanksysteme wird. Obwohl viele Datenbankexperten glauben, daß QUEL technisch gesehen eine bessere Datenbanksprache als SQL ist, verliert QUEL immer mehr an Bedeutung. Deswegen werden wir in diesem Buch ausschließlich die INGRES-Schnittstelle zu SQL beschreiben und verwenden. In folgendem Abschnitt wird die Entwicklung der SQL-Sprache erläutert.
-26-
1.4.1 Die Datenbanksprache SQL SQL ist eine Datenbanksprache, die auf dem relationalen Datenmodell basiert. Der Name steht als Abkürzung für "Structured Query Language", d.h. strukturierte Abfragesprache. Die Entstehungsgeschichte von SQL ist eng mit dem Projekt "System R" bei IBM verbunden. "System R" sollte beweisen, daß ein relationales Datenbanksystem allen praktischen Anforderungen gerecht werden kann. Ein derartiges System soll also sehr leistungsfähig sein und alle Funktionen beinhalten, die für die alltägliche praktische Anwendung notwendig sind [CHA81]. Die Entwicklung von "System R" war in drei Phasen untergliedert. Das Ziel der ersten Phase ("Phase Null") war die schnelle Implementierung eines Prototyps, der nur einen Teil der vorgesehenen Funktionen beinhalten sollte. Als Datenbanksprache wurde in dieser Phase "SEQUEL" gewählt. Für diese Sprache wurde ein Interpreter in PL/I geschrieben, der ihre Anweisungen ausführen konnte. Im weiteren Verlauf dieser Phase wurde der Name der Sprache in "SQL" geändert. Trotz dieser Änderung wird SQL heute noch häufig als "Sequel" (sprich: "siekwel") ausgesprochen. Die implementierte Untermenge der SQL-Sprache bot die Möglichkeit, sowohl die Datenbank abzufragen und zu modifizieren als auch dynamische Änderungen des Datenbankdesigns durchzuführen. Zusätzlich wurden Unterabfragen implementiert. Damit war es möglich, die Suche in mehreren Tabellen durchzuführen; das endgültige Ergebnis konnte nur aus einer Tabelle entnommen werden. In dieser Phase wurde "System R" als Einplatzsystem implementiert, d.h. die Abfragen mittels SQL konnten nur von einem Bildschirm aus gestartet werden. Die schwerste Aufgabe in dieser Phase war die Arbeit an optimierenden Algorithmen. Das Ziel war, die Anzahl der Zugriffe auf Datenbanken bei Abfragen zu minimieren. Diese Phase dauerte zwei Jahre, von 1974 bis 1975, und hat in bezug auf SQL bewiesen, daß die Sprache in der Praxis einsetzbar war. Die zweite Phase von "System R", "Phase Eins", dauerte von 1976 bis Mitte 1977. Der Prototyp aus der ersten Phase wurde jedoch nicht weiterentwickelt, sondern eine ganz neue Version von "System R" konstruiert. Diese Version beinhaltete alle schon erwähnten Funktionen und war ein Mehrplatzsystem. Die wichtigsten Komponenten der zweiten Phase, in bezug auf SQL waren die Implementierung von Abfragen, die mehrere Tabellen verknüpfen, und das Subsystem für Datenschutz. Dieses Subsystem sicherte jedem Benutzer genau den Zugriff zu, der ihm vom Eigentümer des entsprechenden Objektes eingeräumt wurde. Zusätzlich dazu wurde SQL in zwei höhere Programmiersprachen, COBOL und PL/I, eingebettet. Das Ziel war, jedem -27-
Programmierer dieselben Möglichkeiten zu geben, ungeachtet dessen, ob er interaktive Abfragen oder die COBOL- bzw. PL/I-Schnittstelle benutzt. Das erklärte Ziel der zweiten Phase war, "System R" für die IBM-Betriebssysteme VM/CMS und MVS/TSO lauffähig zu machen. In der dritten Phase, von Juni 1977 bis Ende 1979, wurde "System R" intern bei IBM und bei drei ausgewählten Kunden getestet. Die SQL-Benutzerschnittstelle von "System R" wurde generell als einfach und mächtig bewertet. Die Sprache war so strukturiert, daß die Anwender sie in relativ kurzer Zeit erlernen konnten. Nach dem Erfolg von "System R" war klar, daß IBM marktreife Produkte auf der Basis von "System R" entwickeln würde, die auch SQL beinhalten sollten. Trotzdem war es ein anderes Produkt - ORACLE von Relational Software Inc. -, das als erstes relationales Datenbanksystem im Jahre 1980 auf den Markt kam. 1981 kam das erste relationale Datenbanksystem von IBM - SQL/DS heraus. Die erste Version von SQL/DS war für das Betriebssystem DOS/VSE freigegeben, die nächste (1982) für VM/CSM. Im Jahre 1983 gab IBM ein weiteres relationales System - DB2 - frei. DB2 war weitgehend kompatibel zu SQL/DS. In den folgenden Jahren gaben weitere Hersteller ihre relationalen Datenbanksysteme frei, wie SQL-Schnittstelle für INGRES (Relational Technology Inc., 1985) usw. Im Jahre 1982 gründete das American National Standards Institute (ANSI) ein Gremium, das einen Standard für relationale Datenbanksprachen entwerfen sollte. Im Laufe der Zeit wurde zum größten Teil der SQL-Dialekt von IBM als Standard übernommen und im Oktober 1986 verabschiedet [ANS86]. Nach der Verabschiedung des ersten SQL-Standards hat sich dasselbe Gremium dem Entwurf eines neuen Standards mit dem Namen SQL2 gewidmet. Bis heute sind einige vorläufige Entwürfe des SQL2-Standards veröffentlicht worden, von welchen der letzte im April 1991 erschienen ist. [ANS91]
1.5
Notation Für die Darstellung der Syntax aller in diesem Buch definierten SQL-Anweisungen wird eine einfache formale Sprache benutzt, die nachfolgend definiert wird: Notation Bedeutung SCHLÜSSELWORT Jedes Schlüsselwort der SQL-Sprache wird in Großbuchstaben angegeben. (Beispiel: CREATE TABLE). Zusätzliche Erklärungen zu Schlüsselwörtern können Sie in Kapitel 2 finden. variable Die Schreibweise mit Kleinbuchstaben bezeichnet eine Variable. Beispiel: CREATE -28-
var_1|var_2
[ ]
Voreinstellung
...
{ } ...
TABLE tabelle (für „tabelle“ muß ein aktueller Wert eingegeben werden). Alternative Darstellung: Einer der Ausdrücke, der durch einen senkrechten Strich von den anderen getrennt ist, ist auszuwählen, z.B. ALL|DISTINCT. Eckige Klammern bezeichnen optionale Werte. Werte innerhalb der eckigen Klammern dürfen also weggelassen werden. Unterstrichene Werte kennzeichnen die Voreinstellung. Dieser Wert wird also implizit angenommen, wenn explizit keine Alternative angegeben ist. Wiederholungszeichen: Der unmittelbar vorhergehende Ausdruck darf mehrmals wiederholt werden (getrennt durch ein oder mehrere Leerzeichen). Der Ausdruck, der innerhalb der geschweiften Klammern erscheint, darf mehrmals wiederholt werden (getrennt durch ein oder mehrere Leerzeichen).
Zusätzlich zur Notation werden alle Objekte der Beispieldatenbank im Text fettgedruckt dargestellt. Die Schlüsselwörter in Maskten bzw. Listenprogrammen werden fettgedruckt und mit Kleinbuchstaben geschrieben.
Aufgaben A.1.1 A.1.2 A.1.3
Was bedeutet Datenunabhängigkeit und welche Arten von Datenunabhängigkeit existieren? Welches ist das Grundelement einer relationalen Datenbank? Was stellt die Tabelle arbeiten in Bezug auf die anderen Tabellen dar?
-29-
SQL-Komponenten
SQL-Komponenten.........................................................................................................................30 2
SQL-Komponenten..................................................................................................................30 2.1 Grundelemente der SQL-Sprache 30 2.1.1 Literale 31 2.1.2 Begrenzer 31 2.1.3 Namen 32 2.1.4 Schlüsselwörter 32 2.2 Datentypen 32 2.2.1 Numerische Datentypen 32 2.2.2 Alphanumerischer Datentyp 33 2.2.3 Abstrakte Datentypen 34 2.3 Prädikate 35 2.4 Aggregatfunktionen 35 2.5 Skalare Funktionen 36 2.5.1 Datumsfunktionen 37 2.5.2 Zeichenkettenfunktionen 37 2.5.3 Spezielle Konstanten und die DBMSINFO-Systemfunktion 39 2.6 Arithmetische und Boolesche Operatoren 40 2.7 NULL-Werte 40 2.8 Klassifizierung der SQL-Anweisungen 41 Aufgaben. 42
SQL-Komponenten In diesem Kapitel werden gemeinsame Elemente aller SQL-Anweisungen beschrieben. Zuerst werden die Grundelemente von SQL erörtert, im Anschluß daran die SQL-Datentypen definiert. Zusätzlich dazu werden Aggregat- und skalare Funktionen, arithmetische und Boolesche Operatoren sowie Prädikate kurz dargestellt. Am Ende des Kapitels wird die Klassifizierung aller SQL-Anweisungen vorgenommen.
Grundelemente der SQL-Sprache SQL ist eine nichtprozedurale Sprache, die wie jede andere Programmiersprache folgende Grundelemente hat: - Literale, - Begrenzer, - Namen und - Schlüsselworte. -30-
Literale Ein Literal ist eine alphanumerische, hexadezimale oder numerische Konstante. Eine alphanumerische Konstante (auch Zeichenkette genannt) beinhaltet ein oder mehrere Zeichen des SQL-Zeichensatzes, die zwischen zwei Apostrophen stehen. Die hexadezimalen Konstanten dienen der Darstellung der nicht abdruckbaren Zeichen. Jede hexadezimale Konstante fängt mit dem Buchstaben "X" an, ge folgt von einer geraden Anzahl von Buchstaben oder Ziffern, die innerhalb von zwei Apostrophen stehen. Beispiel 2.1 Es folgen Beispiele für alphanumerische und hexadezimale Konstanten: 'Stuttgart' '1000 Berlin 30' '9876' 'Apostroph wird mit '' dargestellt' X'53514C0D' Beispiel 2.2 Bei folgenden Beispielen handelt es sich nicht um alphanumerische Konstanten: 'AB'C' (ungerade Anzahl von Apostrophen) 'Muenchen (Apostrophe sind nicht paarweise vorhanden) Zu numerischen Konstanten gehören alle Ganz-, Festpunkt- und Gleitpunktzahlen mit oder ohne Vorzeichen. Beispiel 2.3 Es folgen Beispiele für numerische Konstanten: 130 -130.00 -0.357E5 22.3E-3 Ein Literal besitzt immer einen Datentyp und eine Länge, die beide vom Format des Literals abhängig sind. Zusätzlich besitzt jede numerische Konstante eine Genauigkeits- und Skalierungsangabe. Begrenzer Mit Hilfe der Begrenzer werden die lexikalischen Einheiten (token) einer Programmiersprache voneinander getrennt. INGRES-SQL kennt folgende Begrenzer: , ( ) < > . : = + - * / <> <= >= Kommentare und alphanumerische Konstanten stellen auch Begrenzer dar. Ein Kommentar kann auf verschiedene Weise in einer INGRES-SQL-Anweisung dargestellt werden. Die Zeichenpaare "/*" und "*/" definieren den dazwischen befindlichen Text, der sich auch auf me hrere Zeilen erstrecken kann, als Kommentar. Ab INGRES Version 6 wird auch das Zeichenpaar "--" als Kommentarzeichen erkannt. Dabei wird dieses Zeichenpaar, zusammen mit dem Rest der Zeile, als Kommentar betrachtet. -31-
Namen Namen in SQL sind durch Zeichenketten dargestellt, die bis zu 24 alphanumerische Zeichen enthalten können. Jeder Name muß mit einem Buchstaben oder dem Zeichen "_ " beginnen. Die SQL-Namen bei INGRES dürfen nicht mit dem Zeichenpaar "ii" anfangen, weil solche Namen vom System reserviert sind. Schlüsselwörter Jede Programmiersprache hat eine Menge von Wörtern, die eine gewisse vorgegebene Bedeutung haben, und die in der jeweils vorgegebenen Form geschrieben werden müssen. Solche Wörter heißen Schlüsselwörter. In der SQL-Sprache gibt es eine Vielzahl solcher Wörter, die zusätzlich, wie in den meisten anderen Programmiersprachen, reserviert sind. Das bedeutet, daß solche Wörter als Objektnamen nicht benutzt werden dürfen. Verschiedene INGRES-Produkte enthalten unterschiedliche Schlüsselwörter. Aus diesem Grund werden wir auf die Auflistung der Schlüsselwörter verzichten. Die Liste aller Schlüsselwörter einzelner INGRES-Produkte kann in den entsprechenden Manualen gefunden werden.
Datentypen Alle Datenwerte einer Spalte müssen denselben Datentyp haben. INGRES-SQL kennt mehrere Datentypen, die in folgende Klassen unterteilt werden können: - numerische Datentypen; - alphanumerische Datentypen und - abstrakte Datentypen. Numerische Datentypen Numerische Datentypen ermöglichen die Darstellung von Zahlen. INGRES-SQL enthält folgende numerische Datentypen: - INTEGER1; - SMALLINT; - INTEGER; - FLOAT4 und - FLOAT. Der Datentyp INTEGER1 beschreibt ganzzahlige numerische Werte, die in einem Byte gespeichert werden können. SMALLINT beschreibt ebenfalls ganzzahlige numerische Werte, die in zwei Byte gespeichert werden können und deswegen zwischen -32768 und 32767 liegen. INTEGER2 ist die alternative Schreibweise für SMALLINT. Mit dem Datentyp INTEGER können die ganzzahligen numerischen -32-
Werte dargestellt werden, die in vier Byte gespeichert werden können und deswegen zwischen -2147483648 und 2147482647 liegen. Die ganzzahligen Werte, die außerhalb dieses Intervals liegen, werden intern in Gleitkommazahlen umgewandelt. INTEGER4 ist das Synonym für INTEGER. Der Datentyp FLOAT4 beschreibt Gleitkommazahlen, die in vier Byte gespeichert werden können. Diese Zahlen werden als positive oder negative Dezimalzahlen geschrieben, gefolgt von dem Buchstaben E (oder e) und einer ganzzahligen Konstante (zum Beispiel: -3.2E-08). Mit der in Kapitel 15 beschriebenen Umgebungsvariablen II_DECIMAL kann der Dezimalpunkt (in Komma, zum Beispiel) geändert werden. Der Datentyp FLOAT beschreibt die Gleitkommazahlen, die in acht Byte gespeichert werden können. Die alternative Schreibweise für diesen Datentyp ist FLOAT8. Sowohl mit dem Datentyp FLOAT4 als auch mit FLOAT können die Gleitkommazahlen dargestellt werden, die zwischen -10**38 und +10**38 liegen. Alphanumerischer Datentyp Der alphanumerische Datentyp ermöglicht die Darstellung von Zeichenketten. INGRES unterstützt Zeichenketten fester und variabler Länge. Die alphanumerischen Datentypen fester Länge sind - CHAR und -C während - VARCHAR und - TEXT alphanumerische Datentypen variabler Länge darstellen. Die Zeichenketten des Datentyps CHAR können alle darstellbaren und nicht darstellbaren Zeichen enthalten. Der Datentyp C wird nur noch aus Kompatibilitätsgründen zu den früheren INGRES-Versionen unterstützt. Die Zeichenketten dieses Datentyps können nur darstellbare Zeichen enthalten. Die nicht darstellbaren Zeichen werden beim Datentyp C in Leerzeichen umgewandelt. VARCHAR kennzeichnet eine Zeichenkette variabler Länge, die sowohl darstellbare als auch nicht darstellbare Zeichen sowie NULL-Zeichen enthalten kann. Der Datentyp TEXT wird nur noch aus Kompatibilitätsgründen zu den früheren INGRES-Versionen unterstützt. Die Zeichenketten dieses Datentyps können nur darstellbare und NULL-Zeichen enthalten. Der Datentyp VARCHAR unterscheidet sich aus Benutzersicht nicht vom Typ CHAR. Der einzige Unterschied besteht in der physikalischen Darstellung. Falls der Inhalt einer CHAR(n) Zeichenkette kürzer als n Zeichen ist, wird der Rest der Zeichenkette mit Leerzeichen aufgefüllt. Die Zeichenkette vom Typ VARCHAR wird hingegen genau in ihrer tatsächlichen Länge gespeichert. (Das gilt nur für die Tabellen, die komprimierte -33-
Datenwerte enthalten.) Abstrakte Datentypen INGRES unterstützt vier abstrakte Datentypen - MONEY, - DATE, - OBJECT_KEY und - TABLE_KEY. Der Datentyp MONEY wird für die Darstellung von Geldbeträgen verwendet. Deswegen werden die Werte vom Typ MONEY standardmäßig als Dezimalzahlen mit genau zwei Stellen hinter dem Komma dargestellt. INGRES gibt die Werte vom Typ MONEY als eine Zeichenkette der Länge 20 aus. Mit der Umgebungsvariablen II_MONEY_FORMAT kann das Währungszeichen (standardmäßig "$") und mit II_DECIMAL das Symbol für den Dezimalpunkt (standardmäßig ".") geändert werden. Die Ausgabegenauigkeit eines Geldbetrages kann mit der Umgebungsvariablen II_MONEY_PREC modifiziert werden. (Alle Umgebungsvariablen sind in Kapitel 15 beschrieben.) DATE stellt in numerischem Format entweder eine Datums- und eine Zeitangabe oder ein Zeitinterval dar. Ein Datenwert vom Typ DATE wird intern als numerischer Wert, der die Anzahl der Sekunden ab einem im voraus festgelegten Zeitpunkt darstellt, gespeichert. INGRES unterstützt u.a. folgende Ausgabeformate für die Werte vom Typ DATE: dd-mmm-yyyy und dd-mmm-yyyy hh:mm:ss wobei dd für Tag, mmm für Monat, yyyy für Jahr, hh für Stunde, mm für Minute und ss für Sekunde steht. Beispiel 2.4 28-may-1961 (das erste Format) 28-may-1961 07:58:00 (das zweite Format). Genauso ist es bei INGRES möglich, ein Zeitinterval anzugeben. Dabei wird, falls notwendig, der signifikante Teil des Intervals in der Länge von 25 Zeichen ausgegeben. Das ganze Format eines Zeitintervals sieht folgendermaßen aus: yy yrs mm mon dd days hh hrs mm mins ss secs Beispiel 2.5 Für das Zeitinterval von zehn Jahren, neun Monaten, acht Tagen, sieben Stunden, sechs Minuten und 55 Sekunden, das folgendermaßen bei INGRES dargestellt wird 10 yrs 9 mon 8 days 7 hrs 6 min 55 secs wird 10 yrs 9 mon 8 days 7 hrs ausgegeben. Die Eingabe der Werte vom Typ DATE hängt von der Umgebungsvariablen II_DATE_FORMAT ab. Diese Umgebungsvariable ist in Kapitel 15 beschrieben. OBJECT_KEY und TABLE_KEY sind zwei spezifische Datentypen des -34-
INGRES-Systems, die jeweils eine zusätzliche Spalte in einer INGRES-Tabelle bilden. Die Werte der TABLE_KEY-Spalte sind als CHAR(8), die Werte der OBJECT_KEY-Spalte als CHAR(16) definiert. Sowohl die Spalte vom Datentyp OBJECT_KEY als auch die Spalte vom Typ TABLE_KEY können entweder vom INGRES-System (durch die Angabe SYSTEM_MAINTAINED) oder vom Benutzer (durch die Angabe NOT SYSTEM_MAINTAINED) verwaltet werden. Falls beide Spalten vom System verwaltet werden, werden sie als interne Spalten des INGRES-Systems betrachtet, die vom Benutzer nicht verändert werden können und die garantieren, daß jede Reihe einer Tabelle intern eindeutig gekennzeichnet ist. Der Unterschied zwischen diesen beiden Datentypen liegt darin, daß die Werte der TABLE_KEY-Spalte alle Reihen innerhalb der Tabelle, der sie angehören eindeutig kennzeichnen, während die Werte der OBJECT_KEY-Spalte alle Reihen innerhalb des ganzen INGRES-Systems eindeutig identifizieren. Falls der Benutzer sich entscheidet, durch die Angabe NOT SYSTEM_MAINTAINED die Spalten mit den Datentypen OBJECT_KEY bzw. TABLE_KEY selbst zu verwalten, muß er jeder Reihe die entsprechenden Datenwerte zuweisen. Die Quelle dieser Zuweisung können Werte der beiden Spalten einer anderen Tabelle oder Zeichenketten sein.
Prädikate Ein Prädikat kennzeichnet eine logische Bedingung, die auf Reihen einer Tabelle angewendet wird. Die herkömmlichen logischen Bedingungen mit zwei Werten ("richtig", "falsch") sind bei SQL um einen dritten Wert ("unbekannt") erweitert worden. SQL unterstützt folgende Prädikate: - alle Vergleichsoperatoren; - BETWEEN-Operator; - IN-Operator; - LIKE-Operator; - NULL-Operator; - ALL- und ANY-Operator und - EXISTS-Operator. All diese Prädikate werden ausführlich in Kapitel 7 beschrieben.
Aggregatfunktionen Die Aggregatfunktionen werden auf eine Gruppe von Datenwerten aus einer Spalte angewendet. Das Ergebnis jeder Aggregatfunktion ist immer ein einziger Wert. Es gibt fünf Aggregatfunktionen, die von INGRES-SQL unterstützt werden: - AVG; - MAX; - MIN; -35-
- SUM und - COUNT. Funktion AVG
MAX
SUM COUNT
Bedeutung (Kürzung für "average") berechnet das arithmetische Mittel der Datenwerte in einer Spalte. Die Spalte muß numerische Werte beinhalten. berechnet den größten und MIN den kleinsten Datenwert einer Spalte. Die Spalte kann numerische oder alphanumerische Werte beinhalten. berechnet die Summe aller Datenwerte in einer Spalte. Die Spalte muß numerische Werte beinhalten. berechnet die Anzahl der Datenwerte innerhalb einer Spalte. Die einzige Aggregatfunktion, die nicht auf Spalten angewendet wird, ist COUNT(*). Diese Funktion berechnet die Anzahl der Reihen.
Skalare Funktionen Zusätzlich zu den Aggregatfunktionen existieren skalare Funktionen, mit denen auch Ausdrücke gebildet werden können. Alle skalaren Funktionen können in vier Gruppen unterteilt werden. - numerische Funktionen; - Datumsfunktionen; - Zeichenkettenfunktionen und - Systemfunktionen. Numerische Funktionen bei INGRES sind diverse mathematische Funktionen, die der Manipulation numerischer Werte dienen. Zu den numerischen Funktionen gehören: - ABS; - ATAN; - COS; - LOG; - MOD; - SIN und - SQRT. Funktion ABS(n) ATAN(n) COS(n) LOG(n) MOD(n,b) SIN(n)
Bedeutung berechnet den absoluten Wert von n. berechnet den Arkustangens von n. Der Ausgabewert ist vom Datentyp FLOAT. berechnet den Kosinus von n. Der Ausgabewert ist vom Typ FLOAT. berechnet den natürlichen Logarithmus von n. berechnet den Wert n Modulo b. n und b müssen vom Typ INTEGER sein. berechnet den Sinus von n. Der Ausgabewert ist vom Typ FLOAT. -36-
SQRT(n)
berechnet die Quadratwurzel von n.
Datumsfunktionen Datumsfunktionen berechnen entweder aus einem Ausdruck die entsprechende Datums- bzw. Zeitangabe oder sie liefern den Wert aus einem Zeitinterval. Alle Datumsfunktionen verwenden folgende Datums- bzw. Zeiteinheiten: - second (seconds, sec oder secs) für die Sekunde; - minute (minutes, min oder mins) für die Minute; - day (days) für den Tag; - week (weeks, wk oder wks) für die Woche; - month (months, mo oder mos) für den Monat; - quarter (quarters, qtr oder qtrs) für das Vierteljahr; - year (years, yr oder yrs) für das Jahr; INGRES unterstützt folgende drei Datumsfunktionen: - DATE_TRUNC; - DATE_PART und - INTERVAL. Funktion DATE_TRUNC (einheit,datum)
Ergebnis liefert als Ergebnis den bis auf die Angabe einheit abgerundeten Wert von datum. Beispiel: DATE_TRUNC('month',date('13- may-1991'))
liefert als Ergebnis '01- may-1991'. DATE_PART (einheit,datum) liefert als Ergebnis die Ganzzahl, die jenen Teil von datum darstellt, der durch einheit vorgegeben ist. Beispiel: DATE_PART('day',date('13-may1991'))=13 INTERVAL (einheit,datum) wandelt ein Zeitinterval in eine Gleitkommakonstante, in die von dem Benutzer durch einheit vorgegebene Form um.
Zeichenkettenfunktionen Zeichenkettenfunktionen werden zur Manipulation der Datenwerte einer Spalte vom Typ CHAR, VARCHAR, C oder TEXT angewendet. INGRES unterstützt folgende Zeichenkettenfunktionen: - LEFT; - LENGTH; - LOCATE; - LOWERCASE; - PAD; -37-
- RIGHT; - SHIFT; - SIZE; - SQUEEZE; - TRIM und - UPPERCASE. Funktion LEFT (z1,länge)
Ergebnis liefert als Ergebnis die linke Teilzeichenkette der Zeichenkette z1 in der Länge länge. Beispiel: LEFT('format',4) = 'form' LENGTH (z1) liefert als Ergebnis die tatsächliche Länge der Zeichenkette z1 (ohne anschließende Leerzeichen). Beispiel: LENGTH('Tabelle') = 7 LOCATE(z1,z2) liefert als Ergebnis die Anfangsstelle der Teilzeichenkette z2 innerhalb der Zeichenkette z1. Falls z2 in z1 nicht enthalten ist, wird der Wert LENGTH(z1) + 1 geliefert. Beispiel: LOCATE('Panama','ana') = 2 LOWERCASE(z1) wandelt alle Großbuchstaben der Zeichenkette z1 in Kleinbuchstaben um. Die Kleinbuchstaben und Ziffern bleiben unverändert. Beispiel: LOWERCASE('ABC123abc') = 'abc123abc' PAD(z1) liefert als Ergebnis die Zeichenkette z1 mit allen anschließenden Leerzeichen. Diese Funktion kann nur mit Werten vom Typ VARCHAR bzw. TEXT sinnvoll verwendet werden. RIGHT(z,länge) liefert als Ergebnis die rechte Teilzeichenkette der Zeichenkette z in der Länge länge. Beispiel: RIGHT('format',3) = 'mat' SHIFT(z1,zahl) verschiebt die Zeichenkette z1 um zahlStellen nach rechts, falls zahl>0 bzw. nach links, falls zahl<0. Bei Zeichenketten fester Länge wird das Ergebnis mit Leerzeichen aufgefüllt. Falls z1 variabler Länge ist, findet das Auffüllen mit Leerzeichen nicht statt. Beispiel: SHIFT('Otto ',4)='Ott' SIZE(z1) liefert die deklarierte Größe der Ganzzahl z1, die vom Datentyp SMALLINT sein muß. SQUEEZE(z1) komprimiert jede Sequenz von Leer-, NULL-, NL("new line")- FF("form feed")-, RETURN- und Tabulatorzeichen. TRIM(z1) liefert als Ergebnis eine Zeichenkette vom Typ VARCHAR bzw. TEXT (ohne anschließende Leerzeichen). UPPERCASE(z1) wandelt alle Kleinbuchstaben der Zeichenkette z1 in Großbuchstaben um. Die Großbuchstaben und Ziffern bleiben unverändert. Beispiel: UPPERCASE('ABC123abc') = 'ABC123ABC' -38-
Alle Zeichenkettenfunktionen können untereinander beliebig geschachtelt werden. Spezielle Konstanten und die DBMSINFO-Systemfunktion INGRES unterstützt drei spezielle Konstanten - TODAY, - NOW und - USER . Der Wert jeder Konstante wird der entsprechenden Variablen oder Tabellenspalte zugewiesen. Konstante TODAY NOW USER
Ergebnis liefert das aktuelle Datum. liefert das aktuelle Datum, zusammen mit der aktuellen Zeitangabe. liefert den Namen des aktuellen Benutzers.
Die DBMSINFO-Funktion liefert die in Bezug auf den angegebenen Parameter existierende Information aus einer Datenbank. Die wichtigsten Werte des Parameters sind: Parameter DATABASE DBA _VERSION TERMINAL _BINTIM
_CPU_MS _ET_SEC
Ergebnis gibt den Datenbanknamen aus. gibt den Namen des Datenbankeigentümers (DBA) aus. gibt die aktuelle INGRESVersionsnummer aus. gibt die Bildschirmadresse aus. gibt die aktuelle Zeit und das aktuelle Datum als Anzahl von Sekunden seit 1.1.1970 aus. gibt die CPU-Zeit der INGRES-Sitzung aus. gibt die verbrauchte Zeit aus.
TRANSACTION_STATE '1' bedeutet, daß eine Transaktion gestartet wurde, während '0' den Zustand außerhalb einer Transaktion anzeigt. AUTOCOMMIT_STATE '1' bedeutet AUTOCOMMIT ON, während '0' AUTOCOMMIT OFF anzeigt. (Für die Beschreibung der AUTOCOMMIT-Anweisung siehe Kapitel 13.) Falls Ihr INGRES-System auch die optionale Knowledge Management-Komponente enthält (siehe Kapitel 20), kann der DBMSINFO-Parameter auch folgende Werte haben: -39-
Parameter GROUP ROLE QUERY_IO_LIMIT
QUERY_ROW_LIMIT
Ergebnis gibt den Namen der Benutzergruppe bzw. Leerzeichen aus. gibt den Namen der benutzerdefinierten Regel bzw. Leerzeichen aus. gibt den Wert für die maximal erlaubte Anzahl von E/A-Operationen für eine Abfrage aus. gibt den Wert für die maximal erlaubte Anzahl von ausgewählten Reihen einer Abfrage aus.
Arithmetische und Boolesche Operatoren INGRES-SQL beinhaltet unäre und binäre arithmetische Operatoren. Unäre Operatoren sind + (als Vorzeichen) (als Vorzeichen) Binäre Operatoren sind + (Addition) (Subtraktion) * (Multiplikation) / (Division) INGRES unterstützt auch den Verkettungsoperator + . Beispiel 2.6 Die Verkettung der Zeichen 'Daten' + 'bank' + 'anwendung' liefert als Ergebnis 'Datenbankanwendung' . INGRES-SQL ermö glicht auch die Benutzung folgender Boolescher Operatoren - NOT - AND - OR Auf die arithmetischen Operatoren wird an vielen Stellen des Buches Bezug ge nommen, während die Booleschen Operatoren hauptsächlich in Kapitel 7 und 8 behandelt werden.
NULL-Werte Ein NULL-Wert ist ein spezieller Wert, der für eine Spalte zugelassen werden kann. Dieser Wert wird vorwiegend dann verwendet, wenn die Information für einen Datenwert der Spalte fehlt oder nicht bekannt ist. Er kann in einer Tabelle mit Personaldaten vorkommen, z.B. wenn eine Person nicht über einen Telefonanschluß verfügt. In diesem Fall ist es sinnvoll, diese Spalte mit dem NULL-Wert zu besetzen. In einem arithmetischen Ausdruck liefert ein NULL-Wert wieder -40-
den Wert NULL. Also haben bei den unären, arithmetischen Ausdrücken, falls A ein Ausdruck mit Wert NULL ist, auch +A und -A den Wert NULL. In den binären Ausdrücken haben, falls wenigstens einer der Operanden A oder B den NULL-Wert hat, auch A + B A - B A * B und A / B den Wert NULL. (A und B müssen numerische Aussdrücke sein.) Falls ein Ausdruck eine Vergleichsoperation beinhaltet und einer der Operanden (oder beide) den Wert NULL hat, ist das Ergebnis dieser Operation "unbekannt". Jeder der Ausdrücke A = B A <> B A < B A > B hat also als Ergebnis "unbekannt", falls A oder B (oder beide) den NULL-Wert haben. In den Booleschen Funktionen Konjunktion, Disjunktion und Negation, die jeweils mit den Symbolen AND, OR und NOT dargestellt sind, wird das Verhalten der NULL-Werte mit Hilfe von Wahrheitstabellen dargestellt: AND R ? F OR R ? F NOT R R ? F R R R R R F ? ? ? F ? R ? ? ? ? F F F F F R ? F F R wobei R für richtige, F für falsche logische Aussage und ? für "unbekannt" steht. Bei den Aggregatfunktionen AVG, SUM, MAX und MIN werden zunächst alle NULL-Werte aus der Spalte entfernt; erst dann wird das Ergebnis der jeweiligen Funktion berechnet. Beinhaltet die Spalte ausschließlich NULL-Werte, liefert die Funktion den Wert NULL. Die Aggregatfunktion COUNT(*) zählt alle Reihen, einschließlich derer mit den NULL-Werten. Bei COUNT DISTINCT werden zunächst alle NULL-Werte aus der Spalte entfernt; erst dann wird das Ergebnis berechnet. Falls die Spalte nur NULL-Werte beinhaltet, ist das Ergebnis 0. Für die Darstellung eines NULL-Wertes gilt, daß er sich von allen anderen Werten unterscheiden muß. Für die numerischen Datentypen unterscheidet das System also zwischen dem Wert 0 einerseits und den NULL-Wert andererseits. Dasselbe gilt für die leere Zeichenkette und den NULL-Wert bei alphanumerischen Datenwerten.
Klassifizierung der SQL-Anweisungen Wie jede andere Programmiersprache besteht SQL aus einer Vielzahl von Anweisungen, mit welchen der Benutzer unterschiedliche Operationen durchführen kann. Alle SQL-Anweisungen lassen sich auf Grund ihrer Funktionen in zwei verschiedene Gruppen unterteilen: - Datendefinition (Data Definition Language - DDL) - Datenmanipulation (Data Manipulation Language - DML) Die Anweisungen für die Datendefinition bestimmen die -41-
Struktur einer Datenbank. Mit diesen Anweisungen werden alle Objekte (Tabellen, Indexe usw.) erzeugt, geändert und gelöscht. In Kapitel 6 dieses Buches werden diese Anweisungen beschrieben. Sobald die Objekte einer Datenbank mittels Datendefinitonsanweisungen definiert sind, kann die Datenbank bearbeitet werden. Mit den Datenmanipulationsanweisungen werden Datenwerte eingefügt, geändert, gelöscht und abgefragt. Diese Gruppe von Anweisungen wird in mehreren Kapiteln beschrieben. In den Kapiteln 7 und 8 wird die Anweisung zur Abfrage der Datenwerte (SELECT) betrachtet. In Kapitel 9 werden die Anweisungen zum Einfügen (INSERT), Ändern (UPDATE) und Löschen (DELETE) von Datenwerten behandelt. Zusätzlich zu Datendefinitionsund Datenmanipulationsanweisungen existieren in SQL noch die Anweisungen für den Datenschutz und die Datensicherheit. Die Anweisungen für den Datenschutz bestimmen den Schutz der Datenwerte einer Datenbank durch die Gewährung bzw. Einschränkung von Zugriffsrechten. Der Datenschutz wird ausführlich in Kapitel 14 behandelt. Die Anweisungen zur Datensicherheit dienen der Erhaltung der Konsistenz einer Datenbank. Sie beziehe n sich sowohl auf Transaktionen, als auch auf das Sperren von Tabellen oder Reihen. Die Transaktionen sowie alle Anweisungen zur Datensicherheit werden in Kapitel 13 behandelt.
Aufgaben. A.2.1 A.2.2 A.2.3
Welche Konstanten werden innerhalb von Apostrophen geschrieben? Was ist der wichtigste Unterschied zwischen den Datentypen CHAR und VARCHAR? Welches Ergebnis liefern folgende Ausdrücke: a) A + NULL b) NULL = NULL c) B OR NULL d) B AND NULL , wobei A einen numerischen, NULL einen NULL-Wert und B einen logischen Ausdruck darstellt.
-42-
INGRES-frames
INGRES-frames .............................................................................................................................43 3
INGRES-frames.......................................................................................................................43 3.1 INGRES/MENU 43 3.2 Erstellung von Tabellen mit Hilfe von INGRES/MENU 45 Aufgaben 48
2 INGRES-frames In diesem Kapitel werden wir den Aufbau und die Bedienung der INGRES-frames erläutern. Zuerst wird der Zusammenhang zwischen dem Menüsystem und den entsprechenden Masken in einem frame erklärt, und einzelne Funktionen werden definiert. Danach wird die Erstellung der Tabellen der Beispieldatenbank gezeigt.
2.1
INGRES/MENU INGRES verwendet frames, eine Kombination aus Masken und Menüs, um den Benutzern die Möglichkeit zu geben, auf eine einfache Weise unterschiedliche INGRES-Subsysteme aufzurufen bzw. angebotene Funktionen auszuführen. Mit Hilfe der Masken können die Daten in einer benutzerfreundlichen Form ausbzw. eingegeben werden. Die Aufgabe der Menüs ist, der aktuell erscheinenden Maske alle dazugehörenden Funktionen anzubieten. Die kombinierte Darstellung der Masken und der entsprechenden Menüfunktionen ermöglicht dem Benutzer, auf eine leicht erlernbare Weise den Umgang mit dem INGRES-System auszuüben. Das Hauptmenü von INGRES wird mit dem Betriebssystemkommando ingmenu [db_name] aufgerufen, wobei db_name der Name der Datenbank ist. (Die Erstellung einer Datenbank wird mit dem Kommando createdb ausgeführt, das in Kapitel 15 beschrieben ist.) Danach erscheint das in Abbildung 3-1 dargestellte frame,
INGRES/MENU Tables
Create, update or lookup tables in the database -43-
Forms JoinDefs Reports Graphs Applications Queries
Tables
Use Query-By-Forms or the Visual-Forms-Editor Use Query-By-Forms to design/test Join Definitions Use Report-By-Forms to design/test/run INGRESReport Use Vigraph to design/test/plot INGRES Graphs Use Application-By-Forms to design/test application Enter Interactive SQL or QUEL Statements
Forms
JoinDefs
Reports
Graphs
Application
Queries
Abb. 3-1 INGRES/MENU-frame Funktion Tables
Forms
JoinDef
Reports
Graphs
Applications
Queries
Bedeutung ermöglicht die Erstellung neuer Tabellen bzw. das Abfragen von Informationen über die schon existierenden Tabellen. ermöglicht die Erstellung neuer und die Anzeige schon existierender Masken. Eine weitere von Forms angebotene Funktion ist es, die schon existierenden Masken für die Datenabfrage bzw. Datenmodifikation zu verwenden. INGRES/Forms ist in Kapitel 4 ausführlich beschrieben. gibt dem Benutzer die Möglichkeit, die Verknüpfungen zwischen verschiedenen Tabellen einer Datenbank zu erstellen, anzuzeigen und zu verwenden. Die Tabellenverknüpfung mit dem Join-Operator ist in Kapitel 8 beschrieben, während die Verwendung der Menüfunktion JoinDef in Kapitel 4 erörtert wird. ermöglicht die Erstellung, die Anzeige und die Ausführung von Listenprogrammen. Mit den Listenprogrammen wird die Ausgabe der ausgewählten Datenwerte einer Datenbank durchgeführt. Die Funktion Reports ist zusammen mit dem Dienstprogramm Report Writer" in Kapitel 5 beschrieben. gibt dem Benutzer die Möglichkeit, die Datenwerte in Form von Grafiken auszugeben. Die Menüfunktion Graphs und das dazugehörende Dienstprogramm VIGRAPH ("VIsual GRAPHics Editor") werden in Kapitel 5 erörtert. Führt den Benutzer zu dem Dienstprogramm ABF ("Applicatio n-By-Forms"), mit dem die benutzerspezifischen Anwendungen mit Hilfe einer nichtprozeduralen Sprache erstellt werden können. ermöglicht die Abfrage der Daten. Zu diesem Zweck stehen dem Benutzer die beiden INGRESDatenbanksprachen SQL und QUEL sowie das Dienstprogramm QBF zur Verfügung.
Die Funktionsauswahl in INGRES/MENU erfolgt durch das -44-
Setzen der Schreibmarke auf die gewünschte Funktion und der anschließenden Auswahl der Funktion "Go" aus der Menüleiste. Die alternative Möglichkeit ist, in das Menü zu wechseln und nachfolgend den entsprechenden Funktionsnamen aus der Menüleiste zu wählen. (INGRES verlangt nur das Eintippen des signifikanten Anfangsteils eines Funktionsnamens.) Sowohl der Wechsel aus der Maske in das Menü (oder umgekehrt) als auch die Auswahl einer Menüfunktion wird bei INGRES durch Funktionstasten unterstützt. Genauso wird eine gewisse Funktionstaste sowohl für die Beendigung des INGRES/MENU als auch für den Wechsel aus einem Untermenü in das nächsthöhere verwendet. Verschiedene INGRES-Portierungen verwenden unterschiedliche Funktionstasten für die Ausführung der Menüfunktionen. Die spezifische Verwendung der Funktionstasten auf Ihrem System finden Sie in den entsprechenden Manualen. Das Zeichen ">" in der Menüleiste rechts deutet darauf hin, daß die Menüleiste weitere Funktionen enthält. Mit Hilfe der entsprechenden Funktionstaste kann zwischen der ersten und der zweiten Menüleistenhälfte hin und her gewechselt werden. Das Zeichen ":" kennzeichnet das tatsächliche Ende einer Menüleiste.
2.2
Erstellung von Tabellen mit Hilfe von INGRES/MENU Jede Datenbank muß eine oder mehrere Tabellen enthalten, damit sie überhaupt praktisch benutzt werden kann. Die Erstellung einer Tabelle kann von einem nicht erfahrenen INGRES-Benutzer am leichtesten mit Hilfe von INGRES/MENU durchgeführt werden. Am Beispiel der Tabelle arbeiten der Beispieldatenbank werden wir in diesem Abschnitt die Erstellung einer Tabelle mit Hilfe des Menüsystems zeigen. Die anderen drei Tabellen der Beispieldatenbank können dementsprechend erstellt werden. Zur Erstellung einer Tabelle muß in INGRES/MENU zuerst die Funktion Tables entweder in der Maske oder im Menü ausgewählt werden. Danach erscheint das in Abbildung 3-2 dargestellte frame Tables Catalog mit dem dazugehörigen Menü:
TABLES - Tables Catalog Name
Owner
Type
Place cursor on row and select desired operation from menu.
-45-
Create
Destroy Examine Query Report Top Bottom Abb. 3-2 Tables Catalog-frame Für die Beispieldatenbank ist noch keine Tabelle erstellt worden; deswegen ist die Maske in Abb. 3.2 leer. Durch die Auswahl der Menüfunktion Create erscheint das INGRES- frame Create a Table, das in Abbildung 3-3 dargestellt ist.
TABLES - Create a Table Enter the name of the new table: arbeit en Enter the column specifications of the new table: Column Name m_nr pr_nr aufgabe
Data Type float8 char(4) char(15 ) date
einst_dat
Insert
Delete
Blank
Move
Ke y#
Null s no no yes
Default s no no n/a
yes
n/a
GetTableDef
Save
Find
Top
Abb. 3-3 Create a Table-frame Der Name der neuzuerstellenden Tabelle (arbeiten) muß zuerst eingetragen werden. Danach werden für jede der vier Spalten (m_nr, pr_nr, aufgabe und einst_dat) folgende Spalteneigenschaften definiert: Eigenschaft Column Name Data Type Key #
Nulls
Bedeutung Der Name der Spalte. Der Datentyp der Spalte. Die optionale Angabe für einen Sortierbegriff. Die Spalte mit der Angabe 1 wird als Sortierbegriff verwendet; d.h. alle Reihen einer Tabelle werden aufsteigend auf Grund dieser Spalte sortiert. Falls mehrere Spalten den Sortierbegriff bilden, enthält "Key #" des ersten Sortierkriteriums die Ziffer 1, des zweiten die Ziffer 2 usw. definiert, ob die entsprechende Spalte NULL-Werte enthalten darf oder nicht. Falls "y" angegeben wird, wird jedem Datenwert, dem kein expliziter Wert zugewiesen ist, implizit der NULL-Wert zugewiesen. Bei der Angabe "n" wird in solchem Fall (falls erlaubt) jeder Spalte mit einem numerischen Datentyp der Wert 0 bzw. jeder Spalte mit einem -46-
Defaults
alphanumerischen Datentyp das Leerzeichen zugewiesen (siehe auch die Erklärung zu der Angabe Defaults). steht in Verbindung mit dem NULL-Eintrag und steuert die Wertzuweisung einer Spalte, die NULLWerte nicht erlaubt. Bei der Angabe "y" wird 0 für numerische bzw. das Leerzeichen für alphanumerische Spalten zugewiesen. Die Angabe "n" verlangt vom Benutzer einen Wert einzugeben. (Die dritte Angabe "n/a" wird vom System standardmäßig für die Spalten mit den NULL-Werten eingetragen.)
Der Eintrag für jede Spezifikation wird bei den meisten Umgebungen mit der RETURN-Taste abgeschlossen. Danach springt die Schreibmarke automatisch zu dem nächsten Spezifikationsfeld. Mit der Menüfunktion "Save " kann die Information über die erstellte Tabelle gesichert werden. Zusätzlich zu der Funktion Create im Menü des Tables Catalog-frame existieren auch folgende Funktionen: Funktion Destroy
Examine Query Report Top Bottom End
Bedeutung löscht eine existierende Tabelle, auf die die Schreibmarke zeigt. Das Löschen einer Tabelle bedeutet, daß sie aus dem Systemkatalog entfernt wird und alle ihre Reihen gelöscht werden. gibt die gesamte Information über eine Tabelle aus. ermöglicht die Datenabfrage bzw. die Datenmodifikation der ausgewählten Tabelle. erstellt ein Standard-Listenprogramm für die ausgewählte Tabelle. bewegt die Schreibmarke zu der ersten Zeile in der Maske. bewegt die Schreibmarke zu der letzten Zeile in der Maske. beendet die Funktion Create a Table.
Im Unterschied zu den anderen oben genannten Funktionen erscheinen die Funktionen Top, Bottom und End auch in den anderen Menüs, wo sie eine analoge Bedeutung haben. Die Information über die schon erstellten Tabellen kann durch die Auswahl der Funktion Examine ausgegeben werden. Die Abbildung 3-4 zeigt die Information über die Tabelle arbeiten. TABLES - Examine a Table Information on table abteilung Owner: ingres Row Width: 40 Row: 3 Columns: 3
Table Type: user table Storage Structure: heap Pages/Overflow: 1/0 Journaling: disabled -47-
Column Name abt_nr abt_name Stadt
Data Type char(4) char(20) char(15)
Key #
Nulls no no yes
Defaults no no n/a
NewTable Find Top Bottom Help End Abb. 3-4 Examine a Table-frame Das Examine a Table-frame hat eine ähnliche Form wie Create a Table-frame. Im Unterschied zu Create a Table-frame beinhaltet Examine a Table noch folgende Informationen: Angabe Owner Row Width Rows Columns Table Type Storage Structure Pages/Overflow Journaling
Bedeutung gibt den Tabelleneigentümer aus. gibt die Länge aller Tabellenspalten in Bytes aus. gibt die Anzahl der Reihen der Tabelle aus. gibt die Spaltenanzahl aus. gibt den Tabellentyp (Benut zertabelle, View usw.) aus. gibt die Speicherstruktur der Tabelle aus. gibt die Anzahl der physikalischen Seiten bzw. der Überlaufseiten, die die Tabelle belegt, aus. gibt aus, ob eine Journal-Datei für die Tabelle angelegt wird.
Aufgaben A.3.1
A.3.2 A.3.3
Erstellen Sie die übrigen drei Benutzertabellen der Beispieldatenbank (projekt, mitarbeiter und abteilung) mit Hilfe des INGRES/MENU. Mit Hilfe des INGRES/MENU holen Sie die Information über die Spalten der Tabelle mitarbeiter. Erstellen Sie eine neue Datenbank namens software, die die Tabelle system mit folgenden Spalten enthält: sys_name - alphanumerisch, max. 15 Zeichen version - Ganzzahl hersteller - alphanumerisch, max. 20 Zeichen ort - alphanumerisch, max. 20 Zeichen
-48-
INGRES/Forms, JoinDefs und INGRES/QBF
INGRES/Forms, JoinDefs und INGRES/QBF ...............................................................................49 4
INGRES/Forms, JoinDefs und INGRES/QBF ........................................................................49 4.1 Einfache Masken 49 4.2 JoinDefs 53 4.3 Komplexe Masken 58 Aufgabe. 59
3 INGRES/Forms, JoinDefs und INGRES/QBF Dieses Kapitel beschreibt die Erstellung und den Ablauf von Masken. Zuerst wird die Generierung einfacher Maskenspezifikationen mit Hilfe von INGRES/Forms erläutert. Nach der Erklärung des Begriffs JoinDefinition und der Erstellung mehrerer JoinDefs wird die Generierung komplexer Maskenspezifikationen beschrieben. Sowohl für die einfachen als auch für die komplexen Maskenspezifikationen werden die Operationen wie das Suchen, Ändern, Einfügen und Löschen von Daten mit Hilfe von INGRES/QBF gezeigt.
3.1
Einfache Masken Masken bieten dem Benutzer die Möglichkeit, auf einfache Weise das Suchen, Ändern und Löschen existierender Datenwerte in einer Datenbank durchzuführen. Zusätzlich dazu ist es möglich, mit Hilfe von Masken Datenwerte in eine Datenbank zu laden. Die Maskenspezifikation bei INGRES wird mit der INGRES-Komponente VIFRED durchgeführt. Um eine Maskenspezifikation zu erstellen, muß im Hauptmenü von INGRES die Funktion Forms aufgerufen werden. Mit diesem Aufruf wird die Komponente VIFRED ("Visual-Forms-Editor") aktiviert. VIFRED ist ein visuelles Dienstprogramm, das der Erstellung und Modifizierung von Maskenspezifikationen dient. Nach der Auswahl der Funktion Forms erscheint das in Abbildung 4-1 gezeigte frame. VIFRED - Frames Catalog Name
Owner
Short Remark
-49-
Place cursor on row and select derived operation from menu Create Destroy Edit Rename MoreInfo Utilities Go Abb. 4-1 VIFRED-frame Das Funktionsmenü der Abbildung 4-1 enthält folgende Funktionen: Funktion Create Delete Edit
Rename MoreInfo
Utilities Go
Bedeutung erstellt eine neue Maskenspezifikation. löscht eine existierende Maskenspezifikation. editiert eine existierende Maskenspezifikation. Der Benutzer hat dann die Möglichkeit, diese Spezifikation zu ändern. Benennt eine existierende Maskenspezifikation um. gibt zusätzliche Information (Erstellungsdatum, Kommentar usw.) über eine existierende Maskenspezifikation aus. gibt das Menü mit den Operationen Übersetzen, Drucken usw. für die Maskenspezifikation aus. startet den Ablauf der durch die Schreibmarke gekennzeichneten Maskenspezifikation.
Wir werden an einem Beispiel zeigen, wie eine einfache Maske für eine Tabelle erstellt werden kann. Zuerst muß die Funktion Create ausgewählt werden und danach erscheint das gleiche frame wie in Abb. 4-1 mit den neuen Menüfunktionen: - TableDefault; - Blankform und - JoinDefDefault . Menüunktion TableDefault
Bedeutung erstellt eine Standard-Maskenspezifikation für eine Benutzertabelle aufgrund der Einträge in den INGRES-Systemtabellen. Blankform gibt eine leere Maske aus. JoinDefDefault erstellt eine Standard-Maskenspezifikation für ein schon definiertes JoinDef aufgrund der Einträge in den INGRES-Systemtabellen. Um die Maskenspezifikation für die Tabelle arbeiten zu erstellen, muß zuerst die Funktion TableDefault ausgewählt und danach der Tabellenname (arbeiten) angegeben werden. Der Benutzer hat jetzt die Möglichkeit, zwischen zwei unterschiedlichen Ausgabeformen - Simplefields und TableField - zu wählen. SimpleFields gibt immer je -50-
eine Reihe aus, während TableField mehrere Reihen im Tabellenfo rmat auf einmal ausgibt. Nach der Auswahl von SimpleFields erscheint die in Abbildung 4-2 dargestellte Maskenspezifikation:
ARBEITEN Table M_nr: f_____________ Aufgabe: c____________
Pr_nr: c___ Einst_dat: c____________________
--------------------------End-of-Form------------------------------Abb. 4-2 Maskenspezifikation für die Tabelle arbeiten Die in Abbildung 4-2 gezeigte Maskenspezifikation für die Tabelle arbeiten kann nachträglich, falls gewünscht, geändert werden. Diese Änderungen werden mit der Funktion Edit angeleitet und können sowohl an den Überschriften als auch an den Darstellungsformaten einzelner Spalten durchgeführt werden. Mit der Funktion Save wird eine erstellte Maskenspezifikation gesichert. Nachdem eine Maskenspezifikation erstellt und gesichert wurde, kann sie zum Suchen, Ändern, Löschen und Einfügen von Daten verwendet werden. Das INGRES-Subsystem QBF ("Query-By-Forms") unterstützt den Benutzer bei der Durchführung dieser Operationen. Um QBF aufzurufen, muß im INGRES/MENU zuerst die Funktion Queries und danach QBF ausgewählt werden. (INGRES/QBF kann auch mit dem Betriebssystemkommando qbf aufgerufen werden. Dieses Kommando ist in Kapitel 15 beschrieben.) QBF arbeitet in zwei Phasen: In der ersten Phase werden Daten, die gesucht, geändert oder eingefügt werden sollen, festgelegt. Die Festlegung von Daten kann über drei QBF-Funktionen: - Tables, - JoinDefs und - QBF-Names durchgeführt werden. Funktion Tables JoinDefs
QBFNames
Bedeutung definiert eine Tabelle bzw. ein View, das für die Festlegung der Datenmenge verwendet wird. definiert zwei oder mehrere Tabellen, die miteinander verknüpft sind und die für die Festlegung der Datenmenge verwendet werden. definiert eine existierende Maskenspezifikation, die mit VIFRED erstellt und entweder mit einer Tabelle oder einem JoinDef in Verbindung steht.
Nach der Auswahl einer der drei Funktionen wird die zweite QBF-Phase gestartet, in der die Daten dann -51-
ausgewählt, geändert oder eingefügt werden. Um die in Abbildung 4-2 gezeigte Maske aufzurufen, muß der Name der Maskenspezifikation (arbeiten) markiert und die Funktion Go ausgewählt werden. Das in Abbildung 4-3 gezeigte frame entspricht der in Abbildung 4-2 definierten Maskenspezifikation. Das gewünschte Einfügen, Suchen und Ändern von Daten wird mit den drei, in der Menüleiste existierenden Funktionen Append, Retrieve, und Update respektive durchgeführt.
ARBEITEN Table
M_nr: Aufgabe: Append
Help
Pr_nr: Einst_dat: End
Abb. 4-3 Die Maske für die Tabelle arbeiten Durch die Auswahl der Funktion Append kann der Anwender neue Reihen der Tabelle arbeiten einfügen. Wie wir schon erwähnt haben, existieren zwei Möglichkeiten, die Ausgabe der Tabellenreihen zu formen. Die Ausgabe mit Hilfe der Funktion SimpleFields , die je eine Reihe ausgibt ist in Abbildung 4-2 dargestellt. Die Verwendung der anderen Funktion TableField, die mehrere Reihen im Tabellenformat gleichzeitig ausgibt, empfiehlt sich besonders, wenn eine Tabelle viele Reihen hat. Die Abbildung 4-4 zeigt das Retrieve-frame für die Tabelle arbeiten, das durch die Auswahl der Funktion TableField und anschließend analog dem in Abbildung 4-3 gezeigten frame entstanden ist. ARBEITEN TABLE(S) M_nr >=10000
Pr_nr
Aufgabe *leiter
Einst_dat
?3
Go Blank LastQuery Order Help End Abb. 4-4 Suchkriterium für die Tabelle arbeiten Die Funktion Retrieve ermöglicht die Suche nach bestimmten Reihen einer Tabelle, die eine Vorgabe des Benutzers erfüllen. Dies geschieht durch die Angabe eines Datenwertes oder durch die Verwendung der Suchoperatoren. Die Angabe des Datenwertes 28559 in der Spalte M_nr würde z.B. die Ausgabe folgender zwei Reihen 28559|p1| |01-aug-1988 und -52-
28559|p2|Sachbearbeiter|01-feb-1989 bedeuten. Die komplexeren Formen der Suche sind in Abbildung 4-5 gezeigt. Die Angabe des Wertepaares ">=1000" und "*leiter" in einer bzw. des Wertes "?3" in der anderen Reihe bedeutet eine ODER-Verknüpfung, die wahr ist, falls entweder die Bedingung der einen oder die Bedingung der anderen Reihe oder beide erfüllt sind. Das Zeichen ">=" definiert den grösser- gleich Operator, der, genauso wie die anderen Vergleichsoperatoren beim Vergleich numerischer Werte benutzt werden kann. Das Zeichen "*" kennzeichnet eine beliebige Zeichenfolge von n Zeichen (n>=0), während "?" genau ein beliebiges Zeichen darstellt. Die Kombination zweier (oder mehrerer) Suchkriterien in einer Zeile (">=10000" und "*leiter") definiert eine UND-Verknüpfung, die wahr ist, falls beide Bedingungen erfüllt sind. Die Abbildung 4-5 zeigt die Treffermenge der Reihen, die mit den in Abbildung 4-4 definierten Suchbedingungen in der Tabelle arbeiten gefunden wurde.
ARBEITEN TABLE(S)
M_nr 10102 10102 9031 2581
Pr_nr p1 p3 p3 p3
Aufgabe Projektleiter Gruppenleiter Sachbearbeiter Projektleiter
Einst_dat 01-oct-1988 01-jan-1989 15-nov-1988 15-oct-1989
Abb. 4-5 Gefundene Treffermenge in der Tabelle arbeiten
3.2
JoinDefs Die elementaren Funktionen, die das INGRES-Subsystem QBF unterstützt, sind das Suchen, Ändern und Löschen existierender Datenwerte einer Tabelle sowie das Einfügen neuer Reihen einer Tabelle. Diese QBF-Operationen, die sich auf einzelne Tabellen beziehen, werden oft in der Praxis benutzt; sie decken aber nur einen kleinen Teil der Anforderungen an QBF ab. Mit solchen elementaren QBF-Operationen ist es z.B. nicht möglich, aus der Beispieldatenbank gleichzeitig den Namen und das Einstellungsdatum eines Mitarbeiters zu erfragen. Für diese und ähnliche Aufgaben ist es notwendig, solche Operationen zu benutzen, die mehrere Tabellen miteinander verknüpfen. -53-
Die Voraussetzung, QBF-Programme zu erstellen, die gleichzeitig die Information aus mehreren Tabellen liefern, ist die Verwendung des INGRES-Subsystems JoinDef. JoinDef ermöglicht sowohl die Beschreibung von Tabellenverknüpfungen einer Datenbank als auch die Beschreibung von Eigenschaften dieser Verknüpfungen. Bevor das INGRES-Subsystem JoinDef mit allen seinen Funktionen beschrieben wird, werden die möglichen Tabellenverknüpfungen der Beispieldatenbank erörtert. Die ersten beiden Tabellen, die miteinander verknüpft werden können, sind abteilung und mitarbeiter. Die beiden Tabellen können mit Hilfe der Spalte abt_nr, die die Abteilungsnummer der Firma enthält, miteinander verknüpft werden. Diese Spalte ist in beiden Tabellen gleichnamig, was nicht unbedingt notwendig ist. Damit haben wir die Spalten abteilung. abt_nr und mitarbeiter.abt.nr als Verknüpfungsspalten der Tabellen abteilung und mitarbeiter. Wie aus der Definition der Beispieldatenbank ersichtlich, gehören jeder Abteilung mehrere Mitarbeiter an. Diese Beziehungsart, wo jeder Reihe einer Tabelle mehrere Reihen einer anderen Tabelle entsprechen, wird Master/Detail- Beziehung genannt. Im Falle der Tabellen abteilung und mitarbeiter ist damit abteilung die Masterund mitarbeiter die Detail- Tabelle. In der Beispieldatenbank können zwei weitere Tabellenverknüpfungen definiert werden. Die Tabelle projekt kann mit der Tabelle arbeiten verknüpft werden, wobei projekt.pr_nr und arbeiten.pr_nr die Verknüpfungsspalten sind. Aus der Definition der Beispieldatenbank geht hervor, daß in jedem Projekt mehrere Mitarbeiter arbeiten. Damit ist projekt die Master- und arbeiten die Detail-Tabelle. Schließlich können auch die Tabellen mitarbeiter und arbeiten mit Hilfe der Spalten mitarbeiter.m_nr und arbeiten.m_nr verknüpft werden. Weil jede Mitarbeiternummer einmal in der Tabelle mitarbeiter und mehrmals in der Tabelle arbeiten vorkommt, ist mitarbeiter die Master- und arbeiten die Detail- Tabelle. Um diese Tabellenverknüpfungen dem INGRES-System bekannt zu machen, muß die Funktion JoinDefs des INGRES/MENU (Abb. 4-6) aufgerufen werden.
QBF JoinDefs Catalog
-54-
Name
Owner
Short Remark
Place cursor on row and select desired operation from menu Create Destroy Edit Re name MoreInfo Go Abb. 4-6 JoinDefs Catalog-frame Die Menüfunktionen des JoinDefs-Katalogs haben folgende Bedeutung: Funktion Create Destroy Edit
Rename MoreInfo
Go
Bedeutung Mit dieser Funktion kann eine Tabellenverknüpfung definiert werden. löscht eine schon existierende Tabellenverknüpfung. Mit Edit kann die Information über eine schon mit JoinDefs definierte Tabellenverknüpfung ausgegeben werden. Ändert den Namen einer mit JoinDefs definierten Tabellenverknüpfung. Mit dieser Funktion können sowohl stichwortartige als auch lange Kommentare zu einer mit JoinDefs definierten Tabellenverknüpfung erstellt werden. (Der stichwortartige Kommentar erscheint in der Spalte Short Remarks der Abbildung 4-6.) führt ein JoinDef aus.
Um eine Tabellenverknüpfung zu erstellen, muß die Funktion Create aufgerufen werden. Nach dem Aufruf dieser Funktion erscheint das in Abbildung 4-7 gezeigte frame.
QBF - JoinDef Definition Form JoinDef Name: abt_mit For each table in the JoinDef, enter table name (with optional abbreviation for table name) below. For Master/Detail JoinDefs enter Master or detail under Role (Default is Master if blank). Role MASTER detail
Table Name abteilung mitarbeiter
Abbreviation
Table Field Format? (y/n): YES Select the "Go" to run the Join Definition -55-
Go
Blank
ChangeDisplay
Joins
Rules
Abb. 4-7 JoinDef Definition Form-frame In Abbildung 4-7 sind die Angaben für die Verknüpfung der Tabellen abteilung und mitarbeiter gemacht worden. In der Role-Spalte haben wir die Master/Detail- Beziehung zwischen diesen beiden Tabellen definiert, während in der Abbreviation-Spalte ein Kürzel für jede Tabelle eingetragen werden kann. Nachdem die Namen der zu verknüpfenden Tabellen und ihre Master/Detail- Beziehung definiert sind, müssen noch die Verknüpfungsspalten der beiden Tabellen spezifiziert werden. Diese Spezifikation erfolgt durch die Auswahl der Funktion Joins, wonach das in der Abbildung 4-8 gezeigtes frame erscheint. QBF - JoinDef Join Specification
Column abteilung.abt_nr
Join MD
Column mitarbeiter.m_nr
To get help on a table enter the table name or identifier below and select the "GetTableDef" menu item. Column m_nr m_name m_vorname abt_nr
Data Type i4 char(20) char(20) char(4)
Rules
GetTableDef Forget Help End Abb. 4-8 JoinDef Specification-frame Das in Abbildung 4-8 gezeigte frame enthält die Verknüpfungsspalten sowie die mit MD gekennzeichnete Master/Detail- Beziehung der Tabellen abteilung und mitarbeiter. Im unteren Teil der Maske kann der Benutzer durch die Eintragung eines Tabellennamens und den Aufruf der Funktion GetTableDef, alle Spalten einer Tabelle am Bildschirm angezeigt bekommen. Damit hat er die Möglichkeit, das logische Schema der Tabelle anzuschauen, um sich z.B. ins Gedächtnis zu rufen, welche Spalte die Verknüpfungsspalte ist. Hinweis. INGRES unterscheidet zwischen Master/Detail- und Master/Master-Beziehung. Die Master/Master-Beziehung kennzeichnet die Beziehungsart, wo jeder Reihe einer Tabelle genau eine Reihe einer anderen Tabelle entspricht. Eine solche Beziehungsart wird in der Role-Spalte (Abb. 4-7) -56-
durch die entsprechende Angabe definiert. Die zweite wichtige Funktion des JoinDef Definition-frame ist Rules. Mit dieser Funktion können Re geln für das Ändern bzw. Löschen der Reihen der beiden in einer Master/Detail- Beziehung stehenden Tabellen definiert werden. Die Abbildung 4-9 zeigt das JoindDef Update & Delete Rules-frame.
QBF - JoinDef Update & Delete Rules Update Information: To enable modification of join fields in Update mode, enter "Yes" under UPDATE? Column Column abteilung.abt_nr mitarbeiter.abt_nr
Update? no no
Delete Information:To disable deletion of rows in a table during Update mode, enter "No" under Delete? column Column abteilung.abt_nr mitarbeiter.abt_nr Joins
Forget
Help
Delete? yes yes End
Abb. 4-9 JoinDef Update & Delete Rules-frame Das JoinDef Update & Delete Rules-frame beschreibt die Regeln, die beim Ändern einer Verknüpfungsspalte, bzw. Löschen einer Reihe der beiden verknüpften Tabellen gelten. Die Änderungsregeln beziehen sich auf die Frage, was passiert, wenn die Werte einer der beiden Verknüpfungsspalten geändert werden. Sollten die entsprechenden Werte der Tabelle mitarbeiter geändert werden, wenn z.B. der Datenwert a1 der Spalte abt_nr der Tabelle abteilung in a4 geändert wird? Falls ja, muß die Angabe in der Update?-Spalte in der zweiten Zeile "yes" enthalten. Dieselbe Frage kann auch gestellt werden, wenn Datenwerte der Spalte abt_nr der Tabelle mitarbeiter geändert werden. Falls die entsprechende Änderung auch in der Tabelle abteilung durchgeführt werden sollte, muß in der Update?-Spalte in der ersten Zeile "yes" eingetragen werden. Die Regeln zum Löschen entsprechen bis auf einen Punkt genau den Änderungsregeln. Der einzige Unterschied ist, daß die Änderungsregeln sich auf Spalten und die Regeln zum Löschen auf Reihen beziehen. Falls z.B. die Reihen der Tabelle mitarbeiter, die den Datenwert a3 enthalten, nicht gelöscht sein sollten, nachdem die Reihe der Tabelle abteilung mit demselben Datenwert gelöscht wurde, muß die Angabe in der -57-
Delete?-Spalte in der zweiten Zeile "no" lauten.
3.3
Komplexe Masken Im Unterschied zu einfachen Masken basieren komplexe Masken gewöhnlich auf einer Verknüpfung zweier oder mehrerer Tabellen. In dem vorherigen Abschnitt haben wir gezeigt, wie die Verknüpfung von Tabellen mit Hilfe von JoinDef spezifiziert werden kann. In diesem Abschnitt werden die so erzeugten Verknüpfungen für die Erstellung und Ausführung von Maskenspezifikationen verwendet. Um dies zu zeigen, werden wir das existierende JoinDef, das die Tabellen abteilung und mitarbeiter verknüpft, benutzen. Die Erstellung und die Ausführung einer mit Hilfe von JoinDef erzeugten Maskenspezifikation erfolgt analog der Erstellung und Ausführung einer einfachen Maskenspezifikation. Zuerst wird die Funktion Forms im INGRES-Hauptmenü und danach die Funktion Create aufgerufen. Um eine Maskenspezifikation mit Hilfe von JoinDef zu erstellen, muß jetzt die Menüfunktion JoinDefDefaults ausgewählt und der JoinDef-Name (abt_mit) angegeben werden (Abb. 4-10).
ABTEILUNG TABLE Abt_nr:c___ Abt_name: c______________ Stadt:c______________
MITARBEITER TABLE(S) M_nr f_____
M_name c______________
M_vorname c______________
Create Delete Edit Move Undo Order Save FormAttr Location Help Abb. 4-10 Maskenspezifikation für die Tabellen abteilung und mitarbeiter Die in Abbildung 4-10 gezeigte Maske kann anschließend wie bei den einfachen Masken schon beschrieben - mit der Funktion Edit geändert und mit Save gesichert werden. Die Operationen an Daten erfolgen mit Hilfe von QBF analog zu den schon beschriebenen Operationen bei einfachen Masken. Abbildung 4-11 zeigt die Ausführung der Funktion Retrieve nach der Eingabe des Wertes a1 für die Spalte abt_nr.
ABTEILUNG TABLE
-58-
Abt_nr: a1 Stadt: Muenchen
Abt_name: Beratung
MITARBEITER TABLE(S) M_nr 18316 28559
M_name Mueller Mozer
M_vorname Gabriele Sibille
Abb. 4-11 Die Ausführung der Funktion Retrieve
Aufgabe. A.4.1
Mit Hilfe des INGRES-Subsystems JoinDef erstellen Sie eine Tabellenverknüpfung für die Tabellen projekt und arbeiten. Die Datenwerte der Spalte pr_nr der Tabelle projekt sollen nicht geändert werden, falls die entsprechenden Werte der Spalte pr_nr der Tabelle arbeiten modifiziert werden und umgekehrt. Die Reihen der Tabelle arbeiten sollen nicht gelöscht werden, falls die entsprechende Reihe der Tabelle projekt gelöscht wird und umgekehrt.
-59-
INGRES/RBF, Report-Writer und INGRES/VIGRAPH INGRES/RBF, Report-Writer und INGRES/VIGRAPH...............................................................60 5
INGRES/RBF, Report-Writer und INGRES/VIGRAPH........................................................60 5.1 Einleitung 60 5.2 INGRES/RBF 61 5.3 Report-Writer 66 5.4 INGRES/GRAPHS 71 Aufgaben. 73
4 INGRES/RBF, Report-Writer und INGRES/VIGRAPH In diesem Kapitel werden zuerst zwei INGRES-Komponenten dargestellt, die in Bezug zu Listenprogrammen stehen. Es tenprogramm erstellen. Am Ende des Kapitels wird die INGRES/VIGRAPH-Komponente vorgestellt, mit der die gewünschten Berichte in der graphischen Form ausgegeben werden können.
4.1
Einleitung Die Listenprogramme dienen zur formatierten Ausgabe einer Abfrage. Im Unterschied zu INGRES/QBF, das auch das Ändern, Einfügen und Löschen von Datenwerten ermöglicht, können INGRES/RBF und Report-Writer nur solche Programme spezifizieren bzw. erstellen, die für die Suche von Datenwerten benutzt werden. Die Listenprogramme werden bevorzugt, wenn eine Abfrage viele Reihen einer Tabelle enthält und das Ergebnis in einer gut lesbaren Form im zeitlichen Ablauf ständig benötigt wird. Die Ausgabe einer Liste kann entweder auf einem Drucker, auf dem Bildschirm oder in einer Datei erfolgen. Die einfachsten Listenprogramme, die erstellt werden können, werden Standard-Listenprogramme genannt. Die Standard-Listenprogramme können bei INGRES mit INGRES/RBF erstellt werden. Abbildung 5-1 zeigt die Ausgabe eines Standard-Listenprogramms, das für die Tabelle arbeiten erstellt wurde:
05-DEC-1991
15:30:35 -60-
Report on Table: arbeiten M_nr 2581 9031 10102 18316 25348 28559 29346
Pr_nr p3 p3 p1 p1 p3 p2 p2 p2 p1 p2 p1
Aufgabe Projektleiter Sachbearbeiter Gruppenleiter Projektleiter Gruppenleiter
Einst_dat 15-oct-1989 15-nov-1988 15-apr-1989 01-oct-1988 01-jan-1989 01-jun-1989 15-feb-1988 01-feb-1988 01-aug-1988 15-dec-1987 01-apr-1989
Sachbearbeiter Sachbearbeiter
Sachbearbeiter -1-
Abb. 5-1 Standard-Listenausgabe für die Tabelle arbeiten Die Ausgabe eines Standard-Listenprogramms enthält, genauso wie die Ausgabe jedes Listenprogramms, eine oder mehrere Ausgabeseiten, von welchen jede folgende Teile beinhaltet: - Kopfzeile (mit Datum und Uhrzeit), - Titel, - Spaltenüberschrift, - Datenwerte und - Fußzeile (mit Seitenangabe). Bei einem Standard-Listenprogramm sind die Namen in der Spaltenüberschrift identisch mit den Spaltennamen der Tabelle. Die Reihenfolge dieser Namen entspricht genau der Reihenfolge der Spalten im Tabellenschema.
4.2
INGRES/RBF Das INGRES-Subsystem INGRES/RBF ("Report-by-Forms") ermöglicht dem Benutzer, mit Hilfe von frames und ohne irgendwelche Programmierkenntnisse, Listenprogramme zu spezifizieren. Das frame von INGRES/RBF hat sehr viele Ähnlichkeiten mit dem JoinDefs Catalog-frame. Diese Ähnlichkeit äußert sich sowohl bei einigen Masken als auch bei der Funktionalität. INGRES/RBF kann entweder mit Hilfe der Funktion Reports im INGRES/MENU oder mit dem Betriebssystemkommando rbf aufgerufen werden. (Das Kommando rbf ist in Kapitel 15 beschrieben.) Der Aufruf der Funktion Reports im INGRES/MENU erzeugt, bis auf die Titelzeile, die gleiche Maske wie in der Abbildung 4-6 angezeigt. Nach der Auswahl der Unterfunktion Create und der anschließenden Eingabe eines Tabellennamens, erscheint die neue Menüleiste mit den folgenden Funktionen: - Block Mode, - Column Mode, - Wrap Mode und -61-
- Default Mode, wobei jede dieser Funktionen unterschiedliche Ausgabearten von Daten in einer Liste beschreibt. Funktion Block Mode Column Mode
Wrap Mode
Default Mode
Bedeutung die Ausgabe von Datenwerten erscheint rechts neben der entsprechenden Überschrift. die Ausgabe erfolgt spaltenweise. (Falls die Ausgabe zu lang ist, wird sie auf dem Bildschirm rechts abgeschnitten und auf dem Drucker in mehrere Zeilen umgebrochen.) entspricht dem Column-Modus, wenn die Tabellenspalten in eine Zeile passen. Falls nicht, wird die Ausgabe automatisch umgebrochen. entspricht dem Column-Modus, falls die Ausgabe in eine Zeile paßt, sonst dem Block-Modus.
Am Beispiel der Tabelle arbeiten (Abb. 5-2) werden wir die Erstellung der Listenspezifikation im Column- Modus durchführen.
------------------------------Title----------------------------------Report on Table: arbeiten --------------------------Column-Headings-------------------------M_nr Pr_nr Aufgabe Einst_dat --------------------------Detail- Lines-------------------------------f____________ c___ c______________ c_______________ ----------------------End-of-Detail-Lines-----------------------------
Create Delete Edit Move Undo Order ReportOptions Save Abb. 5-2 Listenspezifikation für die Tabelle arbeiten Das Funktionsmenü in Abbildung 5-2 enthält folgende Funktionen: Funktion Create
Delete Edit Move Undo
Bedeutung erstellt ein neues Objekt für die Listenspezifikation. Dieses Objekt kann eine beliebige Überschrift, eine neue bzw. eine leere Zeile usw. sein. löscht ein existierendes Objekt einer Listenspezifikation. editiert ein existierendes Objekt einer Listenspezifikation. bewegt ein existierendes Objekt einer Listenspezifikation. hebt die zuletzt ausgeführte Operation auf. -62-
Order
definiert, welche Spalten für die Sortierfolge und Gruppenbildung verwendet und welche Aggregatfunktionen benutzt werden. ReportOption definiert das Layot der Liste. s Save übersetzt und sichert die Listenspezifikation. Mit der Funktion Save kann die in Abbildung 5-2 gezeigte Listenspezifikation gespeichert und anschließend mit der Funktion Go ausgeführt werden. Weil wir keine Änderungen an der Spezifikation vorgenommen haben, wird daraus ein Standard-Listenprogramm erstellt, dessen Ausgabe Abbildung 5-1 zeigt. Die Funktionen Edit und Move werden benötigt, wenn die Elemente der angezeigten Spezifikation eine andere Ausgabeform haben sollen. Diese beiden Funktionen können nur einfache Operationen (wie z.B. neue Elemente einfügen bzw. existierende Elemente verschieben) ausführen. Um komplexere Operationen wie z.B. Gruppenbildung durchzuführen, muß die Funktion Order (Abb. 5-3) aufgerufen werden.
RBF - Order Columns Scroll through the column names. Select the sorting sequence (0-127), sorting direction("a" or "d") and whether to break ("y" or "n") for each column.
Column Name m_nr pr_nr aufgabe einst_dat ColumnOptions
Sequence 0 2 1 0 Top
Direction
Break
a a
n y
Bottom
Abb. 5-3 Order Columns-frame für die Tabelle arbeiten Die Column Name-Spalte zeigt alle Spalten der Tabelle arbeiten an. Für jede dieser Tabellenspalten kann bei Sequence angegeben werden, ob die Spalte für den Sortiervorgang benutzt (ungleich 0) wird oder nicht (=0). Die Zahlen 1,2,... definieren die Reihenfolge der Sortierung, wobei zuerst nach der Spalte mit dem Wert 1 sortiert wird usw. Die Direction-Spalte definiert die aufsteigende ("a") bzw. absteigende ("d") Sortierung. Die Break-Spalte schließlich kennzeichnet mit "y" die Tabellenspalten, die für die Gruppenbildung benutzt werden sollen. In Abbildung 5-3 werden damit die Spalten aufgabe und pr_nr in dieser Reihenfolge als Sortierbegriffe verwendet. -63-
Die Sortierung erfolgt bei beiden Spalten aufsteigend. Zusätzlich werden die Werte der Spalte aufgabe zur Gruppenbildung verwendet. Mit der Auswahl der Funktion Column Options in der Maske Order Columns können die etwaigen Aggregatfunktionen definiert, bzw. das Layout einer Gruppenbildung beschrieben werden. Um die Funktion ColumnOptions für die Spalte aufgabe (Abb. 5-4) aufzurufen, muß die Schreibmarke in der Maske Order Columns auf dieser Spalte positioniert werden. Dasselbe gilt für die Spalte m_nr (Abb. 5-5).
RBF - Column Options Column Name: aufgabe
Break Column: y
When to print values: b (b=break, p=page, t=both, a=always) Lines to skip on break: 1 (-1 to skip to top of new page) Selection criteria at run time: n (n=none, v=value, r=range) Enter "x" to select Aggregate/Break Combinations for Columns
Aggregate
Over Repo rt
Over Break
Over Pages
Any Count Count (Unique) Maximum Minimum Help End Abb. 5-4 Column Options-frame für die Spalte aufgabe
RBF - Column Options Column Name: m_nr
Break Column: n
Selection criteria at run time: n (n=none, v=value, r=range) Enter "x" to select Aggregate/Break Combinations for Columns
Aggregate
Over Report
Over Break -64-
Over Pages
Any Count Count (Unique) Maximum Minimum Sum
x
Help End Abb. 5-5 Column Options-frame für die Spalte m_nr In Abbildung 5-4 sind die Eigenschaften der Spalte aufgabe beschrieben. Weil diese Spalte mit der Angabe Break=y definiert ist, enthält ihre Column Options-Maske auch die Zeilen, in denen das Layout für die Gruppenbildung beschrieben ist. Mit der Angabe b wird definiert, daß jeder Datenwert dieser Spalte nur am Anfang jeder Gruppenbildung erscheint. Die Angabe 1 in der nächsten Maskenzeile spezifiziert die Anzahl der eingefügten leeren Zeilen nach jeder Gruppenbildung. Abbildung 5-5 enthält keine Angaben für Gruppenbildung, weil mit der Spalte m_nr keine Gruppenbildung durchgeführt wird. Diese Spalte wird aber für die Ausgabe des maximalen Datenwertes nach jeder Gruppenbildung mit der Spalte aufgabe benutzt. Deswegen muß, in der Over Break-Spalte und in der Maximum- Zeile, das Zeichen "x" gesetzt werden. Die Ausgabe der entsprechenden Liste (nach dem Sichern und der Übersetzung der Listenspezifikation) ist in Abbildung 5-6 dargestellt. 05-JAN-1992
15:45:35
Report on Table : arbeiten M_nr 9031 10102
Pr_nr
Aufgabe
Einst_dat
p1 p3
Gruppenleiter
15-apr-1989 01-jan-1989
Projektleiter
01-oct-1988 15-oct-1989
Sachbearbeiter
01-apr-1988 01-feb-1988 15-feb-1988 15-nov-1988
Totals: Gruppenleiter Max: 10102 10102 p1 2581 p3 Totals: Projektleiter Max: 10102 29346 p1 28559 p2 25348 p2 9031 p3
-65-
Totals: Sachbearbeiter Max: 29346 28559 p1 29346 p2 18316 p2 Totals: Max: 29346
01-aug-1988 15-dec-1987 01-jun-1989
-1Abb. 5-6 Listenausgabe für die Tabelle arbeiten
4.3
Report-Writer Im Unterschied zu INGRES/RBF, das für die Spezifikation eines Listenprogramms keine Programmierkenntnisse erfordert, enthält Report-Writer für die Listenprogrammerstellung eine einfache Programmiersprache. Ein mit dem Report-Writer erstelltes Listenprogramm kann anschließend mit dem Betriebssystemkommando sreport übersetzt und danach mit report ausgeführt werden. (Für die Beschreibung beider Kommandos siehe Kapitel 15.) Die Verwendung der Programmiersprache des Report-Writers werden wir an einem Beispiel erläutern. Beispiel 5.1 .NAME bsp0501 .LONGREMARK Dieses Listenprogramm waehlt Datenwerte der Mitarbeiter aus, die in einem vom Benutzer eingegebenen Jahr in ihren Projekten eingestellt wurden. Alle ausgewaehlten Mitarbeiter werden nach Projektnummern gruppiert und zu jeder Gruppe wird das frueheste Einstellungsdatum ausgegeben. .ENDREMARK .OUTPUT b0501.lst .QUERY SELECT pr_nr,arbeiten.m_nr,m_name, einst_dat FROM arbeiten, mitarbeiter WHERE arbeiten.m_nr = mitarbeiter.m_nr AND date_part ('year',einst_dat) = $jahr .SORT pr_nr .FORMAT einst_dat(d"01-02-03") /* Listenkopfbeschreibung */ .HEADER REPORT .NEWLINE 5 .LEFT .PRINT "Datum:",current_date (d"February 3 1901") .NEWLINE 3 .UNDERLINE .TAB 20 .PRINT "Einstellungen im Jahre 1988" .NOUNDERLINE .NEWLINE 4 .UNDERLINE .TAB 2 .PRINT "Projektnummer" .TAB 16 .PRINT "Mitarbeiternummer" .TAB 35 .PRINT "Mitarbeitername" .TAB 55 .PRINT "Eingestellt am:" -66-
.NOUNDERLINE .FOOTER REPORT .NEWLINE 2 .CENTER .PRINT "Listenende" .NEWLINE 1 .HEADER pr_nr .NEWLINE 3 .TAB 2 .PRINT "Projektnummer: " .TAB 20 .PRINT pr_nr c(4) .FOOTER pr_nr .NEWLINE 1 .PRINT "-----------------------------------------------------------" .NEWLINE 1 .TAB 2 .PRINT "Das frueheste Einstellungsdatum ist: ",min(einst_dat) .NEWLINE 1 .PRINT "-----------------------------------------------------------" .NEWLINE 1 .DETAIL .TAB 2 .PRINT pr_nr c(4) .TAB 16 .PRINT m_nr n(6) .TAB 35 .PRINT m_name c(20) .TAB 55 .PRINT einst_dat c(8) .NEWLINE 1 Das Programm in Beispiel 5.1 wählt Datenwerte der Mitarbeiter aus, die im Jahr, das vom Benutzer beim Ablauf eingegeben wird, in ihren Projekten eingestellt wurden. Alle ausgewählten Mitarbeiter werden nach Projektnummern gruppiert, und zu jeder Gruppe wird das früheste Einstellungsdatum ausgegeben. Jede Report-Writer-Anweisung hat zwei Teile: Das Zeichen "." als Präfix, gefolgt von einem Report-Writer-Schlüsselwort. Jedes Schlüsselwort hat ein oder mehrere Kürzel, die als Synonyme benutzt werden können. Die Report-Writer-Anweisungen können in mehrere Gruppen unterteilt werden, abhängig davon, welche Aufgabe sie in einem Listenprogramm haben. Zu der ersten Gruppe gehören sogenannte Report Setup-Anweisungen, die allgemeine Eigenschaften eines Listenprogramms (Listenprogrammname, SELECT-Anweisung für die Datenauswahl usw.) beschreiben. Zu dieser Gruppe gehören folgende, in Beispiel 5.1 verwendete Anweisungen: - .NAME, - .LONGREMARK, - .OUTPUT, - .QUERY und - .SORT . Die .NAME-Anweisung beschreibt den Namen des Listenprogramms. Diese Anweisung muß als erste Anweisung eines Report-Writer-Programms erscheinen. Unter dem Listenprogrammnamen (in unserem Beispiel bsp0501) wird die Listenspezifikation im INGRES-Systemkatalog gespeichert. Diese Aufgabe führt das Kommando sreport aus. Beim Ablauf des Listenprogramms mit Hilfe des report-Kommandos muß der -67-
Listenprogrammname in der Kommandozeile explizit angegeben werden. Das Kürzel .NAM ist das Synonym für .NAME. Mit der Anweisung .LONGREMARK kann ein Kommentar, der mehrere Zeilen umfaßt, geschrieben werden. Diese Anweisung dient zum Dokumentieren eines Listenprogramms. Das Kürzel .LREM ist das Synonym für .LONGREMARK. Weil der Kommentar mehrere Zeilen lang sein kann, existiert beim Report-Writer eine zusätzliche Anweisung - .ENDREMARK -, mit der das Kommentarende gekennzeichnet wird. Das Kürzel .ENDREM ist das Synonym für .ENDREMARK. Eine zweite, ähnliche Anweisung wie .LONGREMARK ist .SHORTREMARK (.SREM), mit der ein einzeiliger Kommentar geschrieben werden kann. Report-Writer unterstützt auch das Dokumentieren von Listenprogrammen mittels allgemeiner Kommentare. Den Anfang eines solchen Kommentars bildet das Zeichenpaar "/*" und sein Ende das Zeichenpaar "*/". Der Unterschied zwischen den mit .LONGREMARK bzw. .SHORTREMARK erstellten Kommentaren einerseits und den allgemeinen Kommentaren andererseits ist, daß die ersten im INGRES-Systemkatalog gespeichert werden und beim Aufruf des Catalog-frames von INGRES/RBF am Bildschirm ausgegeben werden. Die Anweisung .OUTPUT (.OUT) definiert den Dateinamen für die Listenausgabe. Falls diese optionale Anweisung in einem Listenprogramm nicht angegeben ist, wird die Liste standardmäßig auf dem Bildschirm ausgegeben. (Der Schalter "- f" des report-Kommandos kann nachträglich das Ausgabemedium einer Liste ändern.) Die .QUERY-Anweisung enthält eine komplette SELECT-Anweisung, mit der Daten aus einer oder mehreren Tabellen einer Datenbank ausgewählt werden. Die Abfragen mittels SELECT-Anweisung sind in Kapitel 7 und 8 beschrieben. Die in Beispiel 5.1 verwendete SELECT-Anweisung wählt Daten aus zwei Tabellen der Beispieldatenbank arbeiten und mitarbeiter - aus. In der WHERE-Klausel dieser SQL-Anweisung existiert die Bedingung date_part ('year', einst_dat) = $jahr . Mit der INGRES-Funktion date_part wird die Jahresangabe der Spalte einst_dat der Tabelle arbeiten ausgewählt und mit der Variablen jahr verglichen. (Alle Variablen eines Report-Writer-Programms werden mit dem Präfix "$" gekennzeichnet. Jeder Teil einer SELECT-Anweisung (Tabellenname, Spaltenname usw.) kann durch eine Variable ersetzt werden, die beim Ablauf des Listenprogramms durch die Benutzereingabe ersetzt wird. Die Wertzuweisung einer Variablen kann in der Kommandozeile des report-Kommandos erfolgen. Falls dies unterlassen wird, wird der Benutzer vom System aufgefordert, die Angabe nachträglich zu machen. Hinweis. Die Erstellung der komplexen SELECT-Anweisungen (wie in Beispiel 5.1) soll außerhalb des Listenprogramms vorgenommen werden. Der Weg, den wir empfehelen, ist, die entsprechende -68-
SELECT-Anweisung mit der isql-Schnittstelle von INGRES zu erstellen und auszutesten. Nachdem sich der Benutzer überzeugt hat, daß die SELECT-Anweisung korrekt ist, sollte diese Anweisung in einer Datei gespeichert und anschließend in das Listenprogramm eingefügt werden. Die Anweisung .QUERY muß in einem Report-Writer-Programm angegeben werden, falls die alternative Anweisung .DATA nicht verwendet wird. Die .DATA-Anweisung hat eine ähnliche, aber eingeschränkte Funktionalität im Vergleich zu der .QUERY-Anweisung. Das Kürzel .QUER ist das Synonym für .QUERY. Mit der .SORT-Anweisung werden die Spalten definiert, die zur Sortierung verwendet werden. Wenn mehrere Spalten in der .SORT-Anweisung genannt sind, definiert ihre Reihenfolge gleichzeitig die Reihenfolge der Sortierung. Falls die .BREAK-Anweisung in einem Report-Writer-Programm nicht angegeben ist, spezifiziert die .SORT-Anweisung zusätzlich die Gruppenbildung im Listenprogramm. Das Kürzel .SRT ist das Synonym für .SORT. Die zweite Gruppe der Report-Writer-Anweisungen definiert die Struktur eines Listenprogramms. Zu dieser Anweisungsgruppe gehören: - .HEADER, - .FOOTER und - .DETAIL . Die .HEADER-Anweisung spezifiziert den Beginn einer Anweisungsgruppe, die am Listen-, Seiten- oder Grupppenbildungsanfang ausgeführt wird. Dementsprechend kann diese Anweisung drei unterschiedliche Angaben enthalten: - REPORT, - PAGE und - spalten_name . Die REPORT- bzw. PAGE-Angabe kennzeichnet den Beginn der Anweisungsgruppe, die am Listen- bzw. Seitenanfang ausgeführt wird. spalten_name ist eine in der Anweisung .SORT bzw. .BREAK angegebene Tabellenspalte, die die beim Gruppenwechsel auszuführende Anweisungsfolge kennzeichnet. In Beispiel 5.1 spezifiziert .HEADER pr_nr, welche Anweisungen bei jedem Gruppenwechsel der Spalte pr_nr ausgeführt werden. Die .FOOTER-Anweisung definiert den Beginn einer Anweisungsgruppe, die am Listen-, Seiten- oder Gruppenbildungsende ausgeführt wird. Die Syntax der .FOOTER-Anweisung entspricht genau der Syntax der .HEADER-Anweisung. Mit der .DETAIL-Anweisung wird der Beginn einer Anweisungsfolge gekennzeichnet, die bei der Ausgabe jeder Datenzeile ausgeführt wird. Diese Anweisung hat keine zusätzlichen Angaben. Das Kürzel .DET ist das Synonym für .DETAIL. Eine der meistverwendeten Report-Writer-Anweisungen ist die -69-
.PRINT-Anweisung. Diese Anweisung veranlaßt die Ausgabe von Zeichenketten, Tabellenspalten oder Ausdrücken aller Art in eine Liste. Der Programmierer kann die Ausgabe mit Hilfe zahlreicher unterschiedlicher Formate steuern, von denen wir die in Beispiel 5.1 verwendeten c(20), n(6) und d("February 3 1901") erläutern werden. Das Format c(n) gibt eine Zeichenkette aus, wobei n die maximale Anzahl der auszugebenden Zeichen definiert. Das zweite Format n(b) wird für die Ausgabe numerischer Werte benutzt, während d("February 3 1901") in Beispiel 5.1 die Ausgabe der Systemfunktion current_date regelt. Report-Writer verwendet das spezifische Datums- und Zeitformat Sunday, February 3 1901 at 4:05:06 p.m. als Musterformat für Werte vom Typ DATE. Dieses Format kann auch teilweise benutzt werden, wie in Beispiel 5.1 ersichtlich ist. Die letzte Gruppe von Anweisungen, die wir beschreiben werden, sind die Anweisungen zur Textpositionierung. Zu diesen Anweisungen gehören u.a.: - .TAB, - .NEWLINE, - .LEFT und - .CENTER . Mit der .TAB-Anweisung wird die Position in der aktuellen Zeile geändert, so daß der Text ab der definierten Stelle ausgegeben wird. Die Änderung der aktuellen Position kann sowohl absolut (z.B. .TAB 32) als auch relativ (z.B. .TAB +12) sein. Das Kürzel .T ist das Synonym für .TAB. Die Anweisung .NEWLINE n verschiebt die Textausgabe um n Zeilen. Falls die optionale Angabe n ausgelassen wird, wird standardmäßig der Wert 1 angenommen, was die Verschiebung an den Anfang der nächsten Zeile bedeutet. Das Kürzel .NL ist das Synonym für .NEWLINE. Die Anweisung .LEFT n positioniert einen Textblock relativ um n Stelen nach links. Falls n ausgelassen wird, wird der Textblock linksbündig ausgegeben. (Report-Writer unterstützt auch die analoge Anweisung .RIGHT.) Die Anweisung .CENTER zentriert einen Textblock. Report-Writer ermöglicht auch die Verwendung von Kontrollstrukturen mittels der IF-Anweisung. Diese Anweisung hat hier dieselbe Syntax und Funktionalität wie in den meisten anderen Programmiersprachen. Die Listenausgabe des Beispiels 5.1 sieht folgendermaßen aus: Datum:August 14 1991 Einstellungen im Jahre 1988 Projektnummer Mitarbeiternummer Mitarbeitername -70-
Eingestellt am:
p1 p1
285591 10102
Mozer Huber
88-08-01 88-10-01
Das juengste Einstellungsdatum ist: 88-08-01
p2 p2
285592 25348
Mozer Keller
88-02-01 88-02-15
Das juengste Einstellungsdatum ist: 88-08-01
p3
090313
Meier
88-11-15
Das juengste Einstellungsdatum ist: 88-08-01 Listenende
4.4
INGRES/GRAPHS INGRES/GRAPHS ist eine weitere Komponente von INGRES, die die Erstellung von Berichten ermöglicht. Im Unterschied zu INGRES/REPORTS bzw. Report-Writer, die beide Berichte in einer Listenform erstellen, erzeugt INGRES/GRAPHS die Ausgabe in grafischer Form. Um die von INGRES/GRAPHS erstellten Grafiken auszugeben, benötigt der Benutzer einen entsprechenden grafischen Bildschirm. (Ein alphanumerischer Bildschirm kann u.U. auch für die Ausgabe von den mit INGRES/GRAPHS erstellten Grafiken benutzt werden; in diesem Fall aber werden die Linien durch Buchstaben ersetzt, was eine zusätzliche Vorstellungskraft des Benutzers erfordert, um die Grafik erfassen zu können.) INGRES/GRAPHS enthält eine einzige Komponente - VIGRAPH ("Visual Graphic Editor") - die aufgerufen wird, nachdem die Funktion Graphs im INGRES/MENU ausgewählt, bzw. das Betriebssystemkommando vigraph ausgeführt wird. Nach dem Aufruf der Funktion Graphs erscheint das VIGRAPH-frame (Abb. 5-7):
VIGRAPH - Graph Catalog
Graph Name bar1.gr line1.gr pie1.gr scatt1.gr
Owner
Description
demo demo demo demo
basic bar chart basic line chart basic pie chart basic scatter chart -71-
Position cursor on the name of the graph you wish to select, then use the menu to choose the appropriate operation.
CreateBlank Destroy Edit Rename MoreInfo Sketch Profile MapData Plot Abb. 5-7 VIGRAPH-frame Das in Abbildung 5-7 dargestellte frame enthält vier existierende Grafikspezifikationen: bar1.gr, line1.gr, pie1.gr und scatt1.gr, die als Demonstrationsbeispiele gemeinsam mit dem System ausgeliefert werden. Die Spezifikation bar1.gr ist in der Form eines Balkendiagramms, line1.gr in Form einer Liniengrafik, pie1.gr in Form eines Kuchen- und scatt1.gr in Form eines Punktediagramms dargestellt. Graphic Catalog-frame enthält folgende Funktionen: Funktion CreateBlan k Destroy Edit Rename MoreInfo
Sketch Profile
MapData Plot
Bedeutung unterstützt die Erstellung einer ganz neuen Grafikspezifikation. löscht eine existierende Grafikspezifikation. editiert eine existierende Grafikspezifikation. benennt eine existierende Grafikspezifikation um. gibt die zusätzliche Information (Erstellungsdatum, Kommentar usw.) über eine existierende Grafikspezifikation aus. zeichnet eine kleine Grafik zu Anschauungszwecken in der rechten unteren Bildschirmecke. gibt alle VIGRAPH-Parameter am Bildschirm aus. Der Benutzer hat anschließend die Möglichkeit, die Parameter zu ändern. erlaubt dem Benutzer, Daten für eine Grafikspezifikation zu definieren. zeichnet die Grafik für die ausgewählte Spezifikation.
An einem einfachen Beispiel werden wir die Benutzung von INGRES/GRAPHS zeigen. Das Beispiel gibt, mit Hilfe eines Balkendiagramms, die Mittel aller Projekte aus. Durch die Auswahl der Funktion Edit für die Spezifikation bar1.gr in der Graph Catalog-Maske und der anschließenden Auswahl der Funktion MapData erscheint das Data Mapping-frame in Abbildung 5-8.
VIGRAPH - Data Mapping Specification Table or view to graph : projekt Horizontal axis(X) : pr_nr
Available Columns Column Name -72-
Format
Vertical axis(Y) : mittel Optional series column(Z) : sort : yes
ListTables
IQUEL
ISQL
pr_nr
c(4)
pr_name
c(20)
mittel
f8
Plot
Abb. 5-8 Data Mapping-frame für die Tabelle projekt Weil wir die Mittel aller Projekte aus der Tabelle projekt sehen wollen, muß zuerst die Tabelle projekt angegeben werden. Im Falle eines Balkendiagramms kennzeichnet die X-Koordinate den Balkennamen, während die Y-Koordinate die Balkenhöhe definiert. (Die Z-Koordinate definiert den Namen der Teilbalken.) In unserem Beispiel ist die Spalte pr_nr die X-, mittel die Y-Koordinate, während die Z-Koordinate keine Angabe enthält, weil keine Teilbalken in Grafiken gebildet werden. Nach dem Speichern der so geänderten Spezifikation und der Auswahl der Funktion Plot wird das Balkendiagramm (Abb. 5-9) für die Mittel aller Projekte ausgegeben.
200000
100000
0 P1
P2
P3
Abb. 5-9 Balkendiagramm für die Mittel aller Projekte
Aufgaben. A.5.1
Erstellen Sie die Liste aller Projekte, in denen einzelne Mitarbeiter arbeiten, gruppiert nach Mitarbeiternamen. Am Anfang jeder Gruppe soll der Name und der Vorname des Mitarbeiters ausgegeben werden, gefolgt von den einzelnen Daten jedes Projektes (Projektnummer, Projektname und Aufgabe des Mitarbeiters in diesem Projekt). -73-
A.5.2
A.5.3
Erstellen Sie die Liste aller Mitarbeiter, gruppiert nach dem Standort ihrer Abteilung. Die Liste soll die Personalnummer und den Namen jedes Mitarbeiters enthalten. Erstellen Sie die Liste aller Mitarbeiter, gruppiert nach den Namen der Abteilungen, denen sie angehören. Die Liste soll Namen, Vornamen und Projektaufgabe jedes Mitarbeiters enthalten.
-74-
Datendefinition Datendefinition...............................................................................................................................75 6
Datendefinition ........................................................................................................................75 6.1 Erstellen der Objekte 6.2 Löschen der Objekte 78 Aufgaben 78
75
5 Datendefinition In diesem Kapitel werden wir alle SQL-Anweisungen für die Datendefinition beschreiben. Zuerst werden alle Datendefinitionsanweisungen für logische Objekte in zwei Gruppen eingeteilt und erläutert. Die erste Gruppe beinhaltet die Anweisungen zum Erstellen und die zweite die Anweisungen zum Löschen der logischen Objekte.
5.1
Erstellen der Objekte Die Organisation der Datenwerte einer Datenbank basiert auf einer Hierarchie verschiedener Objekte. Dazu gehören Tabellen, Spalten, Reihen, Indexe und die Datenbank selbst. Alle diese Objekte werden mit den Anweisungen für die Datendefinition erzeugt. In Kapitel 3 haben wir die Erstellung von Tabellen mit Hilfe von frames gezeigt. In diesem Kapitel wird die Erstellung der Tabelle und weiterer logischer Objekte mit Hilfe von SQL-Anweisungen erklärt. Das erste Objekt, das erzeugt werden muß, ist die Datenbank. Das Erzeugen der Datenbank wird mit Hilfe des Dienstprogramms createdb durchgeführt. Dieses Dienstprogramm ist in Kapitel 15 beschrieben. Mit der SQL-Anweisung CREATE TABLE wird eine Tabelle mit allen dazugehörigen Spalten und deren Datentypen erzeugt. Die Syntax dieser Anweisung hat zwei Formen: (1) CREATE TABLE tab_nam(sp_name1 sp_typ1[,sp_name2 sp_typ2,..]) [with-klausel] (2) CREATE TABLE tab_nam(sp_name1 sp_typ1[,sp_name2 sp_typ2,..]) AS select_anw [with-klausel] tab_nam ist der Name der neuerstellten Tabelle. Die Tabelle kann ab der Version 6.3 maximal 300 Spalten haben. Jede Reihe der Tabelle darf maximal 2008 Byte lang sein. Der Name kann gekennzeichnet sein. Vor dem Tabellennamen kann also der Name -75-
des Benutzers, der die Tabelle erstellt hat, stehen. Der Tabellenname darf nicht mit dem Zeichenpaar "ii" beginnen, weil in INGRES das Präfix "ii" für systeminterne Dateien reserviert ist. sp_name1 ist der Spaltenname; er muß innerhalb der Tabelle, in der sich die Spalte befindet, eindeutig sein. Dieser Name kann auch dadurch gekennzeichnet sein, daß man den Tabellennamen vor den Spaltennamen setzt: tab_nam.sp_name1. Das Kennzeichnen eines Spaltennamens ist bei den Abfragen notwendig, bei denen Tabellen, die gleichnamige Spalten enthalten, miteinander verknüpft werden. sp_typ1 bezeichnet den Datentyp und das Verhalten des Systems während des Einfügens neuer Werte. Die möglichen Datentypen sind in Kapitel 2 beschrieben. Beim Einfügen der Werte existieren drei mögliche Angaben für den Fall, daß für eine Spalte kein Wert explizit angegeben ist. Dies sind: - WITH NULL, - NOT NULL WITH DEFAULT und - NOT NULL NOT DEFAULT . WITH NULL besagt, daß der NULL-Wert implizit der Spalte zugewiesen wird. Bei NOT NULL WITH DEFAULT wird der Standardwert (0 für Spalten mit einem numerischen bzw. das Leerzeichen für Spalten mit einem alphanumerischen Datentyp) eingefügt. NOT NULL NOT DEFAULT verursacht die Ausgabe einer Fehlermeldung, falls kein Wert explizit angegeben ist. Die Voreinstellung ist WITH NULL. Falls NOT NULL angegeben ist, wird NOT NULL NOT DEFAULT angenommen. with-klausel beschreibt unterschiedliche Eigenschaften einer Tabelle. Diese Klausel fängt immer mit dem Schlüsselwort WITH und einer der folgenden Angaben an: LOCATION = (...) , [NO]JOURNALING und [NO]DUPLICATES. Die Angabe LOCATION beschreibt ein oder mehrere Plattenspeicherbereiche, in denen die erstellte Tabelle gespeichert wird. JOURNALING verursacht die Sicherung des ursprünglichen Inhalts aller modifizierten Reihen nach einer Datenmanipulationsanweisung, während NOJOURNALING dies unterläßt. DUPLICATES erlaubt die Existenz identischer Reihen in einer Tabelle, während NODUPLICATES dies untersagt. Die zweite Form der CREATE TABLE-Anweisung unterscheidet sich von der ersten durch die Existenz einer SELECT-Anweisung, mit der die erstellte Tabelle zusätzlich mit den Reihen aus einer, in der SELECT-Anweisung angegebenen Tabelle geladen wird. In einer Datenbank können mehrere gleichnamige Tabellen existieren, falls sie von verschiedenen Benutzern erstellt wurden. INGRES unterscheidet zwischen öffentlichen und privaten Tabellen. Die öffentlichen Tabellen werden von dem Datenbankadministrator und die privaten von den einzelnen Benutzern erstellt. Auf eine private Tabelle -76-
kann grundsätzlich nur der Benutzer, der sie erstellt hat, zugreifen. Die Tabellen der Beispieldatenbank aus Kapitel 1 können mit der CREATE TABLE-Anweisung auf folgende Weise erstellt werden: Beispiel 6.1 CREATE TABLE abteilung (abt_nr CHAR(4) NOT NULL , abt_name CHAR(20) NOT NULL, stadt CHAR(15)); CREATE TABLE mitarbeiter (m_nr INTEGER NOT NULL , m_name CHAR(20) NOT NULL, m_vorname CHAR(20) NOT NULL, abt_nr CHAR(4)); CREATE TABLE projekt (pr_nr CHAR(4) NOT NULL , pr_name CHAR(25) NOT NULL, mittel FLOAT8); CREATE TABLE arbeiten (m_nr INTEGER NOT NULL , pr_nr CHAR(4) NOT NULL, aufgabe CHAR(15), einst_dat DATE); Hinweis. Alle interaktiven SQL-Anweisungen, die in diesem Buch als Beispiele gegeben sind, können bei INGRES entweder mit dem interaktiven SQL-System isql oder mit dem Dienstprogramm sql erstellt und ausgeführt werden. isql ist eine frame-basierte INGRES-Komponente, die entweder mit dem Betriebssystemkommando isql oder durch die Auswahl der Funktion Queries und der Unterfunktion sql im INGRES/MENU aufgerufen wird. Das Dienstprogramm sql hat dieselbe Funktionalität wie isql , ist aber batch-orientiert und damit weniger benutzerfreundlich. Dieses Dienstprogramm kann mit dem Betriebssystemkommando sql aufgerufen werden. Mit der Anweisungsfolge in Beispiel 6.1 werden vier Tabellen der Beispieldatenbank erstellt. Das Zeichen ";" dient als Begrenzer zwischen den SQL-Anweisungen. Zusätzlich zu den schon erwähnten Tabellen, die auch als Benutzer- bzw. Basistabellen bezeichnet werden, gibt es spezielle Tabellen, die Views heißen. Die Basistabellen existieren physikalisch, während Views virtuelle Tabellen darstellen, die physikalisch nicht existieren und immer aus der (den) darunterliegenden Tabelle(n) abgeleitet werden. Die Anweisung CREATE VIEW erzeugt ein neues View aus einer schon existierenden Basistabelle mittels einer SELECT-Anweisung. Weil die SELECT-Anweisung die Basis für CREATE VIEW ist, gehört CREATE VIEW eher zu den Anweisungen für Datenmanipulationen als zu den Anweisungen für -77-
Datendefinition. Aus diesem Grund werden wir Views erst dann behandeln, wenn alle Anweisungen für Datenmanipulation definiert und erklärt sind. Kapitel 11 ist ausschließlich dem Thema Views gewidmet. CREATE INDEX ist die Anweisung zur Erstellung der Indexe. Der wichtigste Grund für die Erstellung eines Index steht mit den Abfragen in Zusammenhang. Durch die Existenz eines Index können die Antwortzeiten einer Abfrage wesentlich beschleunigt werden. CREATE INDEX hat folgende Form CREATE [UNIQUE] INDEX ind_name ON tab_name (sp_1 [ASC|DESC] [sp_2 [ASC|DESC],...]) [with-klausel]; Die ganze Problematik der Indexvergabe wird in Kapitel 12 ausführlich behandelt. Im Unterschied zu den meisten anderen relationalen Datenbanksystemen unterstützt INGRES keine SQL-Anweisung zur Änderung der Struktur von Datenbankobjekten. Eine Änderung der Speicherstruktur der Objekte kann mit der Anweisung MODIFY durchgeführt werden, die in Kapitel 12 beschrieben ist.
5.2
Löschen der Objekte Alle SQL-Anweisungen, die das Löschen der Objekte einer Datenbank veranlassen, haben folgende allgemeine Form: DROP objekt obj_name; wobei objekt ein Objekt der Datenbank ist und obj_name den Namen dieses Objektes darstellt. Mit der Anweisung DROP TABLE tab_name; wird eine existierende Tabelle gelöscht. Das Löschen einer Tabelle bedeutet, daß sie aus dem Systemkatalog entfernt wird. Alle Reihen und Indexe, die dieser Tabelle angehören, werden ebenso gelöscht. Mit der Anweisung DROP INDEX ind_name; wird ein existierender Index gelöscht. Mit der Anweisung DROP VIEW view_name; wird ein View gelöscht.
Aufgaben A.6.1
In Beispiel 6.1 sind einige Spalten der vier erstellten Tabellen mit der NOT NULL-Angabe definiert. Für welche Spalten ist diese Angabe unbedingt erforderlich und für welche nicht? A.6.2 Warum sind die Spalten m_nr und abt_nr als CHAR-Werte (und nicht als numerische Werte) definiert? A.6.3 Erstellen Sie die Tabelle systeme , die folgende Spalten -78-
A.6.4
enthält: sys_name - alphanumerisch, max. 15 Zeichen version - Ganzzahl hersteller - alphanumerisch, max. 20 Zeichen ort - alphanumerisch, max. 20 Zeichen Beschreiben Sie genau, was passiert, wenn eine Tabelle mit der DROP TABLE-Anweisung gelöscht wird.
-79-
Einfache Abfragen Einfache Abfragen..........................................................................................................................80 7
Einfache Abfragen...................................................................................................................80 7.1 Grundform der SELECT-Anweisung 80 7.2 Die WHERE-Klausel 82 7.2.1 Boolesche Funktionen 83 7.2.2 Die Operatoren IN und BETWEEN 86 7.2.3 Der NULL-Operator 88 7.2.4 Der Operator LIKE 89 7.3 Einfache Unterabfragen 91 7.3.1 Unterabfrage und Vergleichsoperatoren 7.3.2 Unterabfragen und IN-Operator 92 7.3.3 Die Operatoren ANY und ALL 93 7.3.4 Der Operator EXISTS 95 7.4 Die GROUP BY-Klausel 95 7.5 Aggregatfunktionen 96 7.5.1 Die Funktione n MIN und MAX 97 7.5.2 Die Funktion SUM 98 7.5.3 Die Funktion AVG 99 7.5.4 Die Funktion COUNT 99 7.6 Die HAVING-Klausel 100 7.7 Die ORDER BY-Klausel 101 7.8 Der Mengenoperator UNION 103 Aufgaben 106
91
6 Einfache Abfragen In den nächsten beiden Kapiteln werden wir die wichtigste SQL-Anweisung, nämlich SELECT vorstellen. Dieses Kapitel dient der Einführung der SELECT-Anweisung und der Erläuterung einfacher Abfragen. Jede Klausel der SELECT-Anweisung wird separat erklärt und mit Hilfe der Beispieldatenbank dargestellt. In dem zweiten Teil des Kapitels werden die Aggregatfunktionen und der Operator UNION beschrieben.
6.1
Grundform der SELECT-Anweisung Mit der SELECT-Anweisung werden Datenwerte aus einer Datenbank ausgewählt. Sie können aus einer oder aus mehreren, miteinander verbundenen Tabellen ausgewählt werden. Das Ergebnis einer solchen Auswahl ist erneut eine Tabelle, die -80-
keine, eine oder mehrere Reihen und eine oder mehrere Spalten hat. Die SELECT-Anweisung hat folgende allgemeine Form: SELECT [ALL|DISTINCT] *|{ausdr_1 AS alias_name1 },... FROM tab1 [tab_alias1] [,tab_2 [tab_alias2],...] [WHERE bedingung_1] [GROUP BY ausdr_3,...] [HAVING bedingung_2] [ORDER BY ausdr_5 [ASC|DESC],...]; Die einzelnen Klauseln werden wir im Verlauf dieses Kapitels vorstellen und anhand von Beispielen praktisch erläutern. Wie aus der allgemeinen Form ersichtlich, beinhaltet die einfachste SELECT-Anweisung, neben SELECT, noch die FROM-Klausel: SELECT [ALL|DISTINCT] *|{ausdr_1 AS alias_name1} ,... FROM tab1 [tab_alias1] [,tab_2 [tab_alias2],...]; tab_1 kennzeichnet den Namen der Tabelle, aus der Daten ausgewählt werden. tab_alias1 stellt den Aliasnamen der Tabelle tab1 dar. In einer SELECT-Anweisung können mehrere Tabellen angegeben werden. In diesem Kapitel wird nur die Auswahl der Daten aus einer Tabelle behandelt. Die Verknüpfung mehrerer Tabellen in einer SELECT-Anweisung wird Thema von Kapitel 8 sein. Falls die SELECT-Anweisung nur den Namen einer Tabelle beinhaltet, werden mit ihr alle Reihen und eine oder mehrere Spalten dieser Tabelle ausgewählt. Das Zeichen "*" kennzeichnet alle Spalten aller in der FROM-Klausel angegebenen Tabellen, während die explizite Auswahl einzelner Spaltennamen durch die Angabe ausdr_1, ... erfolgt. Die Auswahl der Spalten in einer SELECT-Anweisung wird Projektion und die Auswahl der Reihen Selektion genannt. Die Angabe AS alias_name1 kann jedem Ausdruck, der Bezug zu einer einzelnen Tabellenspalte hat, zugewiesen werden. In diesem Fall wird alias_name1 als Spaltenüberschrift des Auswahlergebnisses verwendet. Beispiel 7.1 Wählen Sie alle Reihen der Tabelle abteilung aus. SELECT * FROM abteilung; Das Ergebnis ist: abt_nr a1 a2 a3
abt_name Beratung Diagnose Freigabe
stadt Muenchen Muenchen Stuttgart
(3 rows) Mit dieser Anweisung werden alle Reihen und alle Spalten der Tabelle abteilung ausgewählt. Hinweis. SELECT * liefert die Spalten einer Tabelle in der Reihenfolge wie sie in der CREATE TABLE-Anweisung definiert wurden. Die -81-
Spaltennamen bilden die _berschrift. Beispiel 7.2 SELECT abt_nr, abt_name, stadt FROM abteilung; Das Ergebnis ist: abt_nr a1 a2 a3
abt_name Beratung Diagnose Freigabe
stadt Muenchen Muenchen Stuttgart
(3 rows) Das Beispiel 7.2 liefert dasselbe Ergebnis wie die SELECT-Anweisung in Beispiel 7.1.
6.2
Die WHERE-Klausel Die einfachste Form der SELECT-Anweisung wird in der Praxis nicht so oft benutzt, wie die Form, in der eine oder mehrere Bedingungen existieren. Die Bedingungen werden mit Hilfe der WHERE-Klausel definiert, die die auszuwählenden Reihen bestimmt. Beispiel 7.3 Finden Sie die Namen und Nummern aller Abteilungen, die in München ihren Sitz haben. SELECT abt_name, abt_nr FROM abteilung WHERE stadt = 'Muenchen'; Das Ergebnis ist: abt_name Beratung Diagnose
abt_nr a1 a2
(2 rows) Die Bedingung in einer WHERE-Klausel muß nicht unbedingt auf Gleichheit geprüft werden. SQL kennt folgende Vergleichsoperatoren: Operator = <> oder != oder ^= > < >= <=
Funktion gleich nicht gleich groesser als kleiner als groesser gleich kleiner gleich
Beispiel 7.4 Nennen Sie die Namen und Vornamen aller Mitarbeiter, deren -82-
Personalnummer größer oder gleich 15000 ist. SELECT m_name, m_vorname FROM mitarbeiter WHERE m_nr >= 15000; Das Ergebnis ist: m_name Keller Mueller Probst Mozer
m_name Hans Gabriele Andreas Sibille
(4 rows) In der WHERE-Klausel können auch Ausdrücke erscheinen. Beispiel 7.5 Finden Sie alle Projekte, deren Finanzmittel mehr als 60000$ betragen. Der augenblickliche Kurs soll bei 0,55 Dollar für 1 DM liegen. SELECT pr_name FROM projekt WHERE mittel * 0.55 > 60000; Das Ergebnis ist: pr_name Apollo Merkur (2 rows) Beim Vergleich von CHAR- bzw. VARCHAR-Ausdrücken werden die einzelnen Zeichen auf Grund der ASCII- Tabelle verglichen. Ein Zeichen ist kleiner als ein anderes, falls es in der ASCII-Tabelle vor diesem Zeichen steht. Gleichermaßen ist ein Zeichen größer als ein anderes, falls es hinter diesem Zeichen in der ASCII-Tabelle steht. Die numerischen Werte werden in der üblichen mathematischen Weise verglichen. Beim Vergleich von Ausdrücken vom Typ DATE ist ein Datum kleiner als ein anderes, wenn es älter als dieses ist. Kommen in einer Spalte NULL-Werte vor und beinhaltet die WHERE-Klausel eine Bedingung mit dieser Spalte, werden alle NULL-Werte beim Vergleich außer acht gelassen. (Im weiteren Verlauf dieses Kapitels werden wir zeigen, wie man explizit die NULL-Werte auswählen kann.) 6.2.1 Boolesche Funktionen Boolesche Funktionen sind: Konjunktion ("logisches UND"), Disjunktion ("logisches ODER") und Negation. Diese Funktionen werden durch die Symbole AND, OR und NOT dargestellt. Das Verhalten der Operatoren AND, OR und NOT haben wir schon mit Hilfe der Wahrheitstabellen in Kapitel 2 gezeigt. -83-
Beispiel 7.6 Gesucht werden Personalnummer, Projektnummer und Aufgabe der Mitarbeiter, die im Projekt p2 Sachbearbeiter sind. SELECT m_nr, pr_nr, aufgabe FROM arbeiten WHERE pr_nr = 'p2' AND aufgabe = 'Sachbearbeiter'; Das Ergebnis ist: m_nr pr_nr aufgabe 25348 p2 Sachbearbeiter 28559 p2 Sachbearbeiter (2 rows) Beim AND-Operator werden nur die Reihen ausgewählt, bei denen die beiden Bedingungen, die AND verbindet, erfüllt sind. Beispiel 7.7 Gesucht wird die Personalnummer der Mitarbeiter, die entweder im Projekt p1 oder p2 oder in beiden arbeiten. SELECT m_nr FROM arbeiten WHERE pr_nr = 'p1' OR pr_nr = 'p2'; Das Ergebnis lautet: m_nr 10102 25348 18316 29346 9031 28559 28559 29346 (8 rows) Im Ergebnis des Beispiels 7.7 sind einige Datenwerte der Spalte m_nr mehrfach vorhanden. Falls jeder Datenwert dieser Spalte nur einmal im Ergebnis vorkommen sollte, muß die DISTINCT-Angabe benutzt werden: SELECT DISTINCT m_nr FROM arbeiten WHERE pr_nr = 'p1' OR pr_nr = 'p2'; Das Ergebnis lautet dann: m_nr 9031 10102 18316 -84-
25348 28559 29346 (6 rows) Im Gegensatz zu AND werden bei dem Operator OR alle Reihen ausgewählt, bei denen wenigstens eine der Bedingungen erfüllt ist. Die WHERE-Klausel kann mehrere gleiche oder unterschiedliche Boolesche Operatoren beinhalten. Dabei ist zu beachten, daß AND eine größere Priorität als OR hat. Die Nichtbeachtung dieser Regel kann zu falschen Ergebnissen führen, wie wir am nächsten Beispiel sehen werden. Beispiel 7.8 SELECT * FROM mitarbeiter WHERE m_nr = 25348 AND m_name = 'Keller' OR m_vorname = 'Hans' AND abt_nr = 'a1'; SELECT * FROM mitarbeiter WHERE ((m_nr = 25348 AND m_name = 'Keller') OR m_vorname = 'Hans') AND abt_nr = 'a1'; Das Ergebnis ist: m_nr m_name 25348 Keller
m_vorname Hans
abt_nr a3
m_vorname
abt_nr
(1 row) m_nr m_name
(0 rows) Wie aus dem Ergebnis ersichtlich, haben die SELECT-Anweisungen in Beispiel 7.8 unterschiedliche Ergebnisse. In der ersten SELECT-Anweisung werden zunächst die beiden Operationen mit AND ausgewertet (zuerst die linke und dann die rechte); die Operation mit OR wird erst danach durchgeführt. In der zweiten SELECT-Anweisung werden, unter Benutzung von Klammern, die Operationen von links nach rechts abgearbeitet. Bei Verwendung mehrerer Boolescher Operatoren innerhalb einer WHERE-Klausel, ist diese in den meisten Fällen schwer verständlich. In solchen Fällen empfiehlt sich daher grundsätzlich die Benutzung von Klammern, auch in Fällen wo sie nicht unbedingt notwendig sind. Dadurch wird eine SELECT-Anweisung lesbarer. Die erste SELECT-Anweisung in Beispiel 7.8 hat durch die Benutzung von Klammern folgende Form: SELECT * -85-
FROM mitarbeiter WHERE (m_nr = 25348 AND m_name = 'Keller') OR (m_vorname = 'Hans' AND abt_nr = 'a1'); Der dritte Boolesche Operator, NOT, ändert den logischen Wert einer Bedingung. Wie man aus der Wahrheitstabelle für NOT in Kapitel 2 sehen kann, ist die Negation eines richtigen logische n Wertes falsch, eines falschen richtig und eines unbekannten Wertes wieder unbekannt. Die Negation hat von allen drei Booleschen Operatoren die höchste Priorität. Wenn in einem logischen Ausdruck alle drei Operatoren existieren, wird also zuerst die Negation, danach das logische UND und erst am Ende das logische ODER abgearbeitet. Beispiel 7.9 Nennen Sie Personalnummer und Nachnamen der Mitarbeiter, die nicht in der Abteilung a2 arbeiten. SELECT m_nr, m_name FROM mitarbeiter WHERE NOT abt_nr = 'a2'; Das Ergebnis ist: m_nr 25348 10102 18316 28559
m_name Keller Huber Mueller Mozer
(4 rows) Der Boolesche Operator NOT kann durch den Vergleichsoperator "<>" (ungleich) ersetzt werden. Beispiel 7.10 ist mit Beispiel 7.9 identisch. Beispiel 7.10 SELECT m_nr, m_name FROM mitarbeiter WHERE abt_nr <> 'a2'; 6.2.2 Die Operatoren IN und BETWEEN Mit dem IN-Operator können mehrere Konstanten angegeben werden, auf die dann die Suche beschränkt wird. Es werden lediglich die Reihen ausgewählt, bei denen der Datenwert der Spalte in der WHERE-Klausel einer der angegebenen Konstanten entspricht. Beispiel 7.11 Finden Sie alle Mitarbeiter, deren Personalnummer entweder 29346, 28559 oder 25348 ist. SELECT * FROM mitarbeiter WHERE m_nr IN (29346, 28559, 25348); Das Ergebnis ist: m_nr
m_name
m_vorname -86-
abt_nr
25348 Keller 29346 Probst 28559 Mozer
Hans Andreas Sibille
a3 a2 a1
(3 rows) Der IN-Operator kann durch eine Reihe von OR-Operatoren ersetzt werden. Beispiel 7.12 ist mit Beispiel 7.11 identisch. Beispiel 7.12 SELECT * FROM mitarbeiter WHERE m_nr = 29346 OR m_nr = 28559 OR m_nr = 25348; Der IN-Operator kann auch gemeinsam mit dem Booleschen Operator NOT verwendet werden. In diesem Fall werden nur die Reihen ausgewählt, für die der Datenwert der Spalte in der WHERE-Klausel keine von den angegebenen Konstanten beinhaltet. Beispiel 7.13 Nennen Sie alle Mitarbeiter, deren Personalnummer weder 10102 noch 9031 ist. SELECT * FROM mitarbeiter WHERE m_nr NOT IN (10102, 9031); Das Ergebnis ist: m_nr 25348 18316 29346 2581 28559
m_name Keller Mueller Probst Kaufmann Mozer
m_vorname Hans Gabriele Andreas Brigitte Sibille
abt_nr a3 a1 a2 a2 a1
(5 rows) Der IN-Operator kann auch für Unterabfragen verwendet werden. Diese Form des IN-Operators werden wir später in diesem Kapitel erörtern. Im Unterschied zum IN-Operator, der einzelne Werte festlegt, definiert der Operator BETWEEN immer einen Bereich, in dem dann die Werte gesucht werden, die die Bedingung erfüllen. Beispiel 7.14 Nennen Sie Namen und Mittel aller Projekte, deren finanzielle Mittel zwischen 95.000 DM und 120.000 DM liegen. SELECT pr_name, mittel FROM projekt WHERE mittel BETWEEN 95000 AND 120000; Das Ergebnis ist: pr_name
mittel -87-
Apollo Gemini
120000.000 95000.000
(2 rows) Wie aus Beispiel 7.14 ersichtlich ist, beinhaltet der Bereich auch die beiden Grenzwerte, die beim BETWEEN-Operator angegeben sind. Der BETWEEN-Operator kann auch durch Vergleichsoperatoren ersetzt werden. Beispiel 7.15 entspricht dem Beispiel 7.14. Beispiel 7.15 SELECT pr_name, mittel FROM projekt WHERE mittel <= 120000 AND mittel >= 95000; Obwohl die beiden SELECT-Anweisungen in den Beispielen 7.14 und 7.15 identisch sind, ist die Form mit dem BETWEEN-Operator vorzuziehen. Die Anweisung in 7.14 ist transparenter und deswegen leichter lesbar. Der BETWEEN-Operator kann, in gleichem Maße wie der IN-Operator, mit dem Booleschen Operator NOT gemeinsam verwendet werden. In diesem Fall werden die Reihen ausgewählt, für die der Datenwert der Spalte in der WHERE-Klausel außerhalb des angegebenen Bereichs liegt. Beispiel 7.16 Nennen Sie die Personalnummer aller Mitarbeiter, die Projektleiter sind und vor oder nach 1988 eingestellt wurden. SELECT m_nr FROM arbeiten WHERE aufgabe = 'Projektleiter' AND einst_dat NOT BETWEEN '01-jan-1988' AND '31-dec-1988'; Das Ergebnis ist: m_nr 2581 (1 row) 6.2.3 Der NULL-Operator Wie schon in Kapitel 2 erwähnt, werden NULL-Werte einer Spalte außer acht gelassen, falls in der WHERE-Klausel ausschließlich Vergleichsoperatoren verwendet worden sind. Damit auch Datenwerte, die NULL-Werte beinhalten, in einer SELECT-Anweisung ausgewählt werden können, wurde der NULL-Operator eingeführt. Die allgemeine Form dieses Operators ist: spalte_1 IS [NOT] NULL Beispiel 7.17 Finden Sie die Personal- und Projektnummer aller Mitarbeiter, die im Projekt p1 arbeiten und deren Aufgabe noch nicht -88-
festgelegt ist. SELECT m_nr, pr_nr FROM arbeiten WHERE pr_nr = 'p1' AND aufgabe IS NULL; Das Ergebnis ist: m_nr pr_nr 28559 p1 (1 row) In der Syntax des NULL-Operators befindet sich eine potentielle Fehlerquelle beim Programmieren mit SQL. Der Vergleich auf NULL bzw. NOT NULL wird nicht, wie in allen anderen Fällen mit dem Vergleichsoperator "=" durchgeführt. Das liegt an der Logik des NULL-Wertes. Dieser Wert kann nicht mit einem beliebigen Wert verglichen werden, oder ihm entsprechen. Deswegen wurde bei der Syntaxdefinition absichtlich das Verb IS eingeführt. Die Bedingung spalte_1 IS NOT NULL ist mit der Bedingung NOT (spalte_1 IS NULL) identisch. Hinweis. Die Bedingung mit dem Vergleich auf NULL bzw. NOT NULL kann richtig oder falsch, aber auf keinen Fall NULL sein! 6.2.4 Der Operator LIKE Der LIKE-Operator ist ein Vergleichsoperator, der Datenwerte einer Spalte mit einem vorgegebenen Muster vergleicht. Die allgemeine Form des LIKE-Operators ist spalte_2 [NOT] LIKE 'muster' wobei muster eine Zeichenkette darstellt, die das Vergleichsmuster definiert. Diese Zeichenkette enthält alphanumerische Zeichen, wobei zwei Zeichen eine besondere Bedeutung haben. Dies sind: % (Prozentzeichen) und _ (Unterstrich) Das Zeichen "%" kennzeichnet eine beliebige Zeichenfolge von n Zeichen, wobei n eine nichtnegative ganze Zahl ist, also auch 0 sein kann. Das Zeichen "_" kennzeichnet ein beliebiges alphanumerisches Zeichen. Jedes andere alphanumerische Zeichen kennzeichnet sich selbst. Beispiel 7.18 Finden Sie Namen und Personalnummer aller Mitarbeiter, deren Name mit dem Buchstaben "K" beginnt. SELECT m_name, m_nr FROM mitarbeiter WHERE m_name LIKE 'K%'; -89-
Das Ergebnis ist: m_name Keller Kaufmann
m_nr 25348 2581
(2 rows) Beispiel 7.19 Nennen Sie Namen, Vornamen und Personalnummer aller Mitarbeiter, deren Vorname als zweiten Buchstaben "a" hat. SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE m_vorname LIKE '_a%'; Das Ergebnis ist: m_nr 25348 18316 9031
m_name Keller Mueller Meier
m_vorname Hans Gabriele Rainer
(3 rows) Der Benutzer kann mit der Angabe ESCAPE selbst festlegen, welches Zeichen als Entwertungszeichen verwendet wird. Die Angabe spalte_3 LIKE '%z_%' ESCAPE 'z' sucht alle Datenwerte in der Spalte spalte_3, die das Zeichen "_" (Unterstrich) an irgendeiner Stelle der Zeichenkette enthalten. Die Bedingung spalte_4 NOT LIKE 'muster' ist identisch mit NOT (spalte_4 LIKE 'muster') Beispiel 7.20 Nennen Sie alle Mitarbeiter, deren Name nicht mit "mann" endet. SELECT * FROM mitarbeiter WHERE m_name NOT LIKE '%mann'; Das Ergebnis ist: m_nr 25348 10102 18316 29346 9031 28559
m_name Keller Huber Mueller Probst Meier Mozer
m_vorname Hans Petra Gabriele Andreas Rainer Sibille
(6 rows) -90-
abt_nr a3 a3 a1 a2 a2 a1
6.3
Einfache Unterabfragen Bis jetzt wurde der Vergleich in der WHERE-Klausel immer mit einer Konstanten bzw. einem Ausdruck durchgeführt. Zusätzlich dazu ist es möglich, den Vergleich mit dem Ergebnis einer weiteren SELECT-Anweisung durchzuführen. Eine solche SELECTAnweisung, die Teil der Bedingung einer WHERE-Klausel ist, wird einfache Unterabfrage genannt. (Neben der einfachen Unterabfrage gibt es auch die korrelierten Unterabfragen, die in Kapitel 8 erklärt werden.) Jede Unterabfrage wird häufig als innere SELECT-Anweisung bezeichnet, dies im Unterschied zur äußeren SELECT-Anweisung, in der die innere eingeschlossen ist. In einer Abfrage mit einfacher Unterabfrage wird stets zunächst die innere SELECTAnweisung ausgewertet und ihr Ergebnis dann an die äußere SELECT-Anweisung weitergegeben. Eine einfache Unterabfrage kann im Zusammenhang mit folgenden Operatoren auftreten: - allen Vergleichsoperatoren; - dem IN-Operator; - dem ANY bzw. ALL-Operator und - dem EXISTS-Operator.
6.3.1 Unterabfrage und Vergleichsoperatoren Beispiel 7.21 Nennen Sie die Abteilungsnummer des Mitarbeiters, der am 15.10.1989 eingestellt wurde. SELECT abt_nr FROM mitarbeiter WHERE m_nr = (SELECT m_nr FROM arbeiten WHERE einst_dat = '15-oct-1989'); Das Ergebnis ist: abt_nr a2 (1 row) Wie aus Beispiel 7.21 ersichtlich, wird die Unterabfrage immer in Klammern eingeschlossen. Die Unterabfrage kann auch mit den anderen Vergleichsoperatoren im Zusammenhang stehen. Beispiel 7.22 Nennen Sie die Nummern aller Projekte, in welchen Mitarbeiter arbeiten, deren Personalnummer kleiner als die Nummer des Mitarbeiters namens Mueller ist. SELECT DISTINCT pr_nr -91-
FROM arbeiten WHERE m_nr < (SELECT m_nr FROM mitarbeiter WHERE m_name = 'Mueller'); Das Ergebnis ist: pr_nr p1 p3 (2 rows) Wie aus den beiden letzten Beispielen ersichtlich, darf die innere SELECT-Anweisung nur einen Wert als Ergebnis liefern, falls sie im Zusammenhang mit einem Vergleichsoperator erscheint. Dies ist auch logisch, weil der Vergleich mit mehreren Ergebniswerten nicht sinnvoll wäre. 6.3.2 Unterabfragen und IN-Operator Wie schon im vorherigen Abschnitt erklärt, liefert der IN-Operator als Ergebnis eine Anzahl von Konstanten, die dann mit den Datenwerten einer Spalte verglichen werden. Die Verwendung des IN-Operators mit der Unterabfrage verläuft ähnlich. Die innere SELECT-Anweisung liefert einen oder mehrere Ergebniswerte, mit denen die Datenwerte einer Spalte verglichen werden. Beispiel 7.23 Nennen Sie die Daten aller Mitarbeiter, die in München arbeiten. SELECT * FROM mitarbeiter WHERE abt_nr IN (SELECT abt_nr FROM abteilung WHERE stadt = 'Muenchen'); Das Ergebnis ist: m_nr 18316 29346 9031 2581 28559
m_name Mueller Probst Meier Kaufmann Mozer
m_vorname Gabriele Andreas Rainer Brigitte Sibille
abt_nr a1 a2 a2 a2 a1
(5 rows) Jede Unterabfrage kann eine weitere Unterabfrage enthalten. In diesem Fall sprechen wir von geschachtelten Unterabfragen. Die Anzahl der geschachtelten Unterabfragen ist von der Implementierung und von dem Speicherplatz abhängig, der -92-
für eine Abfrage (mit allen dazugehörigen Unterabfragen) festgelegt ist. Bei allen geschachtelten, einfachen Unterabfragen wird zunächst die innerste SELECT-Anweisung abgearbeitet und dann das Ergebnis der nächsthöheren SELECT-Anweisung übergeben. So wird weiter verfahren, bis die äußere SELECT-Anweisung erreicht und abgearbeitet wird. Beispiel 7.24 Nennen Sie die Namen aller Mitarbeiter, die im Projekt Apollo arbeiten. SELECT m_name FROM mitarbeiter WHERE m_nr IN ( SELECT m_nr FROM arbeiten WHERE pr_nr = ( SELECT pr_nr FROM projekt WHERE pr_name = 'Apollo')) Das Ergebnis ist: m_name Huber Probst Meier Mozer (4 rows) Die innerste SELECT-Anweisung wählt den Datenwert der Spalte pr_nr (p1) der Tabelle projekt aus, dessen Projektname Apollo lautet. Die mittlere SELECT-Anweisung vergleicht anschliessend diesen Wert mit den Datenwerten der gleichnamigen Spalte in der Tabelle arbeiten. Das Ergebnis der mittleren SELECT-Anweisung beinhaltet mehrere Werte, die mit Hilfe des IN-Operators mit den Datenwerten der Spalte m_nr in der Tabelle mitarbeiter verglichen werden. 6.3.3 Die Operatoren ANY und ALL Die Operatoren ANY und ALL werden immer im Zusammenhang mit einem Vergleichsoperator benutzt. Die allgemeine Form beider Operatoren ist: ausdruck vergl_op [ANY | ALL] (unterabfrage) , wobei vergl_op einen der Vergleichsoperatoren darstellt. Beim ANY-Operator wird die Bedingung als wahr ausgewertet, falls die Unterabfrage wenigstens eine Reihe als Ergebnis liefert, die dem angegebenen Vergleich entspricht. Beispiel 7.25 Finden Sie Personalnummer, Projektnummer und Aufgabe der Mitarbeiter, deren Eintrittsdatum in das entsprechende Projekt nicht das älteste ist. SELECT m_nr, pr_nr, aufgabe -93-
FROM arbeiten WHERE einst_dat > ANY (SELECT einst_dat FROM arbeiten); Das Ergebnis ist: m_nr 10102 10102 25348 18316 2581 9031 28559 28559 9031 29346
pr_nr p1 p3 p2 p2 p3 p1 p1 p2 p3 p1
aufgabe Projektleiter Gruppenleiter Sachbearbeiter Projektleiter Gruppenleiter Sachbearbeiter Sachbearbeiter Sachbearbeiter
(10 rows) In Beispiel 7.25 wird jeder Datenwert der Spalte einst_dat mit allen Datenwerten derselben Spalte (auch mit sich selbst) verglichen. Für alle Datenwerte, abgesehen von einem, gilt, daß beim Vergleich die Bedingung wenigstens in einem Fall erfüllt ist. Die Reihe mit dem ältesten Eintrittsdatum wird nicht ausgewählt, weil für sie der Vergleich nicht erfüllt ist. Beispiel 7.26 Nennen Sie die Namen und Vornamen aller Mitarbeiter, die im Projekt p1 arbeiten. SELECT m_name, m_vorname FROM mitarbeiter WHERE m_nr = ANY (SELECT m_nr FROM arbeiten WHERE pr_nr = 'p1'); Das Ergebnis ist: m_name Huber Probst Meier Mozer
m_vorname Petra Andreas Rainer Sibille
(4 rows) Beim ALL-Operator wird die Bedingung als wahr ausgewertet, wenn alle Ergebnisse der Unterabfrage dem angegebenen Vergleich entsprechen. Beispiel 7.27 Nennen Sie die Aufgabe des Mitarbeiters, der die kleinste Personalnummer hat. -94-
SELECT aufgabe FROM arbeiten WHERE m_nr <= ALL (SELECT m_nr FROM mitarbeiter); Das Ergebnis ist: aufgabe Projektleiter (1 row) Neben den Operatoren ANY und ALL existiert noch ein Operator - SOME. Dieser Operator ist gleichbedeutend mit ANY. Die Operatoren ALL und ANY haben einige Nachteile, die gegen ihre Verwendung bei Unterabfragen sprechen. Der größte Nachteil ist, daß die korrekte semantische Bedeutung beider Operatoren verwechselt werden kann. Auf der anderen Seite können die beiden Operatoren leicht durch den EXISTS-Operator ersetzt werden, wie wir im nächsten Kapitel zeigen werden. Deswegen empfehlen wir, die Verwendung beider Operatoren auf ein Minimum zu reduzieren. 6.3.4 Der Operator EXISTS Der EXISTS-Operator prüft das Ergebnis einer Unterabfrage; falls die Unterabfrage zumindest eine Reihe als Ergebnis liefert, wird die Bedingung als wahr ausgewertet. Falls die Unterabfrage keine Reihe als Ergebnis liefert, wird die Bedingung in der WHERE-Klausel als falsch ausgewertet. Die allgemeine Form des EXISTS-Operators ist: [NOT] EXISTS (unterabfrage) Die Beispiele zum EXISTS-Operator finden Sie in Kapitel 8.
6.4
Die GROUP BY-Klausel Die GROUP BY-Klausel definiert eine oder mehrere Spalten als Gruppenkennzeichen, wonach die Reihen gruppiert werden. Beispiel 7.28 Welche Aufgaben üben die Mitarbeiter der Firma aus? SELECT aufgabe FROM arbeiten GROUP BY aufgabe; Das Ergebnis ist: aufgabe Projektleiter Gruppenleiter Sachbearbeiter (4 rows) Wie wir aus Be ispiel 7.28 ersehen können, baut die GROUP -95-
BY-Klausel für jeden unterschiedlichen Datenwert (auch für den NULL-Wert) der genannten Spalte eine Gruppe auf. Falls die GROUP BY-Klausel angegeben ist, muß jede Spalte in der Projektion auch in der GROUP BY-Klausel erscheinen. Zudem darf die Projektion nur alphanumerische Konstanten und Aggregatfunktionen anderer Spalten enthalten, die in der GROUP BY-Klausel nicht erscheinen müssen. In der GROUP BY-Klausel können auch mehrere Spalten angegeben werden. In diesem Fall wird die Gruppierung auf Grund aller angegebenen Spalten durchgeführt. Beispiel 7.29 Gruppieren Sie die Mitarbeiter nach Projektnummer und Aufgabe. SELECT pr_nr, aufgabe FROM arbeiten GROUP BY pr_nr, aufgabe; Das Ergebnis ist: pr_nr p1 p1 p1 p1 p2 p2 p3 p3 p3
aufgabe Gruppenleiter Projektleiter Sachbearbeiter Sachbearbeiter Gruppenleiter Projektleiter Sachbearbeiter
(9 rows) Wie aus dem Ergebnis ersichtlich, gibt es insgesamt 9 Gruppen, in denen Projektnummer und Aufgabe unterschiedlich sind. Die beiden Gruppen, die mehr als eine Reihe beinhalten, sind p2 Sachberabeiter 25348, 28559 p2 NULL 18316, 29346 Die Reihenfolge der Spalten in der GROUP BY-Klausel muss nicht unbedingt der Reihenfolge der Spalten in der Projektion entsprechen.
6.5
Aggregatfunktionen Zu den Aggregatfunktionen gehören folgende Funktionen: - MIN; - MAX; - SUM; - AVG und - COUNT. Alle Aggregatfunktionen werden stets auf eine Gruppe von Reihen angewendet, wobei die Gruppe auch alle Reihen -96-
einer Tabelle umfassen kann. Als Ergebnis liefert jede Aggregatfunktion einen konstanten Wert, der in einer separaten Spalte im Ergebnis erscheint. Die Aggregatfunktionen können in einer SELECT-Anweisung mit oder ohne GROUP BY-Klausel erscheinen. Falls die SELECT-Anweisung die GROUP BY-Klausel nicht enthält, dürfen in der Projektion nur die Spaltennamen angegeben werden, die als Parameter der Aggregatfunktion erscheinen. Deswegen ist das folgende Beispiel falsch: Beispiel 7.30 SELECT m_name, MIN(m_nr) FROM mitarbeiter; Alle Spaltennamen, die nicht Parameter der Aggregatfunktion sind, dürfen in der SELECT-Anweisung erscheinen, falls sie zur Gruppierung verwendet werden. Für alle Aggregatfunktionen kann zwischen den Angaben - ALL und - DISTINCT gewählt werden. ALL kennzeichnet alle Werte einer Spalte und stellt die Voreinstellung dar. Sie gilt also, wenn weder ALL noch DISTINCT angegeben sind. DISTINCT kennzeichnet alle unterschiedlichen Werte einer Spalte. 6.5.1 Die Funktionen MIN und MAX Die Aggregatfunktionen MIN und MAX berechnen den kleinsten bzw. den größten Datenwert einer Spalte. Beispiel 7.31 Nennen Sie die kleinste Personalnummer eines Mitarbeiters. SELECT MIN (m_nr) FROM mitarbeiter; Das Ergebnis ist: col1 2581 (1 row) Das Ergebnis dieses Beispiels ist nicht sehr aussagekräftig. Wir wissen nicht, wie der Mitarbeiter mit der kleinsten Personalnummer heißt. Wie schon gezeigt, ist die direkte Angabe des Mitarbeiternamens in der SELECT-Anweisung nicht erlaubt. Damit der Name auch angegeben werden kann, muß eine Unterabfrage mit der Funktion MIN aufgebaut werden. Beispiel 7.32 Nennen Sie Personalnummer und Namen des Mitarbeiters mit der kleinsten Personalnummer. SELECT m_nr, m_name FROM mitarbeiter WHERE m_nr = (SELECT MIN(m_nr) FROM mitarbeiter); -97-
Das Ergebnis ist: m_nr m_name 2581 Kaufmann (1 row) Beispiel 7.33 Finden Sie die Personalnummer des Projektleiters, der als letzter eingestellt wurde. SELECT m_nr FROM arbeiten WHERE einst_dat = (SELECT MAX(einst_dat) FROM arbeiten WHERE aufgabe = 'Projektleiter'); Das Ergebnis ist: m_nr 2581 (1 row) Die Funktionen MIN und MAX können auf alle alphanumerischen Werte angewendet werden. Bei den alphanumerischen Werten wird der kleinste bzw. der größte Wert durch den Vergleich der entsprechenden Zeichen nach ihrem Binärcode ermittelt. Die Angabe DISTINCT, die mehrfach vorhandene Werte in einer Spalte entfernt, hat keine Bedeutung für die Aggregatfunktionen MIN und MAX. Falls MIN bzw. MAX auf eine Spalte mit NULL-Werten angewendet wird, werden alle NULL-Werte vor der Berechnung der Aggregatfunktion entfernt. 6.5.2 Die Funktion SUM Die Aggregatfunktion SUM berechnet die Summe der Werte einer Spalte. Die Spalte muß vom numerischen Datentyp sein. Beispiel 7.34 Berechnen sie die Summe der finanziellen Mittel aller Projekte. SELECT SUM(mittel) AS summe FROM projekt; Das Ergebnis ist: summe 401500.000 (1 row) Durch die Angabe DISTINCT werden die mehrfach vorhandenen Werte in der betreffenden Spalte vor Verwendung der Funktion SUM eliminiert. Falls die Funktion SUM auf eine Spalte mit NULL-Werten angewendet wird, werden vor der Berechnung der -98-
Summe alle NULL-Werte entfernt. 6.5.3 Die Funktion AVG Die Aggregatfunktion AVG berechnet das arithmetische Mittel der Datenwerte einer Spalte. Diese Spalte muß einen numerischen Datentyp haben. Beispiel 7.35 Berechnen Sie das arithmetische Mittel der Geldbeträge, die höher als 100.000 DM sind. SELECT AVG(mittel) AS avg_mittel FROM projekt WHERE mittel > 100000; Das Ergebnis ist: avg_mittel 153250.000 (1 row) Durch die Angabe DISTINCT werden alle mehrfach vorhandenen Werte vor der Berechnung des arithmetischen Mittels aus der betreffenden Spalte eliminiert. Falls die Funktion AVG auf eine Spalte mit NULL-Werten angewendet wird, werden alle NULL-Werte vor der Berechnung des arithmetischen Mittels entfernt. 6.5.4 Die Funktion COUNT Die Aggregatfunktion COUNT hat zwei verschiedene Formen. Die erste Form sieht wie folgt aus: COUNT (DISTINCT sp_name) Sie berechnet die Anzahl der Werte der Spalte sp_name , wobei alle mehrfach vorhandenen Werte nicht berücksichtigt werden. DISTINCT muß in diesem Fall angegeben werden. Beispiel 7.36 Finden Sie heraus, wieviele verschiedene Aufgaben in jedem Projekt ausgeübt werden. SELECT pr_nr, COUNT(DISTINCT aufgabe) AS anzahl FROM arbeiten GROUP BY pr_nr; Das Ergebnis ist: pr_nr p1 p2 p3
anzahl 3 1 3
(3 rows) Die Funktion COUNT DISTINCT entfernt alle NULL-Werte aus der betreffenden Spalte. -99-
Die zweite Form der Funktion COUNT sieht folgendermaßen aus: COUNT (*) Sie berechnet die Anzahl der Reihen. Beispiel 7.37 Finden Sie heraus, wieviele Mitarbeiter in jedem Projekt arbeiten. SELECT pr_nr, COUNT(*) AS anzahl FROM arbeiten GROUP BY pr_nr; Das Ergebnis ist: pr_nr p1 p2 p3
anzahl 4 3 3
(3 rows) Im Unterschied zu der ersten Form der COUNT-Funktion, berücksichtigt COUNT(*) auch Reihen mit NULL-Werten. Beispiel 7.38 Gruppieren Sie die Reihen der Tabelle arbeiten nach den vorhandenen Aufgaben und finden Sie die Anzahl der Mitarbeiter der jeweiligen Aufgaben heraus. SELECT aufgabe, COUNT(*) AS anz_aufg FROM arbeiten GROUP BY aufgabe; Das Ergebnis ist: aufgabe anz_aufg Gruppenleiter 2 Projektleiter 2 Sachbearbeite r 4 3 (4 rows) Das Ergebnis der Funktion COUNT ist 0, falls sie auf eine leere Menge angewendet wird (d.h. das Ergebnis liefert keine Reihe).
6.6
Die HAVING-Klausel Die HAVING-Klausel hat dieselbe Funktion für die GROUP BYKlausel wie die WHERE-Klausel für die SELECT-Anweisung. Mit anderen Worten: Die HAVING-Klausel definiert die Bedingung nach der die Reihengrupen ausgewählt werden. Die allgemeine Form der HAVING-Klausel ist: HAVING bedingung wobei bedingung Aggregatfunktionen und Konstanten enthalten -100-
darf. Der Ausdruck in bedingung muß je Gruppe stets einen einzigen Wert als Ergebnis liefern. In der Praxis werden fast ausschließlich Vergleiche mit Aggregatfunktionen durchgeführt. Beispiel 7.39 Nennen Sie alle Projekte, mit denen weniger als vier Mitarbeiter befasst sind. SELECT pr_nr FROM arbeiten GROUP BY pr_nr HAVING COUNT(*) < 4; Das Ergebnis ist: pr_nr p3 (1 row) Alle Reihen der Tabelle arbeiten werden zunächst in bezug auf die Spalte pr_nr gruppiert. Die Aggregatfunktion COUNT(*) zählt alle Reihen jeder Gruppe und wählt die Gruppen aus, die weniger als vier Reihen beinhalten. Die HAVING-Klausel kann auch ohne die GROUP BY-Klausel benutzt werden, obwohl dies in der Praxis selten vorkommt. In diesem Fall wird die gesamte Tabelle als eine einzige Gruppe betrachtet.
6.7
Die ORDER BY-Klausel Die ORDER BY-Klausel definiert die Reihenfolge der Ausgabe aller ausgewählten Reihen einer SELECT-Anweisung. Diese Klausel ist optional und erscheint immer am Ende einer SELECT-Anweisung. Die allgemeine Form der ORDER BY-Klausel ist: ORDER BY {spalte | ganzzahl [ASC|DESC]},... spalte bezeichnet eine Spalte, mit der der Sortierbegriff definiert wird. Mit der Angabe von ganzzahl wird eine ganze Zahl definiert, die die Position einer Spalte kennzeichnet. ASC kennzeichnet die aufsteigende und DESC die absteigende Sortierfolge. Fehlt diese Angabe, werden die Reihen aufsteigend sortiert. Beispiel 7.40 Geben Sie Personalnummer, Namen und Vornamen aller Mitarbeiter, sortiert nach Personalnummern, an. SELECT m_nr, m_name, m_vorname FROM mitarbeiter ORDER BY m_nr; Das Ergebnis ist: m_nr 2581
m_name Kaufmann
m_vorname Brigitte -101-
9031 10102 18316 25348 28559 29346
Meier Huber Mueller Keller Mozer Probst
Rainer Petra Gabriele Hans Sibille Andreas
(7 rows) Die in der ORDER BY-Klausel erscheinende Spalte muß in der Projektion vorhanden sein. Die Spalte kann auch implizit durch die Angabe des Zeichens "*" für alle Spalten einer Tabelle vorhanden sein. Der Sortierbegriff kann, wie aus der Beschreibung der Syntax ersichtlich, mehrere Spalten beinhalten. Beispiel 7.41 Geben Sie Namen, Vornamen und Abteilungsnummer aller Mitarbeiter an, deren Personalnummer kleiner als 20000 ist. Die Ausgabe soll aufsteigend nach Namen und Vornamen der Mitarbeiter sortiert sein. SELECT m_name, m_vorname, abt_nr FROM mitarbeiter WHERE m_nr < 20000 ORDER BY m_name, m_vorname; Das Ergebnis ist: m_name Huber Kaufmann Meier Mueller
m_vorname Petra Brigitte Rainer Gabriele
abt_nr a3 a2 a2 a1
(4 rows) Jede Spalte in der ORDER BY-Klausel kann durch eine ganze Zahl ersetzt werden, die die Position dieser Spalte in der Projektion definiert. Im letzten Beispiel könnte die ORDER BY-Klausel auch folgende Form haben: ORDER BY 1,2 Die Verwendung der ganzen Zahlen in der ORDER BY-Klausel ist die einzige Alternative, falls der Sortierbegriff durch eine Aggregatfunktion definiert ist, wie das folgende Beispiel verdeutlicht. Beispiel 7.42 Finden Sie die Anzahl aller Mitarbeiter in jedem Projekt und sortieren Sie sie anschließend in absteigender Reihenfolge. SELECT pr_nr, COUNT(*) AS anzahl FROM arbeiten GROUP BY pr_nr ORDER BY 2 DESC; Das Ergebnis ist: -102-
pr_nr p1 p2 p3
anzahl 4 4 3
(3 rows) Bei INGRES-SQL werden die NULL-Werte bei aufsteigender Sortierfolge nach allen anderen Werten, und bei absteigender Sortierfolge vor allen anderen Werten ausgegeben.
6.8
Der Mengenoperator UNION Zusätzlich zu allen Formen der SELECT-Anweisung, die wir bis jetzt beschrieben haben, ist es möglich, zwei SELECT-Anweisung mit einem Mengenoperator zu verbinden. INGRES-SQL unterstützt die Mengenoperation Vereinigung. Die Vereinigung zweier Datenmengen beinhaltet alle Daten, die entweder in der ersten, oder in der zweiten oder in beiden Datenmengen enthalten sind. Der Operator für die Vereinigung der Datenmengen heißt bei SQL UNION. Die allgemeine Form des Operators UNION ist: select_1 UNION [ALL] select_2 [UNION [ALL] select_3 ...] select_1 und select_2 kennzeichnen zwei SELECT-Anweisungen, die der Operator UNION verbindet. Die Angabe ALL legt fest, daß auch mehrfach vorhandene Reihen der Ergebnistabelle ausgegeben werden sollen. Diese Angabe beim Operator UNION hat dieselbe Bedeutung wie in der Projektion einer SELECT-Anweisung. Trotzdem gibt es einen wichtigen Unterschied. In der Projektion ist die Angabe ALL Voreinstellung, d.h. die mehrfach vorhandenen Reihen der Ergebnistabelle einer SELECT-Anweisung werden ausgegeben, falls weder ALL noch DISTINCT angegeben ist. Beim UNION-Operator muß man ALL explizit angeben, damit die mehrfach vorhandenen Reihen ausgegeben werden. Damit wir auch praktische Beispiele mit der Mengenoperation Vereinigung zeigen können, werden wir eine neue Tabelle mit_erweiter erstellen. Die Tabelle mit_erweiter entspricht der Tabelle mitarbeiter bis auf eine zusätzliche Spalte wohnort. Die Spalte wohnort beschreibt den Wohnort jedes Mitarbeiters. Die Tabelle mit_erweiter hat nach der Erstellung und dem Einfügen der Daten folgende Form: mit_erweiter m_nr 25348 10102 18316 29346 9031
m_name Keller Huber Mueller Probst Meier
m_vorname Hans Petra Gabriele Andreas Rainer -103-
abt_nr a3 a3 a1 a2 a2
wohnort Muenchen Landshut Rosenheim Augsburg Augsburg
2581 28559
Kaufmann Mozer
Brigitte Sibille
a2 a1
Muenchen Ulm
Beispiel 7.43 Finden Sie alle Wohnorte der Mitarbeiter und alle Standorte der Abteilungen. SELECT wohnort FROM mit_erweiter UNION SELECT stadt FROM abteilung; Das Ergebnis ist: wohnort Augsburg Landshut Muenchen Rosenheim Stuttgart Ulm (6 rows) Damit zwei SELECT-Anweisungen mit dem UNION-Operator verbunden sein können, müssen folgende Voraussetzungen erfüllt sein: a) die Anzahl der Spalten in den beiden Projektionen muß gleich sein; b) die entsprechenden Spalten müssen denselben Typ haben. Falls die Ausgabe sortiert sein soll, darf die ORDER BY-Klausel nur in der letzten SELECT-Anweisung angegeben werden. Der Sortierbegriff in der ORDER BY-Klausel darf in diesem Fall nur mit Hilfe einer Ganzzahl definiert werden, die die Position der Spalte kennzeichnet. Beispiel 7.44 Finden Sie die Personalnummer der Mitarbeiter, die entweder der Abteilung a1 angehören oder vor dem 1.1.1988 in das Projekt eingetreten sind. Die Personalnummer sollen sortiert ausgegeben werden. SELECT m_nr FROM mitarbeiter WHERE abt_nr = 'a1' UNION SELECT m_nr FROM arbeiten WHERE einst_dat < '01-jan-1988' ORDER BY 1; Das Ergebnis ist: m_nr -104-
18316 28559 29346 (3 rows) Der UNION-Operator ist dem Operator OR ähnlich. In manchen Fällen ist es auch möglich, die Verbindung zweier SELECT-Anweisungen mit dem UNION-Operator durch eine SELECT-Anweisung mit dem Operator OR zu ersetzen. Beispiel 7.45 Finden Sie alle Mitarbeiter, die entweder der Abteilung a1 oder a2 oder aber beiden angehören. SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE abt_nr = 'a1' UNION SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE abt_nr = 'a2'; Das Ergebnis ist: m_nr 2581 9031 18316 28559 29346
m_name Kaufmann Meier Mueller Mozer Probst
m_vorname Brigitte Rainer Gabriele Sibille Andreas
(5 rows) Dieselbe Aufgabe kann mit dem OR-Operator einfacher gelöst werden: Beispiel 7.46 SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE abt_nr = 'a1' OR abt_nr = 'a2'; Der UNION-Operator kann nicht durch den OR-Operator ersetzt werden, falls man Reihen aus zwei verschiedenen Tabellen wählt, wie es in den Beispielen 7.43 und 7.44 der Fall ist. Zusätzlich zu der Operation Vereinigung existieren in der Mengenlehre zwei weitere Operationen: - Durchschnitt und - Differenz. Der Durchschnitt zweier Datenmengen beinhaltet alle Daten, die sowohl in der ersten als auch in der zweiten Datenmenge enthalten sind. Die Differenz zweier Datenmengen beinhaltet alle Daten, die in der ersten Datenmenge enthalten sind und in der zweiten nicht. INGRES-SQL unterstützt diese zwei Operationen nicht direkt. -105-
Trotzdem ist es möglich, die beiden Operationen mit Hilfe anderer Operatoren zu ersetzen. Der Durchschnitt kann durch den operator EXISTS (siehe Beispiel 8.21) und die Differenz durch den Operator NOT EXISTS (siehe Beispiel 8.22) ersetzt werden.
Aufgaben A.7.1 A.7.2 A.7.3
A.7.4 A.7.5
A.7.6 A.7.7
A.7.8 A.7.9 A.7.10 A.7.11 A.7.12 A.7.13
A.7.14
Wählen Sie alle Reihen der Tabellen arbeiten und mitarbeiter aus. Finden Sie die Personalnummer aller Sachbearbeiter. Finden Sie die Personalnummer der Mitarbeiter, die in Projekt p2 arbeiten und deren Personalnummer kleiner als 10000 ist. Finden Sie die Personalnummer der Mitarbeiter, die nicht im Jahr 1988 in ihr Projekt eingesetzt sind. Finden Sie Personalnummer aller Mitarbeiter, die in Projekt p1 eine leitende Aufgabe (Gruppen- oder Projektleiter) haben. Finden Sie das Einstellungsdatum der Mitarbeiter in Projekt p2, deren Aufgabe noch nicht festgelegt ist. Finden Sie Personalnummer, Namen und Vornamen aller Mitarbeiter, deren Name mit "M" bzw. "H" anfängt und mit "er" endet. Nennen Sie die Personalnummer aller Mitarbeiter, deren Standort Stuttgart ist. Finden Sie Namen und Vornamen aller Mitarbeiter, die am 01-apr-1989 eingesetzt worden sind. Gruppieren Sie alle Abteilungen auf Grund ihres Standortes. Nennen Sie die grösste existierende Personalnummer. Welche Aufgaben werden von mehr als zwei Mitarbeiter ausgeübt? Finden Sie die Personalnummer aller Mitarbeiter, die entweder Sachbearbeiter sind oder der Abteilung a3 angehören. Warum ist folgende Aufgabe falsch SELECT pr_name FROM projekt WHERE pr_nr = (SELECT pr_nr FROM arbeiten WHERE aufgabe = 'Sachbearbeiter'); Wie sollte diese Aufgabe richtig lauten?
-106-
Komplexe Abfragen Komplexe Abfragen..............................................................................................................................................................107 8
Komplexe Abfragen ...................................................................................................................................................107
8.1 Verknüpfen zweier oder mehrerer Tabellen........................................................... 107 8.1.1 Der Equijoin ................................................................................................... 108 8.1.2 Das Kartesische Produkt................................................................................. 110 8.1.3 Der Natürliche Join......................................................................................... 111 8.1.4 Der Thetajoin.................................................................................................. 113 8.1.5 Verknüpfung von mehr als zwei Tabellen...................................................... 114 8.1.6 Eine Tabelle mit sich selbst verknüpfen......................................................... 115 8.1.7 Der Outer Join ................................................................................................ 116 8.2 Korrelierte Unterabfragen ...................................................................................... 118 8.3 Der EXISTS-Operator in Unterabfragen................................................................ 121 Aufgaben ............................................................................................................................ 124
7 Komplexe Abfragen Dieses Kapitel ist die inhaltliche Fortsetzung des vorherigen Kapitels. Zuerst sollen alle möglichen Arten von Verknüpfungen zweier oder mehrerer Tabellen definiert und durch Beispiele dargestellt werden. Daran anschließend wird die korrelierte Abfrage erklärt sowie eine ausführliche Beschreibung des EXISTS-Operators gegeben.
7.1
Verknüpfen zweier oder mehrerer Tabellen Die Möglichkeit, zwei oder mehrere Tabellen einer Datenbank miteinander zu verknüpfen, ist eine der grundsätzlichen Eigenschaften des relationalen Datenmodells. Diese Eigenschaft stellt gleichzeitig einen der wichtigsten Unterschiede zwischen relationalen und nichtrelationalen Datenbanken dar. Das Verknüpfen der Tabellen wird mit Hilfe des relationalen Operators Join durchgeführt, was im allgemeinen bedeutet, daß Datenwerte aus zwei oder mehreren Tabellen mittels einer SELECT-Anweisung ausgewählt werden. Der Operator Join kann auch zum Verknüpfen einer Tabelle mit sich selbst verwendet werden, was im Verlauf dieses Kapitels gezeigt wird. (Eine strikte mathematische Definition des relationalen Operators -107-
Join liegt außerhalb des Rahmens dieses Buches.) Hinweis. In Kapitel 7 haben wir schon den Mengenoperator UNION beschrieben, der im Grunde auch zwei Tabellen verknüpft. Trotzdem gibt es zwei wesentliche Unterschiede zwischen diesen beiden Arten von Tabellenverknüpfungen. Der Mengenoperator UNION verknüpft immer zwei SELECT-Anweisungen, während der Operator Join die Verknüpfung mehrerer Tabellen mittels einer SELECT-Anweisung durchführt. Weiter werden für die Verknüpfung mit dem Operator UNION immer die Reihen der Tabellen verwendet, während der Operator Join, wie wir noch sehen werden, gewisse Spalten der Tabellen für die Verknüpfung benutzt. Der Operator Join kann sowohl auf Basistabellen als auch auf Views angewendet werden. In diesem Kapitel werden wir nur die Verknüpfung der Basistabellen beschreiben, während die Verknüpfung zweier oder mehrerer Views bzw. einer Basistabelle mit einem View in Kapitel 11 beschrieben wird. In diesem Kapitel sollen folgende Arten der Tabellenverknüpfungen vorgestellt werden: - Equijoin, - Kartesisches Produkt, - natürlicher Join, - Thetajoin und - Outer Join. 7.1.1 Der Equijoin Der Equijoin wird mit einem Beispiel dargestellt. Beispiel 8.1 Finden Sie für jeden Mitarbeiter, zusätzlich zu seiner Personalnummer, Namen und Vornamen, auch die Abteilungsnummer und den Standort der Abteilung. Die doppelten Spalten beider Tabellen sollen ausgegeben werden. SELECT mitarbeiter.*, abteilung.* FROM mitarbeiter, abteilung WHERE mitarbeiter.abt_nr = abteilung.abt_nr; Das Ergebnis ist: m_nr 18316 28559 2581 9031 29346 10102 25348
m_name Mueller Mozer Kaufmann Meier Probst Huber Keller
m_vorname Gabriele Sibille Brigitte Rainer Andreas Petra Hans
abt_nr a1 a1 a2 a2 a2 a3 a3
abt_nr a1 a1 a2 a2 a2 a3 a3
abt_name Beratung Beratung Diagnose Diagnose Diagnose Freigabe Freigabe
(7 rows) Die Projektion in Beispiel 8.1 umfaßt alle Spalten beider -108-
stadt Muenchen Muenchen Muenchen Muenchen Muenchen Stuttgart Stuttgart
Tabellen. Das ist ein Merkmal von Equijoin. Die äquivalente Schreibweise für die Projektion in einem Equijoin wäre die Verwendung des Zeichens "*". Die FROM-Klausel definiert die Tabellen, die verknüpft werden; die WHERE-Klausel gibt die jeweiligen Spalten, die miteinander verglichen werden, an. Diese Spalten heißen Joinspalten. Sie müssen denselben Datentyp haben. Wir wollen nun zeigen, wie das Ergebnis eines Joins entsteht. Diese Darstellung beschreibt nur ein gedankliches Konzept und nicht unbedingt die Arbeitsweise des INGRES-Systems. Das Konzept wird praktisch mit Beispiel 8.1 erklärt. Zunächst werden alle Reihen der Tabelle mitarbeiter mit jeder einzelnen Reihe der Tabelle abteilung verkettet. Dadurch entsteht eine Tabelle mit insgesamt 21 Reihen, die folgendermaßen aussieht: m_nr -25348 -10102 18316 -29346 - 9031 - 2581 28559 -25348 -10102 -18316 29346 9031 2581 -28559 25348 10102 -18316 -29346 -9031 -2581 -28559
m_name Keller Huber Mueller Probst Meier Kaufmann Mozer Keller Huber Mueller Probst Meier Kaufmann Mozer Keller Huber Mueller Probst Meier Kaufmann Mozer
m_vorname Hans Petra Gabriele Andreas Rainer Brigitte Sibille Hans Petra Gabriele Andreas Rainer Brigitte Sibille Hans Petra Gabriele Andreas Rainer Brigitte Sibille
abt_nr a3 a3 a1 a2 a2 a2 a1 a3 a3 a1 a2 a2 a2 a1 a3 a3 a1 a2 a2 a2 a1
abt_nr a1 a1 a1 a1 a1 a1 a1 a2 a2 a2 a2 a2 a2 a2 a3 a3 a3 a3 a3 a3 a3
abt_name Beratung Beratung Beratung Beratung Beratung Beratung Beratung Diagnose Diagnose Diagnose Diagnose Diagnose Diagnose Diagnose Freigabe Freigabe Freigabe Freigabe Freigabe Freigabe Freigabe
(21 rows) Abb. 8-1 Kartesisches Produkt der Tabellen mitarbeiter und abteilung Danach werden die Reihen entfernt, die die Bedingung mitarbeiter.abt_nr = abteilung.abt_nr in der WHERE-Klausel nicht erfüllen. Diese Reihen sind mit dem Zeichen "- " in Abbildung 8-1 gekennzeichnet. Die übrigen Reihen stellen das Ergebnis des Beispiels 8.1 dar. Die Spaltenpaare, die in einem Vergleich beim Join erscheinen, unterliegen in der Praxis einer weiteren Bedingung, nämlich der, daß die sinnvolle Verknüpfung zweier Tabellen nur über Spaltenpaare durchgeführt wird, die -109-
stadt Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Muenchen Stuttgart Stuttga rt Stuttgart Stuttgart Stuttgart Stuttgart Stuttgart
dieselbe logische Bedeutung in der Anwendung haben. In der Beispieldatenbank existieren insgesamt drei solche Spaltenpaare. Die Tabellen abteilung und mitarbeiter lassen sich durch die Joinspalten abteilung.abt_nr und mitarbeiter.abt_nr verbinden. Genauso lassen sich die Tabellen mitarbeiter und arbeiten durch die Joinspalten mitarbeiter.m_nr und arbeiten.m_nr und die Tabellen projekt und arbeiten durch die Joinspalten projekt.pr_nr und arbeiten.pr_nr verbinden. Wie man hieraus erkennt, hat jedes Spaltenpaar der Beispieldatenbank denselben Namen, was im allgemeinen nicht der Fall sein muß. Die gekennzeichneten Namen in der WHERE-Klausel, wie z.B. mitarbeiter.abt_nr und abteilung.abt_nr, sind unbedingt anzugeben, falls die Spaltennamen in der SELECT-Anweisung nicht eindeutig sind. Zusätzlich zu Bedingungen mit Joinspalten können weitere Bedingungen in der WHERE-Klausel existieren. Beispiel 8.2 Finden Sie alle Daten der Mitarbeiter, die im Projekt Gemini arbeiten. SELECT * FROM arbeiten, projekt WHERE arbeiten.pr_nr = projekt.pr_nr AND pr_name = 'Gemini'; Das Ergebnis ist
m_nr
pr_n aufgabe r
einst_dat
pr_n r pr_name mittel
25348 p2
Sachbearbeiter 15-feb-1988
p2
Gemini
95000.000
18316 p2
01-jun-1989
p2
Gemini
95000.000
29346 p2
15-dec-1987 p2
Gemini
95000.000
Gemini
95000.000
28559 p2
Sachbearbeiter 01-feb-1989
p2
(4 rows) Für die Spalte pr_name ist es nicht unbedingt notwendig, den gekennzeichneten Namen anzugeben, weil dieser Spaltenname in Beispiel 8.2 eindeutig ist. 7.1.2 Das Kartesische Produkt Im vorherigen Abschnitt haben wir die Entstehung des Ergebnisses in Beispiel 8.1 mit Hilfe eines Konzeptes erklärt. Im ersten Schritt dieses Konzeptes sind alle Reihen der Tabelle mitarbeiter mit jeder einzelnen Reihe der Tabelle abteilung verkettet worden. Das so entstandene Zwischenergebnis heißt Kartesisches Produkt. Beispiel 8.3 SELECT * -110-
FROM mitarbeiter, abteilung; Das Ergebnis ist in Abbildung 8-1 dargestellt. Die Ergebnistabelle des Kartesischen Produktes enthält so viele Reihen wie das Produkt der Reihenanzahl der ersten und zweiten Tabelle. In Beispiel 8.3 lautet das Ergebnis 7x3=21 Reihen. Das folgende Beispiel stellt das Kartesische Produkt zweier Tabellen dar, in dem die Bedingung in der WHERE-Klausel existiert, das aber nicht den Vergleich zwischen Joinspalten enthält. Beispiel 8.4 SELECT * FROM arbeiten, projekt WHERE arbeiten.pr_nr = 'p3'; Das Ergebnis ist: pr_n aufgabe r
einst_dat
pr_n r pr_name mittel
p3
Gruppenleiter
01-jan-1989
p1
Apollo
120000.00 0
10102 p3
Gruppenleiter
01-jan-1989
p2
Gemini
95000.000
p3
Gruppenleiter
01-jan-1989
p3
Merkur
186500.00 0
p3
Projektleiter
15-oct-1989
p1
Apollo
120000.00 0
2581 p3
Projektleiter
15-oct-1989
p2
Gemini
95000.000
p3
Projektleiter
15-oct-1989
p3
Merkur
186500.00 0
p3
Sachbearbeiter 15-nov-1988 p1
Apollo
120000.00 0
9031 p3
Sachbearbeiter 15-nov-1988 p2
Gemini
95000.000
p3
Sachbearbeiter 15-nov-1988 p3
Merkur
186500.00 0
m_nr 10102
10102 2581
2581 9031
9031 (9 rows) In der Praxis wird das Kartesische Produkt äußerst selten bewußt benutzt. Manchmal kommt es vor, daß der Anwender das Kartesische Produkt unbewußt erzeugt, wenn er vergißt, den Vergleich zwischen den Joinspalten in der WHERE-Klausel anzugeben. Dieses Ergebnis entspricht dann nicht dem tatsächlichen Resultat, das der Anwender erhalten wollte. 7.1.3 Der Natürliche Join Der natürliche Join entsteht aus einem Equijoin, wenn die doppelte Spalte aus der Projektion entfernt wird. Der -111-
Equijoin in Beispiel 8.1 kann auf folgende Weise in einen natürlichen Join umgewandelt werden: Beispiel 8.5 SELECT mitarbeiter.*, abt_name, stadt FROM mitarbeiter, abteilung WHERE mitarbeiter.abt_nr = abteilung.abt_nr; Das Ergebnis ist:
m_nr 18316 28559 2581 9031 29346 10102 25348
m_name m_vornam e Mueller Gabriele Mozer Sibille Kaufman Brigitte n Meier Rainer Probst Andreas Huber Petra Keller Hans
abt_name abt_nr a1 a1 a2
Beratung Beratung Diagnose
a2 a2 a3 a3
Diagnose Diagnose Freigabe Freigabe
stadt Muenchen Muenchen Muenchen Muenchen Muenchen Stuttgart Stuttgart
(7 rows) Die Projektion in Beispiel 8.5 beinhaltet alle Spalten der ersten Tabelle und jene Spalten der zweiten Tabelle, die nicht die Joinspalte darstellen. Die Spalten der zweiten Tabelle abt_name und stadt sind in der Projektion mit nicht gekennzeichneten Namen, d.h. ohne Tabellennamen angegeben. Dies ist möglich, weil die beiden Namen in der Tabelle eindeutig sind. Der natürliche Join wird in der Praxis von allen Joinarten am häufigsten angewendet. Deswegen impliziert das Kürzel "Join" immer einen natürlichen Join. Die Projektion eines natürlichen Joins muß nicht unbedingt alle unterschiedlichen Spalten beider Tabellen beinhalten, was auch das folgende Beispiel zeigt. Beispiel 8.6 Nennen Sie die Abteilungsnummern aller Mitarbeiter, die am 15.10.1989 eingestellt wurden. SELECT abt_nr FROM mitarbeiter, arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr AND einst_dat = '15-oct-1989'; Das Ergebnis ist: abt_nr a2 (1 row) Beispiel 8.6 ist identisch mit dem Beispiel 7.21, nur werden beide auf unterschiedliche Weise gelöst. Wie aus beiden Beispielen ersichtlich, ist die Lösung mit Join einfacher als die Lösung mittels einer Unterabfrage. Zusätzlich -112-
dazu hat die Unterabfrage mit dem Gleichheitszeichen eine Einschränkung: Die Unterabfrage darf nur eine Reihe als Ergebnis liefern. Diese Einschränkung gilt nicht für den Join. 7.1.4 Der Thetajoin Der Thetajoin kennzeichnet jene SELECT-Anweisung, bei der die Joinspalten in der WHERE-Klausel mit einem der Vergleichsoperatoren verglichen werden. Die allgemeine Form eines Thetajoins sieht folgendermaßen aus: SELECT tabelle_1.spalten, tabelle_2.spalten FROM tabelle_1, tabelle_2 WHERE join_spalte_1 Ý join_spalte_2; wobei "Ý" einen beliebigen Vergleichsoperator darstellt. Jeder Equijoin ist gleichzeitig auch ein Thetajoin, wenn man für "Ý" das Gleichheitszeichen benutzt. Im folgenden Beispiel wird die Tabelle mit_erweiter verwendet. Beispiel 8.7 SELECT mit_erweiter.*, abteilung.* FROM mit_erweiter, abteilung WHERE wohnort > stadt; Das Ergebnis ist: m_nam m_vorname abt_n m_nr e wohnort r Mueller
Gabriele
a1
18316 Mueller
Gabriele
a1
18316
abt_n abt_name stadt r
Rosenhei m
a1
Rosenhei m
a2
Beratung Muenchen Diagnose Muenchen
28559 Mozer
Sibille
a1
Ulm
a1
Beratung
Muenchen
28559 Mozer
Sibille
a1
Ulm
a2
Diagnose
Muenchen
28559 Mozer
Sibille
a1
Ulm
a3
Freigabe
Stuttgart
(5 rows) In Beispiel 8.7 wurden die Spalten wohnort und stadt verglichen und alle Mitarbeiter- und Abteilungsdaten ausgewählt; der Wohnort des Mitarbeiters folgt jeweils alphabetisch nach dem Standort der Abteilung. Die Verwendung der Form des Thetajoins mit einem anderen Vergleichsoperator als dem Gleichheitszeichen kommt in der Praxis nicht oft vor. Am häufigsten wird noch der Vergleich auf die Ungleichheit durchgeführt. Beispiel 8.8 Finden Sie alle Mitarbeiter- und Abteilungsdaten, bei denen die Abteilungsnummer in den beiden Tabellen nicht -113-
übereinstimmt. SELECT * FROM mitarbeiter, abteilung WHERE mitarbeiter.abt_nr <> abteilung.abt_nr; Das Ergebnis ist: m_name m_nr
m_vornam abt_n abt_n e r r abt_name stadt
25348 Keller
Hans
a3
a1
Beratung
Muenchen
10102 Huber
Petra
a3
a1
Beratung
Muenchen
29346 Probst
Andreas
a2
a1
Beratung
Muenchen
Rainer
a2
a1
Beratung
Muenchen
a2
a1
Beratung
Muenchen
9031 Meier
2581 Kaufmann Brigitte 25348 Keller
Hans
a3
a2
Diagnose
Muenchen
10102 Huber
Petra
a3
a2
Diagnose
Muenchen
18316 Mueller
Gabriele
a1
a2
Diagnose
Muenchen
28559 Mozer
Sibille
a1
a2
Diagnose
Muenchen
18316 Mueller
Gabriele
a1
a3
Freigabe
Stuttgart
29346 Probst
Andreas
a2
a3
Freigabe
Stuttgart
Rainer
a2
a3
Freigabe
Stuttgart
a2
a3
Freigabe
Stuttgart
a1
a3
Freigabe
Stuttgart
9031 Meier
2581 Kaufmann Brigitte 28559 Mozer
Sibille
(14 rows) 7.1.5 Verknüpfung von mehr als zwei Tabellen Die Anzahl der Tabellen, die miteinander verknüpft sein können, ist theoretisch unbegrenzt. Trotzdem hat das INGRES-System eine implementierungsbedingte Einschränkung, die die Anzahl der möglichen Joins begrenzt hält. Beispiel 8.9 Nennen Sie Namen und Vornamen aller Projektleiter, deren Abteilung den Standort Stuttgart hat. SELECT m_name, m_vorname FROM arbeiten, mitarbeiter, abteilung WHERE arbeiten.m_nr = mitarbeiter.m_nr AND mitarbeiter.abt_nr = abteilung.abt_nr AND aufgabe = 'Projektleiter' AND stadt = 'Stuttgart'; Das Ergebnis ist:
-114-
m_name
m_vorname
Huber
Petra
(1 row) In Beispiel 8.9 müssen drei Tabellen, nämlich arbeiten, mitarbeiter und abteilung miteinander verknüpft werden, damit die notwendige Information mittels einer SELECT-Anweisung ausgewählt wird. Diese drei Tabellen werden mit Hilfe von zwei Paaren von Joinspalten verknüpft: (arbeiten.m_nr, mitarbeiter.m_nr) (mitarbeiter.abt_nr, abteilung.abt_nr) Im folgenden Beispiel werden alle vier Tabellen der Beispieldatenbank miteinander verknüpft. Beispiel 8.10 Nennen Sie die Namen der Projekte, in denen die Mitarbeiter arbeiten, die zur Abteilung Diagnose gehören. SELECT DISTINCT pr_name FROM projekt, arbeiten, mitarbeiter, abteilung WHERE projekt.pr_nr = arbeiten.pr_nr AND arbeiten.m_nr = mitarbeiter.m_nr AND mitarbeiter.abt_nr = abteilung.abt_nr AND pr_name = 'Diagnose'; Das Ergebnis ist: pr_name Apollo Gemini Merkur (3 rows) 7.1.6 Eine Tabelle mit sich selbst verknüpfen Join kann nicht nur auf zwei oder mehrere Tabellen angewendet werden, sondern auch auf eine einzige Tabelle. In diesem Fall wird die Tabelle mit sich selbst verknüpft, wobei eine einzige Spalte dieser Tabelle gewöhnlich mit sich selbst verglichen wird. Wird eine Tabelle mit sich selbst verknüpft, erscheint ihr Name doppelt in der FROM-Klausel einer SELECT-Anweisung. Damit der Tabellenname in beiden Fällen unterschieden werden kann, müssen Aliasnamen benutzt werden. Gleichzeitig müssen alle Spalten dieser Tabelle in der SELECT-Anweisung gekennzeichnet sein und zwar mit dem entsprechenden Aliasnamen als Präfix. Beispiel 8.11 Finden Sie alle Abteilungen, die sich an demselben Standort -115-
befinden. SELECT a.abt_nr, a.abt_name, a.stadt FROM abteilung a, abteilung b WHERE a.stadt = b.stadt AND a.abt_nr <> b.abt_nr; Das Ergebnis ist: abt_nr abt_name
stadt
a1
Beratung
Muenchen
a2
Diagnose
Muenchen
(2 rows) In der FROM-Klausel des obigen Beispiels sind zwei Aliasnamen für die Tabelle abteilung eingeführt worden. Die erste Erscheinung der Tabelle hat den Aliasnamen a und die zweite den Aliasnamen b. Die erste Bedingung in der WHERE-Klausel definiert die Joinspalten, während die zweite Bedingung notwendig ist, damit aus dem Ergebnis die mehrfach vorhandenen Reihen entfernt werden. Beispiel 8.12 Finden Sie Personalnummer, Namen und Wohnort der Mitarbeiter, die im gleichen Ort wohnen und zu derselben Abteilung gehören. SELECT a.m_nr, a.m_name, a.wohnort FROM mit_erweiter a, mit_erweiter b WHERE a.wohnort = b.wohnort AND a.abt_nr = b.abt_nr AND a.m_nr <> b.m_nr; Das Ergebnis ist: m_nr
m_name
wohnort
29346
Probst
Augsburg
9031
Meier
Augsburg
(2 rows) 7.1.7 Der Outer Join In den bisherigen Beispielen für Equi-, Theta- und natürlichen Join sind die Spalten zweier Tabellen immer miteinander verglichen worden. Auf Grund dieses Vergleichs werden dann die Reihen ausgewählt. Manchmal ist es in der Praxis notwendig, nicht nur die Reihen auszuwählen, die die Bedingung mit Joinspalten erfüllen, sondern auch die Reihen einer der beiden Tabellen, die diese Bedingung nicht erfüllen. Eine solche Verknüpfung der Tabellen heißt Outer Join. -116-
Damit der Unterschied zwischen Equijoin und Outer Join transparenter wird, werden wir zunächst ein Beispiel mit Equijoin zeigen. Beispiel 8.13 Finden Sie alle Kombinationen von Mitarbeiter- und Abteilungsdaten für Orte, die gleichzeitig Wohnorte der Mitarbeiter und Standorte der Abteilungen sind. SELECT * FROM mit_erweiter, abteilung WHERE wohnort = stadt; Das Ergebnis ist: m_name
m_vorname
m_nr Kaufman 2581 n 25348 Keller Kaufman 2581 n 25348 Keller
abt_nr wohnort Brigitte
a2
abt_n abt_name stadt r
Muenchen a2
Diagnose Muenchen
Hans
a3
Muenchen a1
Beratung
Brigitte
a2
Muenchen a1
Beratung
Muenchen Muenchen
Hans
a3
Muenchen a2
Diagnose
(4 rows) Beispiel 8.13 stellt einen Equijoin dar. Falls wir zusätzlich auch alle anderen Wohnorte der Mitarbeiter auswählen wollen, muß Outer Join verwendet werden. Das Ergebnis dieses Outer Joins kann man sich als die Erweiterung des Ergebnisses des Beispiels 8.13 um eine zusätzliche Spalte vorstellen. Jeder Datenwert dieser Spalte erhält den NULL-Wert, falls die Bedingung in der WHERE-Klausel für diese Reihe nicht erfüllt ist bzw. den entsprechenden Datenwert der zweiten Tabelle, falls die Bedingung in der WHERE-Klausel erfüllt ist. INGRES bietet keinen expliziten Operator für den Outer Join. Grundsätzlich ist es möglich, den Outer Join mit Hilfe des UNION-Operators und der Unterabfrage mit dem EXISTS-Operator zu ersetzen, wie das nachfolgende Beispiel zeigt. Beispiel 8.14 Finden Sie alle Kombinationen der Mitarbeiter- und Abteilungsdaten für die Orte, die entweder nur Wohnorte der Mitarbeiter oder gleichzeitig Wohnorte der Mitarbeiter und Standorte der Abteilungen sind. SELECT mit_erweiter.*, abteilung.abt_nr FROM mit_erweiter, abteilung WHERE wohnort = stadt UNION SELECT mit_erweiter.*, ' ' FROM mit_erweiter WHERE NOT EXISTS (SELECT * -117-
Muenchen
FROM abteilung
WHERE stadt = wohnort); Das Ergebnis ist: m_name m_nr
m_vorname abt_n r wohnort
abt_nr
2581
Kaufmann
Brigitte
a2
Muenchen
a1
25348
Keller
Hans
a3
Muenchen
a1
2581
Kaufmann
Brigitte
a2
Muenchen
a2
25348
Keller
Hans
a3
Muenchen
a2
10102
Huber
Petra
a3
Landshut
18316
Mueller
Gabriele
a1
Rosenheim
29346
Probst
Andreas
a2
Augsburg
9031
Meier
Rainer
a2
Augsburg
28559
Mozer
Sibille
a1
Ulm
(9 rows) Die erste SELECT-Anweisung stellt den natürlichen Join der Tabellen mitarbeiter und abteilung mit den Joinspalten wohnort und stadt dar. Diese Anweisung wählt alle Reihen aus, in denen die Orte gleichzeitig Wohnorte der Mitarbeiter und Standorte der Abteilungen sind. Die zweite SELECT-Anweisung mit der Unterabfrage wählt zusätzliche Reihen aus, in denen die Orte nur Wohnorte der Mitarbeiter sind.
7.2
Korrelierte Unterabfragen Eine Unterabfrage wird dann korreliert genannt, wenn die innere SELECT-Anweisung eine Spalte enthält, deren Werte in der äußeren SELECT-Anweisung festgelegt sind. Die beiden folgenden Beispiele zeigen, wie dieselbe Aufgabe mit Hilfe einer einfachen und einer korrelierten Unterabfrage gelöst werden kann. Beispiel 8.15 Nennen Sie die Namen aller Mitarbeiter, die im Projekt p3 arbeiten. SELECT m_name FROM mitarbeiter WHERE m_nr IN (SELECT m_nr FROM arbeiten WHERE pr_nr = 'p3'); Das Ergebnis ist: -118-
m_name Huber Meier Kaufmann (3 rows) Beispiel 8.15 zeigt eine einfache Unterabfrage, in der zunächst die innere SELECT-Anweisung, unabhängig von der äußeren SELECT-Anweisung, berechnet wird und als Ergebnis die Werte für den IN-Operator geliefert werden. Diese Werte werden dann zur Bildung des endgültigen Ergebnisses benutzt. Beispiel 8.16 SELECT m_name FROM mitarbeiter WHERE 'p3' IN (SELECT pr_nr FROM arbeiten WHERE arbeiten.m_nr = mitarbeiter.m_nr); Das Ergebnis ist: m_name Huber Meier Kaufmann (3 rows) In Beispiel 8.16 kann die innere SELECT-Anweisung nicht in einem Schritt ausgewertet werden, weil sie die Spalte m_nr beinhaltet, die der Tabelle mitarbeiter aus der äußeren SELECT-Anweisung angehört. Die innere SELECT-Anweisung ist also von einer Variablen abhängig, die in der äußeren SELECT-Anweisung berechnet sein muß. In Beispiel 8.16, wie dies bei der korrelierten Unterabfrage grundsätzlich der Fall ist, untersucht das System zunächst die erste Reihe der Tabelle mitarbeiter und vergleicht die Mitarbeiternummer (25348) mit der Spalte arbeiten.m_nr in der inneren SELECT-Anweisung. Die innere SELECT-Anweisung liefert nach diesem Vergleich als Ergebnis den Datenwert p2 für die Spalte pr_nr. Dieser Wert ist nicht gleich dem Datenwert p3 in der äußeren SELECT-Anweisung. Deswegen wird die zweite Reihe der Tabelle mitarbeiter auf dieselbe Weise untersucht. Der Vergleich arbeiten.m_nr = mitarbeiter.m_nr in der inneren SELECT-Anweisung liefert als Ergebnis zwei Datenwerte: p1 und p3. Weil p3 zu der Menge der Datenwerte gehört, die die innere SELECT-Anweisung liefert, ist die Bedingung in der äußeren SELECT-Anweisung erfüllt, und der Datenwert der -119-
Spalte m_name in der zweiten Reihe (Huber) wird ausgewählt. Danach werden auf dieselbe Weise alle Reihen der Tabelle mitarbeiter untersucht und die Ergebnistabelle gebildet. Im folgenden Beispiel wird eine korrelierte Unterabfrage gezeigt, in der die Tabelle mit sich selbst verknüpft wird. Beispiel 8.17 Finden Sie alle Abteilungen, die sich an demselben Standort befinden. SELECT a.abt_nr, a.abt_name, a.stadt FROM abteilung a WHERE a.stadt IN (SELECT b.stadt FROM abteilung b WHERE a.abt_nr <> b.abt_nr); Das Ergebnis ist abt_nr abt_name
stadt
a1
Beratung
Muenchen
a2
Diagnose
Muenchen
(2 rows) Die Aufgabe in Beispiel 8.17 ist mit der Aufgabe in Beispiel 8.11 identisch. Im folgenden Beispiel wird eine korrelierte Unterabfrage mit der Aggregatfunktion MIN dargestellt. Beispiel 8.18 Alle Mitarbeiter sind nach ihren Aufgaben in verschiedene Gruppen (Sachbearbeiter usw.) unterteilt. Finden Sie Persona l-, Projektnummer und Aufgabe aller Mitarbeiter, die nicht die kleinste Personalnummer in ihrer Aufgabengruppe haben. SELECT a.m_nr, a.pr_nr, a.aufgabe FROM arbeiten a WHERE m_nr > (SELECT MIN(m_nr) FROM arbeiten b WHERE a.aufgabe= b.aufgabe); Das Ergebnis ist: m_nr
pr_nr
aufgabe
10102
p1
Projektleiter
10102
p3
Gruppenleiter
25348
p2
Sachbearbeiter
28559
p2
Sachbearbeiter
29346
p1
Sachbearbeiter -120-
(5 rows) Wie bereits erwähnt, darf die innere SELECT-Anweisung bei der Verwendung eines Vergleichsoperators in der Unterabfrage, wie in Beispiel 8.18, nur einen Datenwert liefern. Falls das Ergebnis der inneren SELECT-Anweisung mehrere Datenwerte beinhaltet, muß der IN-Operator benutzt werden. Weitere Beispiele für die korrelierte Unterabfrage finden Sie im nächsten Abschnitt.
7.3
Der EXISTS-Operator in Unterabfragen Im vorherigen Kapitel haben wir den EXISTS-Operator bereits definiert. In diesem Abschnitt wird dieser Operator mit Hilfe von Beispielen ausführlich erklärt. Beispiel 8.19 Finden Sie die Namen aller Mitarbeiter, die im Projekt p1 arbeiten. SELECT m_name FROM mitarbeiter WHERE EXISTS (SELECT * FROM arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr AND pr_nr = 'p1'); Das Ergebnis ist: m_name Huber Probst Meier Mozer (4 rows) Beim EXISTS-Operator wird die Bedingung in der äußeren SELECT-Anweisung als wahr betrachtet, wenn die innere SELECT-Anweisung zumindest eine Ergebnisreihe liefert. Dementsprechend ist die Bedingung in der äußeren SELECT-Anweisung falsch, wenn die innere SELECT-Anweisung keine einzige Reihe als Ergebnis liefert. Bei Unterabfragen mit dem EXISTS-Operator ist in der Praxis die innere SELECT-Anweisung immer von einer Variablen abhängig, die in der äußeren SELECT-Anweisung berechnet wird. Alle praxisbezogenen SELECT-Anweisungen mit dem EXISTS-Operator stellen damit gleichzeitig auch korrelierte Unterabfragen dar. Wie das INGRES-System eine SELEC T-Anweisung mit dem -121-
EXISTS-Operator bearbeitet, wird an Beispiel 8.19 erklärt. In der Tabelle mitarbeiter wird zunächst der Datenwert (Keller) der Spalte m_name der ersten Reihe ausgewählt. Danach wird getestet, ob für den Mitarbeiter dieses Namens die Bedingungen in der WHERE-Klausel der inneren SELECT-Anweisung erfüllt sind oder nicht. Wenn die Bedingungen erfüllt sind, liefert die innere SELECT-Anweisung eine oder mehrere Ergebnisreihen. Das bedeutet gleichzeitig, daß der ausgewählte Name zur Ergebnistabelle gehört. Beim Mitarbeiter namens Keller ist die zweite Bedingung nicht erfüllt; deshalb gehört dieser Name auch nicht zu der Ergebnistabelle. Danach wird der Datenwert der zweiten Reihe (Huber) ausgewählt und die Bedingungen überprüft. Bei diesem Mitarbeiter sind beide Bedingungen erfüllt. Alle anderen Reihen der Tabelle mitarbeiter werden dementsprechend nacheinander abgearbeitet. Beispiel 8.20 Nennen Sie die Namen aller Mitarbeiter, deren Abteilung als Standort eine andere Stadt als Stuttgart hat. SELECT m_name FROM mitarbeiter WHERE NOT EXISTS (SELECT * FROM abteilung WHERE mitarbeiter.abt_nr=abteilung.abt_nr AND stadt = 'Stuttgart'); Das Ergebnis ist: m_name Mueller Probst Meier Kaufmann Mozer (5 rows) Die innere SELECT-Anweisung einer Unterabfrage mit dem EXISTS-Operator wird in der Praxis als SELECT *
geschrieben. Als Schreibweise wäre auch SELECT spalten_liste
möglich, wobei spalten_liste eine oder mehrere Spalten kennzeichnet. Der Grund für verschiedene Darstellungsmöglichkeiten ist, daß beim EXISTS-Operator nur die Existenz bzw. Nichtexistenz der Ergebnisreihen in der inneren SELECT-Anweisung von Bedeutung ist und nicht die Anzahl der ausgewählten Spalten. Mit Hilfe des EXISTS-Operators ist es möglich, die -122-
Mengenoperationen Durchschnitt und Differenz darzustellen. Das nächste Beispiel zeigt, wie der Durchschnitt durch den EXISTS-Operator ersetzt werden kann. In diesem, wie auch in dem folgenden Beispiel, wird die Tabelle mit_erweiter benutzt. Beispiel 8.21 Finden Sie die Städte, die sowohl die Wohnorte der Mitarbeiter als auch die Standorte der Abteilungen sind. SELECT DISTINCT wohnort FROM mit_erweiter WHERE EXISTS (SELECT stadt FROM abteilung WHERE stadt = wohnort); Das Ergebnis ist: wohnort Muenchen (1 row) Das Beispiel 8.22 zeigt, wie die Differenz durch den NOT EXISTS-Operator dargestellt werden kann. Beispiel 8.22 Finden Sie die Wohnorte der Mitarbeiter, die nicht an den Firmenstandorten wohnen. SELECT DISTINCT wohnort FROM mit_erweiter WHERE NOT EXISTS (SELECT stadt FROM abteilung WHERE wohnort = stadt); Das Ergebnis ist: wohnort Augsburg Rosenheim Landshut Ulm (4 rows) Der EXISTS-Operator kann auch verwendet werden, um die beiden Operatoren ANY und ALL zu ersetzen. Dabei gilt grundsätzlich, daß der Operator ANY durch den EXISTS-Operator zu ersetzen ist, während NOT EXISTS für die Ersetzung des ALL-Operators benutzt wird. Das folgende Beispiel ist mit Beispiel 7.26 identisch. -123-
Beispiel 8.23 Nennen Sie die Namen und Vornamen aller Mitarbeiter, die im Projekt p1 arbeiten. SELECT m_name, m_vorname FROM mitarbeiter WHERE EXISTS (SELECT * FROM arbeiten WHERE pr_nr = 'p1' AND mitarbeiter.m_nr = arbeiten.m_nr); Das Ergebnis ist: m_name
m_vornam e
Huber
Petra
Probst
Andreas
Meier
Rainer
Mozer
Sibille
(4 rows) Das Beispiel 8.24 ist mit Beispiel 7.27 identisch. Beispiel 8.24 Nennen Sie die Aufgabe des Mitarbeiters, der die kleinste Personalnummer hat. SELECT DISTINCT aufgabe FROM arbeiten WHERE NOT EXISTS (SELECT * FROM mitarbeiter WHERE NOT arbeiten.m_nr <= mitarbeiter.m_nr); Das Ergebnis ist: aufgabe Projektleiter (1 row)
Aufgaben A.8.1 Erstellen Sie ein: - Equijoin, - Natürlicher Join und ein - Kartesisches Produkt für die Tabellen projekt und arbeiten. A.8.2 Finden Sie Personalnummer und Aufgabe aller Mitarbeiter, die im Projekt Gemini arbeiten. -124-
A.8.3 Finden Sie Namen und Vornamen aller Mitarbeiter, die entweder Beratung oder Diagnose durchführen. A.8.4 Finden Sie das Einstellungsdatum der Mitarbeiter, die zu Abteilung a2 gehören und in ihrem Projekt Sachbearbeiter sind. A.8.5 Finden Sie die Namen des Projekts, in dem zwei oder mehrere Sachbearbeiter arbeiten. A.8.6 Nennen Sie Namen und Vornamen der Mitarbeiter, die Gruppenleiter sind und im Projekt Merkur arbeiten. A.8.7 Finden Sie in der erweiterten Beispieldatenbank die Personalnummer der Mitarbeiter, die im gleichen Ort wohnen und zu derselben Abteilung gehören. A.8.8 Finden Sie die Personalnummer aller Mitarbeiter, die zur Abteilung Freigabe gehören. Lösen Sie diese Aufgabe mit Hilfe: a) des Join-Operators b) der korrelierten Unterabfrage.
-125-
Änderung der Tabellen
Änderung der Tabellen .........................................................................................................................................................126 9
Änderung der Tabellen ..............................................................................................................................................126
9.1 Die INSERT-Anweisung........................................................................................ 126 9.1.1 Einfügen einer Reihe ...................................................................................... 127 9.1.2 Einfügen mehrerer Reihen.............................................................................. 128 9.2 Die UPDATE-Anweisung ...................................................................................... 130 9.3 Die DELETE-Anweisung....................................................................................... 132 Aufgaben ............................................................................................................................ 132
8 Änderung der Tabellen In diesem Kapitel werden die übrigen drei SQL-Anweisungen, die zur Datenmanipulation gehören, vorgestellt. Mit diesen Anweisungen können die Datenwerte eingefügt, geändert und gelöscht werden. Es handelt sich um: - INSERT, - UPDATE und - DELETE.
8.1
Die INSERT-Anweisung Mit der INSERT-Anweisung können Reihen oder Teile von Reihen in einer bestehenden Tabelle eingefügt werden. Dabei existieren zwei unterschiedliche Formen: (1) INSERT INTO tab [(spalte_1,...)] VALUES (wert_1,...); (2) INSERT INTO tab [(spalte_1,...)] select_anw; Mit der ersten Form der INSERT-Anweisung werden eine einzige Reihe oder Teile einer Reihe in der Tabelle tab eingefügt. Mit der zweiten Form werden mehrere Reihen oder Teile mehrerer Reihen mit Hilfe einer SELECT-Anweisung gleichzeitig in der Tabelle tab eingefügt. Für beide Formen der INSERT-Anweisung gilt, daß jeder eingefügte Datenwert denselben Datentyp wie die Spalte haben muß, in der er eingefügt wird. Dabei sollen die alphanumerischen Werte und die Datums- und Zeitangaben immer in Apostrophen eingeschlossen werden, während man numerische Werte ohne Apostrophe schreiben kann.
-126-
8.1.1 Einfügen einer Reihe Bei der ersten Form der INSERT-Anweisung ist die explizite Angabe der Spaltenliste optional. Falls alle Spalten einer Tabelle eingefügt werden sollen, kann auf die Angabe der Liste verzichtet werden. In den folgenden vier Beispielen werden die Reihen aller Tabellen der Beispieldatenbank eingefügt. Damit wird gleichzeitig die Möglichkeit aufgezeigt, die Reihen einer Datenbank zu laden. Beispiel 9.1 Laden Sie alle Reihen der Tabelle arbeiten. INSERT INTO arbeiten VALUES (10102,'p1','Projektleiter','01-oct-1988'); INSERT INTO arbeiten VALUES (10102,'p3','Gruppenleiter','01-jan-1989'); INSERT INTO arbeiten VALUES (25348,'p2','Sachbearbeiter','15-feb-1988'); INSERT INTO arbeiten VALUES (18316,'p2',NULL,'01-jun-1989'); INSERT INTO arbeiten VALUES (29346,'p2',NULL,'15-dec-1987'); INSERT INTO arbeiten VALUES (2581,'p3','Projektleiter','15-oct-1989'); INSERT INTO arbeiten VALUES (9031,'p1','Gruppenleiter','15-apr-1989'); INSERT INTO arbeiten VALUES (28559,'p1',NULL,'01-aug-1988'); INSERT INTO arbeiten VALUES (28559,'p2','Sachbearbeiter','01-feb-1988'); INSERT INTO arbeiten VALUES (9031,'p3','Sachbearbeiter','15-nov-1988'); INSERT INTO arbeiten VALUES (29346,'p1','Sachbearbeiter','01-apr-1989'); Beispiel 9.2 Laden Sie alle Reihen der Tabelle projekt. INSERT INTO projekt VALUES('p1','Apollo',120000.0); INSERT INTO projekt VALUES('p2','Gemini',95000.0); INSERT INTO projekt VALUES('p3','Merkur',186500.0); Beispiel 9.3 Laden Sie alle Reihen der Tabelle abteilung. INSERT INTO abteilung VALUES('a1','Beratung','Muenchen'); INSERT INTO abteilung VALUES('a2','Diagnose','Muenchen');
INSERT INTO abteilung VALUES('a3','Freigabe','Stuttgart');
Beispiel 9.4 Laden Sie alle Reihen der Tabelle mitarbeiter. INSERT INTO mitarbeiter VALUES(25348,'Keller','Hans','a3'); INSERT INTO mitarbeiter VALUES(10102,'Huber','Petra','a3'); INSERT INTO mitarbeiter VALUES(18316,'Mueller','Gabriele','a1'); INSERT INTO mitarbeiter VALUES(29346,'Probst','Andreas','a2'); INSERT INTO mitarbeiter VALUES(9031,'Meier','Rainer','a2'); INSERT INTO mitarbeiter VALUES(2581,'Kaufmann','Brigitte','a2'); -127-
INSERT INTO mitarbeiter VALUES(28559,'Mozer','Sibille','a1'); Falls wir die Datenwerte eines neuen Mitarbeiters der Firma einfügen wollen, kann dies auf verschiedene Weise geschehen. Beispiel 9.5 INSERT INTO mitarbeiter VALUES(15201,'Lang','Viktor',NULL); Diese INSERT-Anweisung entspricht in ihrer Form den INSERT-Anweisungen in den Beispielen 9.1 - 9.4. Wie aus den Beispielen 9.1 und 9.5 ersichtlich, können NULL-Werte durch die explizite Angabe des Schlüsselwortes NULL eingefügt werden. Falls nur ein Teil der Datenwerte einer Reihe eingefügt werden soll, muß die Liste der Spalten explizit angegeben werden. Dabei dürfen in der Liste nur solche Spalten ausgelassen werden, die die NULL-Werte erlauben. Jeder fehlenden Spalte in der Liste wird implizit der NULL-Wert zugewiesen. Beispiel 9.6 INSERT INTO mitarbeiter (m_nr, m_name, m_vorname) VALUES (15201,'Lang','Viktor'); Beispiel 9.6 entspricht dem Beispiel 9.5. Die Spalte abt_nr ist die einzige Spalte der Tabelle mitarbeiter, welche NULL-Werte erlaubt. (Alle drei anderen Spalten dieser Tabelle
sind in der CREATE TABLE-Anweisung mit der Angabe NOT NULL erstellt worden.) Falls eine Spalte, die keine NULL-Werte erlaubt, in der Liste der Spalten ausgelassen wird, wird vom System ein Fehler gemeldet. Die Reihenfolge der Spalten in der INSERT-Anweisung muß nicht unbedingt der Reihenfolge der Spalten in der CREATE TABLE-Anweisung entsprechen. In diesem Fall ist es unbedingt notwendig, die Liste der Spalten explizit anzugeben, ungeachtet dessen ob eine ganze Reihe oder ein Teil einer Reihe eingefügt werden soll. Beispiel 9.7 INSERT INTO mitarbeiter (m_name, m_vorname, abt_nr, m_nr) VALUES ('Lotter', 'Wolfgang', 'a1', 8413); 8.1.2 Einfügen mehrerer Reihen Mit der zweiten Form der INSERT-Anweisung werden die Reihen einer existierenden Tabelle ausgewählt und in einer anderen Tabelle eingefügt. Beispiel 9.8 Erstellen Sie die Tabelle aller Abteilungen, die in München ihren Standort haben, und laden Sie sie mit den entsprechenden Reihen aus der Tabelle abteilung. CREATE TABLE muench_abt (abt_nr CHAR(4) NOT NULL, abt_name CHAR(20) NOT NULL); INSERT INTO muench_abt(abt_nr, abt_name) SELECT abt_nr, abt_name -128-
FROM abteilung WHERE stadt = 'Muenchen'; Die neue Tabelle muench_abt hat dieselben Spalten wie die Tabelle abteilung, abgesehen von der Spalte stadt. Mit der SELECT-Anweisung werden alle Reihen der Tabelle abteilung ausgewählt, deren Spalte stadt den Datenwert Muenchen hat. Diese Reihen werden anschließend in der Tabelle muench_abt eingefügt. Die Reihen der Tabelle muench_abt können mit der Anweisung: SELECT * FROM muench_abt; ausgewählt werden. Das Ergebnis dieser Anweisung ist: abt_nr abt_name a1
Beratung
a2
Diagnose
(2 rows) Beispiel 9.9 Erstellen Sie eine Tabelle aller Sachbearbeiter, die im Projekt p2 arbeiten, und laden Sie sie mit den entsprechenden Reihen aus der Tabelle arbeiten. CREATE TABLE sach_arb (m_nr INTEGER NOT NULL, pr_nr CHAR(4) NOT NULL, einst_dat DATE); INSERT INTO sach_arb(m_nr, pr_nr, einst_dat) SELECT m_nr, pr_nr, einst_dat FROM arbeiten WHERE aufgabe = 'Sachbearbeiter' AND pr_nr = 'p2'; Die Tabelle sach_arb enthält nun folgende Reihen m_nr pr_nr
einst_dat
25348 p2
15-feb 1989
28559 p2
01-feb-1989
(2 rows) In den Beispielen 9.8 und 9.9 waren die beiden Tabellen muench_abt und sach_arb leer, bevor die Reihen mit der INSERT-Anweisung eingefügt wurden. Dies muß nicht grundsätzlich der Fall sein. Ist eine Tabelle nicht leer, werden die Reihen mit der INSERT-Anweisung immer angefügt. Mit Hilfe der INSERT-Anweisung ist es möglich, die Spalte(n) einer Tabelle zu löschen. Das folgende Beispiel zeigt diese Anwendung der INSERT-Anweisung, die für INGRES wichtig ist, weil INGRES-SQL die -nderung des Tabellenschemas mit Hilfe der ALTER TABLE-Anweisung nicht unterstützt. -129-
Beispiel 9.10 Löschen Sie die Spalte einst_dat in der Tabelle arbeiten. CREATE TABLE arb_hilf (m_nr INTEGER NOT NULL, pr_nr CHAR(4) NOT NULL, aufgabe CHAR(15)); INSERT INTO arb_hilf(m_nr, pr_nr, aufgabe) SELECT m_nr, pr_nr, aufgabe FROM arbeiten; DROP TABLE arbeiten; CREATE TABLE arbeiten (m_nr INTEGER NOT NULL, pr_nr CHAR(4) NOT NULL, aufgabe CHAR(15)); INSERT INTO arbeiten SELECT * FROM arb_hilf; DROP TABLE arb_hilf; In Beispiel 9.10 wurde zunächst eine Hilfstabelle arb_hilf erstellt, die, abgesehen von der zu löschenden Spalte einst_dat mit der Tabelle arbeiten identisch ist. Mit der ersten INSERT-Anweisung werden die Datenwerte aus der Tabelle arbeiten in der Tabelle arb_hilf eingefügt. Die Tabelle arbeiten wird danach gelöscht und noch einmal erstellt, nun jedoch ohne die Spalte einst_dat. Am Ende werden die Datenwerte aus der Hilfstabelle in der neuerstellten Tabelle arbeiten eingefügt, und die Hilfstabelle wird gelöscht. Das Einfügen der Reihen eines Views kann nicht uneingeschränkt durchgeführt werden. Diesem Thema ist ein Abschnitt des Kapitels 10 gewidmet.
8.2
Die UPDATE-Anweisung Die UPDATE-Anweisung ändert die Datenwerte der Reihen einer Tabelle. Diese Anweisung hat folgende Form: UPDATE tab SET spalte_1=ausdr_1 [,spalte_2=ausdr_2,...] [WHERE bedingung]; Bei der UPDATE-Anweisung werden die Reihen der Tabelle tab auf Grund der Bedingung in der WHERE-Klausel zunächst ausgewählt. Danach wird der Spalte spalte_1 der Wert ausdr_1 zugewiesen. Falls mehrere Spalten in der SET-Klausel angegeben sind, wird jeder von ihnen der entsprechende Wert zugewiesen. ausdr_1 kann eine Konstante oder ein Ausdruck sein. Die Angabe der WHERE-Klausel ist optional. Falls sie ausgelassen wird, werden alle Reihen der Tabelle geändert. Mit einer UPDATE-Anweisung kann nur eine Tabelle geändert werden. Das -ndern der Reihen eines Views entspricht dem -ndern der Reihen einer Basistabelle. Im Unterschied zu Basistabellen ist das - ndern der Reihen bei Views nur eingeschränkt -130-
möglich. In Kapitel 11 wird dies ausführlich behandelt. Beispiel 9.11 -ndern Sie die Aufgabe des Mitarbeiters mit der Personalnummer 18316 im Projekt p2. Er soll Gruppenleiter dieses Projektes werden. UPDATE arbeiten SET aufgabe = 'Gruppenleiter' WHERE m_nr = 18316 AND pr_nr = 'p2'; Mit dieser UPDATE-Anweisung wird nur eine Reihe der Tabelle arbeiten geändert, weil die Kombination beider Bedingungen mit den Spalten m_nr und pr_nr immer nur eine Reihe als Ergebnis liefert. Im obigen Beispiel wurde die Aufgabe des Mitarbeiters, die bis dahin unbekannt war (NULL-Wert), definiert. Im nächsten Beispiel wird den Datenwerten einer Spalte ein Ausdruck zugewiesen. Beispiel 9.12 Die Finanzmittel aller Projekte sollen geändert und in Schweizer Franken dargestellt werden. (Der augenblickliche Währungskurs soll bei 0,89 SFR für 1 DM sein.) UPDATE projekt SET mittel = mittel * 0.89; In Beispiel 9.12 werden alle Reihen der Tabelle projekt geändert, weil die WHERE-Klausel fehlt. Die geänderten Reihen der Tabelle projekt können mit der folgenden Anweisung SELECT * FROM projekt ausgewählt werden. Das Ergebnis dieser Anweisung ist pr_nr
pr_name
mittel
p1
Apollo
106800.000
p2
Gemini
84550.000
p3
Merkur
165985.000
(3 rows) Beispiel 9.13 Die Aufgaben von Frau Huber sollen, wegen längerer Krankheit, in allen Projekten auf NULL gesetzt werden. UPDATE arbeiten SET aufgabe = NULL WHERE m_nr = (SELECT m_nr FROM mitarbeiter WHERE m_name = 'Huber'); Im obigen Beispiel beinhaltet die WHERE-Klausel der UPDATEAnweisung eine Unterabfrage. Das Ergebnis dieser Unterabfrage darf in diesem Fall nur einen Datenwert liefern. -131-
8.3
Die DELETE-Anweisung Mit der DELETE-Anweisung werden Reihen aus einer Tabelle gelöscht. Die allgemeine Form dieser Anweisung ist: DELETE FROM tab [WHERE bedingung]; Die WHERE-Klausel liefert die Reihen, die aus einer Tabelle gelöscht werden. Die explizite Angabe von Spalten in einer DELETE-Anweisung ist nicht notwendig, weil alle Datenwerte einer oder mehrerer Reihen gelöscht werden. Beispiel 9.14 Löschen Sie die Datenwerte der Gruppenleiter aller Projekte. DELETE FROM arbeiten WHERE aufgabe = 'Gruppenleiter'; Die WHERE-Klausel in der DELETE-Anweisung kann, wie bei der UPDATE-Anweisung auch, eine Unterabfrage enthalten. Beispiel 9.15 Die Mitarbeiterin namens Mozer scheidet aus der Firma aus. Löschen Sie zunächst alle Reihen in der Tabelle arbeiten, die diese Mitarbeiterin betreffen, und danach auch die entsprechende Reihe der Tabelle mitarbeiter. DELETE FROM arbeiten WHERE m_nr = (SELECT m_nr FROM mitarbeiter WHERE m_name = 'Mozer'); DELETE FROM mitarbeiter WHERE m_name = 'Mozer'; Die Angabe der WHERE-Klausel in der DELETE-Anweisung ist optional. Ist diese Klausel nicht angegeben, werden alle Reihen einer Tabelle gelöscht. Beispiel 9.16 Löschen Sie alle Reihen der Tabelle arbeiten. DELETE FROM arbeiten;
Das Löschen der Reihen eines Views kann nur mit Einschränkungen ausgeführt werden. In Kapitel 11 wird das Löschen der Reihen eines Views beschrieben. Hinweis. Die DELETE-Anweisung unterscheidet sich von der DROP TABLEAnweisung dadurch, daß sie alle Reihen einer Tabelle löscht, während die DROP TABLE-Anweisung sowohl alle Reihen als auch die Tabellen- und Spaltenspezifikation einer Tabelle löscht.
Aufgaben A.9.1 .................................................................................................... Erstellen Sie eine neue Tabelle aller Mitarbeiter, die in
den Projekten a1 und a2 arbeiten, und laden Sie sie mit den entsprechenden Reihen der Tabelle mitarbeiter. -132-
A.9.2
A.9.3 A.9.4 A.9.5
A.9.6 A.9.7
Erstellen Sie eine neue Tabelle aller Mitarbeiter, die im Jahr 1989 eingestellt worden sind, und laden Sie sie mit den entsprechenden Reihen aus der Tabelle mitarbeiter. Ändern Sie die Aufgabe aller Gruppenleiter in Projekt p1. Sie sollen ab sofort als Sachbearbeiter tätig sein. Die Mittel aller Projekte sind bis auf weiteres nicht festgelegt. Weisen Sie den Mitteln den NULL-Wert zu. Ändern Sie die Aufgabe der Mitarbeiterin mit der Personalnummer 28559. Sie soll ab sofort in allen Projekten Gruppenleiterin werden. Löschen Sie alle Reihen der Tabelle abteilung, deren Standort München ist. Das Projekt p3 ist beendet worden. Löschen Sie zunächst alle Daten der Mitarbeiter in der Tabelle mitarbeiter, die in diesem Projekt gearbeitet haben, und danach auch die entsprechende Reihe der Tabelle projekt.
-133-
Views
Views ............................................................................................................................................134 10
Views ..................................................................................................................................134 10.1 Datendefinitionsanweisungen und Views 134 10.1.1 Erstellen von Views 135 10.1.2 Views löschen 138 10.2 Abfragen in Views 139 10.3 Ändern eines Views 139 10.3.1 INSERT-Anweisung und View 140 10.3.2 UPDATE-Anweisung und View 142 10.3.3 DELETE-Anweisung und View 143 Aufgaben 144
9
Views Dieses Kapitel ist ausschließlich den Views gewidmet. Die Struktur des Kapitels ähnelt der Beschreibung der Basistabellen aus den Kapiteln 7 bis 9. Zunächst werden die Anweisungen zur Datendefinition beschrieben. Im zweiten Teil werden die Anweisungen SELECT, UPDATE, DELETE und INSERT erörtert, wobei die SELECT-Anweisung getrennt von den übrigen drei behandelt wird. Im Unterschied zu Basistabellen gelten bei Datenmanipulationsanweisungen für Views einige Einschränkungen, die am Ende des jeweiligen Abschnitts erklärt werden.
9.1
Datendefinitionsanweisungen und Views In den bisherigen Kapiteln sind ausschließlich die Basistabellen erörtert worden. Eigenschaft aller Basistabellen ist, daß sie physikalisch existieren. Eine Basistabelle enthält also Objekte, die alle auf der Platte gespeichert sind. Im Unterschied zu Basistabellen belegen Views keinen Speicherplatz. Views werden immer aus existierenden Basistabellen abgeleitet, und das System trägt die Eigenschaften jedes Views in die Systemtabellen ein. Lediglich die Einträge in den verschiedenen Systemtabellen existieren physikalisch von einem View. Deswegen heißen Views auch virtuelle Tabellen. In der Systemtabelle iiviews befinden sich die wichtigsten Einträge für Views. iiviews beinhaltet je eine Reihe für jedes im System erstellte View. Diese Systemtabelle enthält -134-
mehrere Spalten, von welchen die erste, table_name den Namen des Views enthält. Die zweite Spalte, table_owner, enthält den Eigentümernamen des Views. Wichtig ist auch die Spalte text_segment, die die SELECT-Anweisung beinhaltet, die aus der CREATE VIEW-Anweisung übernommen wird. 9.1.1
Erstellen von Views Jedes View wird mit der Anweisung CREATE VIEW erstellt. Die allgemeine Form dieser Anweisung ist: CREATE VIEW view_name [(spalten_liste)] AS select_anw [WITH CHECK OPTION]; view_name ist der Name des neuerstellten Views. select_anw kennzeichnet die SELECT-Anweisung, mit der die Auswahl der Reihen und Spalten aus der Basistabelle durchgeführt wird. spalten_liste ist eine Liste der Namen der Viewspalten. Falls diese optionale Angabe ausgelassen wird, werden die Namen der Spalten aus der Projektion der SELECT-Anweisung übernommen. Die Angabe "WITH CHECK OPTION" wird später in diesem Kapitel beschrieben. Die Erstellung eines Views kann aus verschiedenen Gründen erfolgen. In umfangreicheren Datenbankanwendungen enthalten einige Basistabellen eine sehr große Anzahl von Reihen bzw. Spalten. Für den Benutzer, der z.B. nur mit einem kleinen Ausschnitt einer Basistabelle arbeitet, ist es nicht notwendig, alle Spalten der Basistabelle zu kennen. In diesem Fall ist es sinnvoll, ein View zu erstellen, das nur die Teile der Basistabelle enthält, die für den Benutzer von Bedeutung sind. Genauso kann es sinnvoll sein, ein View zu erstellen, falls man gewissen Benutzern den Zugriff auf eine oder mehrere Spalten einer Basistabelle untersagen will. Dabei kann es sich um die Spalte mit den Gehältern der Mitarbeiter oder um die Spalten mit den sicherheitsrelevanten Daten der Firma handeln. (Dieser Aspekt des Views wird hier nicht weiter verfolgt; er wird ausführlich in Kapitel 14 erörtert.). Beispiel 10.1 Erstellen Sie ein View, das alle Daten der Sachbearbeiter der Firma beinhaltet. CREATE VIEW v_sach_arb AS SELECT m_nr, pr_nr, einst_dat FROM arbeiten WHERE aufgabe = 'Sachbearbeiter'; In Beispiel 10.1 werden mit der SELECT-Anweisung die Reihen der Basistabelle arbeiten ausgewählt, die die Bedingung in der WHERE-Klausel erfüllen. Alle ausgewählten Reihen bilden das neuerstellte View v_sach_arb. m_nr pr_nr aufgabe 9031 p3 Sachbearbeiter
einst_dat 15-nov-1988 -135-
28559 28559 9031 2581 29346 18316 25348 10102 10102 29346
p2 p1 p1 p3 p2 p2 p2 p3 p1 p1
Sachbearbeiter Gruppenleiter Projektleiter
Sachbearbeiter Gruppenleiter Projektleiter Sachbearbeiter
01-feb-1989 01-aug-1988 15-apr-1989 15-oct-1989 15-dec-1987 01-jun-1989 15-feb-1989 01-jan-1989 01-oct-1988 01-apr-1989
Abb.10-1 Die Basistabelle arbeiten Abbildung 10-1 zeigt die Basistabelle arbeiten, wobei das View v_sach_arb grau unterlegt dargestellt ist. Jeder Anwender, der ein View benutzt, arbeitet mit ihm genau wie mit jeder anderen Basistabelle. Gewisse Einschränkungen, die für Views gelten, werden im Laufe dieses Kapitels nacheinander erörtert. Die Abfragen, die ein View betreffen, werden tatsächlich auf der zugrundeliegenden Basistabelle durchgeführt. (Wir erinnern noch einmal daran, daß ein View physikalisch nicht existiert; es existieren nur Einträge in Systemtabellen, die es definieren.). Folgende Abfrage auf das View v_sach_arb SELECT m_nr FROM v_sach_arb WHERE pr_nr = 'p2'; wird vom System auf Grund der Definition des Views in die Abfrage auf die zugrundeliegende Basistabelle arbeiten umgewandelt: SELECT m_nr FROM arbeiten WHERE pr_nr = 'p2' AND aufgabe = 'Sachbearbeiter'; Jedes Ändern einer Basistabelle, aus der ein View abgeleitet ist, betrifft automatisch auch das View. Genauso werden alle Änderungen eines Views automatisch auf die zugrundeliegende Basistabelle übertragen und durchgeführt. (Für das Ändern eines Views gelten gewisse Einschränkungen, die in einem späteren Abschnitt dieses Kapitels beschrieben sind.). In Beispiel 10.1 wurde ein View erstellt, in dem einige Reihen aus der Basistabelle arbeiten ausgewählt wurden. Genauso ist es möglich, einige Spalten bzw. einige Reihen und Spalten einer Basistabelle auszuwählen und ein View zu erstellen. Beispiel 10.2 Leiten Sie aus der Basistabelle projekt ein View ab, bei dem die Spalte mittel nicht sichtbar ist. CREATE VIEW v_teil_pr AS SELECT pr_nr, pr_name FROM projekt; Das View v_teil_pr beinhaltet alle Reihen der Basistabelle -136-
projekt, abgesehen von der Spalte mittel. Wie aus der allgemeinen Form der CREATE VIEW-Anweisung ersichtlich, ist die Angabe der Spaltennamen eines Views optional. Falls die Spaltennamen des Views identisch mit den Spaltennamen der Basistabelle sein sollen, können sie weggelassen werden. Diese Eigenschaft ist in Beispiel 10.1 benutzt worden. In der Praxis ist dies auch meist der Fall. Andererseits ist es auch möglich, andere Namen für die Spaltennamen des Views zu vergeben. In zwei Fällen ist es sogar notwendig, die Spaltennamen eines Views explizit anzugeben, und zwar: a) wenn eine Spalte in einem View aus einem Ausdruck oder einer Aggregatfunktion abgeleitet ist; b) wenn Spaltennamen in einem View nicht eindeutig sind. Beispiel 10.3 Erstellen Sie ein View, das folgende Spalten beinhaltet: Die Projektnummer und die Anzahl der dem Projekt zugehörenden Mitarbeiter. CREATE VIEW v_arb_anzahl (pr_nr, anzahl) AS SELECT pr_nr, COUNT(*) FROM arbeiten GROUP BY pr_nr; In Beispiel 10.3 müssen die Spaltennamen des Views v_arb_anzahl explizit angegeben werden, weil die Projektion in der SELECT-Anweisung die Aggregatfunktion COUNT(*) beinhaltet. Beispiel 10.4 Erstellen Sie ein View, das alle Daten der Mitarbeiter beinhaltet, deren Standort München ist. CREATE VIEW v_mch(m_nr,name,vorname,abt_nr,nr,aufg,stadt) AS SELECT mitarbeiter.*, abteilung.* FROM mitarbeiter, abteilung WHERE mitarbeiter.abt_nr = abteilung.abt_nr AND stadt = 'Muenchen'; Die Projektion beinhaltet zwei Spalten mit demselben Namen: abt_nr. Deswegen ist es notwendig, die Spaltennamen des Views v_mch explizit anzugeben. (Die andere Möglichkeit wäre die Benutzung eines natürlichen Joins in der SELECT-Anweisung anstatt des Equijoins. In dem Fall wären alle Spaltennamen des Views eindeutig.). Die optionale Angabe "WITH CHECK OPTION" in der CREATE VIEW-Anweisung prüft alle Reihen, die über das View in der Basistabelle eingefügt oder geändert werden. Dabei wird die Bedingung in der SELECT-Anweisung innerhalb CREATE VIEW überprüft und, falls sie nicht erfüllt ist, das Ändern bzw. Einfügen der Reihen mit einer Fehlermeldung abgewiesen. Falls die Angabe "WITH CHECK OPTION" fehlt, werden alle Reihen ohne Überprüfung über das View in der Basistabelle eingefügt bzw. geändert. Es werden also auch die Reihen eingefügt bzw. geändert, die die Bedingung in der -137-
SELECT-Anweisung nicht erfüllen. Diese Reihen können anschließend mit demselben View nicht abgefragt werden. Die Beispiele 10.2 und 10.6 zeigen die Verwendng der Angabe "WITH CHECK OPTION". Die SELECT-Anweisung innerhalb CREATE VIEW stellt eine Unterabfrage dar. Als solche darf sie den UNION-Operator und die ORDER BY-Klausel nicht beinhalten. Abgesehen von diesen beiden Einschränkungen kann jede mögliche Form der SELECT-Anweisung benutzt werden. Ein View kann nicht nur aus einer Basistabelle, sondern auch aus einem existierenden View abgeleitet werden. Beispiel 10.5 Erstellen Sie ein View, das die Personalnummern aller Sachbearbeiter in Projekt p2 beinhaltet. CREATE VIEW v_p2_sach AS SELECT m_nr FROM v_sach_arb WHERE pr_nr = 'p2'; Für die Erstellung des Views v_p2_sach haben wir ein schon existierendes View v_sach_arb (Beispiel 10.1) benutzt. Alle Abfragen auf das View v_p2_sach werden vom System in die Abfragen auf die Basistabelle arbeiten umgewandelt. Die SELECT-Anweisung innerhalb CREATE VIEW kann Spalten aus zwei oder mehreren Basistabellen beinhalten. In Beispiel 10.4 sind mit Hilfe des Join-Operators die Basistabellen mitarbeiter und abteilung verknüpft und das View v_mch erstellt. Genauso ist es möglich, Basistabellen mit Views bzw. mehrere Views miteinander zu verknüpfen. Das folgende Beispiel zeigt die Verknüpfung einer Basistabelle mit einem View. Beispiel 10.6 Erstellen Sie ein View, das die Personalnummern aller Mitarbeiter enthält, die im Projekt Apollo arbeiten. Diese Aufgabe soll mit Hilfe des Views v_teil_pr (Beispiel 10.2) gelöst werden. CREATE VIEW v_arb_teilpr AS SELECT m_nr FROM arbeiten, v_teil_pr WHERE arbeiten.pr_nr = v_teil_pr.pr_nr AND pr_name = 'Apollo'; 9.1.2
Views löschen Mit der Anweisung DROP VIEW view_name wird ein existierendes View gelöscht. Das Löschen eines Views bedeutet das Entferne n aller Einträge aus Systemtabellen, die im Zusammenhang mit dem View stehen. Beispiel 10.7 DROP VIEW v_arb_anzahl; In diesem Beispiel wird das View v_arb_anzahl, das in -138-
Beispiel 10.3 erstellt wurde, gelöscht. Wenn ein View gelöscht wird, werden auch alle aus ihm abgeleiteten Views gelöscht. Beispiel 10.8 DROP VIEW v_sach_arb; Das Löschen des Views v_sach_arb verursacht auch das implizite Löschen des Views v_p2_sach (siehe Beispiele 10.1 und 10.5).
9.2
Abfragen in Views Wie wir schon im vorherigen Abschnitt gezeigt haben, wird jede Abfrage auf ein View vom System in eine entsprechende Abfrage auf die zugrundeliegende Basistabelle umgewandelt. Beispiel 10.9 Erstellen Sie ein View mit den Personalnummern und Namen aller Mitarbeiter, die der Abteilung a2 angehören. Danach wählen Sie die Mitarbeiter aus, deren Namen mit dem Buchstaben "M" beginnen. CREATE VIEW v_a2_mit AS SELECT m_nr, m_name FROM mitarbeiter WHERE abt_nr = 'a2'; SELECT m_name FROM v_a2_mit WHERE m_name LIKE 'M%'; Das Ergebnis ist: m_name Meier (1 row) Die SELECT-Anweisung aus Beispiel 10.9 wird vom System in die folgende Form umgewandelt: SELECT m_name FROM mitarbeiter WHERE m_name LIKE 'M%' AND abt_nr = 'a2'; Für alle Abfragen auf Views gilt folgendes: Jede Abfrage auf ein View ist gültig, falls die zugrundeliegende Abfrage auf die Basistabelle gültig ist.
9.3
Ändern eines Views In diesem Abschnitt wird die Verwendung der drei SQL-Anweisungen - INSERT, UPDATE und DELETE auf Views erörtert. Grundsätzlich gilt für all diese Anweisungen, daß sie nur eingeschränkt auf Views angewendet werden können.
-139-
9.3.1
INSERT-Anweisung und View Das Einfügen der Reihen eines Views bedeutet das tatsächliche Einfügen der entsprechenden Reihen in der zugrundeliegenden Basistabelle. Beispiel 10.10 CREATE VIEW v_abt_teil AS SELECT abt_nr, abt_name FROM abteilung; INSERT INTO v_abt_teil VALUES ('a4', 'Test'); In Beispiel 10.10 wurde ein View aus der Basistabelle abteilung erstellt, in dem die ersten beiden Spalten dieser Basistabelle ausgewählt wurden. Die anschließende INSERT-Anweisung weist diesen Spalten die Werte 'a4' und 'Test' zu, während die nicht angegebene Spalte stadt der Basistabelle den NULL-Wert zugewiesen bekommt. Die INSERT-Anweisung wird ordnungsgemäß durchgeführt, weil die Spalte stadt NULL-Werte zuläßt. Beispiel 10.11 CREATE VIEW v_pr_teil AS SELECT pr_name, mittel FROM projekt; INSERT INTO v_pr_teil VALUES ('Luna', 2286000.00); Beispiel 10.11 wird nicht ordnungsgemäß durchgeführt. Die INSERT-Anweisung weist den Spalten pr_name und mittel die Werte Luna und 2286000.00 zu, während die nicht angegebene Spalte pr_nr der Basistabelle den NULL-Wert zugewiesen bekommt. Dies wird vom System mit einer Fehlermeldung abgewiesen, weil die Spalte pr_nr keine NULL-Werte erlaubt. Beispiel 10.12 CREATE VIEW v_arb_1988 AS SELECT m_nr, pr_nr, einst_dat FROM arbeiten WHERE einst_dat BETWEEN '01-jan-1988' AND '31-dec-1988' WITH CHECK OPTION; INSERT INTO v_arb_1988 VALUES (22334, 'p2', '15-apr-1989'); In Beispiel 10.12 wird die Rolle der "WITH CHECK OPTION"-Angabe gezeigt. In der INSERT-Anweisung dieses Beispiels wird überprüft, ob der Datenwert der Spalte einst_dat ('15-apr-1989') die Bedingung in der WHERE-Klausel der SELECT-Anweisung erfüllt. Da dies nicht der Fall ist, wird die INSERT-Anweisung mit einer Fehlermeldung abgewiesen. Beispiel 10.13 CREATE VIEW v_arb_1988 AS SELECT m_nr, pr_nr, einst_dat FROM arbeiten WHERE einst_dat BETWEEN '01-jan-1988' AND '31-dec-1988'; INSERT INTO v_arb_1988 VALUES (22334, 'p2', '15-apr-1989'); -140-
SELECT * FROM v_arb_1988; Das Ergebnis ist: m_nr 28559 10102 9031
pr_nr p1 p1 p3
einst_dat 01-aug-1988 01-oct-1988 15-nov-1988
(3 rows) Beispiel 10.13 unterscheidet sich von Beispiel 10.12 nur durch die fehlende Angabe "WITH CHECK OPTION" und der zusätzlichen SELECT-Anweisung. In diesem Beispiel wird die INSERT-Anweisung ordnungsgemäß durchgeführt und die angegebene Reihe in der Basistabelle arbeiten eingefügt. Die anschließende SELECT-Anweisung wählt, wie das Ergebnis zeigt, die neu eingefügte Reihe nicht aus, weil sie mit dem View v_arb_1988 nicht abgefragt werden kann. Falls das Einfügen der Reihen in der zugrundeliegenden Basistabelle mit Hilfe eines Views durchgeführt wird, gelten dafür folgende Einschränkungen: a) das View darf nur aus einer einzigen Tabelle abgeleitet werden; b) keine Spalte des Views darf aus einer Aggregatfunktion abgeleitet werden; c) keine Spalte des Views darf aus einer skalaren Funktion, einer Konstanten oder einem arithmetischen Ausdruck abgeleitet werden; d) die SELECT-Anweisung innerhalb CREATE VIEW darf die Angabe DISTINCT nicht enthalten; e) die SELECT-Anweisung innerhalb CREATE VIEW darf die GROUP BY-Klausel nicht enthalten. Beispiel 10.14 CREATE VIEW v_pr_sum(summe) AS SELECT SUM(mittel) FROM projekt; SELECT * FROM v_pr_sum; Das Ergebnis ist: summe 281500,000 (1 row) Beispiel 10.14 zeigt, warum keine Spalte eines Views aus einer Aggregatfunktion abgeleitet werden darf. Wie aus dem Ergebnis des Beispiels ersichtlich, ist jede INSERT-Anweisung mit dem View v_pr_sum sinnlos.
-141-
9.3.2
UPDATE-Anweisung und View Das Ändern der Datenwerte eines Views bedeutet das tatsächliche Ändern der entsprechenden Datenwerte der zugrundeliegenden Basistabelle. Beispiel 10.15 Erstellen Sie ein View, das Personalnummer und Aufgabe aller Mitarbeiter in Projekt p1 beinhaltet. Ändern Sie anschließend die Aufgabe aller Projektleiter in den NULL-Wert. CREATE VIEW v_arb_p1 AS SELECT m_nr, aufgabe FROM arbeiten WHERE pr_nr = 'p1'; UPDATE var_arb_p1 SET aufgabe = NULL WHERE aufgabe = 'Projektleiter'; Die UPDATE-Anweisung in Beispiel 10.15 wird vom System in folgende Anweisung umgewandelt: UPDATE arbeiten SET aufgabe = NULL WHERE pr_nr = 'p1' AND aufgabe = 'Projektleiter'; Die Angabe "WITH CHECK OPTION" ist für die UPDATE-Anweisung auf dieselbe Weise wie für die INSERT-Anweisung wirksam. Beispiel 10.16 CREATE VIEW v_pr_100 AS SELECT pr_nr, mittel FROM projekt WHERE mittel > 100000 WITH CHECK OPTION; UPDATE v_pr_100 SET mittel = 92500 WHERE pr_nr = 'p2'; Die UPDATE-Anweisung in Beispiel 10.16 wird abgewiesen, weil der geänderte Datenwert der Spalte mittel in Projekt p2 die WHERE-Bedingung nicht erfüllt. Falls das Ändern der Datenwerte in der zugrundeliegenden Basistabelle mit Hilfe eines Views durchgeführt wird, gelten für dieses View dieselben Einschränkungen wie für die INSERTAnweisung. Beispiel 10.17 CREATE VIEW v_pr_usd (pr_nr, mittel_usd) AS SELECT pr_nr, mittel * 0.55 FROM projekt WHERE mittel > 100000; SELECT * FROM v_pr_usd; Das Ergebnis ist: pr_nr p3
mittel_usd 102575,0000 -142-
(1 row) Beispiel 10.17 zeigt, warum keine Spalte eines Views aus einem arithmetischen Ausdruck abgeleitet werden darf. Das View v_pr_usd beinhaltet alle Reihen der Basistabelle projekt, deren Mittel größer als DM 100.000 sind. Zusätzlich dazu enthält dieses View eine Spalte mittel_usd, die aus der Spalte mittel durch Multiplizieren abgeleitet ist. (Die Geldmittel sind damit statt in DM in US$ angegeben.). Aus dem Ergebnis der anschließenden SELECT-Anweisung wird offensichtlich, daß jede UPDATE-Anweisung mit der Spalte mittel_usd nicht möglich ist. 9.3.3
DELETE-Anweisung und View Das Löschen der Reihen einer Basistabelle kann auch mit Hilfe eines abgeleiteten Views durchgeführt werden. Beispiel 10.18 CREATE VIEW v_arb_p1 AS SELECT m_nr, aufgabe FROM arbeiten WHERE pr_nr = 'p1'; DELETE FROM v_arb_p1 WHERE aufgabe = 'Sachbearbeiter'; Die DELETE-Anweisung in Beispiel 10.18 wird vom INGRES-System in folgende Anweisung umgewandelt: DELETE FROM arbeiten WHERE pr_nr = 'p1' AND aufgabe = 'Sachbearbeiter'; Wie für das Ändern und Einfügen gelten auch für das Löschen der Reihen mit Hilfe eines Views einige Einschränkungen: a) das View darf nur aus einer einzigen Tabelle abgeleitet werden; b) keine Spalte des Views darf aus einer Aggregatfunktion abgeleitet werden; c) die SELECT-Anweisung innerhalb CREATE VIEW darf die GROUP BY-Klausel nicht enthalten; d) die SELECT-Anweisung innerhalb CREATE VIEW darf die Angabe DISTINCT nicht enthalten. Im Unterschied zur INSERT- und UPDATE-Anweisung erlaubt die DELETE-Anweisung die Verwendung solcher Views, die aus einem arithmetischen Ausdruck, einer skalaren Funktion oder einer Konstante abge leitet sind. Beispiel 10.19 CREATE VIEW v_pr_mult(mult_f) AS SELECT mittel* 0.55 FROM projekt; DELETE FROM v_pr_mult; Mit der DELETE-Anweisung werden alle Reihen der Tabelle projekt, die dem View v_pr_mult zugrunde liegt, gelöscht.
-143-
Aufgaben A.10.1 Erstellen Sie ein View, das die Daten aller Mitarbeiter enthält, die im Jahr 1988 eingestellt worden sind. A.10.2 Erstellen Sie ein View, das die Daten aller Mitarbeiter enthält, die der Abteilung a3 angehören. A.10.3 Erstellen Sie ein View, das Namen und Vornamen aller Mitarbeiter beinhaltet, die im Projekt p3 arbeiten. A.10.4 Überprüfen Sie, ob Ihre Lösung der Aufgabe A.10.3 ein View darstellt, das modifizierbar ist. Ist das nicht der Fall, erstellen Sie ein solches View. A.10.5 Erstellen Sie ein View, das Personalnummer und Aufgabe aller Mitarbeiter enthält, die im Projekt Merkur arbeiten. A.10.6 Erstellen Sie ein View, das Namen und Vornamen aller Mitarbeiter enthält, deren Personalnummer kleiner als 10000 ist. Das View soll die WHERE-Klausel jeder UPDATEbzw. DELETE-Anweisung überprüfen. A.10.7 Schreiben Sie für das in A.10.6 erstellte View eine INSERT-Anweisung, die vom INGRES-System akzeptiert wird, und eine DELETE-Anweisung, die abgewiesen wird.
-144-
INGRES-Data Dictionary und Systemkatalog
INGRES-Data Dictionary und Systemkatalog.............................................................................145 11
INGRES-Data Dictionary und Systemkatalog...................................................................145 11.1 INGRES-Master-Datenbank 145 11.2 INGRES-Systemkatalog 146 11.3 Abfragen auf Systemtabellen
149
10 INGRES-Data Dictionary und Systemkatalog In diesem Kapitel werden das INGRES-Data Dictionary behandelt und Systemkataloge beschrieben. Zuerst wird die INGRES-Master-Datenbank und der Zugriff auf sie erläutert. Danach werden die wichtigsten Tabellen des Systemkatalogs beschrieben. In einem weiteren Abschnitt werden praktische Abfragen auf gewählte Systemtabellen gezeigt.
10.1 INGRES-Master-Datenbank Die INGRES-Master-Datenbank (iidbdb) kennzeichnet eine Datenbank, die alle übergeordneten Informationen wie Benutzer- und Datenbanknamen enthält. Weil sie die globale Information bezüglich aller Datenbanken eines INGRES-Systems beinhaltet, kann man sie als oberste Ebene des INGRES-Data Dictionarys betrachten. Die Master-Datenbank iidbdb wird bei der Installation eines INGRES Systems automatisch erstellt. Sie hat dieselbe Form wie jede andere INGRES-Datenbank, kann aber nur vom INGRES-Systemadministrator geändert werden. Die Aktualisierung dieser Datenbank wird bei jeder Aktion, wie etwa bei der Erstellung einer neuen Datenbank oder bei der Einrichtung eines neuen Benutzers, durchgeführt. Das INGRES-System unterstützt zwei Dienstprogramme, accessdb und catalogdb, die den Zugriff auf iidbdb ermöglichen. accessdb kann nur vom INGRES-Systemadministrator benutzt werden, um iidbdb sowohl zu ändern als auch abzufragen, während catalogdb jedem DBA nur Abfragen erlaubt, die ihn selbst betreffen. Die beiden Dienstprogramme werden ausführlich in Kapitel 15 beschrieben. Eine weitere Möglichkeit, Abfragen auf iidbdb zu erstellen, bieten die von Benutzern selbst erstellten Anwendungsprogramme.
-145-
10.2 INGRES-Systemkatalog Den zweiten, logisch untergeordneten Teil des INGRES-Data Dictionarys bildet der Systemkatalog. Der Systemkatalog beinhaltet interne Informationen, die das INGRES-System für einen reibungslosen Ablauf benötigt. Im Systemkatalog befindet sich die Information über Tabellen, Spalten, Indexe, Zugriffsrechte, Format- und Listenprogramme usw. Diese Information ist für das einwandfreie Funktionieren des Systems unerläßlich. Bei INGRES besteht der Systemkatalog aus Systemtabellen, die in ihrem Aufbau den Benutzertabellen entsprechen. Im Unterschied zu den Benutzertabellen haben die Namen aller Systemtabellen das Präfix "ii". INGRES hat zwei Systemkatalogtypen - den Standard-Systemkatalog und - den erweiterten Systemkatalog. Der Standard-Systemkatalog ermöglicht dem Benutzer, die Informationen über logische Datenbankobjekte (Tabellen, Views, Indexe usw.) der existierenden Datenbanken abzufragen. Der erweiterte Systemkatalog enthält Informationen über Anwendungen, die mit QBF, RBF oder anderen front-end-Produkten von INGRES erstellt worden sind. Der wichtigste Unterschied zwischen diesen beiden Systemkatalogen ist, daß der Standard-Systemkatalog die Systemtabellen mit allgemein wichtigen Informationen enthält, während der erweiterte Katalog spezifische Informationen bezüglich jedes einzelnen INGRES-Subsystems besitzt. Deswegen werden wir im weiteren Verlauf dieses Kapitels nur die Tabellen des Standard-Systemkatalogs beschreiben. Die Informationen über einzelne Systemtabellen des erweiterten Systemkatalogs finden Sie in den entsprechenden INGRES-Manualen. Hinweis. Die Anwendungen eines Benutzers haben weder auf dem Standard- noch auf dem erweiterten Systemkatalog direkten Zugriff. Jede Abfrage auf Systemtabellen in einer Anwendung verwendet die Standard-Katalog-Schnittstelle ("Standard-Catalog-Interface"), um auf Systemtabellen der beiden Katalogtypen zuzugreifen. Die Standard-Katalog-Schnittstelle besteht aus Views, die von INGRES unterstützt werden. Der Grund dafür liegt darin, daß INGRES sich eine rseits die Änderung der Systemtabellen in den künftigen Versionen vorbehält, andererseits dem Benutzer eine standardisierte Schnittstelle, die auch in den künftigen INGRES-Versionen gelten wird, zur Verfügung stellen möchte. Im folgenden werden die wichtigsten Views des Standard-Systemkatalogs mit den dazugehörigen Spalten dargestellt.
iitables -146-
Die wichtigste und in den Abfragen meistbenutzte Systemtabelle. Sie enthält je eine Reihe für die Beschreibung jedes Objektes (Tabelle, View oder Index). Die wichtigsten Spalten dieser Tabelle sind: Spalte table_name table_owner create_ date table_type
Beschreibung Der Name des Objektes (Tabelle, View oder Index). Der Eigentümer des Objektes. Das Erstellungsdatum des Objektes Der Objekttyp. "T" definiert eine Tabelle, "V" ein View und "I" einen Index. table_subtype Der Typ einer Tabelle bzw. eines Views bezüglich der Systemzugehörigkeit. "N" definiert ein Objekt einer lokalen INGRES-Datenbank, "L" ein Objekt aus dem INGRES-Netz und "I" ein Objekt eines fremden Datenbanksystems table_stats besagt, ob ein Eintrag des Objektes in der Systemtabelle iistats vorhanden ist ("Y") oder nicht ("N"). table_indexes besagt, ob ein Eintrag des Objektes in der Systemtabelle iiindexes vorhanden ist ("Y") oder nicht ("N"). num_rows Die geschätzte Reihenanzahl der Tabelle. Falls die Anzahl unbekannt ist, wird der Wert -1 eingetragen. number_page Die geschätzte Anzahl der physikalischen Seiten, die s einer Tabelle gehören. Falls die Anzahl unbekannt ist, wird der Wert -1 eingetragen.
iicolumns Diese Tabelle beinhaltet Information über die Spalten einer Tabelle bzw. eines Views. Für jedes dieser Objekte existieren ein oder mehrere Einträge in iicolumns. Die wichtigsten Spalten dieser Tabelle sind: Spalte table_name table_owner column_name column_datatyp e column_length column_nulls key_sequence
Beschreibung Der Tabellenname. Der Eigentümer der Tabelle. Der Name der Spalte. Der Datentyp der Spalte. Die physikalische Länge der Spalte. besagt, ob die Spalte NULL-Werte enthalten darf oder nicht. besagt, ob die Spalte einen Teil des SpeicherstrukturIndex bildet.
iiviews Diese Tabelle beinhaltet einen oder mehrere Einträge für jedes View einer Datenbank. Die wichtigsten Spalten dieser Tabelle sind: Spalte table_name
Beschreibung Der Name des Views. -147-
table_owner check_option
Der Eigentümer des Views. besagt, ob das View mit der Angabe "WITH CHECK OPTION" definiert ist ("Y") oder nicht ("N"). (Für die Definition der Angabe "WITH CHECK OPTION" siehe Kapitel 11.). Der Text, der die Definition des Views enthält.
text_segment
iiindexes Diese Systemtabelle beinhaltet je einen Eintrag für jedes Objekt der Tabelle iitables vom Typ "I". Die wichtigsten Spalten sind: Spalte index_name index_owner base_name base_owner unique_rule
Beschreibung Der Indexname Der Eigentümer des Index. Der Name der zugehörigen Tabelle. Der Eigentümer der Tabelle. besagt, ob jeder Datenwert der indizierten Spalte nur einmal ("U") oder mehrmals ("D") vorkommen kann.
iiindex_columns Diese Tabelle beinhaltet je einen Eintrag für diejenigen Spalten, die einen Index bilden. Die wichtigsten Spalten sind: Spalte index_name index_owner column_name column_sequenc e
Beschreibung Der Name des Index, der die Spalte enthält. Der Eigentümer des Index. Der Spaltenname. Definiert die Stelle, die die Spalte innerhalb des Speicherstruktur-Index eines Sekundär-Index einnimmt.
iistats Diese Tabelle beinhaltet je einen Eintrag für jede Spalte, für die Statistiken vorliegen. Die wichtigsten Spalten dieser Systemtabelle sind: Spalte table_name table_owner column_name has_unique
Beschreibung Der Name der Tabelle, zu der die Spalte gehört. Der Eigentümer der Tabelle. Der Name der Spalte, für die die Auswertung vorliegt. besagt, ob die Spalte eindeutige ("Y") oder mehrfache Werte ("N") erlaubt.
Wie bereits erwähnt, ist die Form der System- und Benutzertabellen identisch. Deswegen können bei INGRES die Systemtabellen genauso wie die Benutzertabellen abgefragt und modifiziert werden. Die Abfragen auf Systemtabellen unterscheiden sich nicht von den Abfragen auf Benutzertabellen. Die Erlaubnis, eine Systemtabelle zu modifizieren, wird mit -148-
dem Dienstprogramm accessdb erteilt. Die Modifizierung der Systemtabellen kann auf keinen Fall empfohlen werden, weil dies weitere Arbeiten mit einer Datenbank unmöglich machen könnte.
10.3 Abfragen auf Systemtabellen In diesem Abschnitt werden einige Beispiele gezeigt, die die Abfragen auf Systemtabellen behandeln. Beispiel 11.1 Finden Sie den Tabellentyp und die Anzahl der Reihen der Tabelle mitarbeiter. SELECT table_type, num_rows FROM iitables WHERE table_name = 'mitarbeiter'; Das Ergebnis ist table_ num_rows T
7
(1 row) Wie aus dem Ergebnis des Beispiels 11.1 ersichtlich, handelt es sich bei der Tabelle mitarbeiter um eine Basistabelle (table_type ="T"), die insgesamt sieben Reihen enthält. Beispiel 11.2 Finden Sie alle Tabellen der Beispieldatenbank, die die Spalte pr_nr enthalten. SELECT iitables.table_name FROM iitables, iicolumns WHERE iitables.table_name = iicolumns.table_name AND iicolumns.column_name = 'pr_nr'; Das Ergebnis ist table_name projekt arbeiten (2 rows) Beispiel 11.3 Welche Spaltennamen sind in mehreren Tabellen der Beispieldatenbank enthalten? SELECT DISTINCT column_name FROM iicolumns WHERE table_owner ='peter' GROUP BY column_name HAVING COUNT(*) > 1 Das Ergebnis ist column_name m_nr -149-
abt_ nr pr_nr (3 rows) In Beispiel 11.3 wird angenommen, daß der Benutzer peter der Eigentümer der Tabellen der Beispieldatenbank ist.
-150-
Speicherstrukturen und Indexe
Speicherstrukturen und Indexe .....................................................................................................151 12
Speicherstrukturen und Indexe ...........................................................................................151 12.1 Speicherstrukturen 151 12.1.1 Stapel 152 12.1.2 Hash 152 12.1.3 ISAM 153 12.1.4 BTREE 153 12.2 Indexe 154 12.2.1 SQL-Anweisungen in Bezug auf Indexe 155 12.2.2 Indexe und Schlüssel 159 12.2.3 Kriterien zur Erstellung eines Index 160 12.3 Allgemeine Kriterien zur Verbesserung der Effizienz 12.3.1 Join statt korrelierter Unterabfrage 162 12.3.2 Unvollständige Anweisungen 163 12.3.3 LIKE-Operator 163 12.4 Optimieren von Suchanweisungen 164 Aufgaben 165
162
11 Speicherstrukturen und Indexe In diesem Kapitel werden zunächst die von INGRES unterstützten Speicherstrukturen dargestellt und ihre Rolle beim Zugriff auf Daten erläutert. Im weiteren Verlauf des Kapitels werden Indexe definiert und alle SQL-Anweisungen, die Indexe betreffen, erklärt. Am Ende des Kapitels werden die Möglichkeiten dargestellt, die INGRES bietet, um die Optimierung der Suchanweisungen zu beeinflussen.
11.1 Speicherstrukturen Die Reihen aus einer Tabelle werden mit Hilfe der SELECT-Anweisung ausgewählt. Die WHERE-Klausel innerhalb einer SELECT-Anweisung schränkt die Menge der ausgewählten Reihen und damit auch der Datenwerte ein. Die Suche nach Datenwerten, die durch eine SELECT-Anweisung mit der WHERE-Bedingung festgelegt sind, kann auf zwei verschiedene Weisen durchgeführt werden: - sequentiell und - direkt. Sequentielle Suche bedeutet, daß jede Reihe der Tabelle einzeln auf die Bedingung in der WHERE-Klausel überprüft -151-
wird. Nacheinander werden also alle Reihen in der Reihenfolge, wie sie physikalisch gespeichert sind, geprüft. Die direkte Suche kennzeichnet das gezielte Suchen nach den Reihen, die die angegebene Bedingung erfüllen. Welche Art der Suche angewendet wird, hängt primär von der Speicherstruktur, mit der die Reihen einer Tabelle auf der Platte gespeichert sind, ab. Einige Speicherstrukturen erlauben den Zugriff auf Daten mit Hilfe eines Schlüssels. In diesem Fall ist die direkte Suche möglich. Falls der Zugriff auf Daten ohne Schlüssel durchgeführt wird, muß die sequentielle Suche angewendet werden. INGRES unterstützt insgesamt vier unterschiedliche Arten von Speicherstrukturen: - Stapel ("heap"), - Hash, - ISAM und - BTREE. 11.1.1 Stapel Der Stapel kennzeichnet die Speicherstruktur, bei der die Reihen ungeordnet sind, d.h. beim Stapel wird jede neue Reihe am Dateiende angefügt. Alle Tabellen bei INGRES werden bei ihrer Erstellung standardmäßig in Form eines Stapels gespeichert. Die einzige Ausnahme bildet die Tabelle, die mit Hilfe der AS-Angabe aus einer existierenden Tabelle abgeleitet ist. (Für weitere Einzelheiten siehe die Beschreibung der SET Anweisung mit der RESULT_STRUCTURE-Angabe später in diesem Kapitel.) Der Stapel kann sehr effektiv zum Einfügen der Reihen einer Tabelle angewendet werden. Andererseits muß jede Suche in einem Stapel sequentiell durchgeführt werden, was eine Ineffizienz dieser Operation nach sich zieht. 11.1.2 Hash Die Hash-Speicherstruktur basiert auf einer Funktion (Hash-Funktion), die auf eine oder mehrere Spalten einer Tabelle (Hash-Schlüssel) angewendet wird. Im Unterschied zum Stapel basiert das Hash-Verfahren auf einem Schlüssel, d.h. die Reihen der Tabelle sind auf Grund des Hash-Schlüssels geordnet. Mit der Hash-Funktion wird die Adresse eines physikalischen Blockes direkt aus dem vorgegebenen Schlüsselwert berechnet. Damit kann der Block, in dem sich die gesuchte Reihe befindet, meistens mit einer einzigen E/A-Operation ermittelt werden. Das Hash-Verfahren kann effektiv bei der Suche mit dem kompletten Schlüsselwert angewendet werden. Falls der Schlüsselwert nur teilweise bekannt ist (z.B. m_name LIKE 'K%'), wird dieses Verfahren ineffektiv, weil die -152-
Hash-Funktion den kompletten Wert des Schlüssels braucht, um ihn im Speicher ausfindig zu machen. Genauso ist das Hash-Verfahren ineffektiv bei der Suche mittels einer Vergleichsoperation (z.B. m_nr > 10000), weil die Datenwerte, die logisch hintereinander kommen (10001, 10002 usw.), nicht unbedingt in denselben oder benachbarten physikalischen Blöcken liegen. Das Hash-Verfahren kann besonders empfohlen werden, für Tabellen, deren Schlüssel aus mehreren Spalten zusammengesetzt und dadurch sehr lang sind. 11.1.3 ISAM Die Speicherstruktur ISAM ("Indexed Sequential Access Method") unterscheidet sich von den beiden vorherigen dadurch, daß zusätzliche physikalische Seiten auf der Pla tte für den Index angelegt werden. Der Index einer Tabelle, die mit der ISAM-Speicherstruktur erstellt wurde, kann eine oder mehrere ihrer Spalten umfassen, die als Primärschlüssel bezeichnet werden. Jede Suche, die mit Hilfe des Index ausgeführt werden kann, ist sehr effektiv. Dabei ist unerheblich, ob die Suche nach einem einzigen Datenwert (z.B. m_nr = 10102) oder auf Grund eines Vergleichoperators (z.B. m_nr <= 20000) durchgeführt wird, vorausgesetzt die Spalte m_nr ist der Primärschlüssel. Andererseits ist nicht jede Suche ohne Hilfe des Index effektiv, weil sie sequentiell durchgeführt werden muß. Der Index für ISAM wird immer bei der Erstellung dieser Speicherstruktur angelegt. Damit ist die Struktur des Index festgelegt, und kann nur durch die komplette Reorganisation aller Daten geändert werden. Deswegen muß das Einfügen neuer Reihen u.U. in sogenannten Überlauf-Blöcken durchgeführt werden, falls der existierende Datenblock, in den die Reihe eingefügt werden soll, voll ist. 11.1.4 BTREE Die Speicherstruktur BTREE ("binary tree") benutzt genauso wie ISAM den Index, um auf die Daten einer Tabelle zuzugreifen. Der Unterschied zwischen BTREE und ISAM liegt nur darin, daß die von BTREE benutzte Indexdatei dynamisch ist. Das heißt, die Überlauf- Blöcke werden bei BTREE nicht benutzt, weil nach jedem Einfügen einer Reihe die Indexdatei restrukturiert wird, falls der existierende Datenblock, in dem die Reihe mit Hilfe des Index eingefügt werden soll, voll ist. Dieser Unterschied zwischen ISAM und BTREE zeigt gleichzeitig den jeweiligen Vorteil jeder der beiden Speicherstrukturen: ISAM kann bei der Suche in statischen Tabellen effektiver eingesetzt werden, während BTREE bei Tabellen, die viele Änderungen aufweisen, verwendet werden sollte. -153-
11.2 Indexe Wie schon aus dem vorherigen Abschnitt ersichtlich, verwenden die Speicherstrukturen ISAM und BTREE Indexe, um auf Daten zuzugreifen. Ein Index kann mit dem Inhaltsverzeichnis eines Buches verglichen werden. Mit Hilfe eines Inhaltsverzeichnisses ist es möglich, einen Begriff, der im Buch ein- oder mehrmals vorkommt, gezielt zu suchen und anschließend im Text ausfindig zu machen. Um Indexe zu erklären, werden wir die BTREE-Speicherstruktur bei INGRES näher erklären. Indexe der BTREE-Speicherstruktur sind mit Hilfe einer Datenstruktur namens B+ -Baum intern aufgebaut. Ein B+ -Baum hat eine baumartige Datenstruktur mit der Eigenschaft, daß alle Blattknoten des Baums gleich weit von der Wurzel entfernt sind. Diese Struktur wird auch aufrechterhalten, wenn neue Einträge eingefügt, bzw. alte gelöscht werden. Am Beispiel der Tabelle mitarbeiter wird die Struktur eines B+ -Baums dargestellt und die direkte Suche nach der Reihe mit der Personalnummer 25348 in der Tabelle mitarbeiter erläutert. (Das setzt voraus, daß die Spalte m_nr der Tabelle mitarbeiter indiziert ist.) 2581 9031 9031 18316
-
m_nr m_name
Abb. 12-1-B+-Baum für die Tabelle mitarbeiter 25348 Keller 10102 Wie aus Abbildung 12-1 ersichtlich, beinhaltet 10102 jeder Huber 18316 B+ -Baum eine Wurzel, eine Blattknotenebene und keine, 18316 Mueller eine oder mehrere dazwischen - liegenden Knotenebenen. 29346 Die Probst Blattknotenebene enthält einen Eintrag für jeden Datenwert 18316 9031 Meier der Spalte(n), für die ein Index erstellt wurde, und je einen 29346 2581 Kaufmann Zeiger, - der die physikalische Adresse der entsprechenden 28559 Mozer 25348 Reihe enthält. Das Suchen des Datenwertes 28559 25348 verläuft folgendermaßen: - B+ -Baums wird immer der Wert Angefangen von 28559 der Wurzel des gesucht, der größer oder gleich dem Suchwert ist. Abbildung 29346 12-1 folgend wird -zuerst der Datenwert 29348 auf der höchsten Ebene ausgesucht, danach der29346 Wert 28559 auf der nächsten und der gesuchte Wert 25348 auf der - niedrigsten Ebene. Mit Hilfe des dazugehörigen Zeigers wird-anschließend die entsprechende Reihe ausgewählt. (Eine andere, aber äquivalente Suchart wäre, stets den Wert zu suchen, der kleiner oder gleich dem Suchwert ist.) Besonders bei Tabellen mit sehr vielen Reihen ist der Vorteil der direkten Suche offensichtlich. Das System findet jede Reihe einer Tabelle mit wenigen Ein/Ausgabe-Operationen immer in etwa gleich kurzer Zeit, während die sequentielle Suche -154-
um soviel länger dauert, je weiter eine Reihe vom Anfang der Tabelle entfernt ist. 11.2.1 SQL-Anweisungen in Bezug auf Indexe INGRES ermöglicht das Erstellen und Löschen von Indexen. Beim Erstellen eines Index trägt INGRES die Information über die Indexeigenschaften in der Systemtabelle iiindexes ein. Diese Systemtabelle beinhaltet je eine Reihe für jeden erstellten Index. Die Systemtabelle iiindexes enthält u.a. fo lgende Spalten: - den Namen des erstellten Index; - den Namen des Eigentümers, der den Index erstellt hat; - den Namen der Tabelle, zu welcher der Index gehört; - den Indextyp. Die ausführliche Beschreibung dieser Systemtabelle befindet sich in Kapitel 11. Mit der Anweisung CREATE INDEX wird ein Index erstellt. Diese Anweisung hat folgende Form: CREATE [UNIQUE] INDEX index_name ON tabelle (sp_1 [ASC|DESC] [,sp_2 [ASC|DESC] ...]) [WITH-Klausel]; index_name kennzeichnet den Namen des erstellten Index. Für den Indexnamen index_name gilt dasselbe wie für den Namen einer Tabelle: Er muß mit einem Buchstaben beginnen und darf maximal 24 Zeichen lang sein. Ein Index kann für eine oder mehrere Spalten erstellt werden. Ein Index, der mehrere Spalten umfaßt, wird zusammengesetzter Index genannt. Ein zusammengesestzter Index darf maximal 30 Spalten umfassen. Jede Spalte einer Tabelle kann indiziert sein, wobei der Typ und die Länge der Spalte beliebig sein können. Die Angabe ASC bzw. DESC definiert die Sortierfolge der Reihen der Tabelle. Falls ASC angegeben ist, werden die Reihen der indizierten Spalte aufsteigend und bei DESC absteigend sortiert. Für einen zusammengesetzten Index gilt, daß zuerst auf Grund der erstangegebenen Spalte in der CREATE INDEX-Anweisung sortiert wird. Wenn zwei oder mehrere Datenwerte in dieser Spalte gleich sind, wird auf Grund der zweiten Spalte sortiert, usw. Falls weder ASC noch DESC angegeben ist, wird ASC als Voreinstellung angenommen. Die optionale Angabe UNIQUE legt fest, daß jeder Datenwert nur einmal in der indizierten Spalte vorkommen darf. Bei einem zusammengesetzten Index darf der verkettete Wert aller Datenwerte der zusammenhängenden Spalten nur einmal vorkommen. Falls UNIQUE nicht angegeben ist, dürfen Datenwerte in der indizierten Spalte mehrfach vorkommen. Die WITH-Klausel enthält das Schlüsselwort WITH, gefolgt von einer oder mehreren der folgenden Angaben, die durch Kommata voneinander getrennt sind: STRUCTURE=CBTREE|BTREE|CISAM|ISAM|CHASH|HASH KEY = (spalten_liste), -155-
FILLFACTOR = n, MINPAGES = n, MAXPAGES = n, LEAFFILL = n, NONLEAFFILL = n und LOCATION = (bereich_1,...) Angabe STRUCTURE
KEY
MINPAGES MAXPAGES
LEAFFILL
NONLEAFFILL
LOCATION
Funktion Mit STRUCTURE kann die Speicherstruktur eines Index festgelegt werden. ISAM, BTREE und HASH sind die Angaben für die entsprechenden Speicherstrukturen, die im vorherigen Abschnitt erläutert wurden. CBTREE und CHASH bezeichnen komprimierte BTREEbzw. HASH-Speicherstrukturen, d.h. die Datenwerte der indizierten Spalte werden in der Indexdatei komprimiert gespeichert. Falls bei der Erstellung eines Index keine STRUCTUREAngabe vorliegt, wird standardmäßig ISAM genommen. Die Angabe KEY=(spalten_liste) definiert die geordnete Untermenge aller Spalten eines Index. Falls z.B. ein Index für die Spalten a, b, c und d definiert ist, kann die KEY-Angabe a, ab, abc oder abcd sein. (Die Voreinstellung ist abcd.) bzw. MINPAGES bzw. MAXPAGES kennzeichnen die aproximativ minimale bzw. die aproximativ maximale Anzahl der physikalischen Seiten, die eine Tabelle mit der HASH- oder CHASH-Struktur haben muß. Falls sowohl MINPAGES als auch MAXPAGES in einer CREATE INDEX-Anweisung spezifiziert sind, darf MINPAGES nicht größer als MAXPAGES sein. Die LEAFFILL-Angabe definiert den Prozentsatz des belegten Speichers aller Speicherseiten, die für die Blattknoten eines Index reserviert sind. Die Angabe LEAFFILL ist nur für die Tabellen mit der BTREE- bzw. CBTREE-Struktur zulässig. Die NONLEAFFILL-Angabe definiert den Prozentsatz des freigehaltenen Speichers aller Speicherseiten, die für das Innere des Index reserviert sind. Die Angabe NONLEAFFILL ist nur für die Tabellen mit der BTREE- bzw. CBTREE-Struktur zulässig. Die LOCATION-Angabe definiert Speicherbereiche, wo ein Index nach seiner Erstellung gespeichert werden soll. Die angegebenen Speicherbereiche müssen dem INGRES-System im voraus bekannt sein. Falls -156-
LOCATION nicht angegeben ist, wird der Index standardmäßig im Datenbank-Speicherbereich gespeichert. Beispiel 12.1 Erstellen Sie einen Index für die Spalte m_nr der Tabelle mitarbeiter. CREATE INDEX i_mit_mnr ON mitarbeiter (m_nr); Beispiel 12.2 Erstellen Sie einen zusammengesetzten Index für die Spalten m_nr und pr_nr der Tabelle arbeiten. Die Werte in den zusammenhängenden Spalten m_nr und pr_nr dürfen nicht mehrfach vorkommen. Die Speicherstruktur dieses Index soll BTREE sein. CREATE UNIQUE INDEX i_arb_mpr ON arbeiten (m_nr, pr_nr) WITH STRUCTURE = BTREE; Das Erstellen eines UNIQUE-Index für eine Spalte ist nicht möglich, falls diese Spalte mehrfach vorhandene Werte enthält. Damit ein solcher Index erstellt werden kann, darf jeder existierende Datenwert in der Spalte nur einmal vorkommen, was auch für NULL-Werte gilt. Wie das INGRES-System die Erstellung eines UNIQUE-Index für eine Spalte mit mehrfach vorhandenen Werten nicht zuläßt, so ist es nicht möglich, mehrfach vorhandene Werte in einer Spalte, für die ein UNIQUE-Index schon erstellt wurde, einzufügen. Der Versuch, mit einer UPDATE- bzw. INSERT-Anweisung einen schon existierenden Datenwert in einer Spalte mit UNIQUE-Index einzufügen, wird vom System abgewiesen. Im folgenden Beispiel wird die Möglichkeit gezeigt, wie mehrfach vorhandene Werte einer Spalte gefunden und entfernt werden können, damit ein UNIQUE-Index für diese Spalte erstellt werden kann. Beispiel 12.3 Entfernen Sie alle Reihen der Tabelle arbeiten, bis auf die Reihen, die für jeden Mitarbeiter das jüngste Einstellungsdatum enthalten. CREATE VIEW v_hilftab (m_nr, max_dat) AS SELECT m_nr, max(einst_dat) FROM arbeiten GROUP BY m_nr HAVING COUNT(*) > 1; DELETE FROM arbeiten WHERE EXISTS (SELECT * FROM v_hilftab WHERE arbeiten.m_nr = v_hilftab.m_nr) AND arbeiten.einst_dat < (SELECT max_dat -157-
FROM v_hilftab WHERE arbeiten.m_nr = v_hilftab.m_nr); Das View v_hilftab, das in Beispiel 12.3 zuerst erstellt wurde, enthält die Reihen der Tabelle arbeiten, deren Spalte m_nr mehrfach vorhandene Werte beinhaltet und von diesen nur diejenigen mit dem jüngsten Einstellungsdatum. Folgende SELECT-Anweisung zeigt alle Reihen des Views v_hilftab: SELECT * FROM v_hilftab; Das Ergebnis ist: m_nr 9031 29346 10102 28559
max_dat 15-apr-1989 01-apr-1989 01-jan-1989 01-feb-1989
(4 rows) Die erste innere SELECT-Anweisung in der WHERE-Klausel der DELETE-Anweisung wählt alle mehrfach vorhandenen Reihen der Tabelle arbeiten aus. Bei der zweiten SELECT-Anweisung wird die Auswahl auf nur die Reihen eingeschränkt, deren Spalte einst_dat das jüngste Einstellungsdatum nicht enthält. Damit werden mit Hilfe des Views v_hilftab alle Reihen gelöscht, deren Spalte m_nr mehrfach vorhandene Werte und deren Spalte einst_dat die Datenwerte, die nicht die jüngsten sind, enthält. Die Tabelle arbeiten hat, nachdem die Anweisungsfolge in Beispiel 12.3 abgearbeitet wird, folgenden Inhalt:
-158-
m_nr 9031 29346 2581 18316 25348 10102 28559
pr_nr p1 p1 p3 p2 p2 p3 p2
aufgabe Gruppenleiter Sachbearbeiter Projektleiter Sachbearbeiter Gruppenleiter Sachbearbeiter
einst_dat 15-apr-1989 01-apr-1989 15-oct-1989 01-jun-1989 15-feb-1988 01-jan-1989 01-feb-1989
Wie aus der obigen Darstellung ersichtlich, kommt jeder Datenwert in der Spalte m_nr nur einmal vor. Damit ist es jetzt möglich, einen UNIQUE-Index für die Tabelle arbeiten zu erstellen. Das erstellte View v_hilftab kann, nachdem die Aufgabe in Beispiel 12.3 gelöst wurde, mit der Anweisung DROP VIEW v_hilftab; gelöscht werden, weil wir es nur als Hilfsmittel benutzt haben. Mit der Anweisung DROP INDEX wird der erstellte Index gelöscht. Die allgemeine Form dieser Anweisung sieht folgendermaßen aus: DROP INDEX index_name1 [,index_name2,...]; Beispiel 12.4 Löschen Sie den Index, der in Beispiel 12.1 erstellt wurde. DROP INDEX i_mit_mnr 11.2.2 Indexe und Schlüssel Wie wir schon gezeigt haben, sind Indexe ein Teil der SQL-Sprache. Im Unterschied zu ihnen sind Schlüssel im ANSI-SQL-Standard vom 1986 nicht definiert, jedoch aber im SQL2-Standard. Bevor der Zusammenhang zwischen Indexen und Schlüsseln erklärt wird, müssen die verschiedenen Schlüssel erläutert werden. Im relationalen Datenmodell existieren folgende Schlüsselarten: a) Kandidatenschlüssel; b) Primärschlüssel und c) Fremdschlüssel. Der Kandidatenschlüssel kennzeichnet eine Spalte oder Spaltengruppe einer Tabelle, für die die minimale Eindeutigkeit gilt. Eine Tabelle kann einen oder mehrere Kandidatenschlüssel aufweisen. Einer dieser Schlüssel wird ausgewählt und als Primärschlüssel bezeichnet. Als Fremdschlüssel wird eine Spalte oder Spaltengruppe einer Tabelle bezeichnet, die Primärschlüssel einer anderen Tabelle ist. Die Datenwerte der Spalte, die einen Fremdschlüssel darstellt, bilden gewöhnlich eine Untermenge der Datenwerte der Spalte, die den entsprechenden Primärschlüssel bildet. -159-
Die Beispieldatenbank enthält folgende Schlüssel: a) Die Spalte abt_nr der Tabelle abteilung ist sowohl ein Kandidaten- als auch ein Primärschlüssel. b) Die Spalte pr_nr der Tabelle projekt ist sowohl ein Kandidaten- als auch ein Primärschlüssel. c) Die Spalte m_nr der Tabelle mitarbeiter ist sowohl ein Kandidaten- als auch ein Primärschlüssel. Gleichzeitig ist die Spalte abt_nr ein Fremdschlüssel. d) Die Spaltengruppe (m_nr, pr_nr) der Tabelle arbeiten ist sowohl ein Kandidaten- als auch ein Primärschlüssel. Zusätzlich dazu ist jede dieser Spalten ein Fremdschlüssel. Für jeden Primär- und Kandidatenschlüssel einer Tabelle soll grundsätzlich ein Index erstellt werden, der die UNIQUE-Angabe enthält. Die Angabe UNIQUE steht im Zusammenhang mit der Definition beider Schlüssel; sowohl Primär- als auc h Kandidatenschlüssel lassen keine mehrfach vorhandenen Datenwerte zu. Zusätzlich soll jede Spalte, die einen Primär- bzw. Kandidatenschlüssel darstellt, die NOT NULL-Angabe in der CREATE TABLE-Anweisung enthalten. Die Notwendigkeit der NOT NULL-Angabe stammt aus dem relationalen Modell. Jeder Primär-- bzw. Kandidatenschlüssel wird im relationalen Modell als eine (mathematische) Funktion betrachtet, die jedes Element eindeutig identifiziert. Beispiel 12.5 Erstellen Sie einen UNIQUE-Index für jeden Primär- und Kandidatenschlüssel der Beispieldatenbank. CREATE UNIQUE INDEX i_abt_nr ON abteilung (abt_nr); CREATE UNIQUE INDEX i_m_nr ON mitarbeiter (m_nr); CREATE UNIQUE INDEX i_pr_nr ON projekt (pr_nr); CREATE UNIQUE INDEX i_mpr_nr ON arbeiten (m_nr,pr_nr); Auch für jeden Fremdschlüssel ist es empfehlenswert, einen Index zu erstellen. Eine der Joinspalten, die bei der Verknüpfung zweier Tabellen benutzt wird, stellt gewöhnlich einen Fremdschlüssel dar. Das Erstellen eines Index für einen Fremdschlüssel wird die Abarbeitungszeit eines Joins wesentlich verkürzen. Die Angabe UNIQUE wäre in diesem Fall falsch, weil ein Fremdschlüssel mehrfach vorhandene Werte zuläßt. Beispiel 12.6 Erstellen Sie einen Index für jeden Fremdschlüssel der Beispieldatenbank. CREATE INDEX i_m_abtnr ON mitarbeiter (abt_nr); CREATE INDEX i_arb_mnr ON arbeiten (m_nr); CREATE INDEX i_arb_prnr ON arbeiten (pr_nr); 11.2.3 Kriterien zur Erstellung eines Index Obwohl INGRES keine Einschränkungen bezüglich der Anzahl von Indexen aufweist, ist es sinnvoll, diese Anzahl relativ gering zu halten. Erstens verbraucht jeder Index -160-
Speicherplatz; wenn für eine Anwendung sehr viele Indexe erstellt wurden, kann es sogar vorkommen, daß die Indexe mehr Speicherplatz brauchen als alle Datenwerte einer Datenbank. Zweitens werden im Unterschied zu Abfragen, die mit Hilfe eines Index wesentlich beschleunigt werden können, die Anweisungen INSERT und DELETE u.U. mit der Speicherstruktur BTREE bzw. CBTREE langsamer. Der Grund dafür ist, daß jedes Einfügen bzw. Löschen der Reihen einer Tabelle mit einer indizierten Spalte u.U. eine Änderung der Struktur der Indexdatei verursacht. Dementsprechend wird die Indexdatei auch geändert, wenn eine indizierte Spalte selbst geändert wird. Im folgenden werden wir einige Empfehlungen geben, wann ein Index erstellt werden soll und wann nicht. Für alle Beispiele dieses Kapitels gilt die Annahme, daß alle Tabellen der Beispieldatenbank eine große Anzahl von Reihen haben. a) WHERE-Klausel Wenn die WHERE-Klausel in einer SELECT-Anweisung eine Bedingung mit einer einzigen Spalte enthält, ist es empfehlenswert, diese Spalte zu indizieren. Dabei ist es wichtig zu wissen, welchen Prozentsatz der Reihen die SELECT-Anweisung auswählt. Das Erstellen eines Index wird die Abfrage am stärksten beschleunigen, wenn der Prozentsatz unter 5% liegt. Andererseits ist es nicht sinnvoll, wenn die Menge der ausgewählten Reihen konstant 80% oder mehr beträgt, einen Index zu erstellen. In diesem Fall werden für die existierende Indexdatei zusätzliche E/A-Operationen notwendig, die dann die Zeitgewinne, die mit der direkten Suche erreicht wurden, zunichte machen. Die WHERE-Klausel kommt auch in den Datenmanipulationsanweisungen UPDATE und DELETE vor. Für die WHERE-Klausel gilt bei diesen beiden Anweisungen ähnliches, was schon für die SELECT-Anweisung gesagt wurde. Der einzige Unterschied ist, daß die UPDATE- und DELETE-Anweisung bei der Speicherstruktur BTREE bzw. CBTREE u.U. zusätzliche Änderungen der Indexdatei verursachen. Dieser Zeitverlust muß in Betracht gezogen werden, wenn die Entscheidung getroffen wird, ob der Index erstellt werden soll oder nicht. (In der Praxis wird die Spalte, die in der Bedingung einer WHERE-Klausel in einer UPDATE- bzw. DELETE-Anweisung vorkommt, meist auch in einer SELECT-Anweisung benutzt.) b) AND-Operator Wenn eine Bedingung in der WHERE-Klausel einen oder mehrere AND-Operatoren enthält, empfiehlt es sich, einen zusammengesetzten Index zu erstellen, der alle Spalten umfaßt, die in der Bedingung vorkommen. Dabei spielt die Länge aller Spalten, für die der zusammengesetzte Index erstellt werden soll, eine wesentliche Rolle. Je größer die Gesamtlänge aller Spalten ist, desto weniger empfehle nswert ist es, den Index zu erstellen. Beispiel 12.7 -161-
CREATE INDEX i_arb_mdat ON arbeiten (m_nr, einst_dat); SELECT * FROM arbeiten WHERE m_nr = 29346 AND einst_dat = '01-apr-1989'; In Beispiel 12.7 wird mit der SELECT-Anweisung sowohl nach der Personalnummer als auch nach dem Einstellungsdatum des Mitarbeiters gesucht. In diesem Fall empfiehlt es sich, einen zusammengesetzten Index zu erstellen, wie es auch im Beispiel gemacht wurde. c) Join Beim Verknüpfen zweier Tabellen mit Join ist es empfehlenswert, beide Joinspalten zu indizieren. Joinspalten stellen gewöhnlich den Primärschlüssel der einen und den entsprechenden Fremdschlüssel der anderen Tabelle dar. Nach den Kriterien des vorherigen Abschnitts sollten diese Spalten indiziert sein. Beispiel 12.8 SELECT m_name, m_vorname FROM mitarbeiter, arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr AND einst_dat = '15-oct-1989'; In diesem Beispiel wäre es sinnvoll, für die Spalten mitarbeiter.m_nr und arbeiten.m_nr je einen Index zu erstellen. Nach dem Kriterium b) soll ein zusätzlicher Index für die Spalte einst_dat erstellt werden.
11.3 Allgemeine Kriterien zur Verbesserung der Effizienz Im letzten Abschnitt haben wir gezeigt, wie das Leistungsverhalten einer Datenbankanwendung mit Hilfe von Indexen verbessert werden kann. In diesem Abschnitt wird erläutert, wie die Programmierung die Effizienz der Anwendung beeinflussen kann. Das INGRES-System erarbeitet für eine gegebene SQL-Anweisung gleichzeitig me hrere mögliche Lösungen. Nachdem die Lösungen erstellt wurden, versucht ein spezieller Systemteil - der Optimierer - die effizienteste Lösung auszuwählen. Der INGRES-Optimierer ist nicht imstande, für jeden Fall die bestmögliche Lösung zu finden. Deswegen ist es sehr wichtig herauszufinden, wie der Programmierer die Effizienz seiner Anwendung verbessern kann. Für INGRES gelten folgende Kriterien: 11.3.1 Join statt korrelierter Unterabfrage Die meisten SELECT-Anweisungen mit der korrelierten Unterabfrage können auch mit Hilfe von Join dargestellt werden. Diese beiden äquivalenten Lösungsmethoden unterscheiden sich dadurch, daß Join wesentlich effizienter -162-
als die korrelierte Unterabfrage ist. Dies wird in folgendem Beispiel verdeutlicht. Beispiel 12.9 Nennen Sie die Namen aller Mitarbeiter, die in Projekt p3 arbeiten. A. SELECT m_name FROM mitarbeiter, arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr AND pr_nr = 'p3'; B. SELECT m_name FROM mitarbeiter WHERE 'p3' IN (SELECT pr_nr FROM arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr); Die Lösung A ist effizienter als die Lösung B. Bei der Lösung B muß jede Reihe der äußeren SELECT-Anweisung mit allen Reihen der inneren SELECT-Anweisung verglichen werden. Dieses Verfahren ist nicht optimal, weil das einmalige Suchen nach dem Datenwert p3 in der Spalte pr_nr (wie bei Lösung A) wesentlich schneller ist. 11.3.2 Unvollständige Anweisungen In der Praxis kann es vorkommen, daß der Programmierer aus Versehen eine INGRES-SQL-Anweisung unvollständig angibt. Diese Anweisung ist nicht nur fehlerhaft, sondern kann auch das Leistungsverhalten der ganzen Anwendung sehr beeinträchtigen. Typisches Beispiel dafür ist das Kartesische Produkt. Das Kartesische Produkt ist schon in Kapitel 7 ausführlich beschrieben worden. Das Ergebnis eines Kartesischen Produkts enthält jede Kombination der Reihen beider Tabellen. Wenn eine Tabelle z.B. 10000 und die andere 100 Reihen hat, enthält das Kartesische Produkt dieser Tabellen als Ergebnis insgesamt eine Million Reihen. Dementsprechend groß ist der Zeitverlust, der bei dieser Verknüpfung entsteht. Das absichtliche Erzeugen eines Kartesischen Produkts ist in der Praxis sehr selten. Vielmehr handelt es sich um einen Fehler des Programmierers, der vergessen hat, das notwendige Join in der WHERE-Klausel anzugeben. 11.3.3 LIKE-Operator Der LIKE-Operator ist ein Vergleichsoperator, der Datenwerte einer Spalte mit einer vorgegebenen Zeichenkette vergleicht. Falls diese Spalte indiziert ist, wird die Suche nach der Zeichenkette mit Hilfe des existierenden Index ausgeführt. Die Verwendung des Zeichens "%" bzw. "_" am Anfang der Zeichenkette macht es aber unmöglich, das Suchen von der Wurzel des Indexbaums aus zu starten. -163-
Beispiel 12.10 Finden Sie die Personalnummer aller Mitarbeiter, deren Name mit "mann" endet. SELECT m_nr FROM mitarbeiter WHERE m_name LIKE '%mann'; In Beispiel 12.10 kann die Suche nicht von der Wurzel des Indexbaums aus durchgeführt werden, auch wenn der Index für die Spalte m_name existiert. Der Grund dafür ist, daß die ersten Zeichen des Vergleichsmusters in der Bedingung nicht bekannt sind.
11.4 Optimieren von Suchanweisungen Die Aufgabe des Optimierers ist es, die besten Strategien für die Ausführung der Datenmanipulationsanweisungen (meistens Abfragen) zu erstellen. INGRES erlaubt dem Benutzer, den ausgewählten Ausführungsplan ("query execution plan" - QEP) mit Hilfe des Terminalmonitors zu verfolgen. Eine andere Möglichkeit, die Ausführung der Abfragen zu optimieren, bietet die MODIFY-Anweisung an. Diese Anweisung modifiziert die Speicherstruktur einer Tabelle oder eines Index. Die Syntax dieser Anweisung ist: MODIFY tab_name|index_name TO neu_struktur [ON spalten_name [ASC|DESC]],... [WITH-Klausel]; Die Speicherstruktur der Tabelle tab_name bzw. des Index index_name wird mit der MODIFY-Anweisung in neu_struktur geändert. Die WITH-Klausel hat dieselben Angaben wie die gleichnamige Klausel bei der CREATE INDEX-Anweisung. Damit ist es möglich, mit Hilfe der MODIFY-Anweisung auch den Speicherort einer Tabelle zu ändern. Beispiel 12.11 MODIFY mitarbeiter TO CBTREE ON m_nr WITH LEAFFILL = 60, FILLFACTOR = 50; Die Speicherstruktur der Tabelle mitarbeiter wird in Beispiel 12.11 in CBTREE umgewandelt. Die physikalischen Seiten, die für Daten reserviert sind, können bis zu 50% und die für Blattknoten bis zu 60% aufgefüllt werden. Die SET-Anweisung mit der RESULT_STRUCTURE-Angabe kann auch benutzt werden, um die Speicherstruktur einer Tabelle zu ändern, falls diese Tabelle aus einer schon existierenden abgeleitet wird. Beispiel 12.12 SET RESULT_STRUCTURE HASH; CREATE tab_neu AS SELECT * FROM mitarbeiter; CREATE INDEX i_tab_neu ON tab_neu(m_nr) WITH STRUCTURE=HASH, MINPAGES=30; -164-
In Beispiel 12.12 wird die Tabelle tab_neu erstellt und mit den Reihen der Tabelle mitarbeiter geladen. Die logischen Strukturen der beiden Tabellen sind identisch, während die Speicherstruktur der Tabelle tab_neu in HASH modifiziert wird. Weil die Hash-Speicherstruktur statisch ist, ist es sinnvoll, einen Schlüssel mit der minimalen Anzahl der physikalischen Seiten zu definieren.
Aufgaben A.12.1 Erstellen Sie unter der Annahme, daß alle Tabellen der Beispieldatenbank eine sehr große Anzahl von Reihen haben, die notwendigen Indexe für folgende SELECT-Anweisungen: a) SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE m_name = 'Kaufmann'; b) SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE m_name = 'Meier' AND m_vorname = 'Rainer'; c) SELECT aufgabe FROM arbeiten, mitarbeiter WHERE arbeiten.m_nr = mitarbeiter.m_nr; d) SELECT m_name, m_vorname FROM mitarbeiter, abteilung WHERE mitarbeiter.abt_nr=abteilung.abt_nr AND abt_name = 'Beratung';
-165-
Transaktionen und Restaurierung
Transaktionen und Restaurierung.................................................................................................166 13
Transaktionen und Restaurierung.......................................................................................166 13.1 Transaktionen 166 13.2 Restaurierung 168 13.2.1 Fehlerquellen 168 13.2.2 Log- und Journal-Datei 169 13.3 Sperren 171 13.3.1 Objekte sperren 171 13.3.2 Die SET LOCKMODE-Anweisung
172
12 Transaktionen und Restaurierung In diesem Kapitel werden die drei verwandten Begriffe konkurrierender Datenzugriff, Transaktionen und die Restaurierung von Datenbanken behandelt. Zuerst werden die Definition der Transaktion gegeben und die INGRES-SQL-Anweisungen, die in Bezug zu Transaktionen stehen, erläutert. Danach werden die Möglichkeiten erörtert, welche das INGRES-System bietet, um eine Datenbank nach verschiedenen Fehlern zu restaurieren. Am Ende des Kapitels wird das Sperren der INGRES-Objekte behandelt, womit der konkurrierende Datenzugriff mehrerer Benutzer auf die Daten einer INGRES-Datenbank gewährleistet ist.
12.1 Transaktionen Eine Datenbank wird in der Regel von mehreren Anwendern gleichzeitig benutzt. Dieser Umstand wirft eine Reihe von Problemen auf, die alle mit völliger Korrektheit abgehandelt werden müssen. Der konkurrierende Datenzugriff mehrerer Benutzer muß bei jedem Datenbanksystem gewährleistet sein. Ein weiteres Problem, mit dem Datenbanksysteme konfrontiert sind, sind Fehler, die sowohl in der Software als auch in der Hardware auftreten können. Ein Datenbanksystem sollte in der Lage sein, nach denkbaren Ausfällen die betroffenen Datenbanken in den letzten konsistenten Zustand zu überführen. Sowohl der konkurrierende Datenzugriff mehrerer Benutzer als auch die Erhaltung der Konsistenz der Datenbanken nach Hardware- oder Softwareausfall wird mit Hilfe der Transaktionen gewährleistet. -166-
Eine Transaktion kennzeichnet mehrere, nacheinander folgende, logisch zusammenhängende Anweisungen. An folgendem Beispiel wird dieser Begriff praktisch erläutert. In der Beispieldatenbank soll der Mitarbeiter namens Huber eine neue Personalnummer bekommen. (Der Grund dafür könnte die Änderung des Arbeitsstandortes o.ä. sein.) Die neue Personalnummer soll 39831 sein. Die Änderung der Personalnummer muß gleichzeitig in zwei Tabellen durchgeführt werden: Eine Reihe der Tabelle mitarbeiter muß geändert werden sowie alle Reihen der Tabelle arbeiten, die Daten des Herrn Huber enthalten. Falls nur eine der beiden Tabellen geändert würde (z.B. die Tabelle mitarbeiter), würde die Beispieldatenbank inkonsistent, weil der Primär- und Fremdschlüssel des Herrn Huber nicht mehr zueinander passen würden. Es wären also alle Abfragen, die die Tabellen mitarbeiter und arbeiten mit dem Join-Operator verknüpfen und die Datenwerte des Herrn Huber auswählen, falsch. Die Konsistenz der Beispieldatenbank kann nur erhalten bleiben, wenn die UPDATE-Anweisungen entweder vollständig durchgeführt oder - im Falle eines Fehlers während der Transaktion - rückgängig gemacht werden. Beispiel 13.1 Ändern Sie die Personalnummer des Mitarbeiters namens Huber in allen Reihen der Beispieldatenbank. Die neue Nummer soll 39831 sein. UPDATE mitarbeiter SET m_nr = 39831 WHERE m_nr = 10102; UPDATE arbeiten SET m_nr = 39831 WHERE m_nr = 10102; Das Beispiel stellt damit eine Transaktion dar, d.h. eine logische Einheit, deren Eigenschaft es ist, die Konsistenz einer Datenbank dadurch zu erhalten, daß entweder alle oder keine der Anweisungen einer Sequenz durchgeführt werden. INGRES kennt drei SQL-Anweisungen, die im Zusammenhang mit den Transaktionen stehen: - COMMIT, - ROLLBACK und - SAVEPOINT. Mit der Anweisung COMMIT [WORK]; wird eine Transaktion beendet, und alle Änderungen, die innerhalb der Transaktion angegeben sind, werden in der Log-Datei geschrieben. Gleichzeitig wird mit Beendigung der einen Transaktion die nächste gestartet. Mit der Anweisung ROLLBACK [WORK] [TO name]; werden alle schon ausgeführten SQL-Anweisungen innerhalb einer Transaktion rückgängig gemacht. Diese Anweisung -167-
wird entweder vom System implizit durchgeführt oder muß explizit vom Programmierer im Programm angegeben werden. Der Programmierer verwendet die Anweisung ROLLBACK, wenn er nicht sicher ist, ob alle Änderungen an der Datenbank korrekt ausgeführt wurden. (Die optionale Angabe WORK bei den Anweisungen COMMIT und ROLLBACK existiert aus Kompatibilitätsgründen zu anderen Datenbanksystemen.) Die Anweisung SAVEPOINT name; erstellt eine Marke für eine SQL-Anweisung innerhalb einer Transaktion. Diese Anweisung stellt ein nützliches Konstrukt dar, um die Ausführung verschiedener Teile einer Transaktion, mit Hilfe von Bedingungen zu ermöglichen. Andererseits widerspricht ihre Verwendung dem Prinzip, daß eine Transaktion so kurz wie möglich sein soll. Nachdem die Marken innerhalb einer Transaktion gesetzt wurden, ist es mit ROLLBACK TO name; möglich, alle Anweisungen innerhalb der Transaktion und bis zur Marke name zurückzusetzen. Die Transaktion bleibt offen und alle Anweisungen, die sich in der Transaktion vor der Marke name befinden, bleiben unverändert. INGRES bietet dem Benutzer die Möglichkeit, zwischen den impliziten und expliziten Transaktionen zu wählen. Die Anweisung SET AUTOCOMMIT hat zwei Schalter - ON und OFF - die die Transaktionsart festlegen. Falls SET AUTOCOMMIT ON; in einem Programm angegeben ist, werden alle nachfolgenden SQL-Anweisungen als einzelne Transaktionen betrachtet. Demgegenüber verlangt der OFF-Schalter die explizite Anwendung der Anweisung COMMIT bzw. ROLLBACK.
12.2 Restaurierung INGRES besitzt ein gut abgestimmtes Konzept zur Restaurierung von Datenbanken, mit dem Fehler verschiedenen Ursprungs unterschiedlich behoben werden können. Damit dieses Konzept verständlich wird, müssen zuerst die möglichen Fehlerquellen sowie die zur Verfügung stehenden Möglichkeiten zur Wiederherstellung der Konsistenz erläutert werden. 12.2.1 Fehlerquellen Die Inkonsistenz einer Datenbank kann durch vier unterschiedliche Fehlergruppen verursacht werden. Das sind: - Programmfehler, - Systemfehler, - Plattenfehler und - Kombination unterschiedlicher Fehler. Ein Programmfehler betrifft die Anwendung eines einzelnen -168-
Benutzers und hat gewöhnlich keinen Einfluß auf das ganze System. Programmfehler können im Regelfall nicht vom System abgefangen werden; es ist vielmehr Aufgabe des Programmierers, solche Fehler selbst abzufangen und entsprechende Maßnahmen zu treffen. Innerhalb einer Transaktion auftretende Programmfehler müssen dementsprechend berücksichtigt werden. Für einen Systemfehler ist typisch, daß die im Arbeitsspeicher gespeicherten Daten verlorengehen. Dies tritt z.B. bei Stromausfall auf. Damit betrifft ein Systemfehler alle im Arbeitsspeicher befindlichen Anwendungen. Die Konsistenz aller zum Zeitpunkt des Systemfehlers im Arbeitsspeicher befindlichen DB-Anwendungen wird bei INGRES durch die Verwendung der sogenannten Log-Datei wiederhergestellt. Die Log-Datei wird später in diesem Abschnitt behandelt. Ein Plattenfehler äußert sich dadurch, daß die Information auf einer Platte oder auf einem Teil der Platte verlorengeht. Die Ursache eines Plattenfehlers kann mechanischer (z.B. Schreib/Lesekopf-Fehler) oder verwaltungstechnischer Art sein. Bei einer Kombination unterschiedlicher Fehler kann es vorkommen, daß sich die Datenbanken im System in einem nicht konsistenten Zustand befinden. In dem Fall muß die Restaurierung der nicht konsistenten Datenbanken vorgenommen werden. 12.2.2 Log- und Journal-Datei Abhängig davon, welche Art von Fehler aufgetreten ist, existieren bei INGRES zwei unterschiedliche Restaurierungs-Prozesse: - die automatische Restaurierung und - die Restaurierung mit Hilfe der rollforwarddb-Anweisung Wenn eine Transaktion gestartet wird, wird die Information aus dieser Transaktion in einer speziellen Datei namens Log-Datei gespeichert. Damit enthält die Log-Datei die Information über abgeschlossene und nicht abgeschlossene Transaktionen eines Systems. Die Log-Datei wird vom sogenannten "Recovery Manager" benutzt, um leichte Systemfehler automatisch zu beheben. Wenn z.B. Daten im Arbeitsspeicher des Rechners verlorengehen, wird die automatische Restaurierung durchgeführt, indem der "Recovery Manager" alle nichtabgeschlossenen Transaktionen zurücksetzt. Der Speicherungsort einer Log-Datei wird mit Hilfe der Umgebungsvariablen II_LOG_FILE gesetzt. (II_LOG_FILE ist in Kapitel 15 beschrieben.) Es ist empfehlenswert, die Log-Datei nicht auf einer Platte mit sehr intensiven E/A-Operationen zu speichern, weil dadurch die Effizienz des ganzen INGRES-Systems beeinträchtigt werden kann. Der zweite -169-
wichtige Punkt, der die Beeinträchtigung der Effizienz des Systems verursachen kann, ist die Belegung einer Log-Datei. Standardmäßig gilt: Falls eine Log-Datei zu 80% gefüllt ist, wird die älteste, noch nicht abgeschlossene Transaktion zurückgesetzt und der Benutzer darüber informiert. Falls eine Log-Datei zu 95% gefüllt ist, werden Aktivitäten aller Benutzer gestoppt bis wieder genügend Platz in der Log-Datei verfügbar ist. Damit die Log-Datei nicht zu voll wird und dadurch die nicht abgeschlossenen Transaktionen verlorengehen, besitzt INGRES ein Subsystem namens "Archiver", das in bestimmten Zeitabständen, die vom Prozentsatz der Belegung der Log-Datei abhängig sind, erfolgreich abgeschlossene Transaktionen in eine Journal- Datei kopiert. Alle kopierten Transaktionen werden anschließend aus der Log-Datei entfernt. Die Voraussetzung für die Übertragung der erfolgreich abgeschlossenen Transaktionen einer Tabelle aus der Log- in die Journal- Datei ist entweder die Existenz der Angabe WITH JOURNALING in der CREATE TABLE-Anweisung dieser Tabelle oder die Verwendung der SET JOURNALING-Anweisung innerhalb einer INGRES-Sitzung. Die Journal-Datei stellt eine permanentere Form der Datensicherung als die Log-Datei dar. Sie wird dementsprechend für die Behebung schwerer Fehler (wie z.B. Plattenausfall) verwendet. Damit die Journal- Datei nach einem Plattenausfall benutzt werden kann, ist es dringend zu empfehlen, sie auf einem anderen Speichermedium (eine andere Platte oder ein Magnetbandgerät) als dem, auf dem sich die ihr zugehörige Datenbank befindet, zu speichern. Ein weiterer Begriff, der im Zusammenhang mit der Journal-Datei steht, ist die Sicherungskopie einer Datenbank. INGRES ermöglicht dem Datenbank-Administrator (DBA) mit Hilfe eines Dienstprogramms, eine Sicherungskopie der Datenbank zu erstellen. Die Erstellung der Sicherungskopie bei INGRES wird "Checkpoint" genannt. INGRES unterstützt zwei Dienstprogramme - ckpdb und - rollforwarddb, die im Zusammenhang mit der Restaurierung der Datenbanken stehen. Das Dienstprogramm ckpdb erstellt die Sicherungskopie einer oder mehrerer Datenbanken, und kann nur vom DBA als Betriebssystem-Kommando aufgerufen werden. Nach der Erstellung der Sicherungskopie werden alle existierenden Einträge in der Journal-Datei als ungültig markiert, weil sie nicht mehr vom System benötigt werden. Das zweite Dienstprogramm rollforwarddb wird für die Wiederherstellung einer oder mehrerer Datenbanken verwendet. Dieses Dienstprogramm benötigt die Sicherungskopie der Datenbank(en) und die aktuelle Journal-Datei. Um die Wiederherstellung der Datenbanken mit Hilfe der -170-
Sicherungskopie und der Journal-Datei durchführen zu können, müssen beide aktiv ("online") sein. Die Syntax der Betriebssystemkommandos rollforwarddb und ckpdb wird in Kapitel 15 ausführlich dargestellt.
12.3 Sperren Ein Datenbanksystem muß bei einem Mehrbenutzerbetrieb Mechanismen enthalten, die allen Benutzern zur selben Zeit den Zugriff auf Daten einer Datenbank ermöglichen. Dies wird mit Hilfe von Sperren erreicht. Das INGRES-System kennt zwei Arten von Sperren: SHARED und EXCLUSIVE. Wenn ein Benutzer ein Objekt im SHARED-Modus sperrt, dürfen alle anderen Benutzer nur lesend auf dieses Objekt zugreifen. Alle Abfragen, die dieses Objekt betreffen, sind dann erlaubt. Demgegenüber wird jeder Versuch, das Objekt zu ändern, abgewiesen bzw. in den Wartezustand versetzt. Ein Objekt kann mehrere SHARED-Sperren haben. Der EXCLUSIVE-Modus kennzeichnet eine wesentlich restriktivere Art der Sperre. Wenn ein Benutzer ein Objekt im EXCLUSIVE-Modus sperrt, werden alle Versuche anderer Benutzer, auf dieses Objekt schreibend oder lesend zuzugreifen, abgewiesen bzw. in den Wartezustand versetzt. 12.3.1 Objekte sperren INGRES kann drei verschiedene Objekttypen sperren: - eine physikalische Seite, - eine Tabelle und - eine ganze Datenbank. Die physikalische Seite ist das Objekt, das bei INGRES in den meisten Fällen gesperrt wird. Die Seitensperre sperrt die physikalische Seite, in der sich die Reihe befindet, die gesperrt werden soll. Sie ist gleichzeitig die kleinste Sperreinheit. (Die Reihensperre existiert bei INGRES nicht.) Eine Seitensperre kann vom System unter Umständen in eine Tabellensperre befördert werden. Dies geschieht, wenn die Anzahl der Seitensperren einen im voraus definierten maximalen Wert überschreitet oder wenn in der SET LOCKMODE-Anweisung die Tabellensperre als Einheit explizit angegeben ist. (Die SET LOCKMODE-Anweisung wird später in diesem Kapitel beschrieben.) Eine Tabelle kann auch implizit vom INGRES-System gesperrt werden. Dies geschieht z.B. bei der SQL-Anweisung CREATE INDEX. Dabei wird die ganze Tabelle, für die der Index erstellt werden soll, im EXCLUSIVE-Modus gesperrt. Die Datenbanksperre sperrt die ganze Datenbank im EXCLUSIVE-Modus. Nach einer solchen Sperre kann kein -171-
weiterer Benutzer, weder lesend noch schreiben, auf die Datenbank zugreifen. Das Dienstprogramm rollforwarddb z.B. benutzt die Datenbanksperre für jede Datenbank, die mit diesem Kommando restauriert wird. 12.3.2 Die SET LOCKMODE-Anweisung Die Anweisung SET LOCKMODE ermöglicht es dem Benutzer, Einfluß auf das Sperrverhalten des INGRES-Systems zu nehmen. Mit dieser Anweisung ist es möglich, sowohl die Art als auch die Obergrenze für die Anzahl der Sperren festzulegen. Die Anweisung hat folgende Syntax: SET LOCKMODE SESSION|ON tabellen_name|index_name WHERE [LEVEL=PAGE|TABLE|SESSION|SYSTEM] [,READLOCK=NOLOCK|SHARED|EXCLUSIVE|SESSION|SYSTEM] [,MAXLOCKS = n|SESSION|SYSTEM] [,TIMEOUT = n|SESSION|SYSTEM]; Mit der SET LOCKMODE-Anweisung kann zwischen der standardmäßigen Sperrart des Systems und der voreingestellten Sperrart der INGRES-Sitzung gewählt werden. Diese Anweisung bietet zwei Angaben - SESSION und ON tabellen_name -, die die Auswahl der Sperrobjekte regeln, und vier weitere Optionen - LEVEL, - READLOCK, - MAXLOCKS und - TIMEOUT, die die Sperreigenschaften insgesamt festlegen. Die Angabe ON tabellen_name gibt den Namen der Tabelle an, deren Eigenschaften durch Optionen festgelegt werden. Die alternative Angabe SESSION dagegen definiert die Sperreigenschaften aller Objekte während einer INGRES-Sitzung (siehe Beispiel 13.2). index_name ist der Name des Index, dessen Eigenschaften durch Optionen festgelegt werden. Ein Index kann vom INGRES-System nur bei den Speicherstrukturen BTREE und CBTREE gesperrt werden. (Bei der Speicherstrukturen ISAM und CISAM bleibt der Index statisch; deshalb sind keine Sperren notwendig.) Beim Sperren eines Index werden immer zwei seiner Knotenebenen gesperrt. Um die Teilung eines Indexknotens zu bewerkstelligen, wird vom System eine exklusive Sperre gesetzt. Name LEVEL PAGE
SYSTEM
Beschreibung Mit der Option LEVEL wird das Objekt festgelegt, das als Sperreinheit benutzt wird. definiert die physikalische Seite und TABLE die Tabelle als Sperreinheit, während SESSION die augenblicklich geltende Sperreinheit für die ganze NGRES-Sitzung wählt. definiert den aktuellen Standardwert des -172-
READLOCK
NOLOCK SHARED und EXCLUSIVE
MAXLOCKS
SESSION
TIMEOUT
Systems als geltende Sperreinheit. Die Option READLOCK spezifiziert den Sperrmodus, wenn die Daten von einem Benutzer gelesen werden. sperrt keine Daten während des Lesevorgangs. definieren die beiden am Anfang dieses Abschnitts schon beschriebenen Sperrmodi, während SESSION und SYSTEM den augenblicklichen Sperrmodus für die INGRESSitzung bzw. den generellen Modus für das ganze System festlegen. Mit der Option MAXLOCKS wird die maximale Anzahl der Seitensperren definiert, deren Überschreitung die Beförderung der Sperreinheit in die Tabellensperre verursacht. n ist eine Ganzzahl, die die maximale Anzahl der Seitensperren definiert (standardmäßig ist n=10). legt die augenblicklich geltende Anzahl der Seitensperren für die INGRES-Sitzung fest, während SYSTEM die Voreinstellung für das ganze System definiert. TIMEOUT spezifiziert das Verhalten eines Programms, von dem aus versucht wird, eine gesperrte Sperreinheit abzufragen bzw. zu ändern. n ist eine Ganzzahl, die das Zeitlimit in Sekunden definiert. n=0 ist die Voreinstellung und bedeutet, daß das Programm ohne zeitliche Begrenzung im Wartezustand bleibt. SESSION legt den augenblicklich geltenden Wert für die INGRES-Sitzung fest, während SYSTEM die Voreinstellung für das ganze System definiert.
Beispiel 13.2 SET LOCKMODE ON arbeiten WHERE LEVEL= PAGE, READLOCK=EXCLUSIVE, MAXLOCKS=20, TIMEOUT=45; In Beispiel 13.2 werden die Sperreigenschaften der Tabelle arbeiten explizit festgelegt. Die Seitensperre geht in eine Tabellensperre über, wenn die Anzahl der gesperrten Seiten 20 überschreitet. Der Wartezustand eines Programms, das auf eine gesperrte Einheit der Tabelle arbeiten zugreift, beträgt 45 Sekunden und jede zum Lesen gesperrte Seite wird exklusiv gesperrt. Beispiel 13.3 SET LOCKMODE SESSION WHERE LEVEL=SYSTEM, READLOCK=SYSTEM, MAXLOCKS=SYSTEM, TIMEOUT=SYSTEM; Nach der SET LOCKMODE-Anweisung in Beispiel 13.3 werden alle Sperreigenschaften der augenblicklichen INGRES-Sitzung auf System-Standardwerte (d.h. LEVEL=PAGE, READLOCK=SHARED, -173-
MAXLOCKS=10 und TIMEOUT=0) gesetzt.
-174-
INGRES-Sicherheitskonzept und Integrität INGRES-Sicherheitskonzept und Integrität .................................................................................175 14
INGRES-Sicherheitskonzept und Integrität .......................................................................175 14.1 Das INGRES-Sicherheitskonzept 175 14.1.1 Die Anweisungen GRANT und DROP PERMIT 14.1.2 Einschränkung des Datenzugriffs mit Views 178 14.2 Integritätsvorschriften 179 Aufgaben. 181
177
13 INGRES-Sicherheitskonzept und Integrität In diesem Kapitel werden zwei in Bezug auf Systemeinschränkungen verwandte Themen - Sicherheitskonzept und Integrität - beschrieben. Zuerst werden alle von INGRES verwendeten Benutzerklassen definiert und erläutert. Danach werden die SQL-Anweisungen GRANT und DROP PERMIT beschrieben, die die Vergabe bzw. den Entzug der Zugriffsrechte regeln. Am Ende des ersten Teils wird die Rolle von Views im Zusammenhang mit dem Sicherheitskonzept erklärt. Der zweite Teil des Kapitels ist der Datenintegrität vorbehalten. In diesem Zusammenhang wird die Anweisung CREATE INTEGRITY ausführlich beschrieben. (Die Datenbank-Prozeduren und Regel, die auch der Datenintegrität gewährleisten, werden in Kapitel 20 beschrieben.)
13.1 Das INGRES-Sicherheitskonzept Das Sicherheitskonzept von INGRES ermöglicht eine flexible Vergabe von Zugriffsrechten. Diese Vergabe kann sowohl selektiv als auch dynamisch sein. Selektiv bedeutet, daß an verschiedene Benutzer unterschiedliche Rechte vergeben werden können, während dynamisch die Möglichkeit kennzeichnet, die schon vergebenen Rechte zum Teil oder ganz zu entziehen. (In diesem Abschnitt wird mehrfach das Wort "Benutzer" als Synonym für die Benutzerkennung verwendet.) INGRES benutzt zur Identifizierung die Kennung eines Benutzers. Damit ist die Benutzerkennung das einzige Bindeglied zwischen Datenbanksystem einerseits und dem Anwender andererseits. Sie wird benutzt, um die Zugriffsrechte eines Anwenders zu überprüfen, zu vergeben oder zu entziehen. Alle INGRES-Benutzer gehören zu einer der folgenden vier Benutzerklassen: -175-
- Systemadministrator, - Superuser, - Datenbankadministrator (DBA) und - Benutzer. Der INGRES-Systemadministrator ist diejenige Person, die das INGRES-System installiert und die Autorisierung anderer Benutzer durchführt. Während der Installation wird die INGRES Master-Datenbank (iidbdb) erstellt, in der die Information über die Benutzerautorisierung und die existierenden Datenbanken gespeichert wird. Nachdem die INGRES-Installation abgeschlossen ist und iidbdb erstellt wurde, wird das Dienstprogramm accessdb vom Systemadministrator gestartet, um weiteren Benutzern den Zugang zum System zu ermöglichen. Eine ganz geringe Anzahl von Benutzern soll der Superuser-Klasse angehören. Bei dieser Klasse handelt es sich um die Benutzer, die nach der INGRES-Installation dieselben Rechte wie der Systemadministrator besitzen, d.h. das Recht, sowohl das Dienstprogramm accessdb auszuführen als auch die Rolle jedes anderen INGRES-Benutzers zu übernehmen. (Weil die Rechte eines Superusers identisch zu denen des Systemadministrators sind, werden wir im weiteren Verlauf des Buches die beiden Benutzerklassen einheitlich INGRES-Systemadministrator nennen.) Eine größere Anzahl von Benutzern wird vom Systemadministrator durch die Eintragung der Erlaubnis zur Datenbankerstellung zu Datenbankadministratoren (DBA's) ernannt. Dies geschieht auch mit Hilfe des Dienstprogramms accessdb. Ein DBA hat das Recht, sowohl die Rolle jedes gewöhnlichen INGRES-Benutzers zu übernehmen als auch, mit Hilfe des Dienstprogramms createdb Datenbanken zu erstellen. (Alle in diesem Abschnitt erwähnten Dienstprogramme sind in Kapitel 15 ausführlich beschrieben.) Eine Datenbank kann entweder global oder privat sein. Eine globale Datenbank kann grundsätzlich von allen INGRES-Benutzern verwendet werden, während der Zugang zu einer privaten Datenbank nur dem Benutzer, der sie erstellt hat, dem Systemadministrator und autorisierten Benutzern vorbehalten ist. Zu den INGRES-Benutzern gehören alle Benutzer, die mit dem Dienstprogramm accessdb als solche bezeichnet sind und kein DBA-Recht haben. Ein INGRES-Benutzer kann nur die Datenbanken benutzen, zu denen er Zugang hat. Für diese Datenbanken kann er die Tabellen erstellen, die dann seine private Tabellen sind. Hinweis. Der Zugang zu einer Datenbank gewährleistet nicht automatisch den Zugriff auf die Tabellen dieser Datenbank! Der Tabellenzugriff wird durch einen anderen Autorisierungsmechanismus, der im weiteren Verlauf dieses Abschnitts beschrieben wird, ermöglicht. -176-
13.1.1 Die Anweisungen GRANT und DROP PERMIT Mit der GRANT-Anweisung können Zugriffsrechte für Tabellen, Views oder Datenbank-Prozeduren an unterschiedliche Benutzer vergeben werden. Die Syntax dieser Anweisung ist GRANT liste_der_rechte ON [TABLE] tab_name|proz_name TO kennung_liste | PUBLIC; liste_der_rechte kenzeichnet eine oder mehrere Zugriffsarten, die durch Kommata getrennt werden. Es existieren insgesamt sechs erlaubte Zugriffsarten: SELECT, UPDATE [(spalte_1,...)] , INSERT, DELETE, ALL [PRIVILEGES] und EXECUTE. Zugriffsart SELECT
UPDATE
INSERT DELETE ALL EXECUTE
Wirkung Das SELECT-Zugriffsrecht erlaubt dem Benutzer den lesenden Zugriff auf Reihen einer Tabelle. Das UPDATE-Zugriffsrecht erlaubt dem Benutzer das Ändern der Reihen einer Tabelle bzw. eines Views. Sind einzelne Spalten der Tabelle bzw. des Views explizit angegeben, wird das Recht zur Änderung nur für diese Spalten vergeben. Falls die Spaltenliste fehlt, können alle Spalten geändert werden. erlaubt dem Benutzer das Einfügen von Reihen in einer Tabelle bzw. in einem View. erlaubt dem Benutzer das Löschen von Reihen in einer Tabelle bzw. in einem View. ALL beinhaltet alle oben beschriebenen Zugriffsrechte. (PRIVILEGES ist optional.) erlaubt dem Benutzer die Ausführung einer Datenbank-Prozedur.
kennung_liste kennzeichnet eine oder mehrere Benutzerkennungen - getrennt durch Kommata -, denen das Zugriffsrecht vergeben wird, das in tab_recht_liste angegeben ist. PUBLIC bezeichnet alle Benutzerkennungen. Die GRANT-Anweisung kann nur von einem Benutzer ausgeführt werden, der Datenbankadministrator für diese Datenbank ist. Dabei kann der Tabellenzugriff nur für die Tabellen der Datenbank gewährleistet werden, die dem DBA gehören. Beispiel 14.1 GRANT SELECT ON mitarbeiter TO petra, lothar; Die Benutzer petra und lothar haben die Erlaubnis, die Reihen der Tabelle mitarbeiter auszuwählen. Beis piel 14.2 -177-
GRANT UPDATE(m_nr,einst_dat), DELETE ON arbeiten TO toni; Der Benutzer toni hat die Erlaubnis, die Spalten m_nr und einst_dat der Tabelle arbeiten zu ändern und Reihen zu löschen. Beispiel 14.3 GRANT ALL ON projekt TO margit; Der Benutzer margit hat alle Zugriffsrechte für die Tabelle projekt. Beispiel 14.4 GRANT SELECT,INSERT,DELETE,UPDATE ON projekt TO PUBLIC; Alle Benutzer können Reihen der Tabelle projekt auswählen, einfügen, löschen und modifizieren. INGRES unterstützt z.Zt. nicht die vom SQL-Standard definierte Anweisung REVOKE, mit der die vorgegebenen Zugriffsrechte entzogen werden können. Die entsprechende Anweisung bei INGRES heißt DROP PERMIT, und ihre Syntax hat folgende Form DROP PERMIT ON tab_name|proz_name ALL|ganzzahl_liste; Mit der ganzzahl_liste wird eine Liste von Ganzzahlen definiert, die die Zugiffsrechte der Tabelle bzw. des Views namens tab_name , die entzogen werden sollen, spezifizieren. (Dasselbe gilt für die Datenbank-Prozedur proz_name .) Der existierende Zusammenhang zwischen den Zugriffsrechten einerseits und den angegebenen Ganzzahlen andererseits kann mit Hilfe der HELP PERMIT-Anweisung erlangt werden. Mit der Angabe ALL werden alle für die Tabelle tab vergebenen Zugriffsrechte entzogen. Beispiel 14.5 DROP PERMIT ON projekt 2,3; Mit der DROP PERMIT-Anweisung in Beispiel 14.5 werden das zweite und das dritte Zugriffsrecht für die Tabelle projekt entzogen. 13.1.2 Einschränkung des Datenzugriffs mit Views Wie schon in Kapitel 10 gezeigt, können Views für folgende Zwecke benutzt werden: - um den verschiedenen Benutzern unterschiedliche Sichtweisen auf Datenwerte einer Datenbank zur Verfügung zu stellen; - um die Tabellen mit einer großen Anzahl von Spalten den Benutzern gegenüber in einer eingeschränkten Form darzustellen, wodurch die Wahrnehmung und Handhabung für die Benutzer einfacher wird. Zusätzlich dazu können die Datenwerte einer Datenbank mit Hilfe von Views gezielt geschützt werden. Wenn z. B. eine Tabelle eine Spalte mit Gehältern der Mitarbeiter einer Firma enthält, ist es möglich, durch die Erstellung eines Views, das die Gehälter-Spalte nicht beinhaltet, den Zugriff auf die Tabelle einzuschränken. In diesem Fall könnten z.B. alle Benutzer das SELECT-Zugriffsrecht auf das erstellte View haben, während nur eine kleine Anzahl ausgewählter Benutzer -178-
dasselbe Zugriffsrecht auf die ganze Tabelle und damit auch auf die Spalte mit den Gehältern bekommen könnte. Dieselbe Regel gilt auch für alle sicherheitsrelevanten Datenwerte einer Datenbank. Mit Hilfe der Beispieldatenbank werden verschiedene Möglichkeiten gezeigt, wie der Zugriff auf Datenwerte einer Datenbank eingeschränkt werden kann. Beispiel 14.6 Erstellen Sie ein View, das den Zugriff auf die Mittel der Tabelle projekt nicht erlaubt. CREATE v_pr_mittn AS SELECT pr_nr, pr_name FROM projekt; Die Benutzer des Views v_pr_mittn sehen einen Spaltenausschnitt der Tabelle projekt. Mit der Erstellung dieses Views kann der Zugriff auf die Tabelle projekt eingeschränkt werden. Damit kann allen Benutzern der Zugriff auf das View ermöglicht und nur den Benutzern, die die Projektmittel kennen dürfen, der Zugr iff auf die ganze Tabelle erlaubt werden. Beispiel 14.7 Erstellen Sie ein View, das alle Mitarbeiter der Abteilung a2 enthält. CREATE VIEW v_mit_a2 AS SELECT m_nr, m_name, m_vorname, pr_nr FROM mitarbeiter WHERE pr_nr = 'a2'; Das View v_mit_a2 stellt einen Ausschnitt der Reihen der Tabelle mitarbeiter dar. Beispiel 14.8 Erstellen Sie ein View, das Personalnummer, Namen und Vornamen aller Projektleiter beinhaltet. CREATE VIEW v_arbmit_pl AS SELECT mitarbeiter.m_nr, m_name, m_vorname FROM mitarbeiter, arbeiten WHERE mitarbeiter.m_nr = arbeiten.m_nr AND aufgabe = 'Projektleiter'; Das View v_arbmit_pl stellt einen Ausschnitt der Reihen und Spalten der Tabellen mitarbeiter und arbeiten dar.
13.2 Integritätsvorschriften Die Integrität einer Datenbank ist eine der grundlegenden Eigenschaften, die ein Datenbanksystem gewährleisten muß. Sie bezieht sich auf die Integrität von Datenwerten, die in der Datenbank gespeichert sind. Ein Datenbanksystem muß in der Lage sein, unsinnige bzw. widersprüchliche Datenwerte zu erkennen und abzuweisen. Die Vorschriften, mit denen das Einfügen bzw. das Modifizieren der Datenwerte einer Datenbank untersagt wird, falls die Definition eines Datenbankobjektes bzw. -179-
eine vom Benutzer definierte Regel verletzt wird, werden Integritätsvorschriften ("integrity constraints") genannt. INGRES unterstützt zwei Angaben: - NOT NULL in der CREATE TABLE-Anweisung und - UNIQUE in der CREATE INDEX-Anweisung, die die Verletzung der Definition einer Tabelle bzw. eines Index regulieren. Mit der Angabe NOT NULL wird jeder Versuch, einen NULL-Wert in die entsprechende Spalte einzufügen, vom System abgewiesen. Genauso weist das System den Versuch, das Einfügen der mehrfach vorhandenen Werte in eine Spalte, für die ein UNIQUE-Index existiert, ab. Abgesehen von diesen im INGRES-System existierenden Integritätsvorschriften ist es bei INGRES möglich, explizite Vorschriften für Tabellen zu definieren. Diese Vorschriften können mit Hilfe: - einer INGRES-Benutzerschnittstelle wie QBF, RBF oder Report-Writer; - des Knowledge Management-Moduls und - der CREATE INTEGRITY-Anweisung definiert werden. In diesem Kapitel werden die Integritätsvorschriften, die mit der CREATE INTEGRITY-Anweisung definiert werden können, beschrieben. (Die Möglichkeiten, die INGRES-Benutzerschnittstellen in dieser Hinsicht bieten, werden z.T. in den Kapiteln 4 und 5 erläutert. Der Knowledge Management-Modul wird ausführlich in Kapitel 20 erklärt.) Die CREATE INTEGRITY-Anweisung definiert eine Integritätsvorschrift für eine Tabelle. Diese Anweisung hat folgende Form CREATE INTEGRITY ON tab_name [alias_name] IS bedingung; tab_name ist der Name der Tabelle für die eine Integritätsvorschrift definiert wird. alias_name kennzeichnet den optionalen Aliasnamen dieser Tabelle. bedingung definiert eine Bedingung, die die Integritätsvorschrift darstellt. Nur der Tabelleneigentümer darf für eine Tabelle die Integritätsvorschriften mit Hilfe der CREATE INTEGRITY-Anweisung definieren. Wenn die Integritätsvorschrift für eine geladene Tabelle definiert wird, müssen alle existierenden Reihen dieser Tabelle die Vorschrift erfüllen. Falls dies nicht der Fall ist, wird die CREATE INTEGRITY-Anweisung mit einer Fehlermeldung abgewiesen. Nach der erfolgreichen Definition einer Integritätsvorschrift wird jede Änderung der Tabelle, die in der CREATE INTEGRITY-Anweisung genannt ist, auf die Integrität überprüft. Alle Versuche, die definierte Vorschrift zu verletzen, werden vom INGRES-System abgewiesen, ohne einen Fehler zu melden. Beispiel 14.8 CREATE INTEGRITY ON projekt -180-
IS mittel < 200000; In Beispiel 14.8 ist eine Integritätsvorschrift definiert, die die obere Grenze der Mittel aller Projekte auf 200000DM festsetzt. Hinweis. Der Zeitpunkt der Ausführung einer CREATE INTEGRITY-Anweisung muß genau überlegt werden, weil diese Anweisung die Tabelle, auf die sich die Integritätsvorschrift bezieht, exklusiv sperrt. Zusätzlich dazu kann die Ausführung dieser Anweisung relativ lange dauern, wenn die Tabelle, auf die sich die Vorschrift bezieht, groß ist. Mit der Anweisung DROP INTEGRITY ON tab_name ALL | zahl_1 [,zahl_2,...]; können eine oder mehrere, für die Tabelle tab_name definierte Integritätsvorschriften gelöscht werden. Die Ganzzahlen zahl_1, zahl_2,... kennzeichnen die mit der CREATE INTEGRITY-Anweisung definierten Integritätsvorschriften für die Tabelle tab_name . Der existierende Zusammenhang zwischen den Integritätsvorschriften einerseits und den angegebenen Ganzzahlen andererseits kann mit Hilfe der HELP INTEGRITY-Anweisung erlangt werden. Mit der Angabe ALL werden alle für die Tabelle tab_name definierten Integritätsvorschriften gelöscht. Jede vorübersetzte und gespeicherte Abfrage, die sich auf eine in der DROP INTEGRITY-Anweisung angegebene Tabelle bezieht, muß noch einmal übersetzt und gespeichert werden. Der Grund für diese Maßnahme ist, daß alle existierenden Integritätsvorschriften bei der Erstellung einer solchen Abfrage berücksichtigt werden. (Dasselbe gilt für die in Kapitel 18 beschriebenen Prozeduren.)
Aufgaben. A.14.1 Die Tabelle systeme wird mit der folgenden CREATE TABLE-Anweisung erstellt: CREATE TABLE systeme (s_name CHAR(15) NOT NULL, version CHAR(5) NOT NULL, hersteller CHAR(20), ort CHAR(20), preis FLOAT); Vergeben Sie den genannten Benutzern folgende Zugriffsrechte: a) Dem Benutzer gabi alle Rechte für die obige Tabelle. b) Dem Benutzer rainer SELECT-Rechte für die ganze Tabelle. c) Dem Benutzer alex INSERT- und DELETE-Rechte für die ganze Tabelle. d) Dem Benutzer peter UPDATE-Rechte für die Spalten hersteller und preis. Dieser Benutzer darf diese Rechte weiteren Benutzern vergeben. -181-
A.14.2 Entziehen Sie den Benutzern gabi und peter die in A.14.1 vergebenen Zugriffsrechte.
-182-
Systemumgebung
Systemumgebung .........................................................................................................................183 15
Systemumgebung ...............................................................................................................183 15.1 Umgebungsvariablen 183 15.2 Betriebssystemkommandos 186 15.2.1 ingmenu-Kommando 186 15.2.2 Kommando qbf 186 15.2.3 Kommando vifred 187 15.2.4 Kommandos rbf, sreport und report 187 15.2.5 Kommando vigraph 188 15.2.6 Kommando sql 188 15.3 INGRES-Dienstprogramme 189 15.3.1 Das Dienstprogramm accessdb 189 15.3.2 Das Dienstprogramm catalogdb 192 15.3.3 Dienstprogr amme createdb und destroydb 15.3.4 Dienstprogramme ckpdb und rollforwarddb 15.3.5 Das Dienstprogramm unloaddb 194 15.3.6 Dienstprogramm auditdb 195
192 193
14 Systemumgebung In diesem Kapitel werden verschiedene Aspekte beschrieben, die zur Umgebung des INGRES-Systems gehören. Zuerst werden die Umgebungsvariablen dargestellt und erörtert. Danach werden die wichtigsten Betriebssystem-Kommandos beschrieben.
14.1 Umgebungsvariablen Bei der Installation des INGRES-Systems ist es notwendig, die ganze INGRES-Umgebung, die aus vielen Dateien besteht, zu definieren. In der Anwendungsphase eines INGRES-Systems müssen die veschiedenen Modi, in denen INGRES-Daten bearbeitet werden, festgelegt werden. Diese beiden Aktivitäten werden mit Hilfe der Umgebungsvariablen durchgeführt. Die wichtigsten Umgebungsvariablen, die die Installation des INGRES-Systems beeinflussen, sind: - II_SYSTEM, - II_DATABASE, - II_CHECKPOINT, - II_JOURNAL und -183-
- II_LOG_FILE Variable II_SYSTEM
.
Funktion Diese Umgebungsvariable wird von INGRESSubsystemen verwendet, um den Installationsort des Datenbank-Managementsystems zu finden. Weil II_SYSTEM keinen voreingestellten Wert hat, muß jeder Anwender den Wert dieser Umgebungsvariablen explizit setzen. Beispiel (UNIXBourne Shell) II_SYSTEM=/u/install export II_SYSTEM (In diesem Beispiel wird angenommen, daß sich das DBMS von INGRES im Dateiverzeichnis /u/install befindet.)
II_DATABASE
Die Umgebungsvariable II_DATABASE definiert das physikalische Gerät, in dem die Datenbanken eines INGRES-Systems gespeichert sind. Bei den meisten Rechnern ermöglicht INGRES die Verwendung eines logischen Namens, der dann einem physikalischen Gerät zugewiesen werden kann. Durch die Änderung der Verbindung zwischen dem logischen und dem physikalischen Namen ist es möglich, einen neuen Speicherungsort für Datenbanken eines Systems zu bestimmen, ohne das ganze System neuinstallieren zu müssen.(In UNIX ist es möglich, mit Hilfe des Dienstprogramms createdb, den Speicherungsort der neuzuerstellenden Datenbanken zu ändern.) Die in Kapitel 11 beschriebene Master-Datenbank iidbdb ist an dem durch II_DATABASE definierten Speicherungsort gespeichert. Diese Datenbank ist gleichzeitig die einzige, deren Speicherungsort nicht geändert werden kann. II_CHECKPOINT II_CHECKPOINT definiert den Speicherungsort der Sicherungskopie einer Datenbank. Diese Umgebungsvariabale wird während des Installationsvorgangs vom INGRES-System gesetzt und kann nicht nachträglich geändert werden. Dem INGRES-Systemverwalter wird dringend empfohlen, die Sicherungskopie einer Datenbank nicht gemeinsam mit der Datenbank zu speichern. Falls die Anlage nur ein Plattenlaufwerk hat, sollte der Speicherungsort der Sicherungskopie ein Magnetbandgerät sein. II_JOURNAL II_JOURNAL definiert den Speicherungsort der -184-
II_LOG_FILE
Journal-Datei. Diese Umgebungsvariable wird während des Installationsvorgangs des INGRESSystems gesetzt und kann nicht nachträglich geändert werden. Diese Umgebungsvariable definiert den Speicherungsort der Log-Datei des ganzen INGRES-Systems. II_LOG_FILE wird auch während des Installationsvorgangs des INGRESSystems gesetzt.
Eine weitere Gruppe von Umgebungsvariablen beeinflußt das Format der verschiedenen INGRES-Datentypen sowie den Bildschirmtyp eines Rechners. Zu dieser Gruppe gehören: - II_DECIMAL, - II_MONEY_FORMAT, - II_MONEY_PREC, - II_DATE_FORMAT und - TERM_INGRES . Variable II_DECIMAL
II_MONEY_FORMA T
II_MONEY_PREC
II_DATE_FORMAT
Funktion Die Umgebungsvariable II_DECIMAL definiert das Trennzeichen zwischen Vor- und Nachkommastellen einer Dezimalzahl. Die einzigen zugelassenen Zeichen sind "." und ",", wobei der Punkt die Voreinstellung ist. Diese Umgebungsvariable legt den Währungsbezeichner eines Geldbetrages fest. Sie besteht aus zwei Teilen, die durch das Zeichen ":" getrennt sind. Der erste Teil definiert die Position des Währungsbezeichners wobei "L" ("leading") den Währungsbezeichner am Anfang und "T" ("trailing") den Währungsbezeichner am Ende des Geldbetrages setzt. Der zweite Teil der Umgebungsvariablen setzt den tatsächlichen Wert für den Währungsbezeichner fest. II_MONEY_PREC definiert die Anzahl der Nachkommastellen eines Geldbetrages. Die möglichen Angaben sind 0,1 und 2 wobei 2 die Voreinstellung ist. Mit der Umgebungsvariablen II_DATE_FORMAT wird das Ausgabeformat eines Datums festgelegt. Die Voreinstellung für das Ausgabeformat ist dd-mmm- yyyy . Das Eingabeformat kann folgende drei Formen haben dd-mmm- yyyy, mm/dd/yy und mmddyy . Das Setzen der Umgebungsvariablen -185-
TERM_INGRES
II_DATE_FORMAT bewirkt das Ersetzen eines der drei Formate durch das angegebene Format. Mit dieser Umgebungsvariablen wird der Bildschirmtyp festgelegt. Die richtige Einstellung von TERM_INGRES ist besonders für die formatgesteuerten INGRESKomponenten wie VIFRED und QBF von Bedeutung.
14.2 Betriebssystemkommandos In den Kapiteln 3 bis 5 haben wir das formatgesteuerte Menüsystem mit allen Funktionen wie INGRES/FORMS, INGRES/REPORTS usw. dargestellt. Eine andere Möglichkeit, diverse INGRES-Subsysteme aufzurufen, bieten die Betriebssystemkommandos von INGRES. 14.2.1 ingmenu-Kommando Das erste Betriebssystemkommando, das schon in Kapitel 3 verwendet wurde, ist ingmenu. Dieses Kommando ermöglicht den Aufruf des formatgesteuerten Menüsystems von INGRES und hat folgende Form ingmenu [-e] db_name [-uben] db_name ist der Datenbankname. Der optionale Schalter "-e" ruft ingmenu im sogenannten leeren Modus auf. In diesem Modus werden alle Masken, die im Zusammenhang mit den INGRES-Subsystemen stehen, leer am Bildschirm ausgegeben, um dem Benutzer die Möglichkeit zu geben, spezifische Objektnamen eintragen zu können. Der Schalter "- u" ist dem Systemadministrator bzw. dem DBA für die Datenbank db_name vorbehalten, um ingmenu als Benutzer ben aufrufen zu können. 14.2.2 Kommando qbf Mit dem Kommando qbf wird das INGRES-Subsystem QBF direkt aufgerufen. Die Syntax dieses Kommandos ist qbf db_name [-s] [-mmodus] [-uben] [-f|-j|-t|-l] obj_name db_name ist der Datenbankname. Der Schalter "-s" unterdrückt alle Meldungen bei der Ausgabe am Bildschirm. Mit dem Schalter "- m" wird die QBF-Funktion JoinDef umgangen und mit modus eine der vier Unterfunktionen Retrieve, Append, Update oder All angegeben. Der INGRES-Systemverwalter oder der DBA können den Schalter "- u" verwenden, um das QBF als Benutzer ben aufzurufen. obj_name ist ein QBF-Name bzw. der Name einer Tabelle oder eines JoinDefs. Welches von diesen Objekten tatsächlich -186-
ausgesucht wird, wird mit einem der Schalter "- f", "-j", "-t" oder "- l" genau festgelegt. "- f" steht für einen QBF-Namen, "-j" für einen JoinDef, "-t" für eine Tabelle und "- l" für eine fest vorgeschriebene Suche nach dem Objekt obj_name (zuerst wird obj_name unter dem QBF-Namen, dann unter JoinDefs und am Ende unter dem Tabellennamen gesucht). Falls keiner der vier Schalter angegeben wird, wird standardmäßig angenommen, daß obj_name der Name einer Tabelle ist. 14.2.3 Kommando vifred Mit dem Kommando vifred wird das INGRES-Subsystem VIFRED direkt aufgerufen. Die Syntax dieses Kommandos ist vifred db_name [obj_name [-f|-t|-j]] [-e] [-uben] db_name ist der Datenbankname. Der Schalter "- u" ist dem Systemverwalter und dem DBA vorbehalten, um VIFRED als Benutzer ben aufzurufen. obj_name ist entweder der Name einer existierenden Maske oder der Name einer Tabelle, bzw. eines JoinDefs. Welches dieser Objekte tatsächlich ausgesucht wird, wird entweder mit dem Schalter "- f" (Maske), oder "-t" (Tabelle) oder mit "-j" (JoinDef) genau festgelegt. Falls keiner dieser Schalter angegeben wird, wird standardmäßig angenommen, daß es sich um eine Maske handelt. Mit dem Schalter "-e" wird eine leere Maske angezeigt und dem Benutzer die Möglichkeit gegeben, den Namen des Objektes selbst einzutragen. 14.2.4 Kommandos rbf, sreport und report Die wichtigsten INGRES-Kommandos bezüglich der Erstellung und des Ablaufs von Listenprogrammen sind rbf, sreport und report. Mit dem Kommando rbf wird das INGRES-Subsystem RBF direkt aufgerufen. Die Syntax dieses Kommandos ist rbf [-s] [-uben] [-r]|[-m[modus]] [-lmax_länge] [-e] db_name obj_name db_name ist ein Datenbankname, während obj_name entweder der Name eines Listenprogramms oder einer Tabelle ist. Der Schalter "- s" unterdrückt alle Ausgabemeldungen am Bildschirm. Der INGRES-Systemverwalter oder der DBA können den Schalter "-u" verwenden, um das Subsystem RBF als Benutzer ben aufzurufen. Der Schalter "- r" teilt dem Subsystem mit, daß es sich bei obj_name um den Namen eines Listenprogramms handelt. Falls der Schalter "-r" ausgelassen wird, sucht RBF zuerst nach dem Listenprogramm obj_name und, falls es nicht existiert, nach dem gleichen Tabellennamen. Beim Vorhandensein eines solchen Tabellennamens wird ein Standard-Listenprogramm für diese Tabelle erstellt. Mit dem Schalter "- m" wird ein Standard-Listenprogramm für -187-
die Tabelle obj_name erstellt. Die Angabe modus beschreibt die Form des Standard-Listenprogramms näher. Sie kann die Werte wrap, column oder block annehmen. Der Schalter "- l" definiert die maximale Länge einer Listenprogrammzeile. Der voreingestellte Wert für max_länge hängt vom Bildschirmtyp ab und beträgt meistens 80. Der Schalter "-e" verursacht den Aufruf der Funktion Report Catalog mit einer leeren Maske. Mit dem Kommando sreport [-s] [-uben] db_name listen_quelle wird die Definition eines mit dem Subsystem Report Writer erstellten Listenprogramms in die Datenbank geschrieben. Mit dem Schalter "-s" werden alle Ausgabemeldungen am Bildschirm unterdrückt. Der INGRES-Systemadministrator und der DBA können mit dem Schalter "- u" das Kommando report als Benutzer ben aufrufen. db_name ist der Name der Datenbank, auf die sich das Listenprogramm bezieht, während listen_quelle der Name der Datei ist, die das Listenprogramm enthält. Nachdem ein Listenprogramm mit einem der beiden Kommandos rbf oder sreport erstellt wurde, kann es mit dem Kommando report ausgeführt werden. Zusätzlich dazu kann report auch ein Standard- Listenprogramm ausführen. Die Syntax dieses Kommandos ist report [-fdatei_name] [-s] [-uben] [-r|[-mmodus]] [-lmax_laenge] db_name obj_name [param_1=wert_1,...] Die für das Kommando report existierenden Schalter haben dieselbe Bedeutung wie die gleichnamigen Schalter des Kommandos rbf. report ermöglicht zusätzlich die direkte Angabe eines oder mehrerer Parameterwerte, die beim Ablauf des Listenprogramms den entsprechenden Parametern zugewiesen werden. 14.2.5 Kommando vigraph Mit dem Kommando vigraph [-b] [-ggraph_name] [-e] [-uben] db_name wird der grafische Editor direkt aufgerufen. db_name kennzeichnet die Datenbank, deren Daten für die Grafik verwendet werden sollen. Mit dem Schalter "-b" wird die VIGRAPH-Sitzung mit einer leeren Grafik eröffnet, während "-g" zur Sitzungseröffnung die Grafik namens graphik_name anzeigt. Mit dem Schalter "-e" wird die VIGRAPH-Sitzung mit der Funktion Catalog Frame eröffnet, und die Maske dieser Funktion erscheint leer am Bildschirm. Schließlich, können mit dem Schalter "-u" der DBA und der INGRES-Systemadministrator VIGRAPH als Benutzer ben starten. 14.2.6 Kommando sql Mit dem Kommando sql wird das INGRES-DBMS aufgerufen. Die -188-
Syntax dieses Kommandos ist sql [schalter_liste] [<eingabe_dat] [>ausgabe_dat] db_name db_name ist der Name einer existierenden Datenbank. schalter_liste kann einen oder mehrere Schalter enthalten, von welchen wir die wichtigsten erörtern werden. Mit dem Schalter "+U" bzw. "- U" wird dem Benutzer die Erlaubnis, den INGRES-Systemkatalog zu ändern, erteilt bzw. entzogen. Die Voraussetzung, den Schalter "+U" benutzen zu dürfen, wird mit dem Dienstprogramm accessdb erteilt. Die Voreinstellung ist "+U", und es wird dringend empfohlen, den Schalter "+U" nicht zu benutzen, weil die Änderung der Systemtabellen sehr gefährlich sein kann. Z.B. würde das Löschen interner Informationen über eine Tabelle unter Umständen das weitere Arbeiten mit dieser Tabelle und damit mit den ganzen Datenbankanwendungen unmöglich machen. Mit "- uben" kann der DBA als Benutzer ben das INGRES-DBMS aufrufen. Der Schalter "- vzeichen" legt den Spaltenbegrenzer für die Ausgabe auf dem Bildschirm bzw. auf dem Drucker fest. Die Voreinstellung ist "|". Mit dem Schalter "- l" kann ein Benutzer die Datenbank db_name exklusiv sperren. Eine schon eröffnete Datenbank kann in diesem Fall nicht benutzt werden. Mit dem Schalter "+s" werden die Ausgabemeldungen am Bildschirm ausgegeben. Der alternative Schalter "-s" unterdrückt alle Meldungen. Falls keiner dieser Schalter angegeben ist, wird "+s" angenommen. Mit der Angabe "<eingabe_dat" wird die SQL-Anweisungsfolge aus der Datei eingabe_dat geholt und ausgeführt. Diese Datei soll alle notwendigen Anweisungen enthalten, um eine INGRES-Sitzung ausführen zu können. Dementsprechend wird mit ">ausgabe_dat" die Ausgabe vom Bildschirm zur Datei ausgabe_dat umgeleitet. Beispiel 15.1 sql beispiel ausgabe In Beispiel 15.1 werden alle Anweisungen der INGRES-Sitzung aus der Datei create geholt und die entsprechende Ausgabe in die Datei ausgabe umgeleitet.
14.3 INGRES-Dienstprogramme INGRES-Dienstprogramme sind fester Bestandteil der INGRES-Software. Sie unterstützen die Benutzer bei der Bewältigung wichtiger Aufgaben, wie der Erstellung der Diagnoseinformationen aus eine r Datenbank, der Portierung einer Datenbank von einem Rechner zu einem anderen usw. 14.3.1 Das Dienstprogramm accessdb Das Dienstprogramm accessdb ist ein frame-basiertes Menüsystem von INGRES, mit dem der INGRES-Systemverwalter -189-
folgende Aufgaben durchführen kann: - Die existierenden Datenbanken auflisten bzw. modifizieren; - Den Speicherungsort von Datenbanken, Journal-Dateien und Sicherungskopien abfragen, bzw. diesen Objekten neue Speicherungsorte zuweisen; - Die Information über die existierenden Benutzer einer Datenbank abfragen bzw. neue Benutzer eintragen oder die existierenden löschen. Nach dem Aufruf des Betriebssystemkommandos accessdb db_name erscheint das frame-basiertes Menüsystem mit folgenden Funktionen im Hauptmenü: - Databases, - ExtendDB, - Location Name, - User und - Catalog . Mit der Funktion Databases kann die Information über eine existierende Datenbank abgefragt sowie weiteren INGRES-Benutzern der Zugriff auf diese Datenbank ermöglicht werden. Die Abbildung 15-1 zeigt das Databases-frame.
Database: beispiel Owner: peter
Global Access: y
Database Location: ii_database Checkpoint Location: ii_checkpoint
Database Users
Journal Location: ii_journal
root petra
Save
Help
End
Abb. 15-1 Das Databases-frame In Abbildung 15-1 wird Information für die Datenbank beispiel ausgegeben. U.a. sind der DBA sowie die Speicherungsorte der Objekte dieser Datenbank aufgelistet. Die Liste der existierenden Benutzer kann, falls notwendig, erweitert werden. Die Voraussetzung dafür ist, daß es sich um einen INGRES-Benutzer handelt; d.h. dieser Benutzer wurde mit der Funktion User in der INGRES-Benutzerliste schon eingetragen. -190-
Mit der Funktion ExtendDB ist es möglich, die einem DBA zugehörigen Datenbanken aufzulisten und, falls notwendig, einzelnen Datenbanken neue Speicherungsorte zuzuweisen. Die neuzugewiesenen Speicherungsorte müssen dem INGRES-System im voraus bekannt sein. Die Funktion Location Name dient dazu, die an einem Speicherungsort existierenden Objekte (Datenbanken, Journal-Dateien oder Sicherungskopien) abzufragen. Damit kann mit dieser Funktion die genaue Einsicht über den Inhalt einzelner Speicherungsorte gewonnen und die Definition neuer Speicherungsorte veranlaßt werden. Die Funktion User listet alle existierenden INGRES-Benutzer auf. Mit dieser Funktion ist es auch möglich, neue Benutzer einzutragen bzw. existierende Benutzer zu löschen. Die Abbildung 15-2 zeigt das User-frame.
User Name: peter Permissions: create database: y
set trace flags: n
update sys cat: n
superuser:
Owns
May Access
n
dbname
dbname
beispiel
beispiel verkauf
Abb. 15-2 Das User- frame Abbildung 15-2 zeigt sowohl die allgemeinen Zugriffsrechte des Benutzers peter als auch die Liste der Datenbanken, für welche dieser Benutzer der DBA ("Own") ist, bzw. für welche er das Zugriffsrecht ("May Access") hat. Mit der Funktion Catalog wird die allgemeine Systeminformation selektiv ausgegeben. Dabei können folgende Unterfunktionen ausgewählt werden: - Databases, - Location Names und - Users. Funktion Databases Location Names Users
Ergebnis gibt eine Tabelle mit der Liste aller Datenbanken des INGRES-Systems aus. listet alle existierenden Speicherungsorte der Komponenten des INGRES-Systems auf. listet die Namen aller Benutzer und ihre -191-
Zugriffsrechte in Tabellenform auf.
USER SUMMARY Permission legend: user name $ingres peter ingres root
a y y y y
b y n y n
c y n y n
d y n y n
a -- May create database b -- May update system catalog c -- May set trace flags d -- Superuser
Abb. 15-3 Das User Summary-frame Die Benutzerliste in Abbildung 15-3 enthält vier Benutzer: $ingres, peter, ingres und root. Diese vier Benutzer haben unterschiedliche Zugriffsrechte, die in vier Spalten angegeben sind. Die Angabe "y" kennzeichnet das vergebene Zugriffsrecht. Diese Angabe in der ersten Spalte bedeutet, daß der Benutzer das DBA-Recht hat, d.h. er darf die Datenbanken erstellen. Das zweite Zugriffsrecht erlaubt einem Benutzer die Änderung der INGRES-Systemtabellen. Die Angabe "y" in der dritten Spalte erlaubt die Verwendung von "trace"-Schaltern, während dieselbe Angabe in der vierten Spalte den Benutzer zum Superuser ernennt. 14.3.2 Das Dienstprogramm catalogdb Mit dem Dienstprogramm catalogdb kann ein Benutzer alle ihm zugehörigen Datenbanken auflisten bzw. der DBA die Liste aller einem Benutzer gehörenden Datenbanken erstellen. catalogdb wird mit dem Betriebssystemkommando catalogdb [-uben] aufgerufen. catalogdb ist ein formatgesteuertes Menüsystem, mit dem ein Benutzer die Datenbanken auflisten kann, für die er den Zugriff hat. Der Schalter "- u" ist dem INGRES-Systemadministrator vorbehalten, um die Liste aller dem Benutzer ben zugehörigen Datenbanken zu erstellen. 14.3.3 Dienstprogramme createdb und destroydb Mit dem Dienstprogramm createdb wird eine neue Datenbank erstellt. Der Benutzer, der die Datenbank erstellt hat, wird der Datenbankadministrator dieser Datenbank. createdb wird mit dem Betriebssystemkommando createdb [-uben] [-p] [-csp_ort1] [-dsp_ort2] [-jsp_ort3] db_name aufgerufen. Der Schalter "- u" ist dem INGRES-Systemadministrator vorbehalten. Mit diesem Schalter -192-
kann eine neue Datenbank für den Benutzer ben eingerichtet werden. Der Schalter "-p" schränkt den Zugriff auf die Datenbank db_name für den DBA und die mit dem Dienstprogramm accessdb explizit genannten Benutzer ein. db_name ist der Name der zu erstellenden Datenbank. Mit den Schaltern "-c" , "-d" und "-j" werden die Speicherungsorte der Sicherungskopie, der Datenbank-Dateien und der Journal-Dateien in dieser Reihenfolge gespeichert. Jeder Speicherungsort muß im voraus mit dem Dienstprogramm accessdb erstellt werden. Beispiel 15.4 createdb beispiel -cdev1_ing -ddev2_ing -jdev3_ing In Beispiel 15.4 wird eine neue Datenbank beispiel erstellt. Die Sicherungskopie, die Datenbank- und Journal-Dateien werden an drei unterschiedlichen Orten gespeichert. Mit dem Dienstprogramm destroydb wird eine erstellte Datenbank gelöscht. Das Dateiverzeichnis der Datenbank und alle ihm zugehörigen Dateien werden gelöscht. destroydb wird mit dem Betriebssystemkommando destroydb [-p] [-uben] db_name aufgerufen. Nur der DBA und der INGRES-Systemadministrator (falls er den Schalter "- u" verwendet) können eine Datenbank löschen. Der Schalter "-p" veranlaßt das System vom Benutzer eine zusätzliche explizite Zustimmung zum Löschen der Datenbank zu erteilen. 14.3.4 Dienstprogramme ckpdb und rollforwarddb Das Dienstprogramm ckpdb erzeugt die Sicherungskopie einer oder mehrerer Datenbanken eines INGRES-Systems und kennzeichnet alle bis zu diesem Augenblick erstellten Journal-Dateien als ungültig. Dieses Dienstprogramm wird mit dem Betriebssystemkommando ckpdb [-d] [+j|-j] [-mgeraet] [-uben] [-s] db_liste aufgerufen. Mit dem Schalter "-d" werden die zuletzt erstellte Sicherungskopie der Datenbank und die Jorunal-Datei gelöscht. "+j" bzw. "-j" schaltet die Erstellung der Journal-Dateien ein bzw. aus. Der Schalter "- m" definiert mit geraet das Speichermedium für die Sicherungskopie der Datenbank, während "- u" die Sicherungskopien aller dem Benutzer ben zugehörigen Datenbanken erstellt. Der Schalter "- s" kann nur der DBA benutzen, um den systemübergreifenden Zugriff auf jede Datenbank des INGRES-Systems zu ermöglichen. db_liste stellt die Liste der Datenbanken dar, für die die Sicherungskopie erstellt werden soll. Beispiel 15.2 ckpdb +j -m /dev/tape beispiel In Beispiel 15.2 wird die Sicherungskopie der Datenbank beispiel erstellt und auf dem Magnetbandgerät /dev/tape gesichert. Gleichzeitig wird die Erstellung der -193-
Journal-Dateien veranlaßt. Mit dem Dienstprogramm rollforwarddb wird die Restaurierung einer oder mehrerer Datenbanken mit Hilfe der zuletzt erstellten Sicherungskopie(n) und der Journal- Dateien gestartet. rollforwarddb wird mit dem Betriebssystemkommando rollforwarddb [+c|-c] [+j|-j] [-mgeraet] [-s] [-uben] [-v] db_liste aufgerufen. Die Schalter "+j", "- j", "- m", "-s" und "-u" haben dieselbe Bedeutung wie die gleichnamigen Schalter des Kommandos ckpdb. Mit dem Schalter "+c" wird die Restaurierung einer oder mehrerer Datenbanken gestartet während "-c" dies unterdrückt. Der Schalter "- v" veranlaßt die Protokollierung aller Operationen während des Restaurierungsprozesses. Das Dienstprogramm rollforwarddb sperrt die Datenbank(en) exklusiv, während ckpdb ab der version 6.3 dies unterläßt. Beispiel 15.3 rollforwarddb +j -m /dev/tape beispiel In Beispiel 15.3 wird die Datenbank beispiel restauriert, indem zuerst die existierende Sicherungskopie des Magnetbandgeräts /dev/tape benutzt wird, und danach die Journal-Dateien angewendet werden. 14.3.5 Das Dienstprogramm unloaddb Das Dienstprogramm unloaddb erstellt Prozeduren, mit denen eine Datenbank entladen bzw. neugeladen werden kann. unloaddb wird mit dem folgenden Betriebssystemkommando unloaddb [-uben] [-c] [-dpfad_name] [-lsql] db_name aufgerufen. unloaddb ist ein sehr nützliches Dienstprogramm, mit dem ein DBA zwei Kommandodateien unload.ing und reload.ing erstellen kann. Die erste Datei enthält Anweisungen, um jede Benutzer- und Systemtabelle einer Datenbank in ein explizit angegebenes Dateiverzeichnis zu kopieren. Die Datei reload.ing ermöglicht das Neuladen einer Datenbank mit Hilfe der Information, die in den mit unload.ing erstellten Dateien enthalten ist. Das Dienstprogramm unloaddb liefert zusätzlich zu den Informationen über Benutzertabellen auch die Informationen über alle Views, Integritätsvorschriften, Masken und Listenprogramme einer Datenbank. Damit eignet sich unloaddb besonders dann, wenn die ganze Datenbank neu aufgebaut werden muß. Hinweis. Die Funktionalität des Dienstprogramms unloaddb besteht nicht darin, eine Datenbank neuzuladen bzw. zu entladen. unloaddb erstellt nur die dafür notwendigen Prozeduren. Der DBA muß anschließend die Prozeduren starten, damit die Datenbank entladen bzw. neugeladen wird. Mit dem Schalter "- u" wird dem DBA erlaubt, das Dienstprogramm unloaddb als der Benutzer namens ben -194-
auszuführen. Der Schalter "-c" veranlaßt die Kommandos in den beiden generierten Kommandodateien das Kopieren der Dateien mit Hilfe vom ASCII-Zeichensatz durchzuführen. Damit wird die Portierung der Datenbanken eines INGRES-Systems von einem Rechner zu einem anderen Rechner ermöglicht. Falls die beiden Dateien unload.ing und reload.ing nicht im aktuellen Dateiverzeichnis gespeichert werden sollen, muß der Schalter "-d" mit dem absoluten oder relativen Pfadnamen des neuen Dateiverzeichnisses angegeben werden. db_name schließlich definiert den Namen der Datenbank, die geladen bzw. entladen werden soll. 14.3.6 Dienstprogramm auditdb Das Dienstprogramm auditdb ermöglicht die Auswertung der Journal-Dateien. Durch die verschiedenen Schalter kann die Ausgabe spezifischer Information aus einer Journal-Datei sowohl für eine Datenbank als auch für eine Tabelle veranlaßt werden. auditdb wird mit dem Betriebssystemkommando auditdb [-bzeit] [-ezeit] [-f] [-iben1] [-s] [-ttab_name] [-uben2] db_name aufgerufen. Mit dem Schalter "-b" bzw. "-e" können alle erfolgreich beendeten Transaktionen vor bzw. nach der Zeitangabe zeit ausgegeben werden. Die Maske dieser Zeitausgabe ist "dd- mmm- yyyy:hh:mm:ss" wobei "dd" für Tag. "mmm" für Monat, "yyyy" für Jahr, "hh" für Stunde, ""mm" für Minute und "ss" für Sekunde steht. Der Schalter "-t" gibt alle Einträge der Journal- Datei bezüglich der Tabelle tab_name aus. Mit dem Schalter "- f" kann eine binäre Datei erstellt werden, die die Reihen der mit dem Schalter "- t" definierten Tabelle enthält. Dabei handelt es sich um die Reihen, die entweder in der Tabelle eingefügt oder hineinkopiert bzw. aus der Tabelle gelöscht wurden. Mit dem Schalter "- i" werden alle Einträge der Journal-Datei, die im Zusammenhang mit den Aktivitäten des Benutzers ben1 protokolliert wurden, ausgegeben. "- u" gibt alle Einträge aus, die im Zusammenhang mit den Datenbanken des Benutzers ben2 stehen. Mit dem Schalter "-s" wird dem INGRES-Systemadministrator die Möglichkeit gegeben, auf INGRES-Datenbanken zuzugreifen. db_name schließlich definiert die Datenbank, für die die Einträge aus der Journal-Datei ausgewertet werden.
-195-
Die eingebettete SQL-Sprache
Die eingebettete SQL-Sprache ................................................ Fehler! Textmarke nicht definiert. 16
Die eingebettete SQL-Sprache ...........................................................................................196 16.1 Einleitung 196 16.1.1 Kennzeichnen der eingebetteten SQL-Anweisungen 198 16.1.2 Variablen 198 16.1.3 Fehlerbehandlung 201 16.2 Eingebettete SQL-Anweisungen ohne Cursor 204 16.3 Verwendung des Cursors 214 16.4 Übersetzung eines ESQL/C- bzw. ESQL/COBOL-Programms 16.5 Eingebettete Anweisungen und Programmierstil 222 16.5.1 Optimierung der Datenbankzugriffe 223 16.5.2 Explizite Abfrage bei der Änderung von Reihen 227 16.5.3 Explizite Angabe aller Reihen in einer SELECT- bzw. INSERT-Anweisung 227 Aufgaben 228
15 Die eingebettete SQL-Sprache In diesem Kapitel werden die eingebetteten SQL-Anweisungen bei INGRES beschrieben, wie sie in den Programmiersprachen C und COBOL benutzt werden können. Zuerst werden die eingebetteten SQL-Anweis ungen ohne Cursor-Benutzung dargestellt. Im zweiten Teil des Kapitels werden die Anwendung des Cursors erklärt und alle SQL-Anweisungen, die im Zusammenhang mit dem Cursor benutzt werden, erläutert. Der letzte Teil des Kapitels ist dem Programmierstil gewidmet; anhand von Beispielen wird erklärt, wie man ein Anwendungsprogramm mit Hilfe einfacher Regeln optimieren kann.
15.1 Einleitung In den bisherigen Kapiteln, die sich mit den SQL-Anweisungen von INGRES befaßt haben, haben wir erklärt, wie diese Anweisungen interaktiv, d.h. direkt am Bildschirm benutzt werden können. Die andere Möglichkeit stellt die Benutzung der Anweisungen innerhalb eines Anwendungsprogramms dar. Ob SQL-Anweisungen interaktiv oder in einem Anwendungsprogramm benutzt werden, hängt von der Art ihrer Verwendung ab. Interaktiv werden die Anweisungen meist -196-
222
227
benutzt, wenn Datendefinitionsanweisungen bzw. einfache Datenmanipulationsanweisungen angegeben werden sollen. Die eingebetteten SQL-Anweisungen werden vor allem dann verwendet, wenn komplexe Abfragen und Änderungen einer Datenbank durchgeführt werden sollen. Dabei ist die Linie, die diese zwei Verwendungsarten trennt, nicht genau. Es kommt sowohl vor, daß eine Datendefinitionsanweisung eingebettet benutzt wird, als auch, daß komplexere Abfragen und Änderungen der Datenbank interaktiv durchgeführt werden. Die interaktiven und eingebetteten SQL-Anweisungen sind im Prinzip sehr ähnlich; der größte Unterschied ist, daß die eingebettete SQL-Sprache zusätzliche Anweisungen beinhaltet, die bei der interaktiven Anwendung nicht vorkommen. Jede SQL-Anweisung, die interaktiv verwendet wird, kann also auch eingebettet in einem Anwendungsprogramm, eventuell mit kleinen Änderungen, benutzt werden. (Diese Änderungen betreffen nur die Datenmanipulationsanweisungen und von diesen besonders die SELECT-Anweisung.) INGRES unterstützt z.Zt. in der UNIX- und VMS-Umgebung die Schnittstellen zu folgenden Programmiersprachen der dritten Generation: - Ada , - C, - COBOL, - FORTRAN, - Basic, - Pascal und - PL/I. Alle Beispiele in diesem und im nächsten Kapitel sind in zwei Programmiersprachen - C und COBOL - geschrieben. Wir haben uns für diese beiden Sprachen aus folgendem Grund entschieden: COBOL ist noch immer die meistverwendete Programmiersprache für die kommerziellen Anwendungen und C die wichtigste Sprache für die Betriebssysteme UNIX und DOS. Trotz dieser Einschränkung auf COBOL und C gelten die Beschreibungen in diesem und im nächsten Kapitel generell für alle INGRES-Schnittstellen zu den Programmiersprachen der dritten Generation. Wir gehen davon aus, daß jeder Leser entweder die eine oder die andere Programmiersprache kennt. Es ist nicht notwendig, die Beispiele in beiden Sprachen nachzuvollziehen; sie sind so gestaltet, daß jedem COBOL-Beispiel ein ähnliches C-Beispiel unmittelbar folgt. Die eingebetteten SQL-Anweisungen können auf zwei verschiedene Weisen abgearbeitet werden. Meist wird ein Vorübersetzer zur Verfügung gestellt, der jede SQL-Anweisung in die Host-Sprache übersetzt. Damit wird, nach der Vorübersetzer-Phase, ein Programm mit einheitlichem Code erzeugt, das anschließend übersetzt werden kann. Die andere Möglichkeit, die eingebetteten Anweisungen abzuarbeiten, ist, eine Modulbibliothek zu verwenden und jede -197-
SQL-Anweisung an den entsprechenden Modul zu übergeben. Die Verwendung der Modulbibliothek kommt in der Praxis nicht so oft vor wie die Verwendung des Vorübersetzers. Alle INGRES-Schnittstellen zu den Sprachen der dritten Generation enthalten einen Vorübersetzer, der die eingebetteten SQL-Anweisungen in die Host-Sprache übersetzt. Die INGRES-Komponente SQL/FORMS, die eine Erweiterung der eingebetteten SQL ist und die Erstellung der maskenbasierten Anwendungen ermöglicht, wird in diesem Buch nicht beschrieben. Die Beschreibung dieser Komponente finden Sie in den entsprechenden INGRES-Manualen. 15.1.1 Kennzeichnen der eingebetteten SQL-Anweisungen Damit ein Vorübersetzer die eingebetteten SQL-Anweisungen von den Anweisungen der Host-Sprache unterscheiden kann, ist es erforderlich, alle SQL-Anweisungen besonders zu kennzeichnen. Dabei muß sowohl der Anfang, als auch das Ende einer SQL-Anweisung gekennzeichnet werden. Die eingebetteten SQL-Anweisungen haben am Anfang als Präfix die Schlüsselwörter EXEC SQL . Dies gilt für alle INGRES-Schnittstellen. Das Ende einer eingebetteten SQL-Anweisung wird für verschiedene Host-Sprachen unterschiedlich gekennzeichnet. Bei COBOL wird das Schlüsselwort "END-EXEC." und bei C das Zeichen ";" als Endekennzeichen benutzt. 15.1.2 Variablen In einem Anwendungsprogramm werden am häufigsten Datenmanipulationsanweisungen (SELECT, INSERT, UPDATE und DELETE) verwendet. Jede dieser Anweisungen ermöglicht den Datenaustausch mit Hilfe von Variablen zwischen dem Anwendungsprogramm einerseits und der Datenbank andererseits. Die Variablen befinden sich innerhalb der eingebetteten SQL-Anweisung und sind nichts anderes als Variablen der Host-Sprache, die auch als solche definiert werden müssen. Damit sie innerhalb der SQL-Anweisung von den Datenbankobjekten unterschieden werden können, werden sie mit dem Präfix ":" gekennzeichnet. In der eingebetteten SQL-Anweisung existieren zwei unterschiedliche Arten von Variablen - Host-Variablen und - Indikator-Variablen. Host-Variablen dienen als Ziel-Variablen einer Abfrage bzw. können anstelle einer Konstante in den Datenmanipulationsanweisungen verwendet werden. Sie können auch als Argumente in verschiedenen anderen SQL-Anweisungen verwendet werden. Host-Variablen können nur die Datentypen haben, die in -198-
den Host-Sprachen erlaubt sind. Auf der anderen Seite werden ihnen die Datenwerte zugewiesen bzw. werden sie mit Datenwerten verglichen, die SQL-Datentypen haben. Damit die Zuweisung bzw. ein Vergleich durchgeführt werden kann, muß jede Variable zu ihrem entsprechenden Datenwert kompatibel sein. Indikator-Variablen sind spezielle Variablen, die nur im Zusammenhang mit Host-Variablen benutzt werden können. Jede Host-Variable kann also eine zugehörige Indikator-Variable haben, die zusätzliche Information über den gespeicherten Wert in der Host-Variablen liefert. Die Host-Variable und die dazu gehörende Indikator-Variable werden immer nacheinander in der folgenden Form geschrieben: :hostvar:indvar . Indikator-Variablen ermöglichen: - die Prüfung, ob einer Host-Variablen ein NULL-Wert zugewiesen wurde; - die Prüfung, ob bei der Zuweisung eines Datenwertes an eine Host-Variable der zugewiesene Wert gekürzt wird und damit Information verloren geht; - das Einfügen von NULL-Werten. Wenn in einer SELECT-Anweisung ein Datenwert, der einen NULL-Wert enthält, einer Host-Variablen zugewiesen wird, beinhaltet die Host-Variable nach der Zuweisung einen undefinierten Wert. Die Prüfung einer solchen Zuweisung kann mit Hilfe einer Indikator-Variablen durchgeführt werden, die dann den Wert -1 enthält, während die entsprechende Host-Variable unverändert bleibt. Wenn in einer SELECT-Anweisung ein Datenwert gekürzt einer Host-Variablen zugewiesen wird (z.B. wenn die Länge der Host-Variablen kleiner als die definierte Länge des Datenwertes ist), kann dies mit Hilfe der Indikator-Variablen festgestellt werden. In diesem Fall enthält die Indikator-Variable die tatsächliche Länge des zugewiesenen Datenwertes. Anstatt das Schlüsselwort NULL in einer INSERT- bzw. UPDATEAnweisung zu verwenden, kann das Einfügen eines NULL-Wertes mit Hilfe der Indikator-Variablen durchgeführt werden. In diesem Fall muß der Indikator-Variablen der Wert -1 zugewiesen werden (siehe Beispiel 16.7). Alle Indikator-Variablen werden in einer Host-Sprache intern als 2 Byte lange Ganzzahlen gespeichert. Bei COBOL kann jede Indikator-Variable mit S9(5) COMP und bei C mit short (bzw. short int) definiert werden. Jede Host- bzw. Indikator-Variable muß explizit definiert werden, wobei die Variablen mit Datenbankobjekten gleichnamig sein können. In jedem Programm wird ein spezieller Abschnitt, -199-
der sogenannte DECLARE-Abschnitt aufgebaut, in dem nur Hostund Indikator-Variablen definiert werden dürfen. In jedem Programm darf nur ein DECLARE-Abschnitt existieren. Der DECLARE-Abschnitt beginnt mit der SQL-Anweisung BEGIN DECLARE SECTION und endet mit einer weiteren SQL-Anweisung END DECLARE SECTION . Diese beiden eingebetteten SQL-Anweisungen müssen als solche innerhalb des Programms gekennzeichnet sein. In COBOL wird also jede von ihnen zwischen den Schlüsselwörtern EXEC SQL und END-EXEC. geschrieben. Die SQL-Anweisungen BEGIN DECLARE SECTION und END DECLARE SECTION sind keine ausführbaren SQL-Anweisungen, im Gegensatz zu allen bis jetzt beschriebenen. Sie gehören zu Vereinbarungs-Anweisungen, von welchen weitere im Verlauf dieses Kapitels vorgestellt werden. Im Unterschied zu ausführbaren SQL-Anweisungen generieren die Vereinbarungs-Anweisungen keinen Code. Die folgenden Beispiele zeigen die Form des DECLARE-Abschnitts in COBOL bzw. in C. Beispiel 16.1 (COBOL) EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 VORNAME PIC X(15). 77 NACHNAME PIC X(15). 77 IND-VORNAME PIC S9(5) COMP. 77 IND-NACHNAME PIC S9(5) COMP. EXEC SQL END DECLARE SECTION END-EXEC. Beispiel 16.2 (C) EXEC SQL BEGIN DECLARE SECTION; char vorname[16]; char nachname[16]; short ind_vorname; short ind_nachname; EXEC SQL END DECLARE SECTION; Eine weitere, in jedem Programm befindliche eingebettete SQL-Anweisung ist die CONNECT-Anweisung. Mit dieser Anweisung wird das Programm mit einer Datenbank verbunden. Die Syntax dieser Anweisung ist CONNECT db_name [IDENTIFIED BY ben] [OPTIONS=sch_1,...] Die Angaben der CONNECT-Anweisung sind den Angaben des in Kapitel 15 beschriebenen sql-Kommandos ähnlich. Die Angabe IDENTIFIED BY entspricht dem Schalter "- u" und sch_1,... den anderen vom sql-Kommando unterstützten Schaltern. db_name ist der Name der Datenbank, mit der das Programm verbunden ist. db_name kann auch mit Hilfe einer Host-Variablen definiert werden. Die CONNECT-Anweisung muß jeder anderen SQL-Anweisung, die in -200-
Bezug zur Datenbank db_name steht, vorangehen. Ein Programm kann zu einem Zeitpunkt nur mit einer einzigen Datenbank verbunden werden. Die zweite Anweisung DISCONNECT beendet die Verbindung zu einer Datenbank. Diese SQL-Anweisung ist unbedingt notwendig, falls in einem Programm die Verbindung zu einer zweiten Datenbank hergestellt werden soll. In diesem Fall muß zuerst die Verbindung zur ersten Datenbank mit Hilfe der DISCONNECT-Anweisung beendet werden, bevor mit der CONNECT-Anweisung die Verbindung zu der neuen Datenbank aufgebaut wird. Mit der Anweisung CALL ermöglicht INGRES den Aufruf eines Betriebssystemkommandos bzw. eine s Subsystems (wie QBF, Report-Writer usw.) aus einem Programm heraus. Diese Anweisung hat zwei verschiedene Formen CALL SYSTEM (COMMAND = kommando) bzw. CALL subsystem (DATABASE=db_name [,param_1=wert_1,...]) Mit der ersten Form der CALL-Anweisung kann ein Betriebssystemkommando und mit der zweiten ein INGRES-Subsystem aufgerufen werden. 15.1.3 Fehlerbehandlung INGRES unterstützt drei verschiedene Möglichkeiten, bei einer eingebetteten SQL-Anweisung zu prüfen, ob sie erfolgreich ausgeführt wurde. Diese Möglichkeiten sind: - die Prüfung der SQLCA-Struktur, - die Verwendung der WHENEVER-Anweisung oder - die Verwendung der INQUIRE_INGRES-Anweisung. Nach der Abarbeitung jeder ausführbaren SQL-Anweisung wird die Information, ob diese Anweisung erfolgreich ausgeführt wurde, an eine spezielle Struktur geliefert. Diese Struktur heißt SQLCA (SQL Communication Area) und muß in jedem Host-Programm explizit eingefügt werden, damit die in ihr gespeicherte Information benutzt werden kann. Das Einfügen der SQLCA-Struktur erfolgt mit folgender SQL-Anweisung: INCLUDE SQLCA Die INCLUDE-Anweisung ist eine Vereinbarungs-Anweisung. Während der Vorübersetzungs-Phase wird diese Anweisung durch die Definition mehrerer Host-Variablen ersetzt. Diese Host-Variablen enthalten Informationen über die gerade ausgeführte SQL-Anweisung. Jedes Programm führt standardmäßig die eingebettete SQL-Anweisung aus, ohne sich um eventuell aufgetretene Fehler zu kümmern. Mit Hilfe der Host-Variablen, die in der SQLCAStruktur definiert sind, ist es möglich, den Zustand des Programms nach jeder ausführbaren SQL-Anweisung zu überprüfen und eventuell notwendige Maßnahmen zu treffen. -201-
Bei allen Programmiersprachen enthält die SQLCA-Struktur u.a. folgende Variablen: - SQLCODE, - SQLERRM, - SQLERRD und - SQLWARN. SQLCODE ist ein 4 Byte langes, binäres Datenfeld, das den Rückgabewert der zuletzt ausgeführten SQL-Anweisung enthält. Ist der Rückgabewert 0, wurde die SQL-Anweisung erfolgreich ausgeführt. Bei einem Fehler im Programm, der in einer SQL-Anweisung aufgetreten ist, wird der Rückgabewert negativ. Jedem existierenden negativen Rückgabewert entspricht eine Fehlermeldung, deren Text in den entsprechenden Manualen des Systems nachgelesen werden kann. Der positive Rückgabewert zeigt eine Warnung an, wie z.B. im Falle einer SELECT-Anweisung, die keine einzige Reihe als Ergebnis liefert. SQLERRM ist eine variabel lange Zeichenkette, die den Text der Fehlermeldung enthält, der dem negativen Rückgabewert der Variablen SQLCODE entspricht. SQLERRD ist ein Vektor mit 6 Elementen, die als 4 Byte lange binäre Datenfelder definiert sind. Diese Datenfelder sollen den internen Zustand des Systems beschreiben. Z.Zt. wird nur SQLERRD(3) unterstützt, das die Anzahl der verarbeiteten Reihen bei einer Datenmanipulationsanweisung anzeigt. SQLWARN ist eine Struktur mit 8 Elementen, die als 1 Byte lange alphanumerische Datenfelder definiert sind. Jedes dieser Datenfelder zeigt Warnungen für verschiedene Bedingungen an, die nach der Ausführung einer SQL-Anweisung auftreten können. Der Wert jedes Datenfeldes kann entweder das Zeichen "W" oder das Leerzeichen sein, abhängig davon, ob eine entsprechende Bedingung aufgetreten ist oder nicht. In der Praxis sollte nach jeder ausführbaren SQL-Anweisung der Wert der SQLCODE-Variablen überprüft werden. Falls der Rückgabewert ungleich 0 ist, sollten weitere Maßnahmen ergriffen werden. Beispiel 16.5 zeigt, wie die Prüfung der SQLCODE-Variablen aussehen könnte. Als Alternative zur expliziten Prüfung der SQL-Anweisungen mit Hilfe der SQLCA-Struktur bietet sich die Benutzung der WHENEVER-Anweisung an. Sie ist eine Vereinbarungs-Anweisung, die jede eingebettete SQL-Anweisung nach ihrer Ausführung überprüft. Die allgemeine Form der WHENEVER-Anweisung ist WHENEVER bedingung massnahme wobei bedingung die Werte - SQLERROR, - SQLWARNING, - NOT FOUND sowie - SQLMESSAGE und massnahme die Werte - GO TO marke, -202-
- STOP, - CONTINUE und - CALL prozedur haben kann. SQLERROR kennzeichnet die Bedingung, die erfüllt ist, wenn ein Fehler nach der Ausführung einer SQL-Anweisung aufgetreten ist, wenn also der Rückgabewert der SQLCODE-Variablen negativ ist. SQLWARNING kennzeichnet die Bedingung, die erfüllt ist, wenn mindestens eine Warnung vom System gemeldet wird. In dem Fall wird das erste Datenfeld des Vektors SQLWARN überprüft; falls der Wert gleich "W" ist, wird die Bedingung als erfüllt betrachtet. NOT FOUND kennzeichnet die Bedingung, die erfüllt ist, wenn keine oder keine weitere Reihe nach der Ausführung einer Datenmanipulationsanweisung gefunden wird. Dieser Fall tritt auf, wenn der Rückgabewert der SQLCODE-Variablen 100 ist. Die Bedingung SQLMESSAGE tritt auf, falls der Wert der SQLCODE-Variablen 700 beträgt. Diese Warnung kennzeichnet die Ausführung der MESSAGE-Anweisung in einer Datenbankprozedur. GO TO marke setzt den Ablauf des Programms ab der Stelle fort, die mit "marke " gekennzeichnet ist, falls die Bedingung in der WHENEVER-Bedingung erfüllt ist. Diese Maßnahme entspricht dem Einfügen der Anweisung IF bedingung THEN GO TO marke nach jeder ausführbaren SQL-Anweisung in einem Programm. Mit STOP wird ein fehlerhaftes Programm nach der Ausgabe einer Fehlermeldung beendet. Eine zu diesem Zeitpunkt eröffnete Datenbank wird geschlossen (d.h. die DISCONNECT-Anweisung wird vom System implizit durchgeführt.) CONTINUE kennzeichnet die Fortsetzung des Programms, ohne Maßnahmen zu ergreifen. Damit entspricht diese Klausel dem Fall, in dem keine zusätzliche IF-Anweisung nach jeder ausführbaren SQL-Anweisung eingeführt wird. CALL prozedur verursacht den Aufruf einer Prozedur, die Teil der Hostsprache ist (z.B. die Ausführung eines Abschnitts entsprechend der PERFORM-Anweisung in COBOL). Die Wirkung einer WHENEVER SQLERROR-Anweisung beschränkt sich von ihrer Position im Modul, wo sie angegeben ist, bis zur nächsten WHENEVER SQLERROR-Anweisung bzw. bis zum Modulende, falls keine weitere WHENEVER SQLERROR-Anweisung existiert. Die Voreinstellung für WHENEVER SQLERROR-Anweisung ist die CONTINUE-Angabe. Die WHENEVER SQLERROR-Anweisung muß erst nach der Anweisung INCLUDE SQLCA im Programm erscheinen, um überhaupt benutzt werden zu können. Für WHENEVER SQLWARNING bzw. WHENEVER NOT FOUND gilt alles, was bereits über WHENEVER SQLERROR gesagt wurde. Die INQUIRE_INGRES-Anweisung ist eine spezifische INGRES-Anweisung, die dem Benut zer die Möglichkeit bietet, nach jeder ausgeführten SQL-Anweisung die Diagnoseinformationen den unterschiedlichen Host-Variablen -203-
eines Programms zuzuweisen. Die Syntax dieser Anweisung ist INQUIRE_INGRES (host_var1=angabe_1 [,host_var2=angabe_2,..]) angabe_1, angabe_2,... sind die unterschiedlichen Angaben der INQUIRE_INGRES-Anweisung. Die wichtigsten von ihnen liefern dieselbe Information wie die unterschiedlichen Variablen der SQLCA-Struktur (z.B. entspricht die Angabe ROWCOUNT SQLERRD(3), die Angabe ERRORTEXT SQLERRM usw.) Die Entscheidung, ob die Fehlerbehandlung explizit mit der Prüfung der SQLCA-Struktur oder implizit mit Hilfe der WHENEVER- bzw. der INQUIRE_INGRES-Anweisung durchgeführt werden soll, ist dem Programmierer überlassen. Jede dieser Methoden hat Vor- und Nachteile: Die Logik der WHENEVER-Anweisung ist komplizierter als die explizite Prüfung der SQLCA-Struktur und die Verwendung der GO TO-Anweisung kann zu unübersichtlichen Programmen führen. Auf der anderen Seite erfordert die explizite Prüfung der SQLCA-Struktur einen erhöhten Programmieraufwand. Wichtig ist nicht, welche Methode gewählt wird, sondern daß auf jeden Fall eine Fehlerbehandlung in den Programmen stattfindet. In den meisten Beispielen dieses und des nächsten Kapitels, die nur Teilprogramme oder einzelne eingebettete SQL-Anweisungen zeigen, werden wir auf die Fehlerbehandlung verzichten, damit sie so klein wie möglich gehalten werden können. In den Beispielen, die ganze Programme umfassen wird die explizite Prüfung des Wertes der SQLCODE-Variablen in den COBOL-Programmen durchgeführt während die C-Programme die WHENEVER-Anweisung beinhalten.
15.2 Eingebettete SQL-Anweisungen ohne Cursor Die eingebetteten SQL-Anweisungen, die ohne Cursor verwendet werden können, sind: - alle Datendefinitionsanweisungen; - alle Datensteuerungsanweisungen; - die SELECT-Anweisung, die nur eine Reihe als Ergebnis liefert; - die UPDATE-Anweisung ohne CURRENT-Klausel - die DELETE-Anweisung ohne CURRENT-Klausel; - die INSERT-Anweisung. Anhand von Beispielen werden die obigen SQL-Anweisungen dargestellt. Wie bereits in den vorhergehenden, wird auch in diesen Beispielen die Beispieldatenbank verwendet. Beispiel 16.3 Versorgen Sie die Host-Variablen MNAME und MVORNAME mit den Werten des Namens und Vornamens jedes Mitarbeiters, dessen Personalnummer durch die Host-Variable PERSNUM gegeben ist. (COBOL) EXEC SQL SELECT m_name, m_vorname INTO :MNAME, :MVORNAME FROM mitarbeiter -204-
WHERE m_nr = :PERSNUM END-EXEC. (C)
EXEC SQL SELECT m_name, m_vorname INTO :MNAME, :MVORNAME FROM mitarbeiter WHERE m_nr = :PERSNUM; Im Unterschied zur interaktiven SELECT-Anweisung beinhaltet die eingebettete SELECT-Anweisung die INTO-Klausel mit einer Reihe von Variablen. Die INTO-Klausel hat folgende Form INTO :hostvar1[:indvar1] [, :hostvar2[:indvar2]] ... Liefert die Bedingung in der WHERE-Klausel in Beispiel 16.3 nur eine Reihe als Ergebnis, werden die entsprechenden Datenwerte der Spalten m_name und m_vorname der Host-Variablen MNAME und MVORNAME zugewiesen. Wenn die Bedingung für keine der Reihen der Tabelle mitarbeiter erfüllt ist, wird der Wert der SQLCODE-Variable auf 100 gesetzt. Falls die Bedingung mehr als eine Reihe als Ergebnis liefert, wird SQLCODE auf einen negativen Wert gesetzt. Jede der Host-Variablen MNAME, MVORNAME und PERSNUM muß einen kompatiblen Datentyp zu ihrem entsprechenden Datenwert m_name, m_vorname bzw. m_nr haben. Die notwendigen Datenkonvertierungen führt das System durch. Die Spalten m_name und m_vorname sind als CHAR(20) definiert, während m_nr als INTEGER definiert ist. Dementsprechend könnte der DECLARE-Abschnitt für die Host-Variablen wie folgt aussehen: (COBOL) EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 MNAME PIC X(20). 77 MVORNAME PIC X(20). 77 PERSNUM PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. (C) EXEC SQL BEGIN DECLARE SECTION; char MNAME[21]; char MVORNAME[21]; int PERSNUM; EXEC SQL END DECLARE SECTION; Beispiel 16.4 Versorgen Sie die Host-Variablen ABTNR und STADT mit der Abteilungsnummer und dem Standort für jene Abteilung, deren Name durch die Host-Variable ABTNAME gegeben ist. Den Host-Variablen ABTNR und STADT sollen die Indikator-Variablen INDABTNR und INDSTADT zugewiesen werden. (COBOL) EXEC SQL SELECT abt_nr, stadt INTO :ABTNR:INDABTNR, :STADT:INDSTADT FROM abteilung WHERE abt_name = :ABTNAME -205-
END-EXEC. (C)
EXEC SQL SELECT abt_nr, stadt INTO :ABTNR:INDABTNR, :STADT:INDSTADT FROM abteilung WHERE abt_name = :ABTNAME; Die Indikator-Variablen sind schon im vorherigen Abschnitt beschrieben worden. Mit Hilfe der Indikator-Variablen INDSTADT kann z.B. überprüft werden, ob der Host-Variablen STADT ein NULL-Wert zugewiesen wurde bzw. ob der zugewiesene Wert gekürzt wurde. (Mit der Indikator-Variablen INDABTNR kann nur geprüft werden, ob der zugewiesene Wert der Host-Variablen ABTNR gekürzt wurde, weil die Spalte abt_nr keine NULL-Werte zuläßt.) Wie aus Beispiel 16.4 ersichtlich, kann eine Host-Variable denselben Namen wie ihre entsprechende Spalte haben. Der Vorübersetzer erkennt grundsätzlich die Host- bzw. IndikatorVariablen an dem Präfix ":". Die Indikator-Variablen dürfen nicht in der WHERE- bzw. HAVING-Klausel verwendet werden. Der DECLARE-Abschnitt für Beispiel 16.4 könnte so aussehen: (COBOL) EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 ABTNR PIC X(4). 77 STADT PIC X(15). 77 ABTNAM PIC X(20). 77 INDABTNR PIC S9(5) COMP. 77 INDSTADT PIC S9(5) COMP. EXEC SQL END DECLARE SECTION END-EXEC. (C) EXEC SQL BEGIN DECLARE SECTION; char ABTNR[5]; char STADT[16]; char ABTNAM[21]; short INDABTNR, INDSTADT; EXEC SQL END DECLARE SECTION; Im nächsten Beispiel wird die Benutzung der Host-Variablen in der HAVING-Klausel gezeigt. Beispiel 16.5 Die Host-Variable PRNR soll mit der Nummer des Projektes versorgt werden, an dem genau die dem Wert der Host-Variablen ANZAHL entsprechende Anzahl von Mitarbeitern beteiligt ist. Nach der Ausführung der SELECT-Anweisung soll die Prüfung der SQLCODE-Variablen durchgeführt werden. (COBOL) EXEC SQL SELECT pr_nr INTO :PRNR FROM arbeiten -206-
GROUP BY pr_nr HAVING COUNT(*) = :ANZAHL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in SELECT" DISPLAY SQLCODE OF SQLCA STOP RUN ELSE DISPLAY "Select-Anweisung ausgefuehrt". (C)
EXEC SQL SELECT pr_nr INTO :PRNR FROM arbeiten GROUP BY pr_nr HAVING COUNT(*) = :ANZAHL if (SQLCA.SQLCODE < 0) { printf ("Fehler in SELECT\n"); printf ("%d\n", SQLCA.SQLCODE); exit (SQLCA.SQLCODE); } Die Fehlerbehandlung in Beispiel 16.5 überprüft zuerst den Wert der SQLCODE-Variablen nach der Ausführung der SELECT-Anweisung. Wenn dieser Wert kleiner 0 ist, wird zuerst der Rückgabewert und danach der entsprechende Fehlermeldungstext ausgegeben. Anschließend wird das Programm beendet. Wenn der Wert der SQLCODE-Variablen größer gleich 0 ist, wird lediglich die Bestätigung für die ausgeführte SELECT-Anweisung ausgegeben. Das nächste Beispiel zeigt die Verwendung der Host-Variablen in der VALUES-Klausel der INSERT-Anweisung. Beispiel 16.6 Fügen Sie eine Reihe mit den Datenwerten einer neuen Abteilung ein, deren Nummer, Name und Standort jeweils durch die Host-Variablen ABTNR, ABTNAME und STADT gegeben sind. (COBOL) MOVE "a4" TO ABTNR. MOVE "Test" TO ABTNAME. MOVE "Freiburg" TO STADT. EXEC SQL INSERT INTO abteilung(abt_nr, abt_name, stadt) VALUES(:ABTNR, :ABTNAME, :STADT) END-EXEC. (C) strcpy(ABTNR, "a4"); strcpy(ABTNAME, "Test"); strcpy(STADT, "Freiburg"); EXEC SQL INSERT INTO abteilung(abt_nr,abt_name,stadt) VALUES (:ABTNR, :ABTNAME, :STADT); Wie im folgenden Beispiel ersichtlich, können Indikator-Variablen verwendet werden, um NULL-Werte -207-
einzufügen. Beispiel 16.7 Fügen Sie eine Reihe mit den Datenwerten eines neuen Projekts ein, dessen Nummer, Name und Mittel durch die Host-Variablen PRNR, PRNAME und MITTEL gegeben sind. Der Wert der zugewiesenen Geldmittel ist noch nicht bekannt. (COBOL) MOVE "p1" TO PRNR. MOVE "Mars" TO PRNAME. MOVE -1 TO INDMITTEL. EXEC SQL INSERT INTO PROJEKT(pr_nr, pr_name, mittel) VALUES(:PRNR, :PRNAME, :MITTEL:INDMITTEL) END-EXEC. (C) strcpy(PRNR,"p1"); strcpy(PRNAME, "Mars"); INDMITTEL = -1; EXEC SQL INSERT INTO PROJEKT(pr_nr, pr_name, mittel) VALUES(:PRNR, :PRNAME, :MITTEL:INDMITTEL); Durch die Zuweisung des Wertes -1 an die Indikator-Variable INDMITTEL vor der INSERT-Anweisung, wird der Datenwert der Spalte mittel auf NULL gesetzt. Das nächste Beispiel zeigt die Benutzung der Host-Variablen in der SET-Klausel der UPDATE-Anweisung. Beispiel 16.8 Ändern Sie die Aufgabe des Mitarbeiters in Projekt p2 mit Hilfe der Host-Variablen AUFGABE. Die Personalnummer des Mitarbeiters ist durch die Host-Variable MNR gegeben. (COBOL) MOVE "Gruppenleiter" TO AUFGABE. MOVE 18316 TO MNR. EXEC SQL UPDATE arbeiten SET aufgabe = :AUFGABE:INDAUFGABE WHERE m_nr = :MNR AND pr_nr = 'p2' END-EXEC. (C) strcpy (AUFGABE, "Gruppenleiter"); MNR = 18316; EXEC SQL UPDATE arbeiten SET aufgabe = :AUFGABE:INDAUFGABE WHERE m_nr = :MNR AND pr_nr = 'p2'; In einer DELETE-Anweisung können die Host-Variablen in der WHERE-Klausel verwendet werden. Beispiel 16.9 Löschen Sie die Datenwerte des Projektleiters in dem Projekt, -208-
dessen Nummer durch die Variablen PRNR gegeben ist. (COBOL) MOVE "p1" TO PRNR. EXEC SQL DELETE FROM arbeiten WHERE AUFGABE = 'Projektleiter' AND pr_nr = :PRNR END-EXEC. (C) strcpy (PRNR, "p1"); EXEC SQL DELETE FROM arbeiten WHERE AUFGABE = 'Projektleiter' AND pr_nr = :PRNR; Obwohl die Datenmanipulationsanweisungen von allen SQL-Anweisungen am häufigsten in eine Host-Sprache eingebettet werden, kann dies ebenso mit den Datendefinitions- wie auch mit den Datensteuerungsanweisungen durchgeführt werden. Das folgende Beispiel zeigt die Einbettung einer CREATE INDEX-Anweisung. Beispiel 16.10 Erstellen Sie einen zusammengesetzten Index für die Spalten m_nr und einst_dat der Tabelle arbeiten. (COBOL) EXEC SQL CREATE INDEX i_mnr_dat ON arbeiten(m_nr,einst_dat) END-EXEC. (C) EXEC SQL CREATE INDEX i_mnr_dat ON arbeiten(m_nr,einst_dat); Das folgende Programm zeigt die Verwendung der SELECT-Anweisung in einem COBOL-Programm. Beispiel 16.11 Finden Sie Namen und Vornamen des Mitarbeiters, dessen Personalnummer am Bildschirm eingegeben wird. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1611. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 MNAME PIC X(20) VALUE SPACES. 77 MVORNAME PIC X(20) VALUE SPACES. 77 MNR PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-SELECT. -209-
PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-SELECT. DISPLAY "Geben Sie die Mitarbeiternummer an". ACCEPT MNR. EXEC SQL SELECT m_name, m_vorname INTO :MNAME, :MVORNAME FROM mitarbeiter WHERE m_nr = :MNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in SELECT" ELSE DISPLAY "Select ausgefuehrt". DISPLAY "Der Mitarbeiter mit der Personalnummer: ", MNR. DISPLAY "heisst: ", MNAME, MVORNAME. DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Im A100-SELECT-Abschnitt wird der Vergleich in der WHERE-Klausel der SELECT-Anweisung durchgeführt und die ausgewählten Datenwerte der Spalten m_name und m_vorname werden den Host-Variablen MNAME und MVORNAME zugewiesen. Das Ergebnis der SELECT-Anweisung ist immer eine einzige Reihe, weil jeder Mitarbeiter eine eindeutige Personalnummer hat. Der Abschnitt DBOPEN erstellt die Verbindung zu der entsprechenden Datenbank. Das wird mit der SQL-Anweisung CONNECT durchgeführt. Der letzte Abschnit DBCLOSE beendet die Verbindung zwischen der Datenbank und dem Programm. Die Fehlerbehandlung in Beispiel 16.11, wie in allen nachfolgenden Beispielen, ist mit der expliziten Prüfung der SQLCODE-Variablen durchgeführt worden. Das folgende C-Programm ist mit dem vorherigen COBOL-Beispiel identisch. Beispiel 16.12 /* Beispiel 16.12 */ EXEC SQL INCLUDE sqlca; #include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; char m_name[21 ]; char m_vorname[21]; -210-
int m_nr; EXEC SQL END DECLARE SECTION; main() { EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL CONNECT beispiel; printf("Geben Sie die Nummer des Mitarbeiters an:\n"); scanf ("%d", &m_nr); EXEC SQL SELECT m_name, m_vorname INTO :m_name, :m_vorname FROM mitarbeiter WHERE m_nr = :m_nr; printf("Mitarbeiter mit der Personalnummer %d\n",m_nr); printf("heisst: %s %s\n", m_name ,m_vorname); exit(0); fehler: printf ("Fehler: %d\n",sqlca.sqlcode); EXEC SQL DISCONNECT ; } Im folgenden Beispiel wird die Änderung der Datenwerte mittels einer UPDATE-Anweisung gezeigt. Beispiel 16.13 Ändern Sie die Aufgabe jenes Mitarbeiters, dessen Personalund Projektnummer am Bildschirm eingegeben werden. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1613. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 PRNR PIC X(4) VALUE SPACES. 77 AUFGABE PIC X(15) VALUE SPACES. 77 MNR PIC S9(9) COMP. 77 IND-AUFGABE PIC S9(5) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-SELECT. PERFORM A200-UPDATE. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL -211-
END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-SELECT. DISPLAY "Geben Sie die Mitarbeiternummer an". ACCEPT MNR. DISPLAY "Geben Sie die Projektnummer an". ACCEPT PRNR. EXEC SQL SELECT aufgabe INTO :AUFGABE FROM arbeiten WHERE m_nr = :MNR AND pr_nr = :PRNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in SELECT" ELSE DISPLAY "Select ausgefuehrt". DISPLAY "Der Mitarbeiter mit der Personalnummer: ", MNR. DISPLAY "arbeitet im Projekt ", PRNR, "als ", AUFGABE. DISPLAY "Geben Sie die neue Aufgabe des Mitarbeiters an". ACCEPT AUFGABE. A200-UPDATE. EXEC SQL UPDATE arbeiten SET aufgabe = :AUFGABE:IND-AUFGABE WHERE m_nr = :MNR AND pr_nr = :PRNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in UPDATE" ELSE DISPLAY "UPDATE ausgefuehrt". DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Das Programm beinhaltet unter anderem zwei Abschnitte: A100-SELECT und A200-UPDATE. Mit dem ersten Abschnitt wird die Aufgabe des Mitarbeiters, dessen Personal- und Projektnummer eingegeben sind, ausgewählt und am Bildschirm angezeigt. Damit wird im Programm eine zusätzliche Überprüfung der Datenwerte eingebaut. Im Abschnitt A200-UPDATE wird schließlich die Änderung der Aufgabe des Mitarbeiters durchgeführt. Das folgende C-Programm entspricht dem vorherigen COBOL-Beispiel. Beispiel 16.14 /* Beispiel 16.14 */ -212-
#include <stdio.h> EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; char pr_nr[5]; char aufgabe[16]; int m_nr; short ind_aufgabe; EXEC SQL END DECLARE SECTION; main() { EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL CONNECT beispiel; printf("Geben Sie die Nummer des Mitarbeiters an:\n"); scanf ("%d", &m_nr); printf("Geben Sie die Projektnummer an\n"); scanf("%s", pr_nr); EXEC SQL SELECT aufgabe INTO :aufgabe FROM arbeiten WHERE m_nr = :m_nr AND pr_nr = :pr_nr; printf("Der Mitarbeiter mit der Personalnummer: %d\n",m_nr); printf("ist %s im Projekt %s\n\n",aufgabe ,pr_nr); printf("Geben Sie die neue Aufgabe des Mitarbeiters an:\n"); scanf("%s", aufgabe); EXEC SQL UPDATE arbeiten SET aufgabe = :aufgabe:ind_aufgabe WHERE m_nr = :m_nr AND pr_nr = :pr_nr; exit(0); fehler: printf ("Fehler: %d\n", sqlca.sqlcode); EXEC SQL DISCONNECT; } INGRES unterstützt die Anweisung DECLARE tab_name TABLE , die Spalten einer Tabelle, gemeinsam mit ihren Datentypen auflistet. Diese Anweisung ist keine ausführbare SQL-Anweisung, sondern dient nur als Kommentar innerhalb des Variablendeklarationsteils. Mit der DECLARE TABLE-Anweisung ist es lediglich möglich, die Dokumentation eines Programms zu verbessern. Sie wird auch vom Dienstprogramm DCLGEN verwendet. Das Dienstprogramm DCLGEN ("Declaration Generator") generiert automatisch eine Struktur in der eingebetteten Sprache, -213-
die analog dem Schema einer Tabelle ist. D.h. jede Spalte einer Tabelle wird in der generierten Struktur durch eine entsprechende Variable dargestellt. Die so generierte Struktur erleichtert das Einlesen der Datenwerte einer Tabellenreihe in das Programm, bzw. das Schreiben der Datenwerte aus dem Programm in eine Tabellenreihe. Mit dem Betriebssystemkommando dclgen sprache db_name tab_name dat_name struk_name kann dieses Dienstprogramm aufgerufen werden. sprache definiert die eingebettete Programmiersprache, während db_name , tab_name und struk_nmame jeweils Datenbank-, Tabellen- und Strukturnamen darstellen. dat_name ist der Name der Ausgabedatei, die die von DCLGEN generierte Strukturdeklaration enthält. Diese Datei enthält auch die DECLARE TABLE-Anweisung, die als Gedächtnisstütze für das Tabellenschema dient. Mit Hilfe der Vereinbarungsanweisung INCLUDE ist es möglich, die Datei dat_name dem Deklarationsteil eines Programms hinzufügen.
15.3 Verwendung des Cursors Im vorherigen Abschnitt haben wir gezeigt, wie man Abfragen durchführen kann, wenn die eingebettete SELECT-Anweisung nur eine Reihe als Ergebnis liefert. Wenn die Anzahl der Reihen, die eine SELECT-Anweisung liefert, im voraus nicht bekannt bzw. größer als eins ist, muß zusätzlicher Aufwand betrieben werden, damit alle Reihen der Abfrage ausgewählt werden können. Der Grund dafür liegt in den Unterschieden zwischen SQL einerseits und den Host-Sprachen andererseits. SQL ist eine mengenorientierte Sprache. Mit den Datenmanipulationsanweisungen wie SELECT, DELETE usw. wird also vom Benutzer nicht festgelegt, wie der Weg zum gewünschten Ergebnis erreicht werden soll, sondern lediglich das, was gemacht werden soll. Dadurch kann der Benutzer in der Regel nicht wissen, wieviele Reihen eine Abfrage liefern wird. Die Host-Sprachen können nicht gleichzeitig mehrere Datensätze bearbeiten, sondern holen sich einen Datensatz nach dem anderen. Damit dieser Unterschied zwischen Host-Sprachen und SQL überbrückt werden kann, wird ein Datenpuffer benutzt, in welchem dann alle Reihen, die als Ergebnis einer Abfrage ausgewählt wurden, gespeichert werden. Dieser Datenpuffer wird mit einer Art Zeiger bearbeitet, der bei SQL Cursor heißt. Der Cursor hat gewisse Ähnlichkeiten mit einer Datei. Jede Datei muß zuerst definiert werden. Danach wird sie eröffnet und anschließend wird sequentiell ein Datensatz nach dem anderen bearbeitet. Am Ende wird die Datei geschlossen. Für einen Cursor gilt dasselbe. Für jeden der genannten Schritte existiert in der eingebetteten SQL-Sprache je eine zusätzliche Anweisung, die im interaktiven Modus nicht -214-
vorhanden ist. Diese Anweisungen sind: - DECLARE CURSOR, - OPEN, - FETCH und - CLOSE. Mit der DECLARE CURSOR-Anweisung wird ein Cursor definiert. Diese Anweisung gehört zu den SQL-Vereinbarungs-Anweisungen und sie hat folgende allgemeine Form: DECLARE cursor_name CURSOR FOR select_anw [FOR UPDATE [DEFERRED|DIRECT] OF spalte_1,...] cursor_name kennzeichnet den Namen des definierten Cursors, der eindeutig innerhalb eines Host-Programms sein muß. Der Name kann eine Konstante oder eine alphanumerische Host-Variable sein und ist nur innerhalb des Host-Programms bekannt, in dem er definiert wurde. select_anw bezeichnet eine SELECT-Anweisung (oder eine Vereinigung mehrerer SELECT-Anweisungen mit Hilfe des Operators UNION), die fest mit dem Cursor cursor_name verbunden ist. Jeder Cursor, der mit der "FOR UPDATE"-Klausel definiert ist, kann später in einer UPDATE- bzw. DELETE-Anweisung mit der CURRENT OF-Klausel benutzt werden (siehe die Erklärung für diese beiden Anweisungen später in diesem Kapitel). spalte_1,... kennzeichnet alle Spalten, die mit der UPDATE-Anweisung modifiziert werden können. Der DEFERREDbzw. DIRECT-Modus bei der FOR UPDATE-Klausel definiert den Zeitpunkt der Änderungen der Reihen, die mit dem Cursor definiert sind. Beim DEFERRED-Modus werden alle Änderungen an den Reihen erst beim Schließen des Cursors durchgeführt. Der DIRECT-Modus führt die Änderungen der Reihen zu dem Zeitpunkt der Ausführung einer UPDATE- bzw. einer DELETE-Anweisung aus. Damit werden im DIRECT-Modus die Änderungen an den Reihen für das Programm, das der Cursor eröffnet hat, gleich sichtbar. Falls keiner der beiden Modi explizit angegeben ist, wird DEFERRED standardmäßig angenommen. Die DECLARE CURSOR-Anweisung muß vor jeder anderen SQL-Anweisung im Programm erscheinen, die den in ihr definierten Cursor verwenden will. Die Gültigkeit der Cursor-Definition erstreckt sich auf das ganze Host-Programm, in dem sie definiert wurde. Im Unterschied zu der DECLARE CURSOR-Anweisung sind die drei anderen SQL-Anweisungen, die Cursor betreffen, ausführbare SQL-Anweisungen. Mit der Anweisung OPEN cursor_name [FOR READONLY] wird die SELECT-Anweisung, die sich innerhalb der DECLARE CURSOR-Anweisung befindet, ausgeführt. Das Ergebnis einer solchen SELECT-Anweisung wird oft die Treffermenge ("active set") genannt. Die optionale Klausel FOR READONLY teilt dem System mit, daß die Treffermenge nur gelesen, und nicht geändert wird. Nach der Ausführung der OPEN-Anweisung steht der Cursor -215-
vor der ersten Reihe der Treffermenge. Die Änderung der Host-Variablen in der WHERE-Klausel der SELECT-Anweisung nach der Ausführung der OPEN-Anweisung ändert nicht die einmal festgelegte Treffermenge. Diese Änderung hat erst dann eine Wirkung, wenn die OPEN-Anweisung noch einmal ausgeführt wird. Ein erneutes Öffnen des Cursors mit Hilfe der OPEN-Anweisung bedeutet also das implizite Schließen des Cursors und das Erstellen eines neuen Kriteriums für die in der SELECTAnweisung verwendeten Host-Variablen. Mit der FETCH-Anweisung wird der Cursor auf der nächsten Reihe der Treffermenge positioniert. Diese Anweisung hat folgende Form FETCH cursor_name INTO :host_var1[,:host_var2] ... Der Cursor muß zuvor definiert und eröffnet werden. Nach der ersten FETCH-Anweisung wird die Treffermenge festgelegt und der Cursor auf die erste Reihe positioniert. Die Reihen der Treffermenge können nur sequentiell abgearbeitet werden. Der Cursor kann also in diesem Fall nur vorwärts von einer Reihe zur nächsten bewegt werden. Die FETCH-Anweisung, die ausgeführt wird, nachdem der Cursor schon auf die letzte Reihe der Treffermenge positioniert war, weist der SQLCODE-Variablen den Wert 100 zu. Mit der Anweisung CLOSE cursor_name wird ein eröffneter Cursor geschlossen. Mit dieser Anweisung wird der Cursor nur inaktiv; eine anschließende OPEN-Anweisung eröffnet ihn wieder. Durch eine Änderung der Host-Variablen, die sich in der WHERE-Klausel der SELECT-Anweisung befinden, kann das Kriterium für die Treffermenge neu definiert werden. Eine FETCH-Anweisung mit einem geschlossenen Cursor wird vom System mit einer Fehlermeldung abgewiesen. Die Anweisungen COMMIT und ROLLBACK schließen alle eröffneten Cursor. Das folgende Beispiel zeigt die Verwendung des Cursors in einem COBOL-Programm. Beispiel 16.15 Finden Sie Personalnummer, Namen, Vornamen und Abteilungsnummer der Mitarbeiter, deren Personalnummer kleiner als der am Bildschirm eingegebene Wert ist. Die ausgewählten Reihen sollen nach Personalnummern sortiert werden. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1615. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 MNAME PIC X(20) VALUE SPACES. 77 MVORNAME PIC X(20) VALUE SPACES. 77 ABTNR PIC X(4) VALUE SPACES. -216-
77 MNR PIC S9(9) COMP. 77 MINZAHL PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-DECLARE. PERFORM A200-OPEN. PERFORM A300-FETCH UNTIL SQLCODE OF SQLCA = 100. PERFORM A400-CLOSE. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-DECLARE. DISPLAY "Geben Sie die obere Grenze an". ACCEPT MINZAHL. EXEC SQL DECLARE crs_mit CURSOR FOR SELECT m_nr, m_name, m_vorname, abt_nr FROM mitarbeiter WHERE m_nr < :MINZAHL ORDER BY m_nr END-EXEC. A200-OPEN. EXEC SQL OPEN crs_mit END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in OPEN". A300-FETCH. EXEC SQL FETCH crs_mit INTO :MNR, :MNAME, :MVORNAME, :ABTNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in FETCH". IF SQLCODE OF SQLCA NOT = 100 DISPLAY MNR," ",MNAME," ",MVORNAME," ",ABTNR. A400-CLOSE. EXEC SQL CLOSE crs_mit END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in CLOSE". -217-
DBCLOSE. EXEC SQL DISCONNECT END-EXEC. In Beispiel 16.15 existieren vier Programmabschnitte, die genau den vier beschriebenen SQL-Anweisungen DECLARE CURSOR, OPEN, FETCH und CLOSE entsprechen. Im Abschnitt A100-DECLARE wird der Cursor crs_mit definiert. Der A200-OPEN-Abschnitt führt die OPEN-Anweisung aus. Die Festlegung der Treffermenge und die Ausführung der FETCH-Anweisung für jede Reihe wird im Programmabschnitt A300-FETCH durchgeführt. Mit dem A400-CLOSE-Abschnitt wird der Cursor crs_mit geschlossen. Das folgende C-Programm ist mit dem vorherigen COBOL-Programm identisch. Beispiel 16.16 /* Beispiel 16.16 */ #include <stdio.h> EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; char m_name[21]; char m_vorname[21]; int m_nr, min_zahl; EXEC SQL END DECLARE SECTION; main() { EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL WHENEVER NOT FOUND GOTO :schlies; EXEC SQL CONNECT beispiel; printf("Geben Sie die obere Grenze an\n"); scanf ("%d", &min_zahl); EXEC SQL DECLARE crs_mit CURSOR FOR SELECT m_nr, m_name, m_vorname FROM mitarbeiter WHERE m_nr < :min_zahl ORDER by m_nr; EXEC SQL OPEN crs_mit; for (;;) { EXEC SQL FETCH crs_mit INTO :m_nr, :m_name, :m_vorname; printf("%d %s%s\n",m_nr,m_name,m_vorname); } -218-
schlies: EXEC SQL CLOSE crs_mit; EXEC SQL DISCONNECT; exit(0); fehler: printf ("Fehler: %d\n", sqlca.sqlcode); } In der DECLARE CURSOR-Anweisung - wie aus ihrer Definition ersichtlich - existiert eine zusätzliche Klausel FOR UPDATE OF spalte_1 [,spalte_2,...] Mit dieser Klausel werden die ausgewählten Reihen mit Hilfe eines Cursors geändert bzw. gelöscht. Die Voraussetzung für das Ändern der Datenwerte bzw. das Löschen der Reihen ist das Vorhandensein der CURRENT OF-Klausel innerhalb der UPDATE- bzw. DELETE-Anweisung. Die UPDATE-Anweisung hat dann folgende Form UPDATE tab_name SET (sp_1=ausdr_1,...) WHERE CURRENT OF cursor_name cursor_name ist der Name des Cursors, der in der DECLARE CURSOR-Anweisung mit der FOR UPDATE-Klausel definiert ist. (Die DELETE-Anweisung hat eine analoge Form.) Der Cursor cursor_name muß eröffnet werden, bevor er innerhalb der UPDATE- bzw. DELETE-Anweisung verwendet wird. Die UPDATE-Anweisung ändert und die DELETE-Anweisung löscht die Reihe, auf welcher der Cursor positioniert ist. Nach der Ausführung der UPDATE- bzw. DELETE-Anweisung wird die Position des Cursors nicht geändert. Erst die nächste FETCH-Anweisung positioniert den Cursor auf die nächste Reihe der Treffermenge. Das folgende Beispiel zeigt die Verwendung des Cursors beim Löschen der Reihen. Beispiel 16.17 Finden Sie alle Reihen der Tabelle arbeiten, deren Spalte m_nr dem eingegebenen Wert entspricht, und löschen Sie sie anschließend. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1617. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 PRNR PIC X(4) VALUE SPACES. 77 MNR PIC S9(9) COMP. 77 AUFGABE PIC X(15) VALUE SPACES. EXEC SQL END DECLARE SECTION END-EXEC. 77 JA-NEIN PIC X. PROCEDURE DIVISION. PERFORM DBOPEN. -219-
PERFORM A100-DECLARE. PERFORM A200-OPEN. PERFORM A300-FETCH UNTIL SQLCODE OF SQLCA = 100. PERFORM A400-CLOSE. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. INF99. EXIT. A100-DECLARE. DISPLAY "Geben Sie die Personalnummer an". ACCEPT MNR. EXEC SQL DECLARE crs_arb CURSOR FOR SELECT m_nr, pr_nr, aufgabe FROM arbeiten WHERE m_nr = :MNR FOR UPDATE OF pr_nr END-EXEC. A200-OPEN. EXEC SQL OPEN crs_arb END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in OPEN". A300-FETCH. EXEC SQL FETCH crs_arb INTO :MNR, :PRNR, :AUFGABE END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in FETCH". IF SQLCODE OF SQLCA NOT = 100 THEN PERFORM A310-ABFRAGE. A400-CLOSE. EXEC SQL CLOSE crs_arb END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in CLOSE". A310-ABFRAGE. DISPLAY MNR, " ", PRNR, " ", AUFGABE. DISPLAY "Moechten Sie diese Reihe loeschen j/n?". ACCEPT JA-NEIN. IF JA-NEIN = "j" THEN PERFORM A311-DELETE. -220-
A311-DELETE. EXEC SQL DELETE FROM arbeiten WHERE CURRENT OF crs_arb END-EXEC. DBCLOSE. EXEC SQL DISCONNECT END-EXEC. In diesem Beispiel - ähnlich wie in Beispiel 16.13 - wird jede Reihe vor dem Löschen auf dem Bildschirm angezeigt und erst nach der expliziten Bestätigung gelöscht. Mit dieser Maßnahme wird eine zusätzliche Kontrolle vor dem Löschen der Reihen eingeführt. Das C-Programm in Beispiel 16.18 verhält sich analog zum vorherigen Beispiel. Beispiel 16.18 /* Beispiel 16.18 */ #include <stdio.h> EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; int m_nr; char pr_nr[5]; char aufgabe[16]; EXEC SQL END DECLARE SECTION; main() { char ja_n[1]; EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL WHENEVER NOT FOUND GO TO schlies; EXEC SQL CONNECT beispiel; printf("Geben Sie die Nummer des Mitarbeiters an:\n"); scanf ("%d", &m_nr); EXEC SQL DECLARE crs_arb CURSOR FOR SELECT m_nr , pr_nr, aufgabe FROM arbeiten WHERE m_nr = :m_nr FOR UPDATE OF m_nr; EXEC SQL OPEN crs_arb; for (;;) { EXEC SQL FETCH crs_arb INTO :m_nr, :pr_nr, :aufgabe; -221-
printf("%d %s%s\n",m_nr, pr_nr, aufgabe); printf("Moechten Sie diese Reihe loeschen: j/n?\n"); scanf ("%s",&ja_n); if (strcmp(ja_n,"n")) { EXEC SQL DELETE FROM arbeiten WHERE CURRENT OF crs_arb; }
} schlies: EXEC SQL CLOSE crs_arb; EXEC SQL DISCONNECT; exit(0); fehler: printf ("Fehler: %d\n", sqlca.sqlcode); }
15.4 Übersetzung eines ESQL/C- bzw. ESQL/COBOL-Programms Sowohl die INGRES-Schnittstelle zu C als auch die zu COBOL verwendet einen Vorübersetzer, mit dem dann die eingebetteten SQL-Programme in C bzw. COBOL übersetzt werden. Der Aufruf des jeweiligen Vorübersetzers geschieht mittels des Betriebssystemkommandos esqlc für C bzw. esqlcbl für COBOL. Weil diese beiden Kommandos die gleiche Syntax haben, wird nur das Kommando esqlc beschrieben. Das Kommando esqlc sch_liste datei_name ermöglicht die Übersetzung eines eingebetteten SQL-Programms in C. datei_name ist der Name der Datei, die das eingebettete SQL-Programm enthält. Zu der sch_liste können ein oder mehrere Schalter gehören, von denen wir die wichtigsten erläutern werden. Der Schalter "- l" schreibt alle Fehlermeldungen der Vorübersetzerphase sowohl in eine Ausgabeliste als auch auf den Bildschirm. Der Schalter "-lo" entspricht dem Schalter "- l", nur wird zusätzlich der generierte C-Code auch in die Ausgabeliste geschrieben. Mit dem Schalter "- f dat_name" wird die Ausgabe der Vorübersetzerphase in die Datei dat_name geschrieben. Falls dat_name nicht angegeben wird, erfolgt die Ausgabe auf die Standard-Ausgabe. Der Schalter "-s" veranlaßt die Eingabe des eingebetteten SQL-Programms von der Standard-Eingabe und die Ausgabe des erstellten Codes auf die Standard-Ausgabe.
15.5 Eingebettete Anweisungen und Programmierstil In diesem Abschnitt werden wir uns einem Thema widmen, das die Programmierung der Datenbankanwendung wesentlich -222-
beeinflussen kann. Es handelt sich um allgemeine Hinweise, die sowohl den Ablauf der Anwendungen beschleunigen, als auch helfen können, mögliche Fehlerquellen zu umgehen. 15.5.1 Optimierung der Datenbankzugriffe Den ersten und gleichzeitig wichtigsten Punkt stellt die optimale Programmierung von Datenbankzugriffen dar. In den Datenbankanwendungen werden häufig Datenmanipulationsanweisungen verwendet, die gleichzeitig viele Reihen einer Tabelle auswählen bzw. ändern. In solchen Fällen ist es immer besser, die Treffermenge aller Reihen mit einer SQL-Anweisung (SELECT, UPDATE oder DELETE) festzulegen, als für jede Reihe einmal die entsprechende SQL-Anweisung durchzuführen. Der Vorteil der ersten Programmierungsart ist, daß die entsprechende SQL-Anweisung eine wesentlich geringere Anzahl von E/A-Operationen erfordert und nur einmal vorübersetzt werden muß. Das folgende Beispiel zeigt, wie man mit einer SELECT-Anweisung eine größere Anzahl vo n Reihen einer Tabelle effizient auswählen sollte. Für dieses und drei folgende Beispiele wird die Beispieldatenbank verwendet, mit einer zusätzlichen Annahme: Wir gehen davon aus, daß die Tabelle mitarbeiter, die in allen Beispielen benutzt wird, insgesamt 100000 Reihen hat, wobei die Spalte m_nr eindeutige Datenwerte zwischen 1 und 100000 aufweist. Beispiel 16.19 Finden Sie Namen und Vornamen aller Mitarbeiter, deren Personalnummer vierstellig ist. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1619. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 MNAME PIC X(20) VALUE SPACES. 77 MVORNAME PIC X(20) VALUE SPACES. 77 VAR1 PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-DECLARE. PERFORM A200-OPEN. PERFORM A300-FETCH VARYING VAR1 FROM 1000 BY 1 UNTIL VAR1 > 9999. PERFORM A400-CLOSE. PERFORM DBCLOSE. STOP RUN. -223-
DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-DECLARE. EXEC SQL DECLARE crs_mit CURSOR FOR SELECT m_name, m_vorname FROM mitarbeiter WHERE m_nr BETWEEN 1000 AND 9999 END-EXEC. A200-OPEN. EXEC SQL OPEN crs_mit END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER IN OPEN". A300-FETCH. EXEC SQL FETCH crs_mit INTO :MNAME, :MVORNAME END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER IN FETCH". IF SQLCODE OF SQLCA NOT = 100 THEN DISPLAY MNAME," ",MVORNAME. A400-CLOSE. EXEC SQL CLOSE crs_mit END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER IN CLOSE". DBCLOSE. EXEC SQL DISCONNECT END-EXEC. In Abschnitt A100-DECLARE wird der Cursor crs_mit definiert, mit dem dann alle Reihen, die die Treffermenge bilden, mit einer SELECT-Anweisung ausgewählt werden. Das folgende Beispiel zeigt eine ineffiziente Art, dieselbe Aufgabe wie in Beispiel 16.19 zu lösen. Beispiel 16.20 IDENTIFICATION DIVISION. PROGRAM-ID. BSP1620. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. -224-
EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 MNAME PIC X(20) VALUE SPACES. 77 MVORNAME PIC X(20) VALUE SPACES. 77 VAR1 PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-SELECT VARYING VAR1 FROM 1000 BY 1 UNTIL VAR1 > 9999. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-SELECT. EXEC SQL SELECT m_name, m_vorname INTO :MNAME :MVORNAME FROM mitarbeiter WHERE m_nr = :VAR1 END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER IN SELECT". DISPLAY MNAME, MVORNAME. DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Die Anzahl der E/A-Operationen in Beispiel 16.20 ist wesentlich höher als in Beispiel 16.19. Zusätzlich dazu wird der Abschnitt A100-SELECT und damit auch die SELECT Anweisung 9000 vorübersetzt. Dadurch wird ein Programm, das einen solchen Teil beinhaltet, wesentlich langsamer als ein Programm, das, wie in Beispiel 16.19 gezeigt programmiert wurde. Das folgende Beispiel zeigt ein C-Programm, das identisch mit Beispiel 16.19 ist und die effizientere Programmierart in C zeigt. Beispiel 16.21 /* Beispel 16.21 */ #include <stdio.h> EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; char m_name[21]; -225-
char m_vorname[21]; int i; EXEC SQL END DECLARE SECTION; main() { EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL WHENEVER NOT FOUND GO TO :schlies; EXEC SQL CONNECT beispiel; EXEC SQL DECLARE crs_mit CURSOR FOR SELECT m_name, m_vorname FROM mitarbeiter WHERE m_nr BETWEEN 1000 AND 9999; EXEC SQL OPEN crs_mit; for (i=1000; i<=9999; i++) { EXEC SQL FETCH crs_mit INTO :m_name, :m_vorname; printf("%s%s\n", m_name, m_vorname); } schlies: EXEC SQL CLOSE crs_mit; EXEC SQL DISCONNECT; exit(0); fehler: printf ("Fehler: %d\n", sqlca.sqlcode); } Die ineffiziente Form dieses Programms zeigt Beispiel 16.22. Beispiel 16.22 /* Beispel 16.22 */ #include <stdio.h> EXEC SQL INCLUDE sqlca; EXEC SQL BEGIN DECLARE SECTION; char c_name[21]; char c_vorname[21]; int i; EXEC SQL END DECLARE SECTION; main() { EXEC SQL WHENEVER SQLERROR GO TO :fehler; EXEC SQL -226-
CONNECT beispiel; for (i=1000; i<=9999; i++) { EXEC SQL SELECT m_name, m_vorname INTO :c_name, :c_vorname FROM mitarbeiter WHERE m_nr = :i; printf("%s%s\n", c_name , c_vorname); } EXEC SQL DISCONNECT; exit(0); fehler: printf ("Fehler: %d\n", sqlca.sqlcode); } 15.5.2 Explizite Abfrage bei der Änderung von Reihen Zu gutem Programmierstil gehört auch die explizite Abfrage, ob eine Reihe geändert bzw. gelöscht werden soll. Wenn eine Reihe mittels einer UPDATE- bzw. einer DELETE-Anweisung bearbeitet werden soll, ist es empfehlenswert, die Datenwerte dieser Reihe am Bildschirm zuerst anzuzeigen, bevor sie geändert bzw. gelöscht werden sollten. Damit gibt man dem Anwender die Möglichkeit, sich zu vergewissern, ob es sich tatsächlich um die Reihe handelt, die er modifizieren will. Die Beispiele 16.17 und 16.18 zeigen diesen Programmierstil. Bevor eine Reihe der Tabelle arbeiten gelöscht wird, werden die Datenwerte dieser Reihe am Bildschirm angezeigt, und erst gelöscht, wenn der Anwender dies explizit bestätigt. 15.5.3 Explizite Angabe aller Reihen in einer SELECT- bzw. INSERT-Anweisung In der Projektion einer SELECT-Anweisung ist es möglich, die Angabe aller Spaltennamen durch das Zeichen "*" zu ersetzen. Bei der interaktiven Anwendung einer SELECT-Anweisung sind beide Schreibweisen gleich gut. Dies ist aber nicht bei den eingebetteten Anweisungen der Fall, bei denen alle Spaltennamen in einer SELECT-Anweisung angegeben sein sollten. Die explizite Angabe aller Spaltennamen ist wichtig, weil jedes Ändern des Tabellenschemas einen Fehler im Programm verursachen kann. In diesem Fall stimmt die Anzahl der Spalten mit der Anzahl der ihnen zugewiesenen Host-Variablen nicht mehr überein. Beispiel 16.23 zeigt, wie die SELECT-Anweisung für die Tabelle mitarbeiter in einem COBOL-Programm geschrieben sein sollte. -227-
Beispiel 16.23 EXEC SQL SELECT m_nr, m_name, m_vorname, abt_nr INTO :MNR, :MNAME, :MVORNAME, :ABTNR FROM mitarbeiter WHERE m_nr < MINZAHL END-EXEC.
Aufgaben Alle Aufgaben dieses Kapitels sollen mit der Programmiersprache Ihrer Wahl programmiert werden. A.16.1 Finden Sie die Aufgabe des Mitarbeiters, dessen Personalund Projektnummer am Bildschirm eingegeben werden. A.16.2 Fügen Sie die Reihe mit Namen, Vornamen und der Personalnummer eines neuen Mitarbeiters ein. Die Mitarbeiterdaten sollen am Bildschirm eingegeben werden. A.16.3 Finden Sie die Aufgabe und Projektnummer des Mitarbeiters mit der Personalnummer 28559. A.16.4 Vergleichen Sie die Laufzeiten der Programme in den Beispielen 16.19 und 16.20 bzw. 16.21 und 16.22.
-228-
Dynamisch formulierte Anweisungen
Dynamisch formulierte Anweisungen..........................................................................................229 17
Dynamisch formulierte Anweisungen................................................................................229 17.1 Einführung 229 17.2 Vorbereitung und Ausführung einer SQL-Anweisung 230 17.3 Dynamisch formulierte SELECT-Anweisungen in ESQL/C 17.4 Dynamisch formulierte SQL-Anweisungen und Optimierung Aufgaben 239
235 235
16 Dynamisch formulierte Anweisungen In diesem Kapitel werden eingebettete SQL-Anweisungen dargestellt, die die Formulierung von Anweisungen zur Laufzeit ermöglichen. Im ersten Teil des Kapitels werden die eingebetteten SQL-Anweisungen PREPARE, EXECUTE und EXECUTE IMMEDIATE erläutert. Anschließend wird die dynamisch formulierte SELECT-Anweisung erklärt. Am Ende des Kapitels wird gezeigt, wie die Formulierung der Anweisungen zur Laufzeit die Effizienz eines Programms erhöhen kann.
16.1 Einführung Alle im vorherigen Kapitel erläuterten SQL-Anweisungen werden explizit in ein Programm eingebettet und sind damit zur Übersetzungszeit bekannt. Mit diesen Anweisungen ist es nicht möglich, solche Anwendungen zu programmieren, bei denen z.B. eine ganze Anweisung am Bildschirm vom Benutzer eingegeben und dann vom System analysiert und ausgeführt wird. Solche Anwendungen können nur mit Hilfe von speziellen SQL-Anweisungen programmiert werden, die eine dynamische Formulierung der Anweisungen zur Laufzeit erlauben. Grundsätzlich existieren zwei Fälle, bei denen die Verwendung solcher speziellen SQL-Anweisungen notwendig ist: - dem Programm ist zur Übersetzungszeit nicht bekannt, welche Anweisung ausgeführt werden soll; - die Anzahl und der Typ der Datenwerte, die eine Anweisung dem Programm zurückgibt, sind unbekannt. Diese beiden Fälle sind grundverschieden und werden deswegen auch unterschiedlich behandelt. Falls eine Anweisung dem Programm zur Übersetzungszeit nicht bekannt ist, muß diese Anweisung zuerst vorübersetzt und dann ausgeführt werden. Die -229-
so bearbeiteten, dynamisch formulierten Anweisungen liefern dem Programm nur die Rückgabewerte, die in der SQLCA-Struktur gespeichert werden, im Unterschied z.B. zur SELECT-Anweisung mit der INTO-Klausel, die auch Datenwerte liefert. Im zweiten Fall, in dem Anzahl und Typ der Datenwerte nicht bekannt ist, muß dem Programm die Information darüber mitgeteilt werden. Das typische Beispiel dafür ist die SELECT-Anweisung mit INTO-Klausel. Das Programm, das eine solche Anweisung dynamisch bearbeitet, muß wissen, wieviele Datenwerte jeder Reihe ausgewählt werden, damit es die entsprechende Anzahl der Host-Variablen zur Verfügung stellen kann. Zusätzlich dazu muß auch der Datentyp und die Länge der Datenwerte dem Programm bekannt sein, damit die Eigenschaften der Host-Variablen definiert werden können. Alle SQL-Anweisungen, die notwendig sind, die beiden obigen Fälle zu behandeln, werden in den folgenden Abschnitten erörtert.
16.2 Vorbereitung und Ausführung einer SQL-Anweisung Wie wir schon erwähnt haben, muß eine Anweisung, die dem Programm zur Übersetzungszeit nicht bekannt ist, vorbereitet und ausgeführt werden. Das geschieht mit den Anweisungen PREPARE und EXECUTE . Mit der PREPARE-Anweisung wird eine Anweisung vorübersetzt und damit für die spätere Ausführung vorbereitet. Die PREPARE-Anweisung hat folgende Form PREPARE obj_name FROM quell_string quell_string ist entweder eine Zeichenkette oder eine Host-Variable, die eine Anweisung enthält. Ist quell_string eine Host-Variable, muß sie als alphanumerischer Datentyp definiert werden. obj_name definiert eine SQL-Variable, die die in quell_string enthaltene und vorübersetzte Anweisung aufnimmt. Die in quell_string gespeicherte Anweisung darf die Schlüsselwörter "EXEC SQL" und "END-EXEC." nicht enthalten. Genauso darf sie keine Host-Variablen enthalten. Anstelle jeder Host-Variablen muß das Zeichen "?" als Platzhalter benutzt werden. Bei der Ausführung wird jedes dieser Zeichen durch den entsprechenden Wert ersetzt. Durch die EXECUTE-Anweisung wird die mit PREPARE vorbereitete Anweisung ausgeführt. Die EXECUTE-Anweisung hat folgende Form: EXECUTE obj_name [USING param_liste] obj_name kennzeichnet die SQL-Variable, die zuvor in der PREPARE-Anweisung angegeben wurde. Sie beinhaltet die vorübersetzte Anweisung, die anschließend ausgeführt wird. param_liste kennzeichnet eine Liste von Host-Variablen. Wie wir schon bei der PREPARE-Anweisung erwähnt haben, dürfen die dynamisch formulierten Anweisungen keine Host-Variablen -230-
beinhalten. Statt dieser enthalten die Anweisungen einen Platzhalter. Jede Host-Variable in param_liste ersetzt den entsprechenden Platzhalter innerhalb der PREPARE-Anweisung. Anzahl und Datentyp der Host-Variablen müssen schon zur Übersetzungszeit bekannt sein. Die folgenden beiden Beispiele zeigen ein COBOL- bzw. ein C-Programm, in denen zuerst eine Host-Variable PSTRING definiert wird. In PSTRING ist die Anweisung gespeichert, die vom Anwender am Bildschirm eingegeben wurde. Mit der PREPARE-Anweisung wird die dynamisch formulierte Anweisung vorübersetzt und der SQL-Variablen obj_1 übergeben. Anschließend wird die Anweisung mittels EXECUTE ausgeführt. Die EXECUTE-Anweisung in den Beispielen 17.1 und 17.2 beinha ltet nicht die USING-Klausel. Deswegen ist es möglich, nur solche Anweisungen am Bildschirm zu formulieren, die keine Platzhalter enthalten. Beispiel 17.1 IDENTIFICATION DIVISION. PROGRAM-ID. BSP1701. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 PRNR PIC X(4) VALUE SPACES. 77 AUFGABE PIC X(15) VALUE SPACES. 77 MNR PIC S9(9) COMP. 77 PSTRING PIC X(80). EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-PREPARE. PERFORM A200-EXECUTE. PERFORM DBCLOSE STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-PREPARE. DISPLAY "Geben Sie die SQL-Anweisung an". ACCEPT PSTRING. EXEC SQL PREPARE obj_1 FROM :PSTRING END-EXEC. A200-EXECUTE. -231-
EXEC SQL EXECUTE obj_1 END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in EXECUTE". DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Beispiel 17.2 /* Beispiel 17.02 */ EXEC SQL INCLUDE SQLCA; #include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; char zkette[71]; int m_nr; EXEC SQL END DECLARE SECTION; main() { EXEC SQL CONNECT beispiel; if (sqlca.sqlcode < 0 ) {printf ("Fehler beim DB-Oeffnen\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit(sqlca.sqlcode); } printf("Geben Sie die SQL-Anweisung an\n"); gets(zkette); EXEC SQL PREPARE obj FROM :zkette; if (sqlca.sqlcode < 0 ) {printf ("Fehler in PREPARE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } EXEC SQL EXECUTE obj; if (sqlca.sqlcode < 0 ) {printf ("Fehler in PREPARE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } EXEC SQL DISCONNECT; } Das COBOL-Programm im folgenden Beispiel zeigt eine EXECUTEAnweisung mit der INTO -Klausel. Das Beispiel danach zeigt ein entsprechendes C-Programm. Beispiel 17.3 -232-
IDENTIFICATION DIVISION. PROGRAM-ID. BSP1703. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 ABTNR PIC X(4) VALUE SPACES. 77 MNR PIC S9(9) COMP. 77 ZKETTE PIC X(80). EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. DISPLAY "Geben Sie die SQL-Anweisung an". ACCEPT ZKETTE. DISPLAY ZKETTE. EXEC SQL PREPARE OBJ FROM :ZKETTE END-EXEC. DISPLAY SQLCODE OF SQLCA. PERFORM A100-EXECUTE UNTIL MNR = 0. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-EXECUTE. DISPLAY "Geben Sie die Mitarbeiternummer an". ACCEPT MNR. EXEC SQL EXECUTE OBJ USING :MNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "Fehler in EXECUTE" DISPLAY SQLCODE OF SQLCA. DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Beispiel 17.4 /* Beispiel 17.04 */ EXEC SQL INCLUDE sqlca; #include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; -233-
char zkette[71]; int m_nr; EXEC SQL END DECLARE SECTION; main() { EXEC SQL CONNECT beispiel; if (sqlca.sqlcode < 0 ) {printf ("Fehler beim DB-Oeffnen\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit(sqlca.sqlcode); } printf("Geben Sie die SQL-Anweisung an\n"); gets(zkette); EXEC SQL PREPARE obj from :zkette; if (sqlca.sqlcode < 0 ) {printf ("Fehler in PREPARE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } printf("Geben Sie die Mitarbeiternummer an:\n"); scanf("%d", &m_nr); EXEC SQL EXECUTE obj using :m_nr; if (sqlca.sqlcode < 0 ) {printf ("Fehler in PREPARE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } EXEC SQL DISCONNECT; } Folgende SQL-Anweisungen können z.B. in den Beispielen 17.3 und 17.4 vorbereitet und ausgeführt werden: UPDATE arbeiten SET pr_nr='p2' WHERE m_nr=?; DELETE FROM arbeiten WHERE m_nr=?; INGRES unterstützt eine weitere eingebettete SQL-Anweisung EXECUTE IMMEDIATE obj_name, mit der eine Anweisung gleichzeitig vorbereitet und ausgeführt werden kann. Damit beinhaltet die EXECUTE IMMEDIATE-Anweisung die Funktionalität der beiden Anweisungen PREPARE und EXECUTE. Im Unterschied zu PREPARE und EXECUTE, mit denen eine Anweisung einmal vorbereitet und mehrmals ausgeführt werden kann, wird bei der EXECUTE IMMEDIATE-Anweisung die Vorbereitung und die Ausführung immer zusammen durchgeführt. Damit eignet sich EXECUTE IMMEDIATE besonders dann, wenn eine Anweisung nur einmal in einem Programm ausgeführt werden soll.
-234-
16.3 Dynamisch formulierte SELECT-Anweisungen in ESQL/C Eine dynamisch formulierte SELECT-Anweisung mit der INTOKlausel kann nicht mit PREPARE und EXECUTE bzw. EXECUTE IMMEDIATE ausgeführt werden. Das Ergebnis einer solchen Abfrage wird an das Progamm zurückgegeben und die Datenwerte den Host-Variablen zugewiesen. Die Anzahl und der Datentyp der ermittelten Datenwerte ist erst zur Laufzeit bekannt. Deswegen ist es nicht möglich, Anzahl und Datentyp der Host-Variablen im voraus im Programm festzulegen. Diese Information muß zur Laufzeit dem Programm mitgeteilt werden. Die dynamisch formulierte SELECT-Anweisung wird mit Hilfe der DESCRIBE-Anweisung und einem Cursor programmiert. Mit der DESCRIBE-Anweisung wird die Liste der Spalten in der SELECTAnweisung untersucht sowie die Anzahl und der Datentyp der Spalten festgestellt. Deswegen muß im Programm Speicherplatz zur Verfügung gestellt werden, in dem dann die Datenwerte der aktuellen Reihe gespeichert werden. Die Aufgabe des Cursors und der mit ihm verbundenen ausführbaren SQL-Anweisungen OPEN, FETCH und CLOSE ist, die Reihen der Treffermenge sequentiell zu lesen und dem Programm zu übergeben. Der Speicherplatz, der im Programm zur Speicherung der Datenwerte einer Reihe benutzt wird, muß dynamisch zur Verfügung gestellt werden. Das heißt: Mit den Sprachen, die über eine dynamische Speicherverwaltung verfügen (wie C und Ada), ist es möglich, die dynamisch formulierte SELECT-Anweisung mit der INTO-Klausel zu programmieren. Die DESCRIBE-Anweisung hat folgende Form: DESCRIBE obj INTO deskriptor_name; obj kennzeichnet die in PREPARE vorübersetzte SQL-Anweisung. deskriptor_name ist ein Zeiger, der auf eine Struktur zeigt, in der Anzahl und Datentyp aller mittels SELECT-Anweisung ausgewählten Datenwerte gespeichert werden. Diese Struktur heißt SQLDA ("SQL Descriptor Area"), und sie muß genauso wie die SQLCA-Struktur mit der INCLUDE-Anweisung im Programm eingefügt werden. Jede SQLDA-Struktur hat u.a. zwei Teile - ein Datenfeld, in dem die Information über die Anzahl aller Datenwerte der Abfrage gespeichert wird sowie einen Vektor, der für jeden Datenwert den Typ und die Länge enthält. Mit der DESCRIBE-Anweisung ist es grundsätzlich möglich (aber nicht unbedingt notwendig), jede mit PREPARE vorübersetzte SQL-Anweisung dynamisch zu formulieren.
16.4 Dynamisch formulierte SQL -Anweisungen und Optimierung Die Ersetzung der zur Übersetzungszeit formulierten SQL-Anweisungen durch die entsprechenden dynamisch formulierten SQL-Anweisungen kann die Effizienz des Programms -235-
erhöhen. Das typische Beispiel ist der Fall, in dem ein Anwender interaktiv Eingaben macht und dadurch eine SQL-Anweisung mehrmals in einer Schleife ausgeführt werden muß. Mit Hilfe der PREPARE-Anweisung ist es oft möglich, die Vorübersetzung der Anweisung außerhalb der Schleife und damit nur einmal auszuführen. Die folgenden COBOL-Beispiele zeigen zwei funktionell gleiche Programmteile, in denen eine UPDATE-Anweisung mit und ohne eine PREPARE-Anweisung ausgeführt wird. Im ersten Beispiel ist diese Anweisung zur Übersetzungszeit und im zweiten erst zur Ablaufzeit formuliert. Beispiel 17.5 Mehrere Mitarbeiter der Firma haben ihre Abteilungen gewechselt. Ändern Sie die Abteilungsnummer dieser Mitarbeiter. Sowohl die Mitarbeiter- als auch die Abteilungsnummer sollen am Bildschirm eingegeben werden. IDENTIFICATION DIVISION. PROGRAM-ID. BSP1705. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 ABTNR PIC X(4) VALUE SPACES. 77 MNR PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. PERFORM A100-UPDATE UNTIL MNR = 0. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-UPDATE. DISPLAY "Geben Sie die Mitarbeiternummer an". DISPLAY "Endekriterium ist 0". ACCEPT MNR. DISPLAY "Geben Sie die neue Abteilungsnummer an". ACCEPT ABTNR. EXEC SQL UPDATE mitarbeiter SET abt_nr = :ABTNR WHERE m_nr = :MNR END-EXEC. -236-
IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEI UPDATE". DBCLOSE. EXEC SQL DISCONNECT END-EXEC. Beispiel 17.6 IDENTIFICATION DIVISION. PROGRAM-ID. BSP1706. ENVIRONMENT DIVISION. CONFIGURATION SECTION. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL BEGIN DECLARE SECTION END-EXEC. 77 ABTNR PIC X(4) VALUE SPACES. 77 MNR PIC S9(9) COMP. EXEC SQL END DECLARE SECTION END-EXEC. EXEC SQL INCLUDE SQLCA END-EXEC. PROCEDURE DIVISION. A1. PERFORM DBOPEN. EXEC SQL PREPARE obj FROM "UPDATE mitarbeiter SET abt_nr=? WHERE m_nr=?" END-EXEC. PERFORM A100-EXECUTE UNTIL MNR = 0. PERFORM DBCLOSE. STOP RUN. DBOPEN. EXEC SQL CONNECT BEISPIEL END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEIM DB-OEFFNEN" STOP RUN. A100-EXECUTE. DISPLAY "Geben Sie die Mitarbeiternummer an". DISPLAY "Endekriterium ist 0". ACCEPT MNR. DISPLAY "Geben Sie die neue Abteilungsnummer an". ACCEPT ABTNR. EXEC SQL EXECUTE obj USING :ABTNR, :MNR END-EXEC. IF SQLCODE OF SQLCA < 0 THEN DISPLAY "FEHLER BEI EXECUTE". DBCLOSE. EXEC SQL DISCONNECT END-EXEC. In Beispiel 17.5 wird für jeden Mitarbeiter, der die -237-
Abteilung gewechselt hat, einmal der Abschnitt A100-UPDATE und damit auch die UPDATE-Anweisung ausgeführt. Bei jeder Wiederholung muß die UPDATE-Anweisung neu vorübersetzt werden. In Beispiel 17.6 wird die UPDATE-Anweisung mit PREPARE außerhalb des A100-EXECUTE-Abschnitts vorbereitet und dadurch nur einmal vorübersetzt. Die Anzahl der E/A-Operationen ist in diesem Beispiel wesentlich geringer als im vorherigen. Die C-Programme in Beispielen 17.7 und 17.8 entsprechen jeweils den COBOL-Programmen in Beispielen 17.5 bzw. 17.6. Beispiel 17.7 /* Beispiel 17.07 */ EXEC SQL INCLUDE sqlca; #include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; char abt_nr[5}; int m_nr; EXEC SQL END DECLARE SECTION; main() { EXEC SQL CONNECT beispiel; if (sqlca.sqlcode < 0 ) {printf ("Fehler beim DB-Oeffnen\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit(sqlca.sqlcode); } m_nr = 1; while (m_nr != 0) { printf("Geben Sie die Nummer des Mitarbeiters an:\n"); printf("Endekriterium ist 0 \n"); scanf ("%d", &m_nr); printf("Geben Sie die neue Abteilungsnummer an\n"); scanf("%s",abt_nr); EXEC SQL UPDATE mitarbeiter SET abt_nr = :abt_nr WHERE m_nr = :m_nr; if (sqlca.sqlcode < 0 ) {printf ("Fehler in UPDATE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } } EXEC SQL DISCONNECT; } Beispiel 17.8 /* Beispiel 17.08 */ -238-
EXEC SQL INCLUDE sqlca; #include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; int m_nr; char abt_nr[5]; EXEC SQL END DECLARE SECTION; main() { EXEC SQL CONNECT beispiel; if (sqlca.sqlcode < 0 ) {printf ("Fehler beim DB-Oeffnen\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit(sqlca.sqlcode); } EXEC SQL PREPARE obj from 'UPDATE mitarbeiter SET abt_nr=? WHERE m_nr=?'; if (sqlca.sqlcode < 0 ) {printf ("Fehler in PREPARE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } m_nr = 1; while (m_nr != 0) { printf("Geben Sie die Mitarbeiternummer an:\n"); printf("Endekriterium ist 0 \n"); scanf("%d", &m_nr); printf("Geben Sie die neue Abteilungsnummer an\n"); scanf("%s", abt_nr); EXEC SQL EXECUTE obj using :abt_nr, :m_nr; if (sqlca.sqlcode < 0 ) {printf ("Fehler in EXECUTE\n"); printf ("Fehlernummer: %d\n", sqlca.sqlcode); exit (sqlca.sqlcode); } } EXEC SQL DISCONNECT; }
Aufgaben A.17.1
Bereiten Sie folgende Anweisungen: a) UPDATE mitarbeiter SET aufgabe = ? WHERE m_nr = ? b) DELETE FROM arbeiten WHERE m_name = ? c) DELETE FROM mitarbeiter WHERE m_name = ? d) UPDATE abteilung SET stadt = ? WHERE abt_nr = ? -239-
mit der Programmiersprache Ihrer Wahl vor und führen Sie sie anschließend aus.
-240-
Datenbank-Prozeduren
Datenbank-Prozeduren.................................................................................................................241 18
Datenbank-Prozeduren.......................................................................................................241 18.1 Einführung 241 18.2 SQL-Anweisungen in Bezug auf DB-Prozeduren 242 18.3 Fehlerbehandlung und Meldungen 244
17 Datenbank-Prozeduren In diesem Kapitel werden INGRES-Datenbank-Prozeduren beschrieben. Nach der Einführung, in der Bedeutung und Nutzen von DB-Prozeduren für die Datenbankanwendungen erläutert werden, werden alle in Bezug auf DB-Prozeduren existierenden INGRES-SQL-Anweisungen definiert und erklärt. Am Ende des Kapitels werden einige praktische Beispiele mit DB-Prozeduren gezeigt.
17.1 Einführung DB-Prozeduren sind eine Art von Funktionen. Sie werden vom DBA (oder vom Benutzer) erstellt und genauso wie alle anderen Datenbankobjekte (Tabellen, Views usw.) in einer Datenbank abgelegt. Jede DB-Prozedur beinhaltet sowohl SQLals auch prozedurale Anweisungen. Durch die kombinierte Anwendung dieser beiden Anweisungsarten hat der Benutzer die Möglichkeit, eine erweiterte Funktionalität (im Vergleich zu der Verwendung reiner SQL-Anweisungen) zu erreichen. Jeder DB-Prozedur können Daten als Werteparameter zugewiesen werden. Die Übergabe wird beim Prozeduraufruf durchgeführt. Innerhalb einer DB-Prozedur können SQL-Anweisungen verwendet werden, um Datenwerte einer Datenbank abzufragen bzw. zu modifizieren. Jede übersetzte DB-Prozedur wird immer in einer ausführbaren Form gespeichert. Diese Vorgehensweise bringt einen entscheidenden Vorteil für oft verwendete Anweisungsfolgen. Jede SQL-Anweisung muß generell mehrere Phasen ("parsing"-, Optimierungsphase usw.) vor der Ausführung durchlaufen. Durch die Erstellung von DB-Prozeduren, die häufig verwendete SQL-Anweisungsfolgen enthalten, ist es möglich, die wiederholte Durchführung solcher Phasen zu eliminieren. Dieses Verfahren bringt auch einen anderen Vorteil: Die Anzahl der Zugriffe zwischen den Anwendungsprogrammen und -241-
dem Datenbank-Server kann wesentlich reduziert werden, falls eine DB-Prozedur mit einer Anweisungsfolge benutzt wird, anstatt SQL-Anweisunge n einzeln zu benutzen. Ein weiterer Vorteil von DB-Prozeduren ist, daß alle Datenbankanwendungen, die das entsprechende Zugriffsrecht haben, die DB-Prozeduren gemeinsam verwenden können. Dadurch wird die Programmierung und die Verwendung der existierenden Ressourcen effizienter. Die DB-Prozeduren können u.a. für folgende Zwecke verwendet werden: - zur Trennung von Datendefinitions- und Datenmanipulationsanweisungen; - zur Einschränkung von Zugriffsrechten auf Datenwerte einer Datenbank; - zur Erstellung einer Protokolldatei, die die Schreib- bzw. Leseoperationen auf Daten einer Tabelle enthält; Die Verwendung von DB-Prozeduren für die Programmierung von Datendefinitionsanweisungen ist sinnvoll, weil dadurch eine klare Trennlinie zwischen der Erstellung von Datenbankobjekten in den DB-Prozeduren einerseits und der Datenmanipulation in den Anwendungsprogrammen andererseits gezogen wird. Die Einschränkung von Zugriffsrechten mit Hilfe von DB-Prozeduren kann als Ergänzung bzw. Alternative zum Autorisierungsmechanismus gesehen werden. Falls z.B. eine Anwendung nur gewisse Spalten einer Tabelle ändern darf (und über die Existenz der anderen nicht informiert sein sollte), kann eine DB-Prozedur geschrieben werden, die das ermöglicht. Die Erstellung einer DB-Prozedur, die lesende bzw. schreibende Zugriffe auf gewählte Tabelle(n) protokolliert, kann als eine zusätzliche Sicherheitsmaßnahme betrachtet werden. Dadurch kann z.B. verfolgt werden, welcher Benutzer, bzw. welches Anwendungsprogramm Datenwerte einer Tabelle verfälscht.
17.2 SQL-Anweisungen in Bezug auf DB-Prozeduren Eine DB-Prozedur wird, ähnlich wie alle anderen Datenbankobjekte (Tabellen, Views usw.), mit der Anweisung CREATE PROCEDURE erstellt. Diese Anweisung hat folgende Form: [CREATE] PROCEDURE proz_name [(param_1=typ_1 [,param_2=typ_2,...])] [declare-Abschnitt] BEGIN anweisung_1; [anweisung_2;...] END proz_name ist der Name der DB-Prozedur, während param_1, param_2,... Parameternamen sind. typ_1, typ_2,... -242-
spezifizieren den Datentyp einzelner Parameter. Im declare -Abschnitt werden alle lokalen Variablen, die in der DB-Prozedur verwendet werden, definiert. Dieser Abschnitt fängt mit dem Schlüsselwort DECLARE an, gefolgt von der Variablendeklaration. anweisung_1, anweisung_2,... stellen die Anweisungen dar, die beim Prozeduraufruf ausgeführt werden. Jede mit CREATE PROCEDURE erstellte DB-Prozedur kann entweder mit der EXECUTE PROCEDURE-Anweisung explizit oder implizit mit Hilfe einer Regel aufgerufen werden. (Für die Beschreibung der Regel siehe Kapitel 20.) Genauso kann jede DB-Prozedur sowohl interaktiv als auch eingebettet erstellt und benutzt werden. Hinweis. Die Syntax der SQL-Anweisungen, die im Zusammenhang mit den DB-Prozeduren stehen, unterscheidet sich, abhängig davon, ob die DB-Prozedur interaktiv oder eingebettet verwendet wird. In diesem Buch werden die Syntax und die Verwendung der Anweisungen in der interaktiven Umgebung beschrieben. Für die Unterschiede zu der Verwendung in der eingebetteten Umgebung, die im allgemeinen geringfügig sind, möchten wir auf INGRES-Manuale verweisen. Jeder Benutzer darf eine DB-Prozedur erstellen, falls er das Zugriffsrecht für die in der DB-Prozedur verwendeten Tabellen bzw. Views hat. Folgende SQL-Anweisungen dürfen im Anweisungsteil einer DB-Prozedur erscheinen: COMMIT MESSAGE UPDATE DELETE RETURN WHILE IF ROLLBACK RAISE ERROR INSERT SELECT Beispiel 18.1 CREATE PROCEDURE proz1 (param_ch=CHAR(8) NOT NULL) AS DECLARE lokal_varch=CHAR(8) NOT NULL; lokal_varfl=FLOAT; lokal_vardat=DATE; BEGIN lokal_varch = 'ABCDEFGH'; lokal_varfl = NULL; lokal_vardat = '25-jun-1991'; param_ch = :lokal_varch; END In Beispiel 18.1 wird die DB-Prozedur proz1 definiert, die einen Parameter param_ch enthält, dessen Datentyp CHAR(8) ist. Im DECLARE-Abschnitt sind drei Variablen lokal_varch, lokal_varfl und lokal_vardat definiert, denen im Anweisungsteil Werte zugewiesen werden. Im Anweisungsteil wird auch der Wert der lokalen Variablen lokal_varch dem Parameter param_ch zugewiesen. Die EXECUTE PROCEDURE-Anweisung, mit der eine existierende DB-Prozedur aufgerufen werden kann, hat folgende Form EXECUTE PROCEDURE proz_name -243-
[param_1=:wert_1 [,param_2=:wert_2,...]] [INTO :status]; proz_name ist der Name der aufgerufenen DB-Prozedur, während param_1,param_2,... die Parameternamen sind, die den gleichnamigen Parametern der CREATE PROCEDURE-Anweisung entsprechen. wert_1, wert_2,... sind numerische, alphanumerische oder NULL-Werte, die den Parametern zugewiesen werden. Der Datentyp eines Parameters muß mit dem zugewiesenen Wert kompatibel sein. Ausdrücke dürfen nicht als Parameterwerte verwendet werden. status kennzeichnet eine ganzzahlige Variable, in der der Rückgabewert der DB-Prozedur gespeichert wird. Jede erstellte DB-Prozedur kann - wie jedes andere Datenbankobjekt (Tabelle, Index usw.) - gelöscht werden. Die Anweisung DROP PROCEDURE proz_name löscht die DB-Prozedur namens proz_name . Nur der Eigentümer einer DB-Prozedur hat das Recht, die DB-Prozedur zu löschen. Prozedurale Anweisungen in DB-Prozeduren sind u.a. - IF, - WHILE und - RETURN. Die IF-Anweisung enthält eine Bedingung und mehrere Anweisungsgruppen, von welchen eine ausgeführt wird. Die Anweisung WHILE wiederholt die Ausführung einer Anweisungsgruppe, solange die angegebene Bedingung erfüllt ist. Sowohl die IF- als auch die WHILE-Anweisung sind allgemein bekannte Anweisungen, deren Syntax identisch der Syntax der gleichnamigen Anweisungen in den meisten Programmiersprachen ist. Die Anweisung RETURN [status]; beendet unmittelbar die Ausführung einer DB-Prozedur und übergibt die Steuerung an die Anwendung zurück, deren Teil die DB-Prozedur ist. Die optionale ganzzahlige Variable status kennzeichnet den von der DB-Prozedur an die Anwendung übergebenen Rückgabewert. (Um den Rückgabewert auswerten zu können, muß die DB-Prozedur mit der Angabe INTO status in der EXECUTE PROCEDURE-Anweisung aufgerufen werden.)
17.3 Fehlerbehandlung und Meldungen Falls ein Fehler innerhalb einer DB-Prozedur auftritt, werden vom INGRES-System standardmäßig Schritte unternommen, die abhängig davon sind, ob die DB-Prozedur explizit mit der EXECUTE PROCEDURE-Anweisung oder durch das Aktivieren einer Regel aufgerufen wird. Beim Aufruf durch das Aktivieren einer Regel wird die DB-Prozedur beendet und sowohl alle ausgeführten Anweisungen in der DB-Prozedur als auch die Anweisung, die die Regel aktiviert hat, -244-
werden zurückgesetzt. In den DB-Prozeduren, die mittels EXECUTE PROCEDURE-Anweisung aufgerufen werden, werden alle ausgeführten Anweisungen innerhalb der DB-Prozedur zurückgesetzt, die DB-Prozedur wird aber nicht beendet, sondern mit der auf die fehlerhafte folgende Anweisung weiter ausgeführt. In beiden Fällen wird sowohl das Feld sqlcode der sqlca-Struktur als auch errerno der INQUIRE INGRES-Anweisung mit der Fehlernummer versorgt. Zusätzlich zu sqlcode und errerno existieren weitere Systemvariablen iierro r, number und iirowcount sowie die SQL-Anweisung RAISE ERROR, die dem Anwendungsprogrammierer die Fehlerbehandlung ermöglichen. Die Variable iierrornumber enthält bei den Datenmanipulationsanweisungen den Wert 0, falls eine Anweisung in der DB-Prozedur erfolgreich beendet wurde bzw. die INGRES-Systemfehlernummer, falls sie fehlerhaft war. Die Variable iirowcount enthält bei den Datenmanipulationsanweisungen im ersten Fall die Anzahl der verarbeiteten Zeilen und im zweiten Fall den Wert 0. Die Anweisung RAISE ERROR fehler_nr [fehler_text]; meldet dem Anwendungsprogramm das Auftreten eines Fehlers. Diese Anweisung kann im Zusammenhang mit dem Aktivieren einer Regel sinnvoll verwendet werden. (siehe Beispiel 20.2). fehler_nr ist eine ganzzahlige Konstante, eine lokale Variable oder ein Parameter. Diese Angabe enthält eine lokale DBMS-Fehlernummer, die im Feld sqlerrd(1) der sqlca-Struktur abgelegt wird. fehler_text definiert den entsprechenden Fehlertext. INGRES unterstützt eine weitere SQL-Anweisung MESSAGE text | nummer | text nummer, mit der Meldungen aus einer DB-Prozedur an das Anwendungsprogramm, das sie aufgerufen hat, geschickt werden können. text kennzeichnet den Meldungstext während nummer die Meldungsnummer darstellt. Die Behandlung der Meldungen durch das Anwendungsprogramm hängt davon ab, ob die DB-Prozedur explizit mit der EXECUTE PROCEDURE-Anweisung oder durch das Aktivieren einer Regel aufgerufen wurde. In beiden Fällen wird standardmäßig der Meldungstext und die Meldungsnummer am Bildschirm ausgegeben. Falls die DB-Prozedur durch das Aktivieren einer Regel aufgerufen wurde, wird, wie oben beschrieben, der Text und die Meldungsnummer ausgegeben. Beim Aufruf der DB-Prozedur mittels EXECUTE PROCEDURE-Anweisung wird der Text und die Nummer an die DB-Prozedur übergeben, und zusätzlich wird dem Feld sqlcode der sqlca-Struktur der Wert +700 zugewiesen. Beispiel 18.2 CREATE PROCEDURE gruppenleiter (m_name CHAR(20)) AS DECLARE -245-
meldung CHAR(80); zaehler INTEGER; BEGIN SELECT COUNT(*) INTO :zaehler FROM arbeiten WHERE m_name = :m_name AND aufgabe= 'Gruppenleiter'; IF zaehler = 0 THEN meldung='Fehler 12345: Mitarbeiter "' + :m_name + '" ist kein Gruppenleiter.'; RAISE ERROR 12345 : meldung; ENDIF; END; In der DB-Prozedur gruppenleiter wird überprüft, ob ein Mitarbeiter der Firma in einem der Projekte Gruppenleiter ist. Falls dies nicht der Fall ist, wird ein Meldungstext und die Meldungsnummer 12345 am Bildschirm ausgegeben.
-246-
Object Management
Object Management ................................................................. Fehler! Textmarke nicht definiert. 19
Object Management ............................................................................................................247 19.1 19.2 19.3 19.4
Einleitung 247 Benutzerdefinierte Datentypen Benutzerdefinierte Funktionen Funktionsinstanzen 251
248 250
18 Object Management Dieses Kapitel beschreibt die Eigenschaften des Object Management-Moduls von INGRES. Nach der Einführung und Beschreibung von Merkmalen dieser INGRES-Komponente werden benutzerdefinierte Datentypen und Funktionen sowie Funktionsinstanzen beschrieben.
18.1 Einleitung Ab Version 6.3 bietet INGRES die sogenannte intelligente Datenbank an, die aus drei Moduln - Data Management, - Object Management und - Knowledge Management besteht. Der Data Management-Modul kennzeichnet das Grundprodukt von INGRES, der alle bis jetzt beschriebenen Komponenten enthält. Der Object Management-Modul ist, genauso wie der Knowledge Management-Modul, ein optionales Produkt, der dem Benutzer die Möglichkeit bietet, eigene Datentypen und Funktionen zu implementieren. Der Knowledge Management-Modul schließlich dient der Erstellung zusätzlicher Regeln. Der Object Management-Modul wird in diesem Kapitel, der Knowledge Management-Modul im nächsten Kapitel beschrieben. Alle herkömmlichen Datenbanksysteme unterstützen nur die einfachen Datentypen wie Zeichenketten, Za hlen usw. Alle abstrakten Datentypen müssen in den einzelnen Anwendungsprogrammen implementiert werden. Diese Vorgehensweise bringt einen großen Implementierungsaufwand mit sich, weil die Programmierung solcher Datentypen lange dauert und, im Falle von allgemein benutzten abstrakten -247-
Datentypen (wie z.B. dem Datentyp "Arbeitstage", der nur die Werktage berücksichtigt) in jedem einzelnen Projekt von neuem implementiert wird. Ein weiterer Nachteil ist, daß alle Integritätsvorschriften, die im Bezug auf einen abstrakten Datentyp existieren, im Anwendungsprogramm berücksichtigt werden müssen. Auch die Verarbeitungsgeschwindigkeit einer Datenbankanwendung wird dadurch beeinträchtigt, daß für die Bearbeitung und die Überprüfung von Integritätsvorschriften abstrakter Datentypen immer Zugriffe vom Anwendungsprogramm auf den Datenbank-Server notwendig sind. INGRES bietet dem Benutzer, mit Hilfe des Object Management-Moduls die Möglichkeit, eigene abstrakte Datentypen zu erstellen. Object Management ermöglicht auch die Manipulation der benutzerdefinierten Datentypen mit Hilfe von benutzerdefinierten Funktionen und Funktionsinstanzen. Alle benutzerdefinierten Datentypen werden nach der Erstellung fester Bestandteil von INGRES-DBMS, so daß alle o.g. Nachteile entfallen. Weil die vom Benutzer geschriebenen Routinen, die abstrakte Datentypen oder neue Funktionen definieren, als Teil des INGRES-DBMS laufen, haben sie dieselbe Bedeutung wie der vom Hersteller implementierte Quell-Code. Deswegen ist es wichtig, die Routinen sehr sorgfältig zu implementieren und auszutesten, weil jeder Fehler einem Fehler in der INGRES-Software entspricht und somit weitgehende Konsequenzen für die existierenden Datenbankanwendungen haben kann. Die Routinen, mit denen benutzerdefinierte Datentypen und Funktionen implementiert werden, können in jeder Programmiersprache der dritten Generation implementiert werden. Trotzdem ist es empfehlenswert, sie in der Sprache C zu programmieren, weil die von INGRES zur Verfügung gestellten Makro-Bibliotheken in C geschrieben sind. Die Implementierung der Benutzerrutinen in einer anderen Sprache verlangt die genaue Einhaltung der Strukturen, die in den Makro-Bibliotheken existieren.
18.2 Benutzerdefinierte Datentypen Jeder abstrakte Datentyp muß zuerst vom Benutzer definiert werden. Die Definition beinhaltet - den Namen, - die Länge und - die Identifikationsnummer des neuen Datentyps. Diese drei Merkmale werden in den entsprechenden Datenfeldern der Struktur iiadd_dt_dfn abgelegt. (iiadd_dt_dfn ist ein Teil der Makro-Bibliothek iiadd.h, die von INGRES zur Verfügung gestellt wird.) Der Name des benutzerdefinierten Datentyps darf maximal 24 -248-
Zeichen lang sein. Falls die Zeichenkette kürzer als 24 Zeichen ist, muß sie mit dem Wert 0 abgeschlossen sein. Die Identifikationsnummer muß eindeutig in Bezug auf alle benutzerdefinierten Datentypen sein und zwischen 16384 und 16511 liegen. Die maximale Anzahl der benutzerdefinierten Datentypen ist z.Z. auf 128 beschränkt. Jeder benutzerdefinierte Datentyp ist nur dem INGRES-DBMS bekannt. Die Benutzerschnittstellen von INGRES (QBF, RBF usw.) wissen nichts von der Existenz dieser Datentypen. Deswegen muß das DBMS die Konvertierung jedes benutzerdefinierten Datentyps für die Benutzerschnittstellen vornehmen. Diese Konvertierung erfolgt auf Grund der Werte, mit denen der Benutzer die Funktion dbtoev versorgt. Nachdem der Datentypname und die Identifikationsnummer festgelgt sind, müssen die von INGRES vorgegebenen Routinen, die die Manipulation des benutzerdefinierten Datentyps beschreiben, versorgt werden. Jede Routine hat folgende Parameter - scb (muß angegeben werden), - arg1 (optional), - arg2 (optional), - ergebnis (muß angegeben werden), die folgende Bedeutung haben Parameter scb
arg1 / arg2
ergebnis
Bedeutung eine Struktur ("Session Control Block"), die die Information für das INGRES Datentyp-Subsystem liefert. zwei optionale Parameter, die als Zeiger definiert sind und auf die Struktur ii_data_values zeigen. ii_data_values beschreibt die von einer Routine benutzten Datenwerte. enthält das Teilergebnis einer Funktion.
Für jeden benutzerdefinierten Datentyp müssen folgende von INGRES vorgegebenen Routinen versorgt werden: Comparehmax dbtoev hmin dhmax keybuild dhmin length_check getempty minmaxdv hashprep tmcvt helem value_check hg_dtln tmlen Routine compare
Funktion vergleicht zwei Datenelemente des benutzerdefinierten Datentyps. Die beiden Argumente arg1 und arg2 zeigen auf die zu vergleichenden Datenelemente. -249-
dbtoev dhmax dhmin
getempty
hashprep helem
hg_dtln hmax hmin keybuild
length_check minmaxdv tmcvt value_check tmlen
legt den INGRES-Datentyp fe st, in den der benutzerdefinierte Datentyp konvertiert wird. definiert den maximalen bzw. minimalen Standardwert, der vom Optimierer für die Berechnung der optimalen Strategien verwendet wird. definiert den Standardwert für den benutzerdefinierten Datentyp (z.B. ist der Standardwert für numerische Werte 0 und für alphanumerische Werte das Leerzeichen). bereitet einen Datumswert zur Verwendung als Hash-Schlüssel vor. erstellt eine (gewöhnlich verkürzte) Darstellung für den Datumswert. Diese Darstellung wird vom Optimierer für die Erstellung der optimalen Strategien verwendet. liefert den Namen und die Länge des als Argument angegebenen Datentyps. definiert den tatsächlich maximalen bzw. minimalen Datenwert für den benutzerdefinierten Datentyp. erstellt aus einem Datenwert den Schlüssel, der bei der ISAM-, CISAM, BTREE- und CBTREESpeicherstruktur für die direkte Suche verwendet wird. überprüft, ob die angegebene Länge des Datentyps gültig ist. liefert den minimalen bzw. maximalen Wert (oder Länge) des als Parameter angegebenen Datentyps. konvertiert den angegebenen Datentyp in eine druckbare Form. überprüft die Gültigkeit der Datenwerte. liefert die Standard- und maximale Länge eines Datentyps, für den Fall, daß er als Text gedruckt werden muß.
18.3 Benutzerdefinierte Funktionen Neben den benutzerdefinierten Datentypen ist es mit Object Management möglich, auch benutzerdefinierte Funktionen zu implementieren, die dem INGRES-DBMS bekannt sind und von ihm als herkömmliche Systemfunktionen behandelt werden. Die benutzerdefinierten Funktionen unterstützen sowohl alle INGRES-Datentypen als auch benutzerdefinierte Datentypen als Parameter. Jede benutzerdefinierte Funktion muß zuerst definiert werden. Ihre Definition beinhaltet - den Funktionsnamen, -250-
- die Identifikationsnummer und - den Operationstyp. Diese Merkmale werden in der Struktur iiadd_fo_dfn abgelegt. (iiadd_fo_dfn ist ein Teil der Makro-Bibliothek iiadd.h, die von INGRES zur Verfügung gestellt wird.) Die Struktur iiadd_fo_dfn hat folgende Felder Feldname Bedeutung fod_object_typ enthält die Konstante II_O_OPERATION. e fod_name stellt den Funktionsnamen dar. (Für den Funktionsnamen gilt alles, was wir schon für den Datentypnamen gesagt haben.) fod_id enthält die Identifikationsnummer der Funktion. Diese muß eindeutig in Bezug auf alle benutzerdefinierten Funktionen und größer als 16384 sein. fod_type stellt den Operationstyp dar.
18.4 Funktionsinstanzen Funktionsinstanzen definieren die Verwendung einer Funktion bzw. eines Operators (z.B. "+") in einem konkreten Fall. Die Existenz der Funktionsinstanzen ist notwendig, weil dieselbe Funktion bzw. derselbe Operator mit verschiedenen benutzerdefinierten Datentypen unterschiedliche Bedeutung haben kann. Die Definition der Funktionsinstanzen beinhaltet: - die Identifikationsnummer; - den Komplementoperator; - die Identifikationsnummer der Funktion, die diesen Operator verwendet; - den Operationstyp; - die Anzahl von Argumenten und ihren jeweiligen Datentyp; - den Datentyp des Ergebnisses; - die Länge des Ergebnisses; - die Adresse der Routine, die die Funktionsinstanz ausführt. Die Identifikationsnummer ist eine 2 Byte lange Ganzzahl, die eindeutig in Bezug auf alle Funktionsinstanzen und größer als 16384 sein muß. Der Komplementoperator einer Funktionsinstanzen muß angegeben werden, falls es sich beim Operator um einen Vergleichsoperator handelt. (Z.B. ist das Komplement von "=" (gleich) "!=" (ungleich)). Die Definition eines Komplementoperators ist notwendig, um dem Optimierer die Möglichkeit zu geben, alternative Wege zur Abarbeitung einer Abfrage mit Hilfe des Komplementoperators -251-
durchzuführen. Der Operationstyp definiert die Eigenschaft eines Operators. Beispiel 19.1 Ein allgemein verwendbarer abstrakter Datentyp ist "Arbeitstag". Bei diesem Datentyp handelt es sich um alle Wochentage, die Werktage sind, also Montag, Dienstag, Mittwoch, Donnerstag und Freitag. Für den Datentyp "Arbeitstag" können z.B. als Operatoren "+" (Addition) und "-" (Subtraktion) definiert werden. Für den ersten Operator kann die Funktion "Addiere" und für den zweiten die Funktion "Subtrahiere", mit folgenden Ergebnissen definiert werden: Addiere ("Montag" + 5) = "Montag", Subtrahiere ("Montag - 1) = "Freitag".
-252-
Knowledge Management
20
Knowledge Management ....................................................................................................253 20.1 Benutzerdefinierte Regel 253 20.1.1 Referentielle Integrität 255 20.2 Erweiterter Autorisierungsmechanismus 258 20.2.1 Gruppen- und Prozedur-Zugriffsrechte 258 20.2.2 Die erweiterte GRANT-Anweisung und die REVOKE-Anweisung 260
19 Knowledge Management In diesem Kapitel wird der Knowledge Management-Modul von INGRES beschrieben. Zuerst wird die Möglichkeit erörtert, mit Hilfe des Knowledge Management-Moduls benutzerdefinierte Regel zu erstellen. Eine der wichtigsten Anwendungen der benutzerdefinierten Regel ist die Unterstützung der referentiellen Integrität, die definiert und an Beispielen erklärt wird. Im zweiten Teile des Kapitels wird der erweiterte Autorisierungsmechanismus, den Knowledge Management auch unterstützt, erklärt. In diesem Zusammenhang werden Gruppen- und Anwendungs-Zugriffsrechte dargestellt und die in Bezug auf erweiterte Zugriffsrechte stehenden SQL-Anweisungen GRANT und REVOKE erörtert.
19.1 Benutzerdefinierte Regel Der Knowledge Management-Modul ist neben dem Object Management-Modul das zweite zusätzliche Produkt, das ab Version 6.3 von INGRES unterstützt wird. Dieser optionale Modul bietet zwei wichtige funktionelle Erweiterungen an: - die Erstellung von benutzerdefinierten Regel und - den erweiterten Autorisierungsmechanismus. Die benutzerdefinierten Regel werden in diesem Abschnitt erörtert, während der erweiterte Autorisierungsmechnismus das Thema des nächsten Abschnitts sein wird. Regel sind benutzerdefinierte Konstrukte, die im Zusammenhang mit DB-Prozeduren benutzt werden können, um gewisse, vom Benutzer gewünschte Tätigkeiten ausführen zu können. Wenn bei der Ausführung einer SQL-Anweisung die Bedingung einer benutzerdefinierten Regel erfüllt ist, wird die im Zusammenhang mit der Regel stehende DB-Prozedur aufgerufen und ausgeführt. Eine Regel kann in Bezug auf -253-
- eine INSERT-, UPDATE- oder DELETE-Anweisung; - die Änderung eines Datenwertes einer Tabellenspalte; - eine Änderung, die eine spezifische, im Zusammenhang mit einer oder mehreren Spalten stehende Bedingung betrifft, definiert werden. Mit der Anweisung CREATE RULE regel_name tab_bedingung EXECUTE PROCEDURE proz_name [(param_1=wert_1 [,param_2=wert_2,...])]; wird eine Regel definiert. regel_name ist der Name der benutzerdefinierten Regel während proz_name der Name der bei der Erfüllung der Bedingung aufgerufenen DB-Prozedur ist. param_1, param_2,... sind die Parameter der DB-Prozedur, denen die Datenwerte wert_1, wert_2,... in dieser Reihenfolge zugewiesen werden. tab_bedingung definiert die Bedingung, die erfüllt sein muß, um die Regel zu aktivieren. Diese Bedingung hat folgende Syntax AFTER anw_1 [,anw_2,...] ON|OF|FROM|INTO tab_name [REFERENCING [OLD AS alt_alias] [NEW AS neu_alias]] [WHERE bedingung] anw_1,anw_2,... stellen die SQL-Anweisungen dar, nach derer Ausführung die Regel regel_name aktiviert wird. Nur folgende SQL-Anweisungen können mit CREATE RULE verwendet werden: - INSERT, - UPDATE [(spalte)] und - DELETE. Jede dieser Anweisungen darf nur einmal in der Anweisungsliste vorkommen. Die UPDATE-Anwesiung ist die einzige, die die explizite Angabe einer Spalte erlaubt. (Falls keine Spalte in der UPDATE-Anweisung angegeben ist, wird die Regel nach der Änderung jeder Tabellenspalte aktiviert.) Die REFERENCING-Klausel ermöglicht die Auswahl von Aliasnamen für einen Spaltennamen. Immer wenn wert_1, wert_2,... Spaltennamen darstellen, müssen diese Datenwerte mit Hilfe von Aliasnamen gekennzeichnet werden. alt_alias spezifiziert den Aliasnamen für den Datenwert vor der Änderung während neu_alias den Aliasnamen für den Datenwert nach der Änderung der Spalte darstellt. Falls die REFERENCING-Klausel ausgelassen wird, wird standardmäßig REFERRENCING OLD AS old NEW AS new angenommen. bedingung kennzeichnet die Änderung der Tabelle tab_name , die auftreten muß, um die Regel zu aktivieren. Eine Regel kann nur derjenige Benutzer erstellen, der sowohl die in der Regel verwendete Tabelle besitzt als auch das Recht zur Ausführung der aufgrund der Regel aufgerufenen DB-Prozedur hat. Jede CREATE RULE-Anweisung verwendet eine -254-
exklusive Sperre für die angesprochene Tabelle. Beispiel 20.1 CREATE RULE fuege_projekt AFTER INSERT INTO projekt EXECUTE PROCEDURE neu_projekt (name=new.pr_name, nummer=new.pr_nr); In Beispiel 20.1 wird die Regel fuege_projekt definiert, die jedesmal aktiviert wird, wenn eine Reihe in der Tabelle projekt eingefügt wird. Die Aktivierung dieser Regel bedeutet, daß die DB-Prozedur neu_projekt aufgerufen wird und den Parametern name und nummer die Datenwerte der Spalten pr_name und pr_nr in dieser Reihenfolge zugewiesen werden. Beispiel 20.2 CREATE RULE mittel_grenze AFTER UPDATE(mittel) REFERENCING OLD AS alt NEW AS neu WHERE neu.mittel > 200000.00 EXECUTE PROCEDURE geld_limit (name=alt.pr_name, mittel=neu.mittel); In Beispiel 20.2 wird die Regel mittel_grenze erstellt, die aktiviert wird, falls Mittel eines existierenden Projektes über 200.000DM erhöht werden. Die REFERENCING-Klausel definiert die Angabe alt für die Kennzeichnung aller Datenwerte vor der Änderung und die Angabe neu für die Kennzeichnung aller Datenwerte nach der Änderung der Spalte. Mit der Anweisung DROP RULE regel_name kann eine existierende Regel gelöscht werden. Eine Regel kann nur von ihrem Eigentümer gelöscht werden. Alle Regeln, die in Bezug zu einer Tabelle stehen, werden implizit gelöscht, falls die Tabelle mit der DROP TABLE-Anweisung gelöscht wird. 19.1.1 Referentielle Integrität Benutzerdefinierte Regel eignen sich u.a. für die Überprüfung der referentiellen Integrität einer Datenbank. Die referentielle Integrität beschreibt eine besondere Beziehung zwischen den Spalten zweier Tabellen einer Datenbank. Eine Spaltengruppe der Tabelle tab2 wird Fremdschlüssel genannt, falls alle ihre Werte, die nicht NULL-Werte sind, identische Werte im Primärschlüssel einer anderen Tabelle tab1 haben. Die Tabelle tab1 wird Zieltabelle und die Tabelle tab2 die referenzierte Tabelle genannt. (Die Tabellen tab1 und tab2 können u.U. dieselbe Tabelle darstellen.) Die referentielle Integrität kennzeichnet die Eigenschaft, daß jeder Datenwert (außer dem NULL-Wert) eines Fremdschlüssels den identischen Wert in dem entsprechenden Primärschlüssel haben muß. Diese Eigenschaft kann an den Tabellen der Beispieldatenbank gezeigt werden. In der -255-
Beispieldatenbank existieren folgende Fremdschlüssel: - Die Spalte abt_nr in der Tabelle mitarbeiter. (Der entsprechende Primärschlüssel ist die Spalte abt_nr der Tabelle abteilung.) - Die Spalte m_nr in der Tabelle arbeiten. (Der entsprechende Primärschlüssel ist die Spalte m_nr in der Tabelle mitarbeiter.) - Die Spalte pr_nr in der Tabelle arbeiten. (Der entsprechende Primärschlüssel ist die Spalte pr_nr in der Tabelle projekt.) Es existieren insgesamt vier Fälle, in denen das Ändern der Datenwerte im Fremdschlüssel bzw. in dem entsprechenden Primärschlüssel die Integrität einer Datenbank verletzen kann. Alle diese Fälle werden mit Hilfe der Beispieldatenbank gezeigt und erklärt. 1) INSERT INTO arbeiten(m_nr,...) VALUES (1111,...); Mit der INSERT-Anweisung wird ein Wert in der Spalte m_nr der Tabelle arbeiten eingefügt, zu welchem dann kein entsprechender Wert in der Spalte m_nr der Tabelle mitarbeiter existiert. 2) UPDATE arbeiten SET m_nr=11111 WHERE ......; Mit der UPDATE-Anweisung wird ein existierender Wert in der Spalte m_nr der Tabelle arbeiten durch einen anderen ersetzt, zu welchem dann kein entsprechender Wert in der Spalte m_nr der Tabelle mitarbeiter existiert. 3) UPDATE mitarbeiter SET m_nr=22222 WHERE m_nr=10102; Mit der UPDATE-Anweisung wird jetzt der Wert im Primärschlüssel der Tabelle mitarbeiter durch einen neuen Wert ersetzt. Dadurch entstehen überschüssige Werte in der Spalte m_nr der Tabelle arbeiten. 4) DELETE FROM mitarbeiter WHERE m_nr=10102; Dieser Fall ist dem vorigen ähnlich. Durch das Löschen der Reihe in der Tabelle mitarbeiter entstehen überschüssige Werte in der Spalte m_nr der Tabelle arbeiten. Um die referentielle Integrität in einem Datenbanksystem zu unterstützen, können verschiedene Ansätze gewählt werden. Eine Möglichkeit ist, die Definition des Primärund Fremdschlüssels explizit mit Hilfe der CREATE TABLE-Anwesiung zu unterstützen. Die zweite Möglichkeit ist, dem Benutzer die Mittel zu geben, die referentielle Integrität selbst zu implementieren. Von den beiden Alternativen ist die erste aus zwei Gründen zu bevorzugen. Erstens ist sie in dem kommenden SQL-Standard (SQL2) beschrieben und dadurch für alle Datenbanksysteme einheitlich festgelegt. Zweitens muß der Benutzer in diesem -256-
Fall nichts machen; die Implementierung ist durch das System gewährleistet. INGRES unterstüzt mit den benutzerdefinierten Regel des Knowledge Management-Moduls die zweite Möglichkeit. Es ist zu hoffen, daß eine der nächsten INGRES-Versionen auch die Definition des Primär- und Fremdschlüssels und damit die explizite Unterstützung der referentiellen Integrität enthalten wird. Genauso wie es verschiedene Ansätze für die Lösung der referentiellen Integrität gibt, gibt es verschiedene Maßnahmen, um die referentielle Integrität einer Datenbank beim Einfügen, Löschen und Ändern der Reihen aufrechtzuerhalten. Diese Maßnahmen können am besten durch folgende Begriffe definiert werden: - Restriktion ("Restrict") - Setze auf NULL ("Set NULL") und - Kaskade ("Cascade") . Die Restriktion erlaubt das Löschen bzw. das Ändern nur solchen Reihen der Zieltabelle, deren Werte im Primärschlüssel über keine entsprechenden Werte im Fremdschlüssel der referenzierten Tabelle verfügen. Die Implementierung dieser Maßnahme mit Hilfe von Regeln bedeutet, daß die Anweisung, die die Regel aktiviert hat, zurückgesetzt werden muß, falls die Integrität der Daten verletzt ist. Diese Maßnahme kann bei INGRES mit Hilfe der RAISE ERROR-Anweisung erreicht werden. Die Maßnahme Setze auf NULL erlaubt das Löschen bzw. das Ändern aller Reihen der Zieltabelle, wobei entsprechenden Datenwerten der Spalten, die den Fremdschlüssel in der referenzierten Tabelle bilden, der NULL-Wert zugewiesen wird. Hinweis. Die Maßnahme Setze auf NULL kann erweitert werden in dem Sinne, daß alle Datenwerte der Spalten, die den Fremdschlüssel in der referenzierten Tabelle bilden, ein vordefinierter Datenwert zugewiesen wird. Die Kaskade kann sich entweder auf das Einfügen der Werte im Fremdschlüssel oder auf das Ändern bzw. das Löschen der Werte im Primärschlüssel beziehen. Im ersten Fall werden für jeden neueingefügten Datenwert im Fremdschlüssel eine zusätzliche Reihe in der Zieltabelle, die diesen Wert als Primärschlüssel hat, eingefügt. Im zweiten Fall werden für alle gelöschten bzw. geänderten Datenwerte des Primärschlüssels die Reihen der referenzierten Tabelle, die diesen Datenwert als Fremdschlüssel enthalten, gelöscht bzw. geändert. Beispiel 20.3 CREATE PROCEDURE loesche_mnr (nummer=INTEGER) AS DECLARE meldung=CHAR(80) NOT NULL; BEGIN -257-
meldung = 'Loeschen der Reihen mit der M_nr "' + :nummer + '"'; MESSAGE :me ldung; DELETE FROM arbeiten WHERE m_nr=:nummer; IF iirowcount > 0 THEN Meldung='Insgesamt sind ' + VARCHAR(iirowcount) + + ' Reihen geloescht'; ELSE meldung='Keine Reihe geloescht'; ENDIF MESSAGE :meldung; END; CREATE RULE loesche_mitarb AFTER DELETE FROM mitarbeiter EXECUTE PROCEDURE loesche_mnr (nummer=old.m_nr); In Beispiel 20.3 ist eine Regel erstellt worden, die die referentielle Integrität zwischen den Tabellen mitarbeiter und arbeiten in Form einer Kaskade aufrechterhält. Jedesmal wenn eine Reihe der Tabelle mitarbeiter gelöscht wird, wird die Regel loesche_mitarb aktiviert, die ihrerseits die DB-Prozedur loesche_mnr aufruft. Die DB-Prozedur loesche_mnr löscht alle Reihen der Tabelle arbeiten, die dieselbe Mitarbeiternummer wie die gelöschte Reihe in der Tabelle mitarbeiter haben. Mit der lokalen Variablen meldung wird die Anzahl der gelöschten Reihen der Tabelle arbeiten am Bildschirm ausgegeben.
19.2 Erweiterter Autorisierungsmechanismus Der Knowledge Management-Modul unterstützt neben der Erstellung von Regeln auch einen erweiterten Autorisierungsmechanismus, mit dem Zugriffsrechte für Datenbanken, ausgewählte Benutzergruppen und Anwendungen vergeben bzw. entzogen werden können. 19.2.1 Gruppen- und Prozedur-Zugriffsrechte Die in Kapitel 15 beschriebene GRANT-Anweisung ermöglicht die Vergabe der Tabellen-Zugriffsrechte entweder an einzelne oder an alle Benutzer eines INGRES-Systems. Dieser Weg ist aufwendig, wenn man einer großen Benutzergruppe die Zugriffsrechte vergeben wird. Mit der Anweisung CREATE GROUP ist es möglich, einer im voraus erstellten Benutzergruppe einen Namen zu geben. Die ser Name kann anschließend in der GRANT-Anweisung verwendet werden, um auf eine einfache Weise allen Benutzern dieser Gruppe gewünschte Zugriffsrechte zu vergeben. Die Anweisung CREATE GROUP hat folgende Form CREATE GROUP gruppen_name [WITH USERS=(ben_1 [,ben_2,...])]; gruppen_name kennzeichnet den Namen der Benutzergruppe. -258-
Die Information über alle mit der CREATE GROUP-Anweisung definierten Gruppennamen werden in der Systemtabelle iiusergroup abgelegt. ben_1, ben_2,... stellen die Namen der Benutzer dar, die zu der mit gruppen_name definierten Benutzergruppe gehören. Falls die Angabe WITH USERS ausgelassen wird, können Benutzer mit Hilfe der ALTER GROUP-Anweisung nachträglich definiert werden. Die CREATE GROUP-Anweisung kann nur der INGRES-Systemverwalter dann ausführen, wenn er in einer Sitzung arbeitet, die mit der INGRES-Master-Datenbank iidbdb verbunden ist. Beispiel 20.4 CREATE GROUP vertrieb WITH USERS = (meier,mueller,berger,kunz); In Beispiel 20.4 wird eine Benutzergruppe namens vertrieb definiert, zu der die Benutzer meier, mueller, berger und kunz gehören. Mit der Anweisung ALTER GROUP gruppen_name ADD USERS (ben_1 [,ben_2,...]) | DROP USERS (ben_3 [,ben_4,...]) | DROP ALL; kann eine Benutzergruppe modifiziert werden. gruppen_name muß ein existierender Name einer Benutzergruppe sein. Mit der Angabe ADD USERS können neue Benutzer in die Benutzergruppe aufgenommen werden. Die DROP USERS-Angabe dagegen löscht einen oder mehrere Benutzer, die einer Benutzergruppe angehören. Die DROP ALL-Angabe löscht alle Benutzer einer Benutzergruppe. Diese Angabe ist notwendig, falls ein existierender Gruppenname anschließend mit der DROP GROUP-Anweisung gelöscht werden soll. In einer ALTER GROUP-Anweisung können nicht gleichzeitig Benutzer gelöscht und neue hinzugefügt werden. Nur der INGRES-Systemverwalter, der in einer mit iidbdb verbundenen INGRES-Sitzung abeitet, kann die Anweisung ALTER GROUP ausführen. Mit der Anweisung DROP GROUP gruppen_name1 [,gruppen_name2,...]; kann ein oder mehrere Gruppennamen gelöscht werden. Die Voraussetzung für das Löschen eines Gruppennamens ist, daß die zugewiesene Benutzergruppe leer ist. (Das Löschen aller Benutzer einer Benutzergruppe kann mit der Anweisung DROP GROUP ausgeführt werden.) Die zweite Gruppe der SQL-Anweisungen, die der Knowledge Management-Modul unterstützt, regelt das Erstellen, Modifizieren und Löschen von Identifikatoren, die im Zusammenhang mi den Zugriffsrechten für INGRES-Anwendungen stehen. Mit der Anweisung CREATE ROLE identifikator WITH NOPASSWORD | PASSWORD=kennwort; kann ein Identifikator definiert werden, der später, mit -259-
Hilfe der erweiterten GRANT-Anwesiung benutzt werden kann, um Zugriffsrechte für INGRES-Anwendungen zu vergeben. Nachdem ein solcher Identifikator definiert ist und die Zugriffsrechte vergeben sind, kann er u.a. mit der SQL-Anweisung CONNECT verwendet werden, um die vergebenen Zugriffsrechte innerhalb einer INGRES-Sitzung zu aktivieren. Die Angabe PASSWORD=kennwort definiert das Kennwort für den angegebenen Identifikator. Das Kennwort muß anschließend in der CONNECT-Anweisung explizit angegeben werden, um die vergebenen Zugriffssrechte zu aktivieren. Bei der NOPASSWORD-Angabe wird kein Kennwort definiert. Dadurch kann jede INGRES-Sitzung den Zugriff auf den definierten Identifikator und damit auch auf die mit ihm verbundenen Zugrifffsrechte haben. Beispiel 20.5 CREATE ROLE mitarb_neu WITH PASSWORD='axbycz'; Mit der Anweisung ALTER ROLE identifikator WITH NOPASSWORD | PASSWORD=neu_kennwort; kann ein existierendes Kennwort, das im Zusammenhang mit dem Identifikator identifikator steht, geändert (PASSWORD= neu_kennwort) bzw. gelöscht (NOPASSWORD) werden. Falls das Kennwort gelöscht wird, kann jede INGRES-Sitzung den Zugriff auf den Identifikator und damit auf die mit ihm verbundenen Zugriffsfrechte haben. Mit der Anweisung DROP ROLE ident_1 [, ident_2,...]; werden die schon definierten Identifikatoren ident_1, ident_2,... gelöscht. Für Anweisungen CREATE ROLE, ALTER ROLE und DROP ROLE gilt generell, daß nur der INGRES-Systemverwalter sie ausführen kann, wenn er in einer Sitzung arbeitet, die mit iidbdb verbunden ist. 19.2.2 Die erweiterte GRANT-Anweisung und die REVOKE-Anweisung Abgesehen von der in Kapitel 15 beschriebenen GRANT-Anweisung, die im allgemeinen gilt, unterstützt der Knowledge management-Modul eine gleichnamige SQL-Anweisung, die eine erweiterte Funktionalität hat. Die erweiterte GRANT-Anweisung regelt zusätzlich zu der Vergabe der Tabellen- und Prozeduren- auch die Vergabe der Datenbank-Zugriffsrechte. Die erweiterte GRANT-Anweisung hat folgende Syntax GRANT recht_1 [recht_2,...] ON [obj_typ] obj_name1 [,obj_name2,...] TO PUBLIC | [auto_typ] id_1 [,id_2,...]; obj_typ kennzeichnet den Objekttyp, für den die Zugriffsrechte recht_1, recht_2,... vergeben wurden. Dieser Objekttyp kann entweder eine Tabelle, oder eine DB-Prozedur oder eine Datenbank sein. Falls der Objekttyp ausgelassen -260-
wird, wird standardmäßig Tabelle angenommen. Abhängig vom Objekttyp kennzeichnet obj_name1, obj_name2,... Tabellen-, DB-Prozedur- oder Datenbanknamen. Alle Objektnamen einer erweiterten GRANT-Anweisung müssen demselben Objekttyp angehören. Die TO-Klausel identifiziert den Benutzer, dem die Zugriffsrechte vergeben wurden. PUBLIC kennzeichnet alle Benutzer eines INGRES-Systems. auto_typ spezifiziert den Autorisierungstyp, der - USER, - GROUP oder - ROLE sein kann. id_1, id_2,... charakterisieren die Identifikatoren jeweiligen Autorisierungstyps. Wenn z.B. eine erweiterte GRANT-Anweisung den Autorisierungstyp GROUP hat, müssen die Identifikatoren Gruppennamen sein. Falls auto_typ ausgelasen wird, wird standardmäßig USER als Autorisierungstyp und Benutzernamen als Identifikatoren angenommen. recht_1, recht_2,... kennzeichnen die vergebenen Zugriffsrechte, die sich, abhängig vom Objekttyp unterscheiden. Die möglichen Tabellen-Zugriffsrechte - SELECT, - UPDATE, - INSERT, - DELETE und - ALL sind mit allen ihren Eigenschaften in Kapitel 15 beschrieben. Bei den DB-Prozeduren existiert nur ein Zugriffsrecht - EXECUTE, mit dem einem oder mehreren Benutzern die Möglichkeit gegeben wird, die genannte DB-Prozedur auszuführen. (Standardmäßig darf nur der Eigentümer die DB-Prozedur ausführen.) Die Datenbank- Zugriffsrechte werden vom DBA oder dem INGRES-Systemverwalter vergeben. Mit ihnen ist es möglich, den Zugriff von Benutzern auf Tabellen, DB-Prozeduren oder anderen Datenbank-Ressourcen zu steuern. Zu den Datenbank-Zugriffsrechten gehören - [NO]QUERY_IO_LIMIT, - [NO]QUERY_ROW_LIMIT, - [NO]CREATE_TABLE, - [NO]CREATE_PROCEDURE, - [NO]LOCKMODE und - [NO]DB_ADMIN. Das Datenbank_Zugriffsrecht QUERY_IO_LIMIT definiert die maximal erlaubte Anzahl von E/A-Operationen für eine Abfrage. Dementsprechend kennzeichnet QUERY_ROW_LIMIT die maximal erlaubte Anzahl der ausgewählten Reihen einer Abfrage. NOQUERY_IO_LIMIT bzw. NOQUERY_ROW_LIMIT sind die -261-
Voreinstellungen, die keine Einschränkungen bezüglich der E/A-Operationen bzw. der Reihenanzahl bei einer Abfrage setzen. Beispiel 20.6 GRANT QUERY_ROW_LIMIT 2000 ON DATABASE beispiel, vertrieb TO USER peter, paul, mary; In Beispiel 20.6 ist die Reihenanzahl jeder Abfrage auf die Datenbanken beispiel und vertrieb für die Benutzer peter, paul und mary auf 2000 beschränkt. (Die Einschränkung wird generell schon als erfüllt betrachtet, falls der optimale Ausführungsplan des Optimierers für diese Abfrage eine größere als die maximal zulässige Anzahl vorhersagt.) Das Datenbank-Zugriffsrecht CREATE_TABLE ermöglicht die gezielte Vergabe der Erstellungsrechte für Tabellen einer Datenbank. Standardmäßig können alle Benutzer (PUBLIC) eine Tabelle erstellen. Um dies zu verhindern, empfiehlt es sich, zuerst die Anweisung GRANT NOCREATE_TABLE ON DATABASE db_name TO PUBLIC; auszuführen und damit allen Benutzern die Erstellung von Tabellen für die Datenbank db_name zu verbieten. Anschließend muß mit einer zweiten GRANT-Anweisung gezielt gewissen Benutzern, Gruppen oder Anwendungen das Zugriffsrecht CREATE_TABLE vergeben werden. CREATE_PROCEDURE erlaubt die Vergabe der Erstellungsrechte für Prozeduren einer Datenbank. Standardmäßig können alle Benutzer (PUBLIC) DB-Prozeduren erstellen. Um dies zu verhindern, kann genauso vorgegangen werden wie beim CREATE_TABLE-Zugriffsrecht. Das Zugriffsrecht LOCKMODE spezifiziert, wer die SQL-Anweisung SET LOCKMODE verwenden darf. Standardmäßig können alle Benutzer diese Anweisung benutzen. Die Vergabe des DB_ADMIN-Zugriffsrechtes an einen Benutzer bedeutet, daß er - alle DB-Rechte für die angegebene Datenbank hat; - die INGRES-Systemtabellen ändern darf und - den Schalter -u verwenden darf (d.h. auf die angegebene Datenbank als ein anderer Benutzer zugreifen darf.) Beispiel 20.7 GRANT EXECUTE ON PROCEDURE bericht TO PUBLIC; In Beispiel 20.7 wird die Ausführung der DB-Prozedur bericht allen INGRES-Benutzern zugänglich gemacht. Mit der Anweisung REVOKE können die mit der erweiterten GRANT-Anweisung vergebene Datenbank-Zugriffsrechte wieder entzogen werden. Diese Anweisung hat folgende Form REVOKE recht_1 [,recht_2,...] ON DATABASE db_name1 [,db_name2,...] FROM PUBLIC | [auto_typ] id_1 [,id_2,...]; Die REVOKE-Anweisung hat dieselbe Funktion für die erweiterte GRANT-Anweisung wie DROP PERMIT für die -262-
allgemeine GRANT-Anweisung. Beispiel 20.8 REVOKE CREATE_TABLE ON DATABASE beispiel FROM USER a n j a, u w e; Mit der REVOKE-Anweisung in Beispiel 20.8 wird das Datenbank-Zugriffsrecht CREATE_TABLE den Benutzern anja und uwe entzogen. (In diesem Fall wird angenommen, daß dieses Zugriffsrecht den beiden Benutzern mit der erweiterten GRANT-Anweisung schon vorher explizit vergeben wurde.)
-263-
Verteilte Datenbanken bei INGRES
Verteilte Datenbanken bei INGRES ........................................ Fehler! Textmarke nicht definiert. 21
Verteilte Datenbanken bei INGRES ...................................................................................264 21.1 Einleitung 264 21.2 INGRES/NET 265 21.2.1 GCA 265 21.2.2 Zugriff auf eine ferne INGRES-Datenbank mit INGRES/NET 21.3 INGRES/STAR 267 21.3.1 Zwei-Phasen-Commit 269 21.4 Gateways 270
20 Verteilte Datenbanken bei INGRES Zur INGRES-Produktpalette gehören drei Produkte, die verteilte Datenbanken unterstützen: INGRES/NET, INGRES/STAR und INGRES Gateways. Zuerst werden wir INGRES/NET beschreiben, das einen verteilten Zugriff auf Datenbanken ermöglicht. Danach wird INGRES/STAR erörtert, das tatsächlich verteilte Datenbanken unterstützt. Im Zusammenhang mit INGRES/STAR wird gezeigt, wie Datenbankobjekte (Tabellen und Views) Teile eines vernetzten Systems werden können. Außerdem wird das Zwei-Phasen-Commit beschrieben. Am Ende des Kapitels werden Eigenschaften von Gateways erörtert, die die Verbindung zwischen heterogenen Datenbanken ermöglichen.
20.1 Einleitung Der Begriff der verteilten Datenbanken ist ein allgemeiner Begriff, dessen genaue Bedeutung erst durch zusätzliche Erklärungen eingeordnet werden kann. Dieser Begriff wird für drei voneinander unterschiedliche Typen vo n Datenbanken benutzt. Der erste Typ stellt den sogenannten verteilten Zugriff dar. Bei dem verteilten Zugriff befindet sich der Datenbank-Server auf einem Rechner, während INGRES-DB-Anwendungen (RBF, QBF usw.) auf anderen Rechnern im Netz liegen. Der verteilte Zugriff bei INGRES wird durch das Produkt INGRES/NET unterstützt. Die tatsächlich verteilten Datenbanken, die bei INGRES mit dem Produkt INGRES/STAR realisiert sind, haben die Eigenschaft, dem Anwender den Eindruck zu vermitteln, mit einer einzigen Datenbank an einem Rechner zu arbeiten, -264-
266
obwohl die Daten durchaus auf verschiedene Rechner im Netz verteilt werden können. Den dritten Typ stellen die sogenannten heterogenen Datenbanken dar. Diesen Typ kennzeichnet eine andere Verteilungsart von Datenbanken. Das heterogene System beinhaltet die Datenbanken, die sich auf unterschiedlichen Rechnern befinden und mit DB-Produkten verschiedener Hersteller erzeugt wurden. INGRES Gateways unterstützen die Verbindung zwischen den INGRES-Datenbanken einerseits und relationalen bzw. nicht relationalen Datenbanken anderer Hersteller andererseits. Im Vergleich mit anderen relationalen Datenbanksystemen hat INGRES einen technologischen Vorsprung besonders bei den verteilten Datenbanken. Sowohl mit INGRES/NET (1983) als auch mit INGRES/STAR (1986) war INGRES die erste Firma überhaupt, die diese beiden Technologien entwickelt und freigegeben hat. Dasselbe gilt für die Unterstützung der heterogenen Datenbanken mit Hilfe von Gateways. In den bisherigen Kapiteln des Buches haben wir immer angenommen, daß die verwendete Datenbank lokal ist, d.h. Datenbank-Anwendungen, Datenbank-Server und Datenbank selbst befinden sich auf einem Rechner. In den nachfolgenden Abschnitten wird das nicht mehr der Fall sein.
20.2 INGRES/NET INGRES/NET ermöglicht den Anwendern, von einem Rechner auf eine Datenbank, die auf einem anderen, im Netz befindlichen Rechner gespeichert ist, zuzugreifen. Dieser Zugriff verläuft transparent, d.h. der Anwender hat den Eindruck, mit einer lokalen Datenbank zu arbeiten. Abbildung 21-1 zeigt die INGRES/NET-Architektur.
DBAnwendung
DBAnwendung
DBAnwendung
INGRES/NE T
INGRES/NE T
INGRES/NE T
Abb. 21-1 INGRES/NET-Architektur INGRES/NE T 20.2.1 GCA GCA ("GCF Application Interface") DB-Serverist eine Schnittstelle, die auf GCF ("General Communication Facility") basiert und den INGRES-DB-Anwendungen einerseits und INGRES-DB-Servern andererseits die Kommunikation miteinander ermöglicht. Dabei Datenbank können Datenbank-Server und Datenbank-Anwendung sich auf -265-
einem oder auf unterschiedlichen Rechnern im Netz befinden. Die Kommunikation zwischen einer Datenbank-Anwendung und einem Datenbank-Server besteht aus zwei Schritten: Zuerst muß die Datenbank-Anwendung den Datenbank-Server ausfindig machen, und erst dann findet die Kommunikation zwischen den beiden statt. Für jeder den oben genannten Schritte existiert ein Server: Der Namens-Server für das Auffinden von Datenbank-Servern und der Kommunikations-Server für die Herstellung der Kommunikation. Wenn eine Datenbank-Anwendung auf eine Datenbank zugreift, muß zuerst mit Hilfe des Namens-Servers, der Datenbank-Server, der diese Datenbank verwaltet, gefunden werden. Dieses Verfahren gilt auch für den Fall, daß sich der Datenbank-Server und die Datenbank-Anwendung auf einem Rechner befinden. Im Unterschied zum Namens-Server wird der Kommunikations-Server nur dann aktiv, falls sich die DB-Anwendung und der DB-Server nicht auf einem Rechner befinden. Der Kommunikations-Server ist die wichtigste Komponente von INGRES/NET. Er ermöglicht den Zugriff auf Standard-Protokolle für Rechnernetze. Die Implementierung des Kommunikations-Servers enthält die obersten vier Schichten des ISO-Standard Protokolls für Rechnernetze: - die Applikations-Schicht, - die Präsentations-Schicht, - die Sitzungs-Schicht und - die Transport-Schicht. Die Transport-Schicht des Kommunikations-Servers enthält GCF-Funktionen, die abhängig vom Netztyp (TCP/IP, DECnet usw.) sind. Die Sitzungs-Schicht verwaltet die Eigenschaften der einzelnen Sitzungen, während die Präsentations-Schicht die Datenumwandlung durchführt, falls sich Daten nicht in demselben Format wie Daten des gegewärtigen Rechners befinden. Die oberste Schicht des Kommunikations-Servers die Applikations-Schicht - ist die GCA. 20.2.2 Zugriff auf eine ferne INGRES-Datenbank mit INGRES/NET Um auf eine INGRES-Datenbank, die sich auf einem anderen Rechner im Netz befindet, zuzugreifen, müssen folgende allgemeine Voraussetzungen erfüllt werden: - INGRES/NET muß sowohl auf dem lokalen als auch auf dem fernen Rechner installiert werden; - mit Hilfe des Dienstprogramms netu muß der INGRES-Systemadministrator den fernen Rechner, wo sich die Datenbank befindet, definieren. Der Zugriff auf eine ferne Datenbank setzt einige weitere Bedingungen voraus: - der Benutzer muß eine Kennung auf dem fernen Rechner haben bzw. die öffentliche Kennung des fernen Rechners benutzen können; -266-
- mit Hilfe des Dienstprogramms netu muß die Autorisierung für die existierend e bzw. öffentliche Kennung definiert werden. Die Syntax eines Kommandos für den Zugriff auf eine ferne Datenbank ist kommando [schalter_liste] knoten::db_name kommando ist ein Betriebssystemkommando für den Aufruf eines INGRES-Dienstprogramms (qbf, rbf usw.). schalter_liste ist eine Liste von Schaltern, die für das angegebene Kommando erlaubt sind. knoten kennzeichnet den Namen des fernen Rechners, auf dem sich die Datenbank db_name befindet. Beispiel 21.1 ingmenu ararat::beispiel In Beispiel 21.1 wird mit dem Dienstprogramm ingmenu auf die Datenbank beispiel, die sich auf dem fernen Rechner ararat befindet zugegriffen. Die Verwendung von INGRES/NET bringt einige Einschränkungen bezüglich der INGRES-Dienstprogramme mit sich. Folgende Dienstprogramme dürfen u.a. für die fernen Datenbanken nicht benutzt werden: - accessdb, - createdb, - destroydb und - rollforwarddb.
20.3 INGRES/STAR Eine generelle Eigenschaft von INGRES ist, daß immer eine Sammlung von Tabellen als eine Datenbank betrachtet wird. Dem Benutzer ist es erlaubt nur Objekte einer einzigen Datenbank zu einem Zeitpunkt bearbeiten zu könne n. INGRES/STAR hebt diese Einschränkung auf; der Benutzer hat mit Hilfe von INGRES/STAR die Möglichkeit, gleichzeitig auf mehrere Datenbanken zuzugreifen. Damit erscheinen alle Datenbanken, die sich im Netz befinden als eine einzige Datenbank. Die Funktionalität von INGRES/STAR ermöglicht damit den gleichzeitigen Zugriff auf mehrere Datenbanken. Um aber auf andere Datenbanken im Netz zugreifen zu können, muß INGRES/NET verwendet werden. Damit braucht man sowohl INGRES/NET als auch INGRES/STAR für den gleichzeitigen Zugriff auf mehrere Datenbanken im Netz (Abb. 21-2).
DBAnwendung
DBAnwendung
DBAnwendung
INGRES/NE T
INGRES/NE T
INGRES/NE T
-267-
INGRES/NE T
Abb. 21-2 INGRES/STAR-Architektur In einer INGRES/STAR-Umgebung existieren insgesamt drei Arten von Datenbanken: - lokale Datenbanken, - die verteilte Datenbank und - die Koordinations-Datenbank. Lokale Datenbanken sind alle herkömmlichen INGRES-Datenbanken. Die verteilte Datenbank ist die Menge aller Datenbanken, die dem Benutzer als eine Datenbank erscheint. Jede verteilte Datenbank enthält mehrere lokale Datenbanken, die jede für sich auch Teil mehrerer verteilten Datenbanken sein können. Die Koordinations-Datenbank ist eine spezifische Datenbank der INGRES/STAR-Umgebung, die INGRES/STAR-Kataloge enthält. INGRES/STAR hat weder den direkten Zugriff auf lokale Datenbanken noch verwaltet sie sie direkt. Alle Zugriffe werden über den lokalen DB-Server durchgeführt, der auch für die Verwaltung der Datenbank(en) zuständig ist. Mit dem INGRES-Dienstprogramm createdb ist es möglich, eine verteilte Datenbank zu erzeugen. Dieses Dienstprogramm erstellt eine leere verteilte Datenbank, der dann Objekte (Tabellen, Views) folgendermaßen zugewiesen werden können: - durch die Eintragung von Objekten, die in einer lokalen Datenbank schon existieren; - durch die Erstellung neuer Objekte auf der INGRES/STAR-Ebene, die dann automatisch in der verteilten Datenbank eingetragen werden; - durch die Erstellung neuer Objekte in einer lokalen Umgebung, die anschließend in der verteilten Datenbank eingetragen werden (können aber nicht müssen). Aus der obigen Diskussion folgt, daß nicht jedes Objekt einer lokalen Datenbank Teil einer verteilten Datenbank sein muß. Dies ist vorteilhaft, weil damit Objekte einer lokalen Datenbank privat gehalten werden können. Die Eintragung eines existierenden Objektes einer lokalen Datenbank in der verteilten Datenbank wird mit der Anweisung REGISTER AS LINK durchgeführt. Diese Anweisung hat folgende Syntax: REGISTER objekt_typ objekt_name [(sp_1 [,sp_2,...])] AS LINK [FROM [ben_name.]lokal_objname] [WITH [NODE=knoten_name, DATABASE=db_name] [,DBMS=server_name]]; objekt_typ kann entweder das Schlüsselwort TABLE (für eine Tabelle) oder VIEW (für ein View) sein, während objekt_name der Aliasname des Objektes in der INGRES/STAR-Umgebung ist. ben_name ist der Name des Eigentümers des Objektes lokal_objname . knoten_name kennzeichnet den Namen des -268-
Rechners, auf dem sich die Datenbank db_name befindet, während server_name entweder die Zeichenkette "INGRES Gateways" (für nicht INGRES-Datenbanken) oder "INGRES" sein kann. Beispiel 21.2 REGISTER TABLE arb AS LINK FROM peter.arbeiten WITH NODE=montblanc, DATABASE=beispiel; In Beispiel 21.2 wird die Tabelle arbeiten, die sich in der lokalen Datenbank beispiel und auf dem fernen Rechner montblanc befindet, in der verteilten Datenbank unter dem Namen arb aufgenommen. 20.3.1 Zwei-Phasen-Commit Zwei-Phasen-Commit ist ein Mechanismus, der seit der Version 6.3 von INGRES unterstützt wird. Dieser Mechanismus gewährleistet, daß Datenbanken in einer verteilten Umgebung nach einer Transaktion konsistent bleiben. Zwei-Phasen-Commit wird verwendet, wenn innerhalb einer Transaktion Änderungen an zwei oder mehreren im Netz befindlichen Datenbanken durchgeführt werden. Zwei-Phasen-Commit arbeitet in vier Schritten, die in zwei Gruppen (Phasen) unterteilt sind. Der erste Schritt wird vom koordinierenden Programm veranlaßt, indem überprüft wird, ob jede lokale Datenbank ihr zugehörige Anweisungen ausführen kann. Im zweiten Schritt sichert jede lokale Datenbank die für die Anweisung notwendigen Ressourcen und schickt anschließend die Bestätigung an das koordinierende Programm zurück. Die erste Phase wird abgeschlossen, wenn alle betreffenden lokalen Datenbanken die Bestätigung zurückgeschickt haben. Im dritten Schritt veranlaßt das koordinierende Programm die Durchführung jeder Anweisung in den entsprechenden lokalen Datenbanken. Im vierten Schritt wird die Ausführung der Anweisungen von jeder lokalen Datenbank bestätigt. Nur wenn alle lokalen Datenbanken die Ausführung der Anweisungen bestätigt haben, wird die zweite Phase als erfolgreich abgeschlossen betrachtet. In der zweiten Phase kann es vorkommen, daß die Anweisung für eine lokale Datenbank nicht ausgeführt werden kann. In diesem Fall wird eine entsprechende Meldung an das koordinierende Programm geschickt, das anschließend das Zurücksetzen aller übrigen Anweisungen der zu bearbeitenden Transaktion veranlaßt. Damit sorgt das koordinierende Programm dafür, daß entweder alle Anweisungen einer Transaktion ausgeführt oder zurückgesetzt werden. INGRES bietet zwei Anweisungen - PREPARE TO COMMIT und - CONNECT, die der eingebetteten SQL-Sprache angehören und das -269-
Zwei-Phasen-Commit unterstützen. Diese beiden Anweisungen entsprechen jeweils der beiden Phasen des ZweiPhasen-Commit: Die Anweisung PREPARE TO COMMIT realisiert die erste und die Anweisung CONNECT die zweite Phase. Das koordinierende Programm muß vom Benutzer selbst geschrieben werden. Alle Einzelheiten, die das Schreiben eines spezifischen Koordinationsprogramms betreffen, finden Sie in den entsprechenden INGRES-Manualen.
20.4 Gateways INGRES Gateways sind Schnittstellen zwischen INGRES-Anwendungen einerseits und nicht INGRES-Datenbanken andererseits. Mit INGRES Gateways kann ein Benutzer das ganze Spektrum von INGRES-Applikationen (DB-Anwendungen, Benutzer-Applikationen usw.) verwenden, um auf Daten einer nicht INGRES-Datenbank zuzugreifen. Gateways existieren zu den folgenden fremden DB-Systemen: - Rdb, - RMS, - DB2 - SQL/DS, - IMS, - VSAM-Dateien, - dBASE III (auf dem PC) und - DBC/1012 von Teradata. INGRES Gateways können, abhängig davon, ob das fremde Datenbanksystem die SQL-Sprache unterstützt oder nicht in - SQL-Gateways und - nicht SQL-Gateways unterteilt werden. SQL Gateways verbinden ein relationales fremdes DB-System mit den INGRES-front-ends. Merkmal von SQL-Gateways ist, daß abgesehen vom Gateway selbst nur noch das front-end von INGRES verwendet wird. D.h. die Verwaltung und der Zugriff auf nicht INGRES-Daten erfolgt durch das fremde DBMS. Weil SQL-Implementierungen auf unterschiedlichen DB-Systemen verschieden sind, unterstützt INGRES-SQL-Gateways die Sprache Open SQL, die eine Untermenge von SQL ist. Mit Open SQL ist es möglich, INGRES-DB-Anwendungen und INGRES-Benutzer-Applikationen für die Manipulation von Daten fremder relationaler DB-Systeme zu benutzen. Nicht-SQL-Gateways ermöglichen den Zugriff auf nicht relationale DBMS. Im Unterschied zu SQL-Gateways benötigen nicht-SQL-Gateways sowohl INGRES- front end als auch das INGRES-DBMS. Nicht-SQL-Gateway ist ein Teil des INGRES-DBMS. Der Zugriff auf Daten erfolgt direkt, ohne Unterstützung eines fremden DBMS.
-270-