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!
Inhaltsverzeichnis Einleitung Voraussetzungen Über dieses Buch In diesem Buch verwendete Konventionen Notation für die Darstellung von Syntax
Woche 1 Vorschau Tag 1 Die Welt relationaler Datenbanken erforschen 1.1 Structured Query Language (SQL) 1.2 Die Oracle-Produktreihe 1.3 Weitere Oracle-Produkte 1.4 Was bedeutet Client-Server-Datenverarbeitung? 1.5 Die Ursprünge der Client-Server-Datenverarbeitung Die Geburtsstunde des PC PC-Datenbanken LAN-Datenbanken Die Entstehung relationaler Datenbanken Middleware Werkzeuge für die Anwendungsentwicklung »Thick«-Client und »Thin«-Client 1.6 Die Herausforderung an die Client-Server-Datenverarbeitung: der Network Computer 1.7 CORBA 1.8 Zusammenfassung 1.9 Wie geht es weiter? 1.10 Fragen und Antworten 1.11 Workshop Test
Übungen
Tag 2 Richtlinien für die Entwicklung einer Oracle-Anwendung 2.1 Anwendungsentwicklung Schritt für Schritt Sammeln von Anforderungen Entwicklung und Implementierung Testen der Anwendung Inbetriebnahme der Anwendung Pflege der Anwendung 2.2 Zusammenfassung 2.3 Wie geht es weiter? 2.4 Fragen und Antworten 2.5 Workshop Test Übungen
Tag 3 Logischer Datenbankentwurf 3.1 Theorie relationaler Datenbanken Eine Tabelle besteht aus Zeilen und Spalten Die Reihenfolge der Zeilen ist beliebig Die Reihenfolge der Spalten ist beliebig 3.2 Datenintegrität Primärschlüssel Kein Teil des Primärschlüssels hat einen NULL-Wert Referentielle Integrität Beziehungen Normalisierung Normalisierungsregel 1: Jede Spalte sollte genau einen Teil der Informationen enthalten Normalisierungsregel 2: Alle Tupel hängen nur vom Primärschlüssel ab Normalisierungsregel 3: Alle Spalten hängen einzig und allein vom Primärschlüssel ab Anwenden der Normalisierung auf den Datenbankentwurf 3.3 Werkzeuge für die Erstellung von Entity-Relationship-Diagrammen 3.4 Star Schema
3.5 Das Beispielschema Anforderungen Die Student-Tabelle Die Department-Tabelle Die Instructor-Tabelle Die Course-Tabelle Die Class-Location-Tabelle Die Schedule-Type-Tabelle Die Schedule-Type-Details-Tabelle Die Class-Tabelle Die Student-Schedule-Tabelle 3.6 Terminologie zu Oracle Datenbanksitzung Datenbankbenutzer Externe Prozeduren 3.7 Oracle Enterprise Manager 3.8 Datentypen Zahlen Zeichenfolgen Datums- und Zeitinformationen Große Objekte Lange Zeichenfolgen Verwenden von CLOBs 3.9 Zusammenfassung 3.10 Wie geht es weiter? 3.11 Fragen und Antworten 3.12 Workshop Test Übungen
Tag 4 Implementierung des logischen Modells: physischer Datenbankentwurf 4.1 Grundlagen der create table-Anweisung Benennen einer Tabelle Benennen einer Spalte Beispiele für die Erstellung von Tabellen Festlegen des Primärschlüssels Festlegen von Fremdschlüsseln
4.2 Einschränken eines Spaltenwerts Wertebereiche und die Check-Integritätsbedingung Zu einer Spalte kann es mehr als eine Check-Integritätsbedingung geben In einer Check-Integritätsbedingung auf andere Spalten verweisen Pseudospalten und SQL-Funktionen in einer check-Integritätsbedingung verwenden Verzögerte Prüfung von Integritätsbedingungen Integritätsbedingungen abrufen 4.3 Festlegen eines Standardwerts für eine Spalte 4.4 Verwendung von ALTER TABLE zur Änderung der Tabellendefinition Definieren eines Primärschlüssels nach Erstellen der Tabelle Änderung einer Spaltendefinition von NOT NULL in NULL Änderung einer Spaltendefinition von NULL in NOT NULL Erweitern der Kapazität einer Spalte Verringern der Kapazität einer Spalte 4.5 Der Fremdschlüssel und referentielle Integrität Prüfung des Fremdschlüssels beim Einfügen in eine Tabelle Vereinbaren eines Fremdschlüssels nach Erstellen der Tabelle Löschen eines referenzierten Primärschlüssels Löschen eines Fremdschlüssels Deklaration von Unique-Integritätsbedingungen Unterschiede zwischen Primary Key- und Unique-Integritätsbedingungen 4.6 Tabellenindizes B-Baum Indizes Erstellen eines Index Warum man keine eindeutigen Indizes erstellen sollte 4.7 Skript für die Erstellung der Beispieltabellen 4.8 Zusammenfassung 4.9 Wie geht es weiter? 4.10 Fragen und Antworten 4.11 Workshop Test Übungen
Tag 5 Oracle Designer in der Anwendungsentwicklung
5.1 Installation des Oracle SCM und Einrichten von Benutzern 5.2 Anlegen eines Application System und Vergabe von Zugriffsrechten für den Benutzer 5.3 Erstellen eines Server-Modell-Diagramms mit dem Design Editor des Oracle Designer 5.4 Eine Tabelle dem Diagramm hinzufügen 5.5 Festlegen der Spalten für eine Tabelle 5.6 Den Primärschlüssel für die Tabelle festlegen 5.7 Einen Fremdschlüssel festlegen 5.8 Die DDL-Anweisungen für die Tabellen erstellen 5.9 Ein Datenmodell mittels »Reverse Engineering« aus einem existierenden Datenbank-Schema erzeugen 5.10 Die Grundlagen von Oracle Designer Process Modeller Entity Relationship Diagrammer Function Hierarchy Diagrammer Transformer für das Datenbank-Design und das Anwendungs-Design Modulentwurf im Design Editor 5.11 Zusammenfassung 5.12 Wie geht es weiter? 5.13 Workshop Test
Tag 6 Einführung in SQL 6.1 Abrufen und Ändern von Daten 6.2 SQL-Syntax 6.3 Syntax der select-Anweisung Eine einfache select-Anweisung Die select-Liste Von select zurückgelieferte Werte Verwendung von Ausdrücken in der select-Liste 6.4 Festlegen von Kriterien in der where-Klausel Kombinieren von Bedingungen mit AND und OR 6.5 Sortieren von Daten in der order by-Klausel 6.6 Zählen von Zeilen in einer Tabelle 6.7 Ermitteln von Zeilen mit Spaltenwert NULL
6.8 Suche nach Zeilen mit Hilfe des like-Operators 6.9 Suche nach Zeilen mit dem between-Operator 6.10 Der in-Operator 6.11 Aliasnamen für Spalten 6.12 Eine Unterabfrage verwenden Erstellen einer neuen Tabelle mit Hilfe der select-Anweisung 6.13 Zusammenfassung 6.14 Wie geht es weiter? 6.15 Fragen und Antworten 6.16 Workshop Test Übungen
Tag 7 Daten mit SQL ändern 7.1 SQL als Datenmanipulationssprache Hinzufügen von Zeilen mit Hilfe der insert-Anweisung Ändern der Daten mit Hilfe der update-Anweisung Abgleichen von Daten mit der merge-Anweisung Einen Spaltenwert auf NULL setzen Löschen von Daten mit Hilfe der delete-Anweisung Entfernen aller Zeilen mit Hilfe der truncate table-Anweisung 7.2 Transaktionen Sichern der Arbeit mit Hilfe der commit-Anweisung Rückgängig machen von Änderungen mit der rollback-Anweisung Savepoints 7.3 Nebenläufigkeit 7.4 Zusammenfassung 7.5 Wie geht es weiter? 7.6 Fragen und Antworten 7.7 Workshop Test Übungen
Woche 2 Vorschau Tag 8 SQL-Funktionen
8.1 Bearbeiten von Zeichenfolgen Eine Teilzeichenkette aus einer Zeichenkette gewinnen Einen Teil einer Zeichenkette ersetzen Entfernen führender und nachfolgender Leerzeichen in einer Zeichenfolge Auffüllen einer Zeichenkette Ändern der Groß- und Kleinschreibung Die Funktion decode Dezimal- und Hexadezimaldarstellung von Zeichenketten 8.2 Bearbeiten von Datumsangaben Das aktuelle Datum und die aktuelle Uhrzeit Datumsformate Umwandlung von Zeichenfolgen in Datumsangaben Datums- und Zeitangaben Berechnen des Zeitraums zwischen zwei Datumsangaben Speicherung von Zahlen in Oracle Umwandlung einer Zahl in eine Zeichenfolge Umwandlung einer Zahl in eine Zeichenfolge mit Hilfe der Funktion to_char Umwandlung einer Zeichenfolge in eine Zahl Runden und Abschneiden von Zahlen Ermitteln des größten und des kleinsten Werts Ermitteln, ob ein Wert NULL ist 8.3 Zusammenfassung 8.4 Wie geht es weiter? 8.5 Fragen und Antworten 8.6 Workshop Test Übungen
Tag 9 Fortgeschrittene Abfragen mit SQL 9.1 SELECT FOR UPDATE 9.2 Gruppenfunktionen Die Funktion count Die Funktionen avg und sum Kombinieren von Gruppenfunktionen mit anderen Spalten 9.3 Gruppieren von Zeilen 9.4 Nach eindeutigen Zeilen suchen
9.5 Unterabfrage und FROM-Klausel 9.6 Der Umgang mit hierarchischen Informationen 9.7 Verwenden des exists-Operators 9.8 Die Join-Operation Ein einfacher Join mit zwei Tabellen Mehrdeutige Spalten Vorsicht vor dem kartesischen Produkt Verknüpfung von mehr als zwei Tabellen Self-Joins Outer-Join 9.9 Verwendung von Mengenoperatoren in einer select-Anweisung Der intersect-Operator Der union-Operator Der minus-Operator 9.10 Datensichten verwenden Die Syntax einer Datensicht Den Zugriff auf Daten mit einer Datensicht einschränken Komplexität mit einer Datensicht verbergen 9.11 Zusammenfassung 9.12 Wie geht es weiter? 9.13 Fragen und Antworten 9.14 Workshop Test Übungen
Tag 10 Datenbanksicherheit und Optimierung 10.1 Benutzer und Rollen Vordefinierte Benutzer SYS und SYSTEM Vordefinierte Rollen Systemrechte und Objektrechte Erstellen und Verwenden einer Rolle 10.2 Synonyme 10.3 Virtual Private Database und Label Security. 10.4 Datenbankoptimierung Netzwerk Anwendung Optimierung von SQL Anweisungen
Ausführungspläne Analyse einer Anweisung mit Hilfe von TKPROF Optimierung der Oracle-Instanz Einschränken der Ressourcen mit Hilfe eines Profils Die Leistungsfähigkeit durch Erstellen von Indizes verbessern Die SGA vergrößern Konflikte beim Zugriff auf Plattenspeicher verringern Hindernisse für eine optimale Systemleistung 10.5 Zusammenfassung 10.6 Wie geht es weiter? 10.7 Fragen und Antworten 10.8 Workshop Test Übungen
Tag 11 Tabellen und Indizes für Fortgeschrittene 11.1 Partitioning Option Range Partitioning Hash Partitioning Composite Partitioning List Partitioning Eliminierung von Partitionen Operationen auf partitionierten Tabellen Partitionierte Indizes 11.2 Externe Tabellen 11.3 Objektrelationale Merkmale Der Datentyp OBJECT Verschachtelte Tabellen Der Datentyp Varray Katalogsichten für benutzerdefinierte Datentypen 11.4 Indizes für Fortgeschrittene Funktionsbasierte Indizes Bitmap Indizes Bitmap Join Indizes Domänenindizes 11.5 Zusammenfassung 11.6 Wie geht es weiter? 11.7 Fragen und Antworten
11.8 Workshop Test Übungen
Tag 12 Programmierung einer Oracle-Datenbank mit PL/SQL 12.1 Die Blockstruktur von PL/SQL Der Deklarationsteil und Standardwerte Der Ausführungsteil Der Exception-Block 12.2 Variablendeklaration mit PL/SQL Deklaration einer Variablen mit Hilfe von %type Deklaration einer Variablen mit %rowtype 12.3 Einige bekannte Kontrollstrukturen Die if-Anweisung Die einfache loop-Anweisung Die exit-Anweisung Die while-loop-Anweisung Die for-loop-Anweisung Die goto-Anweisung Die null-Anweisung Die Zuweisungsanweisung Einfügen von Kommentaren in ein PL/SQL-Unterprogramm 12.4 SQL-Anweisungen in einem PL/SQL-Programm verwenden PL/SQL und die select-Anweisung 12.5 PL/SQL-Unterprogramme 12.6 Verwenden von Unterblöcken 12.7 Deklarieren einer Prozedur 12.8 Deklarieren einer Funktion Prozedur- und Funktionsargumente 12.9 Zusammenfassung Test Übungen
Tag 13 Programmentwicklung mit PL/SQL 13.1 Erstellen einer gespeicherten Prozedur oder Funktion Ermitteln von Fehlermeldungen bei Erstellen gespeicherter Prozeduren
13.2 PL/SQL-Quellcode und Datenbankkatalog Ermitteln einer Liste von Prozeduren, Funktionen, Paketen und Paketrümpfen 13.3 Vorwärtsdeklaration für Prozeduren und Funktionen 13.4 Gespeicherte Funktionen in einer SQL-Anweisung verwenden 13.5 Speichern von Rückgabewerten in einer Tabelle 13.6 Aufrufen einer gespeicherten Prozedur oder Funktion 13.7 Pakete Deklarieren eines Pakets Deklarieren eines Paketrumpfs Entwerfen eines Pakets für Datenbanktrigger 13.8 Zusätzliche PL/SQL-Datentypen Der Datentyp Boolean Der Datentyp binary_integer PL/SQL Collections Deklarieren von PL/SQL Index-By Tabellen 13.9 Zusammenfassung Test Übungen
Tag 14 Weitere PL/SQL-Programmiertechniken 14.1 Fehlerbehandlung in PL/SQL Der Exception-Block Vordefinierte Exceptions Die DUP_VAL_ON_INDEX-Exception Die INVALID_NUMBER-Exception Die NO_DATA_FOUND-Exception Die TOO_MANY_ROWS-Exception Die VALUE_ERROR-Exception Deklarieren einer Exception Erfolg oder Fehler: Auswerten von sqlcode und sqlerrm Rückgabe von Fehlern mit der Prozedur RAISE_APPLICATION_ERROR 14.2 Abrufen von Daten mit einem Cursor Deklarieren eines Cursors Öffnen eines Cursors Lesen einer Tabelle mit einem Cursor Schließen eines Cursors Cursor-gesteuerte for-Schleifen
Cursor-Attribute %FOUND und %NOTFOUND Ermitteln der Zeilenanzahl mit Hilfe von %ROWCOUNT 14.3 Datenbanktrigger Erstellen eines Triggers auf einer Tabelle Trigger auf Anweisungsebene und Zeilenebene Zugriff auf Spaltenwerte im Trigger-Rumpf Auslösen von Ereignissen BEFORE- und AFTER-Trigger Mögliche Trigger für eine Tabelle Spaltenwerte mit Hilfe eines Triggers auf Gültigkeit prüfen Sicherheit mit Hilfe eines Triggers implementieren Automatisches Nummerieren mit einem Trigger Kaskadierende Trigger Commit- und Rollback-Anweisungen in Triggern Trigger auf Datenbankereignisse Löschen, Aktivieren und Deaktivieren von Triggern 14.4 Zusammenfassung 14.5 Wie geht es weiter? 14.6 Fragen und Antworten 14.7 Workshop Test Übungen
Woche 3 Vorschau Tag 15 Einführung in Oracle Forms 15.1 Die Werkzeuge der Internet Developer Suite (iDS) 15.2 Erstellen eines einfachen Formulars mit dem Forms Builder Eine Verbindung zu einer Oracle-Datenbank aufbauen Erstellen eines neuen Blocks Testen des neuen Formulars Durch ein Formular navigieren Abfrageverhalten von Oracle Forms Einen Datensatzes mit Oracle Forms ändern Speichern eines Formulars Komponenten von Oracle Forms Formulare Menüs
Bibliotheken 15.3 Elemente eines Formulars Mit dem Object Navigator arbeiten Objekteigenschaften Blöcke Leinwände (Canvases) Fenster Trigger Datensatzgruppen (Record Groups) Wertelisten Warnungen (Alerts) Parameter 15.4 Ändern von Oracle-Komponenten mit Hilfe des Registrierungseditors 15.5 Zusammenfassung 15.6 Wie geht es weiter? 15.7 Fragen und Antworten 15.8 Workshop Test Übung
Tag 16 Entwicklung einer Benutzeroberfläche mit Oracle Forms 16.1 Ein Master-Detail-Formular erstellen Definieren des Master-Blocks Den Detail-Block definieren Definieren der Beziehung 16.2 Mit dem Layout Editor arbeiten Text formatieren Die Größe von Objekten einstellen Text ändern Größe, Abstand und Ausrichtung von Objekten einstellen 16.3 Das Master-Detail-Formular ausführen 16.4 Verbessern des Formulars Die Aktivierungsreihenfolge von Feldern steuern Erstellen einer Werteliste 16.5 Zusammenfassung 16.6 Wie geht es weiter? 16.7 Fragen und Antworten
16.8 Workshop 16.9 Übungen
Tag 17 Anwendungsentwicklung mit Oracle Forms 17.1 Eine Übersicht über Trigger Standard-Trigger 17.2 Werteabfragen aus einer anderen Tabelle mit Hilfe eines Triggers 17.3 Eine Abfrage mit Hilfe eines Triggers auf Formularebene ausführen 17.4 Benutzereingaben mit Hilfe eines Triggers auf ihre Gültigkeit überprüfen 17.5 Elemente eines Menüs 17.6 Entwickeln eines Menüs 17.7 Integrieren neuer Menüs Elemente zu einem Menü hinzufügen Speichern des Menüs Erzeugen des Menüs 17.8 Entwickeln einer Multiform-Anwendung 17.9 Erzeugen einer Windows-Verknüpfung (Shortcut) zum Starten der Anwendung Schließen eines Formulars 17.10 Zusammenfassung 17.11 Wie geht es weiter? 17.12 Fragen und Antworten 17.13 Workshop Test Übungen
Tag 18 Entwicklung von Berichten mit Oracle Reports 18.1 Elemente eines Oracle-Berichts Datenmodell Layout Parameterformular 18.2 Die Komponenten von Oracle Reports 18.3 Einen tabellarischen Bericht auf Basis einer einzelnen Tabelle erstellen Aufbau einer Verbindung zu einer Oracle-Datenbank
Erstellen eines Berichts mit dem Report Wizard Bestandteile eines Berichts Änderungen am Layout im Live Previewer durchführen Datum und Seitenzahlen einem Bericht hinzufügen Testen des Berichts Einen Bericht drucken Speichern des Berichts 18.4 Einen Master-Detail-Bericht erstellen Erstellen eines Master-Detail-Berichts mit dem Report Wizard Datenmodell des Master-Detail-Berichts Änderungen am Layout des Master-Detail-Berichts Testen und Speichern des Berichts Erzeugen einer Laufzeit-Version eines Berichts 18.5 Einen Format-Trigger einsetzen 18.6 Eine Verknüpfung zum Starten eines Berichts erzeugen 18.7 Aufrufen eines Berichts aus einem Oracle-Formular 18.8 Zusammenfassung 18.9 Wie geht es weiter? Test Übungen
Tag 19 Oracle Graphics und Procedure Builder 19.1 Die Rolle von Oracle Graphics 19.2 Die Elemente von Oracle Graphics 19.3 Erstellen eines Diagramms mit dem Graphics Builder Aufbau einer Verbindung zu einer Oracle-Datenbank Ein neues Diagramm erstellen Wählen des Diagrammtyps Anpassen des Diagramm-Layouts 19.4 Das Diagramm speichern Erzeugen eines Shortcut zum Aufrufen eines Diagramms 19.5 Die Rolle des Procedure Builder im Prozess der Anwendungsentwicklung 19.6 Komponenten des Procedure Builder Aufbau einer Verbindung zu einer Oracle-Datenbank 19.7 Den Object Navigator des Procedure Builder verwenden Der Program Unit Editor Der PL/SQL Interpreter
Der Stored Program Unit Editor für gespeicherte Programmeinheiten Der Database Trigger Editor für Datenbank-Trigger 19.8 Eine gespeicherte Programmeinheit anzeigen und bearbeiten 19.9 Erstellen eines Datenbank-Triggers mit dem Database Trigger Editor Den neuen Datenbank-Trigger testen 19.10 Zusammenfassung 19.11 Wie geht es weiter? 19.12 Fragen und Antworten 19.13 Workshop Test Übungen
Tag 20 Entwicklung von Web-Anwendungen mit Java und JDeveloper 20.1 Architektur und Aufbau von Web-Anwendungen 20.2 Der Oracle JDeveloper 20.3 Ihre erste Java Server Page 20.4 Komplexere Anwendungen 20.5 Anwendungen der realen Welt JSP Taglibs Style-Sheets Model View Controller (MVC) 20.6 Einbindung in den Oracle Internet Application Server 20.7 Zusammenfassung 20.8 Wie geht es weiter? 20.9 Fragen und Antworten 20.10 Workshop
Tag 21 Oracle Business Components for Java 21.1 Komponenten und Aufbau des BC4J-Framework 21.2 Eine erste Anwendung mit BC4J 21.3 Erweiterung der BC4J-Anwendung 21.4 Die BC4J-Anwendung wird zur Web-Anwendung 21.5 Zusammenfassung 21.6 Fragen und Antworten
21.7 Workshop
Anhang A Antworten zu den Tests und Übungen A.1 Tag 1, »Die Welt relationaler Datenbanken erforschen« Test Übung A.2 Tag 2, »Richtlinien für die Entwicklung einer Oracle-Anwendung« Test Übungen A.3 Tag 3, »Logischer Datenbankentwurf« Test Übungen A.4 Tag 4, »Implementierung des logischen Modells: physischer Datenbankentwurf« Test Übung A.5 Tag 5, »Oracle Designer in der Anwendungsentwicklung« Test A.6 Tag 6, »Einführung in SQL« Test Übung A.7 Tag 7, »Daten mit SQL ändern« Test Übung A.8 Tag 8, »SQL-Funktionen« Test Übung A.9 Tag 9, »Fortgeschrittene Abfragen mit SQL« Test Übung A.10 Tag 10, »Datenbanksicherheit und Optimierung« Test Übung A.11 Tag 11, »Tabellen und Indizes für Fortgeschrittene« Test Übung A.12 Tag 12, »Programmierung einer Oracle-Datenbank mit PL/SQL«
Test Übung A.13 Tag 13, »Programmentwicklung mit PL/SQL« Test Übungen A.14 Tag 14, »Weitere PL/SQL-Programmier-techniken« Test Übungen A.15 Tag 15, »Einführung in Oracle Forms« Test Übung A.16 Tag 16, »Entwicklung einer Benutzeroberfläche mit Oracle Forms« Test Übungen A.17 Tag 17, »Anwendungsentwicklung mit Oracle Forms« Test Übung A.18 Tag 18, »Entwicklung von Berichten mit Oracle Reports« Test Übung A.19 Tag 19, »Oracle Graphics und Procedure Builder« Test Übungen A.20 Tag 20, »Entwicklung von Web-Anwendungen mit Java und JDeveloper« Test Übung A.21 Tag 21, »Oracle Business Components for Java« Test Übung
D Data Definition Language ❍ Data Definition Language 102 Data Dictionary ❍ Data Dictionary 76, 401 ❍ Data Dictionary 76, 401 Data Guard ❍ Data Guard 33 Data Warehouse ❍ Data Warehouse 33, 348 ❍ Data Warehouse 33, 348 Star Schema ■ Star Schema 69 und kostenbasierter SQL-Optimierer ■ und kostenbasierter SQL-Optimierer 317 Database Design Transformer ❍ Database Design Transformer 163 Database Trigger Editor ❍ Database Trigger Editor 583 date ❍ date 90, 243 ❍ date 90, 243 Daten abrufen ■ abrufen 174, 440 ■ abrufen 174, 440 ändern ■ ändern 174, 209 ■ ändern 174, 209 Anforderungen
Anforderungen 50 hierarchische ■ hierarchische 271 löschen ■ löschen 217 sortieren ■ sortieren 182 Zugriff ■ Zugriff 296 Datenbank Aufbau einer Verbindung ■ Aufbau einer Verbindung 554 Definition ■ Definition 76 LAN■ LAN- 38 maximale Größe ■ maximale Größe 76 nur zum Lesen Öffnen ■ nur zum Lesen Öffnen 77 PC■ PC- 38 relationale ■ relationale 30 Datenbankbenutzer und Rollen ■ und Rollen 302 Datenbankblockgröße variable ■ variable 78 Datenbankentwurf logischer ■ logischer 60 physischer ■ physischer 102 Datenbankereignis ❍ Datenbankereignis 466 Datenbankkatalog ❍ Datenbankkatalog 76 Datenbankprogramm ❍ Datenbankprogramm 38 ■
vsize 251 Funktionsbasierter Index ❍ Funktionsbasierter Index 357 ■
G geographische Informationssysteme siehe Oracle Spatial ❍ geographische Informationssysteme siehe Oracle Spatial GMT siehe Greenwich Mean Time ❍ GMT siehe Greenwich Mean Time goto ❍ goto 380 GRANT ❍ GRANT 305 Greenwich Mean Time ❍ Greenwich Mean Time 91 Groß-/Kleinschreibung ändern ■ ändern 239 Großrechner ❍ Großrechner 37 group by-Klausel ❍ group by-Klausel 267 Gruppenfunktionen ❍ Gruppenfunktionen 265, 266 ❍ Gruppenfunktionen 265, 266 H Hash Partitioning ❍ Hash Partitioning 333 Hauptfenster ❍ Hauptfenster 543 having-Klausel ❍ having-Klausel 267 Hebräisch ❍ Hebräisch 88 Heterogeneous Services ❍ Heterogeneous Services 33
Hexadezimaldarstellung von Zeichenketten ■ von Zeichenketten 242 High Water Mark ❍ High Water Mark 218 HTML Formulare ■ Formulare 618 Formular-Elemente ■ Formular-Elemente 620 für Web-Anwendungen ■ für Web-Anwendungen 596 Parameter ■ Parameter 619 Tags ■ Tags 604 I IBM
IBM 31 identifizierend ❍ identifizierend 64 IDLE_TIME ❍ IDLE_TIME 322 if ❍ if 376 imp ❍ imp 345 IMP_FULL_DATABASE ❍ IMP_FULL_DATABASE 304 Implementierung ❍ Implementierung 51, 101 ❍ Implementierung 51, 101 Import von transportierbaren Tablespaces ❍ Import von transportierbaren Tablespaces 345 in ❍ in 190, 391 ❍ in 190, 391 in out ❍ in out 391 ❍
like 186 List Partitioning ❍ List Partitioning 337 Liste von Funktionen ■ von Funktionen 402 von Paketen ■ von Paketen 402 von Prozeduren ■ von Prozeduren 402 Listener ❍ Listener 79 Live Previewer ❍ Live Previewer 557 Änderungen am Layout ■ Änderungen am Layout 559 LOB siehe Large Object ❍ LOB siehe Large Object Local Area Network ❍ Local Area Network 38 Locally Managed Tablespace ❍ Locally Managed Tablespace 75 LOGICAL_READS_PER_CALL ❍ LOGICAL_READS_PER_CALL 322 long Datentyp ■ Datentyp 89 loop ❍ loop 377 LOV ❍ LOV 496 lower ❍ lower 240 lpad ❍ lpad 237 lsnrctl ❍ lsnrctl 80 ltrim ❍
Die CD-ROM Die CD-ROM zum Buch enthält den Buchtext im HTML-Format, SQL-Skripte, InternetLinks, Dokumentation und einen Export-Dump des Datenbankschemas des fiktiven Flugle College. SQL-Skripte, die im Buch mit einem CD-Piktogramm gekennzeichnet sind, finden Sie im Verzeichnis sql auf der CD-ROM. Sie können sich Tipparbeit sparen, wenn Sie beim Experimentieren mit SQL-Anweisungen die HTML-Version des Buches öffnen und Anweisungen mit Kopieren und Einfügen in SQL*Plus oder SQL*Plus Worksheet ausführen. Der Zugriff auf Dokumentation zu den Produkten Oracle 9i Datenbank, Internet Developer Suite und JDeveloper erfolgt über die Datei dokumentation.html. Sie benötigen einen HTML-Browser oder den Acrobat Reader. Die Datei links.html enthält interessante Internet-Links rund um das Thema Oracle. Die Datei flugle\flugle.dmp ist ein Oracle9i Export-Dump des Flugle Beispielschemas. Indem Sie diese Datei mit dem Import-Werkzeug (imp) in der Version 9.0 oder höher in eine Oracle-Datenbank importieren, können Sie den in jeder Lektion vorgestellten Beispielen folgen. Die Datei flugle\readme.txt enthält Hinweise darüber, wie man flugle.dmp in eine Oracle-Datenbank importiert.
Antworten zu den Tests und Übungen A.1 Tag 1, »Die Welt relationaler Datenbanken erforschen« Test 1. Nennen Sie zwei Vorteile relationaler Datenbanken gegenüber Dateiverwaltungssystemen. Antwort: Eine relationale Datenbank bietet deklarative Integrität. Der Begriff deklarativ bedeutet, dass die Integrität durch einfaches Deklarieren der Integritätsregeln erzwungen wird, wenn eine Tabelle erstellt oder verändert wird. Eine relationale Datenbank bietet außerdem eine ad-hoc-Abfragemöglichkeit, die ein Formulieren von komplexen Abfragen ohne Programmierung ermöglicht. 2. Nennen Sie drei Vorteile der Architektur für die Client-Server-Datenverarbeitung im Vergleich zu der Datenverarbeitungsarchitektur von Großrechnern. Antwort: Vorteile der Client-Server-Architektur sind die Fähigkeit, unterschiedliche ClientBetriebssysteme zu unterstützen, unabhängig vom Netzwerkprotokoll und der Aufteilung von Prozessen zwischen Client und Server (der Client kontrolliert beispielsweise die Benutzerschnittstelle, wohingegen der Server die Datenspeicherung und -verwaltung bietet). 3. Wie heißt das Middleware-Produkt von Oracle? Antwort: Der Name des Oracle-Middleware-Produkts ist Oracle Net Services. 4. Was ist ein »dicker« Client? Was ist ein »dünner« Client? Antwort: Heutzutage wird eine traditionelle Client-Server-Architektur, in der ein ClientRechner eine ziemlich große, ausführbare Datei, die notwendigen Bibliotheken und ein Middleware-Produkt beherbergt, als »fat«-Client bezeichnet. Ein »thin«-Client beschreibt einen Client-Rechner, in dem ein Web-Browser verwendet wird, um eine Anwendung auszuführen, entweder mittels HTML oder dem Herunterladen von Java-Applets nach Bedarf.
Übung Wie bereits erwähnt, wird in diesem Buch eine Beispieldatenbank eines kleinen College verwendet. Denken Sie darüber nach, welche Art von Informationen Ihrer Meinung nach ein College speichern und abfragen möchte. Fragen Sie sich, welche Fragen ein Student, ein Professor oder ein Administrator an die Datenbank stellen könnte. Notieren Sie, wie Sie diese Informationen anordnen könnten. Sie werden am Tag 3, »Logischer Datenbankentwurf«, auf diese Informationen zurückkommen. Antwort: Ein College würde bestimmt Informationen verwalten über die
● ● ● ● ●
Studenten, Departments (Fachbereiche), Dozenten, Kurse, Einrichtungen sowie Gebäude und Ausrüstung.
Ein Student wird wahrscheinlich wissen wollen, welche Vorlesungen es gibt, wo sie gehalten werden und wer der Dozent ist; ebenfalls die Kosten für jede Klasse. Ein Dozent wird etwas über die Klassen wissen wollen, die ihm zum Unterrichten zugewiesen wurden. Ein Dozent muss in der Lage sein, jedem Studenten einer Klasse eine Note zuzuweisen. Ein Administrator wird in der Lage sein wollen, jeder Klasse eine geeignete Räumlichkeit zur Verfügung zu stellen (in Bezug auf die Zahl der Sitzplätze und der Laborressourcen). Ein Administrator wird sich außerdem die Möglichkeit wünschen, ein Department mit einem anderen auf verschiedene Arten vergleichen zu können - Lehrbelastung, Dienstalter der Dozenten oder die Durchschnittsnote in einer Klasse.
A.2 Tag 2, »Richtlinien für die Entwicklung einer OracleAnwendung« Test 1. Richtig oder falsch? Wenn Sie ein Werkzeug für die Anwendungsentwicklung benutzen, welches eine objektorientierte Entwicklung unterstützt, ist die Chance größer, eine erfolgreiche Anwendung zu implementieren. Antwort: Falsch. Ein objektorientiertes Entwicklungswerkzeug würde den Aufwand der Entwicklung und Wartung einer Anwendung reduzieren. Das bedeutet aber nicht, dass dies zu einer erfolgreichen Realisierung führt; es gibt noch viele andere Faktoren, die den Verlauf eines Projektes beeinflussen. 2. Nennen Sie drei Arten von Anforderungen, die bei der Entwicklung eines Systems erforderlich sind. Antwort: Anforderungen an die Daten, die Funktionalität und die Leistung. 3. Richtig oder falsch? Sie sollten erst mit der Software-Entwicklung beginnen, wenn Ihnen die Anforderungen vollständig bekannt sind. Antwort: Falsch. Sie sollten mit der Entwicklung von Software warten, bis Sie eine größere Anzahl von Anforderungen ermittelt haben. Wenn Sie jedoch warten, bis alle Anforderungen aufgestellt sind, warten Sie unter Umständen eine lange Zeit. Viele Anforderungen entstehen erst dann, wenn Endanwender einen Prototyp der Anwendung testen. Außerdem sind Anforderungen nicht statisch, sie verändern sich definitiv im Lauf der Zeit.
Übungen 1. Welche Arten von Risiken könnten bei der Entwicklung einer Oracle-Anwendung auftreten? 2. Welche Faktoren könnten zu diesen Risikokategorien gehören? 3. Welche Schritte könnten Sie unternehmen, um diese Risiken zu mildern? Antwort: Sie können die Risiken während der Entwicklung einer Oracle-Anwendung wie
folgt kategorisieren: ●
Risiko Zeitplan: Das Risiko, dass die Anwendung die Termine der Entwicklungsmeilensteine versäumt. Ein offensichtlicher Grund, warum ein Projekt oftmals die Entwicklungsetappen nicht erreicht, ist die Tatsache, dass die Etappen von Beginn an unrealistisch waren. Oftmals ist der Druck auf das Projektmanagement schlicht zu hoch; einzelne Mitglieder werden gedrängt, die Zeit und die Ressourcen, welche für die Erledigung eines Arbeitsabschnitts notwendig sind, zu gering einzuschätzen. Auch kann ein Projekt frühe Etappen erreichen, wie z.B. die Analyse der Anforderungen, scheitert aber bei der Erreichung von Etappen, welche die aktuelle Anwendung bietet. Was oftmals während solcher Projekte passiert, ist, dass die frühen Etappen erreicht zu werden scheinen, eine sorgfältige Untersuchung zeigt aber ungelöste Probleme. Aus diesem Grund ist es bei einem großen Projekt für den umsichtigen Kunden wichtig, eine unabhängige Überprüfung des Projekts für jede frühe Etappe einzusetzen.
●
Risiko Budget: Das Risiko, dass die Anwendung das Budget, das dafür zur Verfügung steht, überschreitet. Oftmals, wenn ein Projekt seinen Zeitplan überschreitet, geschieht dies auch in Bezug auf das Budget. Der Grund ist simpel: der größte Teil eines Projekt-Budgets ist üblicherweise für die Durchführung der Arbeiten gedacht. Wenn die Schätzung für den Arbeitsaufwand zur Absolvierung eines Arbeitsschrittes zu gering war, wird auch die korrespondierende Schätzung für das Budget zu gering ausfallen. Eine Ausnahme hierfür ist ein Projekt, für das die notwendigen Mitarbeiter verspätet eingebunden werden, in diesem Fall ist das Budget innerhalb seines Limits, aber der Zeitplan ist es nicht.
●
Risiko Technik: Die technischen Risiken eines Projekts können viele Ursachen haben. Neue Produkte oder Technologien können ohne eine sorgfältige Analyse ausgewählt worden sein. Entwickler haben unter Umständen nicht die nötige Übung oder ausreichende Erfahrung, um eine zuverlässige Anwendung zu entwickeln. Die Projektentwickler könnten die Anforderungen an den Server- Rechner (zum Beispiel CPU-Geschwindigkeit, Speicheranforderungen) oder notwendige Netzwerkressourcen für die Produktionsumgebung unterschätzt haben. Jedes dieser Elemente sollte sorgfältig bedacht werden. Oftmals ist es Zeit- und Geldverschwendung, einen einfachen Benchmarktest durchzuführen, um die Performance der Anwendung in einer Produktionsumgebung zu charakterisieren.
A.3 Tag 3, »Logischer Datenbankentwurf« Test 1. Richtig oder falsch? Wenn ein Attribut, das nicht Teil des Primärschlüssels ist, einen Fremdschlüssel darstellt, ist es unbedingt erforderlich; es lässt keinen NULL-Wert zu. Antwort: Falsch. Manchmal ist ein Fremdschlüssel obligatorisch und manchmal nicht. Dies hängt von den Firmenregeln ab, die modelliert werden sollen. 2. Kann die Student-Schedule-Tabelle verwendet werden, wenn ein Student einen Kurs wiederholen muss? Warum bzw. warum nicht? Antwort: Ja. Jede Klasse ist eindeutig identifiziert durch die Klassen-ID. Aber jedes erneute Anbieten desselben Kurses wird eine andere Klassen-ID haben. Nehmen Sie zum Beispiel an, ein Student belegt Biologie 101 während des Herbstsemesters 1996 und erreicht den Abschluss des Kurses nicht - dieser hat die Klassen-ID 109230. Im Frühjahr 1997
wiederholt er den Kurs - der jetzt die Klassen-ID 110330 hat. Weil Klassen unterschiedliche IDs haben, kann für beide Klassen eine Zeile in die Student- ScheduleTabelle eingefügt werden. 3. Welche Eigenschaft einer relationalen Datenbank verhindert, dass eine Klasse aus der Class-Tabelle gelöscht wird, wenn in der Student-Schedule-Tabelle Zeilen existieren, welche die zu löschende Class ID enthalten? Antwort: Referentielle Integrität. 4. Es gibt eine Beziehung zwischen der Class- und der Instructor-Tabelle. Handelt es sich hierbei um eine identifizierende oder nichtidentifizierende Beziehung? Antwort: Nichtidentifizierend. Instructor ID ist nicht Teil des Primärschlüssels in der Klassen-Tabelle. 5. Richtig oder falsch? Wenn Sie den entsprechenden Index für eine Tabelle erstellen, werden die Zeilen stets in aufsteigender Reihenfolge der indizierten Spalten abgerufen. Antwort: Falsch. Es gibt keine festgelegte Reihenfolge für die Daten in einer Tabelle. Um zu garantieren, dass Zeilen in einer bestimmten Reihenfolge abgerufen werden, müssen Sie dies in einer SQL-Anweisung festlegen.
Übungen 1. Angenommen, Sie möchten den Kursleiter identifizieren, der auch Bereichsleiter ist. Können Sie mindestens zwei Möglichkeiten nennen, dies herauszufinden? Wie lauten die Stärken und Schwächen der jeweiligen Ansätze? Antwort: Es gibt offensichtlich zwei Möglichkeiten. Eine davon ist, zur Department- Tabelle ein Attribut hinzuzufügen, das den Dozenten identifiziert, der Leiter des Departments ist. Natürlich würde dieses Attribut für die Dozenten-Tabelle ein Fremdschlüssel sein. Auf diese Weise würde die Department-Tabelle aus drei Attributen bestehen: ● ● ●
Department ID Department Name Department Head Die andere Möglichkeit ist, zur Dozenten-Tabelle ein Attribut hinzuzufügen, das festlegt, ob ein Dozent der Leiter eines Departments ist oder nicht. Sie können dieses Attribut ebenfalls Department Head nennen und ihm den Wert Y zuweisen, wenn der Dozent der Leiter eines Departments ist (der Wert kann auch NULL sein, wenn der Dozent nicht der Leiter ist). Das Problem dieses Ansatzes ist, dass diese Information ein Charakteristikum eines Departments ist; aus diesem Grund macht es mehr Sinn, sie in die DepartmentTabelle zu integrieren.
2. Die Student Schedule-Tabelle enthält zur Zeit sowohl die derzeitigen Klassen als auch die früheren Klassen, die ein Student besucht hat. Schlagen Sie einen alternativen Entwurf mit zwei Tabellen vor, wobei eine Tabelle den aktuellen Stundenplan und die zweite Tabelle die Klassen enthält, die der Student früher besucht hat. Nennen Sie alle Attribute in den zwei Tabellen. Welche Vorteile hat dieser Entwurf? Antwort: Die gegenwärtige Definition der Student_Schedule-Tabelle ist:
● ● ● ●
Student-ID (Fremdschlüssel für die Student-Tabelle) Klassen-ID (Fremdschlüssel für die Class-Tabelle) Einstufung Datum der Zuweisung der Einstufung Primärschlüssel: Student-ID, Class-ID Als Alternative hierzu kann die Student_Schedule-Tabelle in zwei Tabellen zerlegt werden: Current Student Schedule und Student Class History. Der gegenwärtige Stundenplan des Studenten wird in Current Student Schedule gespeichert, dies schließt die Einstufung des Studenten nicht mit ein. Sobald einem Studenten Einstufungen zugewiesen werden, würden der Student Class History die entsprechenden Datensätze hinzugefügt werden. Zu Beginn jedes Semesters werden die Inhalte der Current Student Schedule gelöscht. Die Definition für die Current Student Schedule könnte diese sein:
● ● ● ● ● ● ●
Student-ID (Fremdschlüssel für die Student-Tabelle) Klassen-ID (Fremdschlüssel für die Klassen-Tabelle) Die Definition für die Student Class History könnte diese sein: Student-ID (Fremdschlüssel für die Student-Tabelle) Klassen-ID (Fremdschlüssel für die Klassen-Tabelle) Einstufung Datum der Zuweisung der Einstufung Für eine große Universität macht dieser Entwurf mehr Sinn. Nehmen Sie zum Beispiel an, eine Universität hat zwanzigtausend immatrikulierte Studenten. Nehmen Sie weiterhin an, dass jeder Student im Mittel 3,5 Klassen pro Semester belegt. Zu Beginn jedes Semesters würde die Current Student Schedule deshalb 20.000 x 3,5 = 70.000 Datensätze enthalten. Am Ende jedes Semesters würden zur Student Class History weitere 70.000 Datensätze hinzugefügt. Wenn all diese Datensätze in einer einzelnen Tabelle, Student_Schedule, gehalten sein würden, würde die Performance des Systems mit der Zeit zurückgehen. In vierzehn Jahren würde die Student_Schedule-Tabelle nahezu eine Million Zeilen enthalten, was den Vorgang der Einschreibung für eine Klasse deutlich verlangsamen könnte.
A.4 Tag 4, »Implementierung des logischen Modells: physischer Datenbankentwurf« Test 1. Was ist an dieser Anweisung falsch? CREATE TABLE new_table ( first_col number, second_col date third_col number default sysdate); Antwort: Es gibt zwei Probleme mit dieser Anweisung. Zuerst einmal wird ein Komma am Ende der Definition für second_col benötigt. Zweitens ist third_col eine Zahl und kann keinen Standardwert - sysdate - aufnehmen, der vom Datentyp Date ist. 2. Beschreiben Sie eine SQL-Anweisung, welche die folgende Oracle-Fehlermeldung verursachen könnte.
ORA-02266: unique/primary keys in table referenced by enabled foreign keys Antwort: Sie werden diese Fehlermeldung sehen, wenn Sie versuchen, eine Tabelle zu löschen, deren Primärschlüssel vom Fremdschlüssel einer anderen Tabelle referenziert wird. 3. Worin besteht der Unterschied zwischen den check-Integritätsbedingungen für eine Spalte und für eine Tabelle? Antwort: Eine Spalten-Integritätsregel vom Typ CHECK wird auf Spaltenebene definiert und kann keine anderen Spalten in der Tabelle referenzieren. Eine TabellenIntegritätsregel vom Typ CHECK wird auf dem Tabellen-Level definiert und kann jede beliebige Spalte der Tabelle referenzieren.
Übung Die Instructor-Tabelle besitzt eine Spalte namens Position. In dem derzeitigen Tabellenentwurf gibt es eine check-Regel für die Spalte Position, welche den Wert auf assistant professor, associate professor und full professor begrenzt. Ändern Sie den Datenbankentwurf so, dass eine zusätzliche Tabelle, INSTRUCTOR_POSITION, verwendet wird, um zulässige Werte für die Position des Kursleiters anzugeben. Antwort: Erstellen Sie eine Tabelle namens Instructor_Position: create table Instructor_Position (Position varchar2(25), constraint PK_Instructor_Position Primary Key (Position)); Definieren Sie die Dozenten-Tabelle wie folgt neu: create table Instructor (Instructor_ID varchar2(20), Department_ID varchar2(20) constraint NN_Instructor_Dept_ID NOT NULL, Last_Name varchar2(25) constraint NN_Instructor_Last_Name NOT NULL, First_Name varchar2(25), MI varchar2(1), Position varchar2(25), Telephone varchar2(10), Fax varchar2(10), Email varchar2(100), constraint PK_Instructor Primary Key (Instructor_ID), constraint FK_Instructor_Department_ID Foreign Key (Department_ID) references Department (Department_ID), constraint FK_Instructor_Position Foreign Key (Position) references Instructor_Position (Position));
A.5 Tag 5, »Oracle Designer in der Anwendungsentwicklung« Test 1. Richtig oder falsch? Ein Nachteil des Oracle Designer ist, dass er nur Oracle- Datenbanken unterstützt.
Antwort: Falsch. Oracle Designer erlaubt Ihnen, ausgehend von Ihrem Datenbankdesign, die Generierung von Datenbank-Objekten für verschiedene relationale Datenbanksysteme (u.a. RDB, DB2, Sybase, MS SQL-Server). 2. Nennen Sie drei Vorteile der Verwendung des Oracle Designer anstelle eines Texteditors zur Erstellung der DDL-Anweisungen, die Sie zur Erzeugung einer Datenbank benötigen. Antwort: Einige der Vorteile der Verwendung des Oracle Designer sind: ● ● ●
●
Deutlich weniger Eingabefehler, wie z.B. das Eingeben eines falschen Spaltennamens. Die Fähigkeit, die Definitionen von Tabellen und Spalten in derselben Datei zu erfassen. Die Fähigkeit, Informationen im Oracle Designer weiterzuverarbeiten, wie z.B. Anzeigeformate für Spalten. Ein Diagramm ist viel leichter zu verstehen als ein Satz von SQL-Anweisungen und kann anderen Projektmitgliedern im Überblick präsentiert werden.
A.6 Tag 6, »Einführung in SQL« Test 1. Richtig oder falsch? Sie müssen eine Spalte in die select-Liste aufnehmen, wenn Sie die von der select-Anweisung für diese Spalte zurückgegebenen Zeilen sortieren möchten. Antwort: Falsch. Sie können eine Spalte in der ORDER BY-Bedingung festlegen, die sich nicht in der Auswahlliste befindet. 2. Was ist an dieser Anweisung falsch? select First_Name from Student order by Last_Name where Last_Name like '%IN%'; Antwort: Die ORDER BY-Bedingung steht vor der WHERE-Bedingung. 3. Richtig oder falsch? Eine Spalte muss zuerst indiziert werden, bevor sie in der order by-Klausel angegeben werden kann. Antwort: Falsch. Oracle und andere relationale Datenbanken benötigen keine Indizierung einer Spalte, bevor diese für die Sortierung der Reihen benutzt werden kann, die von einer Tabelle abgerufen werden.
Übung Konstruieren Sie unter Verwendung der in dieser Lektion behandelten Course-Tabelle eine selectAnweisung, welche Department ID, Course ID und Course Title sortiert nach Department ID und Course ID zurückgibt, und zwar für alle Kurse, deren Beschreibung das Wort introduc enthält, unabhängig von der Großund Kleinschreibung. Antwort: Hier ist die SELECT-Anweisung, welche diese Aufgabe erfüllt. Beachten Sie, dass die Funktion lower benutzt wird, um sämtliche Kursbeschreibungen in Kleinschrift umzuwandeln, so dass ein einheitlicher
Vergleich mit introduc durchgeführt werden kann. SQL> select Department_ID, Course_ID, Title from Course where lower(Description) like '%introduc%' order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID TITLE ------------- ---------- -----------------------------1000 10001 INTRO TO PHILOSOPHY 1001 10011 INTRO TO ECONOMICS 1002 10021 INTRO TO BIOLOGY 1003 10031 INTRO TO ANTHROPOLOGY 1004 10041 INTRO TO PSYCHOLOGY 1005 10053 GENERAL CALCULUS 1005 10054 NUMBER THEORY 1007 10071 INTRO TO ENGLISH LIT 1007 10074 SEMINAR ON THEME ANALYSIS 1008 10081 INTRO TO STRUCTURES 1008 10082 INTRO TO CIRCUIT THEORY 1008 10083 INTRO TO DYNAMICS 12 rows selected.
A.7 Tag 7, »Daten mit SQL ändern« Test 1. Konstruieren Sie eine SQL-Anweisung, die einen Kurs mit den folgenden Eigenschaften hinzufügt: Department_ID = 1002 (BIOLOGY), Course_ID = 137, Title = 'INSECT BEHAVIOR', Description = 'Indepth study of insect societies and their behavior patterns', Units = 3, keine zusätzlichen Gebühren. Antwort: Hier ist die INSERT-Anweisung: insert into Course (Department_ID, Course_ID, Title, Description, Units) values (1002, 137, 'INSECT BEHAVIOR', 'In-depth study of insect societies and their behavior patterns', 3); 2. Konstruieren Sie eine SQL-Anweisung, die zusätzliche Gebühren in Höhe von $ 50 für alle Kurse in der Abteilung Philosophie einträgt. Antwort: Hier ist die UPDATE-Anweisung: update Course set Additional_Fees = 50 where Department_ID = 1000; 3. Konstruieren Sie eine SQL-Anweisung, die eine geplante Klasse löscht, wenn diese in Viling Tower abgehalten werden soll. Antwort: Hier ist die DELETE-Anweisung:
delete from Class where Class_Building = 'VILING TOWER';
Übung Einige der Lehrer in Flugle College haben sich entschieden, eine neue Abteilung namens Integrated Studies einzurichten. Als Folge davon werden die Abteilungen für Englisch, Geschichte und Philosophie zusammengelegt, um die Abteilung für Integrated Studies zu bilden. Die Department ID für diese neue Abteilung wird 1009 sein. Erzeugen Sie in der Datenbank eine Abteilung für Integrated Studies (ohne die existierenden Abteilungen zu löschen). Verändern Sie zudem die Tabelle Instructor so, dass die Lehrer in den Abteilungen für Englisch, Geschichte und Philosophie der Abteilung für Integrated Studies zugewiesen werden. Antwort: Sie können mit dieser Abfrage ermitteln, dass sechs Dozenten mit den Departments English, History und Philosophy verbunden sind: SQL> select Instructor_ID from Instructor where Department_ID in (1000, 1006, 1007); INSTRUCTOR_ID ------------331 391 491 149 944 332 6 rows selected. Erstellen Sie zuerst das neue Department: SQL> insert into Department (Department_ID, Department_Name) values (1009, 'Integrated Studies'); 1 row created. Erneuern Sie als nächstes die Dozenten-Tabelle dahingehend, dass jeder Dozent der Fachbereiche English, History und Philosophy mit Integrated Studies verbunden wird: SQL> update Instructor set Department_ID = 1009 where Department_ID in (1000, 1006, 1007); 6 rows updated Wenn Sie die Dozenten-Tabelle abfragen, werden Sie sehen, dass dieselben sechs Dozenten jetzt zum Department Integrated Studies gehören: SQL> select Instructor_ID from Instructor
where Department_ID = 1009; INSTRUCTOR_ID ------------331 391 491 149 944 332 6 rows selected. Führen Sie abschließend ein ROLLBACK durch, so dass die Änderungen nicht von Dauer sind. SQL> rollback; Rollback complete.
A.8 Tag 8, »SQL-Funktionen« Test 1. Erstellen Sie eine SQL-Anweisung, die die Zeilen der Tabelle Instructor wie im folgenden Beispiel gezeigt liefert: Professor Parker Antwort: Die SELECT-Anweisung ist SQL> select initcap(position || ' ' || last_name) 2 from instructor; 2. Erstellen Sie eine SQL-Anweisung, die den Instructor liefert, dessen Nachname der erste in der alphabetischen Reihenfolge ist. Antwort: Die SELECT-Anweisung ist SQL> select min(Last_Name) 2 from Instructor; Min(LAST_NAME) -------------ANGELO
Übung Erzeugen Sie eine Tabelle mit Namen employee mit den Spalten name (Zeichenkette) und hiredate (Datum). Erzeugen Sie eine Zeile in der Tabelle, wobei Sie in die Spalte Name das Literal 'Damasio' und in die Spalte hiredate das geeignet konvertierte Literal '01 März 2001' einfügen. Machen Sie die Änderung an der Tabelle dauerhaft. Antwort: SQL> create table employee (name varchar2(30), hiredate date); Table created.
SQL> insert into employee (name, hiredate) values ('Damasio', to_date('01 März 2001','DD Month YYYY', 'NLS_DATE_LANGUAGE=German')); 1 row created. SQL> commit; Commit complete. Da der Monat März in deutscher Schreibweise angegeben ist, muss NLS_DATE_LANGUAGE beim Aufruf von to_date verwendet werden, es sei denn NLS_DATE_LANGUAGE wäre z.B. mit alter session auf German gesetzt worden. Andernfalls gibt Oracle9i den Fehlercode ORA-01843: not a valid month aus.
A.9 Tag 9, »Fortgeschrittene Abfragen mit SQL« Test 1. Erstellen Sie eine SQL-Anweisung, die den Nachnamen eines Lehrers liefert, der einen Kurs mit zusätzlichen Gebühren über $ 50 unterrichtet. Antwort: Hier ist ein Drei-Tabellen-Join, der die Information zurückgibt: SQL> select Last_Name from Class CL, Instructor I, Course CO where CL.Instructor_ID = I.Instructor_ID and CL.Course_ID = CO.Course_ID and Additional_Fees > 50; LAST_NAME --------WEISS 2. Erstellen Sie eine SQL-Anweisung, die eine Liste von Städten, in denen Studenten wohnen, sowie die Zahl der Studenten in jeder Stadt liefert. Antwort: Hier ist die SELECT-Anweisung, welche die gewünschten Resultate liefert: SQL> select City, count(*) from Student group by City; CITY COUNT(*) ------------------------- -------DOVER 14 SPRINGFIELD 17 3. Erstellen Sie eine Datensicht, die jede Klasse auflistet - ihre Class_ID, Course_ID und den Raum - , die am Montag zusammentrifft. Antwort: Hier ist die CREATE VIEW-Anweisung, welche die geforderte Aktion ausführt: SQL> create view Classes_on_Monday as select Class_ID, Course_ID, Class_Room from Class CL, Schedule_Type ST, Schedule_Type_Details STD where CL.Schedule_ID = ST.Schedule_ID and
Übung Die Anzahl der Lehrer am Flugle College ist 18, die Anzahl der angebotenen Klassen ist 10. Trotzdem ist die Zahl der eindeutigen Instructor_IDs in der Tabelle Class gleich 9. Geben Sie unter Verwendung dieser Tabelle und von SQL eine vollständige Erklärung. Antwort: SQL> select count(*) from Class; COUNT(*) --------10 Und es gibt neun Dozenten für diese Klassen: SQL> select distinct Instructor_ID from Class; INSTRUCTOR_ID ------------131 149 331 405 490 491 505 944 983 9 rows selected. Lassen Sie uns sehen, ob einer der Dozenten mehr als eine Klasse unterrichtet: SQL> select Instructor_ID from Class having count(*) > 1 group by Instructor_ID; INSTRUCTOR_ID ------------505 Dozent 505, bei dem es sich um Jerrold Jason handelt, unterrichtet mehr als eine Klasse: SQL> select Class_ID from Class
where Instructor_ID = 505; CLASS_ID -------108600 103400
A.10 Tag 10, »Datenbanksicherheit und Optimierung« Test 1. Richtig oder falsch? Eine Datenbankrolle kann Benutzern und anderen Datenbankrollen zugeteilt werden. Antwort: Richtig. Sie können bestimmte typische Rollen erzeugen, die einige grundlegende Berechtigungen besitzen, und diese Rollen dann mehr spezialisierten Rollen zuteilen. 2. Richtig oder falsch? Das Hinzufügen eines Index verbessert immer die Leistung einer Anwendung. Antwort: Falsch. Ein nicht-eindeutiger Index sollte nur dann hinzugefügt werden, wenn die indizierte Spalte häufig in Abfragen der Tabelle benutzt wird. Außerdem kann der Index die Gesamtleistung herabsetzen, wenn die Inhalte der Tabelle häufig geändert werden, da der Index bei jeder Änderung an der Tabelle ebenfalls verändert werden muss. 3. Richtig oder falsch? Jeder Oracle-Benutzer kann ein öffentliches Synonym erstellen, solange dieses auf eine Tabelle verweist, die sich im Besitz des Benutzers befindet. Antwort: Falsch. Das Erzeugen eines öffentlichen Synonyms erfordert die DBA-Rolle oder das Systemprivileg CREATE PUBLIC SYNONYM.
Übung Sie können diese Fragen allgemein beantworten, ohne die Verwendung von SQL- Anweisungen: Wenn Sie der DBA am Flugle College wären, welche Datenbankrolle würden Sie erzeugen? Welche Berechtigungen würden Sie dieser Rolle gewähren? Antwort: Sie würden wahrscheinlich diese Rollen erzeugen: ● ● ● ●
Eine Rolle für Studenten Eine Rolle für Dozenten Eine Rolle für die Person des Vorsitzenden eines Departments Eine Rolle für den College-Administrator
Objektprivilegien werden im Allgemeinen als Create(C), Read(R), Update(U), Delete(D) oder CRUD bezeichnet. Eine Matrix, die alle diese Berechtigungen für jede Rolle definiert, wird als CRUD-Matrix bezeichnet. Berechtigungen für die Studenten-Rolle: Class: R Class_Location: R Course: R
Department: R Instructor: R Schedule_Type: R Schedule_Type_Details: R Student: R (nur für die eigenen Daten) Student_Schedule: CRUD (nur für die eigenen Daten) Berechtigungen für die Dozenten-Rolle: Class: R Class_Location: R Course: R Department: R Instructor: RU (nur für die eigenen Daten) Schedule_Type: R Schedule_Type_Details: R Student: R (nur für die eigenen Daten) Student_Schedule: RU Berechtigungen für die Rolle des Department-Vorsitzenden: Class: CRUD Class_Location: R Course: CRUD Department: R Instructor: CRUD Schedule_Type: R Schedule_Type_Details: R Student: R Student_Schedule: RU Berechtigungen für die Rolle des College-Administrators:
A.11 Tag 11, »Tabellen und Indizes für Fortgeschrittene« Test 1. Richtig oder falsch? Eine gewöhnliche Tabelle kann mit ALTER TABLE in eine partitionierte Tabelle überführt werden. Antwort: Falsch. Sie können CREATE TABLE ... AS SELECT oder das Paket DBMS_REDEFINITION einsetzen, um eine nicht partitionierte Tabelle in eine partitionierte Tabelle zu überführen. 2. Was ist an dieser Anweisung falsch? TRUNCATE PARTITION q102; Antwort: Es fehlen die Schlüsselwörter ALTER TABLE. Die korrekte Anweisung lautet: ALTER TABLE TRUNCATE PARTITION q102; 3. Richtig oder falsch? Der CONTEXT Index unterscheidet in der Standardeinstellung zwischen Großund Kleinschreibung. Antwort: Falsch. Der CONTEXT Index unterscheidet nur dann zwischen Groß- und Kleinschreibung, wenn das Attribut MIXED_CASE auf YES gesetzt wird.
Übung Legen Sie einen CONTEXT Index auf der Spalte DESCRIPTION der Tabelle COURSE an. Erstellen Sie eine Abfrage, die Kursidentifikation, Kursbezeichnung und Beschreibung aller Kurse ausgibt, deren Beschreibung die Namen Shakespeare oder Milton enthält. Antwort: Zunächst erstellen Sie einen Index des Typs CTXSYS.CONTEXT auf der Spalte description der Tabelle course. Anschließend verwenden Sie den Operator CONTAINS, um die Suche im Textindex zu formulieren. CREATE INDEX course_desc ON course(description) INDEXTYPE IS CTXSYS.CONTEXT; select course_id, title, description
from course where CONTAINS(description,'Shakespeare OR Milton') >0;
A.12 Tag 12, »Programmierung einer Oracle- Datenbank mit PL/SQL« Test 1. Nennen Sie die drei Teile eines PL/SQL-Unterprogramms. Antwort: Die drei Abschnitte eines PL/SQL-Unterprogramms sind der Deklarationsteil, der ausführbare Abschnitt und der Abschnitt für Ausnahmebehandlung (exception handler). 2. Richtig oder falsch? Eine PL/SQL-Variable, die einen Spaltenwert speichert, muss denselben Namen wie die Spalte haben. Antwort: Falsch. Eine PL/SQL-Variable, die einen Spaltenwert speichert, kann jeden gültigen Namen besitzen. 3. Warum ist es eine gute Angewohnheit, bei der Deklaration von Variablen %type zu verwenden? Antwort: Es ist sinnvoll, bei der Variablendeklaration %TYPE zu benutzen, weil dies den benötigten Wartungsaufwand für den PL/SQL-Code reduziert. Wenn Sie eine Variable ohne die Verwendung von %TYPE deklarieren und die korrespondierende Spaltendefinition sich ändert, müssen Sie die Variablendeklaration anpassen. Wenn Sie %TYPE benutzen, müssen Sie die Variablendeklaration nicht ändern.
Übung Programmieren Sie einen anonymen Block, der eine Prozedur enthält. Die Prozedur soll für jeden Kurs am Flugle College, der bisher keine zusätzlichen Gebühren kostet, eine zusätzliche Gebühr von 50 eintragen. Die Prozedur soll eine Department_ID als Argument akzeptieren. Eine Änderung darf nur ausgeführt werden, wenn der Wert von Department_ ID angegeben wird. Antwort: Lassen Sie uns »bisher keine Zusatzgebühren« so interpretieren, dass diese entweder den Wert 0 oder NULL haben. Betrachten wir eine mögliche Lösung: Wie Sie in der folgenden Abfrage sehen, gibt es am Lehrstuhl Mathematik einen Kurs ohne Zusatzgebühren mit der Kurs-ID 10031. SQL> select Course_ID, Additional_Fees from Course where Department_ID = 1003; COURSE_ID ADDITIONAL_FEES ---------- --------------10031 0 10033 7.5 10032 55 Hier ist der anonyme Block, der eine Prozedur (siehe Zeile 2) enthält, die in Zeile 13 aufgerufen wird. Wie festgelegt wurde, enthält die Prozedur ein einzelnes Argument: Department_ID.
SQL> declare procedure Set_Default_Course_Fees (arg_Department_ID course.department_id%type) is begin update Course set Additional_Fees = 50 where Department_ID = arg_Department_ID and Additional_Fees is null or Additional_Fees = 0; end; -- Main block. begin Set_Default_Course_Fees (1003); end; / PL/SQL procedure successfully completed. Sie können aus der folgenden Abfrage ersehen, dass die Prozedur die Zusatzgebühren für die drei Kurse auf 50 gesetzt hat: SQL> select Course_ID, Additional_Fees from Course where Department_ID = 1003; COURSE_ID ADDITIONAL_FEES ---------- --------------10031 50 10033 7.5 10032 55
A.13 Tag 13, »Programmentwicklung mit PL/SQL« Test 1. Richtig oder falsch? Eine gespeicherte Prozedur kann eine gespeicherte Funktion aufrufen, eine gespeicherte Funktion jedoch keine gespeicherte Prozedur. Antwort: Falsch. Eine gespeicherte Prozedur kann andere Prozeduren oder Funktionen aufrufen. Eine gespeicherte Funktion kann ebenfalls andere Prozeduren und Funktionen aufrufen. 2. Nennen Sie drei Gründe für das Speichern von Prozeduren, Funktionen und Paketen in einer Anwendung. Antwort: Effizienz, Wiederverwendbarkeit und Portabilität. 3. Welche Werkzeuge können verwendet werden, um PL/SQL-Unterprogramme zu erstellen? Antwort: SQL*Plus, SQL Worksheet, Procedure Builder und andere Werkzeuge von Drittanbietern. Procedure Builder macht die Arbeit der PL/SQL-Entwicklung viel leichter als die Verwendung von SQL*Plus oder SQL Worksheet. 4. Wenn der Ausdruck x > 32 einer PL/SQL-Variablen zugewiesen wird, wie ist dann der Datentyp der
Variablen? Antwort: Boolean. Einer Boolean-Variablen kann nur ein Boolean-Ausdruck zugewiesen werden.
Übungen 1. Erzeugen Sie einen anonymen PL/SQL-Block, der die Prozedur Assign_Grade aus dem Flugle-Paket aufruft und Anna Anastatia für den Kurs »INTRO TO BIOLOGY« am Lehrstuhl für Biologie die Note B zuweist. Antwort: Wenn Sie die Tabellen Department, Student, Course und Class abfragen, werden Sie folgendes finden: ● ●
Die Student_ID von Anna Anastatia ist 10231324. Die Class_ID des Kurses »INTRO TO BIOLOGY« ist 109100 Wie Sie in der folgenden Abfrage sehen können, hat Anna bisher noch keinen Abschluss im Kurs »INTRO TO BIOLOGY« erreicht: SQL> select * from Student_Schedule where student_Id=10231324; STUDENT_ID CLASS_ID GR DATE_GRADE_ASSIGNED ---------- ---------- -- ------------------10231324 109100 Hier ist ein anonymer Block, der die Prozedur Assign_Grade im Flugle Paket aufrufen wird: SQL> set serveroutput on SQL> declare Status number; begin Flugle.Assign_Grade(10231324,109100,'B', status); dbms_output.put_line('status: ' || to_char(status)); end; / status: 0 PL/SQL procedure successfully completed. Noch einmal: Wenn Sie die Student_Schedule-Tabelle einsehen, werden Sie feststellen, dass Anna jetzt für die Klasse ein B zugewiesen bekommen hat. SQL> select * from Student_Schedule where student_Id=10231324; STUDENT_ID CLASS_ID GR DATE_GRADE_ASSIGNED ---------- ---------- -- ------------------10231324 109100 B 07.11.2001 15:01:09
2. Der Student Jackson Smythe hat eine gespeicherte Funktion namens Change_My_Grade mit zwei Argumenten erstellt: Student_ID und Class_ID. Die Funktion ändert die Note für jeden beliebigen Studenten und eine beliebige Klasse auf A+. Schreiben Sie diese Funktion. Antwort: Hier ist die Funktion, welche diese niederträchtige Tat ausführt: SQL> create or replace function change_My_Grade(arg_student_ID IN
number,
arg_class_ID IN number) return number is counter number; status number; normal CONSTANT number := 0; unsuccessful CONSTANT number := -1; not_registered exception; begin status := normal; -- Determine if the student is registered for this class. select count(*) into counter from student_schedule where student_id = arg_student_id and class_id = arg_class_id; if counter = 0 then -- The student is not taking this class. raise not_registered; status := unsuccessful; else -- Assign the grade for this class. update student_schedule set grade = 'A+' where student_id = arg_student_id and class_id = arg_class_id; end if; commit; return status; exception when not_registered then raise_application_error (-21003, 'Student not registered for class'); when others then null; end; / Function created.
Sie finden die Funktion in der Datei \sql\change_my_grade.sql auf der CD-ROM. Um die Funktion zu testen, erstellen Sie einen anonymen Block, der Change_My_Grade für die Note von Paul Fernandez für die Klasse mit Class ID 104500 aufruft. SQL> declare Status number; begin status := Change_My_Grade(10231311,104500); dbms_output.put_line('status: ' || to_char(status)); end; /
status: 0 PL/SQL procedure successfully completed. Wie Sie bei der folgenden Abfrage sehen können, arbeitet die Funktion wie angekündigt: SQL> select * from Student_Schedule; STUDENT_ID CLASS_ID ------------------- -------------------10231311 104200 10231311 104500 10231324 109100 10231311 109100
GR -B A+
DATE_GRAD --------02-JUN-97 04-JUN-97
A.14 Tag 14, »Weitere PL/SQL-Programmier- techniken« Test 1. Wie werden SQLCODE und SQLERRM in PL/SQL-Unterprogrammen verwendet? Antwort: SQLCODE gibt den Oracle-Fehler zurück, der von der letzten SQL-Operation herrührt. Ein Fehlercode von 0 zeigt den normalen Abschluss der Operation an. SQLERRM gibt den Text der Oracle-Fehlermeldung zurück, die mit dem Wert in SQLCODE korrespondiert. 2. Richtig oder falsch? Ein PL/SQL-Unterprogramm kann gleichzeitig mehrere Cursors geöffnet haben. Antwort: Richtig. In einem PL/SQL-Unterprogramm gibt es in Bezug auf die Öffnung mehr als eines Cursors zur selben Zeit keine Beschränkung. 3. Welche sind die Vorteile bei der Verwendung von Datenbanktriggern? Antwort: Trigger bieten mehrere Möglichkeiten. Ein Trigger kann komplexe Gültigkeitsprüfungen ausführen, wenn eine Zeile hinzugefügt oder modifiziert wird. Ein Trigger kann einen Spaltenwert ändern, bevor er in einer Tabelle gespeichert wird. Ein Trigger in einer Tabelle kann benutzt werden, um die Inhalte einer anderen Tabelle zu ändern. 4. Welchen Datenbanktrigger würden Sie verwenden, um den in einer Spalte zu speichernden Wert zu verändern, wenn der Tabelle eine neue Zeile hinzugefügt wird? Antwort: Einen Before-Insert-Trigger, der bei jeder Zeile auslösen wird.
Übungen 1. Erstellen Sie eine gespeicherte Funktion Teaching_Load mit einem einzigen Argument - einer Abteilungsnummer (Department_ID). Teaching_Load soll die durchschnittliche Anzahl an Stunden zurückgeben, die Ausbilder in den entsprechenden Abteilungen zurzeit unterrichten. Antwort: Zuerst müssen Sie die gespeicherte Funktion erzeugen: SQL> create or replace function Teaching_Load(arg_Department_ID IN number) return number is number_of_instructors number;
number_of_classes number; avg_teaching_load number; begin -- Get the number of instructors for this department. select count(*) into number_of_instructors from Instructor where Department_ID = arg_Department_ID; -- Get the number of classes for this department. select count(*) into number_of_classes from Class C, Instructor I where I.Department_ID = arg_Department_ID and C.Instructor_ID=I.Instructor_Id; avg_teaching_load := number_of_classes / number_of_instructors; return avg_teaching_load; exception when others then null; end; / Function created. Um die Funktion zu testen, können Sie sie in einer SELECT-Anweisung aufrufen, welche die Tabelle DUAL referenziert. Wie Sie sehen können, unterrichten die Dozenten im Department Biologie im Durchschnitt 0,5 Klassen: SQL> select Teaching_Load(1002) load from dual; LOAD -------.5 2. Erstellen Sie eine gespeicherte Prozedur Suitable_Locations mit einem einzigen Argument - Anzahl der Sitzplätze. Suitable_Locations soll die ersten drei Gebäude und Räume ausdrucken, die die angegebene Anzahl Sitzplätze überschreiten. Antwort: Hier ist eine gespeicherte Prozedur, welche die Anforderungen erfüllt: SQL> create or replace procedure Suitable_Locations (arg_Seating_Capacity IN number) is cursor Get_Locations is select class_building, class_room, seating_capacity from Class_Location where Seating_Capacity >= arg_Seating_Capacity order by Seating_Capacity desc; begin for Get_Locations_Rec in Get_Locations loop exit when Get_Locations%rowcount > 3; dbms_output.put_line('Building: ' || Get_Locations_Rec.Class_Building|| ' Room: ' || Get_Locations_Rec.Class_Room || ' Capacity: ' || to_char(Get_Locations_Rec.Seating_Capacity)); end loop; exception
when others then null; end; / Procedure created. Um die gespeicherte Prozedur zu testen, können Sie einen anonymen Block benutzen, der diese aufruft: SQL> set serveroutput on SQL> declare Status number; begin Suitable_Locations (30); end; / Building: FLUGLE HALL Room: Building: FLUGLE HALL Room: Building: NARROW HALL Room: PL/SQL procedure successfully
3. Schreiben Sie einen Before-Delete-Trigger für die Tabelle Instructor, der eine Exception auslöst, wenn ein Ausbilder laut Plan eine Klasse unterrichtet. Antwort: Hier ist das Skript für die Erstellung des Triggers. In Zeile 17 wird eine Abfrage benutzt, um zu ermitteln, wie viele Klassen ein Dozent unterrichtet; dieser Wert wird in counter gespeichert. In Zeile 11 wird counter ausgewertet; falls er größer als Null ist, löst der Trigger einen Anwendungsfehler aus, was die Löschung des Dozenten verhindert: SQL> create or replace trigger Instructor_BD 2 before delete on Instructor 3 for each row 4 4 declare 5 5 counter number; 6 6 begin 7 7 select count(*) into counter 8 from Class 9 where 10 Instructor_ID = :old.Instructor_ID; 11 11 if counter > 0 then 12 raise_application_error (-20800, 13 'Instructor is scheduled to teach a class'); 14 end if; 15 15 15 end; 16 / Trigger created.
Um den Trigger zu testen, können Sie versuchen, einen Dozenten zu löschen, der gegenwärtig eine Klasse unterrichtet. Wie Sie sehen können, löst der Trigger den Anwendungsfehler - 20800 - aus, was die erfolgreiche Ausführung der DELETEAnweisung verhindert. SQL> delete from Instructor 2 where 3 Instructor_ID = 405; delete from Instructor * Error at line 1: ORA-20800: Instructor is scheduled to teach a class ORA-06512: at line 9 ORA-04088: error during execution of trigger 'FLUGLE.INSTRUCTOR_BD' Wenn Sie jedoch versuchen, einen Dozenten zu löschen, der keine Klasse unterrichtet, wird der Trigger den Anwendungsfehler nicht auslösen: SQL> delete from Instructor 2 where 3 INSTRUCTOR_ID = 332; 1 row deleted.
A.15 Tag 15, »Einführung in Oracle Forms« Test 1. Richtig oder falsch? Oracle Forms Runtime führt nur eine .fmb-Datei aus. Antwort: Falsch. Das Laufzeitmodul von Oracle Forms führt nur .fmx-Dateien aus, keine .fmb-Dateien. 2. Welche sind die vier Modultypen, die mit dem Forms Builder erzeugt werden können? Antwort: Die vier Modultypen sind Formulare, Menüs, PL/SQL-Bibliotheken und ObjektBibliotheken. 3. Richtig oder falsch? Der Forms Builder kann ein Formular nur in einer Oracle- Datenbank speichern, wenn dieses einen Basistabellenblock enthält. Antwort: Falsch. Der Forms Builder kann ein Formular entweder in einer Datenbank oder einem Dateisystem speichern, unabhängig vom Inhalt des Formulars. 4. Richtig oder falsch? Sie können einen Block erzeugen, der auf einer Tabelle basiert, auf die ein anderer Oracle-Benutzer die Rechte besitzt. Antwort: Richtig. Sie können einen Block erstellen, der auf den Inhalten einer Tabelle basiert, die sich im Besitz eines anderen Oracle-Benutzers befindet, solange Ihnen die entsprechenden Berechtigungen für die Tabelle gewährt wurden.
Übung Erzeugen Sie für die Tabelle Instructor ein Formular. Geben Sie eine ORDER-BY- Klausel ein, damit die
angezeigten Datensätze nach der Abteilungs-ID, dem Nachnamen und dem Vornamen des Dozenten sortiert werden. Testen Sie das Formular. Antwort: Sehen Sie sich bitte die Datei Instructor.fmb im Verzeichnis Tag 15 der CD- ROM an.
A.16 Tag 16, »Entwicklung einer Benutzeroberfläche mit Oracle Forms« Test 1. Richtig oder falsch? Ein Master-Block kann nicht auf einer Datensicht (View) basieren, es muss eine Tabelle sein. Antwort: Falsch. Ein Master-Block kann auf einer Datensicht (View) basieren. 2. Richtig oder falsch? Man kann das Erscheinungsbild eines Feldes mit dem Layout Editor oder durch Ändern der Eigenschaften des Feldes verändern. Antwort: Richtig. Sie können die Anzeige-Eigenschaften eines Feldes ändern - zum Beispiel seine Position oder die Schriftart -, indem Sie das Feld mit dem Object Navigator wählen und mit der rechten Maustaste das Eigenschafts-Fenster öffnen. 3. Wie kann die Reihenfolge der Navigation in einer Gruppe von Feldern geändert werden? Antwort: Sie können die Reihenfolge der Navigation in einer Gruppe von Feldern ändern, indem Sie ihre Reihenfolge im Object Navigator ändern.
Übungen 1. Erstellen Sie ein Master-Detail-Formular, um Schedule_Type und Schedule_Type_ Details einzugeben. Antwort: Sehen Sie sich bitte die Datei Schedule_Type.fmb im Verzeichnis Tag 16 auf der CD-ROM an. 2. Erstellen Sie im Formular Course_Class eine Werteliste (LOV) für Schedule_ID, die die Benutzereingabe überprüft. Antwort: Sehen Sie sich bitte die Datei Course_Class_LOV.fmb im Verzeichnis Tag 16 auf der CD-ROM an.
A.17 Tag 17, »Anwendungsentwicklung mit Oracle Forms« Test 1. Richtig oder falsch? Ein Trigger auf Feldebene innerhalb eines Blocks kann nicht auf Felder eines anderen Blocks verweisen. Antwort: Falsch. Es gibt keine Beschränkungen für die Fähigkeit eines Item-LevelTriggers, Elemente in einem anderen Block zu referenzieren. 2. Wie wird in einem Trigger auf Felder in einem Block Bezug genommen?
Antwort: Um ein Feld in einem Trigger zu referenzieren, verwenden Sie die Schreibweise :block-name.item-name. 3. Welche Exception sollte bei einer ungültigen Benutzereingabe aufgerufen werden, wenn Sie einen Trigger auf Feldebene zur Gültigkeitsüberprüfung von Benutzereingaben erstellen? Antwort: FORM_TRIGGER_FAILURE.
Übung Erstellen Sie ein Formular Instructor_Browse, das aus der Menüoption des Unter-Menüs Browse aufgerufen werden kann. Es soll Angaben aus der Tabelle Instructor tabellarisch und sortiert nach dem Nachnamen, Vornamen und den Initialen anzeigen. Antwort: Sehen Sie sich bitte die Dateien Instructor_Browse.fmb und Main_Menu.mmb im Verzeichnis Tag 17 auf der CD-ROM an.
A.18 Tag 18, »Entwicklung von Berichten mit Oracle Reports« Test 1. Richtig oder falsch? Sie können eine RDF-Datei in einem Source Code Control- System wie z.B. PVCS speichern. Antwort: Falsch. Eine RDF-Datei ist eine binäre Datei. Anstatt zu versuchen, eine Versionskontrolle Ihrer RDF-Dateien zu unterstützen, sollten Sie REX-Dateien generieren, wobei es sich um Textdateien handelt, die unter Versionskontrolle gehalten werden können. 2. Nennen Sie die vier Elemente eines Berichtlayouts. Antwort: Kopfbereich (Header), Fußbereich (Footer), Rumpfbereich (Body) und Randbereich (Margin). Nahezu jeder Bericht besitzt ein Layout für den Rumpfbereich. 3. Richtig oder falsch? Ein Bericht kann nicht mehr als zwei Abfragen miteinander verknüpfen. Antwort: Falsch. Sie können mehrere Abfragen integrieren, die zueinander in Beziehung stehen. Sie können beispielsweise einen Bericht erstellen, der auf drei Abfragen basiert: eine Abfrage der Abteilungen, eine Abfrage der Dozenten jeder Abteilung und eine Abfrage der Veranstaltungen, die jeder Dozent betreut.
Übung Erstellen Sie einen Master-Detail-Bericht, der jeden Klassenraum und die für diesen Raum geplanten Veranstaltungen ohne Berücksichtigung der Unterrichtszeiten auflistet (d.h. alle Veranstaltungen, die in einem bestimmten Raum stattfinden sollen). Antwort: Sehen Sie sich bitte die Datei Classroom.rdf im Verzeichnis Tag 18 auf der CD- ROM an.
A.19 Tag 19, »Oracle Graphics und Procedure Builder« Test
1. Welche beiden Dateitypen verwendet Oracle Graphics? Antwort: Eine .ogd-Datei - was für Oracle Graphics Designer-Datei steht - enthält die Definition für ein Diagramm. Wenn Sie den Graphics Builder benutzen, generieren Sie eine .ogr-Datei (Oracle Graphics Runtime) aus einer .ogd-Datei. 2. Nennen Sie drei Diagrammtypen, die mit dem Graphics Builder definiert werden können. Antwort: Linien-, Balken- und Tortengrafik-Diagramme. 3. Richtig oder falsch? Sie können den Stored Program Unit Editor zur Änderung der Paket-Spezifikation, aber nicht des Paketrumpfs (Body) verwenden. Antwort: Falsch. Der Stored Unit Program Editor kann verwendet werden, um sowohl eine Paket-Spezifikation als auch einen Paketrumpf (Body) zu verändern.
Übungen 1. Erstellen Sie ein Tortendiagramm, das die von den einzelnen Fachrichtungen des Flugle College angebotenen Kurse darstellt. Antwort: Sehen Sie sich bitte die Datei Courses_by_Department.ogd im Verzeichnis Tag 19 auf der CD-ROM an. 2. Ändern Sie den Trigger für die Tabelle Student_Schedule so, dass in der Tabelle Student_Schedule_Journal Änderungen nur dann gespeichert werden, wenn der Wert in der Spalte Grade A, B oder C ist. Antwort: Sehen Sie sich bitte die Datei new_trigger_Student_Schedule.sql im Verzeichnis Tag 19 auf der CD-ROM an.
A.20 Tag 20, »Entwicklung von Web-Anwendungen mit Java und JDeveloper« Test 1. Welche Vorteile hat eine Web-Anwendung gegenüber einer Client-Server- Anwendung mit WindowsProgrammen? Antwort: Eine Web-Anwendung kann von jedem PC mit Netzanschluss bedient werden. Einzige Voraussetzung ist ein Internet-Browser, der bei jeder Standard- Installation vorhanden ist. Die aufwändige Verteilung von Software entfällt. 2. Warum haben sich JSP bei der Entwicklung von Web-Anwendungen gegenüber der Programmierung mit Servlets durchgesetzt? Antwort: In einer JSP wird Java nur für die dynamischen Anteile der jeweiligen Web- Seite eingesetzt. Die statischen Anteile werden in HTML geschrieben. Somit kann die Arbeit geteilt werden. Während der Java-Experte funktionierenden Code schreibt, sorgt der WebDesigner für ein gutes »Look and feel« der Anwendung.
Übung
Erstellen Sie eine HTML-Seite mit einem Eingabeformular für einen neuen Studenten. Die HTML-Tags, die Sie benötigen, finden Sie in Tabelle 20.2 und Tabelle 20.5. Wenn das Formular fertig ist, müssen die Eingaben in die Datenbank eingefügt werden. Bislang haben Sie gesehen, wie man mit JDBC Abfragen (SELECT) an die Datenbank sendet. Doch Sie können auch INSERT-Kommandos an die Datenbank senden. Beachten Sie dabei, dass ein INSERT-Kommando im Gegensatz zu einem SELECT keine Ergebnismenge zurückliefert. In den JSP dieses Kapitels wurden stets SELECT Kommandos, deren Ergebnisse mit der Java Klasse ResultSet ausgelesen wurden, an die Datenbank gesendet. In den Listings sehen Sie, dass die SELECT Kommandos mit executeQuery ausgeführt werden. Für INSERT- Kommandos verwenden Sie an gleicher Stelle die Methode execute. Sie benötigen dann weder das ResultSet noch die while-Schleife, die es ausliest. Geben Sie eine Meldung aus, dass der Datensatz erfolgreich eingefügt wurde. Antwort: Zunächst müssen Sie das Formular erstellen. Da dort nur Eingaben gemacht werden sollen, reicht eine einfache HTML-Seite. Datenbankinhalte müssen noch nicht ausgelesen werden. Damit das Formular besser aussieht, werden die Eingabefelder in Tabellenform ausgegeben. Wenn der Benutzer fertig ist, sendet er seine Angaben mit Mausklick an die JSP, die Sie im nächsten Schritt erstellen. Listing 1.1: Formular zum Einfügen eines neuen Studenten Eingabe eines neuen Studenten Geben Sie bitte hier die Daten ein: Nachdem Sie das Formular in der Datei formular.jsp erstellt haben, schreiben Sie im nächsten Schritt die JSP. Sie nimmt die Benutzereingaben entgegen und fügt den neuen Studenten in die Datenbanktabelle ein. Listing 1.2: JSP zum Einfügen eines Studenten <%@ page import="java.sql.*" %> <%@ page errorPage="Error.jsp" contentType="text/html" %> <% /* 4 */ String vname = request.getParameter("stud_vname"); /* 5 */ String nname = request.getParameter("stud_nname"); /* 6 */ String zip = request.getParameter("stud_zip"); /* 7 */ String city = request.getParameter("stud_city"); /* 8 */ String strasse = request.getParameter("stud_strasse"); /* 9 */ String id= request.getParameter("stud_id"); /* 10 */ DriverManager.registerDriver
A.21 Tag 21, »Oracle Business Components for Java« Test 1. Warum setzt man Frameworks in der Anwendungsentwicklung ein? Antwort: Frameworks entlasten von ständig wiederkehrenden Aufgaben wie beispielsweise Datenbankverbindungen verwalten. Der Programmierer kann sich auf den eigentlichen Sinn seiner Anwendung konzentrieren. 2. Warum ist es sinnvoll, ungültige Eingaben trotz bestehender Datenbank-Constraints durch BC4JValidierungsregeln abzufangen? Antwort: Wenn ungültige Eingaben schon in der BC4J-Anwendung abgefangen werden, entsteht auf dem Datenbankrechner weniger Last. Wenn dieser stark beansprucht wird, ist es durchaus sinnvoll, Last durch BC4J-Validierungsregeln auf den Anwendungsserver zu verlagern.
Übung
1. Erstellen Sie mit BC4J eine Anwendung, mit der die Verwaltung der Studenten möglich wird. Sie sollten neue Studenten eingeben und bestehende löschen oder ändern können. Antwort: Arbeiten Sie mit den Assistenten des JDeveloper und orientieren Sie sich zur Lösung der Aufgabe an den Bildschirmfotos im Kapitel. Die Aufgabe ist der behandelten BC4J-Anwendung sehr ähnlich; lediglich die zugrunde liegenden Tabellen in der Datenbank sind andere, woraus sich auch andere Validierungsregeln ergeben. In der beschriebenen BC4J-Anwendung wird auf die Tabellen COURSE und DEPARTMENT zugegriffen. Ihre Lösung muss mit der Tabelle STUDENT arbeiten. Versuchen Sie, Ihre Lösung einfach zu halten; fangen Sie also nicht jeden möglichen Eingabefehler durch eine Validierungsregel ab.
Oracle Business Components for Java Im vorangegangenen Kapitel haben Sie gelernt, wie man eine einfache Web-Anwendung erstellt. Unsere Anwendung hat sich jedoch darauf beschränkt, Tabellendaten anzuzeigen. Im realen Leben werden hingegen weit höhere Anforderungen gestellt. Der Datenbankzugriff ist nur ein Teil der Arbeit. Zum Schluss des Kapitels konnten Sie lesen, welche Techniken bei Web-Anwendungen eingesetzt werden, damit der entwickelte Code wartbar und überschaubar bleibt. Diese Ausführungen bezogen sich jedoch, wie das gesamte Kapitel, nur auf den darstellenden Teil der Anwendung. Weiterhin haben Sie im letzten Kapitel den gesamten Code Ihrer Anwendung selbst geschrieben, wobei die Anwendung selbst recht einfach war. Stellen Sie sich vor, Ihre Anwendung wird komplexer. Wenn Sie verschiedenste Daten aus der Datenbank einlesen, auswerten und darstellen möchten, haben Sie viel Arbeit... ● ● ● ●
... die unterschiedlichen Komponenten Ihrer Anwendung miteinander zu verbinden, einmal geschriebene Komponenten wieder zu verwerten, performant mit der Datenbank zu kommunizieren und nicht zuletzt... ... ständig wiederkehrende Arbeiten (SQL-Abfragen) immer wieder neu zu codieren.
Diese Arbeiten können durch den Einsatz von Frameworks erleichtert werden. Frameworks erleichtern dem Programmierer die Arbeit und bestehen im Wesentlichen aus einem Codegerüst und Werkzeugen. Entscheidend ist, dass der Programmierer das Framework auf einer sehr hohen, anwendungsbezogenen Ebene bedienen kann. Mit einem Framework programmiert der Entwickler keine Datenbankzugriffe und kein SQL mehr. Vielmehr definiert er Sichten auf Daten, stellt aus verschiedenen Tabellen komplexe Objekte zusammen und entwickelt die Logik, den eigentlichen Sinn der Anwendung.
Oracle stellt Ihnen ein solches Framework zur Verfügung. Oracle Business Components for Java (BC4J) erleichtern Ihnen in den gerade genannten Aspekten die Arbeit. Abbildung 21.1 zeigt den grundsätzlichen Unterschied zwischen der Art und Weise, wie Sie im letzten Kapitel eine Anwendung geschrieben haben, und dem Einsatz eines Frameworks.
Abbildung 21.1: Frameworks im Vergleich zum letzten Kapitel Sie sehen, dass eine auf BC4J basierende Anwendung im Gegensatz zu der im letzten Kapitel nicht mehr direkt mit der Datenbank kommuniziert. Die JSP, die für die Darstellung verantwortlich sind, setzen auf BC4J-Komponenten auf. Diese wiederum kommunizieren untereinander und mit der Datenbank. Ein solches Zusammenspiel von Komponenten ist nur in einer speziellen Laufzeitumgebung möglich. Diese Laufzeitumgebung erstellt (instanziiert) Objekte bei Bedarf, verwaltet und synchronisiert sie mit der Datenbank. Im Framework BC4J wird diese Umgebung als BC4J Runtime bezeichnet. Der Anwendungsentwickler kann sich vollständig auf das Framework verlassen. Bei Anforderung eines BC4J-Objektes hat er stets die Gewähr, dass es den aktuellen Stand in der Datenbank wiedergibt. Wenn er es zerstört oder freigibt, kann er sicher sein, dass gemachte Änderungen in der Datenbank gesichert werden. Die BC4J Runtime ist im JDeveloper und im Oracle Internet Application Server vorhanden.
Sie können Ihre Anwendungen also im JDeveloper nach der Entwicklung testen.
21.1 Komponenten und Aufbau des BC4J-Framework BC4J Anwendungen setzen Datenobjekte (Entity Objects) ein, um Tabelleninhalte der Datenbank abzubilden. Diese Datenobjekte nehmen Änderungsanfragen entgegen und synchronisieren sich bei Bedarf mit der Datenbank. Ebenso enthalten diese Objekte datennahe Logik, also beispielsweise Validierungsregeln. Sie werden in der Übung sehen, was darunter zu verstehen ist. Datenobjekte, die zueinander in Beziehung stehen, werden mit Assoziationen (Associations) miteinander verknüpft. Anhand dieser Verknüpfungen ist das BC4J- Framework in der Lage, von einem Objekt zum anderen zu navigieren und beispielsweise zu einem Fachbereich alle zugehörigen Kurse zu finden. Datenbankseitig verwendet BC4J natürlich die dort hinterlegten Fremdschlüssel. Ansichtsobjekte (View Objects) ermöglichen Ihnen verschiedenste Sichten auf Ihre Daten. Der Aufbau der Datenobjekte richtet sich stets nach den Tabellen in der Datenbank. Möglicherweise benötigt Ihre Anwendung jedoch eine aus zwei Tabellen zusammengesetzte Ansicht. Ohne Framework-Unterstützung müssten Sie die Zusammenführung der Tabellen selbst programmieren. Um Ihnen diese Arbeit abzunehmen, bietet BC4J Ansichtsobjekte an, die Sie entsprechend konfigurieren können. Sie können mit den in diesem Buch erworbenen Kenntnissen über SQL Ansichten beliebiger Komplexität erzeugen, ohne auch nur eine Zeile Java-Code zu schreiben. Zu den Ansichtsobjekten gehören auch die Ansichtsverknüpfungen (View Links). Diese sind den schon erwähnten Assoziationen sehr ähnlich und dienen dazu, zusammengehörige Ansichtsobjekte miteinander zu verbinden. So werden Master-Detail- Formulare möglich. Für das Beispiel von Kursen und Fachbereichen können Sie bei dieser Art von Formularen in einem Teil des Bildschirms einen Fachbereich auswählen, woraufhin im anderen Teil automatisch die Kurse dieses Fachbereichs erscheinen. Das Framework navigiert dabei anhand der Ansichtsverknüpfungen. Alle zu Ihrer Anwendung gehörenden BC4J-Komponenten werden in einem entsprechenden Anwendungsmodul (Application module) zusammengefasst. Aufbauend auf diesem Modul bietet BC4J Ihnen die Möglichkeit, Benutzeroberflächen zusammenzustellen. Weiter hinten in diesem Kapitel lernen Sie diese sehr mächtige Fähigkeit kennen. Sie werden nun eine Anwendung mit BC4J erstellen, die ... ● ● ●
einige Datenobjekte, einige Ansichten, darunter eine frei definierte und eine Benutzeroberfläche enthält.
Technisch gesehen ist eine BC4J Komponente aus zwei Bestandteilen zusammengesetzt. Der Programmcode wird als Java-Klasse implementiert. Deklaratorische Einstellungen
werden in einer XML-Datei abgelegt. Die Verknüpfungen der Attribute zu den konkreten Tabellen in der Datenbank sind deklaratorischer Natur und werden daher in die XML- Datei aufgenommen. Hätte man auch diese Einstellungen in Java implementiert, so wäre es erforderlich, nach jeder Änderung die Java-Klasse neu zu kompilieren. Solange die Objektstruktur gleich bleibt, können Änderungen an den Einstellungen somit recht schnell und einfach durchgeführt werden. Sie können alle Dateien in Ihrem JDeveloper- Projekt sehen und jederzeit verfolgen, wo welches Merkmal hinterlegt ist.
In diesem Kapitel werden Sie die Assistenten im JDeveloper intensiv nutzen. Diese Assistenten nehmen Ihnen viel Arbeit bei der Zusammenstellung Ihrer Anwendung ab. Das bedeutet jedoch nicht, dass Sie zwingend auf die Assistenten angewiesen sind. Das gesamte BC4J Framework ist offen und dokumentiert. So können Sie auch ohne den JDeveloper BC4J-Anwendungen entwickeln. Das bedeutet jedoch, dass Sie den Code, der vom JDeveloper generiert wird, selbst schreiben müssen. Die Werkzeugunterstützung ist jedoch gerade ein Vorteil des BC4J Frameworks.
21.2 Eine erste Anwendung mit BC4J Starten Sie nun den JDeveloper, wenn Sie das nicht schon getan haben. Da wir nun eine neue Anwendung erstellen, ist es am besten, Sie legen sich ein neues Projekt an. Wenn Sie möchten, können Sie natürlich auch einen neuen Arbeitsbereich (Workspace) anlegen, das ist aber nicht unbedingt notwendig. Stellen Sie als nächstes sicher, dass im JDeveloper eine Datenbankverbindung eingerichtet ist. Die Beispiele in diesem Kapitel werden sich wie im letzten wieder auf das Flugle- College beziehen. Wenn Sie feststellen, dass Sie noch keine Datenbankverbindung haben, schlagen Sie bitte im letzten Kapitel nach und richten Sie eine ein. Nun ist alles bereit. Klicken Sie zunächst auf File, dann auf New. In dem darauf folgenden Fenster, welches Sie bereits kennen, wählen Sie links Business Components aus. Sie sollten auf Ihrem Bildschirm Abbildung 21.2 wiedererkennen.
Abbildung 21.2: Eine neue BC4J-Anwendung Die einzige Komponente, die Sie hier auswählen können, heißt Business Components (rechts oben). Alle anderen Auswahlpunkte beziehen sich auf Komponenten einer BC4JAnwendung. Um solche Teile hinzufügen zu können, müssen Sie natürlich zuerst eine Anwendung haben. Wählen Sie also Business Components aus und klicken Sie auf OK. Daraufhin erscheint der in Abbildung 21.3 gezeigte Assistent.
Abbildung 21.3: Eine neue BC4J-Anwendung, 1. Schritt Diese Seite soll Ihnen, wie es im JDeveloper üblich ist, zeigen, wo Sie sich überhaupt befinden und was Sie in den nächsten Schritten tun werden. Sie werden im Einzelnen folgendes festlegen: ●
● ● ●
Welche Datenbankverbindung soll für die BC4J-Anwendung herangezogen werden? Die Einstellungen dieser Datenbankverbindung werden in Ihr Projekt übernommen. Das bedeutet jedoch nicht, dass zum Ablauf der Anwendung stets der JDeveloper erforderlich ist. Wenn Ihre Anwendung nach Entwicklung in einem Anwendungsserver läuft, findet dieser die Datenbank auch ohne JDeveloper wieder. Wie sollen die Java-Klassen geordnet werden? Welchen Namen soll das Java-Paket erhalten? Aus welchen Komponenten soll Ihre Anwendung bestehen?
Klicken Sie auf Next bzw. Weiter, um zu beginnen. Der nächste Schritt ist in Abbildung 21.4 dargestellt und umfasst die Festlegung der Datenbankverbindung. Wenn Sie auf mehreren Datenbanken arbeiten und auch schon zu jeder eine Verbindung eingerichtet haben, können Sie hier auswählen, auf welcher Datenbank die BC4JAnwendung arbeiten soll. Haben Sie bislang nur eine Datenbankverbindung eingerichtet, so haben Sie auch keine Wahlmöglichkeit. Nehmen Sie die, die da ist. Sicher sind Ihnen auch die zwei anderen Auswahlboxen aufgefallen. SQL Flavor bezieht sich
auf den SQL-Dialekt, mit dem sich Ihre BC4J-Anwendung mit der Datenbank unterhalten soll. Da Sie auf einer Oracle-Datenbank arbeiten, belassen Sie die Einstellung bei Oracle. Damit werden die Oracle-spezifischen SQL-Erweiterungen für Ihre Anwendung nutzbar. Type Map bezieht sich auf die Art und Weise, wie die Datentypen der Tabellenspalten in der Datenbank auf Java-Datentypen abgebildet werden. Auch hier haben Sie die Auswahl zwischen dem Standard (Java) und einer Oracle-spezifischen Einstellung. Es ist empfehlenswert, die Auswahl bei Oracle zu belassen. Klicken Sie, wenn Sie fertig sind, auf Next bzw. Weiter. Bevor Sie Abbildung 21.5 sehen können, testet der JDeveloper die von Ihnen ausgewählte Datenbankverbindung.
Abbildung 21.4: Datenbankverbindung der BC4J-Anwendung
Abbildung 21.5: Java-Paketname für Ihre BC4J-Anwendung Wenn der Test erfolgreich war, werden Sie im nächsten Schritt einen Package Name angeben. Alle Java-Klassen Ihrer BC4J-Anwendung werden in Paketen organisiert. Den Namen des Gesamtpaketes können Sie frei festlegen. Geben Sie beispielsweise BC4J_Tag21 ein und klicken Sie auf Next bzw. Weiter.
Abbildung 21.6: Auswahl einer Tabelle für Datenobjekte In Abbildung 21.6 fügen Sie die erste Komponente Ihrer BC4J-Anwendung hinzu. Wie bereits erwähnt, repräsentieren Datenobjekte (Entity Objects) die Tabellen in der Datenbank, mit denen Ihre BC4J-Anwendung arbeitet. Damit die Anwendung die Daten lesen oder verändern kann, benötigt sie jedoch eine Struktur, die diese Daten repräsentiert und stets mit der Datenbank synchronisiert wird. Ein solches Datenobjekt konfigurieren Sie hier. Anhand des Textes Create Entity Objects and Associations... erkennen Sie, dass hier auch Assoziationen zwischen den Datenobjekten erstellt werden. Der JDeveloper orientiert sich dazu anhand den in der Datenbank hinterlegten Fremdschlüsselbeziehungen zwischen den Tabellen. An oberster Stelle sehen Sie ein Auswahlmenü, in dem das Datenbankschema (Benutzername der Datenbankverbindung) angezeigt ist. Wenn Sie dieses Menü aufklappen, sehen Sie auch die anderen Schemas der Datenbank. Sie können somit auch mit den Tabellen anderer Benutzer arbeiten, sofern Sie entsprechende Privilegien haben. Darunter sehen Sie die Tabellen des gerade ausgewählten Benutzers (hier FLUGLE). Diese Ansicht zeigt Ihnen in der Standardkonfiguration nur Tabellen. Möchten Sie zusätzlich benutzerdefinierte Sichten (Views) oder Synonyme sehen, klicken Sie die entsprechenden Kästchen an. Um eine Tabelle als Datenobjekt in Ihre Anwendung zu übernehmen, klicken Sie den entsprechenden Namen an und übernehmen Sie ihn auf die rechte Seite. Um Tabellen anderer Datenbankbenutzer zu übernehmen, wählen Sie oben einen anderen Benutzer aus.
Dessen Tabellen erscheinen nun auf der linken Seite, während die von Ihnen bereits ausgewählten auf der rechten Seite bleiben. Übernehmen Sie nun für Ihre Anwendung die Tabellen DEPARTMENT und COURSE des Benutzers FLUGLE auf die rechte Seite. Der JDeveloper wird Ihre Java-Objekte gemäß den Tabellennamen benennen. Wenn Sie eigene Namen vergeben möchten, können Sie das mit dem Button Edit tun. Im unteren Abschnitt des Fensters sehen Sie noch zwei Kästchen für zusätzliche BC4J-Komponenten. Diese sind bereits angekreuzt. Der JDeveloper wird also zu jedem Datenobjekt ein passendes Ansichtsobjekt und ein Anwendungsmodul, das alle Komponenten enthält, erzeugen. Klicken Sie auf Next bzw. Weiter, um fortzufahren.
Abbildung 21.7: Übersicht über die Komponenten Ihrer BC4J-Anwendung In Abbildung 21.7 sehen Sie nochmals eine Zusammenstellung Ihrer Angaben: ● ● ● ●
Ihre Angaben zur Datenbankverbindung Datenobjekte Ansichtsobjekte Anwendungsmodule
Klicken Sie auf Finish bzw. Fertig, um die Anwendung zu erstellen.
Abbildung 21.8: Ihre vom JDeveloper erstellte BC4J-Anwendung Abbildung 21.8 zeigt, wie Ihr Bildschirm aussieht, nachdem der JDeveloper mit dem Generieren der Komponenten fertig ist. Wenn Sie den Strukturbaum unter BC4J.jpr komplett aufklappen, können Sie alle Komponenten sehen. Schauen Sie sich zunächst die Datei Tag21_BC4J.xml an. Diese wird von der BC4JRuntime stets zuerst gelesen und enthält Verweise auf alle Komponenten, die in Ihrer Anwendung benötigt werden. Sicherlich finden Sie im Strukturbaum schnell die gerade definierten Daten- und Ansichtsobjekte (Department und Course) wieder. Auch das Anwendungsmodul ist vorhanden (BC4J_Tag21Module). Aber ist Ihnen aufgefallen, dass noch zusätzliche Elemente generiert wurden? Schauen Sie sich den Strukturbaum im JDeveloper nochmals an. Da sind die Komponenten FkCourseDepartmentIdAssoc und FkCourseDepartmentIdLink. Diese Objekte haben Sie mit dem Assistenten nicht ausdrücklich definiert. Sie wissen jedoch, dass zwischen den Tabellen COURSE und DEPARTMENT in der Datenbank eine Fremdschlüsselbeziehung besteht. Der Assistent hat diese erkannt und bereits entsprechende Assoziationsobjekte angelegt. Sehen Sie nun in die XML-Datei FkCourseDepartmentIdAssoc.xml. Die Angaben dort
beziehen sich nicht auf Datenbanktabellen, sondern bereits auf die BC4J- Datenobjekte. Diese wiederum enthalten jedoch die Verknüpfung zur Datenbank. Lehnen Sie sich zurück und überdenken Sie nochmals, was hier geschehen ist. Sie haben in Java - ein kleines Datenmodell mit den Tabellen COURSE und DEPARTMENT nachgebildet. Alle Informationen, die in der Datenbank enthalten sind, sehen Sie nun in Ihrem JDeveloper-Projekt. Das heißt doch, dass der JDeveloper Ihnen sehr viel Arbeit abgenommen hat. Denn Sie müssen jetzt »nur« noch die eigentliche Logik Ihrer Anwendung entwickeln. Die Programmierarbeit für Datenbankverbindungen, SQL- Abfragen, -Inserts und Updates fällt weg. Sie brauchen sich darum nicht mehr zu kümmern. Weiter oben in diesem Kapital haben Sie bereits gelernt, dass die BC4J-Runtime mit der Datenbank kommuniziert. Schauen Sie sich den Java-Code zum Datenobjekt COURSE an. Dort ist keine Zeile mit SQL-Code vorhanden. Sie finden lediglich einige Methoden zum Setzen (set...) und Abfragen (get...) der Attribute. Wird eine get...-Methode aufgerufen, so wird die BC4J-Runtime dafür sorgen, dass bei Bedarf eine Datenbankverbindung geöffnet und der Wert aus der Tabelle abgefragt wird. Alle Informationen, die dazu benötigt werden, sind in den Java-Klassen und XML-Dateien vorhanden. Lassen Sie uns nun das generierte Datenmodell noch etwas verfeinern. Negative Werte für die Anzahl der Kurseinheiten (Units) sollten nicht möglich sein. Weiter vorne im Buch haben Sie gelernt, dass die Oracle-Datenbank dazu CHECK Constraints zur Verfügung stellt. Angenommen, Sie entwickeln eine Anwendung mit einem Eingabeformular. Der Benutzer will einen neuen Kurs anlegen und trägt für die Anzahl der Kurseinheiten versehentlich einen negativen Wert ein. Die Anwendung öffnet eine Verbindung zur Datenbank. Als nächstes versucht sie, den Datensatz einzufügen, scheitert aber am hinterlegten CHECK Constraint. Der Constraint ist in Ordnung. Aber bei einem stark belasteten Datenbankrechner kann die Anwendung diese Fälle auch selbst prüfen und dem Datenbankrechner so unnötige Last ersparen.
Abbildung 21.9: Java-Klasse und XML-Datei des Datenobjektes Course Wir wollen also sicherstellen, dass unsere BC4J-Anwendung bei einem Kurs negative Kurseinheiten nicht akzeptiert. Wie stellen wir das sicher? Eine Möglichkeit ist es, in den Java-Code des Datenobjekts COURSE einzusteigen und dort zu programmieren. Es geht aber auch eleganter. Doppelklicken Sie dazu im Strukturbaum auf das Datenobjekt COURSE. Dieses ist einfach nur mit Course benannt (Abbildung 21.9).
Bitte verwechseln Sie das Datenobjekt Course nicht mit dem Ansichtsobjekt CourseView. Prüfen Sie im Fenster die Titelleiste. Dort muss Entity Object stehen.
Abbildung 21.10: Eigenschaften des Datenobjekts COURSE In Abbildung 21.10 sehen Sie den Assistenten, mit dem Sie die Eigenschaften des Datenobjektes COURSE ansehen oder verändern können. Wir wollen sicherstellen, dass die Anwendung keine negativen Kurseinheiten annimmt. Klicken Sie dazu auf die Reiterkarte Validation. Sie sehen das in Abbildung 21.11 gezeigte Fenster.
Abbildung 21.11: Validierungsregeln für das Datenobjekt COURSE Sie können in diesem Fenster für jedes Attribut des Datenobjektes (und damit für jede Spalte der Tabelle COURSE) Validierungsregeln hinterlegen. Klicken Sie nun das Attribut Units an und dann auf den Button New.
Abbildung 21.12: Validierungsregel für die Anzahl der Kurseinheiten In dem in Abbildung 21.12 dargestellten Dialog legen Sie nun eine neue Validierungsregel fest. Wählen Sie aus der Auswahlbox Rules die Option CompareValidator aus, da wir den eingegebenen Wert mit einem anderen vergleichen werden. Als Attribute ist bereits Units eingetragen. Als Operator wählen Sie Greater Than aus. Der Wert, mit dem verglichen werden soll, geben Sie als Literal Value selbst ein.
Abbildung 21.13: Validierungsregel für die Zusatzgebühren eines Kurses Legen Sie doch gleich, wie in Abbildung 21.13 gezeigt, eine Validierungsregel für das Attribut AdditionalFees an. Hier ist eine ähnliche Prüfung angebracht, denn das College wird den Studenten wohl kaum noch Geld für den Besuch eines Kurses bezahlen. Es muss jedoch zugelassen bleiben, dass ein Kurs keine zusätzlichen Gebühren kostet. Demnach ist der Operator nicht Greater than, sondern GreaterOrEqualTo. Wenn Sie beide Regeln eingegeben haben, sieht die Übersicht wie in Abbildung 21.14 gezeigt aus.
Abbildung 21.14: Hinterlegte Validierungsregeln in der Übersicht Klicken Sie im Anschluss auf den Button OK. Nun nimmt der JDeveloper Änderungen an der XML-Datei Course.xml, welche die Einstellungen zum Datenobjekt COURSE enthält, vor. Sie werden jedoch feststellen, dass der Java-Code nicht geändert wurde. Die BC4J- Runtime setzt die Validierungsregeln anhand der Beschreibungen in den XML-Dateien durch. Wenn Sie sich diese nochmals anschauen, werden Sie den in Abbildung 21.15 hervorgehobenen Abschnitt erkennen. Wie Sie an dem Text Protected in der Statuszeile des Editorfensters erkennen können, schützt der JDeveloper Ihre XML-Datei vor unbeabsichtigten Änderungen. So ist sichergestellt, dass Sie Änderungen nur mit den Assistenten machen können und Ihr Projekt nicht durch einen Tippfehler, den Sie lange suchen müssten, unbrauchbar wird. Als erfahrener BC4J-Programmierer können Sie außerhalb des JDeveloper die XML- Dateien mit einem gewöhnlichen Editor ändern. Dabei ist es jedoch wichtig, dass Sie genau wissen, was Sie tun.
Abbildung 21.15: Ihre Validierungsregel in der XML-Datei Sie sollten nun noch eine Schwachstelle in Ihrer Anwendung beseitigen. Ein Attribut des Datenobjektes ist mit CourseId bezeichnet. Wenn einmal ein Kurs mit einer ID versehen wurde, sollte diese nicht mehr geändert werden, da sich andere Anwendungen mitunter auf diese ID verlassen. Eine gängige Praxis bei der Entwicklung von Datenbankanwendungen ist, dass der Primärschlüssel nach Erstellung der Tabellenzeile nicht mehr geändert werden darf.
Abbildung 21.16: Eigenschaften des Feldes CourseId Dies wird datenbankseitig mit Triggern realisiert. BC4J bietet auch hier Wege an, ungültige Änderungsversuche schon von der Anwendungsseite abzufangen. Klicken Sie wiederum im Strukturbaum links auf das Datenobjekt Course. Wählen Sie dann die Reiterkarte Attribute Settings aus. Sie sehen den in Abbildung 21.16 gezeigten Dialog. Rechts oben erkennen Sie den Abschnitt Updateable. Klicken Sie dort auf While New. Damit stellen Sie sicher, dass Sie bei einem neuen Kurs die ID festlegen, diese aber nicht mehr ändern können, sobald der Datensatz in die Datenbanktabelle eingefügt wurde. Klicken Sie auf OK, wenn Sie fertig sind.
21.3 Erweiterung der BC4J-Anwendung Bevor wir eine Benutzeroberfläche generieren, wollen wir unsere Anwendung noch ein wenig erweitern. Bislang hat unser Datenmodell zwei Ansichten und zwar eine auf das Datenobjekt Course (auf die Inhalte der Tabelle COURSE) und eine auf das Datenobjekt Department und damit auf die Inhalte der Tabelle DEPARTMENT. Als nächstes werden Sie lernen, wie Sie Ihrer Anwendung mit dem JDeveloper eine frei definierbare Datensicht hinzufügen können. Dazu werden Sie eine Ansicht erstellen, die alle Fachbereiche anzeigt und zu jedem die angebotenen Kurse zählt.
Ohne BC4J-Unterstützung müssten Sie Java-Code programmieren, der die Datenobjekte Course und Department anspricht, alle Datensätze abfragt und dann die Kurse zählt. Eine andere Variante ist eine JSP, die selbst SQL-Abfragen auf die Datenbanktabellen durchführt. Das wiederum würde bedeuten, dass Sie wie im letzten Kapitel den gesamten Code selbst programmieren müssten.
Abbildung 21.17: Ein neues Ansichtsobjekt Wir wollen also zunächst im Datenmodell ein BC4J-Ansichtsobjekt erzeugen. Klicken Sie dazu erst einmal Ihr Projekt im Strukturbaum an, auf File und dann auf New (Abbildung 21.17). Klicken Sie in diesem Fenster auf View Object. Daraufhin wird der Assistent für neue Ansichtsobjekte gestartet. Zunächst erscheint wie immer eine Art Willkommensfenster, welches Sie mit einem Klick auf Next bzw. Weiter schließen.
Abbildung 21.18: Geben Sie Ihrem Ansichtsobjekt einen Namen Das erste Fenster des Assistenten (Abbildung 21.18) erlaubt Ihnen, der Ansicht einen Namen zu geben. Dieser sollte möglichst sprechend sein. Die Ansicht soll dem Java-Paket Ihrer BC4J-Anwendung zugeordnet werden, daher ändern Sie das Feld Package nicht. Sie können Ansichten erstellen, die bereits bestehende Ansichten weiter verfeinern. Das wollen wir hier aber nicht tun; unsere Ansicht ist eine neue. Daher klicken Sie auf Next bzw. Weiter.
Abbildung 21.19: Festlegung der Datenobjekte für die neue Ansicht Im nächsten Schritt (Abbildung 21.19) legen Sie die Datenobjekte fest, auf denen Ihre neue Ansicht basieren soll. Wir wollen eine Übersicht über die Fachbereiche, daher übernehmen Sie Department auf die rechte Seite. Klicken Sie anschließend auf Next bzw. Weiter.
Abbildung 21.20: Auswahl der Attribute für die Ansicht Im nächsten Fenster (Abbildung 21.20) sehen Sie die Attribute, die Sie Ihrer Ansicht hinzufügen können. Wie Sie wissen, basieren die Datenobjekte direkt auf den Tabellen, daher sind diese Attribute gleichbedeutend mit den Tabellenspalten. Übernehmen Sie die Attribute DepartmentId und DepartmentName für Ihre Ansicht auf die rechte Seite. Mit diesen zwei Attributen enthält unsere Ansicht bereits die Fachbereichsnummer und den Namen. Die Anzahl der Kurse fehlt noch. Die Kurse sollen stets neu gezählt werden, wenn die Ansicht aufgerufen wird. Damit Sie Ihrer Ansicht ein solches Attribut hinzufügen können, klicken Sie auf der rechten Seite unten den Button New, worauf Abbildung 21. auf Ihrem Bildschirm erscheint.
Abbildung 21.21: Definition eines neuen Ansichtsattributes Geben Sie dem Attribut zunächst einen sprechenden Namen (Name). Darunter definieren Sie den Datentyp des Attributs. Da es in der SQL-Abfrage ausgerechnet wird, kreuzen Sie Selected in Query an. Ein so berechnetes Attribut kann natürlich nicht geändert werden, daher belassen Sie die Einstellung für Updateable bei Never. Im unteren Bereich (Query Column) legen Sie Details der SQL-Abfrage fest. Um zu zählen, verwenden Sie die SQL-Funktion COUNT. Unter Alias legen Sie den SQL- Aliasnamen für diese virtuelle Spalte fest. Wenn Sie fertig sind, enthält das in Abbildung 21.20 gezeigte
Fenster auf der rechten Seite das zusätzliche Attribut. Klicken Sie auf Next bzw. Weiter.
Abbildung 21.22: Einstellungen für die Ansichtsattribute In dem in Abbildung 21.22 gezeigten Fenster können Sie zu jedem Attribut, welches Sie für Ihre Ansicht ausgewählt haben, weitere Einstellungen vornehmen. Am wichtigsten ist hier die Einstellung für das Update-Verhalten. Die Ansicht soll Berichtscharakter haben, daher ändern Sie die Einstellung Updateable für jedes Attribut von Always auf Never. Klicken Sie auf Next bzw. Weiter, um fortzufahren.
Abbildung 21.23: Feinarbeit an der SQL-Abfrage Im nächsten Fenster sehen Sie, welche SQL-Abfrage der Assistent aus Ihren Angaben generiert hat. Testen Sie diese, indem Sie auf den Button Test klicken. Daraufhin werden Sie eine Fehlermeldung erhalten, da die SQL-Abfrage die Funktion COUNT für die Spalte CourseId enthält. CourseID ist aber eine Spalte der Tabelle COURSE und kommt nicht in DEPARTMENT vor, insofern müssen Sie die SQL-Abfrage ändern. Kreuzen Sie das Kästchen Expert Mode an. Damit erhalten Sie das in Abbildung 21.23 gezeigte Fenster. Sie können nun die SQL-Abfrage ändern. Geben Sie den in Abbildung 21.23 gezeigten SQL-Code ein. Wenn Sie nochmals auf Test klicken, sollte der Text Query is valid auch bei Ihnen erscheinen. Wenn Sie fertig sind, können Sie die folgenden Fenster überspringen. Klicken Sie immer auf Next bzw. Weiter, bis Abbildung 21. auf Ihrem Bildschirm erscheint. Mit einem Klick auf Fertig bzw. Finish generieren Sie Ihre Ansicht.
Abbildung 21.24: Das neue Ansichtsobjekt ist fertig Sie finden im Strukturbaum links einen neuen Eintrag für Ihr Ansichtsobjekt. Alle Einstellungen sind in der XML-Datei hinterlegt. Die Ansicht wurde zwar generiert, aber noch nicht in Ihre BC4J-Anwendung aufgenommen. Wie Sie wissen, enthält das Anwendungsmodul alle relevanten BC4J- Komponenten und deren Zusammenhänge. Die bisher behandelten Komponenten wurden beim Erzeugen des Projektes am Anfang generiert und dabei automatisch dem Anwendungsmodul hinzugefügt. Um die neue Ansicht in die BC4J-Anwendung aufzunehmen, doppelklicken Sie im Strukturbaum das Anwendungsmodul (Tag21_BC4JModule). Sie sehen daraufhin den in Abbildung 21.25 dargestellten Dialog. Auf der sofort sichtbaren Reiterkarte Data Model sehen Sie Ihre soeben generierte Ansicht Department_Summary auf der linken Seite der verfügbaren, nicht aber auf der rechten Seite der aufgenommenen Komponenten. Klicken Sie die Ansicht an und übernehmen Sie den Eintrag nach rechts. Klicken Sie auf OK, wenn Sie fertig sind.
Abbildung 21.25: Aufnahme der Ansicht in das Anwendungsmodul Die bis hierhin gemachten Einstellungen sollen für das Datenmodell genügen. Der JDeveloper bietet Ihnen nun die Möglichkeit, eine Web-Anwendung als Benutzeroberfläche für das Datenmodell zu generieren. Bevor Sie das tun, sollten Sie den generierten JavaCode kompilieren. Da alles mit Assistenten erstellt wurde, sehen Sie nach Drücken der Tasten Alt und F9 im Statusfenster, welches sich im unteren Bildschirmteil befindet, keine Besonderheiten (Abbildung 21.26).
Abbildung 21.26: Compilermeldungen
21.4 Die BC4J-Anwendung wird zur Web-Anwendung
Abbildung 21.27: Eine BC4J Web-Anwendung Erstellen Sie, wie in Abbildung 21.27 gezeigt, eine Business Components JSP Application. Was eine JSP ist, wie sie funktioniert und wie Sie eine entwickeln können, haben Sie im letzten Kapitel bereits gelernt. Hier werden Sie eine völlig neue Art und Weise sehen, wie man JSP Anwendungen entwickeln kann. Wenn Sie auf OK klicken, sehen Sie den in Abbildung 21.28 gezeigten Dialog. Hier nehmen Sie einige Grundeinstellungen vor. Geben Sie Ihrer Anwendung einen sprechenden Namen und eine Beschreibung. Mit dem Inhalt des Feldes Document root werden im Browser die Internet-Adressen (URL), mit denen Sie Ihre Anwendung ansteuern können, beginnen. Sie werden beim Testen Ihrer Web-Anwendung sehen, was damit gemeint ist. Geben Sie für den Moment irgendetwas ein; Tag21 ist vollkommen in Ordnung.
Abbildung 21.28: Grundeinstellungen für die JSP Anwendung Sie haben bislang nur ein BC4J-Anwendungsmodul erstellt. Daher können Sie im nächsten Fenster (Abbildung 21.29) nichts ändern. Wenn Sie bereits mehrere angelegt hätten, könnten Sie hier auswählen, für welches die Web-Anwendung generiert werden soll. Klicken Sie direkt auf Next bzw. Weiter.
Abbildung 21.29: Welches BC4J-Modul soll's denn sein? Der folgende in Abbildung 21.30 gezeigte Dialog erlaubt Ihnen die Festlegung der Komponenten, die in Ihre Web-Anwendung aufgenommen werden sollen. Sie sehen, dass Ihnen vier Ansichtsobjekte zur Verfügung stehen: ● ● ●
●
Ein Ansichtsobjekt für die Fachbereiche (DepartmentView) Ein Ansichtsobjekt für die Kurse (CourseView) Ein weiteres Ansichtsobjekt für Kurse (CourseView1). Dieses Objekt wurde maschinell für die Beziehung zwischen Kursen und Fachbereichen erstellt. Das von Ihnen selbst generierte Ansichtsobjekt für die Übersicht über die Fachbereiche.
Abbildung 21.30: Welche Komponenten werden in die Web-Anwendung aufgenommen? Entfernen Sie das Kreuzchen Generate Page für das Ansichtsobjekt CourseView. Für CourseView1 generieren Sie bitte nur eine Edit form. Für die DepartmentView entfernen Sie lediglich das Kreuzchen Query form, da es nicht so viele Fachbereiche gibt, so dass sich ein Abfrageformular lohnen würde. Änderungen sind noch am Ansichtsobjekt für die Fachbereichsübersicht zu machen (Abbildung 21.31). Da diese Seite Berichtscharakter haben soll und nur wenige Fachbereiche vorhanden sind, sind sowohl das Abfrageformular (Query form) als auch die Änderungsseite (Edit form) überflüssig. Entfernen Sie also diese Kreuzchen.
Abbildung 21.31: die Übersicht über die Fachbereiche in der Web-Anwendung Wenn Sie fertig sind, klicken Sie auf Next bzw. Weiter. In der folgenden Seite können Sie bestimmen, ob Sie für die Beziehung zwischen Kursen und Fachbereichen ebenfalls eine Seite generieren möchten. Es wird dann ein Master-Detail-Formular erstellt. Sie haben darin eine Auswahlmöglichkeit für einen Fachbereich und bekommen stets dessen Kurse angezeigt. Belassen Sie das Kreuzchen Generate Page aktiviert, damit Ihrer Anwendung eine solche Seite hinzugefügt wird.
Abbildung 21.32: Master-Detail-Formular Das nächste Fenster ist eine Übersichtsseite, die Sie mit einem Klick auf Finish bzw. Fertig bestätigen. Im Anschluss wird der JDeveloper die JSP der Web-Anwendung generieren. Im Strukturbaum links werden Sie anschließend einige neue Komponenten wiederfinden (Abbildung 21.33).
Abbildung 21.33: So sieht der JDeveloper nach Erstellung der Web-Anwendung aus Nun möchten Sie sicherlich die neue Web-Anwendung sehen. Suchen Sie im Strukturbaum links den Eintrag main.html. Diese Datei enthält die Startseite der Web- Anwendung. Klicken Sie den Eintrag an und dann die rechte Maustaste. Im erscheinenden Kontextmenü wählen Sie Run main.html. Daraufhin wird der JDeveloper die komplette Anwendung kompilieren und in seine Testumgebung installieren. Dieser Vorgang kann je nach Ausstattung Ihres Rechners einige Minuten dauern. Im Anschluss wird der Browser gestartet und Sie sollten Abbildung 21.34 auf Ihrem Bildschirm wiedererkennen.
Abbildung 21.34: Ihre Web-Anwendung im Einsatz Links finden Sie Menüpunkte für die Komponenten, zu denen Sie im Assistenten vorhin Generate Page aktiviert haben. Klicken Sie auf Browse unter FkCourseDepartmentIdLink. Sie sehen daraufhin das Master-Detail-Formular. Spielen Sie ein wenig darin herum. Die Bezeichnungen im Menü auf der linken Seite basieren natürlich auf der Benennung der BC4JKomponenten im JDeveloper.
Abbildung 21.35: Master-Detail-Formular im praktischen Einsatz Unter Department_Summary finden Sie die Übersicht über die Fachbereiche. Klicken Sie Browse an (Abbildung 21.36).
Abbildung 21.36: Anzahl der Kurse des Fachbereichs Sie haben nun eine Web-Anwendung entwickelt, ohne auch nur eine Zeile Code zu schreiben. Natürlich ist das Aussehen verbesserungsfähig. Alle Seiten sind ja auch maschinengeneriert. Aber alles entspricht offenen und dokumentierten Standards. Im nächsten Schritt gilt es, das Aussehen der Anwendung zu verbessern. Für diese Anpassung des JSP Codes hält der JDeveloper keine Assistenten mehr bereit. Sie müssen die notwendigen Änderungen selbst vornehmen, also erstmals in den Code der Anwendung einsteigen. Es ist aber ein Unterschied, ob Sie eine JSP aus dem Nichts heraus programmieren oder eine bestehende an Ihre Wünsche anpassen müssen. Sie haben natürlich auch die Möglichkeit, anhand der BC4J-Dokumentation die JSP komplett selbst zu schreiben. Zu den JSP der Web-Anwendung ist festzuhalten, dass diese komponentenbasiert sind. Die Tabelle mit den Fachbereichen und Kursen beispielsweise wird von einer bestimmten JSP erzeugt. Die gleiche Komponente erzeugt in Abbildung 21.35 die Tabelle mit den Kursen des jeweiligen Fachbereichs. Die Verwendung dieser Komponenten ist Ihnen natürlich auch in eigenen Web-Anwendungen möglich. Suchen Sie im JDeveloper die Datei FkCourseDepartmentIdLink.jsp. Diese JSP stellt das in Abbildung 21.35 gezeigte Master-Detail-Formular an. Schauen Sie sich den Code der JSP
an. Ist es nicht verwunderlich, dass diese Logik mit nur knapp 40 Zeilen Code realisiert wurde? Sie ahnen wahrscheinlich bereits, dass die Logik für die Erstellung des Master-DetailFormulars gar nicht in dieser JSP enthalten ist. Sie wird vielmehr von ihr angesprochen. Schauen Sie sich die Zeilen 11 und 12 an (Abbildung 21.37). Diese Zeilen definieren die Datenquellen, aus denen die JSP gespeist wird. In den Zeilen 16 bis 24 werden die Einzelheiten zum Fachbereich und in den Zeilen 26 bis 35 die Kurse dieses Fachbereichs dargestellt. Die eigentliche Logik, welche die Daten aus der Datenbank abruft und anzeigt, ist in spezielle Bibliotheken, die in der konkreten JSP angesprochen werden, implementiert.
Abbildung 21.37: Das Master-Detail-Formular im JDeveloper Im Folgenden werden Sie lernen, wie Sie die offensichtlichen Fehler in der Anwendung ohne allzu großen Aufwand beseitigen können. Sehen Sie sich dazu zunächst noch einmal Abbildung 21.35 an. Dort sind ein ausgewählter Fachbereich (Psychology) und drei Kurse dieses Fachbereichs zu erkennen. Weiterhin sehen Sie, dass dieser Fachbereich fünf Kurse anbietet (1-3 of 5). Es ist nicht nötig, nur drei Kurse anzuzeigen. Wir wollen diesen Maximalwert auf zehn erhöhen, so dass Sie alle fünf Kurse auf einmal sehen können. In Abbildung 21.37 sehen Sie (in der JSP FkCourseDepartmentIdLink.jsp) in Zeile 12 der JSP das Attribut rangesize. Der eingestellte Wert ist drei. Ändern Sie diesen Wert auf zehn.
Da Sie bereits in den Code eingestiegen sind, können Sie auch in Zeile 26 den Text CourseView1 Browse Form in Kurse dieses Fachbereichs ändern (Achten Sie darauf, dass Sie statt »ä« ein »auml;« eingeben, denn ein »ä« stößt manchen Browser vor Probleme). Als nächstes sollten Sie sich das Menü links nochmals anschauen. Alle Menüpunkte sind nach den BC4J-Komponenten des Datenmodells benannt. In Abbildung 21.36 sehen Sie, dass ferner zweimal der Menüpunkt Query auftaucht, obwohl Sie die Abfrageseiten (Query forms) bei Erstellen der JSP deaktiviert hatten. Wie schon im letzten Kapitel eingangs erwähnt, wurden diese Kapitel mit einer (schon fortgeschrittenen) Beta-Version des JDeveloper geschrieben. In der endgültigen Version sollten diese Menüpunkte nicht mehr auftauchen. In Abbildung 21.38 sehen Sie, wie die Datei contents.html aussehen sollte. Die Zeilen 13 bis 23 enthalten die einzelnen Menüpunkte. Sie erkennen, dass die Zeilen 15, 18 und 23 auskommentiert wurden. Die Zeilen 15 und 18 enthalten die überflüssigen Menüpunkte Query. Sie sollten den in Zeile 23 enthaltenen Menüpunkt Web Monitor ebenfalls entfernen. In den anderen Zeilen ersetzen Sie die Texte Browse und die Namen der BC4J-Komponenten durch sprechende Menü-Bezeichnungen.
Abbildung 21.38: Anpassung des Inhaltsverzeichnisses der Web-Anwendung Wenn Sie diese Änderungen durchgeführt haben, speichern Sie und starten die Anwendung, wie vorne beschrieben, neu (Run main.html). Das Ergebnis ist in Abbildung 21.39 dargestellt.
Abbildung 21.39: Formular nach Durchführung der Anpassungen Testen Sie mit Edit das Änderungsformular für einen Kurs. Wenn Sie im Feld für die Kurseinheiten (Units) einen Wert kleiner als Null oder Null eingeben, greift Ihre heute festgelegte Validierungsregel. Ihnen wird eine Fehlermeldung präsentiert (Abbildung 21.40).
Abbildung 21.40: Fehlermeldung bei ungültigem Wert für Kurseinheiten Nun ist diese Fehlermeldung nicht besonders schön formatiert. Auch hier gibt es jedoch einen einfachen Ansatzpunkt, dies zu ändern. Im letzten Kapitel haben Sie bereits gelernt, wie Sie in einer JSP auftretende Fehler mit einer errorPage abfangen können und eine einfache errorPage selbst programmiert. Schauen Sie sich nun nochmals den Strukturbaum Ihres Projektes im JDeveloper an. Irgendwo zwischen Ihren JSP finden Sie die Datei errorpage.jsp. Es wäre reine Fleißarbeit, für jeden möglichen Fehler eine entsprechende deutsche Fehlermeldung zu präsentieren. Die Fehlermeldung in der ersten Zeile in Abbildung 21.40 ist für uns akzeptabel. Die vielen Zeilen darunter sind ein gutes Indiz dafür, dass Sie hier mit einem Framework arbeiten. Denn dies ist eine Ausgabe des Fehler-Stack. In irgendeiner Java-Klasse der BC4J-Runtime wurde beim Prüfen Ihrer Validierungsregel eine JavaAusnahme ausgelöst. Diese wird Klasse für Klasse bis zur JSP zurückgereicht. Ganz oben steht die Java-Klasse, die den Fehler ausgelöst hat, also zuletzt von der darunter stehenden aufgerufen wurde. Der Aufruf ist durch alle aufgeführten Java-Klassen durchgelaufen. Warum ist es ein Indiz für ein Framework? Nun, gerade Frameworks zeichnen sich durch ihren hohen Modularisierungsgrad aus. Jede Java-Klasse hat nur eine kleine Aufgabe; ist ein kleines Rädchen im Gesamtwerk. Insofern müssen zur Erfüllung einer Anfrage sehr viele Klassen zusammenarbeiten. Ausgelöste Java-Ausnahmen werden durch alle Klassen an die
JSP zurückgereicht. Aber wir wollen den Fehler-Stack in unserer Anwendung nicht sehen. Doppelklicken Sie die Datei errorpage.jsp (Abbildung 21.41). Links im Editorfenster sehen Sie den Code der JSP. Die Zeilen 17 - 25 prüfen zunächst, ob es sich um eine BC4J-Fehlermeldung handelt. Es könnte ja auch eine normale Java-Fehlermeldung sein. Wenn es sich um eine BC4JFehlermeldung handelt, fragt die JSP weitere Details dieses Fehlers ab und zeigt diese in der JSP an. Die darauf folgenden Zeilen werden auf jeden Fall ausgeführt. Beachten Sie besonders die Zeile 33. Die Anweisung printStackTrace(...) ist dafür verantwortlich, dass der ganze FehlerStack auf den Bildschirm geschrieben wird.
Abbildung 21.41: JSP Code der Fehlerseite Wenn Sie die Zeilen 30 bis 37 auskommentieren (/* ... */) oder ganz löschen, die Anwendung neu starten (Run main.html) und den Versuch wiederholen, werden Sie eine schönere Fehlerseite sehen, der Fehler-Stack wird nicht mehr angezeigt.
21.5 Zusammenfassung
In dieser Lektion haben Sie gelernt, wie Sie mit dem JDeveloper und dem Framework BC4J Anwendungen entwickeln können. ●
● ●
●
●
●
BC4J befreit Sie von ständig wiederkehrenden Aufgaben wie etwa dem Programmieren von SQL-Abfragen. BC4J bildet das Datenmodell Ihrer Anwendung mit eigenen Komponenten nach. Viele Aspekte einer Anwendung (beispielsweise Validierungsregeln) können Sie mit Assistenten im JDeveloper einstellen, ohne auch nur eine Zeile Java zu programmieren. Der JDeveloper gibt Ihnen die Möglichkeit, mit wenigen Mausklicks eine WebAnwendung zu erstellen. Anpassungen dieser generierten Anwendung können Sie vornehmen, indem Sie direkt in den JSP Code eingreifen. BC4J ist trotz der JDeveloper-Unterstützung offen und dokumentiert. Erfahrene Programmierer können sogar ohne den JDeveloper komplette Anwendungen entwickeln. Die Assistenten machen diese Arbeit jedoch wesentlich einfacher, von der Sicherheit vor Tippfehlern ganz zu schweigen.
21.6 Fragen und Antworten Frage: Kann ich mit BC4J anstatt einer Web-Anwendung auch eine Windows-Anwendung mit Fenstern generieren? Antwort: Ja. Neben JSP bietet der JDeveloper auch diese Möglichkeit an. Schauen Sie im Menü für neue Objekte unter JClient Objects nach. Frage: Ich habe in der behandelten Anwendung Änderungen an einem Kurs vorgenommen und möchte diese speichern. Gleichzeitig hat ein anderer Benutzer am gleichen Kurs ebenfalls Änderungen vorgenommen. Wessen Änderungen gehen verloren? Antwort: Die Änderungen, die zuerst gespeichert werden, werden in der Datenbank berücksichtigt. Der andere Benutzer bekommt einen Hinweis, dass sich die Datenbankinhalte inzwischen geändert haben und er seine Änderungen nochmals prüfen möchte. Insofern gehen keine Änderungen verloren. Frage: Dürfen Datenbanktabellen noch mit SQL*Plus bearbeitet werden, wenn mit BC4JAnwendungen darauf zugegriffen wird? Antwort: Selbstverständlich. Wenn Sie mit SQL*Plus eine Tabelle ändern, bekommt BC4J diese Änderungen nach Abschluss der laufenden Transaktion mit. Wenn die BC4J-Anwendung ein
Commit oder Rollback sendet und die Datenobjekte danach neu abfragt, sind auch von extern gemachte Änderungen sichtbar.
21.7 Workshop Test Frage: Warum setzt man Frameworks in der Anwendungsentwicklung ein? Frage: Warum ist es sinnvoll, ungültige Eingaben trotz bestehender Datenbank-Constraints durch BC4J-Validierungsregeln abzufangen?
Übung Erstellen Sie mit BC4J eine Anwendung, mit der die Verwaltung der Studenten möglich wird. Sie sollten neue Studenten eingeben und bestehende löschen oder ändern können.
Entwicklung von Web- Anwendungen mit Java und JDeveloper Wenn Sie basierend auf dem bisher in diesem Buch gelernten Stoff Anwendungen entwickeln wollen, werden Sie immer wieder auf die Anforderung stoßen, dass die Anwendung internetfähig sein soll. Damit ist gemeint, dass der Benutzer die Anwendungen durch das Inter- oder Intranet bedienen kann. Man spricht dann auch von WebAnwendungen. Im Vergleich zu herkömmlichen Anwendungen ergeben sich damit einige Unterschiede. In diesem Kapitel werden Sie zunächst lernen, was eine Web- Anwendung ausmacht und welche Standards und Programmiermodelle sich dafür inzwischen etabliert haben. Im Anschluss daran werden Sie selbst mit dem Oracle JDeveloper eine Web-Anwendung entwickeln. Der Oracle JDeveloper ist ein Entwicklungswerkzeug für die Programmiersprache Java. Damit Sie die Programmbeispiele verstehen können, sollten Sie zumindest Java-Grundkenntnisse haben. Dieses und das nächste Kapitel wurden mit der Version 9i des Oracle JDeveloper geschrieben, die erst als Release-Candidate vorlag. Zum Erscheinungstermin des Buches wird die endgültige Version jedoch fertig sein. Sie werden höchstens im Detail Unterschiede zu den hier dargestellten Bildschirmfotos finden. Entwicklungsumgebungen sollen Ihnen die Programmierarbeit erleichtern. Das macht sich im Kleinen durch automatische Hervorhebungen im Programmcode bemerkbar. Im Großen zeigt sich die Unterstützung, indem die Entwicklungsumgebung Ihnen tatsächlich Programmierarbeit abnimmt. Schon relativ lange am Markt bewährt ist die Möglichkeit, Fensterdialoge mit der Maus zusammenzustellen, woraufhin die Entwicklungsumgebung Programmcode selbstständig generiert. Wenn Sie Web-Anwendungen programmieren möchten, sollten Sie eine Entwicklungsumgebung verwenden, die Sie auch in diesem Bereich unterstützt. Der Oracle JDeveloper9i ist komplett in Java geschrieben und daher für alle gängigen Betriebssysteme verfügbar. Für die Entwicklung datenbankgestützter Web-Anwendungen bietet er einige Features zu Ihrer Unterstützung: ●
●
Sie können die Inhalte der Datenbank im JDeveloper mit dem integrierten DatenbankBrowser betrachten und müssen somit nicht ständig zwischen dem JDeveloper und einem Datenbank-Werkzeug hin- und herwechseln. Sie können Ihre Web-Komponenten direkt testen.
●
Sie werden bei der Zusammenstellung Ihrer Web-Anwendung durch Assistenten unterstützt. Dieses Feature werden wir uns im nächsten Kapitel ansehen. In diesem Kapitel programmieren Sie selbst.
20.1 Architektur und Aufbau von Web-Anwendungen Da Sie mit einem Web-Browser durch das Internet surfen, müssen auch Web- Anwendungen so programmiert sein, dass ein Browser zum Umgang damit ausreicht. Sie können nicht irgendwelche fenstergestützten Windows-Anwendungen schreiben. Das würde bedeuten, dass der Anwender diese Programme zunächst auf seinem Rechner installieren muss. Ein fremder Anwender wird Ihnen kaum soviel Vertrauen entgegenbringen.
Wenn sich eine Anwendung über das Netzwerk mit einem Server unterhalten kann, spricht man noch nicht von einer Web-Anwendung. Solche Anwendungen werden als Client-Server-Anwendungen bezeichnet, da hier meist ein WindowsProgramm (der Client) sich über das Netzwerk mit einem anderen (Server) unterhält. Die Client-Server-Technologie war in der Zeit von ca. 1993 bis 1997 sehr populär. Insbesondere in den Unternehmen wurden Datenbankserver aufgestellt, deren Daten durch die auf den Arbeitsplätzen installierten Client-Programme für viele Mitarbeiter nutzbar wurden. Schnell wurden jedoch die Grenzen und Tücken dieser Technologie deutlich. ●
●
●
Die Bedeutung des Internet wuchs stetig. Es wurde erforderlich, Daten auch externen Partnern oder Lieferanten zur Verfügung zu stellen. Diese wollten jedoch keine fremden Programme auf ihren Rechnern installieren. Windows-Programme vertragen sich untereinander manchmal nicht; es kann zu Bibliothekskonflikten kommen. Neue Versionen müssen ständig auf allen Arbeitsplätzen installiert werden.
Eine Lösung für solcherlei Probleme verspricht man sich von der Web-Technologie. Jeder Anwender kann mit einem einfachen Internet-Browser wie Netscape, Opera oder dem Internet-Explorer auf die Anwendung zugreifen. Da diese Browser mittlerweile mit jeder Standard-Installation eines PC aufgespielt werden, hat man auch keine SoftwareVerteilungsprobleme mehr. Die Web-Anwendung selbst liegt nicht mehr auf den PCs, sondern auf einem Server; diese liefert bei Anfrage die Web-Seiten aus.
Ihre Anwendung wird also auf einem Server liegen. Man ist davon abgekommen, die Anwendungen auf den Datenbankserver zu legen, denn dieser hat meist genug zu tun. Wenn viele Nutzer gleichzeitig auf die Anwendung zugreifen, entsteht zuviel Last für einen Server. Dementsprechend wird der Server, auf dem die Anwendungen liegen, auch Anwendungsserver oder Application Server genannt. Wie Sie sicherlich wissen, sind Internet-Seiten in HTML, der Hypertext Markup Language geschrieben. Der Browser interpretiert die empfangenen Anweisungen und baut dementsprechend die Seite auf. Die ersten Seiten, die zur Pionierzeit des Internet entstanden, waren statisch. Die HTML-Anweisungen lagen in Dateien auf dem Web- Server; dieser lieferte sie auf Anfrage an den Browser aus. Für eine Web-Anwendung reicht es nicht, HTML in Dateien auf die Festplatte des Servers zu legen. Denn die Anwendung muss dem Benutzer stets einen aktuellen Stand zeigen. Wenn sie, wie in unserem Fall, datenbankgestützt ist, hängt das Aussehen der jeweiligen HTMLSeiten vom Stand in der Datenbank ab. Die Anwendung ist dafür verantwortlich, dass der Browser stets den richtigen HTML-Code ausgeliefert bekommt. Folglich erzeugt Ihre Anwendung als Ausgabe einen Datenstrom von HTML-Anweisungen.
Abbildung 20.1: Java 2 Enterprise Edition
Java 2 Enterprise Edition (J2EE) ist ein Architektur- und Technologiekonzept (nicht nur) für Web-Anwendungen. Es ist als Übersicht in Abbildung 20.1 dargestellt. Wie der Namensteil Enterprise Edition schon vermuten lässt, hat man sich hier vor allem Gedanken über Anwendungen im Unternehmen gemacht. Das J2EE-Konzept identifiziert drei für eine Web-Anwendung relevante Schichten. Zunächst ist die Datenbank zu nennen. Sie befindet sich auf einem eigenen Server und hält alle Daten für die Anwendung bereit. Die nächste Schicht ist die des Anwendungsservers. Er hat zwei Aufgaben, einmal die Abwicklung der Geschäftlogik, wofür J2EE das modulare Konzept der Enterprise Java Beans vorsieht; zum zweiten müssen die Ergebnisse als HTML an den Browser gesendet werden, dazu sind Techniken wie Servlets, JSP oder XML vorgesehen. Die dritte Schicht ist der Client mit seinem Internet-Browser. Wegen dieser drei Schichten wird diese Architektur auch als Drei-Schichten-Technologie bezeichnet. Über J2EE an sich ließe sich schon ein eigenes Buch schreiben. Dieses Kapitel soll Ihnen die Begriffe, die Sie in jeder Computerzeitschrift immer wieder lesen können, kurz vorstellen. Wir werden uns jedoch nur mit einem kleinen Ausschnitt aus J2EE, mit der Präsentation durch Servlets oder JSP, beschäftigen. Auch unsere Anwendung wird Logik enthalten, wir werden diese jedoch nicht nach dem J2EE-Konzept in Module kapseln. Wie bereits beschrieben, muss eine Web-Anwendung HTML-Code an den Client, also den Internet-Browser, ausliefern. Ihre Aufgabe als Entwickler einer solchen Anwendung ist es, diesen HTML-Code zu erzeugen. Um die Netzwerk-Kommunikation mit dem Browser des Anwenders müssen Sie sich nicht kümmern. Der Anwendungsserver stellt Ihnen die dazu nötige Infrastruktur zur Verfügung. Sie senden den HTML-Code in Ihrem Programm einfach an ein Ausgabeobjekt. Der Anwendungsserver stellt Ihnen dieses Objekt zur Verfügung. Um den richtigen Transport zum Browser kümmert sich der Anwendungsserver.
Java-Programme, die in einem Anwendungsserver laufen und HTML erzeugen, nennt man Servlets. Ein Teil des Anwendungsservers, der Servlet Container verwaltet die Servlets, führt sie bei Anfrage durch den Browser des Clients aus und leitet den erzeugten HTML-Datenstrom weiter.
Mit Servlets stieß man jedoch auf Probleme. Da das Servlet HTML-Anweisungen erzeugen muss, sind diese mit dem Java-Code vermischt. Java-Spezialisten sind aber nicht unbedingt die besten Web-Designer und ein Web-Designer versteht nicht unbedingt viel von Java. Um ein ansprechend aussehendes, funktionierendes Ergebnis zustande zu bringen, ist es also nötig, dass beide zusammen am Rechner sitzen und die Web- Anwendung gemeinsam entwickeln. Meist ist jedoch nicht die ganze HTML-Seite dynamisch. Bestimmte Elemente sind bei vielen Seiten immer gleich. So steht beispielsweise das Firmenlogo immer oben rechts und links ist immer eine Navigationsleiste.
Man kam auf die Idee, die feststehenden Anteile von den sich ständig ändernden zu trennen. Die feststehenden Inhalte werden in reinem HTML implementiert. Zwischen diese HTML-Anweisungen wird, wo es nötig ist, Java-Code eingefügt. Eine nach diesem Modell geschriebene dynamische HTML-Seite wird JSP (Java Server Page) genannt. Das gleiche Konzept verwendet Microsoft mit den Active Server Pages, wobei dort nicht Java, sondern Visual Basic oder JScript verwendet wird.
JSP sind nicht mit durch JavaScript erzeugtem dynamischem HTML (DHTML) zu verwechseln. Der Unterschied ist relativ einfach. Eine Java Server Page wird auf dem Server ausgeführt. Für den Browser auf dem Client ist nicht erkennbar, dass die jeweilige HTML-Seite dynamisch aufgebaut wurde, denn dieser bekommt reines HTML ausgeliefert. Mit JavaScript realisiertes DHTML wird auf dem Browser, dem der komplette JavaScript Code ausgeliefert wird, ausgeführt. Beide Konzepte lassen sich aber miteinander in Einklang bringen. So kann auch eine JSP JavaScript ausliefern, um auf dem Browser beispielsweise ein Menü zu erzeugen. Der besondere Vorteil einer JSP besteht darin, dass die Entwicklungsarbeit geteilt werden kann. Der Web-Designer erstellt Gerüst und Design der Seite und der Programmierer fügt die Java-Anweisungen ein.
20.2 Der Oracle JDeveloper Beim ersten Start des JDeveloper werden Sie wie in Abbildung 20.2 dargestellt, begrüßt.
Abbildung 20.2: Willkommensbild des Oracle JDeveloper 9i Sehen Sie sich nun die Konfiguration im JDeveloper an und stellen Sie fest, ob alle Einstellungen für die Arbeit in diesem und dem nächsten Kapitel richtig sind. Klicken Sie in der Menüleiste auf Tools und dann auf Preferences. Das erscheinende Fenster ist in Abbildung 20.3 dargestellt.
Abbildung 20.3: Einstellungen des JDeveloper Da Sie eine Web-Anwendung entwickeln werden, brauchen Sie zum Testen einen InternetBrowser. Sie können festlegen, welchen Browser der JDeveloper zum Testen verwenden soll. Standardmäßig ist nichts eingestellt. Der JDeveloper benötigt den Pfad zur EXE-Datei des Browsers. Mit einem Klick auf Browse können Sie auf Ihrer Festplatte danach suchen. Damit Sie den Microsoft Internet Explorer einsetzen können, sollten Sie die Datei IEXPLORE.EXE auf Ihrer Festplatte suchen und eintragen. (Abbildung 20.4)
Abbildung 20.4: Ausführbare Datei des Internet-Browsers auswählen Zur Information über die vielen anderen Optionen konsultieren Sie bitte die Dokumentation zum JDeveloper oder probieren Sie einfach einige Einstellungen aus.
Wenn Sie Lust auf ein ganz anderes Aussehen Ihrer Fenster haben, klicken Sie links auf Environment und wählen Sie rechts als Look and feel die Option Oracle aus. Starten Sie anschließend den JDeveloper neu. Da Ihre Web-Anwendungen mit einer Datenbank arbeiten werden, sollten Sie nun eine Datenbankverbindung einrichten. Erst wenn Sie eine funktionierende Datenbankverbindung haben, können Sie alle Features des JDeveloper verwenden. Stellen Sie vorher sicher, dass Ihre Datenbank hochgefahren und bereit ist. Um eine neue Datenbankverbindung anzulegen, klicken Sie, wie in Abbildung 20.5 gezeigt, zunächst im System - Navigator auf Connections, Database und dann die rechte Maustaste.
Abbildung 20.5: neue Datenbankverbindung anlegen Zunächst erscheint ein Erklärungsfenster. Es sagt Ihnen nochmals, welchen Assistenten Sie gerade aufgerufen haben und bei welcher Aufgabe er Sie unterstützt. Sie haben die Möglichkeit, ein einmal gesehenes Erklärungsfenster durch Entfernen des Häkchens Show this Page next time gewissermaßen abzubestellen. Klicken Sie anschließend einfach auf Next bzw. Weiter, um den in Abbildung 20.6 gezeigten Dialog zu sehen.
Abbildung 20.6: Benennung Ihrer Datenbankverbindung Geben Sie Ihrer Datenbankverbindung einen sprechenden Namen. Den Connection Type belassen Sie am besten bei Oracle (JDBC). Klicken Sie, wenn Sie fertig sind, auf Next.
Abbildung 20.7: Benutzernamen und Passwort hinterlegen Geben Sie in dem Dialog, der in Abbildung 20.7 dargestellt ist, einen Benutzernamen und ein Passwort an. Damit wird sich der JDeveloper an der Datenbank anmelden. Setzen Sie das Häkchen Deploy Password. Einerseits werden Sie dann nicht bei jedem Neustart nach dem Datenbank-Passwort gefragt, andererseits werden Sie diese Einstellung im nächsten Kapitel benötigen.
Abbildung 20.8: Verbindungsdaten für die Datenbank In Abbildung 20.8 sehen Sie, welche Daten notwendig sind, damit sich der JDeveloper mit der Datenbank verbinden kann. Sie sollten die Einstellung für den JDBC Treiber (Driver) bei thin belassen. Unter Host Name ist der Rechnername zu verstehen, auf dem die Datenbank läuft. Wenn die Datenbank auf Ihrem lokalen Rechner läuft, lassen Sie localhost einfach stehen.
Wenn Sie die SID Ihrer Datenbankinstanz nicht oder nicht mehr wissen, können Sie diese wie folgt herausfinden: Starten Sie SQL*Plus und melden Sie sich als SYSTEM an. Wenn Sie es nicht geändert haben, lautet das Passwort manager. Geben Sie folgendes ein: SQL> select INSTANCE_NAME from V$INSTANCE; Sie sehen daraufhin die festgelegte SID.
Abbildung 20.9: Erfolgreicher Test der Datenbankverbindung Das in Abbildung 20.9 dargestellte Fenster gibt Ihnen die Gelegenheit, die Angaben zunächst zu testen. Wenn alles richtig ist, erscheint im Statusfenster das Wort Success, nachdem Sie den Button test Connection geklickt haben. In den folgenden Abbildungen 20.10 und 20.11 sehen Sie einige gängige Fehler, die beim Test auftreten können.
Abbildung 20.10: Benutzername oder Passwort sind falsch
Abbildung 20.11: Überprüfen Sie die angegebene SID Wenn Ihre Datenbankverbindung erfolgreich getestet wurde, klicken Sie auf Next und im nächsten Fenster auf Finish.
Abbildung 20.12: Datenbankinhalte ansehen Ihre Datenbankverbindung erscheint nun im System-Navigator. Öffnen Sie diese und expandieren Sie den Baum immer weiter durch Klicken auf die Pluszeichen, bis Sie die Datenbankobjekte sehen können. In Abbildung 20.12 sehen Sie ein Beispiel für die Inhalte, die Sie mit dem Datenbank-Browser betrachten können. Richten Sie im nächsten Schritt nun einen Arbeitsbereich und ein Projekt für Ihre Anwendung ein.
Der JDeveloper speichert alle Bestandteile Ihrer Anwendung in einem Project.
Bis zur JDeveloper-Version 3.2.3 konnten Sie nur einen Workspace einrichten, um dort Ihre Projekte zu sammeln. Einen Workspace verstehen Sie am besten als Arbeitsbereich. Mit dem JDeveloper9i können Sie Ihre Projekte in mehreren Arbeitsbereichen organisieren. Unter einem Projekt stellen Sie sich am besten einen Ordner vor, in dem Ihre Programmteile abgelegt werden. Ein Arbeitsbereich ist dann der Schrank, in dem die verschiedenen Ordner stehen. In Ihrem Büro können natürlich mehrere Schränke stehen. Klicken Sie zunächst auf File und dann auf New. Daraufhin erscheint der in Abbildung 20.13 gezeigte Dialog. Wählen Sie dort zunächst Workspace. Im folgenden Fenster geben Sie dem Arbeitsbereich einen Namen. Klicken Sie das Kästchen Add a new empty Project an, damit im gleichen Schritt ein neues, leeres Projekt angelegt wird.
Abbildung 20.13: JDeveloper-Features
Abbildung 20.14: Ein neuer Arbeitsbereich...
Abbildung 20.15: ... und ein neues Projekt Der Arbeitsbereich und Ihr neues Projekt erscheinen nun links oben im System Navigator. Klicken Sie darauf. Wenn Sie dann wiederum auf File und New klicken, können Sie Ihrem Projekt neue Komponenten hinzufügen. Jede Komponente ist mit einem Assistenten ausgestattet. Die Unterstützung der Assistenten reicht von gering (Objects - Java Class) bis sehr umfassend (Business Components). Die wichtigsten sind in Tabelle 20.1 erklärt.
20.3 Ihre erste Java Server Page Nun geht es los. Sie werden nun lernen, wie Java Server Pages (JSP) erstellt werden. Unsere JSP soll eine Web-Seite erzeugen, auf der in einer Liste alle Studenten unseres Colleges zu sehen sind. Wie schon beschrieben, wird mit der Verwendung von JSP die Arbeit des Web-Designers (also das Erstellen von Struktur und Design) von der Arbeit des Programmierers getrennt. Daher ist es sinnvoll, dass auch Sie die JSP in zwei Schritten erstellen. Sie werden zunächst die Struktur Ihrer Web-Seiten festlegen und dann den Java-Code, der aus der Datenbank die Inhalte liest, hinzufügen.
Legen Sie eine neue JSP in Ihrem leeren Projekt an. Klicken Sie auf File, New und wählen Sie in dem Dialog die Kategorie Web Objects. Achten Sie darauf, dass Sie vorher den Eintrag für Ihr Projekt im Strukturbaum angeklickt haben. Sie sehen das in Abbildung 20.16 gezeigte Fenster. Wählen Sie JSP aus. Nachdem Sie, wie in Abbildung 20.17 gezeigt, einen Namen vergeben haben, fügt der JDeveloper Ihrem Projekt zwei Dateien hinzu. ●
●
Im rechten Editor-Fenster sehen Sie eine Vorlage für die JSP. Der Code, den Sie sehen, wurde vom JDeveloper als Beispiel generiert. Sie werden ihn komplett überschreiben. Die Datei web.xml, die Sie im Navigator-Fenster sehen, ist für den Anwendungsserver vorgesehen.
Klicken Sie im JDeveloper-Fenster in der Iconleiste auf die grüne Ampel. Die soeben angelegte JSP wird dann gestartet. Das Ergebnis, das Sie nun im Browser sehen, ergibt sich aus dem Beispiel-Code, mit dem der JDeveloper die JSP angelegt hat.
Abbildung 20.16: Eine JSP zum Projekt hinzufügen...
Abbildung 20.17: ... aber vorher noch einen Namen wählen Löschen Sie nun den Code, den Sie im Editorfenster sehen. Geben Sie dann den HTMLCode unserer JSP, den Sie in Listing 20.1 finden, ein.
Sie können Ihre HTML-Struktur natürlich auch mit Werkzeugen wie FrontPage oder den Netscape Composer erstellen. Lassen Sie sich, nachdem Sie fertig sind, den HTML-Code anzeigen und kopieren Sie diesen in das Editorfenster. Achten Sie bitte darauf, dass Ihre Seitenstruktur der in Listing 20.1 und Abbildung 20.18 dargestellten entspricht, da im weiteren Verlauf dieses Kapitels darauf aufgebaut wird. Listing 20.1: HTML-Code für die Struktur Ihrer ersten JSP
1--> 2--> 4--> 5--> 6--> 7--> 8--> 9--> 10--> 11--> 12--> 13--> 14--> 15-->
Sie haben bereits erkannt, dass jede Zeile mit einer Zeilennummer beginnt. Diese ist in HTML-Kommentarzeichen eingeschlossen. Ein HTML-Kommentar wird mit . Sie brauchen diese Nummern mitsamt der Kommentare natürlich nicht mit einzugeben.
Exkurs: HTML HTML (Hypertext Markup Language) ist eine Auszeichnungssprache. In einem HTMLDokument steht einerseits der Inhalt an sich, zusätzlich sind aber noch Tags enthalten. Diese geben an, wie der Inhalt dargestellt werden soll. Tags werden stets in spitze Klammern eingeschlossen. Ein Tag ist so lange gültig, bis es wieder geschlossen wird. Ein geschlossenes Tag ist am Schrägstrich (/) vor dem Namen erkennbar. Wenn Sie zum Beispiel einen Text fett darstellen möchten, sieht das als HTML Quelltext so aus: Hallo Welt. Benötigt ein Tag zusätzliche Angaben, werden diese als Attribute hinzugefügt. Ein Attribut besteht aus Name und Wert und wird ebenfalls zwischen die spitzen Klammern geschrieben. Ein HTML Tag mit Attribut sieht wie folgt aus:
. Über die wichtigsten in Listing 20.1 verwendeten HTML Tags informiert Tabelle 20.2. Beachten Sie bitte, dass vom HTML-Code in Listing 20.1 nur die Struktur unserer JSP wiedergegeben wird. Sie sollten jedoch hier alle Formatanweisungen, alle Farben und Layoutinformationen hinterlegen. Nachdem Sie in Tabelle 20.2 die Bedeutung der einzelnen Tags erfahren haben, sehen Sie in Tabelle 20.3 die Erklärungen zu Listing 20.1. Wenn Sie nun auf die grüne Ampel klicken, sehen Sie eine Vorschau auf Ihre JSP. Diese sollte wie in Abbildung 20.18 aussehen. Sie sehen die Vorschau auch, wenn Sie im Strukturbaum auf den Eintrag für die JSP, die rechte Maustaste und dann auf Run ... klicken.
Im nächsten Schritt werden Sie den Programmcode, der die Informationen zu den Studenten aus der Datenbank abruft und darstellt, einfügen.
Alle gängigen Datenbanken sind durch das Netzwerk erreichbar. Jede verwendet dazu jedoch ein eigenes Protokoll. Wenn Sie mit einer JSP auf Datenbanken zugreifen möchten, muss sich die JSP mit der Datenbank durch das datenbankeigene Netzwerkprotokoll unterhalten. Nun müssen Sie aber nicht verstehen, wie sich die Oracle-Datenbank über das Netzwerk ansprechen lässt. Damit sie (und andere Java-Programmierer) das nicht müssen, wurde JDBC entwickelt. JDBC besteht aus einer Java-Programmierschnittstelle und einem datenbankspezifischen Treiber, der die Java-Aufrufe für die Datenbank verständlich macht. Die Programmierschnittstelle wird oft auch mit API bezeichnet. API steht für Application Programmers Interface. Somit müssen Sie lediglich mit den JDBC-Kommandos umgehen können. Der JDBC-Treiber übersetzt diese und sorgt für die richtige Kommunikation mit der Datenbank.
Abbildung 20.18: Das Ergebnis Ihrer ersten JSP - noch ohne Java-Code
JDBC-Treiber werden zusammen mit der Oracle-Datenbank ausgeliefert. Auch die JDeveloper-Installation enthält bereits JDBC-Treiber für Oracle-Datenbanken, so dass Sie ohne weiteres mit der Entwicklung Ihrer Anwendungen beginnen können. Da die JDBC-Treiber jedoch nicht zum Standardumfang der Java-Programmiersprache gehören, müssen Sie diese für Ihr Projekt sichtbar machen.
Exkurs: Spracherweiterungen in Java Die Programmiersprache Java ist durch die Möglichkeit zur Definition von Paketen, die meist als Dateien mit der Endung .zip oder .jar ausgeliefert werden, erweiterbar. In diesen Dateien ist der Binärcode für die Spracherweiterung enthalten. Damit Sie die Kommandos der Erweiterung verwenden können, müssen die Dateien gefunden werden. Wichtig ist in diesem Zusammenhang die Betriebssystem-Umgebungsvariable CLASSPATH. Diese enthält die kompletten Suchpfade zu den Dateien. Bei der Arbeit mit dem JDeveloper brauchen Sie sich keine Arbeit mit dem Setzen der Umgebungsvariablen machen. Dazu gibt es einen Dialog, in dem Sie Pakete per Mausklick einbinden können. Sie müssen an alle verwendeten Java-Pakete denken, wenn Ihre Web-Anwendung in einem Anwendungsserver laufen soll. Der JDeveloper bietet Ihnen jedoch die Möglichkeit an, ein eigenes Paket mit Ihrem Code und allen verwendeten Java-Paketen zu erstellen. Dieses Paket wird vollständig in einer Datei gespeichert und ist nicht mehr vom Vorhandensein anderer Dateien abhängig. Klicken Sie zunächst im Strukturbaum auf Ihr Projekt, in der Menüleiste auf Project und dann auf Project settings. Im darauf erscheinenden Dialog (Abbildung 20.19) können Sie Ihr Projekt konfigurieren. Unter Libraries befindet sich der Dialog zum Einbinden von Bibliotheken.
Mitunter umfasst eine Java-Spracherweiterung mehrere Dateien. Java selbst sieht keinen Mechanismus vor, solche zusammengehörigen Dateien auch als Ganzes zu behandeln. Es obliegt Ihnen als Programmierer, dafür zu sorgen, dass alle benötigten Dateien gefunden werden. Der JDeveloper nimmt Ihnen hier Arbeit ab. Zusammengehörige Dateien werden in einer Bibliothek (Library) zusammengefasst. Die Zuordnung der Dateien zu Bibliotheken wird im JDeveloper verwaltet. Der JDeveloper bringt mit seiner Auslieferung schon recht viele Bibliotheken mit. Eine davon ist mit Oracle JDBC bezeichnet. Klicken Sie auf den Eintrag und übernehmen Sie ihn auf die rechte Seite. Damit wird die Bibliothek für Ihr Projekt nutzbar. Stellen Sie ebenfalls sicher,
dass auch die Bibliothek JSP Runtime auf der rechten Seite steht, denn diese benötigen Sie zum Test Ihrer Anwendung im JDeveloper.
Ein Klick auf den Button Edit zeigt, welche konkreten Dateien sich hinter der ausgewählten Bibliothek befinden und wo sie auf Ihrer Festplatte zu finden sind. (Abbildung 20.20). Der Button Neu ermöglicht es Ihnen, eine Bibliothek aus (vielleicht aus dem Internet heruntergeladenen) Java-Paketen zusammenzustellen. Mit dem Button Delete können Sie - Sie denken es sich sicher schon - eine Bibliotheksdefinition dauerhaft löschen. Die konkreten Dateien auf der Festplatte bleiben jedoch erhalten.
Abbildung 20.19: Bibliotheken für das Projekt sichtbar machen
Abbildung 20.20: Inhalte einer Bibliothek Übernehmen Sie die Änderungen mit einem Klick auf OK. Nun kommen wir zur Java-Programmierung. Um die Daten für Ihre Informationsseite aus der Datenbank lesen zu können, muss Ihr Programm folgendes tun: 1. 2. 3. 4.
eine JDBC-Verbindung zur Datenbank aufbauen, die SQL-Abfrage an die Datenbank senden, die Ergebnisse zurückholen und zum Schluss die Datenbankverbindung wieder schließen.
JDBC stellt Ihnen dazu entsprechende Java-Klassen, die sich im Paket java.sql befinden, zur Verfügung. Im Folgenden lernen Sie Schritt für Schritt die Kommunikation Ihrer JSP mit der Datenbank kennen.
Java-Pakete machen es möglich, über alle Klassen den Überblick zu behalten. Außerdem müssen Klassennamen nur innerhalb eines Paketes eindeutig sein. In anderen Paketen kann der Name wieder verwendet werden. Ähnlich dem Verzeichnisbaum auf Ihrer Festplatte können Pakete neben Java-Klassen auch Unterpakete enthalten. Um Java-Klassen in Paketen ansprechen zu können, brauchen Sie einen Pfad,
wie auch bei Ihrer Festplatte. Dieser besteht aus den durch Punkte voneinander getrennten Unterpaketen, wobei der letzte Teil den Namen der eigentlichen JavaKlasse kennzeichnet. Damit Sie nicht jedes Mal diese mitunter recht lange Bezeichnung verwenden müssen, können Sie in Ihrem Programm am Anfang eine import-Anweisung, die den vollen Klassennamen enthält, verwenden. Danach reicht der einfache Klassenname aus. Sie sparen Tipparbeit und Ihr Code wird besser lesbar. Wenn Sie die Java Pakete mit Ordnern und Dateien auf Ihrer Festplatte vergleichen, ist die import-Anweisung mit der PATH-Variablen des Betriebssystems vergleichbar. Java-Code in Ihrer JSP muss als solcher erkennbar sein. Es könnte ja auch eine HTMLSeite geben, die den Code einfach nur darstellen soll. Das finden Sie häufig bei Dokumentationen. Die Tags <% und %> grenzen ausführbaren Java Code ab. In den folgenden Listings sehen Sie nur den jeweils behandelten Abschnitt. Das Gesamtwerk können Sie weiter hinten (und natürlich auf Ihrem Bildschirm) betrachten.
Initialisierung Listing 20.2: Java Code zur Initialisierung des JDBC-Treibers <%@ page import="java.sql.*" %> <% DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver()); %> In der ersten Zeile finden Sie die schon angesprochene import-Anweisung. Durch den Stern können alle Klassen im Paket java.sql in dieser JSP mit dem einfachen Klassennamen angesprochen werden. Die zweite Anweisung lädt die Java-Klasse mit dem JDBC-Treiber in den Arbeitsspeicher. Das ist vor dem Öffnen der Datenbankverbindung im nächsten Schritt zwingend notwendig. Der Oracle JDBC-Treiber befindet sich im Paket oracle.jdbc.driver. In der Java-Klasse mit dem Namen OracleDriver ist der Code zur Kommunikation mit der Oracle- Datenbank enthalten.
Auch das Präfix oracle.jdbc.driver können Sie weglassen, wenn Sie es in die import-Anweisung aufnehmen. Das Code-Fragment würde dann wie in Listing 20.3 aussehen. Listing 20.3: Eine andere Variante zur Initialisierung des JDBC-Treibers
Aufbau der Datenbankverbindung Zum Aufbau der Datenbankverbindung benötigen Sie die gleichen Informationen, die Sie auch zur Einrichtung des Datenbank-Browsers im JDeveloper verwendet haben. Sie brauchen... ● ● ● ●
... den Namen des Rechners, auf dem die Datenbank läuft, den Oracle-Listener-Port, die SID der Datenbankinstanz, einen Benutzernamen und ein Passwort.
Kommt Ihnen das bekannt vor? Auch der Datenbank-Browser des JDeveloper greift mit JDBC auf die Datenbank zu. Listing 20.4: Öffnen einer Datenbankverbindung <% Connection con = DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:ORCL", "flugle", "flugle"); %> Der erste Parameter der Methode getConnection ist der Connect-String. Dieser beschreibt, welcher spezielle JDBC Treiber verwendet werden soll und wo sich die Datenbank befindet. Wir wählen hier den thin-Treiber (jdbc:oracle:thin:). Zu den Treiber-Typen sei nur so viel gesagt, dass der thin-Treiber auch auf Rechnern ohne installierte Oracle Software läuft. Im Gegensatz dazu setzt der oci-Treiber installierte Oracle-Software voraus. Im Connect-String folgen die Angaben zur Datenbank. Rechnername, TCP/IP-Port und SID der Datenbankinstanz sind jeweils durch Doppelpunkte voneinander getrennt. Anstatt localhost und ORCL müssen Sie natürlich Rechnernamen und SID für Ihre Umgebung eingeben. Die nächsten zwei Parameter sind Benutzername und Passwort. Die Tabelle, die wir abfragen wollen, liegt im Schema FLUGLE, also verbinden wir uns als FLUGLE mit dem Kennwort flugle.
Sie haben in diesem Buch bereits etwas über Transaktionen gelernt. Das gilt natürlich auch für JDBC. Wenn Sie weiter nichts einstellen, wird der JDBCTreiber nach jedem Kommando, das Sie an die Datenbank senden, automatisch ein Commit anschließen. Dieses Verhalten wird auch Auto-Commit genannt. Nachdem Sie die Datenbankverbindung aufgebaut haben und diese beispielsweise in einer Variable namens con enthalten ist, können Sie mit der Methode con.setAutoCommit(false) das Auto-Commit unterdrücken. Das bedeutet jedoch, dass Sie ein Commit mit con.commit() und ein Rollback mit con.rollback() selbst an die Datenbank senden müssen.
Abfrage der Datenbanktabelle mit SQL und Übernahme der Ergebnismenge Nachdem die Datenbankverbindung aufgebaut ist, können Sie SQL-Kommandos ausführen. Sie werden folgende Informationen aus der Datenbank abrufen: ● ● ● ●
die ID der Studenten deren Namen deren Heimat-Bundesstaat deren E-Mail-Adressen
Listing 20.5: SQL-Abfrage in der JSP <% Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery ("select student_id, " + "initcap(first_name)||' '||initcap(last_name), " + "initcap(city), email " + "from student"); %> Ein an die Datenbank gesendetes SQL-Kommando wird in der JSP durch eine Variable vom Typ Statement repräsentiert. In der ersten Zeile wird zunächst initialisiert, auf Datenbankseite geschieht noch nichts. Erst die zweite Zeile sendet die SQL-Abfrage an die Datenbank. Denken Sie nochmals an den Unterschied zwischen der mengenorientierten Abfragesprache SQL und der Programmiersprache Java. SQL-Ergebnismengen müssen in Java mit einem Cursor bearbeitet werden. Wie Sie wissen, ist die Ergebnismenge einer SQL-Abfrage eine Tabelle, die nur während der Ausführung existiert. Ein Cursor ist ein Zeiger auf eine Zeile dieser Tabelle. Wenn die Ergebnismenge abgearbeitet wird, wird der Cursor dabei Zeile für Zeile durch diese Tabelle
bewegt. In der JSP wird der Cursor durch eine Variable vom Typ ResultSet repräsentiert.
Im nächsten Schritt werden Sie die Ergebnismenge aus der Datenbank abrufen und darstellen. Mit der while-Schleife wird der Cursor über die Ergebnismenge bewegt. Der Inhalt dieser Schleife soll ausgeführt werden, solange noch weitere Zeilen in der Ergebnismenge vorhanden sind. Innerhalb der while-Anweisung finden Sie die Methode rs.next(). Sie bewegt den Cursor eine Tabellenzeile vorwärts und liefert true (wahr) zurück. Damit kann der Anweisungsblock innerhalb der while-Schleife ausgeführt werden. Steht der Cursor bereits auf der letzten Zeile der Ergebnismenge, so kann er nicht mehr weiter vorwärts bewegt werden. Es wird false (falsch) zurückgeliefert; die while-Schleife endet daraufhin. In den Zeilen 5 bis 18 sind Java und HTML wieder gemischt, wobei die Zeilen 7, 10, 13 und 16 Java-Anweisungen enthalten. Beachten Sie die Art und Weise, mit der diese JavaAnweisungen kenntlich gemacht sind. Durch das Gleichheitszeichen direkt hinter dem <% wird die Methode nicht nur einfach ausgeführt, vielmehr wird ihr Rückgabewert direkt in die HTML-Seite eingebaut. Die E-Mail-Adresse wird gesondert behandelt. Sie soll als Hyperlink dargestellt werden. Mit Klick auf diesen Hyperlink kann der Anwender direkt eine E-Mail an diesen Studenten schreiben.
Schließen der Datenbankverbindung Listing 20.7: Schließen der Datenbankverbindung
1--> <% 2--> rs.close(); 3--> stmt.close(); 4--> con.close(); 5--> %>
In Listing 20.7 sind die Anweisungen zum Schließen des Cursors und der Datenbankverbindung dargestellt.
Denken Sie bitte immer daran, offene Cursor nach Gebrauch, spätestens aber vor Beendigung Ihres Programms zu schließen. Das Öffnen von Datenbankverbindungen ist vergleichsweise langsam, daher sollte es möglichst selten erfolgen. Jedoch gilt auch hier, dass offene Verbindungen spätestens vor dem Programmende geschlossen werden müssen. Viele Anwendungsserver zerstören JSP nach Beendigung nicht, sondern behalten sie aus Performancegründen im Speicher. Wenn Sie Cursors und Datenbankverbindungen nicht schließen, werden mit jeder Ausführung der JSP neue geöffnet. Das wird irgendwann dazu führen, dass die Maximalwerte der Datenbank für offene Cursors oder Sitzungen überschritten werden. Listing 20.8 zeigt den kompletten, bis hierher erarbeiteten Code. Testen Sie die JSP durch Mausklick auf die grüne Ampel. Das Ergebnis sollte wie in Abbildung 20.21 aussehen.
Den nachfolgenden Code finden Sie in der Datei jsp\Tabelle.jsp auf der CDROM. Listing 20.8: Ihre neue JSP ist komplett
1--> <%@ page import="java.sql.*" %> 2--> <% 3 */ DriverManager.registerDriver 4 */ (new oracle.jdbc.driver.OracleDriver());
Soweit, so gut. Ihre JSP funktioniert. Doch wie wird Ihre Web-Anwendung reagieren, wenn die Datenbank nicht zur Verfügung steht oder die Tabelle gelöscht wurde? Wie Sie wissen, wird die Datenbank in diesem Fall eine Fehlermeldung auslösen. Das gilt auch für JDBCAnwendungen. Doch wie werden die Fehlermeldungen der Datenbank an die JSP mitgeteilt?
Wenn Java-Programme auf einen Ausnahmezustand stoßen, können sie eine Exception auslösen. Diese Ausnahme muss entweder behandelt (catch) oder an den übergeordneten Programmcode weitergereicht (throw) werden. Das wird als Catch-or-throw-Regel bezeichnet. Details zum aufgetretenen Ausnahmefehler sind in einem speziellen Objekt, das vom Typ Exception oder einer daraus abgeleiteten Klasse ist, enthalten. JDBCAusnahmen sind vom Typ SQLException; diese Klasse ist aus der Klasse Exception abgeleitet. Wenn Sie weiter nichts tun, wird der Anwendungsserver im Fehlerfall eine einfache weiße Seite präsentieren, auf der Sie Informationen über den aufgetretenen Fehler sehen können. Das ist für Testzwecke sicherlich in Ordnung, eine gute Anwendung sollte Fehler jedoch in allgemein verständliche Sprache übersetzen und dem Benutzer eine Handlungsanweisung geben. Im Folgenden werden Sie für den Fehlerfall eine spezielle JSP erstellen. Diese wird stets aufgerufen, wenn in einer JSP ein Ausnahmezustand auftritt und dort nicht behandelt wird. Zunächst wird die JSP, deren Ausführung einen Ausnahmefehler auslösen könnte, mit einer errorPage-Anweisung versehen. So wird dem Anwendungsserver mitgeteilt, welche andere JSP im Fehlerfall aufgerufen werden soll. Fügen Sie Ihrer JSP am Anfang folgendes hinzu. <%@ page errorPage="Error.jsp" contentType="text/html" %> Legen Sie nun eine neue JSP an und geben Sie ihr den Namen Error.jsp. Im Fehlerfall soll eine Fehlerbeschreibung ausgegeben und die Möglichkeit zur E-Mail an den Administrator eröffnet werden. Zum besseren Verständnis wird sie im Folgenden mit »Fehler-JSP« und Ihre schon bestehende JSP mit »Anzeige-JSP« bezeichnet.
Den Code der Fehler-JSP sehen Sie in Listing 20.9 und in der Datei jsp/Fehler.jsp auf der CD-ROM.
Eine solche Fehlerseite enthält bereits die Variable exception. Sie ist stets vom Typ Exception. Damit können Sie auf Details des aufgetretenen Fehlers zurückgreifen. Diese Fehlerseite können Sie nicht direkt ausführen, denn die exception-Variable hat nur im Fehlerfall einen sinnvollen Inhalt. Wenn Sie die Ausgabe sehen möchten, müssen Sie dafür sorgen, dass während der Ausführung der Anzeige-JSP ein Fehler auftritt. Fahren Sie die Datenbank herunter und starten Sie anschließend Ihre Anzeige-JSP. Sie werden statt dem in Abbildung 20.21 gezeigten Ergebnis die Ausgabe der Fehler-JSP sehen.
Abbildung 20.22: die Datenbank ist heruntergefahren Natürlich ist diese Art der Fehlerbehandlung keine Darstellung in für den Benutzer verständlicher Form. Die Programmierung dieser Form ist jedoch reine Fleißarbeit. Sie müssen feststellen, welche Art von Fehler vorliegt und eine entsprechende Fehlermeldung für den Benutzer ausgeben. In Fehlermeldungen der Oracle-Datenbank sind stets die ORACodes enthalten. So kann die JSP sehr gut auf die unterschiedlichen Fehler reagieren.
Beachten Sie bitte, dass dem Benutzer nicht jeder Datenbankfehler mitgeteilt werden sollte. Meist kann dieser sowieso nichts damit anfangen. Teilen Sie die Fehler in Kategorien ein. Fehler, auf die der Benutzer nicht reagieren kann, können in allgemeiner Form mitgeteilt werden (»Ein interner Anwendungsfehler ist aufgetreten«). Differenzierte Fehlermeldungen sollten Sie nur einsetzen, wenn der Benutzer differenziert auf den Fehler reagieren kann.
20.4 Komplexere Anwendungen
Die einfache Anzeige von Datenbank-Inhalten bringt als Anwendung nur beschränkten Nutzen. Der Nutzen steigt, wenn Sie mit dem Benutzer in Interaktion treten können. Sie kennen wahrscheinlich im Internet gebräuchliche Formulare, die Sie vielleicht auch schon selbst bedient haben. Als nächstes werden Sie lernen, ein solches Formular zu erstellen. Es soll Ihnen die Möglichkeit geben, die Liste der Studenten auf eine bestimmte Stadt zu beschränken. Dazu muss es die folgenden Fähigkeiten haben: ● ● ● ●
Es muss die Städte aus der Datenbank abrufen, diese in einer Auswahlbox anzeigen, nach Mausklick unsere Anzeige-JSP aufrufen und... eine Information über die Auswahl des Benutzers an diese übermitteln.
HTML-Formulare enthalten Elemente zur Interaktion mit dem Benutzer. Solche Elemente sind neben anderen Eingabezeilen, Auswahlboxen oder Checkboxes zum Anklicken. Das Formular unserer Anwendung muss mit der schon bestehenden Anzeige-JSP verknüpft werden und eine Information über die vom Anwender ausgewählte Stadt übermitteln. Daraufhin sendet diese eine veränderte SQL-Abfrage an die Datenbank und stellt die Studenten nur teilweise dar.
Mit HTML-Parametern übermitteln Sie Informationen von einer HTML-Seite an eine andere. HTML-Parameter bestehen aus Name und Wert. Der Name kann frei gewählt werden. Name und Wert werden beim Aufruf der nächsten HTMLSeite vom Browser an dieselbe übermittelt. Eine von diesem Aufruf angesprochene JSP kann diese Informationen auslesen und bearbeiten. Gehen Sie bei Entwicklung der Formular-JSP wie schon gelernt vor. Verwenden Sie den Namen Formular.jsp und erstellen Sie zunächst die Struktur. Diese sieht der Struktur der Anzeige-JSP sehr ähnlich. Listing 20.10: Struktur der Formular JSP
1--> 2--> 4--> 5--> Die Studenten des Flugle College ... 6-->
In der nachfolgenden Tabelle 20.5 sehen Sie Erklärungen zu weiteren HTML Tags, die in Formularen benötigt werden. Einige davon wurden im Listing 20.10 verwendet. Wichtig ist bei all diesen HTML-Tags das Attribut name. Mit diesem Namen kann die JSP, die als Ziel des Formulars aufgerufen wird, auf die eingegebenen Daten zugreifen. In Zeile 13 des Listings 20.10 wird das HTML-Formular definiert. Das Attribut action enthält als Wert den Namen unserer Anzeige-JSP. Der Browser wird angewiesen, die Formulardaten an diese JSP zu übermitteln, wenn der Anwender auf den OK-Button klickt. Das Attribut method bezeichnet die technische Art der Übermittlung. Der Wert get bedeutet, dass der Browser die Werte beim Aufruf in die Internet-Adresse (URL) einbetten soll. Als Formularelement soll eine Auswahlbox erscheinen, so das der Anwender nur noch mit der Maus die gewünschte Stadt anklicken muss. In Zeile 15 finden Sie das entsprechende Tag <select>. Die Anzeige-JSP kann mit dem Namen City den vom Benutzer angeklickten Wert abrufen. Das Tag wird in Zeile 17 wieder geschlossen. Zwischen den Zeilen 15 und 17 werden Sie im nächsten Schritt den Java-Code einfügen, der die Namen der Städte aus der Datenbank liest und in die Auswahlbox aufnimmt. Beachten Sie bitte auch Zeile 21. Dort wird ein OK-Button zum Absenden des Formulars definiert.
1--> <%@ page import="java.sql.*" %> 2--> <%@ page errorPage="Error.jsp" contentType="text/html" %>
<% DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver()); Connection con = DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:ORCL", "flugle", "flugle" ); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery ("select distinct city from student"); %> Die Studenten des Flugle-College
Den in Listing 20.11 dargestellten Code für das Formular finden Sie in der Datei jsp/Formular.jsp auf der CD-ROM. Große Teile des Codes kommen Ihnen wahrscheinlich bekannt vor. Kein Wunder, denn im Großen und Ganzen entspricht diese Formular-JSP der schon bekannten Anzeige-JSP. Wie Sie wissen, stellt das Schlüsselwort distinct in der SQL-Abfrage sicher, dass jede Stadt nur einmal in der Auswahlbox erscheint.
Abbildung 20.23: Auswahl JSP Probieren Sie das Formular aus (Abbildung 20.23). Wählen Sie eine Stadt aus und klicken Sie dann auf den Button Anzeigen.
Warum erscheint nun bei jedem Versuch die komplette Liste der Studenten, obwohl Sie eine bestimmte Stadt ausgewählt haben? Ihre Anzeige-JSP reagiert noch nicht auf die vom Formular übermittelte Information. In Listing 20.12 finden Sie die geänderten Zeilen der Ausgabe-JSP. Listing 20.12: geänderter Teil der Ausgabe-JSP
1--> <%@ page import="java.sql.*" %> 2--> <%@ page errorPage="Error.jsp" contentType="text/html" %> 3--> <% 3a*/ String city = request.getParameter("City"); 3b*/ String sql = "select student_id, " + 3c*/ "initcap(first_name)||' '||initcap(last_name), " + 3d*/ "initcap(city), email " + 3e*/ "from student"; 3f*/ if (!(city == null || city.equals("-1"))) 3g*/ { 3h*/ sql = sql + " where city='"+city+"'"; 3i*/ } 4 */ DriverManager.registerDriver 5 */ (new oracle.jdbc.driver.OracleDriver()); 6 */ Connection con = DriverManager.getConnection 7 */ ("jdbc:oracle:thin:@localhost:1521:ORCL", 8 */ "flugle", "flugle" 9 */ ); 10 */ Statement stmt = con.createStatement(); 11 */ ResultSet rs = stmt.executeQuery(sql); 12 */ // zeile löschen 13 */ // zeile löschen 14 */ // zeile löschen 15 */ %>
Wenn Sie die Formular-JSP aufrufen, eine Stadt an- und dann auf Anzeigen klicken, sehen Sie nur die Studenten aus der ausgewählten Stadt. Alle Studenten sehen Sie, wenn Sie entweder in der Formular-JSP ALLE anklicken oder die Anzeige-JSP direkt aufrufen.
20.5 Anwendungen der realen Welt Mit den Techniken, die Sie bis hierher kennen gelernt haben, können Sie beliebige WebAnwendungen erstellen. Der Komplexität sind dabei keine Grenzen gesetzt. Doch ist Ihnen schon aufgefallen, dass in Ihren JSP viele Wiederholungen vorkommen? So wird die Datenbankverbindung stets mit dem gleichen Code aufgebaut. Wenn die Datenbank auf einen anderen Rechner verlegt wird, müssen Sie an all diesen Stellen Anpassungen vornehmen. Auch wenn Sie das Design der Web-Anwendung ändern wollen, müssen Sie überall die entsprechenden HTML-Anweisungen verändern. Ein Vorhaben, das viel Zeit
kostet und sehr fehleranfällig ist. Weiterhin könnte man bei einer bestehenden WebAnwendung auf den Gedanken kommen, hinter einem Formular ein neues einzufügen und dafür ein anderes wegfallen zu lassen. Spätestens jetzt werden Sie sehr viel Arbeit haben, denn Sie müssen nicht nur die neue JSP entwickeln, sondern auch die vielen Referenzen der JSP untereinander anpassen. Es ist vertretbar, kleine Anwendungen auf die in diesem Kapitel beschriebene Art und Weise zu entwickeln. Große und komplexe Web-Anwendungen sind jedoch nicht wartbar, wenn sie so entwickelt werden. Stehen größere Änderungen an, so können Sie auch gleich alles wegwerfen und von vorne anfangen. Teilweise bietet das J2EE-Konzept Lösungen für diese Probleme, wenn man es vollständig anwendet. Sie haben bisher nur einen kleinen Ausschnitt daraus kennen gelernt. Doch auch J2EE kann nicht alle Probleme lösen, denn viele wurden erst bekannt, nachdem J2EE bereits auf dem Markt war.
Zur Lösung dieser Probleme wurden die sogenannten Design-Patterns entwickelt. Design-Patterns sind Richtlinien zur Entwicklung von WebAnwendungen mit performanter und wartbarer Architektur. Letztendlich ist jedoch festzuhalten, dass Sie bei der Entwicklung komplexer Anwendungen um das Thema Software-Engineering nicht herumkommen werden. Unter SoftwareEngineering versteht man das ingenieurmäßige Design von Anwendungen. Es soll hier nicht vertieft werden, denn das würde den Rahmen dieses Buches völlig sprengen. Einige Antworten auf die beschriebenen Probleme der Programmierung finden Sie in den folgenden Abschnitten.
JSP Taglibs JSP Taglibs ist eine Abkürzung für JSP Tag Libraries. Der Java-Code wird nicht mehr direkt in den HTML-Code eingefügt, sondern in Bibliotheken gekapselt. Den einzelnen Modulen werden JSP-Tags zugeordnet, die den in diesem Kapitel verwendeten HTML- Tags sehr ähnlich sind. Den sich ständig wiederholenden Code-Fragmenten können Sie durch JSP Tag Libraries entgegentreten. JSP Tag Libraries gehören zum J2EE-Konzept. In einer JSP, die Taglibs verwendet, finden Sie nur noch wenig Java-Code oder gar keinen. Stehen ausreichend große und gut dokumentierte JSP Tag Libraries zur Verfügung, kann der Web-Designer auch ohne Programmierkenntnisse JSP entwickeln. Dazu bedient er sich einfach aus der in der JSP Tag Library vorhandenen Funktionalität.
Style-Sheets Cascading Style-Sheets (CSS) sind Teile des HTML-Standards. Sie dienen dazu, Formatanweisungen in eigene Dateien zu kapseln. In Style-Sheets werden Formatklassen definiert. Eine Formatklasse enthält Eigenschaften wie Textfarbe, Schriftart, Größe, Hintergrundfarben und dergleichen mehr. Im HTML-Code selbst werden nur Formatklassen angesprochen. Cascading Style-Sheets eignen sich sehr gut, wenn größere Web-Anwendungen auf allen Ebenen und Seiten ein einheitliches Layout haben sollen. Soll dieses Layout geändert werden, so werden nur die Stylesheet-Dateien angepasst, die einzelnen JSP werden nicht mehr berührt.
Model View Controller (MVC) Hinter diesem Namen verbirgt sich ein Konzept zur Erstellung von komplexen Anwendungen. Damit kann den sich vielfach gegenseitig referenzierenden JSP begegnet werden. Das MVCKonzept gehört nicht zum J2EE-Standard, es ist vielmehr eines der angesprochenen DesignPatterns. Der Programmcode der Web-Anwendung wird aufgeteilt. Die JSP gehören zur »View«. Die View enthält alle für die Darstellung verantwortlichen Komponenten. Zum »Model« gehören die erwähnten JSP Tag Libraries, denn diese enthalten die eigentliche Logik der Anwendung. Der »Controller« enthält die Logik zur Ablaufsteuerung der Anwendung. Die Abfolge der Seiten in einer Anwendung wird als Page flow bezeichnet. Jede Seite der Web-Anwendung wird nicht mehr mit anderen Seiten verbunden, sondern nur noch mit dem Controller. Klickt der Anwender nun auf einen Hyperlink, so wird zunächst der Controller aufgerufen. Er stellt fest, welche Seite dem Benutzer als nächste präsentiert werden muss und leitet den Browser auf diese um. Änderungen im Page flow betreffen nur noch den Controller. MVC sind in Business Components for Java (BC4J) implementiert.
20.6 Einbindung in den Oracle Internet Application Server Bis hierher haben Sie Ihre Web-Anwendung entwickelt und getestet. Nun werden Sie als letzte Einheit dieses Kapitels deren Installation in den Oracle Internet Application Server lernen. Nach dem erfolgreichen Test Ihrer Anwendung im JDeveloper läuft diese auch im Oracle Internet Application Server.
Abbildung 20.24: Bestandteile des Oracle Internet Application Servers Anwendungsserver sind sehr mächtige Produkte. Der Oracle Internet Application Server hat eine Vielzahl von Komponenten für die unterschiedlichsten Aufgaben. Der Java- Container, der auch die JSP verwaltet und ausführt, ist die für Ihre Web-Anwendung relevante Komponente. Die verschiedenen Komponenten des Oracle Internet Application Servers sind in Abbildung 20.24 dargestellt, dabei ist der Java-Container rot markiert. Die folgenden Abschnitte setzen voraus, dass Sie einen Oracle Internet Application Server oder zumindest den Java-Container (OC4J), der auch separat zum Download erhältlich ist, bereits installiert und gestartet haben. 1. Klicken Sie im JDeveloper auf File, dann auf New. Im darauf folgenden Dialog, den Sie schon kennen, wählen Sie links Deployment Profiles aus (Abbildung 20.25).
Abbildung 20.25: Deployment Profile 2. Wählen Sie zunächst J2EE Web Module (WAR file) aus. Das J2EE-Konzept sieht für das Zusammenstellen einer Web-Anwendung ein bestimmtes Archivformat vor. In diesem Archiv stehen bestimmte Dateien an bestimmten Stellen. Der JDeveloper bietet Ihnen an, diese Archive für Sie zusammenzustellen. J2EE kennt zwei Archive: ● ●
WAR (Web Application Archive) EAR (Enterprise Application Archive)
3. In Abbildung 20.26 geben Sie dem neuen Deployment Profil einen Namen. Im darauf folgenden Fenster können Sie Einstellungen ändern, wobei die StandardEinstellungen für den Moment ausreichend sind. 4. Wenn Sie WAR File - WEB-INF/lib anklicken, sehen Sie auf der rechten Seite alle Bibliotheken, die in Ihrem Projekt sichtbar sind. Erinnern Sie sich? Sie haben am Anfang dieses Tages die Bibliothek »Oracle JDBC« für Ihr Projekt sichtbar gemacht. Wenn Sie Ihre Anwendung in einen anderen als den Oracle Internet Application Server installieren, machen Sie ein Häkchen bei Oracle JDBC. Sie müssen vielleicht ein wenig herunter scrollen, damit Sie es sehen können. In diesem Fall werden die Bibliotheken ebenfalls in Ihr Archiv eingepackt und Sie müssen sich keine Gedanken mehr machen, ob auf dem Anwendungsserver die Oracle JDBC-Treiber installiert sind.
Abbildung 20.26: Namensgebung für das Deployment Profil
Abbildung 20.27: zusätzliche Einstellungen 5. Klicken Sie nun, wie in Abbildung 20.28 gezeigt, mit der rechten Maustaste auf Ihr soeben erstelltes Deployment Profil. Wählen Sie im Kontextmenü Deploy to EAR File. Wie Sie dann im Statusfenster unten sehen können, schreibt der JDeveloper Ihre WebAnwendung in ein Archiv auf die Festplatte. Wenn Sie mit dem Windows Explorer in das dort angegebene Verzeichnis gehen und die Datei mit einem Standard-Werkzeug wie WinZip öffnen, können Sie die Strukturen dieses Archivs sehen (Abbildung 20.29).
Abbildung 20.28: Sichern Ihrer Web-Anwendung in einem EAR Archiv
Obwohl Sie das Deployment Profil für ein J2EE WAR Archiv angelegt haben, hat der JDeveloper zusätzlich noch ein EAR Archiv erzeugt. Das WAR Archiv enthält bereits alle Informationen Ihrer Web-Anwendung. Wenn Sie weitere Komponenten der J2EE- Technologie zu Ihrer Anwendung hinzufügen, dürfen diese nicht mehr im WAR Archiv enthalten sein.
Solche Komponenten werden zusammen mit dem WAR Archiv in ein EAR Archiv abgelegt. Der Oracle Internet Application Server kann WAR Archive nicht verarbeiten, er erwartet stets das übergeordnete EAR Archiv. Wie Sie anhand von Abbildung 20.29 erkennen können, enthält dieses Archiv schon einige XMLDateien für den Oracle Internet Application Server.
Abbildung 20.29: Bestandteile eines J2EE EAR Archivs
Abbildung 20.30: Bestandteile eines WAR Archivs
Abbildung 20.31: Verbindung zum Oracle Internet Application Server Grundsätzlich können Sie diese WAR und EAR Archive in jeden J2EEkonformen Anwendungsserver installieren. Der JDeveloper bietet Ihnen neben dem Oracle Internet Application Server eine Integration in den BEA WeblogicServer.
Abbildung 20.32: Verbindung zum Oracle Application Server 6. Geben Sie, wie in Abbildung 20.32 dargestellt, der Verbindung einen Namen und wählen Sie den Oracle9i Application Server aus. Auf der darauf folgenden Seite hinterlegen Sie wie schon bei der Datenbankverbindung Benutzernamen und Passwort. Beachten Sie hier bitte, dass Sie zur Installation einer neuen Anwendung i.d.R. Administratorrechte auf dem Anwendungsserver haben müssen.
Abbildung 20.33: letzte Angaben für die Verbindung 7. Geben Sie im nächsten Dialog (Abbildung 20.33) den Rechnernamen und die Website, auf die Ihre Anwendung installiert werden soll, an. Im darauf folgenden Fenster bekommen Sie nochmals die Gelegenheit zum Test Ihrer Verbindung. Wenn Sie eine Verbindung zu einem Anwendungsserver hinterlegt haben, ist die Installation der Anwendung ein Kinderspiel, wie Sie in Abbildung 20.34 sehen können.
Abbildung 20.34: Installation (Deployment) Ihrer Anwendung per Mausklick
20.7 Zusammenfassung In diesem Kapitel haben Sie den Oracle JDeveloper9i kennen gelernt und eine einfache WebAnwendung erstellt. ●
●
●
●
●
●
Die Mängel der Client-Server-Architektur werden mit der 3-Schichten-Architektur für Web-Anwendungen im Wesentlichen behoben. Für solche Anwendungen sieht das J2EE-Konzept eine Musterarchitektur vor. Grundlage ist Java. Der Oracle JDeveloper ist eine Entwicklungsumgebung für Java mit besonderem Fokus auf der Unterstützung für Web-Anwendungen. Der JDeveloper ist mit einem Datenbank-Browser ausgestattet. Sie können eine Verbindung zu einer Datenbank herstellen und sich die Inhalte ansehen. Der wesentliche Unterschied zwischen einer Java Server Page und einem Servlet liegt in der Art und Weise der Entwicklung. Servlets bestehen aus reinem Java-Code, wobei auch die statischen Anteile der HTML-Seiten mit Java codiert werden müssen. Dies ist in einer JSP nicht der Fall. Die statischen Teile werden in HTML und nur die sich ständig ändernden Teile in Java codiert. Damit eignen sich JSP sehr gut zur Arbeitsteilung zwischen Web-Designer und Java-Programmierer. Java-Programme und damit auch JSP greifen mit JDBC auf Datenbanken zu.
●
●
Eine komplette Web-Anwendung entsteht, indem einzelne Seiten miteinander verkettet werden. Die Installation der fertigen Anwendung in einen Anwendungsserver wird Deployment genannt.
20.8 Wie geht es weiter? Am Tag 21 werden Sie lernen, Web-Anwendungen mit Hilfe eines professionellen Frameworks zu entwickeln. Dazu lernen Sie Oracle Business Components for Java (BC4J) kennen. Während Sie in diesem Kapitel von Hand programmiert haben, werden Sie im nächsten lernen, wie BC4J Sie bei der Entwicklung unterstützt.
20.9 Fragen und Antworten Frage: Kann ich mit JDBC auch PL/SQL-Prozeduren und -Funktionen in der Datenbank nutzen? Antwort: Ja. JDBC stellt dazu die Klasse CallableStatement zur Verfügung. Alle PL/SQL- Prozeduren und -Funktionen können Sie so nutzen. Frage: Gibt es Datentypen in der Oracle-Datenbank, die mit JDBC nicht bearbeitet werden können? Antwort: Nein. Die Oracle JDBC-Treiber unterstützen alle SQL-Datentypen der Oracle- Datenbank. Frage: Ich habe eine Web-Anwendung unter Linux entwickelt. Kann es Probleme geben, wenn jemand mit einem Browser unter Windows darauf zugreifen will? Antwort: Nein. Einer der wichtigsten Vorteile von Web-Anwendungen ist, dass die BetriebssystemPlattform auf dem Client keine Rolle mehr spielt. Sie sollten jedoch das Aussehen der Seiten mit einem Windows-Browser testen.
20.10 Workshop Test Frage: Welche Vorteile hat eine Web-Anwendung gegenüber einer Client-Server- Anwendung mit Windows-Programmen?
Frage: Warum haben sich JSP bei der Entwicklung von Web-Anwendungen gegenüber der Programmierung mit Servlets durchgesetzt?
Übung Erstellen Sie eine HTML Seite mit einem Eingabeformular für einen neuen Studenten. Die HTML-Tags, die Sie benötigen, finden Sie in Tabelle 20.2 und Tabelle 20.5. Wenn das Formular fertig ist, müssen die Eingaben in die Datenbank eingefügt werden. Bislang haben Sie gesehen, wie man mit JDBC-Abfragen (SELECT) an die Datenbank sendet. Doch Sie können auch INSERT-Kommandos an die Datenbank senden. Beachten Sie dabei, dass ein INSERT-Kommando im Gegensatz zu einem SELECT keine Ergebnismenge zurückliefert. In den JSP dieses Kapitels wurden stets SELECT-Kommandos, deren Ergebnisse mit der Java Klasse ResultSet ausgelesen wurden, an die Datenbank gesendet. In den Listings sehen Sie, dass die SELECT-Kommandos mit executeQuery ausgeführt werden. Für INSERT- Kommandos verwenden Sie an gleicher Stelle die Methode execute. Sie benötigen dann weder das ResultSet noch die while-Schleife, die es ausliest. Geben Sie eine Meldung aus, dass der Datensatz erfolgreich eingefügt wurde.
Oracle Graphics und Procedure Builder In den vier vorangegangenen Lektionen haben Sie mit Oracle Forms and Reports gearbeitet, um Teile einer Anwendung zu entwickeln. In dieser Lektion werden Sie mit zwei weiteren Werkzeugen aus der Internet Developer Suite (iDS) arbeiten: Oracle Graphics und dem Procedure Builder.
19.1 Die Rolle von Oracle Graphics Mit Oracle Graphics steht Ihnen ein Werkzeug zur Erzeugung von Diagrammen zur Verfügung, die die dargestellten Werte aus einer oder mehreren Tabellen einer OracleDatenbank ableiten. Sie kennen das alte Sprichwort: »Ein Bild sagt mehr als tausend Worte.« Das Ziel von Oracle Graphics ist es, die grafische Darstellung des Inhalts einer Tabelle aufzubereiten, damit Trends schnell erkannt, große Datenmengen aggregiert und Vergleiche zwischen Datensätzen gezogen werden können. Oracle Graphics ist eng mit Oracle Forms und Oracle Reports integriert. So können Sie z.B. innerhalb eines Formulars mit Hilfe des Chart Wizard ein Diagramm-Feld (Chart Item) erstellen. In Oracle Reports gibt es ebenfalls einen Chart Wizard, mit dem Sie ein Diagramm (Chart Document) innerhalb eines Berichtes erzeugen können. Sie können jedoch auch, wie wir dies in diesem Kapitel tun werden, Oracle Graphics als eigenständiges Werkzeug einsetzen, um die erstellten Diagramme später in eine Anwendung zu integrieren.
19.2 Die Elemente von Oracle Graphics Oracle Graphics besitzt die gleiche Struktur wie Oracle Forms und Oracle Reports. Zum Erzeugen der Definition eines Diagramms wird die Komponente Graphics Builder benutzt. Ein weiteres Programm namens Graphics Runtime führt das erzeugte Diagramm aus. Wenn Sie dies wünschen, können Sie dem Menü eines Formulars einen Menüpunkt hinzufügen, um das Diagramm auszuführen. Wie Oracle Forms und Oracle Reports verwendet Oracle Graphics einen Object Navigator zur Erzeugung, Änderung und Anzeige der Elemente eines Diagramms. Oracle Graphics verwendet den Begriff Grafische Anwendung (Display), um sich auf ein Grafikobjekt zu beziehen. Für eine grafische Anwendung (Display) gibt es folgende Objektknoten: ●
Ein Diagramm enthält mindestens eine Abfrage und ein Layout.
19.3 Erstellen eines Diagramms mit dem Graphics Builder Zu Beginn wollen wir ein Tortendiagramm auf Basis einer einzigen Tabelle entwerfen. Sie werden ein Diagramm erstellen, welches Angaben zu den Studenten eines Jahres darstellt. Zum Aufruf des Graphics Builder wählen Sie aus dem Windows-Menü Start | Programs | Oracle Forms & Reports 6i | Graphics Builder. Der Graphics Builder wird als erstes den Object Navigator anzeigen (siehe Abbildung 19.1). Beachten Sie, dass die vom Object Navigator angezeigte grafische Anwendung den Titel Disp1 hat. Ehe Sie fortfahren können, müssen Sie eine Verbindung zu einer OracleDatenbank herstellen.
Aufbau einer Verbindung zu einer Oracle-Datenbank Um den Graphics Builder mit einer Oracle-Datenbank zu verbinden, wählen Sie den Menüeintrag File | Connect. Im dann erscheinenden Dialogfenster werden Sie zur Eingabe des Benutzernamens, Kennworts und der Datenbank aufgefordert, um die Verbindung zu einer Oracle-Datenbank aufzubauen. Tragen Sie bitte folgende Angaben ein: ● ● ●
Benutzername flugle Kennwort flugle Datenbank Tragen Sie den Service-Namen ein, den Sie nach der Installation der Oracle-Datenbank mit Hilfe des Net Manager eingerichtet haben.
Klicken Sie auf die Schaltfläche Connect. Wenn keine Fehlermeldungen erscheinen, können Sie davon ausgehen, dass Sie sich erfolgreich mit der Datenbank verbunden haben.
Abbildung 19.1: Graphics Builder zeigt zunächst den Object Navigator an
Ein neues Diagramm erstellen Mit den folgenden Schritten erstellen Sie ein neues Diagramm: 1. Wählen Sie den Menüeintrag Chart | Create Chart - es erscheint das Chart Genie. 2. Ändern Sie im Namensfeld den Namen der Abfrage von query0 in Student_Query. 3. Sie möchten ein Tortendiagramm erstellen, in dem die Prozentzahl der Studenten eines bestimmten Jahres dargestellt wird. Dazu brauchen Sie in der SELECTAnweisung eine GROUP-BY-Klausel (siehe Abbildung 19.2). Im Textfeld SQL Statement geben Sie ein: select Year, Count(*) from Student group by Year; 4. Betätigen Sie die Schaltfläche Execute, um die Ausführung der Abfrage zu testen, und verlassen Sie das Fenster mit OK.
Wählen des Diagrammtyps Als nächstes zeigt das Chart Genie ein Fenster an, in dem Sie die Chart Properties
festlegen. 1. Geben Sie im Feld Name Student_by_Year ein. 2. Geben Sie im Feld Title Students by Year ein. 3. In der Auswahl Type wählen Sie Tortendiagramm. Eine weitere Gruppierung namens Subtype erscheint, in der Sie das 3-D-Tortendiagramm (das rechte Symbol) auswählen. Die Abbildung 19.3 zeigt, wie das Dialogfenster aussehen sollte. 4. Klicken Sie auf OK.
Abbildung 19.2: Definition der Abfrage im Chart Genie
Abbildung 19.3: Festlegen der Diagramm-Eigenschaften im Chart Genie Das Layout des 3-D-Tortendiagramms erscheint entsprechend Ihren Angaben (siehe Abbildung 19.4). Dies ist jedoch nur der erste Entwurf für das Layout. Schauen wir uns einige Veränderungen an, die Sie hinzufügen möchten.
Abbildung 19.4: Layout des 3-D-Tortendiagramms
Anpassen des Diagramm-Layouts Wie Sie in Abbildung 19.4 sehen können, sind noch keine Prozentzahlen für die Segmente des Tortendiagramms zu sehen. Mit den folgenden Schritten fügen Sie diese hinzu: 1. Bewegen Sie die Maus in den Mittelpunkt des Diagramms und wählen Sie mit der rechten Maustaste den Menüeintrag Frame aus Es erscheint ein Fenster mit dem Namen Frame Properties. 2. Wählen Sie das Register Pie Frame. 3. Markieren Sie das Kontrollkästchen mit der Bezeichnung Show Percent Values (siehe Abbildung 19.5). 4. Klicken Sie auf OK.
Jetzt sollten Sie die Prozentwerte im Layout sehen. Als nächstes wollen wir die Größe der Überschrift heraufsetzen. 1. Wählen Sie die Überschrift mit der Maus aus. 2. Wählen Sie den Menüeintrag Format | Font | Font. 3. Stellen Sie MS Sans Serif, Fett, 12 Punkt für die Überschrift ein (siehe Abbildung 19.6).
Abbildung 19.5: Änderung der Rahmeneigenschaften, so dass Prozentwerte angezeigt werden
Abbildung 19.6: Formatierung des Diagrammtitels
19.4 Das Diagramm speichern Um das Diagramm zu speichern, wählen Sie den Menüeintrag File | Save As | File System. Geben Sie im darauf erscheinenden Dialogfeld als Namen der Datei ein: Students_by_Year. Der Graphics Builder speichert eine Datei mit der Endung .ogd, die Abkürzung für OracleGraphics-Designer. Um jedoch ein Diagramm mit Graphics Runtime ausführen zu lassen, müssen Sie eine .ogr-Datei aus der entsprechenden .ogd- Datei erzeugen (.ogr ist die Abkürzung für Oracle Graphics Runtime). Dazu wählen Sie im Menü File | Administration | Generate | File System (alternativ können Sie auch die Tasten (Alt) + (T) drücken). Standardmäßig wird im Dialogfeld Save As derselbe Dateiname für die .ogr-Datei vorgeschlagen. Sie können den Graphics Builder verlassen - im nächsten Schritt sollte für die Ausführung des Diagramms mit Graphics Runtime eine Windows-Verknüpfung (Shortcut) erstellt werden.
Achten Sie darauf, Ihre Diagrammdateien in einem Verzeichnis zu speichern, das nicht zum Verzeichnisbaum von Oracle gehört. Denken Sie außerdem daran, mit dem Registrierungs-Editor den Registrierungsschlüssel GRAPHICS60_ PATH um das gewählte Verzeichnis zu erweitern, da sonst Graphics Runtime Ihre Diagrammdateien nicht öffnen kann (siehe Abschnitt 15.4).
Erzeugen eines Shortcut zum Aufrufen eines Diagramms Sie sollten es dem Anwender so leicht wie möglich machen, die gewünschten Ergebnisse zu erhalten. Eine Windows-Verknüpfung (Shortcut) stellt eine solche Möglichkeit dar. Sicher ist sie keine so nahtlose Lösung wie eine integrierte Anwendung, aber sie ist ein einfacher Weg, um den Anwender zu unterstützen. Ehe Sie die Windows-Verknüpfung (Shortcut) erstellen, sollten Sie in Graphics Runtime eine Voreinstellung vornehmen, damit beim Start eine Anmeldung in der Datenbank versucht wird. Dadurch wird Graphics Runtime Sie zur Eingabe des Benutzernamens, Kennworts und der Datenbank auffordern, wenn Sie diese nicht in der Befehlszeile angegeben haben. Dazu führen Sie folgende Schritte aus: 1. Rufen Sie Graphics Runtime auf, indem Sie den Menüeintrag Start | Programs | Oracle Forms & Reports 6i | Graphics Runtime wählen. 2. Wählen Sie den Menüeintrag Edit |Tools Options. 3. Aktivieren Sie Kontrollkästchen mit der Bezeichnung Logon at Startup. 4. Klicken Sie auf die Schaltfläche OK und verlassen Sie das Programm. Zur Erstellung der Windows-Verknüpfung (Shortcut) gehen Sie wie folgt vor: 1. Klicken Sie auf dem Windows-Desktop mit der rechten Maustaste auf Neu | Verknüpfung. Das Fenster Verknüpfung erstellen erscheint. 2. Damit Students by Year zur Laufzeit ausgeführt wird, geben Sie die folgende Zeile in das Feld Befehlszeile ein. %ORACLE_HOME%\bin\gorun60 openfile=c:\user\Students_by_Year.ogr %ORACLE_HOME% steht für das jeweilige Oracle-Stammverzeichnis. 3. Klicken Sie auf die Schaltfläche Weiter. 4. Geben Sie der Verknüpfung den Namen Diagramm - Students by Year. 5. Klicken Sie auf die Schaltfläche Beenden.
Die Verknüpfung sollte auf dem Desktop erscheinen. Doppelklicken Sie darauf, werden Sie zur Eingabe des Benutzernamens, Kennworts und der Datenbank aufgefordert. Wenn Sie wollen, dass der Anwender nicht zur Eingabe dieser Werte aufgefordert wird, ändern Sie die Befehlszeile folgendermaßen ab: %ORACLE_HOME%\bin\gorun60 openfile=c:\user\Students_by_Year.ogr userid=flugle/flugle@flugle
Ein Nachteil dieser Verknüpfung ist, dass sie das Benutzerkennwort enthält und es von Dritten eingesehen werden kann.
19.5 Die Rolle des Procedure Builder im Prozess der Anwendungsentwicklung Im zweiten Teil der Lektion wollen wir noch den Procedure Builder einsetzen, der zur Vereinfachung der eigentlichen Entwicklung und Verwaltung von PL/SQL-Quelltext (sowohl auf dem Client als auch auf dem Server) bereitgestellt wurde.
19.6 Komponenten des Procedure Builder Procedure Builder enthält verschiedene Komponenten: ●
●
●
●
Einen Program Unit Editor zum Erstellen, Ändern und Löschen von Programmeinheiten, wie z.B. Prozeduren und Funktionen, die in PL/SQL- Bibliotheken von Oracle Forms benutzt werden. Einen PL/SQL Interpreter, der Sie einen PL/SQL-Quelltextblock schreiben und testen lässt. Einen Stored Program Unit Editor, der die Erstellung und Verwaltung von Prozeduren, Funktionen und Paketen unterstützt, die in einer Oracle-Datenbank gespeicherten werden. Einen Database Trigger Editor zur Erstellung und Verwaltung von DatenbankTriggern.
Um den Procedure Builder aufzurufen, wählen Sie aus dem Menü Start | Programs | Oracle Forms & Reports 6i | Procedure Builder. Abbildung 19.7 zeigt das Startfenster des Procedure Builder. Sie erkennen, dass der Procedure Builder, wie die anderen Werkzeuge der Oracle Internet Developer Suite (iDS), einen Object Navigator zur Verfügung stellt, um PL/SQLObjekte auszuwählen und deren Eigenschaften zu verändern.
Abbildung 19.7: Vom Procedure Builder angezeigtes Startfenster
Wie die anderen Werkzeuge der Oracle Internet Developer Suite (iDS) verwendet der Procedure Builder einen Object Navigator. Erläuterungen zum Einsatz des Object Navigator finden Sie in der Lektion vom Tag 15. Bevor Sie die einzelnen Komponenten des Procedure Builder einsetzen, sollten Sie eine Verbindung zu einer Oracle-Datenbank aufbauen.
Aufbau einer Verbindung zu einer Oracle-Datenbank Um den Procedure Builder mit einer Oracle-Datenbank zu verbinden, benutzen Sie die an diesem Tag bereits beschriebene Methode.
19.7 Den Object Navigator des Procedure Builder verwenden Der Object Navigator des Procedure Builder arbeitet auf die gleiche Weise wie die anderen Werkzeuge der Internet Developer Suite (iDS) und zeigt sieben Objektknoten an: ● ● ●
In dieser Lektion konzentrieren Sie sich auf Programmeinheiten und Datenbank-Objekte.
Der Program Unit Editor Den Program Unit Editor verwenden Sie, um Pakete, Prozeduren und Funktionen anzuzeigen, zu erstellen, zu ändern und zu löschen. Aufrufen können Sie den Program Unit Editor, indem Sie den Menüeintrag Program | Program Unit Editor wählen. Um z.B. eine neue Funktion zu erstellen, klicken Sie auf New; es erscheint ein Fenster, in dem Sie zur Eingabe des Namens und Typs der Programmeinheit aufgefordert werden (siehe Abbildung 19.8). Nachdem Sie auf OK geklickt haben, erstellt der Program Unit Editor eine Schablone für die Funktion, wie es in Abbildung 19.9 zu sehen ist.
Abbildung 19.8: Erstellen einer neuen Prozedur mit dem Program Unit Editor
Abbildung 19.9: Mit dem Program Unit Editor erstellte Vorlage für eine Funktion
Der PL/SQL Interpreter Den PL/SQL Interpreter rufen Sie auf, indem Sie den Menüeintrag Program | PL/SQLInterpreter wählen. Der PL/SQL Interpreter arbeitet in zwei Zuständen: ●
●
Nicht-modal, wenn Sie den PL/SQL Interpreter direkt aufgerufen haben. In diesem Zustand machen Sie Ihre Eingabe über die Befehlszeile. Die Eingabe könnte aus Zeilen eines anonymen PL/SQL-Blocks bestehen. Abbildung 19.10 zeigt ein manuell eingegebenes und vom PL/SQL Interpreter ausgeführtes Beispiel eines anonymen PL/SQL-Blocks. Modal, wenn der PL/SQL Interpreter durch von Ihnen in PL/SQL-Modulen gesetzte Programmstopps (Breakpoints) aufgerufen wird.
Abbildung 19.10: Verwendung des PL/SQL Interpreter, um einen anonymen PL/SQL-Block einzugeben und auszuführen
Der Stored Program Unit Editor für gespeicherte Programmeinheiten Den Stored Program Unit Editor verwenden Sie, um in einer Oracle-Datenbank gespeicherte Pakete, Prozeduren und Funktionen anzuzeigen, zu erstellen, zu ändern und zu löschen. Um den Stored Program Unit Editor benutzen zu können, müssen Sie bereits mit einer OracleDatenbank verbunden sein. Sie rufen den Stored Program Unit Editor auf, indem Sie den Menüeintrag Program | Stored Program Unit Editor wählen. Wenn das Fenster des Editors erscheint, bemerken Sie im oberen Teil des Fensters die beiden Felder: Owner und Name. Das Textfeld Owner enthält eine Auswahl-Liste der Benutzer, die in der mit dem Procedure Builder verbundenen Oracle-Datenbank existieren. Das Textfeld Name enthält eine Auswahl-Liste der gespeicherten Programmeinheiten, die dem in Owner angezeigten Oracle-Benutzer gehören. Abbildung 19.11 illustriert, wie Sie eine bestimmte gespeicherte Programmeinheit im Textfeld Name auswählen, nachdem Sie als Oracle-Benutzer flugle angegeben haben.
Abbildung 19.11: Anzeige einer gespeicherten Funktion mit Hilfe des Stored ProgramUnit Editor
Nicht jeder Oracle-Benutzer besitzt Pakete, Prozeduren oder Funktionen; einige Benutzer werden keine gespeicherten Programmeinheiten besitzen. In einem solchen Fall wird der Procedure Builder im Feld Name den Wert None anzeigen. Zudem sind die Möglichkeiten, gespeicherte Programmeinheiten zu erstellen oder zu ändern von den System- und Objektrechten Ihres Benutzerkontos abhängig.
Abbildung 19.12: Anzeige der zum Schema flugle gehörenden Knoten
Der Database Trigger Editor für Datenbank-Trigger
Den Database Trigger Editor können Sie auf zwei Wegen aufrufen: Sie können mit dem Object Navigator einen für eine Tabelle existierenden Datenbank-Trigger anzeigen oder den Menüeintrag Program | Database Trigger Editor wählen. Um den Object Navigator zur Anzeige oder Änderung eines vorhandenen DatenbankTriggers zu verwenden, erweitern Sie die Anzeige des Knotens für Datenbank-Objekte und es wird eine Liste der Datenbank-Schemata angezeigt. Wenn Sie ein Datenbank-Schema auswählen und erweitern, finden Sie darunter eine weitere Ebene mit gespeicherten Programmeinheiten (Stored Program Units), PL/SQL-Bibliotheken, Tabellen, Datensichten (Views) und Objekttypen (siehe Abbildung 19.12). Wenn Sie den Knoten für die Tabellen erweitern, sehen Sie eine Liste der im Schema existierenden Tabellen. Wählen Sie eine Tabelle aus und erweitern Sie diese, sehen Sie darunter zwei Knoten: Trigger und Spalten. Erweitern Sie den Knoten für die Trigger und doppelklicken Sie auf das Symbol neben dem Trigger.
19.8 Eine gespeicherte Programmeinheit anzeigen und bearbeiten Schauen wir uns an einem Beispiel an, wie eine gespeicherte Programmeinheit angezeigt und verändert werden kann. Nehmen Sie an, Sie wollen die im Datenbank-Schema flugle gespeicherte Funktion Student_GPA ändern. Wie Sie sich erinnern werden, wird die Funktion Student_GPA dazu benutzt, die in Buchstaben eingegebenen Zensuren der Studenten - A, B, C usw. - in Zahlenwerte zu übersetzen und einen numerischen Notendurchschnitt zurückzugeben. Im Object Navigator erweitern Sie den Knoten für die Datenbank-Objekte und den Knoten Flugle. Wählen Sie den Knoten Student_GPA (siehe Abbildung 19.13). Doppelklicken Sie auf das Symbol links vom Knoten Student_GPA. Es sollte der PL/SQLQuelltext der Funktion Student_GPA vom Stored Program Unit Editor angezeigt werden (siehe Abbildung 19.14). Schauen wir uns die Benutzerschnittstelle des Stored Program Unit Editor etwas genauer an. Oben im Fenster befinden sich zwei Auswahl-Listen: Owner und Name. Wenn Sie auf Owner klicken, wird eine Liste aller vorhandenen Schemata in der Datenbank angezeigt, auch wenn es in diesem Schema keine gespeicherten Programmblöcke gibt (siehe Abbildung 19.15). Die Auswahl-Liste Name zeigt alle gespeicherten Programmeinheiten an Paketspezifikationen, Paketrümpfe, Prozeduren und Funktionen -, die zu dem in Owner angezeigten Datenbank-Schema gehören (siehe Abbildung 19.16).
Abbildung 19.13: Auswahl einer gespeicherten Programmeinheit im Object Navigator
Abbildung 19.14: Anzeige einer gespeicherten Funktion im Stored Program Unit Editor
Abbildung 19.15: Die Auswahl-Liste Owner zeigt alle Schemata in der OracleDatenbank an
Abbildung 19.16: Die Auswahl-Liste Name zeigt alle Programmeinheiten an, die zu dem in Owner angezeigten Schema gehören Außerdem befinden sich im oberen Teil des Fensters sechs Schaltflächen: ● ● ● ● ● ●
New Erstellt eine neue Programmeinheit. Save Speichert alle in der Programmeinheit vorgenommenen Änderungen. Revert Widerruft alle in der Programmeinheit vorgenommenen Änderungen. Drop Verlässt die aktuelle Programmeinheit. Close Schließt den Stored Program Unit Editor. Help Ruft die Online-Hilfe für den Stored Program Unit Editor auf.
Die Schaltfläche Revert steht erst zur Verfügung, nachdem Sie an der Programmeinheit eine Änderung vorgenommen haben. Wenn Sie die Schaltfläche Close wählen, werden Sie gefragt, ob die Änderungen am Programmblock gespeichert oder rückgängig gemacht werden sollen.
Abbildung 19.17: Der Stored Program Unit Editor zeigt Fehler im PL/SQLCode an Wenn Sie der Funktion Student_GPA eine ungültige Zeile hinzufügen und die Schaltfläche Save betätigen, versucht der Stored Program Unit Editor die Funktion zu kompilieren und gibt am unteren Fensterrand eine Fehlermeldung aus (siehe Abbildung 19.17). Wenn Sie auf die Zeile nach begin schauen, sehen Sie eine ungültige Anweisung: not a valid line. Entfernen wir die störende Zeile und lassen Sie uns die Funktion ändern, indem wir die Note F aus der Berechnung des GPA entfernen (siehe Listing 19.1). Klicken Sie auf Save, um die Änderungen zu kompilieren und die Funktion zu speichern. Klicken Sie auf Close, um den Stored Program Unit Editor zu schließen. Listing 19.1: Änderung einer gespeicherten Funktion create or replace function student_GPA (arg_student_ID IN varchar2) return number is GPA number; begin select avg (decode (grade, 'A+', 4.25, 'A', 4, 'A-', 3.75, 'B+', 3.25, 'B', 3, 'B-', 2.75, 'C+', 2.25, 'C', 2, 'C-', 1.75, 'D+', 1.25, 'D', 1, 'D-', 0.75)) into GPA from student_schedule where student_id = arg_student_id; return GPA; end; /
19.9 Erstellen eines Datenbank-Triggers mit dem Database Trigger Editor Den Database Trigger Editor können Sie zur Anzeige, Erstellung, Änderung und Entfernung eines Datenbank-Triggers einsetzen. Schauen wir uns ein Beispiel an. Angenommen, eine Anforderung an das Flugle College Student Information-System bestünde darin, dass ein Journal mit allen Noten eines Studenten, einschließlich aller Noten-Änderungen, geführt werden müsste. Wie Sie noch wissen, enthält die Tabelle Student_Schedule jeden von einem Studenten abgeschlossenen Kurs, einschließlich der erreichten Noten und dem Datum, wann diese vergeben wurden. Um diese Anforderung zu erfüllen, erstellen Sie eine weitere Tabelle namens Student_ Schedule_Journal, in der alle Spalten der Tabelle Student_Schedule sowie drei zusätzliche Spalten enthalten sind:
●
● ●
Operation: ein Buchstabe, der der DML-Operation entspricht: I für Einfügen, U für Update und D für Löschen Changed_by: der Name des Oracle-Benutzers, der die Operation durchführt Changed_date: das Datum, an dem die Operation durchgeführt wurde
Verwenden Sie den Database Trigger Editor, um für die Tabelle Student_Schedule einen Datenbank-Trigger zu erstellen, der die Information über erfolgte Änderungen an einer Zeile in der Protokoll-Tabelle Student_Schedule_Journal speichert. 1. Rufen Sie den Database Trigger Editor auf, indem Sie den Menüeintrag Program | Database Trigger Editor wählen. 2. Wählen Sie FLUGLE aus der Auswahl-Liste Table Owner. 3. Wählen Sie die Tabelle Student_Schedule aus der Auswahl-Liste Table (siehe Abbildung 19.18). 4. Klicken Sie auf New, um den Datenbank-Trigger zu erstellen. Den vorgeschlagenen Namen für den Datenbank-Trigger sollten Sie ändern. 5. Ändern Sie im Feld Name den Namen des Datenbank-Triggers in AIUD_STUDENT_ SCHEDULE. Dieser Name bedeutet, dass dieser Trigger ein After-Insert-UpdateDelete- Trigger für die Tabelle Student_Schedule ist. 6. Aktivieren Sie die Option After für Triggering. 7. In der Gruppe Statement aktivieren Sie alle Kontrollkästchen, d.h. Update, Insert und Delete. 8. Aktivieren Sie das Kontrollkästchen For Each Row. Die Abbildung 19.19 zeigt, was Sie anschließend im Database Trigger Editor sehen sollten.
Abbildung 19.18: Vorbereitungen für das Erstellen eines Datenbank-Trigger für die Tabelle Student_Schedule
Abbildung 19.19: Setzen der Optionen für den neuen Datenbank-Trigger Geben Sie den Text von Listing 19.2 im Textfeld Trigger Body ein und klicken Sie auf Save, um den Trigger zu kompilieren und zu speichern. Listing 19.2: PL/SQL-Block für den Rumpf (Body) des Datenbank-Triggers begin if inserting then insert into student_schedule_journal (operation, student_ID, Class_ID, Grade, Date_Grade_Assigned, Changed_By, Changed_Date) values ('I', :new.Student_ID, :new.Class_ID, :new.Grade, new.Date_Grade_Assigned, user, sysdate); elsif updating then insert into student_schedule_journal (operation, student_ID, Class_ID, Grade, Date_Grade_Assigned, Changed_By, Changed_Date) values ('U', :new.Student_ID, :new.Class_ID, :new.Grade, new.Date_Grade_Assigned, user, sysdate); elsif deleting then
Wie Sie in Listing 19.2 sehen, besteht der Trigger-Rumpf (Trigger Body) aus einem PL/SQL-Block mit einer IF-Anweisung. Jede Klausel der IF-Anweisung überprüft die ausgeführte Operation. So überprüft z.B. der erste Teil der IFAnweisung, ob das trigger-auslösende Ereignis eine Einfüge-Operation INSERT ist. Wenn dies der Fall ist, wird in der Tabelle Student_Schedule_ Journal eine Zeile mit folgenden Werten eingefügt. Klicken Sie anschließend auf Close, um den Database Trigger Editor zu schließen. Operation I, weil es eine INSERT-Operation ist Student_ID :new.student_ID, enthält die eindeutige Nummer für den Studenten, der der Tabelle Student_Schedule hinzugefügt wurde Grade :new.Grade, enthält den Wert für die Note, die der Tabelle Student_Schedule hinzugefügt wurde Date_Grade_Assigned:new.Date_Grade_Assigned, enthält den Wert Date_Grade_Assigned, der der Tabelle Student_Schedule hinzugefügt wurde Changed_by USER, eine vordefinierte Funktion, die den Namen des Oracle- Benutzers, der die Anweisung ausführt, zurückgibt Changed_Date SYSDATE, eine vordefinierte Funktion, die die aktuelle Zeit und das Datum zurückgibt
Den neuen Datenbank-Trigger testen Lassen Sie uns den Trigger ausprobieren. Dies können Sie entweder mit SQL*Plus oder SQL Worksheet tun. 1. Bauen Sie eine Verbindung zur Datenbank mit dem Benutzer flugle auf. 2. Überprüfen Sie zu Beginn, ob die Tabelle Student_Schedule_Journal leer ist (siehe Abbildung 19.20).
3. Rufen Sie aus der Tabelle Student_Schedule alle Datensätze mit der Student_ID 10231324 auf (das ist Anna Anastasia). Wie Sie in Abbildung 19.21 erkennen können, wird nur ein Kurs aufgelistet: 109100 (das ist Biologie 101). 4. Anna hat in diesem Semester schwer gearbeitet und verdient für diesen Kurs die Note A. Um ihr für diesen Kurs die Note A zu erteilen, führen Sie eine UPDATE- Anweisung aus, damit der Wert für GRADE auf A gesetzt wird (siehe Listing 16.3).
Abbildung 19.20: Die Tabelle Student_Schedule_Journal sollte leer sein
Abbildung 19.21: Datensätze in der Tabelle Student_Schedule Listing 19.3: Zuweisung einer Note mit einer Update-Anweisung update Student_Schedule set Grade = 'A', Date_Grade_Assigned = sysdate where Student_ID = 10231324 and Class_ID = 109100;
Abbildung 19.22: Überprüfung, ob der Trigger ausgelöst wurde Wie in Abbildung 19.22 gezeigt, enthält die Tabelle Student_Schedule_Journal jetzt eine einzige Zeile mit dem erwarteten Wert einschließlich: ● ● ●
Operation U, entspricht der ausgeführten UPDATE-Anweisung Grade A, entspricht dem geänderten Wert Date_Grade_Assigned 03-JAN-02, entspricht dem Datum, an dem die Zeile aktualisiert wurde
19.10 Zusammenfassung In dieser Lektion wurden die folgenden Schwerpunkte im Zusammenhang mit Oracle Graphics und dem Procedure Builder behandelt: ●
●
●
●
Oracle Graphics besteht aus zwei Komponenten: Graphics Builder und Graphics Runtime. Durch die Definition einer Abfrage und eines Diagrammtyps erstellen Sie ein Diagramm. Eine Abfrage kann auf ein oder mehreren verknüpften Tabellen oder auf ein oder mehreren Datensichten (Views) basieren. Oracle Graphics stellt viele Möglichkeiten zur Gestaltung von Inhalt und Layout eines Diagramms bereit. Der Procedure Builder ist ein handliches Werkzeug zur Verwaltung von PL/SQLProgrammen, die an unterschiedlichen Standorten eingesetzt werden können:
● ● ● ●
●
●
in Formular-, Berichts- und Diagramm-Modulen in PL/SQL-Bibliotheken, die von den genannten Modulen verwendet werden können in der Datenbank Der Stored Program Unit Editor erlaubt Ihnen das Erstellen, Anzeigen, Ändern und Löschen von gespeicherten Programmeinheiten (Funktionen, Prozeduren, Pakete). Wenn Sie als angemeldeter Benutzer über die erforderlichen Rechte verfügen, können Sie gespeicherte Programmeinheiten und Datenbank-Trigger in anderen Schemata betrachten und bearbeiten. Mit dem Database Trigger Editor können Sie einen Datenbank-Trigger erstellen, ändern und löschen. Sie können alle für den Trigger relevanten Eigenschaften festlegen.
19.11 Wie geht es weiter? Am Tag 20 »Entwicklung von Web-Anwendungen mit Java und JDeveloper« lernen Sie die Java Entwicklungsumgebung JDeveloper kennen. Schritt für Schritt erfahren Sie, wie eine internetfähige Anwendung mit Java entwickelt wird.
19.12 Fragen und Antworten Frage: Bietet Oracle Graphics die Möglichkeit, innerhalb eines Diagramms zu den Einzelheiten eines Diagrammelements zu gehen (Drill-down)? Antwort: Ja. Sie können eine Detail-Abfrage festlegen, die ausgeführt wird, wenn der Benutzer auf ein Element des Diagramms klickt. Das Hauptdiagramm wird als Master-Diagramm bezeichnet. Zur Definition der Beziehung zwischen Master- und Detail-Diagramm wird ein Parameter benutzt. Frage: Welches sind einige der für die Entwicklungswerkzeuge mitgelieferten Built-In-Pakete, die mit dem Procedure Builder betrachtet werden können? Antwort: Die integrierten Pakete umfassen u.a.: ● ●
●
TEXT_IO, um Dateien auszulesen oder in Dateien zu schreiben DDE, das den dynamischen Datenaustausch zwischen Oracle Forms-, Oracle Reports, Oracle Graphics- und Windows-basierten Programmen ermöglicht TOOL_ENV, um die Werte aus Oracle-Umgebungsvariablen auszulesen
19.13 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte
Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Welche beiden Dateitypen verwendet Oracle Graphics? 2. Nennen Sie drei Diagrammtypen, die mit dem Graphics Builder entwickelt werden können. 3. Richtig oder falsch? Sie können den Stored Program Unit Editor zur Änderung der Paket-Spezifikation, aber nicht des Paketrumpfs (Body) verwenden.
Übungen 1. Erstellen Sie ein Tortendiagramm, das die von den einzelnen Fachrichtungen des Flugle College angebotenen Kurse darstellt. 2. Ändern Sie den Trigger für die Tabelle Student_Schedule so, dass in der Tabelle Student_Schedule_Journal Änderungen nur dann gespeichert werden, wenn der Wert in der Spalte Grade A, B oder C ist.
Entwicklung von Berichten mit Oracle Reports In dieser Lektion lernen Sie die grundlegenden Funktionen von Oracle Reports kennen. Sie erfahren etwas über zwei grundlegende Berichtstypen - einen auf einer einzigen Tabelle basierenden tabellarischen Bericht und einen Master-Detail-Bericht. Oracle Reports enthält umfangreiche Funktionalität und diese Lektion soll eine Einführung in die wichtigsten Funktionsmerkmale leisten.
18.1 Elemente eines Oracle-Berichts Wie Oracle Forms besitzt auch Oracle Reports einen Object Navigator, den Sie zur Erstellung und Anpassung von Berichtselementen verwenden. Der Object Navigator stellt Objekte in hierarchischer Anordnung dar. Für jeden Bericht zeigt der Object Navigator eine Reihe von Knoten an: ● ● ● ● ● ●
Eine Erklärung zur Verwendung des Object Navigator finden Sie an Tag 15, »Einführung in Oracle Forms«. Die folgende Lektion befasst sich näher mit den Objekten eines typischen Berichts.
Datenmodell Jeder Bericht benötigt ein Datenmodell. Das Datenmodell beschreibt die Abfragen, die Datensätze aus der Datenbank selektieren. Ein Datenmodell kann sich aus einer oder mehreren Abfragen zusammensetzen. Eine Abfrage kann aus einer beliebigen gültigen SELECT-Anweisung bestehen, d.h. Sie können die Daten aus verschiedenen Tabellen oder Datensichten (Views) auswählen. Außerdem können Sie eine ORDER BY-Klausel verwenden, um die Reihenfolge der abgerufenen Zeilen zu sortieren.
Layout Jeder Bericht muss auch ein Layout enthalten. Sie können bis zu vier Layoutkomponenten definieren - Kopfbereich (Header), Rumpfbereich (Body), Fußbereich (Footer) und Randbereich (Margin). Die Beispiele dieses Kapitels umfassen nur das Layout des Rumpfbereichs (Body). Sie können aber für den Kopfbereich (Header) eines Berichts, der nur einmal ganz zu Anfang eines
Berichts gedruckt wird, ein gesondertes Layout festlegen. Zum Beispiel könnte im Kopfbereich (Header) eines Berichts eine Inhaltsbeschreibung und eine Warnung zur Gewährleistung für die Richtigkeit der im Bericht angezeigten Informationen enthalten sein. Das Layout eines Berichts kann aus vielen verschiedenen Elementen bestehen, einschließlich Rahmen, Feldern, Textelementen und grafischen Elementen, wie z.B. Linien und Rechtecken. Ein Rahmen (Frame) wird verwendet, um die in ihm enthaltenen Elemente zu beschreiben - z.B. wann und in welcher Richtung sie gedruckt werden sollen. Ein Rahmen kann mehrere Felder enthalten. Jedes Feld kann der Spalte einer Abfrage zugeordnet sein - die dann als Quelle des Feldes bezeichnet wird. Die Quelle eines Feldes kann auch das aktuelle Datum oder eine physikalische Seitennummer sein.
Parameterformular Oracle Reports ermöglicht Ihnen, ein angepasstes Runtime-Parameter-Formular zu entwickeln, welches Parameter mit vom Benutzer zur Laufzeit angegebenen Werten enthält. So können Sie z.B. ein Parameter-Formular entwerfen, in das der Benutzer eine Department_ID eingibt und das die Berichtsausgabe auf diese eine Abteilung beschränkt.
18.2 Die Komponenten von Oracle Reports Oracle Reports beinhaltet drei für diese Lektion relevante Werkzeuge - Reports Builder, Reports Compiler und Reports Runtime. Mit dem Reports Builder können Sie vier Modultypen erstellen und anpassen: Berichte, externe Abfragen, Templates und PL/SQL- Bibliotheken. Der Reports Builder speichert einen Bericht als RDF-Datei: Reports Definition File. Dieses binäre Dateiformat ist portierbar, d.h. dass es vom Reports Builder unter Windows, Mac OS oder Unix gelesen werden kann. Der Reports Compiler generiert eine binäre REP-Datei, die zur Ausführung eines Berichts von Reports Runtime verwendet wird. Im Unterschied zu Oracle Forms ist eine Kompilierung von Berichten nicht zwingend notwendig, da RDF-Dateien zum Ausführungszeitpunkt direkt geladen und im Hauptspeicher kompiliert werden können. Eine RDF-Datei kann auch in eine REX-Datei konvertiert werden, die eine vollständige Textbeschreibung des Berichts enthält.
18.3 Einen tabellarischen Bericht auf Basis einer einzelnen Tabelle erstellen Beginnen wir, Oracle Reports einzusetzen, um einen tabellarischen Bericht auf der Basis einer einzigen Tabelle mit Kursinformationen zu entwickeln. Zum Aufruf des Reports Builder wählen Sie den Menüeintrag Start | Programs | Oracle Reports 6i | Report Builder. Verlassen Sie das Begrüßungsfenster mit Cancel. Der Reports Builder zeigt als erstes den Object Navigator an (siehe Abbildung 18.1). Beachten Sie, dass im Object Navigator ein Bericht namens MODULE1 angezeigt wird. Ehe Sie fortfahren, stellen Sie eine Verbindung zu einer Oracle-Datenbank her.
Aufbau einer Verbindung zu einer Oracle-Datenbank
Um sich aus dem Reports Builder mit einer Oracle-Datenbank zu verbinden, wählen Sie den Menüeintrag File | Connect. Im dann erscheinenden Dialogfenster werden Sie zur Eingabe des Benutzernamens, Kennwortes und der Datenbank aufgefordert, um die Verbindung zu einer Oracle-Datenbank aufzubauen. Tragen Sie bitte folgende Angaben ein: ● ● ●
Benutzername flugle Kennwort flugle Datenbank Tragen Sie den Service-Namen ein, den Sie nach der Installation der Oracle-Datenbank mit Hilfe des Net Manager eingerichtet haben.
Klicken Sie auf die Schaltfläche Connect. Wenn keine Fehlermeldungen erscheinen, können Sie davon ausgehen, dass Sie sich erfolgreich mit der Datenbank verbunden haben.
Abbildung 18.1: Der Reports Builder zeigt zu Beginn den Object Navigator an
Erstellen eines Berichts mit dem Report Wizard Mit Hilfe des Report Wizard können Sie in wenigen Schritten einen Bericht erstellen, der anschließend noch verfeinert werden kann. Rufen Sie den Report Wizard über den Menüeintrag Tools | Report Wizard auf.
Der erste Schritt zur Berichtserstellung ist die Festlegung des Stils für den künftigen Bericht. In der linken Abbildung des Dialogfensters sehen Sie jeweils eine symbolische Darstellung des LayoutStils, den Sie im rechten Teil ausgewählt haben. So bekommen Sie einen ersten Eindruck vom Layout des zu erstellenden Berichts. Wählen Sie im Dialogfenster den Stil Tabular aus, geben Sie als Titel des Berichts FLUGLE COLLEGE Course Catalog ein und bestätigen Sie Ihre Eingaben (siehe Abbildung 18.2).
Abbildung 18.2: Festlegung des Stils für den künftigen Bericht Ein Bericht kann entweder auf einer SQL-Abfrage (relational) oder einer mehrdimensionalen Abfrage aus Oracle Express basieren. Wählen Sie SQL Statement und bestätigen Sie die Auswahl. Um eine Abfrage zu erstellen, stehen Ihnen folgende Alternativen zur Verfügung: ● ●
●
Sie können die Abfrage manuell eingeben. Sie können den Query Builder aufrufen, um grafisch eine Abfrage zusammenzustellen. Der Query Builder ist ein eigenständiges Programm, das in Oracle Reports integriert ist. Sie können eine bereits als Text in einer Datei existierende Abfrage importieren.
Rufen Sie mit der Schaltfläche Query Builder das grafische Werkzeug auf, wählen Sie aus der Liste der Tabellen und Datensichten (Views) des Benutzers Flugle die Tabelle Course aus und bestätigen Sie die Auswahl mit Include (Siehe Abbildung 18.3). Das Auswahlfenster muss anschließend mit Close geschlossen werden.
Abbildung 18.3: Auswahl der Tabelle Course für die Ab-frage des Berichts Aktivieren Sie alle Spalten der Tabelle Course im Diagramm und stellen Sie durch Ziehen mit gedrückter linker Maustaste die Reihenfolge der Spalten um, damit sie der von Ihnen im Bericht gewünschten Reihenfolge entspricht: Department_ID, Course_ID, Title, Description, Units und Additional_Fees. Erzeugen Sie die SQL-Abfrage durch Betätigung der Schaltfläche OK (siehe Abbildung 18.4). Der Text der Abfrage erscheint im Textfeld des Registers Data. Obwohl wir dies auch bereits im Query Builder hätten tun können, fügen wird der SELECT-Anweisung manuell eine ORDER BY-Klausel hinzu, damit die Zeilen nach Department_ID und Course_ID sortiert werden (siehe Abbildung 18.5).
Abbildung 18.4: Festlegen der Spalten und ihrer Reihenfolge für die Abfrage
Abbildung 18.5: Hinzufügen einer ORDER BY-Klausel zur SQL-Abfrage
Ein wichtiger Grundsatz der Berichtsentwicklung besteht darin, dass Sie bei der Erstellung eines Berichts durch die Formulierung der Abfrage dem Datenbank-Server möglichst viel Arbeit übertragen. Es lassen sich sehr anspruchsvolle Berichte relativ schnell durch eine geschickte Formulierung der Abfrage erzeugen. Wählen Sie in der Registerkarte Fields alle angebotenen Felder für die Anzeige im Report aus, indem Sie mit Hilfe der Schaltflächen alle Spalten aus der linken Auswahlliste Available Fields in die rechte Auswahlliste Displayed Fields transportieren. Die folgende Registerkarte Totals können Sie mit Next überspringen, da wir in diesem Bericht keine Zusammenfassungen (Summen, Durchschnitte etc.) berechnen wollen. Der Report Wizard erzeugt für jedes Feld einen Bezeichner, indem er Unterstriche in der Spaltenbezeichnungen durch Leerstellen ersetzt und der jeweils erste Buchstabe eines jeden Wortes groß geschrieben wird. So bekommt die Spalte Department_ID die Beschriftung Department Id. Diese Beschriftungen sowie die Breite der Felder können Sie im Register Labels ändern (siehe Abbildung 18.6). In der letzten Registerkarte Template können Sie für den Bericht noch eine Berichts- Schablone (Template) auswählen. Eine Schablone (Template) enthält wiederverwendbare Berichtselemente, wie z.B. eine Kopf- oder Fußgestaltung (Header/Footer) oder Einstellungen zur Verwendung von Schriftfonts und Farben. Oracle Reports wird mit einer Reihe vordefinierter Schablonen (Templates) ausgeliefert. Sie können diese modifizieren oder eigene Schablonen im Reports Builder erstellen. Schablonen (Templates) werden als TDF-Dateien (Template Definition File) verwaltet. Wählen Sie die erste angebotene Schablone aus und beenden Sie die Arbeit mit Finish (siehe Abbildung 18.7).
Der Report Wizard ist wiederbetretbar, d.h. Sie können jederzeit Änderungen am Bericht vornehmen, indem Sie den Report Wizard für eine existierende Berichtsdefinition erneut starten.
Abbildung 18.6: Anpassen der Bezeichner und Feldbreiten
Abbildung 18.7: Auswahl der Schablone (Template) für den Bericht
Nachdem die Berichtsdefinition erzeugt wurde, die wir uns im Anschluss noch etwas genauer anschauen wollen, startet der Reports Builder den Live Previewer. Es erscheint ein Fenster Progress Report -, in dem die Aktivitäten sowohl auf dem Client als auch auf dem Server angezeigt werden. Nach kurzer Zeit sollte auf dem Bildschirm die erste Seite des Berichts wie in Abbildung 18.8 erscheinen.
Abbildung 18.8: Ansicht des Berichts im Live Previewer Der Live Previewer verwendet die vorhandenen Daten in Ihrer Entwicklungs- Datenbank, um den Bericht so zu erzeugen, wie ihn der Anwender in der Laufzeit- Umgebung sehen würde. Der entscheidende Unterschied zur Laufzeit-Umgebung besteht aber darin, dass Sie das Layout des Berichtes im Live Previewer noch bearbeiten können. Da diese Modifikationen direkt im Bericht erfolgen (WYSIWYG), ist dies relativ einfach und sehr anschaulich. Schließen Sie den Live Previewer; wir werden später noch Modifikationen im Live Previewer ausführen.
Bestandteile eines Berichts Nach dem Schließen des Live Previewer sollte der Object Navigator wieder sichtbar werden. Unter dem Knoten des aktuellen Moduls finden Sie die Bestandteile des soeben erstellten Berichts: ● ● ●
Das Datenmodell (Data Model) Das Layout-Modell (Layout Model) Das Parameter-Formular (Parameter Form)
Abbildung 18.9: Datenmodell des Berichts im Data Model Editor Markieren Sie den Knoten Data Model und doppelklicken Sie mit der Maus auf das linke Symbol; der Data Model Editor öffnet sich. Als Sie die SELECT-Anweisung für die Abfrage definiert haben, wurden vom Reports Wizard eine Abfrage Q_1 und eine Gruppe für die Abfrage G_COURSE_ID erstellt, die die ausgewählten Felder enthält (siehe Abbildung 18.9). Den Inhalt der Abfrage können Sie betrachten bzw. bearbeiten, indem Sie das entsprechende Symbol im Diagramm mit einem Doppelklick öffnen. Das Layout-Modell des Berichts können Sie entweder als hierarchische Darstellung im Object Navigator oder grafisch im Layout Editor bearbeiten. Öffnen Sie zunächst den Layout Editor, indem Sie im Object Navigator auf das Symbol links neben dem Knoten Layout Model doppelklicken. Im Layout Editor sehen Sie eine abstrakte Darstellung für das Layout des Rumpfbereichs (Body) (siehe Abbildung 18.10). Zugegebenermaßen ist nicht sofort zu erkennen, was durch all die Kästchen und Linien dargestellt wird. Hier kommt der Vorzug des schon vorgestellten Layout Previewer voll zur Geltung, der mit einer realitätsnahen Darstellung arbeitet. Trotzdem ist der Layout Editor für bestimmte Aufgaben unerlässlich.
Abbildung 18.10: Layout des erzeugten Berichts im Layout Editor
Abbildung 18.11: Vom Object Navigator angezeigte Hierarchie des Standardlayouts Wenn Sie sich das Layout im Object Navigator anschauen, bekommen Sie eine andere Ansicht der Elemente angezeigt (siehe Abbildung 18.11). Wie Sie hier erkennen, kann das Layout eines Berichts aus einem Kopfbereich (Header), Rumpfbereich (Body), Fußbereich (Footer) und Randbereich (Margin) bestehen. Durch das Standardlayout wurden nur Objekte für den Rumpfbereich (Body) und den Randbereich (Margin) erzeugt. Unter dem Rumpfbereich (Body) befindet sich ein Objekt namens M_G_COURSE_ID_GRPFR - ein Rahmen für die gesamte Gruppe. Darunter befinden sich zwei weitere Objekte - M_G_COURSE_ID_HDR und R_G_COURSE_ID. M_G_COURSE_ID_HDR ist ein Rahmen, der den Kopfbereich (Header) enthält und R_G_COURSE_ ID ist ein sich wiederholender Rahmen, der die eigentlichen Felder jedes Datensatzes enthält. Unter dem Knoten Parameter Form gibt es noch keinen Eintrag, da wir kein Parameter- Formular definiert haben.
Änderungen am Layout im Live Previewer durchführen Sie werden jetzt Änderungen am Layout vornehmen und dazu den Live Previewer benutzen. Sie können ihn über das Ampel-Symbol in der linken Werkzeugleiste oder über den Menüeintrag Program | Run Report aufrufen. Noch ist das Aussehen des Berichtes nicht zufriedenstellend. Als erstes ändern Sie mit den folgenden Schritten die Überschrift des Berichts: 1. Markieren Sie im Live Previewer die Überschrift und klicken Sie erneut in den markierten Bereich. Der Mauszeiger verwandelt sich in eine Einfügemarke. Drücken Sie zwischen FLUGLE COLLEGE und Course Catalog die Enter-Taste, damit sie untereinander stehen. 2. Um den Text der Überschrift zu zentrieren, wählen Sie den Menüeintrag Format | Justify | Center. 3. Formatieren Sie den Titel so, dass FLUGLE COLLEGE in MS Sans Serif, Fett, 14 Punkt und Course Catalog mit MS Sans Serif, Fett, 12 Punkt angezeigt werden. Vergrößern Sie die Höhe des Feldes durch Ziehen mit der linken Maustaste, falls der zweizeilige Text nicht mehr vollständig angezeigt wird. Als nächstes ändern Sie die Standard-Formatierung der Felder und Feldbezeichner: 4. Markieren Sie alle Feldbezeichner bei gedrückter Umschalt-Taste. Die Feldbezeichner sollen in MS Sans Serif, Fett, 12 Punkt dargestellt werden. 5. Markieren Sie alle Felder bei gedrückter Umschalt-Taste und stellen Sie als Standard-
Formatierung MS Sans Serif, Normal, 10 Punkt ein. Um etwas Platz im Layout zu gewinnen, ändern Sie die Bezeichner der Felder Course_ID, Department_ID und Additional_Fees. 6. Markieren Sie im Live Previewer den Bezeichner Course ID und klicken Sie erneut in den markierten Bereich. Der Mauszeiger verwandelt sich in eine Einfügemarke. Drücken Sie zwischen den beiden Teilen des Bezeichners auf die Enter-Taste. Vergrößern Sie die Höhe des Bezeichnerfeldes im Layout durch Ziehen mit der linken Maustaste, falls der Platz für die Darstellung nicht ausreicht. 7. Wiederholen Sie den vorhergehenden Schritt für jeden der Bezeichner. Im nächsten Schritt können Sie die Breiten der Spalten anpassen, so dass der verfügbare Platz besser genutzt wird: 8. Verringern Sie die Spaltenbreite für die Spalten Course_ID, Department_ID, Units und Additional_Fees, indem Sie den Bezeichner der Spalte markieren. Verringern Sie mit der Maus die Breite und positionieren Sie die Spalten im Layout. Sie werden feststellen, dass es ausreicht, den Bezeichner zu modifizieren. Die Spalten folgen automatisch den geänderten Einstellungen für den Bezeichner. Verbreitern Sie anschließend die Spalte Description. Zum Schluss wollen Sie noch die Formatmaske für das Feld ändern, welches Additional_Fees anzeigt. In der Standardeinstellung wird kein Währungs-Symbol angezeigt. 1. Markieren Sie das Feld namens Additional_Fees. 2. Betätigen Sie das Symbol Currency in der oberen Werkzeugleiste.
Datum und Seitenzahlen einem Bericht hinzufügen Der Bericht enthält durch die Wahl einer entsprechenden Schablone bereits eine Angabe zu Datum und Zeit der Ausführung. Sie können diese Angabe modifizieren oder löschen, um anschließend mit dem Symbol Insert Date and Time in der oberen Werkzeugleiste ein neues Feld im Layout zu platzieren. Um den Bericht mit einer Seitenzahl zu versehen, führen Sie die folgenden Schritte aus: 1. Betätigen Sie in der oberen Werkzeugleiste das Symbol Insert Page Number. Legen Sie fest, dass sowohl die aktuelle Seite als auch die Gesamtzahl der Seiten in der rechten oberen Ecke jeder Seite angezeigt werden sollen (siehe Abbildung 18.13). 2. Ändern Sie die Schriftattribute für die Seitenzahl auf Schwarz, Arial, Normal, 10 Punkt. Platzieren Sie die Seitenangabe nach Wunsch. Jetzt sollte Ihr Layout ungefähr dem aus Abbildung 18.12 gleichen.
Abbildung 18.12: Modifiziertes Layout des Berichts im Live Previewer
Testen des Berichts Jetzt ist es an der Zeit, den Bericht zu testen. Sie können den Bericht direkt aus dem Live Previewer ausführen und haben die Wahl zwischen: 1. Runtime Preview Sie rufen den Runtime Previewer über den Menüeintrag View | Runtime Preview auf; dies ist nur aus dem Live Previewer möglich. Der Runtime Previewer zeigt den Bericht am Bildschirm an (siehe Abbildung 18.13) und bietet als weitere Optionen: ● ● ●
Drucken des Berichts (File | Print) Versenden als E-Mail (File | Mail) Schreiben des Berichts in eine Datei, wobei als Format PDF, HTML, HTML mit Cascading Style Sheet, RTF, Postscript, ASCII und XML zur Verfügung stehen (File | Generate to File | ...)
2. Web Preview (Menüeintrag) Sie rufen den Web Preview über den Menüeintrag View | Web Preview auf; dies ist nur aus dem Live Previewer möglich. Ist dieser Eintrag einmal aktiviert, wird
der Browser auch bei folgenden Aufrufen des Live Previewer zusätzlich mit der Ansicht des Berichtes gestartet (siehe Abbildung 18.14). Sie können im Menü das Format für die Ansicht im Web einstellen (HTML, HTML mit Cascading Style Sheet, PDF, XML).
Abbildung 18.13: Erstellter Bericht im Runtime Previewer
Abbildung 18.14: Erstellter Bericht im Browser (Web Preview)
Bevor man einen Bericht im Web zur Verfügung stellt, sollten noch einige Änderungen an der Berichtsdefinition vorgenommen werden. So bietet es sich z.B. an, bestimmte Werte mit Bookmarks zu versehen, um die Navigation im Bericht zu vereinfachen. Der Reports Builder enthält einen Web Wizard (Menüeintrag Tools | Web Wizard), der Sie dabei unterstützt.
Einen Bericht drucken Es gibt unterschiedliche Möglichkeiten, einen Bericht auszudrucken. Sie haben bereits gesehen, dass im Reports Builder standardmäßig der Runtime Previewer aus dem Live Previewer aufgerufen wird. Sie können den Bericht anschließend aus dem Runtime Previewer zum Drucken schicken, indem Sie das Drucker-Symbol in der Werkzeugleiste anklicken. Sie können aber auch ein Formular mit Laufzeitparametern erstellen, in dem die Standardausgabe auf dem Drucker erfolgt.
Speichern des Berichts Jetzt ist es ratsam, Ihren Bericht zu speichern. Der Reports Builder kann ebenso wie der Forms Builder die von Ihnen entwickelten Module in Dateien oder in einer Oracle- Datenbank speichern. Um ein Modul in einer Datenbank zu speichern, müssen Sie oder Ihr DBA das von Oracle Reports benötigte Datenbank-Schema laden. Mehr darüber können Sie in der Dokumentation erfahren. Für die Beispiele dieses Buchs speichern Sie den Bericht jedoch in einer Datei. Um dies als Standardeinstellung im Reports Builder festzulegen, wählen Sie den Menüeintrag Tools | Preferences. In der Gruppe Access finden Sie ein Feld namens Access. Hier können Sie File als Standardeinstellung festlegen. Führen Sie folgende Schritte zum Speichern eines Berichts aus: 1. Wählen Sie den Menüeintrag File | Save As. 2. Wählen Sie das von Ihnen zur Speicherung Ihrer anwendungsspezifischen Dateien verwendete Verzeichnis aus. 3. Geben Sie Course als Dateiname ein. 4. Klicken Sie auf OK. Der Reports Builder speichert die Datei unter dem Namen Course.rdf im angegebenen Verzeichnis.
Der Reports Builder erlaubt Ihnen, mehrere Berichte gleichzeitig geöffnet zu haben. Sie werden dies als angenehme Arbeitserleichterung beim Kopieren von Objekten zwischen Berichten empfinden.
18.4 Einen Master-Detail-Bericht erstellen Ein weiterer gebräuchlicher Berichtstyp ist der Master-Detail-Bericht. Ein Beispiel wäre ein Bericht, der jede Abteilung mit den dazugehörigen Lehrkräften auflisten würde. Die Tabelle Department enthält die Master-Datensätze und die Tabelle Instructor die zugehörigen Detail-Datensätze.
Erstellen eines Master-Detail-Berichts mit dem Report Wizard Sie werden mit Hilfe des Report Wizard einen Master-Detail-Bericht erstellen. Gehen Sie dazu in folgenden Schritten vor: 1. Legen Sie ein neues Berichts-Modul über den Menüeintrag File | New | Report an. Akzeptieren Sie die vorgeschlagene Benutzung des Report Wizard oder rufen Sie ihn über den Menüeintrag Tools | Report Wizard auf. 2. Wählen Sie im Dialogfenster den Stil Group Above aus, so dass die Angaben zu jeder Abteilung über den Detail-Datensätzen zu den Lehrkräften der Abteilung erscheinen. Geben Sie als Titel des Berichts FLUGLE COLLEGE Departments and Faculty ein und bestätigen
Sie Ihre Eingaben (siehe Abbildung 18.15). 3. Wählen Sie SQL Statement als Typ der Abfrage und benutzen Sie den Query Builder, um die Abfrage zu konstruieren. Wählen Sie aus der Liste der Tabellen und Datensichten (Views) des Benutzers Flugle die Tabellen Department und Instructor aus, bestätigen Sie die Auswahl mit Include und schließen Sie das Fenster mit Close. Aktivieren Sie alle Spalten der Tabellen Department und Instructor im Diagramm mit Ausnahme der Spalten Telephone, Fax und Email (siehe Abbildung 18.16). 4. Erzeugen Sie die SQL-Abfrage durch Betätigung der Schaltfläche OK. Der Text der Abfrage erscheint im Textfeld des Registers Data. Fügen Sie der SELECT-Anweisung manuell eine ORDER BY-Klausel hinzu, damit die Zeilen nach Last_Name, First_Name und MI sortiert werden (siehe Listing 18.1). Listing 18.1: Die SELECT-Anweisung der Detail-Abfrage Select All Department.Department_ID, Department.Department_Name, Instructor.Instructor_ID, Instructor.Department_ID, Instructor.Last_Name, Instructor.First_Name, Instructor.MI, Instructor.Position from Department, Instructor where (Instructor.Department_ID = Department.Department_ID) order by Instructor.Last_Name, Instructor.First_Name, Instructor.MI;
Abbildung 18.15: Festlegung des Stils für den künftigen Bericht
Abbildung 18.16: Festlegen der Tabellen und Spalten für den Master-Detail-Bericht 5. Gehen Sie zur nächsten Registerkarte Groups, wählen Sie die Spalte Department_ID in der Wertelise Available Fields aus und transportieren Sie sie mit der Pfeil- Schaltfläche in die Werteliste Group Fields. Wiederholen Sie diesen Schritt mit der Spalte Department_Name. Sie müssen darauf achten, dass es im Ergebnis dieses Schrittes in der Werteliste Group Fields nur eine Gruppe (Level1) mit zwei Feldern gibt (siehe Abbildung 18.17). Durch die Festlegung einer Gruppe werden die Datensätze automatisch nach der Department_ID sortiert.
Abbildung 18.17: Definition einer Gruppe 6. Wählen Sie in der Registerkarte Fields alle angebotenen Felder mit Ausnahme von Department_ID1 für die Anzeige im Report aus, indem Sie mit Hilfe der Schaltflächen alle Spalten aus der linken Auswahlliste Available Fields in die rechte Auswahlliste Displayed Fields transportieren. Department_ID1 ist die Fremdschlüsselspalte aus der Tabelle Instructor und wird für die Verknüpfung, nicht aber in der Anzeige benötigt. Die folgende Registerkarte Totals können Sie mit Next überspringen, da wir in diesem Bericht keine Zusammenfassungen (Summen, Durchschnitte etc.) berechnen wollen. 7. Akzeptieren Sie die Bezeichner, wählen Sie Cyan Grid als vordefiniertes Template aus und beenden Sie die Arbeit mit Finish.
Datenmodell des Master-Detail-Berichts Schauen Sie sich einmal das Datenmodell des soeben erzeugten Berichts an, indem Sie mit der Maus auf das Symbol neben dem Knoten Data Model doppelklicken. Im Data Model Editor können Sie erkennen, dass auf Grundlage einer Abfrage zwei Gruppen; je eine für die Felder der MasterTabelle (Department) und der Detail-Tabelle (Instructor), erzeugt wurden (siehe Abbildung 18.18). Dieselbe Aufgabenstellung hätten wir auch lösen können, indem wir für jede der beiden Tabellen eine getrennte Abfrage mit jeweils einer eigenen Gruppe definiert hätten. Mit Hilfe des Data LinkWerkzeugs im Data Model Editor hätten wir beide Abfragen verknüpfen müssen. Der grundsätzliche Unterschied zwischen den beiden Varianten besteht darin, dass im ersten Fall die Verknüpfung zwischen den beiden Tabellen (Join) in der Datenbank ausgeführt wird und nur noch die Ergebnisse zurückgeliefert werden. Im zweiten Fall würden die Ergebnisse jeder einzelnen Abfrage vollständig zurückgeliefert und die Verknüpfung lokal ausgeführt. Auf Grund der begrenzten Ressourcen (Bandbreite der Netzwerkverbindung, lokale Rechenkapazität) ist in der Regel die erste Variante günstiger.
Abbildung 18.18: Datenmodell des Master-Detail-Berichts
Änderungen am Layout des Master-Detail-Berichts Das entstandene Layout sollte noch in folgenden Schritten geändert werden: 1. Markieren Sie im Live Previewer die Überschrift und klicken Sie erneut in den markierten Bereich. Der Mauszeiger verwandelt sich in eine Einfügemarke. Drücken Sie zwischen FLUGLE COLLEGE und Department and Faculty die Enter-Taste, damit sie untereinander stehen. 2. Um den Text der Überschrift zu zentrieren, wählen Sie den Menüeintrag Format | Justify | Center. 3. Formatieren Sie den Titel so, dass FLUGLE COLLEGE in MS Sans Serif, Fett, 14 Punkt und Department and Faculty mit MS Sans Serif, Fett, 12 Punkt angezeigt werden. Vergrößern Sie die Höhe des Feldes durch Ziehen mit der linken Maustaste, falls der zweizeilige Text nicht mehr vollständig angezeigt wird. 4. Richten Sie die Feldbezeichner der Master-Gruppe (Department) über den Menüeintrag Format | Justify | Left linksbündig aus und legen Sie als Standard- Formatierung MS Sans Serif, Normal, 14 Punkt fest. 5. Markieren Sie die Felder der Master-Gruppe (Department) bei gedrückter Umschalt- Taste und stellen Sie als Standard-Formatierung Arial, Normal, 12 Punkt ein. 6. Markieren Sie alle Feldbezeichner der Detail-Gruppe (Instructor) bei gedrückter UmschaltTaste. Die Feldbezeichner sollen in MS Sans Serif, Kursiv, 12 Punkt dargestellt werden. 7. Markieren Sie alle Felder der Detail-Gruppe (Instructor) bei gedrückter Umschalt- Taste und stellen Sie als Standard-Formatierung MS Sans Serif, Normal, 10 Punkt ein.
8. Verbreitern Sie die Spalten MI und Position, so dass die Bezeichner lesbar sind und die Werte einzeilig dargestellt werden können. Im Ergebnis dieser Änderungen sollte Ihr Bericht dem der Abbildung 18.19 ähnlich sein.
Abbildung 18.19: Geändertes Layout des Master-Detail-Berichts im Live Previewer
Testen und Speichern des Berichts 1. Klicken Sie in der horizontalen Werkzeugleiste auf die grüne Lampe. Der erzeugte Bericht wird im Live Previewer sichtbar. 2. Rufen Sie über den Menüeintrag View | Runtime Preview den Bericht im Runtime Previewer auf. Zusätzlich können Sie sich durch die Aktivierung der Option Generate to Browser und die Wahl eines geeigneten Formats den Bericht über View | Web Preview im Browser anzeigen lassen. 3. Schließen Sie den Live Previewer, wenn Sie die Überprüfung Ihres Berichts abgeschlossen haben. Wenn Sie es wünschen, können Sie den Bericht im Anwendungsverzeichnis unter dem Namen Dept_Instructor.rdf speichern.
Erzeugen einer Laufzeit-Version eines Berichts Wie bereits erwähnt wurde, können Sie die binäre Version einer Berichtsdefinition direkt ausführen. Die RDF-Datei wird geladen und im Hauptspeicher kompiliert. Es gibt allerdings zwei
Gründe, auch Reports-Definitionen zu kompilieren: ●
●
Man möchte in manchen Situationen keine durch Unbefugte lesbare Definitionsdateien ausliefern. Die Ausführungszeit kompilierter Reports ist geringer, da die implizite Kompilierung entfällt.
Sie können mit den Tasten Strg+T oder über die Menüauswahl File | Administration | Compile Report eine Laufzeit-Version des Berichts (REP-Datei) erzeugen.
18.5 Einen Format-Trigger einsetzen Im Reports Builder stehen Ihnen viele Möglichkeiten zur Anpassung eines Berichts zur Verfügung. So können Sie z.B. für ein Feld einen Formatierungs-Trigger definieren, damit das Aussehen eines Felds zur Laufzeit von seinem Inhalt abhängt. Wir wollen dies an dem von Ihnen erstellten MasterDetail-Bericht demonstrieren. Nehmen Sie an, im Bericht soll hervorgehoben werden, dass eine Lehrkraft Professor ist, indem der Wert im Feld Instrucor_ID in einer anderen Farbe (Rot) dargestellt wird. Dazu gehen Sie in folgenden Schritten vor: 1. Starten Sie den Bericht Dept_Instructor.rdf im Live Previewer. Markieren Sie das Feld Position und öffnen Sie über die rechten Maustaste den Menüeintrag Conditional Formatting im Kontextmenü. 2. Es öffnet sich ein Dialogfenster, in dem Sie Formatierungs-Bedingungen (Format Exceptions) eingeben können. Legen Sie mit New eine neue Formatierungs- Bedingung an. 3. Im folgenden Fenster können Sie die Bedingung beschreiben (siehe Abbildung 18.20): Bedingung: If the value of POSITION is EQUAL PROFESSOR Folge: wählen Sie als Farbe für den Text Rot aus. 4. Bestätigen Sie die Definition mit OK und schließen Sie auch das folgende Fenster mit den Formatierungs-Bedingungen (siehe Abbildung 18.21).
Abbildung 18.20: Definition einer Formatierungs-Bedingung
Abbildung 18.21: Liste der definierten Formatierungs-Bedingungen für das Feld F_Position Der Live Previewer startet automatisch und erzeugt eine neue Version des geänderten Berichtes, in der die Professoren farblich hervorgehoben sind. Um diese Funktionalität besser verstehen zu
können, schließen Sie den Live Previewer und betrachten Sie den Bericht im Object Navigator. Sie finden im Rumpfbereich innerhalb des sich wiederholenden Rahmens der Detail-Gruppe das Feld F_Position. Das Symbol neben diesem Feld enthält den Buchstaben P, der auf einen existierenden Formatierungstrigger hinweist. Durch einen Doppelklick auf das Symbol können Sie den Text dieses Triggers sehen und bearbeiten (siehe Abbildung 18.22). Er wurde automatisch durch die Festlegung einer Formatierungsbedingung im Live Previewer angelegt und kann jederzeit modifiziert werden.
Abbildung 18.22: Formatierungstrigger für das Feld F_Position Die im Trigger aufgerufene Funktion verwendet ein integriertes Paket namens SRW, das zur Änderung der Formateigenschaften eines mit einem Formatierungs-Trigger verknüpften Feldes eingesetzt wird. Die if-Anweisung überprüft, ob der aktuelle Wert des Feldes Position PROFESSOR ist. Wenn dies der Fall ist, wird der Aufruf der Funktion Set_Text_Color im Paket SRW ausgeführt. Die Funktion gibt einen Wert vom Typ Boolean zurück. Ist dieser Wert True, wird das Feld im Bericht mit den jeweiligen vom Formatierungs-Trigger geänderten Attributen angezeigt; ist der Wert False, wird das Feld im Bericht unverändert angezeigt.
18.6 Eine Verknüpfung zum Starten eines Berichts erzeugen Soll der Benutzer einen Bericht mit einem einzigen Mausklick ausführen können, lässt sich dies durch das Einrichten einer Windows-Verknüpfung (Shortcut) vereinfachen. In diesem Abschnitt
werden Sie durch die erforderlichen Schritte zur Erstellung einer Windows-Verknüpfung (Shortcut) geführt. Sie können wahlweise eine Verknüpfung zu einer binären Definitionsdatei (RDF) oder zu einer kompilierten REP-Datei erzeugen. Die Verknüpfung erstellen Sie wie folgt. Klicken Sie auf dem Windows-Desktop mit der rechten Maustaste und dann auf Neu | Verknüpfung. Im Fenster Verknüpfung erstellen müssen Sie die Befehlszeilenargumente eingeben. Vorher schauen Sie sich noch einige von Reports Runtime verwendete Argumente an: ●
● ● ● ●
userid identifiziert den Benutzer, das Kennwort und, wenn vorhanden, den Service- Namen zur Verbindung mit der Datenbank report identifiziert den auszuführenden Bericht. paramform bestimmt, ob das Formular für Laufzeitparameter angezeigt werden soll. destype legt die Ausgabe fest: Bildschirm, Drucker, Datei oder Mail. desformat legt das Ausgabeformat fest (z.B. PDF, HTML)
Um den Bericht Dept_Instructor.rdf zur Laufzeit auszuführen, geben Sie in das Befehlszeilenfeld folgendes ein: %ORACLE_HOME%\bin\rwrun60 report=c:\User\dept_instructor destype=screen %ORACLE_HOME% steht für das jeweilige Oracle-Stammverzeichnis. Klicken Sie auf die Schaltfläche Weiter. Geben Sie der Verknüpfung einen Namen, wie z.B. Report - Department with Instructors und klicken Sie auf die Schaltfläche Beenden. Die neue Verknüpfung sollte auf Ihrem Desktop erscheinen. Versuchen Sie, die Verknüpfung auszuführen. Sie werden zur Eingabe des Benutzernamens, des Kennworts und der Datenbank aufgefordert. Möchten Sie, dass der Benutzer nicht zur Eingabe dieser Werte aufgefordert wird, ändern Sie Verknüpfung folgendermaßen ab: %ORACLE_HOME%\bin\rwrun60 report=c:\User\dept_instructor userid=flugle/flugle@flugle destype=screen
Ein Nachteil dieser Verknüpfung ist, dass sie das Benutzerkennwort enthält und es von Dritten eingesehen werden kann.
18.7 Aufrufen eines Berichts aus einem Oracle-Formular Wie schon erwähnt wurde, handelt es sich bei der Internet Developer Suite (iDS) um eine Suite integrierter Werkzeuge. In diesem Abschnitt erfahren Sie, wie Sie aus einer Formularanwendung einen Bericht starten können. Als Beispiel werden wir Ihr Menü MAIN_MENU so verändern, dass der Bericht Course Catalog daraus aufgerufen werden kann. Beginnen Sie, indem Sie im Forms Builder das Menü-Modul MAIN_MENU öffnen. Wenn Sie auf das Symbol neben dem Modul doppelklicken, öffnet sich der Menu Editor und zeigt die
Menüstruktur in symbolischer Form. Erweitern Sie das Unter-Menü Reports, so gibt es dort nur einen Menüeintrag namens Course Catalog. Durch einen Doppelklick auf diesen Eintrag öffnet sich das Eigenschaftsfenster des Menüelements (siehe Abbildung 18.23).
Abbildung 18.23: Bearbeitung des Menüelements Course Catalog Bewegen Sie sich jetzt zur Eigenschaft Menu Item Command und klicken Sie in das Feld für den Wert der Eigenschaft, so dass die Schaltfläche More erscheint. Betätigen Sie diese, so erscheint der PL/SQL Editor. Wie in Abbildung 18.24 gezeigt, verwenden Sie die integrierte Funktion Run_Product, um Oracle-Berichte aufzurufen. Die Funktion Run_Product benötigt sieben Argumente: ● ● ●
● ● ●
●
Auszuführendes Produkt: Reports Auszuführendes Objekt: Course Modus ist entweder synchron (d.h., dass die Formular-Anwendung mit ihrer Fortführung wartet, bis die Arbeit mit dem aufgerufenen Produkt abgeschlossen ist) oder asynchron (d.h., dass die Formular-Anwendung aktiv bleibt, während das aufgerufene Produkt seine Aufgaben ausführt): Synchronous Ausführungsmodus, entweder Batch oder Laufzeit: Runtime Speicherort des Objekts, entweder Dateisystem oder Datenbank: Filesystem Parameterliste, Name einer Liste mit Übergabe-Parametern: Null-String, da Sie keine Parameter verwenden Name eines Grafikelements zur Anzeige eines Oracle Graphics-Diagramms: Null, da hier nicht anwendbar
Nachdem Sie die in Abbildung 18.24 angegebene Zeile eingetragen haben, klicken Sie auf Compile und Close. Speichern und erzeugen Sie das Menü, und testen Sie es auf seine korrekte
Funktion.
Abbildung 18.24: Starten eines Berichts aus einem Menüelement mit Hilfe von Run_Product
18.8 Zusammenfassung Nachfolgend werden noch einmal die Schwerpunkte dieser Lektion aufgeführt: ●
●
●
●
●
●
●
Oracle Reports besteht aus mehreren Komponenten, darunter der Reports Builder und Reports Runtime. Sie können den Reports Builder dazu einsetzen, Berichte unterschiedlicher Komplexität zu entwickeln. Der Reports Builder speichert die Berichte als portierbare Binärdateien (RDF). Der Reports Builder verwendet den Object Navigator, um die Komponenten eines Berichts in hierarchischer Form anzuzeigen. Ein Bericht besteht aus einem Datenmodell, dem Layout, einem Parameterformular und Berichts-Triggern. Ein Bericht enthält im Minimum das Datenmodell und das Layout. Der Reports Builder enthält spezielle Editoren und Wizards zur Definition der Berichtskomponenten. Zu diesen Editoren gehören u.a. der Data Model Editor und der Layout Editor. Ein Datenmodell besteht aus einer oder mehreren Abfragen, die die Daten des Berichts aus der Datenbank abrufen. Mit dem Data Model Editor können Sie Abfragen auf Basis gemeinsamer Spalten verknüpfen. Den Layout Editor verwenden Sie zur Gestaltung der äußeren Form eines Berichts. Das Layout hat vier Bestandteile: Kopfbereich (Header), Fußbereich (Footer), Rumpfbereich (Body) und Randbereich (Margin).
18.9 Wie geht es weiter? An Tag 19, »Oracle Graphics und Procedure Builder«, werden Sie zwei weitere Entwicklungswerkzeuge der Internet Developer Suite (iDS) kennen lernen: Oracle Graphics und den Procedure Builder. Sie werden lernen, aus dem Inhalt einer Tabelle ein Diagramm zu erstellen. Außerdem werden Sie erfahren, wie Sie der Procedure Builder bei der Erstellung und Verwaltung von PL/SQL-Modulen in einer Oracle-Datenbank unterstützt.
18.10 Fragen und Antworten Frage: Ist es möglich, die Ausführung eines Berichts aufzuzeichnen? Antwort: Ja, auch wenn es in dieser Lektion nicht behandelt wurde. Es ist möglich, einen Trigger zu definieren, der bei der Ausführung eines Berichts ausgelöst wird. Dieser Berichts-Trigger könnte in einer Protokoll-Tabelle eine Zeile hinzufügen, in der der Name des Berichts, der ausführende Benutzer und der Zeitpunkt der Ausführung festgehalten werden. Es können noch weitere Informationen über den Bericht gespeichert werden. Frage: Kann man einen Benutzer am Ausdruck eines Berichts nach Geschäftsschluss hindern? Antwort: Ja. Sie können auch einen Berichts-Trigger erstellen, der den Wochentag oder die Uhrzeit beachtet und kontrolliert, ob der Bericht zu dieser Zeit gedruckt werden soll. Frage: Ist es zweckmäßig, dass derselbe Bericht von mehr als einem Benutzer zur gleichen Zeit ausgeführt wird? Antwort: Ja. Mehrere Benutzer können den Bericht ohne nachteilige Auswirkungen zeitgleich ausführen, es sei denn, der Trigger ändert die Daten auf ungewöhnliche Weise. Wenn Sie den Oracle Reports Server einsetzen, können Sie jedoch solche Berichte zentral vorhalten, so dass der Bericht nur einmal ausgeführt wird und die Anwender bei Bedarf eine Kopie des Berichts erhalten.
18.11 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Sie können eine RDF-Datei in einem Source Code Control- System wie z.B. PVCS speichern. 2. Nennen Sie die vier Elemente eines Berichtslayouts. 3. Richtig oder falsch? Ein Bericht kann nicht mehr als zwei Abfragen miteinander verknüpfen.
Übungen Erstellen Sie einen Master-Detail-Bericht, der jeden Klassenraum und die für diesen Raum geplanten Veranstaltungen ohne Berücksichtigung der Unterrichtszeiten auflistet (d.h. alle Veranstaltungen, die in einem bestimmten Raum stattfinden sollen).
Anwendungsentwicklung mit Oracle Forms In den beiden vorhergehenden Lektionen haben Sie zwei Formulare erstellt: ein auf einer einzelnen Tabelle basierendes Formular und ein Master-Detail-Formular. Außerdem haben Sie gelernt, wie Sie das Standardlayout verändern können, das vom Forms Builder bei der Erstellung eines neuen Blocks erzeugt wird. Dieses ist Ihre letzte Lektion zu Oracle Forms. Hier werden Sie die Verwendung von PL/ SQL zur Erstellung von Anwendungs-Triggern für eine Reihe von Einsatzzwecken kennen lernen. Weiterhin werden Sie die Entwicklungsschritte für eine Anwendung mit mehreren Formularen durchgehen. Gleichzeitig lernen Sie, wie ein Menü erstellt wird, aus dem unterschiedliche Formulare aufgerufen werden.
17.1 Eine Übersicht über Trigger
Ein Trigger ist eine Folge von PL/SQL-Anweisungen, die ausgeführt wird, wenn ein Ereignis eintritt. Das Ereignis kann direkt mit einer vom Benutzer durchgeführten Aktion zusammenhängen, wie das Betätigen einer Schaltfläche. Oder das Ereignis ist indirekt mit einer vom Benutzer durchgeführten Aktion verbunden, wie der Zeitpunkt vor der Ausführung einer Abfrage. Trigger können auf drei Ebenen definiert werden: ● ● ●
Auf Formularebene Auf Blockebene Auf Feldebene
Häufig enthält ein Formular auf jeder Ebene einen oder mehrere Trigger.
Standard-Trigger In der vorherigen Lektion haben Sie ein Master-Detail-Formular namens Course_Class erzeugt. Als Sie die einzelnen Blöcke angelegt haben, haben Sie das Kontrollkästchen Enforce Data Integrity aktiviert, um anzugeben, dass die Integritätsregeln der Datenbank eingehalten werden sollen. Wenn Sie im Object Navigator unter dem Block Course den Knoten Trigger öffnen, werden Sie drei Trigger sehen, die erzeugt wurden, als Sie den Block Course bzw. die Master-Detail-Verknüpfung zum Block Class definiert haben (siehe Abbildung 17.1). ●
ON-CHECK-DELETE MASTER
● ●
ON-POPULATE-DETAILS KEY-DELREC
Abbildung 17.1: Trigger auf Blockebene, mit denen die Datenintegrität erzwungen wird Wie erzwingen diese Trigger die referentielle Integrität? Schauen wir uns den Trigger KEY- DELREC näher an. Dieser Trigger wird ausgelöst, wenn der Benutzer die Entfernen-Taste drückt. Listing 17.1 enthält den Text dieses Triggers. Beachten Sie, dass der Trigger sich aus einem anonymen PL/SQLBlock zusammensetzt. Der Block stellt fest, ob es von dem zu löschenden Kurs entsprechende Veranstaltungen gibt. Falls dies zutrifft, wird eine Fehlermeldung ausgegeben und eine Exception ausgelöst. Sollte die Tabelle Course durch weitere Fremdschlüssel referenziert werden, gäbe es für jede dieser Referenzen einen weiteren anonymen Block im Triggertext. Im Object Navigator kann der Programmtext eines Triggers durch einen Doppelklick auf denselben betrachtet werden. Listing 17.1: Code für den Trigger KEY-DELREC im Block Course --- Begin default enforce data integrity constraint PK_COURSE section -declare cursor primary_cur is select 'x' from FLUGLE.CLASS where COURSE_ID = :COURSE.COURSE_ID;
primary_dummy char(1); begin if ( ( :COURSE.COURSE_ID is not null ) ) then open primary_cur; fetch primary_cur into primary_dummy; if ( primary_cur%found ) then message('Cannot delete master record when matching detail records exist.'); close primary_cur; raise form_trigger_failure; end if; close primary_cur; end if; end; delete_record; --- End default enforce data integrity constraint PK_COURSE section --
17.2 Werteabfragen aus einer anderen Tabelle mit Hilfe eines Triggers Jetzt wollen wir an einem Beispiel durchgehen, wie in dem Formular Course_Class ein Trigger eingesetzt werden kann, um Werte aus einer anderen Tabelle zu selektieren. Schauen Sie sich den Block Course an, in dem zwar die Department_ID angezeigt wird. Es würde dem Benutzer wahrscheinlich helfen, wenn der Name der Abteilung angezeigt würde. 1. Benutzen Sie den Layout Editor, um im Rahmen Course ein neues Textfeld für den Namen der Abteilung anzulegen. Wählen Sie das Werkzeug Text Item aus der vertikalen Werkzeugleiste und klicken Sie mit der linken Maustaste rechts von Department_ID (siehe Abbildung 17.2). 2. Kümmern Sie sich nicht um die Größe und den Zwischenraum des neuen Textfeldes; Sie werden es anschließend ändern. 3. Markieren Sie das Textfeld und öffnen Sie mit der rechten Maustaste im Kontextmenü Property Palette, um die Eigenschaften des neuen Textfeldes zu ändern. 4. Geben Sie für die Eigenschaft Name Department_Name ein. Blättern Sie nach unten bis zur Eigenschaftengruppe mit dem Namen Navigation. Setzen Sie die Eigenschaft Keyboard Navigable auf No. 5. Das Textfeld Department_Name wird nur zur Anzeige des Abteilungsnamens benutzt; es gibt für den Benutzer keinen Grund, dieses zu aktivieren. Blättern Sie weiter nach unten zur Eigenschaftsgruppe mit dem Namen Database. 6. Setzen Sie die Eigenschaft Database Item auf No (siehe Abbildung 17.3). Der Grund, die Eigenschaft Database Item auf No zu setzen, besteht darin, dass der Block Course auf der Tabelle Course basiert und Department_Name keine Spalte dieser Tabelle ist.
Abbildung 17.2: Hinzufügen eines Textfeldes zum Block Course 7. Kehren wir zum Layout Editor zurück. Markieren Sie das Textfeld Department_ID und öffnen Sie mit der rechten Maustaste im Kontextmenü Property Palette. Blättern Sie nach unten zur Eigenschaft Height oder benutzen Sie die Suchfunktion im oberen Teil des Eigenschaftsfensters. 8. Im Fenster des Object Navigator markieren Sie das Textfeld Department_Name, ohne vorher das Eigenschaftsfenster zu schließen. 9. Beachten Sie, dass das Eigenschaftsfenster jetzt die Eigenschaften für Department_Name anzeigt und dass immer noch die Eigenschaft Height aktiviert ist. 10. Setzen Sie für die Höhe den gleichen Wert wie für Department_ID: 14. 11. Benutzen Sie die Funktionen zur Ausrichtung, die Sie bereits in der vorhergehenden Lektion kennen gelernt haben, um die beiden Felder mit Abteilungsangaben im Block Course auszurichten. Ändern Sie den Bezeichner des Feldes in Department.
Abbildung 17.3: Setzen der Eigenschaften für das Textfeld Department_Name Da Sie jetzt eine Stelle zur Anzeige des Abteilungsnamens haben, benötigen Sie noch einen Trigger, der für die Anzeige sorgt. 1. Verwenden Sie den Object Navigator, um die zum Block Course gehörenden Trigger zu erweitern, und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste, um einen neuen Trigger auf Blockebene zu erzeugen. 2. In dem neu erscheinenden Fenster können Sie das Ereignis auswählen, das den neuen Trigger auslösen soll; wählen Sie POST-QUERY und klicken Sie auf OK (siehe Abbildung 17.4).
Abbildung 17.4: Erstellen eines POST-QUERY-Triggesr für den Block Course 3. Der PL/SQL Editor erscheint. Geben Sie wie in Abbildung 17.5 gezeigt, eine SELECT- Anweisung ein, die den passenden Department_Name für den aktuellen Inhalt des Feldes Department_ID aus der Tabelle DEPARTMENT selektiert und im neu angelegten Textfeld anzeigt. 4. Geben Sie die SELECT-Anweisung aus Listing 17.2 ein und klicken Sie auf Compile. Wenn Sie die Anweisung richtig eingegeben haben, sollten Sie keine Fehlermeldung angezeigt bekommen. Klicken Sie auf Close.
Abbildung 17.5: Definieren des POST-QUERY-Triggers mit Hilfe des PL/SQL Editor
Beachten Sie, dass vor allen Bezügen auf Blöcke oder Felder ein Doppelpunkt steht. Listing 17.2: Definieren eines POST-QUERY-Triggers select department_name into :course.department_name from department where department_id = :course.departmen_id; Versuchen wir, dieses Formular auszuführen, um zu sehen, ob der Trigger korrekt arbeitet. 1. Klicken Sie auf die grüne Ampel in der vertikalen Werkzeugleiste. 2. Um den Block Course abzufragen, klicken Sie auf das Symbol Execute Query in der Werkzeugleiste. 3. Blättern Sie durch die angebotenen Kurse. Wie Sie in Abbildung 17.6 sehen können, hat der Trigger wie erwartet gearbeitet - der Name für jede Abteilung wurde aus der Tabelle DEPARTMENT selektiert.
Abbildung 17.6: Testen des POST-QUERY-Triggers Sehen wir jetzt, was passiert, wenn ein neuer Kurs eingegeben wird. 1. Geben Sie eine neue Course_ID (9999) ein. Anschließend können Sie aus der Werteliste (LOV) eine gültige Abteilung auswählen. Wie Sie in der Abbildung 17.7 sehen können, wird der Name der Abteilung für die Department_ID 1005 nicht angezeigt, wenn Sie zum Titel des Kurses navigieren. Es ist offensichtlich, dass für Department_ID ein Trigger auf Feldebene fehlt. 2. Verlassen Sie Forms Runtime. 3. Im Object Navigator erweitern Sie das Feld Department_ID.
Abbildung 17.7: POST-QUERY funktioniert nicht, wenn eine neuer Datensatz eingefügt wird 4. Im Knoten der zu Department_ID gehörenden Trigger existiert bereits ein Trigger WHENVALIDATE-ITEM, der für die Einhaltung der Integritätsbedingungen, konkret der Fremdschlüsselbeziehung, verantwortlich ist. 5. Öffnen Sie den PL/SQL Editor durch einen Doppelklick auf den Trigger. Wie es in Abbildung 17.8 zu sehen ist, fügen wir dem Triggertext den gleichen anonymen Block wie im POST-QUERYTrigger hinzu. Fügen Sie den anonymen Block am Ende des vorhandenen Textes an. 6. Zusätzlich sollten Sie dem Trigger einen Exception-Abschnitt hinzufügen, um auf die Situation zu
reagieren, dass der Benutzer eine ungültige Department_ID eingibt. Da im Trigger bereits die Fremdschlüsselbeziehung überprüft wird und Sie außerdem vorgegeben haben, dass die Werteliste Department_ID_LOV zur Gültigkeitsprüfung des Felds Department_ID benutzt werden soll, kann diese Exception nie auftreten.
Abbildung 17.8: Erweitern des WHEN-VALIDATE-ITEM-Triggers für das Feld Department_ID Listing 17.3: WHEN-VALIDATE-ITEM-Trigger für das Feld Department_ID begin select department_name into :course.department_name from department where department_id = :course.department_id; exception when no_data_found then message ('Invalid Department ID. Please enter a valid Department ID'); raise form_trigger_failure; end; Um den Trigger auf Elementebene zu testen, lassen wir das Formular erneut ausführen. 1. Geben Sie eine neue Course_ID (9999) ein. Wählen Sie anschließend aus der Werteliste (LOV) eine gültige Abteilung aus. 2. Navigieren Sie mit der Tabulatortaste zum Titel des Kurses. Wie Sie in der Abbildung 17.9 sehen können, wird der Name der Abteilung für die Department_ID 1005 korrekt angezeigt, wenn Sie das Feld Course_ID verlassen. Dies ist exakt der Zeitpunkt, zu dem der WHEN-VALIDATE-ITEMTrigger ausgeführt wird.
Abbildung 17.9: Testen des WHEN-VALIDATE-ITEM-Trigger für das Feld Department_ID
17.3 Eine Abfrage mit Hilfe eines Triggers auf Formularebene ausführen Wir wollen ein weiteres Beispiel für einen Anwendungstrigger erarbeiten. Führen Sie die folgenden Schritte aus, um ein Formular zu erstellen, das beim Aufruf automatisch eine Abfrage ausführt: 1. Erstellen Sie mit dem Forms Builder ein neues Formular, indem Sie den Menüeintrag File | New | Form wählen. 2. Nennen Sie das neue Formular Instructor und erstellen Sie einen neuen Block auf Basis der Tabelle Instructor. Wenn Sie die Gestaltung Ihres Formulars abgeschlossen haben, sollte es dem aus Abbildung 17.10 ähneln. 3. Wenn Sie den Object Navigator verwenden, um das Formular Instructor zu betrachten, erkennen Sie, dass auf Formularebene noch keine Trigger vorhanden sind. Um einen Trigger auf Formularebene zu erstellen, wählen Sie den Trigger-Knoten aus und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste. 4. Wählen Sie im dann erscheinenden Fenster WHEN-NEW-FORM-INSTANCE als Trigger-Typ und klicken Sie auf OK. Der Trigger wird nach dem erfolgreichen Aufruf des Formulars ausgelöst. 5. Der PL/SQL Editor wird aufgeblendet und Sie brauchen nur eine einzige Zeile PL/ SQL-Code für diesen Trigger zu schreiben: einen Aufruf von execute_query. Bei diesem Aufruf handelt es sich um eine so genannte Built-In-Prozedur, die von Oracle Forms mitgeliefert wird. Oracle Forms enthält
eine umfangreiche Liste von Built-In- Prozeduren, -Funktionen und -Paketen, über die Sie sich in der Online-Hilfe informieren können. Die Prozedur execute_query führt im aktuellen Block eine Abfrage aus. 6. Klicken Sie auf Close und testen Sie das Formular, indem Sie auf die grüne Ampel in der vertikalen Werkzeugleiste klicken. Wie Sie der Abbildung 17.11 entnehmen können, wird die Abfrage beim Aufruf des Formulars jedes Mal automatisch ausgeführt.
Abbildung 17.10: Erstellen eines Formulars für die Tabelle Instructor
Abbildung 17.11: Testen des WHEN-NEW-FORM-INSTANCE-Triggers
17.4 Benutzereingaben mit Hilfe eines Triggers auf ihre Gültigkeit überprüfen In diesem Abschnitt untersuchen Sie eine weitere Verwendung eines Triggers auf Feldebene: zur Gültigkeitsüberprüfung von Benutzereingaben. Nehmen Sie an, der Schulleiter hätte Sie informiert, dass eine zusätzliche Lehrkraft für eine Abteilung, der schon mindestens ein Professor als Lehrkraft angehört, nicht auch ein Professor sein kann - er oder sie können höchstens Assistent oder beratender Professor sein. Dazu verändern Sie den bestehenden Trigger WHEN-VALIDATE-ITEM für das Textfeld Position, indem Sie den Knoten für das Textfeld erweitern und einen Doppelklick auf das Symbol links vom Trigger WHEN-VALIDATE-ITEM ausführen. In der Abbildung 17.12 können Sie erkennen, dass der bestehende Trigger die gleiche Logik aufweist, wie die CHECK-Regel für die Spalte Position in der Datenbank-Tabelle Instructor. Dieser Trigger wurde automatisch erzeugt, als bei der Erstellung des Blocks das Kontrollkästchen Enforce Data Integrity aktiviert wurde. Das Listing 17.4 zeigt den Trigger WHEN-VALIDATE-ITEM, nachdem er entsprechend verändert wurde. Listing 17.4: Geänderter WHEN-VALIDATE-ITEM-Trigger declare num_full_professors integer; begin if not( :INSTRUCTOR.POSITION IN ('ASSISTANT PROFESSOR', 'ASSOCIATE PROFESSOR', 'PROFESSOR') ) then message( 'WHEN-VALIDATE-ITEM trigger failed on field - ' || :system.trigger_field );
raise form_trigger_failure; end if; select count(*) into num_full_professors from Instructor where Department_ID = :Instructor.Department_ID and Position = 'PROFESSOR'; if num_full_professors > 0 and :Instructor.Position = 'PROFESSOR' then message ('Cannot add another full professor to this department.'); raise form_trigger_failure; end if; end;
Als erstes stehen die PL/SQL-Anweisungen in einem anonymen Block und eine Variable num_full_professor wird deklariert. Im ersten Teil des Blocks werden die CHECK-Regeln durchgesetzt. Dann wird eine SELECT-Anweisung eingesetzt, um die Anzahl der schon besetzten vollen Professorenstellen in der Abteilung zu ermitteln. Ist diese Zahl größer als Null, wird eine Meldung angezeigt und eine vordefinierte Exception, FORM_TRIGGER_FAILURE, wird ausgelöst, die den Benutzer an der weiteren Arbeit hindert, solange der Wert nicht berichtigt wurde.
Abbildung 17.12: Im WHEN-VALIDATE-ITEM-Trigger vorhandener Code
Abbildung 17.13: Testen des WHEN-VALIDATE-ITEM-Triggers Wir wollen den Trigger jetzt testen. Wählen Sie den Menüeintrag Record | Insert oder das Symbol Insert Record in der Werkzeugleiste, um eine neue Lehrkraft einzurichten. Versuchen Sie, eine neue Lehrkraft für die Anthropologie-Abteilung (Department_ID: 1003) einzugeben, die schon zwei volle Professorenstellen besitzt, Boris Hitchcock und Ranier Poulson. Abbildung 17.13 zeigt den Trigger in Aktion. Wie beabsichtigt, hindert der Trigger Sie jedoch nicht daran, für die Mathematik-Abteilung (Department_ID: 1005) eine neue Professorenstelle einzugeben, da in dieser noch keine volle Professorenstellen besetzt ist. Sollte es Ihnen nicht gefallen, dass die Fehlermeldung nur in der Meldungszeile sichtbar ist, können Sie die Anwendung erweitern, indem Sie ein Meldungsfenster (Alert) hinzufügen.
17.5 Elemente eines Menüs Ein Menü wird wie ein Formular durch eine Zusammenstellung von Objekten definiert. Einige dieser Objekte sind erforderlich, andere optional. Jedes Objekt kann von einem der folgenden Typen sein: ● ● ● ● ● ● ●
Sie werden jetzt ein Menü für eine Multiform-Anwendung mit dem Namen Main_Menu erstellen.
17.6 Entwickeln eines Menüs Beginnen Sie, indem Sie im Forms Builder den Menüeintrag File | Open wählen, um die Datei %ORACLE_HOME%\Tools\DevDem60\Demo\Forms\menudef zu öffnen. Dies ist der Standardpfad, wenn Sie die mit Oracle Forms ausgelieferten Demonstrations-Programme unter Windows installiert haben (%ORACLE_HOME% steht für das jeweilige Oracle- Stammverzeichnis). Wenn Sie für das Menü Menudef im Object Navigator die Property Palette öffnen, so finden Sie eine Eigenschaft namens Main Menu, die auf MAIN_MENU gesetzt ist. Diese Eigenschaft verweist auf das Menü, welches in der Anwendung als Haupt- bzw. Startmenü sichtbar ist.
Wenn Sie einem Formular kein Menü zugeordnet haben, verwendet dieses Formular ein internes, nicht veränderbares Standard-Menü. Die mit den Demonstrations-Programmen ausgelieferte Datei menudef.mmb entspricht in ihrer Struktur exakt diesem internen StandardMenü und ist dafür gedacht, relativ schnell Modifikationen am Standard-Menü durchzuführen. Unter dem Knoten Menus finden Sie folgende Unter-Menüs: ● ● ● ● ● ● ● ●
ACTION BLOCK MAIN_MENU EDIT FIELD HELP QUERY RECORD
Diese Unter-Menüs stellen die Auswahlmöglichkeiten auf oberster Ebene dar. Dies ist jedoch das Standardmenü und Sie möchten auf dieser obersten Ebene drei weitere Auswahlmöglichkeiten hinzufügen: ●
●
●
Einen Menüpunkt mit Optionen, über die der Benutzer Daten in unterschiedliche Tabellen eingeben kann (Data_Entry). Ein Menüpunkt mit Optionen, über die der Benutzer Datensätze in einer Datenbank ansehen aber nicht verändern kann (Browse). Ein Menüpunkt mit Optionen zum Aufruf von Berichten und Diagrammen (Reports).
Um der obersten Ebene drei weitere Unter-Menüs hinzuzufügen, wählen Sie mit der Maus oder den Pfeiltasten den Objektknoten Menus aus und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste. Es erscheint eine Menü mit dem Namen MENUx, das Sie einmal anklicken, so dass es in blauer Farbe angezeigt wird. Jetzt ändern Sie den Namen in DATA_ENTRY. Zur Erstellung der Menüs BROWSE und REPORTS verfahren Sie genauso (siehe Abbildung 17.14). Ziehen Sie die drei Menüs unter das Menü RECORD.
Abbildung 17.14: Erstellen neuer Menüs
17.7 Integrieren neuer Menüs Auch wenn es durch die Betrachtung der Menüs im Object Navigator schwer zu erkennen ist, ist MAIN_MENU das Menü auf der obersten Ebene. 1. Erweitern Sie die Anzeige des Objektknotens Items unter MAIN_MENU. Jetzt können Sie die gegenwärtig definierte Struktur des Menübaumes erkennen. 2. Um dem Hauptmenü das Menü Data Entry hinzuzufügen, wählen Sie den Objektknoten Items und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste. Sie sehen jetzt ein neues Objekt namens ITEMx. 3. Klicken Sie das neue Objekt an und benennen Sie es um in DATA_ENTRY. 4. Wenn die Eigenschaften für DATA_ENTRY nicht angezeigt werden, öffnen Sie mit der rechten Maustaste die Property Palette im Kontextmenü und blättern Sie dann nach unten bis zur Eigenschaft Command Type. Ändern Sie den Wert der Eigenschaft in Menu. 5. Nachdem Sie den Wert in Menu geändert haben, ändert sich die Bezeichnung der folgenden Eigenschaft dynamisch in Submenu Name. Wählen Sie aus der Werteliste das Menü DATA_ENTRY aus (siehe Abbildung 17.15). 6. Blättern Sie nach oben bis zur Eigenschaft Label, und geben Sie Data Entry ein. Dies wird der in der Menüleiste angezeigte Text sein.
7. Wenn Sie sich die zu MAIN_MENU gehörenden Objekte anschauen, sehen Sie, dass DATA_ENTRY das erste Element ist. Sie möchten aber nicht, dass es dort in der Menüleiste erscheint. Um es zwischen EDIT und QUERY zu stellen, wählen Sie DATA_ENTRY mit der Maus und ziehen es bei gedrückt gehaltener linker Maustaste, bis unter EDIT eine horizontale Linie erscheint, und lassen die linke Maustaste los. 8. Um die Unter-Menüs BROWSE und REPORTS in die oberste Ebene von MAIN_MENU einzuordnen, führen Sie die Schritte 3 bis 7 erneut aus. 9. Doppelklicken Sie auf das Symbol links neben MENUDEF, um das Aussehen des Menüs im Menu Editor zu überprüfen. Abbildung 17.16 zeigt, wie es aussehen sollte.
Abbildung 17.15: Festlegen der Eigenschaften Command Type und Submenu Name für das neue Menüelement
Abbildung 17.16: Anzeige des Menüs im Menu Editor
Elemente zu einem Menü hinzufügen Sie haben zwar drei neue Menüs erstellt, aber keines enthält irgendwelche Menüoptionen. Wir wollen nun dem Menü DATA_ENTRY eine Menüoption hinzufügen. 1. Verwenden Sie den Object Navigator, um die Anzeige des Menus DATA_ENTRY zu erweitern (nicht des Items unter MAIN_MENU). 2. Wählen Sie den Objektknoten Items und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste. 3. Im Eigenschaftsfenster ändern Sie den Namen des Elementes in Instructor. 4. Blättern Sie nach unten bis zur Eigenschaft Command Type, der auf PL/SQL gesetzt ist. Ändern Sie diese Einstellung nicht, da es der gewünschte Wert ist. 5. Blättern Sie weiter nach unten bis zur Eigenschaft Menu Item Code und klicken Sie in das Feld für den Wert der Eigenschaft, so dass die Schaltfläche More erscheint. Betätigen Sie diese, so erscheint der PL/SQL Editor. 6. Geben Sie call_form('Instructor',NO_HIDE,NO_REPLACE); ein. Diese PL/SQL- Anweisung ruft die mitgelieferte Built-In-Prozedur call_form auf, die das Formular Instructor anzeigt. 7. Klicken auf Compile and Close. 8. Blättern Sie im Eigenschafts-Fenster nach oben bis zur Eigenschaft Label und geben Sie dort Instructor ein. Jetzt müssen Sie noch je ein Element für die Unter-Menüs BROWSE und REPORTS erstellen. Tun Sie dies nicht, erzeugt der Forms Compiler eine Fehlermeldung, wenn Sie eine MMX-Datei für das Menü erzeugen wollen. 1. Erstellen Sie für das Unter-Menü BROWSE ein Element namens INSTRUCTOR_BROWSE. 2. Als Menu Item Code geben Sie für dieses Element ein: call_form('Instructor_Browse',NO_HIDE,NO_REPLACE);. 3. Blättern Sie im Eigenschafts-Fenster nach oben bis zur Eigenschaft Label und geben Sie dort Browse Instructors ein. 4. Erstellen Sie für das Unter-Menü REPORTS ein Element namens COURSE_CATALOG.
5. Als Menu Item Code geben Sie für dieses Element NULL; ein. Durch die Angabe NULL wird der Menüpunkt zwar im Menü erscheinen, aber noch keine Aktion ausführen. 6. Blättern Sie im Eigenschafts-Fenster nach oben bis zur Eigenschaft Label und geben Sie dort Course Catalog ein. In der nächsten Lektion lernen Sie, wie mittels einer Menüoption ein Oracle-Bericht aufgerufen werden kann.
Speichern des Menüs Bevor Sie fortfahren, sollten Sie das Menü umbenennen und speichern. 1. 2. 3. 4. 5. 6.
Wählen Sie MENUDEF. Wählen Sie den Menüeintrag File | Save as. Achten Sie darauf, dass Sie als Ziel das für Ihre Anwendung bestimmte Verzeichnis angeben. Im Feld Save as wählen Sie Menus (*.mmb). Im Feld File name geben Sie Main_Menu ein. Klicken Sie auf Save.
Erzeugen des Menüs Wird ein Menü im Dateisystem gespeichert, erhält die Datei die Endung .mmb. Forms Runtime kann jedoch nur eine MMX-Menüdatei ausführen, die aus einer MMB-Datei erzeugt wird. Um für das MAIN_MENU eine MMX-Datei zu erzeugen, wählen Sie den Menüeintrag File | Administration | Compile File oder drücken Sie die Tasten (STRG) + (T).
17.8 Entwickeln einer Multiform-Anwendung
Bislang haben Sie nur einzelne Formulare innerhalb des Forms Builder getestet. Jetzt werden Sie die Schritte durchführen, die zur Entwicklung einer Multiform-Anwendung erforderlich sind. Im ersten Schritt muss ein Formular erstellt werden, welches das Hauptfenster der Anwendung enthält; dieses Fenster soll immer das Menü sowie die Meldung- und Statuszeile anzeigen. 1. Dazu wählen Sie den Menüeintrag File | New | Form. Im Object Navigator erscheint ein neues Fenster. 2. Ändern Sie den Modul-Namen dieses Formulars in MDI_FRAME. 3. Erweitern Sie die Anzeige des Objektknotens Windows und wählen Sie das einzige, automatisch erzeugte Fenster aus. Rufen Sie mit der rechten Maustaste die Property Palette aus dem Kontextmenü auf. 4. Blättern Sie zur Eigenschaft Title und setzen Sie diese auf Flugle Information System. 5. Stellen Sie Width auf 600 und Height auf 420. Diese Größenangaben sind von der Bildschirmauflösung abhängig und müssen eventuell angepasst werden. Abbildung 17.17 zeigt die für das Fenster gesetzten Eigenschaften.
Abbildung 17.17: Definieren der Eigenschaften für das Fenster 6. Jetzt müssen Sie noch eine Leinwand (Canvas) für das Formular erstellen. Wählen Sie den Objektknoten Canvas und klicken Sie auf das Symbol + in der vertikalen Werkzeugleiste. 7. Wählen Sie den erstellten Canvas aus und öffnen Sie mit der rechten Maustaste die Property Palette aus dem Kontextmenü. 8. Setzen Sie Width auf 600 und Height auf 420; die gleichen Werte, wie Sie sie für das Fenster festgelegt haben. Dies sind die einzigen für die Leinwand (Canvas) benötigten Eigenschaften. Genaugenommen müssen Sie die Größe nicht setzen, da die Leinwand zur Laufzeit dem Fenster angepasst wird. 9. Wählen Sie jetzt das Formular MDI_FRAME aus und rufen Sie mit der rechten Maustaste die Property Palette aus dem Kontextmenü auf. 10. Setzen Sie die Eigenschaft Title auf Flugle Information System. 11. Blättern Sie nach unten zur Eigenschaft Menu Module und setzen Sie diese auf MAIN_MENU. Dies verweist auf das soeben von Ihnen erstellte Menü. 12. Blättern Sie nach unten bis zur Eigenschaft Initial Menu und setzen Sie diese auf MAIN_MENU.
Dies wird das zuerst verwendete Menü, wenn das Formular MDI_FRAME aufgerufen wird. Abbildung 17.18 zeigt die Eigenschaften des Formulars MDI_FRAME. 13. Speichern Sie das Formular MDI_FRAME als MDI_Frame.mmb in dem für die Anwendungskomponenten eingerichteten Verzeichnis. 14. Erzeugen Sie die .fmx-Datei für das Formular MDI_FRAME, indem Sie die Tasten (STRG)+(T) drücken.
Abbildung 17.18: Definieren der Eigenschaften für das Formular MDI_FRAME
17.9 Erzeugen einer Windows-Verknüpfung (Shortcut) zum Starten der Anwendung Ob Sie es glauben oder nicht, Sie haben die für eine Multiform-Anwendung benötigten Basiselemente erstellt. Um Ihre Anwendung zu testen, richten Sie in Ihrer Oberfläche eine Verknüpfung (Shortcut) ein. Dazu klicken Sie auf dem Desktop von Windows mit der rechten Maustaste und dann auf Neu | Verknüpfung. Im Fenster Verknüpfung erstellen müssen Sie die Befehlszeilenargumente eingeben. Ehe Sie diese für die Verknüpfung eingeben, wollen wir einige der von Forms Runtime verwendeten Argumente vorstellen:
●
●
●
userid identifiziert den Benutzer, das Kennwort und, wenn notwendig, den Service- Namen zur Verbindung mit der Datenbank. module identifiziert das Formular, das Sie ausführen möchten (dies sollte das Formular MDI_FRAME sein, welches das Hauptfenster enthält). window_state ist ein optionaler Parameter, der die Darstellung des in module angegebenen Fensters bestimmt (Maximiert, Minimiert oder Wiederhergestellt).
Um Ihre Formular-Anwendung auszuführen, geben Sie im Feld Befehlszeile folgendes ein: %ORACLE_HOME%\bin\ifrun60 module=c:\User\mdi_frame userid=flugle/flugle@flugle window_state=maximize Klicken Sie auf die Schaltfläche Weiter. Geben Sie der Verknüpfung einen Namen, wie z.B. Flugle Information System, und klicken Sie auf die Schaltfläche Beenden. Die neue Verknüpfung sollte auf Ihrem Desktop erscheinen. Versuchen Sie, die Verknüpfung auszuführen. Rufen Sie den Menüeintrag Data Entry | Instructor auf; das Formular Instructor sollte Ihnen angezeigt werden. Selbstverständlich könnte die Multiform-Anwendung noch erweitert und verbessert werden. So könnte beispielsweise im Fenster des Formulars MDI_FRAME ein statisches Bild als Begrüßung oder Hintergrund angezeigt werden. Die Größe des Fensters könnte nach dem Aufruf durch einen Trigger dynamisch dem äußeren Fenster von Forms Runtime angepasst werden.
Ein Nachteil der Verknüpfung ist es, dass das Kennwort in der Verknüpfung enthalten ist und von anderen eingesehen werden kann. Wenn Sie für userid keinen Wert angeben, fordert Forms Runtime bei Aktivierung der Verknüpfung den Benutzer zur Eingabe des Benutzernamens, Kennworts und der Datenbank auf.
Schließen eines Formulars Wenn Sie das Formular Instructor aus dem Menü Data Entry aufrufen, können Sie feststellen, dass Sie das Formular durch die Auswahl des Menüeintrags Action | Exit schließen können. Klicken Sie jedoch auf das Schließen-Symbol in der rechten oberen Fensterecke des Formulars Instructor, schließt sich das Formular Instructor nicht. Wahrscheinlich möchten Sie aber, dass sich das Formular schließt, wenn der Benutzer dieses Symbol anklickt. Dazu müssen Sie im Formular Instructor einen weiteren Trigger erstellen. 1. 2. 3. 4. 5. 6. 7. 8. 9.
Öffnen Sie mit dem Object Navigator das Formular Instructor. Erweitern Sie das Formular, und markieren Sie den Knoten für die Trigger auf Formularebene. Klicken Sie auf + in der vertikalen Werkzeugleiste, um einen neuen Trigger zu erstellen. Im darauf erscheinenden Fenster wählen Sie das Ereignis WHEN-WINDOW-CLOSED, und klicken Sie auf OK. Doppelklicken Sie auf das im Object Navigator links neben dem Trigger WHEN- WINDOWCLOSED angezeigte Symbol. Im PL/SQL Editor geben Sie eine einzige Zeile ein: exit_form;. Diese mitgelieferte Built-In-Prozedur wird immer dann aufgerufen, wenn das Fenster geschlossen wird. Klicken Sie auf die Schaltfläche Close. Speichern Sie das Formular Instructor und lassen Sie es erneut kompilieren. Benutzen Sie die Verknüpfung, um zu testen, ob das Formular bei einem Klick auf das Schließen-
Symbol geschlossen wird.
17.10 Zusammenfassung Dies waren die wesentlichen Punkte dieser Lektion: ●
●
●
●
● ● ●
●
● ●
Ein Trigger ist ein Gruppe von PL/SQL-Anweisungen, die beim Eintreten bestimmter Ereignisse ausgeführt werden. In einem Formular können Trigger auf unterschiedlichen Ebenen definiert werden: auf Formular-, Block- und Feldebene. Die Ereignisse, die einen Trigger auslösen können, sind von der Ebene des Triggers abhängig. So lösen z.B. bestimmte Fensterereignisse nur Trigger auf Formularebene aus. Einige Trigger werden vom Data Block Wizard automatisch erzeugt, wenn Sie für einen neuen Block die Einhaltung von Integritätsregeln erzwingen. Zur Festlegung der PL/SQL-Anweisungen für einen Trigger verwenden Sie den PL/ SQL Editor. Ein Trigger kann zum Lesen von Werten aus anderen Tabellen eingesetzt werden. Ein Trigger kann zur Gültigkeitsüberprüfung von Benutzereingaben eingesetzt werden. Ein solcher Trigger kann die vordefinierte Exception FORM_TRIGGER_FAILURE aufrufen, wenn die Benutzereingabe ungültig ist. Ein Trigger kann zur automatischen Ausführung einer Abfrage eingesetzt werden, wenn ein Formular aufgerufen wird. Zur Erstellung und Anpassung von Menüs verwenden Sie ebenfalls den Forms Builder. Ein Menü kann aus verschiedenen Objekttypen bestehen: einem Hauptmenü, Unter- Menüs, Objektgruppen, Programmeinheiten, Parametern, Eigenschaftenklassen, verbundenen Bibliotheken und visuellen Attributen.
17.11 Wie geht es weiter? An Tag 18, »Entwicklung von Berichten mit Oracle Reports«, lernen Sie Oracle Reports kennen. Sie werden die Schritte zur Erstellung von zwei Berichten durcharbeiten: einen einfachen, auf einer Tabelle basierenden Bericht und einen Master-Detail-Bericht. Außerdem sehen Sie, wie ein Format-Trigger definiert wird, der PL/SQL-Anweisungen verwendet, um das Aussehen von Berichtselementen abhängig von ihren Werten zu beeinflussen.
17.12 Fragen und Antworten Frage: Wie verarbeitet Oracle Forms auf unterschiedlichen Ebenen definierte Trigger, die auf dasselbe Ereignis reagieren? Antwort: Über die Eigenschaft Execution Hierarchy kann das Verhalten von Triggern gesteuert werden, die für das gleiche Ereignis auf unterschiedlichen Ebenen definiert wurden. Die Standardeinstellung ist Override, d.h. der Trigger auf niedrigerer Ebene wird anstelle des Triggers auf höherer Ebene ausgelöst. Weitere mögliche Einstellungen sind Before und After, mit denen eine Reihefolge bei der Ausführung der beiden Trigger festgelegt wird. Frage: Wie heißen einige der mitgelieferten Built-In-Prozeduren, die in Oracle Forms zur Verfügung stehen?
Antwort: Es gibt eine Reihe von mitgelieferten Built-In-Prozeduren, die aus PL/SQL- Unterprogrammen, in Triggern oder in Bibliotheks-Modulen aufgerufen werden können. Diese Prozeduren schließen ein: ● ●
●
Prozeduren zum Navigieren, wie GO_BLOCK, GO_ITEM und NEXT_ITEM Prozeduren, die den Inhalt eines Blocks ändern, wie CLEAR_RECORD, DELETE_RECORD und INSERT_RECORD Prozeduren, die Abfrageprozesse steuern: ENTER_QUERY und EXECUTE_QUERY
Frage: Wie heißen einige der in Oracle Forms verfügbaren Systemvariablen? Antwort: Es gibt einige Systemvariablen, die in einem Trigger nützlich sein können: ● ● ● ● ●
$$DATE$$ und $$TIME$$ enthalten aktuelles Datum und Zeit: Variablen, die die aktuelle Auswahl in einem Formular beschreiben: SYSTEM.CURRENT_FORM, SYSTEM.CURRENT_BLOCK SYSTEM.CURRENT_ITEM
Frage: Unterstützt Oracle Forms die Verwendung von globalen Variablen? Antwort: Ja. Sie können zur Speicherung von Zeichenfolgen mit bis zu 255 Zeichen globale Variablen verwenden. Eine globale Variable muss nicht explizit deklariert werden; sie wird implizit deklariert, wenn Sie ihr einen Anfangswert zuweisen: :GLOBAL.Instructor_ID _: #a1234';
17.13 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Ein Trigger auf Feldebene innerhalb eines Blocks kann nicht auf Felder eines anderen Blocks verweisen. 2. Wie wird in einem Trigger auf Felder in einem Block Bezug genommen? 3. Welche Exception sollte bei einer ungültigen Benutzereingabe aufgerufen werden, wenn Sie einen Trigger auf Feldebene zur Gültigkeitsüberprüfung von Benutzereingaben erstellen?
Übungen 1. Erstellen Sie ein Formular Instructor_Browse, das aus der Menüoption des Unter- Menüs Browse aufgerufen werden kann. Es soll Angaben aus der Tabelle Instructor tabellarisch und sortiert nach dem Nachnamen, Vornamen und den Initialen anzeigen.
Entwicklung einer Benutzeroberfläche mit Oracle Forms In der vorhergehenden Lektion wurden Sie in einige grundlegenden Konzepte der Oracle Internet Developer Suite (iDS) eingeführt und Sie haben mit Oracle Forms zu arbeiten begonnen. Sie haben gelernt, wie ein einfaches Formular auf der Basis einer einzelnen Tabelle erstellt wird. In dieser Lektion setzen Sie die Arbeit mit dem Forms Builder fort und erstellen ein Master-Detail-Formular. Außerdem lernen Sie, wie Sie mit dem Layout Editor das Aussehen eines Formulars anpassen können.
Ein Master-Detail-Formular ist ein aus zwei Blöcken, dem Master- und dem Detail-Block, bestehendes Formular zur Anzeige von Datensätzen. Master- und Detail-Block sind synchronisiert; wird im Master-Block ein Datensatz ausgewählt, werden im Detail-Block die dazugehörigen Detail-Datensätze angezeigt. Ein Beispiel: Ein Master-Detail-Formular enthält einen Master-Block, der mit einer Abteilungstabelle verbunden ist und einen Detail-Block, der mit einer DozentenTabelle verbunden ist; wird nun im Abteilungs-Block der Datensatz einer bestimmten Abteilung angezeigt, werden im Dozenten-Block die Datensätze aller zur Abteilung gehörenden Dozenten angezeigt.
Für jeden Objekttyp gibt es in Oracle Forms eine Reihe von Eigenschaften. Die Seiten dieses Buches reichen einfach nicht aus, um jede dieser Eigenschaften zu untersuchen. Stattdessen werden wir uns auf die wichtigsten Objekteigenschaften konzentrieren.
16.1 Ein Master-Detail-Formular erstellen An Tag 15, »Einführung in Oracle Forms«, haben Sie den Forms Builder dazu benutzt, ein
einfaches Formular zur Anzeige und Bearbeitung von Studenteninformationen zu erstellen. Jetzt werden Sie erfahren, wie Sie ein Master-Detail-Formular erstellen. Genauer gesagt, werden Sie ein Formular erstellen, mit dem die angebotenen Kurse geplant werden sollen. Im Master-Block sollen der jeweilige Kurs (Course) und im Detail-Block die detaillierten Angaben zu jeder Veranstaltung dieses Kurses (Class) angezeigt werden. Beginnen Sie, indem Sie den Forms Builder mit Start | Programs | Oracle Forms 6i | Form Builder aufrufen. Stellen Sie als nächstes über die Menüoption File | Connect die Verbindung zur OracleDatenbank mit dem Benutzernamen, Kennwort und dem Service-Namen des vorherigen Kapitels her. In der Standardeinstellung erstellt der Forms Builder ein Formular namens MODULE1.
Definieren des Master-Blocks Um den Master-Block Ihres Formulars zu erstellen, führen Sie die folgenden Schritte aus: 1. Sie beginnen, indem Sie den Menüeintrag Tools | Data Block Wizard wählen. Nach dem Begrüßungs-Fenster wählen Sie die Option Table or View als Datenquelle. 2. Klicken Sie auf die Browse-Schaltfläche rechts neben dem Feld Table Or View (siehe Abbildung 16.1). Ein weiteres Fenster mit einer Liste von Datenbank-Tabellen des aktuellen Benutzers öffnet sich. 3. Blättern Sie nach unten zur Tabelle Course, wählen Sie sie aus und klicken Sie auf OK (siehe Abbildung 16.2).
Abbildung 16.1: Anzeige der verfügbaren Datenbank-Tabellen 4. Standardmäßig erhält der Block den Namen der Tabelle. Bewegen Sie mit Hilfe der Schaltflächen alle Spalten aus der linken Auswahlliste Available Columns mit Ausnahme von Additional_Fees, die wir für dieses Beispiel nicht benötigen, in die rechte Auswahlliste Database Items. Aktivieren Sie das Kontrollkästchen Enforce Data Integrity (siehe Abbildung 16.3).
Abbildung 16.2: Auswahl der Tabelle Course als Basistabelle für den Block
Abbildung 16.3: Auswahl der Spalten aus der Tabelle Course, die im MasterBlock enthalten sein sollen
Abbildung 16.4: Festlegen von Layout-Optionen für den Master-Block 5. Rufen Sie jetzt den Layout Wizard auf. Akzeptieren Sie die vorgeschlagene Leinwand (Canvas). Transportieren Sie alle angebotenen Felder aus der linken Auswahlliste Available Items in die rechte Auswahlliste Displayed Items. 6. Verkürzen Sie die Anzeigebreite der Felder Title und Description auf 200 Punkte. Wählen Sie als Layout-Stil Form. Tragen Sie als Überschrift für den Rahmen Course ein, wie es in der Abbildung 16.4 zu sehen ist, und beenden Sie die Arbeit mit Finish. Wie in Abbildung 16.5 zu sehen ist, enthält der Object Navigator einen neuen Block Course. Somit haben Sie den Master-Block erstellt.
Abbildung 16.5: Der Object Navigator enthält den neuen Master-Block
Beachten Sie, dass Sie keine Eigenschaften eingestellt haben, um diesen Block als Master-Block festzulegen. Dieser Block wird zum Master-Block, indem Sie einen weiteren Block erzeugen, der Course als Master-Block referenziert.
Den Detail-Block definieren Um den Detail-Block zu erzeugen, führen Sie zunächst die gleichen Schritte aus, wie sie zur Erstellung des Master-Blocks nötig waren:
1. Sie beginnen, indem Sie den Menüeintrag Tools | Data Block Wizard betätigen. Achten Sie darauf, dass beim Aufruf des Data Block Wizard weder der existierende Block noch der Rahmen markiert sind, da Sie sonst nur die Eigenschaften des Blockes Course ändern können. Wählen Sie die Option Table or View als Datenquelle. 2. Klicken Sie wieder auf die Browse-Schaltfläche rechts neben dem Feld Table Or View, um alle verfügbaren Tabellen aufzulisten. 3. Wählen Sie die Tabelle Class und klicken Sie auf OK. Bewegen Sie mit Hilfe der Schaltflächen alle Spalten mit Ausnahme von Semester und School_Year, die wir für dieses Beispiel nicht benötigen, aus der linken Auswahlliste Available Columns in die rechte Auswahlliste Database Items. Aktivieren Sie das Kontrollkästchen Enforce Data Integrity. 4. Nachdem Sie die Auswahl bestätigt haben, erscheint jetzt jedoch ein neues Fenster (siehe Abbildung 16.6), in dem Sie eine Beziehung zwischen einem Master- und einem Detail-Block definieren können.
Abbildung 16.6: Definition einer Beziehung
Definieren der Beziehung Mit den folgenden Schritten legen Sie die Beziehung zwischen dem Master- und dem DetailBlock fest: 1. Betätigen Sie die Schaltfläche Create Relationship. Da das Kontrollkästchen Auto-join Data Blocks aktiviert ist, schlägt Ihnen der Data Block Wizard eine Beziehung vor, die auf dem in der Datenbank definierten Fremdschlüssel zwischen den beiden Tabellen
basiert. Falls Sie das Kontrollkästchen deaktivieren, können Sie selbst eine Verknüpfung (Join) für die Beziehung festlegen. 2. Im jetzt erscheinenden Fenster ist der Block Course standardmäßig ausgewählt (siehe Abbildung 16.7). Klicken Sie auf OK.
Abbildung 16.7: Auswahl des Master-Blocks 3. Unter Verwendung des für die Tabelle Class bestehenden Fremdschlüssels wird der Forms Builder die Verknüpfungsbedingung zwischen dem Master- und dem DetailBlock erzeugen, wie es in Abbildung 16.8 gezeigt wird. Klicken Sie auf OK, um den Detail-Block Class zu erstellen. 4. Abschließend müssen wir noch das Layout für den Detail-Block fertig stellen. Platzieren Sie die Felder auf der bereits existierender Leinwand (Canvas), so dass die Felder des Master- und des Detail-Blocks auf derselben Leinwand angeordnet sind. Transportieren Sie alle angebotenen Felder mit Ausnahme von Course_ID, die bereit im Master-Block Course angezeigt wird, aus der linken Auswahlliste Available Items in die rechte Auswahlliste Displayed Items. 5. Wählen Sie als Layout-Stil eine tabellarische Anzeige (Tabular). Legen Sie als Überschrift für den Rahmen Classes fest. Es sollen 10 Datensätze angezeigt werden (Records Displayed), wobei zwischen den Datensätzen ein Abstand von einem Punkt bestehen soll (Distance Between Records). Aktivieren Sie das Kontrollkästchen Display Scrollbar, um einen Rollbalken im Detail-Block zu erhalten.
Abbildung 16.8: Anzeige der Verknüpfungsbedingung zwischen Master- und Detail-Block
Abbildung 16.9: Ansicht des Layouts im Layout Editor Schauen wir uns das erzeugte Layout näher an. Wählen Sie den Menüeintrag Tools | Layout Editor. Abbildung 16.9 zeigt, was Sie im Layout Editor sehen sollten. Wie Sie feststellen, müssen Sie an dem Formular noch eine Reihe von Änderungen vornehmen, bevor Sie es verwenden können. Im nächsten Abschnitt erfahren Sie mehr über den Einsatz des Layout Editor zur Bearbeitung des Aussehens und der Eigenschaften von Feldern.
Falls die Felder im Layout Editor den angezeigten schwarzen Rahmen überdecken, können Sie den Rahmen mit der Maus solange vergrößern, bis alle Felder im Rahmen Platz finden. Dieser Rahmen bestimmt die Größe des sichtbaren Leinwandausschnittes (Canvas View) und in diesem Fall gleichzeitig
die Größe des Fensters in der Laufzeit-Umgebung. Wir werden allerdings im Folgenden die Breite der Felder verringern, so dass der vorgegebene Leinwandausschnitt ausreichen sollte.
16.2 Mit dem Layout Editor arbeiten Beim Aufruf des Layout Editor können Sie feststellen, dass sich die Menüpunkte verändern. Im Layout Editor können Sie unter Format und Arrange eine Reihe von Menüpunkten aufrufen. Lassen Sie uns zum Anfang die Schriftart für alle Textfelder ändern.
Text formatieren Um alle Elemente in der Ansicht auszuwählen, drücken Sie die Tasten (Strg)+(A). Um für alle ausgewählten Elemente die Schriftart zu ändern, wählen Sie Format | Font, wonach das Fenster Font erscheint. Wählen Sie MS Sans Serif, Regular als Schriftattribut und 10 Punkt als Schriftgröße aus (siehe Abbildung 16.10). Klicken Sie auf OK, damit die Änderungen wirksam werden.
Um mehrere Elemente auszuwählen, können Sie mit der Maus um die auszuwählenden Elemente ein Rechteck ziehen, indem Sie die linke Maustaste gedrückt halten und die Maus solange ziehen, bis alle Elemente vom Rechteck umgeben sind. Alternativ dazu können Sie auch mehrere Elemente anklicken, während Sie die Umschalt-Taste gedrückt halten.
Die Größe von Objekten einstellen
Abbildung 16.10: Änderung der Schriftart ausgewählter Elemente Im Layout erkennen Sie, dass die Größe einiger Felder geändert werden könnte. So können z.B. die Felder Schedule_ID und Class_Room schmaler eingestellt werden. Um die Breite eines Feldes zu verändern, wählen Sie es mit der Maus aus. Erfassen Sie einen Punkt auf der rechten Seite des Feldes, und ziehen Sie ihn bei gedrückter linker Maustaste nach links, bis das Feld die gewünschte Breite hat. Verringern Sie die Breite der Felder Schedule_ID und Class_Room (siehe Abbildung 16.11). Stören Sie sich nicht daran, dass die Elemente nicht mehr zueinander ausgerichtet sind; in Kürze werden Sie dies berichtigen.
Abbildung 16.11: Verringerung der Breite von Feldern im Detail-Block Sie können nun Breite und Position des Rahmens (Frame) des Master-Blocks mit der Maus verändern, bis die Felder und Bezeichner vollständig vom Rahmen umschlossen werden (siehe Abbildung 16.12).
Abbildung 16.12: Modifizieren der Rahmen (Frames) des Master-Blocks
Text ändern In beiden Blöcken müssen noch einige Feldbezeichner geändert werden: in einigen Fällen sollten passendere Bezeichner gewählt werden, in anderen sollten die vorhandenen Bezeichner formatiert werden. Um z.B. Class Building einfach in Building zu ändern, markieren Sie den Bezeichner mit der Maus und klicken dann erneut mit der linken Maustaste auf den Text. Der Mauszeiger verwandelt sich in ein Fadenkreuz. Für den Bezeichner wird eine Umrandung und eine Einfügemarke angezeigt (siehe Abbildung 16.13). Entfernen Sie das Wort Class aus den Bezeichnern Class Building und Class Room. Ist ein Bezeichner breiter als die dazugehörige Spalte, können Sie die Bezeichner in mehrere Zeilen setzen. Dadurch können Sie die Spalten schmaler einstellen. So können Sie z.B. Instructor ID und Schedule ID so ändern, dass Instructor bzw. Schedule und ID in zwei Zeilen stehen. Dazu wählen Sie den Bezeichner aus und fügen mit der Eingabetaste einen
Zeilenumbruch ein. Um einen Bezeichner zu zentrieren, wählen Sie den Menüeintrag Format | Justify | Center.
Abbildung 16.13: Änderung von Bezeichnern
Größe, Abstand und Ausrichtung von Objekten einstellen Als Sie im Block Class Felder verkleinert haben, haben diese möglicherweise ihre Ausrichtung verloren. Ebenso kann es vorkommen, dass Sie einige der Felder unbeabsichtigt in ihrer Größe verändern. Um eine Gruppe von Feldern auf die gleiche Größe einzustellen, wählen Sie alle aus, indem Sie entweder mit der Maus ein Rechteck um die Objekte ziehen oder jedes davon bei gedrückt gehaltener Umschalttaste anklicken. Wählen Sie z.B. die folgenden sechs Objekte des Blocks Class aus: Class_ID, Schedule_ID,
Class_Building, Class_Room, Instructor_ID und den Rollbalken. Um diesen Objekten die gleiche vertikale Größe zuzuweisen, wählen Sie den Menüeintrag Arrange | Size Objects. Ein Fenster mit dem Namen Size Objects wird geöffnet. Da Sie für keines der Objekte die Breite ändern wollen, lassen Sie die Einstellung No Change für Width unverändert. Für Height wählen Sie das Kontrollkästchen Largest, damit alle Objekte die Höhe des größten Objektes erhalten (siehe Abbildung 16.14). Klicken Sie auf OK, damit die Änderungen wirksam werden.
Abbildung 16.14: Festlegen der Größe von Objekten Jetzt werden Sie die Objekte ausrichten. Stellen Sie sicher, dass die Objekte noch markiert sind. Wählen Sie Arrange | Align Objects, und ein Fenster namens Align Objects erscheint. Wählen Sie Each Other in der Gruppe Align To und Distribute in der Gruppe Horizontally, wodurch der horizontale Raum zwischen den Objekten gleichmäßig verteilt wird. Wählen Sie Align Top in der Gruppe Vertically und klicken Sie auf OK (siehe Abbildung 16.15).
Wenn die Objekte in horizontaler oder vertikaler Richtung nicht gleichmäßig verteilt werden, haben Sie wahrscheinlich ein anderes Objekt versehentlich ausgewählt, das diese Ausrichtung beeinflusst.
Abbildung 16.15: Ausrichten einer Gruppe von Objekten Lassen Sie uns jetzt die Spaltenbezeichner bearbeiten. Da die Felder des Blocks Class horizontal neu angeordnet wurden, kann es sein, dass Sie die Spaltenbezeichner umstellen müssen. 1. 2. 3. 4.
Stellen Sie sicher, dass Sie nur die Spaltenbezeichner ausgewählt haben. Wählen Sie den Menüeintrag Arrange | Align Objects. Im Fenster Align Objects wählen Sie Each Other für die Gruppe Align To. Wählen Sie None für die Gruppe Horizontally; der Raum zwischen den Spaltenbezeichnern soll nicht gleichmäßig verteilt werden, da sonst diese nicht richtig über den Spalten stehen.
Abbildung 16.16: Layout des Master-Detail-Formulars 5. Wählen Sie Align Top für die Gruppe Vertically und klicken Sie auf OK. 6. Abschließend können Sie im Block Course noch die Bezeichner der Felder linksbündig anordnen. Markieren Sie die Bezeichner, rufen Sie erneut Align Objects auf und wählen Sie die Optionen: ● ● ●
Each Other für die Gruppe Align To Align Left für die Gruppe Horizontally None für die Gruppe Vertically
Abbildung 16.16 zeigt die Gestaltung des Master-Detail-Formulars so, wie es auf Ihrem Bildschirm dargestellt werden sollte.
16.3 Das Master-Detail-Formular ausführen
Wahrscheinlich sind Sie gespannt, wie das Ergebnis Ihrer Arbeit aussehen wird. Die beste Methode, herauszufinden, ob ein Formularentwurf korrekt ist, besteht darin, ihn zu testen. Dazu wählen Sie entweder den Menüeintrag Program | Run Form | Client/Server oder klicken in der linken Werkzeugleiste des Object Navigator auf die grüne Ampel. Zuerst wird vom Forms Compiler eine ausführbare FMX-Datei erzeugt. Liegen keine Fehler vor, ruft der Forms Builder zur Ausführung der FMX-Datei die Forms Runtime- Komponente auf. Jetzt sollte ein Fenster namens Oracle Forms Runtime erscheinen. In diesem Fenster sollte ein weiteres Unterfenster mit dem Titel WINDOW1 geöffnet sein. Die Abbildung 16.17 zeigt, wie es auf Ihrem Bildschirm aussehen sollte.
Abbildung 16.17: Fenster bei Aufruf des Master-Detail-Formulars Um eine Abfrage durchzuführen, klicken Sie auf das Symbol Execute Query in der Werkzeugleiste (Smartbar). Wie Sie sehen, stehen die Kurse in keiner bestimmten Reihenfolge. Genausowenig stehen die zugehörigen Veranstaltungen (Classes) in einer bestimmten Reihenfolge. Im nächsten Abschnitt werden Sie die Eigenschaft Order by für den Master- und den Detail-Block ändern. Dazu verlassen Sie Forms Runtime, indem Sie Action | Exit wählen.
16.4 Verbessern des Formulars Im Forms Builder wechseln Sie in das Fenster Object Navigator, indem Sie den Menüeintrag Window | Object Navigator wählen. Markieren Sie den Block Course und wählen Sie nach einem Klick mit der rechten Maustaste die Property Palette aus dem Kontextmenü aus. Blättern Sie bis zur Eigenschaft Order By oder benutzen Sie die Suchfunktion im oberen Teil des Eigenschafts-Fensters, um die gewünschte Eigenschaft zu finden. Geben Sie Course_ID ein (siehe Abbildung 16.18) und klicken Sie außerhalb des Feldes, um die Eigenschaft zu übernehmen.
Abbildung 16.18: Setzen der Eigenschaft Order by für den Block Course Markieren Sie jetzt den Block Class und öffnen Sie die Property Palette für den Block. Blättern Sie nach unten zur Eigenschaft Order by. Geben Sie Class_ID ein und klicken Sie außerhalb des Feldes, um die Eigenschaft zu übernehmen
An dem Formular sollten noch weitere Änderungen vorgenommen werden. Erst einmal sind einige Felder zu breit - diese sind Schedule_ID und Class_Room. Außerdem haben die Anwender den Wunsch geäußert, dass die Instructor_ID noch vor der Raumangaben (Class Building, Class Room) erscheinen soll. Nehmen wir diese Änderungen im Layout Editor vor und lassen das Formular erneut laufen.
Die Aktivierungsreihenfolge von Feldern steuern Führen Sie eine Abfrage im Block Course durch und blättern Sie bis zum Kurs über moderne Philosophie (Course_ID 10003). Navigieren Sie anschließend in den Detail-Block Class. Sie können dies tun: ● ● ●
indem Sie den Mauszeiger direkt in einem Feld des Detail-Blocks positionieren im Menü den Eintrag Block | Next benutzen das Symbol Next Block in der Werkzeugleiste (Smartbar) betätigen.
Innerhalb des Blockes Class können Sie von einem Feld mit der Tabulatortaste zum nächsten Element gelangen. Zum vorherigen Element gelangen Sie mit Umschalt+ Tabulator. Beachten Sie, dass Sie vom Feld Schedule_ID mit der Tabulatortaste in das Feld Class_Building gelangen. Als Sie das Feld Instructor_ID vor dem Feld Class_Building positioniert haben, hat sich die Reihenfolge der Navigation mit der Tabulatortaste nicht geändert. Der bequemste Weg, diese Reihenfolge zu ändern, führt über den Object Navigator. Abbildung 16.19 zeigt die vorhandene Navigationsfolge der Felder im Block Class.
Abbildung 16.19: Navigationsfolge der Felder im Block Class Damit das Feld Instructor_ID zum zweiten Feld in der Navigationsfolge wird, markieren Sie es mit der Maus, ziehen es mit gedrückter linker Maustaste nach oben und lassen Sie die linke Maustaste zwischen Class_Building und Schedule_ID los.
Erstellen einer Werteliste Was könnte noch an diesem Master-Detail-Formular verbessert werden? Eine Möglichkeit wäre die Verwendung einer Werteliste (LOV) für Felder mit einer diskreten Anzahl von Werten. Department_ID sollte z.B. auf die Werte für die existierenden Abteilungen beschränkt sein, wie sie in der Tabelle Department zu finden sind. Um unter Verwendung des Object Navigator eine Werteliste (LOV) für Department_ID anzulegen, wählen Sie den
Knoten LOVs und klicken auf das + in der vertikalen Werkzeugleiste. Sie können entweder den LOV Wizard benutzen oder die Werteliste manuell anlegen. Im ersten Schritt müssen Sie mit dem LOV Wizard eine neue Datensatzgruppe (Record Group) erzeugen. Dazu stehen Ihnen folgende Alternativen zur Verfügung: ● ●
●
Sie können die Abfrage manuell eingeben und deren Syntax überprüfen. Sie können den Query Builder aufrufen, um grafisch eine Abfrage zusammenzustellen. Der Query Builder ist ein eigenständiges Programm, das in Oracle Forms und Oracle Reports integriert ist. Sie können eine bereits als Text in einer Datei existierende Abfrage importieren.
Geben Sie, wie in der Abbildung 16.20 dargestellt, die Abfrage manuell in das Textfeld ein und validieren Sie die Syntax. Der Forms Builder erzeugt aus dieser Abfrage eine neue Datensatzgruppe (Record Group). Klicken Sie auf OK, um die LOV zu erstellen.
Der in diesem Listing und der Abbildung aufgeführte Code kann in der .fmb-Datei auf der CD-ROM eingesehen werden. Sie können das gesamte Formular öffnen und alle Objekte und Eigenschaften betrachten. Listing 16.1: Abfrage zur Erstellung einer neuen Werteliste für die Department_ID select Department_ID, Department_Name from Department
Abbildung 16.20: Eingabe der Abfrage für die Datensatzgruppe
Abbildung 16.21: Auswahl der Spalten aus der Datensatzgruppe für die LOV
Wählen Sie im nächsten Schritt die Spalten Department_ID und Department_Name der Datensatzgruppe (Record Group) für die Verwendung in der Werteliste (LOV) aus (siehe Abbildung 16.21) und gehen mit Next zum nächsten Fenster. Hier können Sie die Bezeichner und die Breite der Spalten in der Werteliste definieren. Allerdings können Sie mit Automatically Size Columns die Breite auch zur Laufzeit in Abhängigkeit von den angezeigten Werten bestimmen lassen. Eine wichtige Festlegung betrifft die Rückgabe eines oder mehrerer Werte (Return Value) aus der Werteliste. In unserem Fall soll die aus der Werteliste ausgewählte Department_ID in das Feld Department_ID im Block Course übernommen werden. Sie können das Feld über die Schaltfläche Look up Return Item aus einer Werteliste auswählen (siehe Abbildung 16.22).
Abbildung 16.22: Festlegung des Rückgabewertes der LOV
Abbildung 16.23: Zuweisung des Rückgabewertes zum Feld Department_ID Geben Sie nun noch einen Titel für die Werteliste ein (z.B. Department LOV) und gehen Sie zum nächsten Fenster. Bestätigen Sie die vorgeschlagenen Werte für das Verhalten der Werteliste und betätigen Sie Next. An dieser Stelle sollten wir noch einmal kurz die bisherigen Schritte zusammenfassen: ●
●
Wir haben eine Datensatzgruppe (Record Group) mit einer Abfrage definiert, die zwei Spalten aus der Tabelle Department selektiert. Wir haben eine Werteliste (LOV) auf der Grundlage dieser Abfrage definiert, die diese zwei Spalten anzeigt und für die in der Werteliste ausgewählte Zeile die Department_ID an das Feld Department_ID im Block Course zurückgibt.
Nun fehlt nur noch eine Verbindung zwischen dem Rückgabewert (Return Value) der Werteliste und dem Feld im Formular. Übernehmen Sie daher das in der Auswahlliste Return Items angebotene Feld Course.Department_ID in die Auswahlliste Assigned Items (siehe Abbildung 16.23) und verlassen Sie den LOV Wizard.
Abbildung 16.24: Umbenennen der Werteliste (LOV) und der Datensatzgruppe (Record Group) Wenn Sie mit dem Object Navigator die Knoten der Objekte betrachten, fallen Ihnen zwei neue Objekte auf: eine LOV namens LOVx und eine Datensatzgruppe (Record Group) namens LOVx. Diese Namen wurden durch den LOV Wizard automatisch vergeben. Um den Namen der Datensatzgruppe zu ändern, markieren Sie sie mit der Maus und klicken Sie noch einmal mit der linken Maustaste darauf. Der Name erscheint in Blau und der Zeiger wird zur Einfügemarke. Nennen Sie die Datensatzgruppe DEPARTMENT_ID_RG, und die LOV DEPARTMENT_ID_LOV, wie es in Abbildung 16.24 zu sehen ist. Obwohl wir mit Hilfe des LOV Wizard bereits alle notwendigen Schritte zur Einrichtung einer Werteliste getan haben, sollten wir uns noch einige wichtige Eigenschaften einer Werteliste anschauen. Dies ist wichtig, wenn wir nachträglich noch bestimmte Eigenschaften ändern wollen. Markieren Sie die Werteliste DEPARTMENT_ID_LOV und rufen mit der rechten Maustaste aus dem Kontextmenü Property Palette auf. Blättern Sie nach unten, bis Sie die Eigenschaft
Column Mapping Properties erreicht haben. Klicken Sie im Eigenschaftsfenster auf die Schaltfläche More (siehe Abbildung 16.25).
Abbildung 16.25: Änderung der Eigenschaften einer Werteliste (LOV) Das Fenster LOV Column Mapping erscheint mit vier Feldern. Das Feld Column Names bezeichnet die Spalten, die durch die Abfrage der Datensatzgruppe (Record Group) zurückgegeben werden. Das Feld Return Item gibt das Feld an, dem der aktuelle Wert im Feld Column Names zurückgegeben werden soll. Das Feld Display Width zeigt die in der Werteliste angezeigte Breite des Feldes an. Schließlich enthält das Feld Column Title noch den Bezeichner der Spalte, so wie er beim Aufruf der Werteliste angezeigt wird.
In Abbildung 16.26 sehen Sie die aktuellen Einstellungen, wie sie vom LOV Wizard entsprechend Ihrer Eingaben gesetzt wurden.
Abbildung 16.26: Aktuelle Eigenschaften LOV Column Mapping einer Werteliste (LOV) Es gibt noch einige durchzuführende Schritte, ehe die Werteliste (LOV) tatsächlich in diesem Formular benutzt wird: 1. Verwenden Sie den Object Navigator, markieren Sie Department_ID und wählen Sie mit der rechten Maustaste Property Palette. 2. Blättern Sie nach unten, bis Sie eine Gruppe von Eigenschaften sehen, die mit List of Values (LOV) betitelt ist. Wir können sehen, dass dem Feld bereits die Werteliste DEPARTMENT_ID_LOV zugeordnet ist. 3. Ändern Sie die Eigenschaft Validate from List in Yes; damit wird verhindert, dass ein Wert in das Feld eingegeben wird, der nicht in der Werteliste (LOV) vorhanden ist. Lassen Sie uns das geänderte Formular ausprobieren. 1. Klicken Sie auf die grüne Ampel in der vertikalen Werkzeugleiste oder wählen Sie den Menüeintrag Program | Run Form | Client/Server. 2. Versuchen Sie einen neuen Datensatz einzugeben, indem Sie im ersten Feld für die Course_ID 99999 eingeben und mit der Tabulatortaste zum nächsten Feld Department_ID navigieren. Geben Sie hier die Nummer 9999 ein und betätigen Sie die Tabulatortaste. Im Formular erscheint im Ergebnis einer Validierung Ihrer Eingabe die Werteliste (LOV) sowie die folgende Meldung in der Statuszeile: FRM:40212: Invalid
value for field DEPARTMENT_ID, da es keine Abteilung mit der Nummer 9999 in der Tabelle Department gibt (siehe Abbildung 16.27). Brechen Sie mit Cancel die Auswahl ab. 3. Der Eingabezeiger befindet sich noch immer im Feld Department_ID, das eine fehlerhafte Eingabe enthält. In der Statuszeile am unteren Bildschirmrand sehen Sie die Angabe , die Ihnen anzeigt, dass es für die Department_ID eine Werteliste (LOV) gibt.
Abbildung 16.27: Die Werteliste erzwingt die Eingabe korrekter Werte für die Department_ID
Abbildung 16.28: Anzeigen der Werteliste (LOV) für das Feld Department_ID 4. Um die LOV anzuzeigen, betätigen Sie den Menüeintrag Edit | Display List oder drücken Sie (F9). An einer definierten Position im Fenster wird die LOV angezeigt (siehe Abbildung 16.28). Im Feld Find der Werteliste (LOV) können Sie in der Werteliste nach den Nummern bestimmter Abteilungen suchen.
Selbst wenn Sie zur Gültigkeitsüberprüfung keine Werteliste (LOV) verwenden, setzt die Oracle-Datenbank Integritätsregeln durch. Eine Werteliste (LOV) hat aber den Vorteil, dass sie eine Liste zulässiger Werte zur Verfügung stellt und
der Benutzer sich die möglichen Werte für ein Feld nicht merken muss. Bevor Sie diese Lektion abschließen, speichern Sie das Formular unter dem Namen Course_Class.fmb, indem Sie den Menüeintrag File | Save wählen.
16.5 Zusammenfassung In dieser Lektion haben Sie folgendes gelernt: ●
● ● ●
●
●
●
Mit dem Forms Builder lassen sich auf einfache Weise Master-Detail-Formulare erstellen. Durch die Erzeugung eines Detail-Blocks wird implizit der Master-Block definiert. Mit dem Layout Editor ändern Sie das Aussehen Ihrer Formulare. Die Eigenschaften mehrerer Felder, z.B. deren Schriftart, lassen sich mit einem Schritt ändern, wenn Sie diese zuvor auswählen. Mit dem Layout Editor können Sie den Formularen grafische Elemente hinzufügen, indem Sie Werkzeuge aus der vertikalen Werkzeugleiste auswählen. Bei der Definition einer Werteliste (LOV) können Sie automatisch eine Datensatzgruppe (Record Group) für die LOV erzeugen, indem Sie für die Datensatzgruppe eine Abfrage festlegen. Um eine Werteliste (LOV) einzusetzen, muss in der Feldeigenschaft List of Values eine existierende Werteliste eingetragen sein. Wollen Sie, dass die Werteliste (LOV) Benutzereingaben auf deren Gültigkeit überprüft, muss außerdem die Eigenschaft Validate from List auf den Wert Yes gesetzt sein.
16.6 Wie geht es weiter? Am Tag 17, »Anwendungsentwicklung mit Oracle Forms«, lernen Sie, ein Menü zu erstellen, einem Block Felder mit abgeleiteten Informationen hinzuzufügen, Trigger auf Formular-, Block- und Feldebene zu erstellen und ein Formular aus einem anderen Formular aufzurufen.
16.7 Fragen und Antworten Frage: Kann ein Master-Detail-Formular aus mehr als einem Detail-Block bestehen? Antwort: Ja. Sie erstellen einfach jeden gewünschten Detail-Block. Danach markieren Sie den Block und rufen den Data Block Wizard auf. Im Register Master-Detail können Sie die MasterDetail-Verknüpfung festlegen. Frage: Können sich mehrere Wertelisten (LOV) auf dieselbe Datensatzgruppe (Record Group) beziehen?
Antwort: Ja. Es gibt keine Einschränkung, die für jede Werteliste (LOV) eine eigene Datensatzgruppe (Record Group) verlangt. Frage: Kann ein Formular mehrere Fenster und Leinwände (Canvases) haben? Antwort: Ja. Ein einzelnes Formular kann aus mehreren Fenstern und Leinwänden (Canvases) bestehen. Wie Sie jedoch in der nächsten Lektion sehen werden, ist es auch möglich, eine Anwendung mit mehreren Formularen zu erstellen, in der jedes Formular nur ein Fenster und nur eine Leinwand (Canvas) hat.
16.8 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Ein Master-Block kann nicht auf einer Datensicht (View) basieren, es muss eine Tabelle sein. 2. Richtig oder falsch? Man kann das Erscheinungsbild eines Feldes mit dem Layout Editor oder durch Ändern der Feld-Eigenschaften verändern. 3. Wie kann die Reihenfolge der Navigation in einer Gruppe von Feldern geändert werden?
16.9 Übungen 1. Erstellen Sie ein Master-Detail-Formular, um Schedule_Type und Schedule_Type_ Details einzugeben. 2. Erstellen Sie im Formular Course_Class eine Werteliste (LOV) für Schedule_ID, die die Benutzereingabe überprüft.
Einführung in Oracle Forms Bisher haben Sie schon sehr viel über den Einsatz von SQL zur Erstellung einer OracleDatenbank, Tabellenabfragen und Veränderungen des Inhalts von Tabellen gelernt. Außerdem haben Sie gelernt, PL/SQL zur Entwicklung von Anwendungslogik in Form von gespeicherten Prozeduren, Funktionen und Paketen einzusetzen - und wie DatenbankTrigger für verschiedene Zwecke erstellt werden. Jetzt ist es an der Zeit, sich mit der Entwicklung von Oracle-Anwendungen zu beschäftigen. In dieser Lektion und an den Tagen 16 bis 19 lernen Sie, welche Rolle die Werkzeuge der Internet Developer Suite (iDS) - Oracle Forms, Oracle Reports, Oracle Graphics und Procedure Builder - bei der Entwicklung einer Client-Server-Anwendung spielen.
15.1 Die Werkzeuge der Internet Developer Suite (iDS) Die Internet Developer Suite (iDS) ist eine Suite integrierter Werkzeuge für den Entwurf und die Erstellung von Anwendungen. Am 5. Tag haben Sie bereits eine Komponente der iDS, den Oracle Designer kennen gelernt. An diesem und den folgenden Tagen werden Sie weitere Werkzeuge aus der iDS kennen lernen: ●
●
●
●
Oracle Forms: Einzusetzen für den Entwurf und die Erstellung masken-basierter Anwendungen einschließlich der Menüs. Diese Masken umfassen Dateneingabe-, Abfrage- und Browser-Formulare. Oracle Reports: Einzusetzen für den Entwurf einer Vielzahl von einfachen bis hin zu komplexen Berichten. Ein Bericht kann vor dem Ausdruck am Bildschirm betrachtet oder direkt zum Drucker gesendet werden. Oracle Graphics: Erlaubt Ihnen, Abfragen zu definieren, die bei ihrer Ausführung Geschäftsgrafiken erzeugen. Procedure Builder: Einzusetzen für den Entwurf und die Verwaltung von PL/SQLProgrammlogik (Prozeduren, Funktionen, Pakete und Trigger) sowohl für den Datenbank-Server als auch für die Anwendungen.
Mit diesen Werkzeugen können grafisch-orientierte Anwendungen für Windows-, Mac- und UNIX-Motif-Umgebungen entwickelt werden. Neben der Möglichkeit, diese Anwendungen auch im Web verfügbar zu machen, können Sie auch zeichenorientierte Versionen Ihrer Anwendungen erstellen.
Die folgende Lektion befasst sich mit Oracle Forms.
15.2 Erstellen eines einfachen Formulars mit dem Forms Builder Lassen Sie uns direkt in Oracle Forms eintauchen. Dieser Abschnitt führt Sie über die Erstellung eines Formulars bis hin zur Anzeige und Veränderung von Informationen über Studenten. ●
●
Um den Forms Builder aus dem Menü aufzurufen, wählen Sie Start | Programs | Oracle Forms 6i | Form Builder. Verlassen Sie das Begrüßungsfenster mit Cancel. Der Forms Builder zeigt nach dem Start den Object Navigator an (siehe Abbildung 15.1).
Ehe Sie fortfahren, sollten Sie eine Verbindung zur Oracle-Datenbank herstellen.
Abbildung 15.1: Der Forms Builder zeigt zunächst den Object Navigator an
Eine Verbindung zu einer Oracle-Datenbank aufbauen
Um den Forms Builder mit einer Oracle-Datenbank zu verbinden, wählen Sie den Menüeintrag File | Connect. Es wird ein Dialogfeld angezeigt, in dem Sie nach dem Benutzernamen, Kennwort und der zu benutzenden Oracle-Datenbank für die Verbindung gefragt werden. Tragen Sie, wie in Abbildung 15.2 gezeigt, folgende Angaben ein: ● ● ●
Benutzername flugle Kennwort flugle Datenbank Tragen Sie den Service-Namen ein, den Sie nach der Installation der Oracle-Datenbank mit Hilfe des Net Managers eingerichtet haben.
Klicken Sie die Schaltfläche Connect an. Wenn keine Fehlermeldungen erscheinen, können Sie davon ausgehen, dass Sie sich erfolgreich mit der Datenbank verbunden haben.
Abbildung 15.2: Aufbau einer Verbindung vom Forms Builder zu einer OracleDatenbank
Erstellen eines neuen Blocks Standardmäßig öffnet der Forms Builder beim ersten Aufruf ein neues Formular mit dem Namen MODULE1. Es gibt drei Elemente, die in fast allen Formularen vorkommen: ● ● ●
einen oder mehrere Blöcke eine Leinwand (Canvas) ein Fenster
Fahren Sie fort, Ihr einfaches Formular durch Einrichten eines Blocks zu erstellen. Oracle Forms verwendet Blöcke, um die Felder in einem Formular den Spalten einer Oracle- Tabelle oder -Datensicht (View) zuzuordnen. Wählen Sie den Menüeintrag Tools | Data Block Wizard. Der Data Block Wizard erscheint mit einem Begrüßungs- Fenster und führt Sie schrittweise durch die Erstellung eines Blockes. Nach dem Begrüßungs-Fenster müssen Sie zunächst die Art der Datenquelle auswählen: ●
Tabelle oder Datensicht (View): Die Daten des Blocks werden in einer Tabelle oder
●
Datensicht (View) in der Datenbank gespeichert. Stored Procedure: Die Daten des Blocks werden durch eine gespeicherte Prozedur verarbeitet, d.h. abgefragt, eingefügt, aktualisiert, gelöscht und gesperrt.
Wählen Sie die Option Table or View (siehe Abbildung 15.3).
Abbildung 15.3: Auswahl der Art der Datenquelle Um die Tabelle Student als Basistabelle des Blocks auszuwählen, betätigen Sie die Schaltfläche Browse. Der Forms Builder zeigt ein weiteres Fenster an, das fünf Kontrollkästchen enthält, um die Ihnen zur Verfügung stehende Auswahl an Datenbankobjekten festzulegen. Standardmäßig sind zwei Kontrollkästchen ausgewählt Current User und Tables. Im unteren Teil des Fensters wird eine Auswahlliste der Objekte angezeigt, die in ihrem Inhalt den aktivierten Kontrollkästchen entspricht. Wenn Sie die Standardeinstellung beibehalten, finden Sie in dieser Liste nur die Tabellen, die dem Benutzer FLUGLE gehören. Wählen Sie aus der Liste die Tabelle Student aus und klicken Sie auf OK (siehe Abbildung 15.4).
Abbildung 15.4: Der Forms Builder bietet mehrere Auswahlmöglichkeiten für die anzuzeigenden Datenbankobjekte Damit haben wir die Tabelle Student als Basistabelle des Blocks festgelegt. Im nächsten Schritt müssen wir eine Auswahl der Spalten treffen, die als Felder im Block zur Verfügung stehen sollen. Da wir alle Spalten im Formular bearbeiten wollen, reicht es aus, wenn Sie die Schaltfläche mit dem nach rechts zeigenden Doppelpfeil betätigen. Aus der linken Auswahlliste Available Columns werden alle angebotenen Spalten in die rechte Auswahlliste Database Items transportiert. Im Fenster finden Sie noch ein Kontrollkästchen Enforce Data Integrity vor. Durch seine Aktivierung werden innerhalb des Formulars Trigger erzeugt (siehe Kapitel 17), um die für diese Tabelle in der Datenbank geltenden Integritätsregeln auch in der Anwendung zu erzwingen. Wählen Sie das Kontrollkästchen Enforce Data Integrity aus und klicken Sie auf Next (siehe Abbildung 15.5).
Abbildung 15.5: Auswahl der Felder der Blocks Der Data Block Wizard gratuliert Ihnen an dieser Stelle zu Recht, da Sie bereits die Struktur Ihres ersten Blockes fertiggestellt haben. Was allerdings noch fehlt, ist das Layout des Blockes, weshalb Sie direkt den Layout Wizard aufrufen sollten (siehe Abbildung 15.6).
Abbildung 15.6: Aufruf des Layout Wizard Nach der obligatorischen Begrüßung durch den Layout Wizard müssen Sie die Leinwand (Canvas) auswählen, auf dem die Felder platziert werden sollen. Stellen Sie sich unter einem Canvas am besten eine Leinwand vor, die in einem Bilderrahmen (Fenster) aufgespannt ist und auf der die sichtbaren Felder angeordnet werden. Da wir mit einem neuen Forms- Modul begonnen haben, bestätigen Sie die Standardeinstellungen, damit eine neue Leinwand (Canvas) vom Typ Content Canvas angelegt wird (siehe Abbildung 15.7).
Abbildung 15.7: Auswahl der Leinwand (Canvas) zur Platzierung der Felder Da nicht alle ausgewählten Felder auch tatsächlich sichtbar sein sollen, müssen Sie im nächsten Fenster eine Auswahl der anzuzeigenden Felder treffen. Da wir alle Felder im Formular anzeigen wollen, reicht es aus, wenn Sie die Schaltfläche mit dem nach rechts zeigenden Doppelpfeil betätigen. Aus der linken Auswahlliste Available Items werden alle angebotenen Felder in die rechte Auswahlliste Displayed Items transportiert. Jedes ausgewählte Feld ist standardmäßig ein Textfeld (siehe Abbildung 15.8).
Abbildung 15.8: Auswahl der sichtbaren Felder des Blocks Im nächsten Fenster können Sie die Feldbezeichner sowie Breite und Höhe der Felder anpassen. Der Layout Wizard schlägt Ihnen einen Bezeichner für jedes Feld vor, der aus der Spaltenbezeichnung in der Datenbank abgeleitet ist. ●
●
ein Unterstrich im Namen einer Spalte wird für den Feldbezeichner in ein Leerzeichen umgewandelt der Anfangsbuchstabe jedes Wortes wird im Feldbezeichner großgeschrieben
Auch für die Breite und Höhe der Felder im Layout finden Sie einen Vorschlag, der aus der Breite der Tabellenspalte in der Datenbank abgeleitet ist. Die Maßeinheit der Größenangaben kann für das Forms-Modul voreingestellt werden und ist standardmäßig Punkte (Points). Ändern Sie die Breite und Höhe des Feldes Email entsprechend den Angaben in der Tabelle und bestätigen Sie alle Angaben mit Next (siehe Abbildung 15.9).
Abbildung 15.9: Bezeichner und Größenangaben für die Felder Eine wichtige Entscheidung, die das Layout des neuen Blockes betrifft, ist die Auswahl eines Layout-Stils: ● ●
Form: Dieser Stil eignet sich für die Anzeige eines einzelnen Datensatzes. Tabular: Dieser Stil eignet sich für die gleichzeitige Anzeige mehrerer Datensätze
Um vorab einen Eindruck zu erhalten, sehen Sie für die jeweils ausgewählte Option im linken Teil des Fensters eine symbolische Darstellung des Layouts. Wählen Sie Form und bestätigen Sie die Auswahl mit Next. Im letzten Schritt können Sie noch festlegen: ● ● ● ●
eine Überschrift für den Rahmen, der automatisch erzeugt wird die Anzahl der anzuzeigenden Datensätze den Abstand zwischen den Datensätzen die Existenz eines Rollbalkens zum Blättern zwischen den Datensätzen
Wählen Sie als Überschrift Student aus; alle anderen Angaben sollten Sie unverändert lassen. Wählen Sie nicht das Kontrollkästchen Scrollbar aus, weil dieser Block jeweils nur einen Datensatz anzeigt und somit ein Rollbalken nicht erforderlich ist (siehe Abbildung 15.10). Bestätigen Sie die Auswahl mit Next, nehmen Sie die Glückwünsche des Layout Wizard entgegen und schließen Sie die Erstellung des Blocks mit Finish ab.
Abbildung 15.10: Weitere Angaben zum Layout des Blockes Der Forms Builder platziert die Felder im Layout Editor. Im Object Navigator ist nun der neu angelegte Block Student sichtbar. Beachten Sie auch, dass ein neues Element vom Typ Canvas und ein Frame automatisch erzeugt wurden, als Sie den Block Student erstellt haben (siehe Abbildung 15.11).
Abbildung 15.11: Betrachtung der neuen Elemente im Object Navigator
Testen des neuen Formulars Das Formular kann nun getestet werden. Wählen Sie den Menüeintrag Program | Run Form | Client/Server oder klicken Sie in der linken Werkzeugleiste des Object Navigator auf die grüne Ampel. Es wird ein Fenster mit dem Titel Oracle Forms Runtime und dem neuen Formular angezeigt. Sie werden feststellen, dass im oberen Teil des Fensters ein Standardmenü und eine Werkzeugleiste mit Symbolen (Smartbar) angezeigt wird (siehe Abbildung 15.12).
Abbildung 15.12: Testen des neuen Formulars Sie können das soeben erstellte Formular mit dem Menüeintrag Program | Run Form | Web auch im Web testen. In diesem Fall wird der mitgelieferte Appletviewer zur Anzeige des Formulars benutzt. Schauen Sie sich jetzt das Standardverhalten des Formulars an. Um eine Abfrage durchzuführen, wählen Sie den Menüeintrag Query | Execute oder das entsprechende Symbol in der Werkzeugleiste aus (Wenn Sie den Mauszeiger über die Symbole bewegen, sehen Sie für jedes Symbol einen Hinweistext.). Das Formular fragt die Tabelle Student ab. Sie werden den ersten Datensatz sehen, der aus der Tabelle geliefert wird. In der linken unteren Fensterecke sehen Sie eine fortlaufende Nummer der angezeigten Datensätze sowie deren Gesamtzahl. Die Zeile, in der diese Anzeige zu sehen ist, wird Statuszeile genannt.
Die Statuszeile ist (zur Laufzeit) der Bereich am unteren Rand des Formulars, in dem wichtige Informationen angezeigt werden, wie z.B. die fortlaufende Nummer
des angezeigten Datensatzes.
Die Meldungszeile erscheint oberhalb der Statuszeile und liefert dem Benutzer Rück- und Fehlermeldungen. Die Zeile oberhalb der Statuszeile wird als Meldungszeile bezeichnet. Sie möchten z.B. wissen, wie viele Datensätze von der Abfrage des Tabellenblocks geliefert wurden und wählen den Menüeintrag Query | Count hits. Für die Tabelle Student zeigt die Meldungszeile an: FRM-40355: Query will retrieve 31 records. Bewegen Sie sich mit der Tabulator-Taste zum nächsten Feld. Um z.B. vom Feld Student_ID ins Feld Last_Name zu wechseln, drücken Sie einmal die Tabulatortaste. Das Feld wird hervorgehoben, wie es in Abbildung 15.13 zu sehen ist. Um zum vorherigen Feld zurückzukehren, drücken Sie die Umschalt- und Tabulatortaste gleichzeitig.
Da Sie keine Reihenfolge angegeben haben, in der die Datensätze aus der Tabelle Student gelesen werden sollen, geschieht dies willkürlich möglicherweise absteigend nach Student_ID. Sie werden noch lernen, die Reihenfolge anzugeben, in der ein Block Datensätze einlesen soll.
Durch ein Formular navigieren Sie können sich in einem Formular auf zwei Wegen zum nächsten Datensatz bewegen: ● ●
Sie wählen Record | Next im Menü Sie klicken auf das Symbol Next Record in der Werkzeugleiste (Smartbar)
So oder so wird der nächste Datensatz im Formular angezeigt werden. Außerdem wird der in der linken unteren Ecke angezeigte Zähler erhöht - in diesem Fall auf 2. Ebenso können Sie die Tasten Pfeil-Nach-Oben und Pfeil-Nach-Unten drücken, um sich in einem Formular durch die Datensätze zu bewegen. Die Pfeil-Nach-Unten-Taste zeigt den nächsten Datensatz und die Pfeil-Nach-Oben-Taste den vorherigen Datensatz an. Versuchen Sie, sich vor den ersten Datensatz zu bewegen, zeigt die Meldungszeile an: FRM-40100: At first record. Versuchen Sie, sich über den letzten Datensatz hinaus zu bewegen, wird dementsprechend angezeigt: FRM-40352: Last record of query retrieved.
Abbildung 15.13: Navigieren durch die Felder des neuen Formulars
Abfrageverhalten von Oracle Forms Oracle Forms stellt einige grundlegende Abfragemöglichkeiten zur Verfügung, die vom Entwickler keine weitere Programmierung erfordern. Ein Formular kann eine Abfrage in zwei Schritten durchführen. Der erste Schritt setzt voraus, dass für das Formular der ENTERQUERY-Modus aktiviert wird. In diesem Modus kann der Benutzer in ein oder mehrere Felder des Formulars Suchkriterien eingeben. Mit dem nächsten Schritt führt das Formular die Abfrage aus, wobei die Suchkriterien verwendet werden. Schauen Sie sich einige Beispiele anhand des Formulars Student an. Sie wechseln in den ENTER-QUERY-Modus, entweder durch die Auswahl des Menüeintrags Query | Enter, Drücken der (F7)-Taste oder Anklicken des entsprechenden Symbols in der Werkzeugleiste (Smartbar). Sie können erkennen, ob sich das Formular im ENTER- QUERYModus befindet - in der Meldungszeile steht: Enter a query; press F8 to execute, Ctrl+q to cancel und in der Statuszeile: ENTER-QUERY. Bewegen Sie sich mit der Tabulatortaste in das Feld Year, und geben Sie FRESHMAN ein (siehe Abbildung 15.14). Um die Abfrage auszuführen, wählen Sie entweder den Menüeintrag Query | Execute, drücken (F8), oder klicken auf das Symbol Execute Query in
der Werkzeugleiste (Smartbar).
Abbildung 15.14: Eingabe eines Abfragekriteriums im ENTER-QUERY-Modus Schauen Sie sich ein Beispiel an, bei dem in mehr als einem Feld ein Suchkriterium eingegeben wird. Wechseln Sie in den ENTER-QUERY-Modus, entweder durch die Auswahl des Menüeintrags Query | Enter, Drücken der (F7)-Taste oder Anklicken des Enter QuerySymbols. Geben Sie im Feld Year SENIOR und im Feld City SPRINGFIELD ein. Abbildung 15.15 zeigt, was bei Ausführung der Abfrage angezeigt werden sollte. Im ENTER-QUERY-Modus können Sie auch % als Platzhalter in einem Feld verwenden. Wechseln Sie für die Ausführung einer Beispielabfrage in den Abfragemodus. Geben Sie im Feld Last_Name M% L% ein, und führen Sie die Abfrage durch. Dieses Suchkriterium wird alle Studenten ausgeben, deren Nachname mit M beginnt und ebenfalls den Buchstaben L enthält. Diese Abfrage gibt zwei Studenten mit den Nachnamen MALLARD und MICHAELS aus. Eine weitere leistungsfähige Eigenschaft von Oracle Forms sind die Einsatzmöglichkeiten der logischen Operatoren >, >=, <, <= und !. Wenn Sie z.B. alle FRESHMEN finden wollen, die nicht in DOVER wohnen und deren Vornamen mit dem Buchstaben G oder höher beginnen, würden Sie > G im Feld First_Name, FRESHMEN im Feld Year und != 'DOVER' im Feld City
eingeben. Nach Ausführung der Abfrage müssten fünf Datensätze abgerufen werden Freshmen mit den Vornamen Paula, Henry, Linda, Richard und Ivan, die alle in Springfield wohnen. Wenn Sie sich im ENTER-QUERY-Modus befinden und mit der Abfrage nicht fortfahren wollen, können Sie diesen Modus verlassen, indem Sie entweder den Menüeintrag QUERY | Cancel wählen, Strg+q drücken oder das Symbol Cancel Query in der Werkzeugleiste (Smartbar) betätigen. Möchten Sie nur die Anzahl der Datensätze erfahren, die eine Abfrage zurückbringen würde, ohne die Datensätze sehen zu wollen, erreichen Sie dies mit dem Befehl Query | Count Hits.
Abbildung 15.15: Ein einzelner Datensatz, der von einer Abfrage zurückgeliefert wurde, bei der in zwei Feldern Abfragekriterien eingegeben wurden So möchten Sie z.B. wissen, wie viele SOPHOMORES es gibt. Stellen Sie für das Formular den ENTER-QUERY-Modus ein und geben Sie in das Feld Year SOPHOMORE ein. Die Anzahl der Datensätze können Sie ermitteln, indem Sie den Menüeintrag Query | Count Hits wählen oder (Umschalt)+(F2) drücken. In der Meldungszeile wird die Anzahl der Datensätze angezeigt, die den Abfragekriterien entsprechen.
Einen Datensatzes mit Oracle Forms ändern
Ein Formular befindet sich immer in einem der beiden Modi, NORMAL oder ENTER QUERY. Sie können keine Änderungen an Datensätzen vornehmen, wenn sich das Formular im ENTER-QUERY-Modus befindet. Nur im NORMAL-Modus können Sie einen Datensatz ändern, löschen oder neu anlegen. Wenn Sie einen vorhandenen Datensatz ändern wollen, bewegen Sie sich zum entsprechenden Datensatz und zu dem Feld, das Sie ändern möchten. Möchten Sie z.B. den Vornamen von Peter Pinkwater in Pierre ändern, fragen Sie die Tabelle ab, um seinen Datensatz abzurufen und geben Sie im Feld First_Name seinen neuen Vornamen ein. Um einen neuen Datensatz anzulegen, wählen Sie den Menüeintrag Records | Insert, drücken Sie (F6) oder wählen Sie das entsprechende Symbol in der Werkzeugleiste (Smartbar). Nehmen Sie als Beispiel das gerade von Ihnen erstellte Formular, um der Tabelle Student einen neuen Studenten hinzuzufügen. Nachdem Sie den Menüeintrag Records | Insert gewählt oder (F6) gedrückt haben, steht die Schreibmarke im ersten Feld des Datensatzes - Student_ID. Geben Sie folgende Werte in die Felder ein: Student ID Last Name First Name MI Year Street Address City State Zipcode Telephone Fax Email
99991234 THOMAS KAREN G FRESHMAN 133 SEQUOIA DR. SPRINGFIELD CA 900004321 8055551291 [email protected]
Um den soeben erstellten Datensatz zu speichern, stehen Ihnen mehrere Möglichkeiten zur Verfügung: ● ● ● ●
Sie können den Menüeintrag Action | Save wählen. Sie können (F10) drücken. Sie können das Disketten-Symbol in der Werkzeugleiste (Smartbar) betätigen. Sie könne solange warten, bis Sie eine weitere Abfrage durchführen oder das Formular verlassen. In beiden Fällen werden Sie gefragt, ob die vorgenommenen Änderungen gespeichert werden sollen (siehe Abbildung 15.16).
Abbildung 15.16: Der Benutzer wird gefragt, ob die Änderungen gespeichert werden sollen
In der Standardeinstellung speichert Oracle Forms vom Benutzer vorgenommene Änderungen nicht automatisch. Im Hintergrund nutzt Oracle Forms die Fähigkeit der Oracle-Datenbank, Transaktionen zu verwalten. Wenn Sie Ihre Änderungen an Datensätzen in einem Formular speichern, führt Oracle Forms für Sie eine COMMIT-Anweisung aus. Geben Sie an, keine Änderungen speichern zu wollen, führt Oracle Forms eine ROLLBACK-Anweisung für Sie aus. Machen Sie sich klar, dass Oracle Forms alle Änderungen seit dem Beginn der Sitzung oder dem letzten Speichern rückgängig macht. Um einen Datensatz zu löschen, gehen Sie zu dem entsprechenden Datensatz und wählen den Menüeintrag Record | Remove. Es wird der vorhergehende Datensatz angezeigt. Der Datensatz wird erst wirklich aus der Tabelle entfernt, wenn Sie einen Speichervorgang durchführen.
Speichern eines Formulars Nachdem Sie nun eine Einführung in das Verhalten von Formularen erhalten haben, verlassen wir das Formular und kehren zum Forms Builder zurück. Um das von Ihnen erstellte Formular zu speichern, wählen Sie den Menüeintrag im Forms Builder File | Save. Standardmäßig speichert der Forms Builder das Formular im Verzeichnis FORMS60 unter dem Stammverzeichnis von Oracle. Abbildung 15.17 zeigt das vom Forms Builder verwendete Fenster zum Speichern der Datei. Im Feld »Save as type« wählen Sie in der Liste FORMS (*.fmb) aus, geben Sie als Dateiname student ein und klicken Sie auf Save. Beachten Sie, dass der Object Navigator das Formular jetzt unter dem Namen STUDENT anzeigt.
Abbildung 15.17: Speicherung eines Formulars
Es ist nicht ratsam, anwendungsspezifische Dateien im Verzeichnisbaum von Oracle zu speichern. Sie verlieren dann u.U. die Übersicht, welche Dateien zu welcher Anwendung gehören. Sie sollten stattdessen ein separates Verzeichnis einrichten, in dem Sie Ihre Anwendungsdateien speichern.
Komponenten von Oracle Forms Oracle Forms besteht aus drei Komponenten - Forms Builder, Forms Compiler und Forms Runtime. Außerdem gibt es noch eine Server-Komponente, den Forms Service, wenn Sie die erstellten Forms-Anwendungen im Web zur Verfügung stellen möchten. Der Forms Builder ermöglicht Ihnen das Erstellen und Verändern von drei Modultypen: Formulare, Menüs und Bibliotheken. Die meisten Forms-Anwendungen bestehen in erster Linie aus Formularen mit
einem oder mehreren Menüs und einer oder mehreren Bibliotheken. Es gibt zwei Arten von Bibliotheken: ● ●
Die von Ihnen entworfenen Module können vom Forms Builder entweder in einer Datei oder in einer Oracle-Datenbank gespeichert werden. Um ein Modul in einer Datenbank zu speichern, müssen Sie oder Ihr DBA das von Oracle Forms benötigte Datenbank- Schema laden. Mehr darüber können Sie in der Dokumentation erfahren. Das Speichern Ihrer Anwendungsmodule in einer Oracle-Datenbank hat Vor- und Nachteile. Wenn Sie mit anderen, geographisch voneinander getrennten Entwicklern zusammenarbeiten, kann es sinnvoll sein, eine Oracle-Datenbank als gemeinsames Repository für Ihre Anwendungsmodule einzusetzen. Wenn Sie jedoch ein Programm für die Versionskontrolle wie PVCS, zur Verwaltung der einzelnen Versionen Ihrer Formulare, Berichte und Grafiken, einsetzen möchten, wollen Sie Ihre Anwendungsmodule wahrscheinlich in Dateien speichern. In Kapitel 5 wurde bereits darauf hingewiesen, dass Sie auch den Oracle SCM zur Verwaltung Ihrer Forms-Module benutzen können. Sollten Sie sich für eine Variante entschieden haben, können Sie die Standardeinstellungen im Forms Builder entsprechend anpassen. Wählen Sie dazu den Menüeintrag Tools | Preferences. In der Registerkarte Access können Sie das Optionsfeld Access auf den gewünschten Wert setzen (siehe Abbildung 15.18). Außerdem sollten Sie das Kontrollkästchen Save Before Building aktivieren - der Vorgang der Formularübersetzung wird bald erläutert. Der Forms Builder erlaubt Ihnen, mehrere Module gleichzeitig geöffnet zu haben. So können Sie z.B. fünf Formulare, ein Menü und eine Bibliothek zur gleichen Zeit geöffnet haben.
Formulare Wenn Sie das Dateisystem zum Öffnen und Speichern Ihrer Formulare einsetzen, verwendet der Forms Builder die Dateierweiterung .fmb. Eine .fmb-Datei ist eine Binärdatei mit einer komplexen Beschreibung des Formulars. Da Sie eine .fmb-Datei aber nicht ausführen können, müssen Sie eine Laufzeitversion der Datei mit der Endung .fmx erzeugen, wozu der Forms Compiler die .fmb-Datei benötigt. Wenn Sie ein Formular verwenden, benutzen Sie zum Ausführen einer .fmx-Datei das Programm Forms Runtime. Außerdem besteht noch die Möglichkeit, eine binäre .fmb-Datei in eine lesbare .fmt-Datei umzuwandeln. Sie können Änderungen an .fmt-Dateien vornehmen und diese anschließend in die Binärdatei rückübersetzen. Dateien vom Typ .fmb und .fmt lassen sich portieren - Sie können diese von einer Umgebung in eine andere übertragen und daraus dann die ablauffähige .fmx-Datei erzeugen.
Abbildung 15.18: Änderung der Speicher-Optionen für den Forms Builder
Menüs Ein Menü wird vom Forms Builder in eine Datei mit der Endung .mmb gespeichert, die dann beim Kompilieren des Menüs zur Erzeugung der .mmx-Datei verwendet wird. Benutzen Sie ein Formular, dem ein Menü zugeordnet ist, führt Forms Runtime die .mmx- Datei aus. Sie können auch eine binäre .mmb-Datei in eine lesbare .mmt-Datei umwandeln.
Bibliotheken Oracle Forms benutzt zwei Arten von Bibliotheken: ●
●
PL/SQL-Bibliotheken bestehen aus PL/SQL-Prozeduren, Funktionen oder Paketen, die von mehreren Forms-Modulen verwendet werden können. Dies erleichert die Wiederverwendbarkeit und zentrale Wartung von Programmeinheiten. Der Forms Builder speichert eine PL/SQL-Bibliothek in einer Datei mit der Endung .pll, die dann beim Kompilieren der PL/SQL-Bibliothek zur Erzeugung der .plx-Datei verwendet wird. Benutzen Sie ein Formular, dem eine Bibliothek zugeordnet ist, führt Forms Runtime den in der .plx-Datei enthaltenen Code aus. Sie können auch eine binäre .pll-Datei in eine lesbare .pld-Datei umwandeln. Objekt-Bibliotheken bestehen aus Forms-Objekten, wie z.B. Blöcken, Feldern, Leinwänden (Canvas) etc., die von mehreren Forms-Modulen verwendet werden können. Die Benutzung dieser Objekte ist wahlweise über einen Kopier- oder Vererbungsmechanismus möglich. Dies erleichtert die Wiederverwendbarkeit und Standardisierung von Objekten. Der Forms Builder speichert eine Objekt-Bibliothek in einer Datei mit der Endung .olb. Objekt-Bibliotheken werden nur während der
Entwicklungsphase benötigt. Beim Kompilieren der Forms-Module werden die Referenzen auf Objekte in der Objekt-Bibliothek aufgelöst. Sie können auch eine binäre .olb-Datei in eine lesbare .olt-Datei umwandeln.
15.3 Elemente eines Formulars Ein Formular wird durch eine Zusammenstellung von Objekten definiert. Einige dieser Objekte sind zwingend erforderlich, andere sind optional. Folgende Typen von Objekten können in einem Formular verwendet werden: ● ● ● ● ● ● ● ●
Zu einem späteren Zeitpunkt dieses Kapitels schauen Sie sich jedes dieser Objekte genauer an. Zuvor wird im folgenden Abschnitt die Verwendung des Object Navigator untersucht.
Mit dem Object Navigator arbeiten Der Object Navigator stellt eine intuitive und bequeme Schnittstelle zur Erstellung und Bearbeitung von Formularen, Menüs und Bibliotheken dar. Der Object Navigator zeigt Objekte in hierarchischer Form an. Für jeden Modultyp - Formular, Menü, PL/ SQL- oder Objekt-Bibliothek - zeigt er eine Reihe von Knoten unterhalb eines jeden Moduls an. So werden z.B. unter jedem Formular die folgenden Knoten angezeigt: Trigger, Warnungen (Alerts), zugeordnete Bibliotheken, Blöcke, Leinwände (Canvases), Editoren, Wertelisten (LOVs), Objektgruppen, Parameter, Programmeinheiten, Datensatzgruppen (Record Groups) und Fenster. Sie können entweder mit der Maus oder den Nach-Oben- und Nach-UntenPfeiltasten zwischen den Knoten wechseln. Links von jedem Knoten befindet sich ein Kästchen. Ist dieses Kästchen leer, gibt es für das Formular keine Objekte dieses Typs. Steht in dem Kästchen ein +, besitzt das Formular Objekte dieses Typs. Das + zeigt an, dass die Anzeige dieses Knotens erweitert werden kann, d.h. zurzeit erscheint es ausgeblendet. Es gibt verschiedene Möglichkeiten, die Anzeige des Knotens zu erweitern: ● ●
Sie können das Kästchen anklicken. Sie können den Menüeintrag Navigator | Expand wählen.
Wird in dem Kästchen ein - angezeigt, existieren Objekte dieses Knotentyps, die aber bereits vollständig zu sehen sind. Es gibt verschieden Möglichkeiten, die Anzeige des Knotens auszublenden:
● ●
Sie können das Kästchen anklicken, um den Knoten auszublenden. Sie können den Menüeintrag Navigator | Collapse wählen.
Nehmen wir das Formular Student, das drei Knoten mit Objekten enthält: Blöcke, Leinwände (Canvases) und Fenster (siehe Abbildung 15.19). Um jeden dieser drei Knoten einzublenden, klicken auf das Kästchen links von jedem Knoten (siehe Abbildung 15.20). Wie Sie sehen, steht vor dem Block Student ein + im Kästchen, was bedeutet, dass dieser nochmals erweitert werden kann.
Abbildung 15.19: Einblenden der Knoten für das Formular Student
Abbildung 15.20: Einblenden der Knoten für Objekte im Object Navigator Jedes dieser Objekte besitzt eine Vielzahl von Eigenschaften. Die Eigenschaften eines bestimmten Objekts lassen Sie sich auf einem der folgenden Wege anzeigen: 1. Klicken Sie mit der rechten Maustaste und wählen Sie im Kontextmenü Property Palette aus. 2. Wählen Sie den Menüeintrag Tools | Property Palette. 3. Doppelklicken Sie auf das links vom Objektnamen angezeigte Symbol. Um z.B. die Eigenschaften des Blocks Student zu sehen, bewegen Sie sich mit der Maus oder den Nach-Oben- und Nach-Unten-Pfeiltasten zum Block Student und öffnen Sie das Eigenschafts-Fenster mit der rechten Maustaste und der Auswahl von Property Palette im Kontext-Menü (siehe Abbildung 15.21).
Abbildung 15.21: Anzeige der Eigenschaften des Blocks Student
Objekteigenschaften Im Object Navigator können Sie die Eigenschaften eines Objekts einsehen und ändern. Dazu klicken Sie mit der linken Maustaste auf den Objektknoten, markieren das Objekt mit der Maus und wählen den Menüeintrag Tools | Property Palette oder Sie klicken mit der rechten Maustaste und wählen dann Property Palette im Kontext-Menü aus. Welche Eigenschaften für ein Objekt angezeigt werden, hängt von seinem Typ ab. Die Eigenschaften sind zu Gruppen zusammengefasst; die Eigenschaften einer Gruppe können erweitert oder ausgeblendet werden. Auf den nächsten Seiten sehen Sie einige Beispiele von Objekteigenschaften.
Blöcke Fast jedes Formular enthält mindestens einen Block. Es gibt zwei Arten von Blöcken: Basistabellenblöcke und Steuerblöcke. Ein Basistabellenblock basiert auf einer bestimmten Tabelle oder Datensicht (View), d.h. innerhalb des Blockes werden Daten aus der Tabelle oder Datensicht (View) angezeigt. Im Unterschied dazu basiert ein Steuerblock nicht auf einer bestimmten Tabelle oder Datensicht (View) und kann z.B. Bedienelemente zur Steuerung der Anwendung enthalten. Fast jeder Block besitzt ein oder mehrere Felder. Mindestens ein Feld eines Basistabellenblocks basiert auf einer Spalte der Tabelle oder Datensicht (View), die dem Block zugrunde liegt.
Ein Basistabellenblock ist ein Block in Oracle Forms, der auf einer Datenbanktabelle oder Datensicht (View) basiert.
Ein Steuerblock ist ein Block in Oracle Forms, der nicht auf einer Datenbanktabelle oder Datensicht (View) basiert und normalerweise Bedienelemente zur Steuerung der Anwendung (z.B. Schaltflächen) enthält.
Abbildung 15.22: Setzen der Blockeigenschaft ORDER BY Zu den Blockeigenschaften gelangen Sie, indem Sie den Block markieren und mit der rechten Maustaste im Kontextmenü Property Palette auswählen. Es wird ein Fenster mit den Eigenschaften des Blocks angezeigt. Möchten Sie z.B. eine Sortierung (ORDER BY-Klausel) für den Block Student festlegen, bewegen Sie sich mit der Pfeiltaste zur Eigenschaft ORDER BY-Klausel. Sie können die ORDER BY-Klausel direkt im Eigenschaftsfeld eingeben oder die Schaltfläche rechts neben dem Feld anklicken, die nach dem Betreten des Feldes dynamisch aufgeblendet wird. Möchten Sie z.B. die Datensätze im Block Student erst nach dem Jahr und dann nach den Namen der Studenten anordnen, geben Sie die in Abbildung 15.22 gezeigte ORDER BY-Klausel ein und klicken Sie dann außerhalb des Feldes, um der Eigenschaft den Wert zuzuweisen.
Basistabellenblöcke Ein Basistabellenblock enthält Felder, die den Spalten einer Tabelle oder Datensicht (View) zugeordnet sind. Jedes Feld hat eine Reihe von Eigenschaften, auf die Sie zugreifen, indem Sie das Feld markieren und mit der rechten Maustaste im Kontextmenü Property Palette auswählen. Eine wichtige Eigenschaft ist der Typ des Feldes (Item Type). Um den Typ eines Feldes zu ändern, markieren Sie die Eigenschaft Item Type und wählen direkt im Eigenschaftsfeld den gewünschten Typ in der Auswahl-Liste aus (siehe Abbildung 15.23).
Abbildung 15.23: Festlegen eines Feldtyps Eine ganze Reihe weiterer Feldeigenschaften beeinflussen das Erscheinungsbild und Verhalten von Oracle Forms-Anwendungen. Einige dieser Feldeigenschaften steuern, was der Benutzer in dieses Feld eingegeben kann. So ist z.B. die Standardeinstellung für Case
Restriction auf Mixed (kleine und große Buchstaben sind zulässig) gesetzt. Um dies zu ändern, wählen Sie das Feld aus und doppelklicken Sie auf die Bezeichnung der Eigenschaft Case Restriction, bis sich der Wert der Eigenschaft in Upper (Umwandlung der Eingabe in Großbuchstaben) geändert hat.
Besitzt eine Feldeigenschaft eine Reihe vordefinierter Werte, können Sie den Wert der ausgewählten Eigenschaft durch wiederholtes Betätigen der Eingabetaste oder Doppelklicken auf die Bezeichnung der Eigenschaft ändern. Eine Eigenschaft hat eine Reihe vordefinierter Werte, wenn im Wertefeld der Eigenschaft eine aufklappbare Auswahl-Liste zu sehen ist.
Abbildung 15.24: Eine Leinwand (Canvas) im Layout Editor
Leinwände (Canvases)
Eine Leinwand (Canvas) ist ein Hintergrundobjekt, auf dem die Felder des Blocks platziert sind. Zu jeder Leinwand gibt es eine Ansicht (Canvas View), die den sichtbaren Ausschnitt der Leinwand definiert, der durch ein Fenster betrachtet werden kann. Mit dem Layout Editor können Sie die Elemente auf dem Canvas bearbeiten. Zum Layout Editor gelangen Sie, indem Sie die Leinwand markieren und mit der rechten Maustaste im Kontextmenü Layout Editor auswählen. Beim Erstellen eines neuen Blocks erzeugt der Forms Builder eine Leinwand (Canvas), falls noch keine existiert und ausgewählt wird, und platziert die Felder darauf. Abbildung 15.24 zeigt das Standardlayout des Formulars Student .
Fenster Ein Fenster ist ein leerer Rahmen, in dem die Leinwand (Canvas) angezeigt wird. Jedes Formular hat mindestens ein Fenster, es kann auch mehrere enthalten. Die Abbildung 15.25 zeigt das Eigenschaftsfenster für das Standardfenster Window1, das zusammen mit dem Block Student erzeugt wurde. Einige der Eigenschaften, die normalerweise verändert werden, sind die Breite, Höhe und Überschrift des Fensters.
Abbildung 15.25: Eigenschaften eines Fensters
Trigger Ein Trigger in Oracle Forms ist eine Folge von PL/SQL-Anweisungen, die beim Auftreten eines bestimmten Ereignisses ausgeführt werden. Ein Trigger kann mit unterschiedlichen Ebenen der Hierarchie von Formularobjekten verbunden sein. So können Sie z.B. einen Trigger definieren, der beim Aktivieren eines Formulars ausgelöst wird. Sie können einen Trigger definieren, der vor der Ausführung einer Abfrage in einem Block ausgelöst wird. Oder Sie definieren eine Trigger, der nur dann ausgelöst wird, wenn sich der Inhalt eines bestimmten Feldes ändert. Es gibt eine ganze Reihe vordefinierter Trigger-Ereignisse. Abbildung 15.26 zeigt die Liste der Trigger-Ereignisse, die angezeigt wird, wenn Sie die Auswahl-Liste für die Trigger-Eigenschaft Name auswählen.
Abbildung 15.26: Liste der Trigger-Ereignisse
Datensatzgruppen (Record Groups) Eine Datensatzgruppe (Record Group) ist eine Gruppe von Werten, die definiert werden kann: ● ● ●
dynamisch, durch eine SELECT-Anweisung, die zur Laufzeit ausgeführt wird. statisch, durch eine Gruppe statischer Werte. programmgesteuert, durch ein PL/SQL-Programm, das zur Laufzeit eine SELECTAnweisung oder eine Gruppe von Werten aufbaut.
Nehmen Sie z.B. an, dass Sie aus den bestehenden Studenten-ID der Tabelle Student eine Datensatzgruppe erzeugen wollen. Dazu verwenden Sie den Object Navigator, wählen den Knoten Record Groups im Formular Student aus und klicken in der vertikalen Werkzeugleiste an der linken Fensterseite das Symbol Create (+) an. Es wird ein Dialogfenster angezeigt, in das Sie die Abfrage für die neue Datensatzgruppe (Record Group) eingeben (siehe Abbildung 15.27). Nachdem Sie Ihre Abfrage eingegeben haben, klicken Sie auf OK und die Datensatzgruppe wird erzeugt. Eine Datensatzgruppe (Record Group) wird typischerweise von einer Werteliste (LOV) verwendet.
Wertelisten Eine Werteliste (LOV-List of Values) basiert auf einer Datensatzgruppe (Record Group) Eine LOV wird üblicherweise benutzt, um eine Liste zulässiger Werte anzuzeigen, aus denen der Benutzer eine Auswahl treffen kann. Eine Werteliste kann mit einer Reihe von Feldeigenschaften verbunden werden. So können Sie z.B. festlegen, ob eine Werteliste auch zur Validierung einer Eingabe durch den Benutzer verwendet werden soll. Damit wird sichergestellt, dass der Benutzer keinen Wert in ein Feld eingeben kann, wenn dieser nicht in der angegebenen Werteliste enthalten ist. Am Tag 16, »Entwicklung einer Benutzeroberfläche mit Oracle Forms«, und am Tag 17, » Anwendungsentwicklung mit Hilfe von Oracle Forms«, lernen Sie mehr über die Verwendung von Wertelisten.
Abbildung 15.27: Festlegen einer Abfrage für eine neue Datensatzgruppe (Record Group)
Warnungen (Alerts) Eine Warnung (Alert) ist ein spezielles Fenster, das den Benutzer über ein Ereignis informiert. Das Fenster ist modal, d.h. ehe er fortfahren kann, muss der Benutzer eine der angebotenen Schaltflächen betätigen.
Parameter Sie können für ein Formular Parameter definieren, die in einem anderen Formular gesetzt werden. Sie können einen Trigger definieren, der den Parameter überprüft und abhängig von seinem Wert, eine bestimmte Aktion auslöst. Nehmen Sie an, Sie wollen ein Formular aus einem anderen Formular aufzurufen. Sie möchten dem Formular Student eine Student_ID übergeben und den entsprechenden Datensatz des Studenten automatisch angezeigt bekommen. Dazu würden Sie einen Parameter Student_ID vom Typ CHAR definieren. Dann würden Sie einen Trigger schreiben, der beim Aktivieren des aufgerufenen Formulars ausgelöst würde und den Datensatz für die im Parameter übergebene Student-ID anzeigt.
15.4 Ändern von Oracle-Komponenten mit Hilfe des Registrierungseditors Es ist ratsam, Ihre gesamten ausführbaren Formulare, Berichte, Grafikdateien und Bibliotheken in einem speziell für Ihre Anwendung eingerichteten Verzeichnis zu speichern. Sie in einem Unterverzeichnis des Oracle-Stammverzeichnisses zu speichern ist aus folgenden Gründen nicht zu empfehlen: ●
Da im Verzeichnisbaum von Oracle sehr viele Verzeichnisse liegen, kann es schnell
●
geschehen, dass eine Datei falsch abgelegt wird. Bei der Installation einer neuen Version der Oracle-Werkzeuge auf Ihrem PC könnten Ihre Dateien entfernt werden.
Richten Sie dagegen ein anderes Verzeichnis ein - z.B. C:\Flugle - müssen Sie einige Einträge in der Windows-Registrierungs-Datenbank ändern. Die Laufzeitkomponenten Forms Runtime, Reports Runtime und Graphics Runtime verwenden nicht die Umgebungsvariable PATH, um nach einer ausführbaren Formular-, Berichts-, Grafik- oder Bibliotheksdatei zu suchen. Stattdessen verwendet jede Laufzeitkomponente einen Registrierungs-Schlüssel, um in bestimmten Verzeichnissen nach ausführbaren Dateien zu suchen. Im Einzelnen sind dies: ● ● ●
Forms Runtime (ifrun60) verwendet FORMS60_PATH Reports Runtime (rwrun60) verwendet REPORTS60_PATH Graphics Runtime (gorun60) verwendet GRAPHICS60_PATH
In den Standardeinstellungen enthalten die Registrierungs-Schlüssel FORMS60_PATH, REPORTS60_PATH und GRAPHICS60_PATH nur Verzeichnisse des OracleVerzeichnisbaums. Wenn Sie ein Formular ausführen wollen, welches im Verzeichnis C:\Flugle abgespeichert ist, zeigt Forms Runtime durch eine Meldung an, dass es dieses Formular nicht öffnen kann. Damit Sie Ihre Formulare, Berichte, Grafiken oder Bibliotheken ausführen können, sollten Sie die Verzeichnisse mit diesen Dateien mit Hilfe des Registrierungseditors hinzufügen. 1. Dazu wählen Sie Start | Run. 2. Geben Sie regedit ein, und klicken Sie auf OK. 3. Erweitern Sie die Anzeige von HKEY_LOCAL_MACHINE, indem Sie auf das links davon stehende Symbol (+) klicken (siehe Abbildung 15.28).
Abbildung 15.28: Registrierungszweig im Registrierungseditor 4. Erweitern Sie die Anzeige von SOFTWARE, indem Sie auf das links davon stehende Symbol (+) klicken. Klicken Sie auf ORACLE. 5. In der rechten Fensterhälfte sollten Sie eine Liste mit Zeichenfolgen sehen können. Blättern Sie bis zum Eintrag FORMS60_PATH nach unten. Klicken Sie mit der rechten Maustaste und dann auf Modify. Ein Fenster mit dem Titel Edit String wird angezeigt. Klicken Sie in das Feld Value Data und fügen Sie ;C:\Flugle am Ende der Zeichenfolge ein (siehe Abbildung 15.29). 6. Führen Sie die gleichen Schritte durch, um die Verzeichnisse GRAPHICS60_PATH und REPORTS60_PATH hinzuzufügen. Danach schließen Sie den Registrierungseditor.
Abbildung 15.29: Änderung von FORMS60_PATH mit dem Registrierungseditor
15.5 Zusammenfassung Dies war eine in die Tiefe gehende Einführung in Oracle Forms. Die Hauptpunkte der Lektion waren: ●
●
●
Die Internet Developer Suite (iDS) enthält unter anderem die vier Werkzeuge Oracle Forms, Oracle Reports, Oracle Graphics und Procedure Builder. Der Forms Builder wird zum Entwurf und Testen, Forms Runtime zum Ausführen von Formularen eingesetzt. Der Forms Builder speichert ein Formular als binäre .fmbDatei, die auf verschiedene andere Plattformen portiert werden kann. Der Forms Builder kann den Forms Compiler aufrufen, um aus einer .fmb-Datei eine .fmx-Datei zu erzeugen, die wiederum von Forms Runtime ausgeführt wird. Forms- Anwendungen können im Client-Server-Modus und im Web als Applet ausgeführt werden. Zur Inspektion von Formularobjekten setzen Sie den Object Navigator ein. Jedes Objekt besitzt eine Gruppe von Eigenschaften, die angezeigt und geändert werden können.
●
●
Der Forms Builder erzeugt vier Arten von Modulen: Formulare, Menüs, PL/SQLBibliotheken und Objekt-Bibliotheken. Im Forms Builder können gleichzeitig mehrere Module aller Arten geöffnet und bearbeitet werden.
15.6 Wie geht es weiter? An Tag 16 lernen Sie, das Erscheinungsbild und Verhalten eines Formulars anzupassen. Sie lernen, wie man ein Menü und ein Master-Detail-Formular erstellt und die unterschiedlichen Eigenschaften von Objekten ändert.
15.7 Fragen und Antworten Frage: Kann ein Basistabellenblock ein Feld enthalten, das nicht Teil der Basistabelle des Blocks ist? Antwort: Auf jeden Fall. Solche Felder werden dazu verwendet, aus anderen Feldern des Blocks berechnete Werte anzuzeigen. So enthält z.B. ein Bestellformular üblicherweise eine Zeilensumme, die dem Benutzer zwar angezeigt werden soll, aber nicht in der Tabelle gespeichert wird. Frage: Kann ein Block auf einer Datensicht (View) statt einer Tabelle basieren? Antwort: Ja. Oft sind dies nur Anzeigeblöcke, die nicht für Änderungen der Daten durch den Benutzer verwendet werden. Frage: Kann eine Forms-Anwendung andere Komponenten wie einen Bericht oder eine Grafik aufrufen? Antwort: Ja. Sie können Menüelemente definieren, die einen Bericht drucken oder eine Grafik anzeigen. Wie dies gemacht wird, sehen Sie an Tag 18, » Entwicklung von Berichten mit Oracle Reports;« und an Tag 19, »Oracle Graphics und den Procedure Builder verwenden«.
15.8 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Oracle Forms Runtime führt nur eine .fmb-Datei aus. 2. Welche sind die vier Modultypen, die mit dem Forms Builder erzeugt werden können? 3. Richtig oder falsch? Der Forms Builder kann ein Formular nur in einer OracleDatenbank speichern, wenn dieses einen Basistabellenblock enthält. 4. Richtig oder falsch? Sie können einen Block erzeugen, der auf einer Tabelle basiert, auf die ein anderer Oracle-Benutzer die Rechte besitzt.
Übung Erzeugen Sie für die Tabelle Instructor ein Formular. Geben Sie eine ORDER BY- Klausel ein, damit die angezeigten Datensätze nach der Abteilungs-ID, dem Nachnamen und dem Vornamen des Dozenten sortiert werden. Testen Sie das Formular.
Tag 15 Einführung in Oracle Forms 477 Tag 16 Entwicklung einer Benutzeroberfläche mit Oracle Forms 501 Tag 17 Anwendungsentwicklung mit Oracle Forms 529 Tag 18 Entwicklung von Berichten mit Oracle Reports 551 Tag 19 Oracle Graphics und Procedure Builder 573 Tag 20 Entwicklung von Web-Anwendungen mit Java und JDeveloper 593 Tag 21 Oracle Business Components for Java 633 Dies ist ihre letzte Woche. In Woche 3 lernen Sie Oracle Forms, Reports, und Procedure Builder kennen. Zwei Lektionen befassen sich mit Java Anwendungsentwicklung, JDeveloper und Oracle9i Internet Application Server. Im Überblick: ●
●
●
●
Tag 15, »Einführung in Oracle Forms« Dies ist die erste von fünf Lektionen, in denen die Komponenten von Developer besprochen werden. Drei dieser Lektionen - die Tage 15, 16 und 17 konzentrieren sich auf Oracle Forms. Tag 16, »Entwicklung einer Benutzeroberfläche mit Forms Developer« In einer weiteren Lektion über Oracle Forms lernen Sie, wie man ein Master/Detail-Formular erstellt, wie man das Erscheinungsbild eines Formulars verändert und man die Eigenschaften von Formularobjekten festlegt. Tag 17, »Anwendungsentwicklung mit Forms Developer« Dies ist die letzte Lektion über Oracle Forms. Diese Lektion behandelt die Verwendung von Triggern in Oracle Forms, mit denen Benutzereingaben überprüft werden, die Verwendung von Menüs in einer Formularanwendung und andere Themen. Tag 18, »Entwicklung von Berichten mit Oracle Reports« In dieser Lektion lernen Sie eine weitere Komponente kennen: Oracle Reports.
●
●
●
Schritt für Schritt werden Sie einen einfachen Bericht erstellen. Außerdem lernen Sie die einzelnen Objekte kennen, aus denen sich ein Bericht zusammensetzt. Tag 19, »Geschäftsgrafiken erstellen und Procedure Builder« In dieser Lektion geht es um Multimedia Komponenten und Procedure Builder. Sie lernen, ein Diagramm zu erstellen, das die in einer Tabelle enthaltenen Werte wiedergibt. Außerdem erfahren Sie, wie Sie den Procedure Builder zur Erstellung eines Datenbanktriggers einsetzen. Tag 20, »Entwicklung von Web-Anwendungen mit Java und JDeveloper« Lernen Sie die Java Entwicklungsumgebung JDeveloper kennen. Schritt für Schritt erfahren Sie, wie eine internetfähige Anwendung mit Java und JDeveloper entwickelt wird. Tag 21, »Oracle Business Components for Java« Setzen Sie das Framework Oracle Business Components for Java (BC4J) ein, um anspruchsvolle Anwendungen zu erstellen.
Weitere PL/SQL- Programmiertechniken Die heutige Lektion umfasst die abschließende Diskussion von SQL und PL/SQL, ehe Sie sich an die Verwendung von Oracle Forms zur Anwendungserstellung wagen. Die heutige Lektion lässt sich in drei Kategorien unterteilen: ●
●
●
Die Behandlung von Fehlern in einem PL/SQL-Unterprogramm. Sie werden lernen, eine Fehlerbehandlungsroutine für Oracle-Fehler und benutzerdefinierte Exceptions zu definieren. Außerdem werden Sie einige Beispiele für vordefinierte Exceptions sehen. Die Deklaration und Verwendung von Cursors in einem PL/SQL-Unterprogramm, um mehrere Zeilen aus der Datenbank abzurufen. Die Verwendung von PL/SQL zur Erstellung eines Datenbanktriggers. Sie werden den Einsatz eines Triggers zur Validierung von Daten, der Durchsetzung von Sicherheitsmaßnahmen und für Änderungen an Datensätzen einer Tabelle erlernen.
14.1 Fehlerbehandlung in PL/SQL Das Oracle-Handbuch »Error Messages and Codes« führt alle Fehlercodes und - meldungen außer denen für betriebssystemspezifische Fehler auf. Zu irgendeinem Zeitpunkt wird Ihre Anwendung wahrscheinlich auf einige dieser Fehler treffen. Unter PL/ SQL werden Oracle-Fehler als Exceptions bezeichnet. Einige dieser Exceptions besitzen vordefinierte Namen, auf die in PL/SQL-Unterprogrammen Bezug genommen werden kann. Zusätzlich zu diesen vordefinierten Oracle-Exceptions können Sie anwendungsspezifische Exceptions in einem PL/SQLUnterprogramm definieren.
Eine Exception ist ein vordefinierter oder benutzerdefinierter Anwendungsfehler, der automatisch vom Oracle-RDBMS oder absichtlich in einem PL/SQL-Unterprogramm ausgelöst wird. Eine Methode zur Fehlerbehandlung in einem PL/SQL-Unterprogramm ist die Validierung auf einen OracleFehlercode nach jeder SQL-Anweisung. Es ist bei diesem Ansatz jedoch problematisch, das entsprechende Unterprogramm nachzuvollziehen. Eine Alternative ist die Möglichkeit, dass Sie unter PL/SQL genau angeben, welche Verarbeitung bei einer bestimmten Exception stattfinden soll. Dieser Abschnitt eines PL/ SQLUnterprogramms wird der Exception-Block eines PL/SQL-Unterprogramms genannt. Eine vordefinierte Exception wird sozusagen »ausgelöst«, wenn während der Ausführung eines PL/SQL-Unterprogramms ein Oracle-Fehler auftritt. Eine benutzerdefinierte Exception lösen Sie aus, in dem Sie in Ihrem PL/SQL-Code die RAISEAnweisung aufrufen.
Der Exception-Block Der Exception-Block ist ein optionaler Abschnitt eines PL/SQL-Unterprogramms, der steuert, wie bestimmte Exceptions behandelt werden sollen.
Die Syntax eines Exception-Blocks lautet folgendermaßen: EXCEPTION WHEN exception-name1 THEN PL/SQL-statements; ... [WHEN exception-nameN THEN PL/SQL-statements;] ... [WHEN OTHERS THEN PL/SQL-statements;] END; Die Platzhalter sind wie folgt definiert: ● ●
exception-name1 bis exception-nameN sind die Namen vor- und benutzerdefinierter Exceptions. PL/SQL-statements sind eine oder mehrere PL/SQL-Anweisungen, die beim Auslösen der Exception ausgeführt werden.
Zur Veranschaulichung enthält Listing 14.1 einen PL/SQL-Block mit einem Exception- Block. Beachten Sie, dass der Exception-Block zwei Exception-Handler enthält: einen für eine vordefinierte Exception - die Exception TOO_MANY_ROWS (in Zeile 11) - und einen für alle anderen Exceptions - gekennzeichnet durch das Wort OTHERS (in Zeile 13). Listing 14.1: Behandlung der TOO_MANY_ROWS-Exception SQL> declare 2 2 Course_Rec Course%ROWTYPE; 3 3 begin 4 5 5 select * 6 into Course_Rec 7 from Course 8 where 9 Department_ID = 1000; 10 10 exception 11 when TOO_MANY_ROWS then 12 dbms_output.put_line('TOO_MANY_ROWS raised - use a cursor'); 13 when OTHERS then 14 NULL; 15 end; 16 / TOO_MANY_ROWS raised - use a cursor Wenn Sie den Exception-Handler für OTHERS entfernen und eine Exception auslösen, für die kein ExceptionHandler vorhanden ist, gibt PL/SQL eine Fehlermeldung zurück. Listing 14.2 zeigt ein Beispiel - eine Zeichenfolge mit 18 Zeichen wird einer Variablen zugewiesen, die bis zu fünf Zeichen speichern kann, was zu einem Oracle-Fehler führt.
Listing 14.2: Der Exception-Block behandelt keine others-Exceptions SQL> declare 2 xyz varchar2(5); 3 begin 4 5 xyz := 'This will not fit!'; 6 exception 7 when TOO_MANY_ROWS then 8 dbms_output.put_line('TOO_MANY_ROWS Exception Raised'); 9 dbms_output.put_line('Occurred in anonymous block'); 10 10 end; 11 / declare * ERROR at line 1: ORA-06502: PL/SQL: numeric or value error ORA-06512: at line 5
Vordefinierte Exceptions Alle Exceptions können entweder als vordefinierte oder benutzerdefinierte Exception eingestuft werden. Vordefinierte Exceptions werden automatisch ausgelöst; z.B. resultiert der Bezug einer SQL-Anweisung auf eine nicht vorhandene Tabelle in einem Oracle- Fehler. Als Beispiel nehmen Sie an, Ihr PL/SQL-Unterprogramm enthalte eine SELECT- Anweisung, die unter bestimmten Umständen keine Zeilen zurückgibt, wodurch die Exception NO_DATA_FOUND ausgelöst wird. Vordefinierte Exceptions besitzen sprechende Namen. Es folgen einige vordefinierte Exceptions, die Ihnen bei der Entwicklung einer Oracle-Anwendung begegnen können: ● ● ● ● ●
Im folgenden Abschnitt werden die Bedingungen betrachtet, die vordefinierte Exceptions auslösen.
Die DUP_VAL_ON_INDEX-Exception Die DUP_VAL_ON_INDEX-Exception wird ausgelöst, wenn eine SQL-Anweisung versucht, doppelte Wert in einer Spalte zu erzeugen, für die ein eindeutiger Index besteht. Zur Veranschaulichung enthält Listing 14.3 einen anonymen Block, der versucht, die Course- Tabelle so zu aktualisieren, dass alle Spalten den gleichen Wert für Course_ID haben und somit die Exception DUP_VAL_ON_INDEX auslöst. Listing 14.3: Behandlung der dup_val_on_index-Exception SQL>
declare
begin update Course set Course_ID = 101; exception when DUP_VAL_ON_INDEX then
Die INVALID_NUMBER-Exception Die INVALID_NUMBER-Exception wird ausgelöst, wenn eine SQL-Anweisung eine ungültige Zahl angibt. So zeigt Listing 14.4 z.B. einen anonymen SQL-Block, der versucht, die Spalte Additional_Fees der Course-Tabelle zu aktualisieren. Die Exception wird ausgelöst, weil die Funktion to_number versucht, den in der Zeichenkettenvariablen Bogus_Value gespeicherten Wert in eine Zahl umzuwandeln. Listing 14.4: Behandlung der invalid_number-Exception SQL> declare Bogus_Value begin
varchar2(30) := 'NOT A NUMBER';
update Course set Additional_Fees = to_number(Bogus_Value); exception when INVALID_NUMBER then dbms_output.put_line('INVALID_NUMBER exception raised'); end; / INVALD_NUMBER exception raised PL/SQL procedure successfully completed.
Die NO_DATA_FOUND-Exception Die NO_DATA_FOUND-Exception wird ausgelöst, wenn eine SELECT-Anweisung keine Zeilen zurückgibt, wie es in Listing 14.5 gezeigt wird. Listing 14.5: Die no_data_found-Exception wird nicht von einem Exception-Handler behandelt SQL> declare Course_Rec Course%ROWTYPE; begin select * into Course_Rec from Course where Course_ID = 777; end; / declare * ERROR at line 1: ORA-01403: no data found ORA-06512: at line 5 Nachdem Sie für NO_DATA_FOUND (in Zeile 11) einen Exception-Handler hinzugefügt haben, gibt PL/SQL nicht länger den Fehler - ORA-01403: no data found - an die aufrufende Umgebung zurück, wie es in Listing 14.6
gezeigt wird. Listing 14.6: Behandlung der no_data_found-Exception SQL> declare Course_Rec begin
Course%ROWTYPE;
select * into Course_Rec from Course where Course_ID = 777; exception when NO_DATA_FOUND then dbms_output.put_line('No data when OTHERS then NULL; end; / No data returned PL/SQL procedure
successfully
returned');
completed.
Die TOO_MANY_ROWS-Exception In der PL/SQL-Umgebung kann eine SELECT-Anweisung nicht mehr als eine Zeile abrufen, ohne die TOO_MANY_ROWS-Exception auszulösen. Um von einer Abfrage eine beliebige Anzahl Zeilen abzurufen, können Sie einen Cursor einsetzen, den Sie sich wie ein Fenster auf das Abfrageergebnis vorstellen können. Listing 14.7 zeigt an einem Beispiel, wie ein Exception-Handler für die TOO_MANY_ROWS-Exception benutzt wird. Listing 14.7: Behandlung der too_many_rows-Exception SQL> declare Course_Rec begin
Course%ROWTYPE;
select * into Course_Rec from Course where Department_ID =
'BIO';
exception when TOO_MANY_ROWS then dbms_output.put_line('TOO_MANY_ROWS raised - use a cursor'); when OTHERS then NULL; end; / TOO_MANY_ROWS raised - use a cursor PL/SQL procedure successfully completed.
Die VALUE_ERROR-Exception
Die VALUE_ERROR-Exception wird bei einer Reihe von Situationen ausgelöst, die in Beziehung zu Fehlern beim Abschneiden und Konvertieren stehen. In Listing 14.8 wird z.B. ein PL/SQL-Block gezeigt, der versucht, die Zeichenfolge 'More than 5 characters' einer Variablen zuzuweisen, die als VARCHAR2(5) deklariert wurde. Listing 14.8: Behandlung der value_error-Exception SQL> declare xyz varchar2(5); begin xyz
:=
'More than 5 characters';
exception when VALUE_ERROR then dbms_output.put_line('VALUE_ERROR raised'); when NULL;
OTHERS
then
end; / VALUE_ERROR raised
Deklarieren einer Exception Zusätzlich zur Verarbeitung vordefinierter Exceptions können Sie auch anwendungsspezifische Exceptions definieren und in der folgenden Form deklarieren: exception-name EXCEPTION; Der Platzhalter exception-name ist die deklarierte Exception und unterliegt den Einschränkungen bei der Namensvergabe für Objekte unter PL/SQL. Listing 14.9 zeigt an einem Beispiel, wie eine Exception namens Life_Threatening_Fever deklariert wird, die aufgerufen wird, wenn die Körpertemperatur eines Patienten über 106 Grad Fahrenheit (41°C) steigt. Listing 14.9: Deklaration einer anwendungsspezifischen Exception SQL> declare 2 Life_Threatening_Fever exception; 3 Patient_ID Patient.Patient_ID%TYPE; 4 begin 5 6 for Patient_Rec in 7 (select Patient_ID, Body_Temp_Deg_F from Patient) 8 8 if Patient_Rec.Body_Temp_Deg_F > 106.0 then 9 9 Patient_ID := Patient_Rec.Patient_ID; 10 raise Life_Threatening_Fever; 11 11 end if; 12 end loop; 13 13 exception 14
loop
14 when Life_Threatening_Fever then 15 dbms_output.put_line(Patient_ID || ' 16 'threatening fever!'); 17 17 end; 18 / GG9999 has a life threatening fever!
has
a
life
'
||
Erfolg oder Fehler: Auswerten von sqlcode und sqlerrm Sqlcode ist eine PL/SQL-Funktion, die den Oracle-Fehlerstatus der zuvor ausgeführten PL/ SQL-Anweisung zurückgibt. Wird eine SQL-Anweisung ohne Fehler ausgeführt, ist sqlcode gleich 0. Die PL/SQL-Funktion sqlerrm liefert die Fehlermeldung zu einem bestimmten sqlcode. Wird eine SQLAnweisung erfolgreich ausgeführt, ist sqlcode gleich 0 und sqlerrm enthält die Zeichenfolge ORA-0000: normal, successful completion, wie sie in Listing 14.10 gezeigt wird. Listing 14.10: Zugriff auf sqlcode und sqlerrm SQL> declare begin dbms_output.put_line('SQLCODE: ' || to_char(SQLCODE)); dbms_output.put_line('SQLERRM: ' || SQLERRM); end; / SQLCODE: 0 SQLERRM: ORA-0000: normal, successful completion Tritt tatsächlich ein Fehler auf, enthalten sqlcode und sqlerrm den jeweils zutreffenden Code und die entsprechende Meldung - wie es in der Ausgabe von Listing 14.11 aufgeführt wird. Listing 14.11: Zugriff auf sqlcode und sqlerrm im Exception-Block SQL> declare 2 Class_Rec Class%ROWTYPE; 3 3 begin 4 5 select * 6 into Class_Rec 7 from Class; 8 8 exception 9 when OTHERS then 10 dbms_output.put_line('SQLCODE: ' || to_char(SQLCODE)); 11 dbms_output.put_line(SQLERRM); 12 end; 13 / SQLCODE: -1422 ORA-01422: exact fetch returns more than requested number of rows PL/SQL
procedure successfully completed.
Rückgabe von Fehlern mit der Prozedur RAISE_APPLICATION_ERROR Das Paket DBMS_STANDARD stellt die Prozedur RAISE_APPLICATION_ERROR zur Verfügung. Diese
Prozedur können Sie dazu einsetzen, anwendungsspezifische Fehlermeldungen an SQL*Plus, ein PL/SQLUnterprogramm oder eine Client- Anwendung zurückzugeben. Oracle reserviert Fehlercodes im Bereich -20000 bis -20999 für diese benutzerdefinierten Fehler. Veranschaulicht wird dies in Listing 14.12 durch einen PL/SQLBlock, der eine Exception Fever_Out_Of_Range deklariert. Ein Cursor For Loop (in Zeile 6) liest jede Zeile der Patiententabelle. Übersteigt die Temperatur eines Patienten 115 Grad Fahrenheit, wird die Exception Fever_Out_Of_Range (in Zeile 8) ausgelöst. Im Exception-Block ruft der Exception-Handler für Fever_Out_Of_Range RAISE_APPLICATION_ ERROR auf und übergibt ihr den Fehlercode -20000 und eine entsprechende Meldung (in Zeile 14). Listing 14.12: Auslösen einer anwendungsspezifischen Exception SQL> declare 2 Fever_Out_of_Range exception; 3 Patient_ID Patient.Patient_ID%TYPE; 4 begin 5 6 for Patient_Rec in 7 (select Patient_ID, Body_Temp_Deg_F from Patient) loop 8 8 if Patient_Rec.Body_Temp_Deg_F > 115.0 then 9 raise Fever_Out_of_Range; 10 end if; 11 11 end loop; 12 12 exception 13 13 when Fever_Out_of_Range then 14 raise_application_error (-20000, 'Fever is out of the range 65 Deg. F to 115.0 Deg. F'); 15 15 end; 16 / declare * ERROR at line 1: ORA-20000: Fever is out of the range 65 Deg. F to 115 Deg. F ORA-06512: at line 14
14.2 Abrufen von Daten mit einem Cursor Wenn Sie SQL*Plus oder SQL*Plus Worksheet einsetzen, können Sie eine Abfrage stellen, ohne sich Gedanken über die Anzahl der zurückgegebenen Zeilen machen zu müssen. Es spielt keine Rolle, ob die Abfrage keine, eine oder tausend Zeilen zurückgibt. Jedoch gilt dies nicht für PL/SQL-Unterprogramme. Sie können keine gewöhnliche SELECT-Anweisung verwenden, um mehr als eine Zeile abzurufen. Wenn eine SELECTAnweisung in einem PL/SQL-Unterprogramm - ob als anonymer Block, gespeicherte Prozedur oder Trigger mehr als eine Zeile abruft, gibt Oracle eine Fehlermeldung zurück. Offensichtlich ist die Fähigkeit, mehr als eine Zeile abzurufen, so wesentlich, dass es dafür einen Mechanismus geben muss. Das von Oracle zur Erledigung dieser Aufgabe zur Verfügung gestellte Konstrukt ist der Cursor. Cursors werden von Oracle- Dienstprogrammen wie SQL*Plus automatisch erstellt und verwendet.
Ein Cursor stellt einen Mechanismus dar, mit dem programmgesteuert eine beliebige Anzahl Zeilen
mit einer SELECT-Anweisung abgerufen werden kann. Listing 14.13 zeigt, wie Oracle einen Fehler zurückgibt, weil die SELECT-Anweisung mehr als einen Instructor zurückgibt. Listing 14.13: Oracle-Fehler, der aus einer select-Anweisung resultiert, die mehr als eine Zeile zurückgibt SQL>
declare Instructor_ID Instructor.Instructor_ID%type; Last_Name Instructor.Last_Name%type; First_Name Instructor.First_Name%type; begin select Instructor_ID,Last_Name,First_Name into Instructor_ID,Last_Name,First_Name from Instructor order by Instructor_ID; end; / declare * ERROR at line 1: ORA-01422: exact fetch returns more than requested number of rows ORA-06512: at line 6 Sie können sich einen Cursor als ein Fenster auf die Ergebnismenge einer Abfrage vorstellen (siehe Abbildung 14.1). Im Allgemeinen führen Sie beim Einsatz eines Cursor vier Schritte durch: 1. Deklarieren des Cursors. Dem Cursor wird ein Name zugewiesen, und er wird einer zu parsenden SELECT-Anweisung zugeordnet. 2. Öffnen des Cursors. Das Oracle-RDBMS führt den Parse-Vorgang aus. Dieser Vorgang beinhaltet die syntaktische und semantische Analyse der SQL-Anweisung und die Berechnung eines optimalen Ausführungsplans. 3. Lesen der Zeilen des Cursors. Die Werte einer jeden Zeile werden der Umgebung des PL/SQLUnterprogramms übergeben. Die Zeilen werden einzeln oder performanter paketweise (BULK COLLECT, Array Fetch) übergeben. 4. Schließen des Cursors. Alle von Oracle in Verbindung mit dem Cursor beanspruchten Ressourcen werden wieder freigegeben.
Abbildung 14.1: Veranschaulichung eines Cursors Es folgt eine gespeicherte Funktion, die feststellt, ob ein bestimmter Kurs in den Unterrichtsplan des Studenten passt. Die Funktion Schedule_Conflict hat zwei Argumente: Arg_Student_ID und Arg_Class_ID. Sie stellt Konflikte fest, indem nach anderen Kursen im gegenwärtigen Unterrichtsplan des Studenten gesucht wird, die zu den gleichen Zeiten wie der vorgeschlagene Kurs stattfinden. Mit einem Cursor wird diese Abfrage durchgeführt. Listing 14.14 enthält den kompletten Text der Funktion. Listing 14.14: Verwendung eines Cursors in einer gespeicherten Funktion SQL> create or replace function schedule_conflict (arg_student_ID IN number, arg_class_ID IN number) return number is conflicting_classes number := -1; normal number := 0; cursor get_other_classes is select SS.Class_ID from Student_Schedule SS, Class C where SS.Class_ID = C.Class_ID and (C.Semester, C.School_Year, C.Schedule_ID) = (select Semester, School_Year, Schedule_ID from Class where Class_ID = arg_class_ID); Conflicting_Class_ID Class.Class_ID%type; status number; begin -- Need to look at the other classes in the student's schedule -- for the same semester and school year. open get_other_classes; loop fetch get_other_classes into Conflicting_Class_ID; exit when get_other_classes%notfound; end loop; if get_other_classes%rowcount > 0 then status := conflicting_classes; else status := normal; end if; close get_other_classes; return status; end; / Function created. SQL> select Student_ID, Class_ID from Student_Schedule where Student_ID = 10231311; STUDENT_ID CLASS_ID ---------- ---------10231311 104200 10231311 104500 10231311 109100 SQL> select schedule_conflict(10231311,104200) conflict from dual; CONFLICT
----------1
Es folgt eine schrittweise Betrachtung der einzelnen Verarbeitungsschritte. Als erstes wird der Cursor Set_Other_Classes als Verbindung zwischen zwei Tabellen - Student_Schedule und Class deklariert. Zweitens wird der Cursor im ausführbaren Teil der gespeicherten Funktion geöffnet. Drittens liest eine Schleifenanweisung Zeilen vom Cursor, bis keine Zeilen mehr empfangen werden. Wie Sie am Ende von Listing 14.14 sehen, ergibt eine Abfrage der Tabelle Student_Schedule für Student_ID 10231311, dass dieser Student für drei Kurse eingeschrieben ist: 104200, 104500 und 109100. Die Funktion wird durch die abschließende SELECT-Anweisung aufgerufen; sie überprüft, ob der Kurs 104200 für den Studenten 10231311 zu einer Überschneidung führt. Dies ist natürlich der Fall, denn der Student ist bereits für diesen Kurs eingeschrieben. Die Funktion gibt -1 zurück, was den Zustand sich überschneidender Kurse anzeigt.
Deklarieren eines Cursors Jeder Cursor muss deklariert werden, ehe er genutzt werden kann. Einen Cursor zu deklarieren, bedeutet ihn zu benennen und die SELECT-Anweisung zu bestimmen, die dem Cursor zugeordnet ist.
Die grundlegende Syntax zur Deklaration eines Cursors unter PL/SQL lautet folgendermaßen: CURSOR cursor-name [(parameter1 parameter1-datatype [:= default1] ... [, parameterN parameterN-datatype [:= defaultN] ] ) ] IS select-stmt; Die Bedeutungen der Platzhalter sind: ●
● ● ● ● ● ● ●
cursor-name ist der Name des Cursor und unterliegt den Vorgaben von Oracle für die Benennung von Objekten. parameter1 ist der Name des ersten an den Cursor übergebenen Parameters. Parameter sind optional. parameter1-datatype ist der Datentyp von parameter1. default1 ist ein optionaler Standardwert für parameter1. parameterN ist der Name des letzten an den Cursor übergebenen Parameters. parameterN-datatype ist der Datentyp von parameterN. defaultN ist ein optionaler Standardwert für parameterN. select-stmt ist eine gültige SELECT-Anweisung, die dem deklarierten Cursor zugeordnet ist.
Listing 14.15 zeigt zwei unterschiedliche Cursors - der erste Cursor hat drei Parameter ohne Standardwerte, und der zweite Cursor hat drei Parameter mit bestimmten Standardwerten. Listing 14.15: Cursor mit und ohne Standardwerte für Parameter cursor patients_with_hypertension (patient_age number, normal_dyastolic, normal_systolic) is
select patient_id, age, dyastolic, systolic from patient where dyastolic > normal_dyastolic * (age+200)/200 and systolic > normal_systolic * (age+200)/200; cursor patients_with_hypertension (patient_age number default 55, normal_dyastolic number default 70, normal_systolic number default 130) is select patient_id, age, dyastolic, systolic from patient where dyastolic > normal_dyastolic * (age+200)/200 and systolic > normal_systolic * (age+200)/200;
Wenn Sie einen der Precompiler von Oracle verwenden, - z.B. Pro*C - müssen Sie einen Cursor einsetzen, um über eine SELECT-Anweisung mehr als eine Zeile abzurufen.
Öffnen eines Cursors Bevor Sie die Zeilen einer Tabelle unter Verwendung eines Cursors lesen können, müssen Sie den Cursor öffnen. Ist dieser geöffnet, sind Sie in der Lage, mit der Anweisung FETCH eine oder mehrere Zeilen (BULK COLLECT) aus einer Tabelle abzurufen.
Die Syntax für das Öffnen eines parameterlosen Cursors lautet: open cursor_name; Der Platzhalter cursor_name steht für den Namen des Cursors. Wurde der Cursor mit Parametern deklariert, müssen Sie, wenn Sie den Cursor öffnen, für jeden Parameter eine PL/SQL-Variable oder einen Literalwert bereitstellen, wie es im Listing 14.16 gezeigt wird. Bei einer Anwendung, die von vielen Benutzern gleichzeitig genutzt wird, sollten Sie grundsätzlich in PL/SQL mit parametrierten Cursors, in anderen Programmiersprachen mit so genannten Bind Variablen arbeiten. Wenn Sie diesen Grundsatz nicht beachten und stattdessen viele Varianten einer SQL-Anweisungen absetzen, die sich nur durch die konkreten Werte in Form von Literalen unterscheiden, reduzieren Sie die Antwortzeit des Datenbankservers beträchtlich. Oracle9i muss dann für jede SQL-Anweisung mit bisher nicht verwendeten Literalen Speicherplatz allozieren, beim Parsen jedes Mal zusätzlich CPU-Zeit verbrauchen und einen neuen optimalen Ausführungsplan berechnen. Außerdem steigt die Wahrscheinlichkeit, dass der Parse-Vorgang auf eine Sperre warten muss (library cache lock des Shared Pool) deutlich an. Ihre Anwendung würde also unnötig langsam werden, weil viele Datenbanksitzungen im Wettbewerb um Sperren stehen, die für das Parsen nötig sind. Listing 14.16: Deklaration und Öffnen eines parametrierten Cursors SQL> declare cursor patients_with_hypertension
(patient_age number, normal_dyastolic number) is select patient_id from patient where dyastolic > normal_dyastolic * (age+200)/200 and systolic > 180; Patient_ID Patient.Patient_ID%type; begin open patients_with_hypertension (45, 80); end; / PL/SQL procedure successfully completed Wurde der Cursor mit Parametern deklariert - aber ohne angegebene Standardwerte für diese Parameter müssen Sie für jeden Parameter eine PL/SQL-Variable oder einen Literalwert bereitstellen. Listing 14.17 zeigt, wie Oracle die Anweisung zum Öffnen des Cursors zurückweist, wenn die erforderlichen Argumente nicht bereitgestellt werden. Listing 14.17: Oracle meldet einen Fehler, wenn für den Cursor keine Argumente geliefert werden SQL> declare 2 2 cursor patients_with_hypertension 3 (patient_age number, 4 normal_dyastolic number, 5 normal_systolic number) is 6 select patient_id 7 from patient 8 where 9 dyastolic > normal_dyastolic * (age+200)/200 and 10 systolic > 180; 11 11 Patient_ID Patient.Patient_ID%type; 12 12 begin 13 13 open patients_with_hypertension; 14 14 end; 15 / declare * ERROR at line 1: ORA-06550: line 13, column 1: PLS-00306: wrong number or types of arguments in call to 'PATIENTS_WITH_HYPERTENSION' ORA-06550: line 13, column 1: PL/SQL: SQL Statement ignored Wurde der Cursor mit Parametern deklariert - und für diese Parameter Standardwerte angegeben - ist es nicht notwendig, dass Sie für jeden Parameter eine PL/SQL-Variable oder einen Literalwert bereitstellen.
Lesen einer Tabelle mit einem Cursor Ist der Cursor einmal geöffnet worden, werden Zeilen mit der FETCH-Anweisung gelesen und in einer PL/SQL-
Variablen speichert. Üblicherweise werden Sie Zeilen innerhalb einer Schleife lesen wollen. Zur Veranschaulichung enthält Listing 14.18 einen anonymen Block, in dem Zeilen aus der Patient-Tabelle gelesen werden. Listing 14.18: Lesen einer Tabelle mit einem Cursor SQL> declare cursor patients_with_hypertension (patient_age number default 55, normal_dyastolic number default 70, normal_systolic number default 130) is select patient_id from patient where dyastolic > normal_dyastolic * (age+200)/200 and systolic > normal_systolic * (age+200)/200; Patient_id Patient.Patient_ID%type; begin open patients_with_hypertension; loop fetch patients_with_hypertension into Patient_id; exit when patients_with_hypertension%notfound; dbms_output.put_line(patient_id); end loop; end; / PL/SQL procedure successfully completed. Wenn alle Zeilen gelesen worden sind, sollten Sie die EXIT-Anweisung verwenden, um die Schleife zu beenden.
Die Syntax lautet folgendermaßen: EXIT [label][WHEN condition]; Die Bedeutungen der Platzhalter sind: ● ●
label ist der optionale Name des Labels, das die zu verlassende Schleife angibt. condition ist eine PL/SQL-Bedingung, die einen Boolean-Wert zurückgibt.
Ein Cursor hat vier Attribute: %ROWCOUNT, %FOUND, %NOTFOUND und %ISOPEN. Auf diese Attribute wird verwiesen, indem sie hinter den Namen des Cursor gesetzt werden. Um eine Schleife mit der EXIT-Anweisung zu beenden, verweisen Sie auf das %NOTFOUND-Attribut eines Cursors wie folgt: exit when get_instructors%notfound;
Schließen eines Cursors Aus zwei Gründen müssen Sie einen Cursor schließen: ● ●
um ihn mit anderen Parametern wieder öffnen zu können, um die vom Cursor belegten Ressourcen freizugeben.
Wenn ein PL/SQL-Programm einen Cursor nicht schließt, schließt Oracle diesen entweder durch Beenden oder ein DISCONNECT, wenn das Unterprogramm sich von der Oracle-Datenbank abmeldet. Einen Cursor zu schließen ist einfach.
Die Syntax lautet folgendermaßen: close cursor_name; Der Platzhalter cursor_name steht für den Namen des Cursors. Listing 14.19 zeigt, wie einem Cursor neue Parameter zugewiesen werden, indem der Cursor geschlossen, die Parameterwerte geändert und der Cursor wieder geöffnet wird. Der Cursor patients_with_hypertension wird zur Veranschaulichung dieses Vorgangs verwendet. Als erstes wird der Cursor mit dem Wert 50 für den Parameter patient_age und dem Wert 80 für den Parameter normal_dyastolic (Zeile 14) geöffnet. Die Zeilen werden in einer Schleife (Zeile 16) gelesen und der Cursor geschlossen. Dann wird der Cursor mit dem Wert 40 für den Parameter patient_age und dem Wert 70 für den Parameter normal_dyastolic (Zeile 22) erneut geöffnet. Die Ergebnisse der zweiten Schleife sind unterschiedlich. Listing 14.19: Änderung der Argumente eines Cursors durch Schließen und erneutes Öffnen des Cursors SQL> declare 2 2 Patient_ID Patient.Patient_ID%type; 3 Age Patient.Age%type; 4 Dyastolic Patient.Dyastolic%type; 5 5 cursor patients_with_hypertension 6 (patient_age number, 7 normal_dyastolic number) is 8 select patient_id, age, dyastolic 9 from patient 10 where 11 dyastolic > normal_dyastolic * (age+200)/200; 12 12 begin 13 14 open patients_with_hypertension (50, 80); 15 15 loop 16 16 fetch patients_with_hypertension 17 into Patient_ID, Age, Dyastolic; 18 exit when patients_with_hypertension%notfound; 19 19 dbms_output.put_line('With age=50, dyas=80: ' || Patient_ID); 20 20 end loop; 21 21 close patients_with_hypertension; 22 22 open patients_with_hypertension (40, 70); 23
23 loop 24 24 fetch patients_with_hypertension 25 into Patient_ID, Age, Dyastolic; 26 exit when patients_with_hypertension%notfound; 27 27 dbms_output.put_line('With age=40, dyas=70: ' || Patient_ID); 28 28 end loop; 29 29 close patients_with_hypertension; 30 30 end; 31 / With age=50, dyas=80: N3393 With age=40, dyas=70: A2002 With age=40, dyas=70: N3393 With age=40, dyas=70: E3893 PL/SQL procedure successfully completed
Cursor-gesteuerte for-Schleifen Als Alternative zum Öffnen, Lesen und Schließen eines Cursor bietet Oracle einen anderen Ansatz - die Cursorgesteuerte FOR-Schleife. Mit dieser Cursor-gesteuerten FOR- Schleife deklariert Oracle implizit eine Variable den Schleifenindex - die vom gleichen Datentyp ist wie der Datensatz des Cursors, was in Listing 14.20 gezeigt wird. Listing 14.20: Verwendung einer Cursor-gesteuerten for-Schleife SQL> declare 2 2 Instructor_ID Instructor.Instructor_ID%type; 3 Last_Name Instructor.Last_Name%type; 4 First_Name Instructor.First_Name%type; 5 5 cursor Get_Associate_Profs is 6 select Instructor_ID, Last_Name, First_Name 7 from Instructor 8 where Position = 'ASSOCIATE PROFESSOR' 9 order by Instructor_ID; 10 10 begin 11 12 for Get_Associate_Profs_Rec in Get_Associate_Profs loop 13 13 dbms_output.put_line('Last name: ' || Get_Associate_Profs_Rec.Last_Name); 14 14 end loop; 15 15 end; 16 / Last name: NILAND Last name: DANIELS Last name: RESTON Last name: JASON
Last name: ANGELO Last name: CHERNOW Last name: YOUNG PL/SQL procedure successfully completed. Der FOR nachgestellte Name ist ein implizit deklarierter Datensatz (RECORD), auf den nur innerhalb der Schleife zugegriffen werden kann. Die Abarbeitung der Schleife endet automatisch, nachdem die letzte Zeile in der Ergebnismenge der select-Anweisung verarbeitet wurde.
Cursor-Attribute %FOUND und %NOTFOUND In den vorangegangenen Beispielen wurde das Attribut %NOTFOUND dazu verwendet, festzustellen, ob eine FETCH-Anweisung eine Zeile einlas oder nicht. Nachdem alle Zeilen der Ergebnismenge eingelesen wurden und die letzte FETCH-Anweisung keine Zeile mehr einlesen kann, ergibt die Auswertung von %NOTFOUND TRUE.
Vor dem ersten Aufruf der FETCH-Anweisung hat %NOTFOUND den Wert NULL. Enthält Ihr PL/SQL-Programm eine cursor-gesteuerte Schleife, mit einer EXIT Bedingung, die %NOTFOUND mit TRUE vergleicht, geht das Programm in eine Endlosschleife, falls die FETCH-Anweisung nie erfolgreich ausgeführt wird. Sie sollten daher die EXIT Bedingung wie folgt formulieren (cursor_name ist der Name eines Cursor): EXIT WHEN cursor_name%NOTFOUND OR cursor_name%NOTFOUND IS NULL;
Ermitteln der Zeilenanzahl mit Hilfe von %ROWCOUNT Sie benötigen keinen Zähler, um die Anzahl der aus einem Cursor eingelesenen Zeilen zu verfolgen. Stattdessen verweisen Sie auf das Attribut %ROWCOUNT des Cursors. Wie Sie in Zeile 13 des Listing 14.21 sehen können, gibt %ROWCOUNT die laufende Anzahl der eingelesenen Zeilen zurück. Listing 14.21: Ermittlung der Anzahl der aus einem Cursor gelesenen Zeilen mit Hilfe von %rowcount SQL> declare 2 2 Instructor_ID Instructor.Instructor_ID%type; 3 Last_Name Instructor.Last_Name%type; 4 First_Name Instructor.First_Name%type; 5 5 cursor Get_Associate_Profs is 6 select Instructor_ID, Last_Name, First_Name 7 from Instructor 8 where Position = 'ASSOCIATE PROFESSOR' 9 order by Instructor_ID; 10 begin 11 12 for Get_Associate_Profs_Rec in Get_Associate_Profs loop 13 13 dbms_output.put_line ('Rowcount: ' || Get_Associate_Profs%rowcount); 14 14 end loop; 15 15 end; 16 /
Rowcount: 1 Rowcount: 2 Rowcount: 3 Rowcount: 4 Rowcount: 5 Rowcount: 6 Rowcount: 7 PL/SQL procedure successfully completed. Anstatt eine Schleife zu verlassen, wenn der Cursor keine Zeilen mehr enthält, können Sie, wie in Listing 14.22 gezeigt, für das Erreichen einer bestimmten Anzahl in %ROWCOUNT eine Abbruchbedingung festlegen (siehe Zeile 13). Listing 14.22: Verlassen einer Cursor-gesteuerten Schleife, nachdem eine bestimmte Anzahl von Zeilen gelesen wurde SQL> declare 2 2 Instructor_ID Instructor.Instructor_ID%type; 3 Last_Name Instructor.Last_Name%type; 4 First_Name Instructor.First_Name%type; 5 5 cursor Get_Associate_Profs is 6 select Instructor_ID, Last_Name, First_Name 7 from Instructor 8 where Position = 'ASSOCIATE PROFESSOR' 9 order by Instructor_ID; 10 begin 11 12 for Get_Associate_Profs_Rec in Get_Associate_Profs loop 13 13 exit when Get_Associate_Profs%rowcount >= 5; 14 dbms_output.put_line ('Rowcount: ' || Get_Associate_Profs%rowcount); 15 15 end loop; 16 16 end; 17 / Rowcount: 1 Rowcount: 2 Rowcount: 3 Rowcount: 4 PL/SQL procedure successfully completed. In Listing 14.22 sehen Sie, wie Sie %ROWCOUNT einsetzen können, um die Anzahl der vom Cursor eingelesenen Zeilen zu verfolgen.
14.3 Datenbanktrigger Mit dem Einsatz des bisher über SQL und PL/SQL Gelernten ist es an der Zeit, die Welt der Datenbanktrigger zu erkunden. In einem Informationssystem, das eine nicht- relationale Datenbank zur Grundlage hat, werden die Geschäftsregeln der Organisation durch Anwendungs-Software umgesetzt. So könnte z.B. eine Geschäftsregel lauten, dass eine Bestellung über eine benötigte Menge ins System eingegeben wird, sobald eine Inventur für einen Artikel einen Bestand ergibt, der unter der Lagermenge dieses Artikels liegt. Diese Regel würden Sie üblicherweise implementieren, indem Sie unter Cobol oder einer anderen Programmiersprache eine Routine schreiben würden, die an entsprechender Stelle im Programm aufgerufen würde. Diese Methode birgt jedoch
einige Probleme: ● ●
Die Regel wird nur dann durchgesetzt, wenn ein Anwendungs- oder Hilfsprogramm diese Routine aufruft. Eine Liste der Algorithmen zu erhalten, die zur Durchsetzung von Regeln verwendet werden, ist schwierig. Sie sind auf möglicherweise unvollständige Unterlagen angewiesen.
Oracle, wie auch andere moderne RDBMS, stellt Mechanismen, so genannte Datenbanktrigger, zur Verfügung, die die Aufgabe erleichtern, Geschäftsregeln einer Organisation zu implementieren. Ein Datenbanktrigger besteht aus einer Gruppe von PL/ SQL-Anweisungen, die ausgeführt werden, wenn eine DELETE-, UPDATEoder INSERT- Anweisung auf eine Tabelle angewendet wird. Einen Datenbanktrigger können Sie zur Erledigung folgender Aufgaben einsetzen: ● ●
●
●
Durchsetzung ausgeklügelter Sicherheitsrichtlinien. Ändern eines Spaltenwerts auf der Grundlage von Werten in anderen Spalten derselben oder einer anderen Tabelle. Komplexe Validierung von Spaltenwerten - so könnte es z.B. notwendig sein, dass Sie einen Spaltenwert mit dem Aggregat eines Spaltenwerts einer anderen Tabelle vergleichen müssen. Dokumentieren der Änderungen an einem Datensatz durch Schreiben der geänderten Werte in eine andere Tabelle.
Ein Trigger auf einer Tabelle ist eine Gruppe von PL/SQL-Anweisungen, die ausgeführt werden, wenn der Inhalt einer Tabelle in irgendeiner Weise verändert wird - entweder durch eine INSERT-, UPDATE- oder DELETE-Anweisung. In dieser Lektion werden die Details zur Erstellung eines Triggers zu verschiedenen Zwecken untersucht.
Erstellen eines Triggers auf einer Tabelle Die Oracle-Anweisung CREATE TRIGGER erstellt (oder ersetzt) einen Trigger, der ausgeführt wird, wenn Daten in einer Tabelle verändert werden.
Die Syntax für die CREATE-TRIGGER-Anweisung lautet wie folgt: CREATE [OR REPLACE] TRIGGER trigger-name {BEFORE | AFTER} triggering-event ON table-name [FOR EACH ROW] [WHEN (condition)] PL/SQL-block Die Bedeutungen der Platzhalter sind: ●
● ● ●
●
trigger-name ist der Name des zu erstellenden Triggers und unterliegt den Namenskonventionen von Oracle. triggering-event ist entweder INSERT, UPDATE oder DELETE entsprechend den drei DML- Anweisungen. table-name ist der Name der dem Trigger zugeordneten Tabelle. FOR EACH ROW ist eine optionale Klausel, die bei ihrer Verwendung den Trigger für jede betroffene Zeile auslöst. condition ist eine optionale Oracle-Bedingung vom Typ Boolean, die TRUE sein muss, damit der Trigger
●
ausgeführt wird. PL/SQL-block ist der PL/SQL-Block, der ausgeführt wird, wenn der Trigger ausgelöst wird - er wird auch als Trigger-Rumpf bezeichnet.
Die folgenden Abschnitte erläutern die Verwendung der CREATE-TRIGGER-Anweisung.
Trigger auf Anweisungsebene und Zeilenebene Ein Trigger auf einer Tabelle gehört in eine der beiden folgenden Kategorien: ●
●
Trigger auf Anweisungsebene enthalten nicht die Klausel FOR EACH ROW in der CREATE- TRIGGERAnweisung. Trigger auf Zeilenebene enthalten die Klausel FOR EACH ROW in der CREATE-TRIGGER- Anweisung.
Ein Trigger auf Anweisungsebene wird durch das auslösende Ereignis nur einmal ausgelöst und hat keinen Zugang zu den Spaltenwerten der einzelnen vom Trigger betroffenen Zeilen. Ein Trigger auf Zeilenebene wird für jede vom Trigger betroffene Zeile ausgelöst und kann auf die Ausgangswerte und neuen Werte der durch eine SQL-Anweisung bearbeiteten Spalte zugreifen. Im Allgemeinen verwenden Sie Trigger auf Anweisungsebene zur Bearbeitung von Informationen über die SQLAnweisung, die den Trigger ausgelöst hat - z.B. wer ihn ausgelöst hat und wann er ausgelöst wurde. Typischerweise verwenden Sie einen Trigger auf Zeilenebene, wenn Sie die Spaltenwerte einer Zeile kennen müssen, um eine Geschäftsregel umzusetzen.
Zugriff auf Spaltenwerte im Trigger-Rumpf Innerhalb eines Trigger-Rumpfs kann ein Trigger auf Zeilenebene auf den Spaltenwert einer Zeile zugreifen, der zum Zeitpunkt der Auslösung des Triggers bestand. Diese Werte hängen davon ab, welche SQL-Anweisung den Trigger ausgelöst hat. ●
●
●
Bei einer INSERT-Anweisung sind die Werte, die eingefügt werden, in :new.column_ name enthalten. Column_name steht für eine Spalte der Tabelle. Bei einer UPDATE-Anweisung ist der ursprüngliche Spaltenwert in :old.column_name und der neue Spaltenwert in :new.column_name enthalten. Bei einer DELETE-Anweisung sind die Spaltenwerte der zu entfernenden Zeile in :old.column_name enthalten.
Auslösen von Ereignissen Wenn Sie einen Trigger auf einer Tabelle erstellen, legen Sie fest, welches Ereignis den Trigger auslöst. Die drei möglichen Ereignisse sind: ● ● ●
Das Einfügen einer neuen Zeile in eine Tabelle mit der INSERT-Anweisung. Das Aktualisieren einer oder mehrerer Zeilen durch eine UPDATE-Anweisung. Das Entfernen einer oder mehrerer Zeilen mit einer DELETE-Anweisung.
Außerdem lassen sich diese drei Ereignisse derart kombinieren, dass ein Trigger immer dann ausgelöst wird, wenn eine INSERT-, UPDATE- oder DELETE-Anweisung ausgeführt wird, wie es in Listing 14.23 gezeigt wird. Listing 14.23: Deklaration eines Triggers, der bei jedem möglichen Ereignis ausgelöst wird SQL> create or replace trigger Block_Trade_After_All After 2 insert or update or delete on Tab1 3 for each row 4
BEFORE- und AFTER-Trigger Ein BEFORE-Trigger auf Zeilenebene wird vor Durchführung der auslösenden Änderung abgearbeitet. Somit können Sie einen BEFORE-Trigger auf Zeilenebene zur Änderung von Spaltenwerten einer Zeile einsetzen. Ein AFTER-Trigger auf Zeilenebene wird nach Auftreten des auslösenden Ereignisses abgearbeitet. Mit einem AFTER-Trigger auf Zeilenebene können Sie keine Spaltenwerte ändern.
Mögliche Trigger für eine Tabelle Auf der Grundlage aller in einer CREATE-TRIGGER-Anweisung möglichen Permutationen, kann eine Tabelle bis zu 12 verschiedene Trigger aufweisen: ●
●
Sechs Trigger auf Zeilenebene für BEFORE DELETE, BEFORE INSERT, BEFORE UPDATE, AFTER DELETE, AFTER INSERT und AFTER UPDATE Sechs Trigger auf Anweisungsebene für BEFORE DELETE, BEFORE INSERT, BEFORE UPDATE, AFTER DELETE, AFTER INSERT und AFTER UPDATE
Wenn Sie den Einsatz eines Entity-Relationship-Modeling-Tools für den Datenbankentwurf planen was ratsam ist - werden Sie feststellen, dass die meisten Werkzeuge automatisch Datenbanktrigger auf Basis der von Ihnen definierten Primär- und Fremdschlüssel generieren. Einige Tools, wie ERwin von LogicWorks, erstellen Trigger entweder im Datenbankkatalog oder speichern Trigger in einer Skriptdatei. Wenn Sie letztere Methode wählen, können Sie das Erstellungsskript für den Trigger verändern, indem Sie jedem generierten Trigger anwendungsspezifische Geschäftsregeln hinzufügen. Nur weil Sie alle 12 Trigger-Arten für eine Tabelle erstellen können, heißt dies nicht, dass Sie auch alle verwenden müssen.
Oracle unterstützt mehrere gleichartige Trigger für ein und dieselbe Tabelle. Die Ausführungsreihenfolge gleichartiger Trigger ist undefiniert. Der folgende Teil der Lektion untersucht einige Verwendungsmöglichkeiten von Triggern.
Spaltenwerte mit Hilfe eines Triggers auf Gültigkeit prüfen
Als Datenbankadministrator einer Kreditkartengesellschaft sind Sie für die Umsetzung der Kreditrichtlinien durch Datenbanktrigger verantwortlich. Untersuchungen des Unternehmens haben ergeben, dass die Wahrscheinlichkeit eines Kreditkartenbetrugs mehr als 80 Prozent beträgt, wenn innerhalb von drei Tagen auf einem einzelnen Konto Belastungen von mehr als $ 1000 zusammenkommen. Der Betriebsleiter möchte, dass alle Konten, auf die diese Kriterien zutreffen, in einer gesonderten Tabelle aufgeführt werden, in der sie dann genau untersucht werden können. Zur Erledigung dieser Aufgabe erstellen Sie den Trigger für die Tabelle Credit_Card_Log, der vor dem Einfügen einer Zeile ausgelöst wird. Der Trigger prüft, ob der Gesamtbetrag der Belastungen der letzten drei Tage für eine bestimmte Kartennummer $ 1000 übersteigt und führt in diesem Fall ein INSERT in die Tabelle Credit_Charge_Attempt_Log aus, in der dieser Datensatz dann von Kreditsachbearbeitern untersucht werden kann. Listing 14.24 veranschaulicht die Erstellung dieses Triggers. Listing 14.24: Erzeugung eines before-insert-Triggers SQL> create or replace trigger Credit_Charge_Log_Ins_Before before insert on Credit_Charge_Log for each row declare total_for_past_3_days number; begin -- Check the credit charges for the past 3 days. -- If they total more than $1000.00, log this entry -- in the Credit_Charge_Attempt_Log for further handling. select sum(amount) into total_for_past_3_days from Credit_Charge_Log where Card_Number = :new.Card_Number and Transaction_Date >= sysdate-3; if total_for_past_3_days > 1000.00 then insert into Credit_Charge_Attempt_Log (Card_Number, Amount, Vendor_ID, Transaction_Date) values (:new.Card_Number, :new.Amount, :new.Vendor_ID, :new.Transaction_Date); end if; end; / Trigger created. Listing 14.25 zeigt den Inhalt der Tabelle Credit_Charge_Log vor Einfügen einer weiteren Zeile. Sie erkennen aus dem Listing, dass der Kreditkarte mit der Kartennummer 0944583312453477 zwischen 18. und 20. Juni bereits ca. $ 1800 belastet wurden. Listing 14.25: Inhalt der Credit_Charge_Log-Tabelle, bevor die nächste Zeile eingefügt wird SQL> select * from credit_charge_log; CARD_NUMBER AMOUNT VENDOR_I TRANSACTI ------------------ ------ -------- --------8343124443239383 128.33 12345678 19-JUN-95 9453128834232243 83.12 98765432 18-JUN-95 4644732212887321 431.1 18181818 19-JUN-95 0944583312453477 211.94 09090909 18-JUN-95 0944583312453477 413.81 08080808 18-JUN-95 0944583312453477 455.31 91919191 19-JUN-95 0944583312453477 225 12341234 20-JUN-95
0944583312453477
512.22 12341234 20-JUN-95
Um den Trigger zu testen, fügen Sie einige Zeilen in die Tabelle Credit_Charge_Log ein, wie es in Listing 14.26 gezeigt wird. Vor dem Einfügen einer Zeile in die Tabelle für die Kartennummer 0944583312453477 wird der Trigger ausgelöst. Die Tabelle wird abgefragt, ob die Belastungen für die Kartennummer innerhalb der letzten drei Tage $ 2000 übersteigt. Ist dies der Fall, wird der Tabelle Credit_Charge_Attempt_Log eine Zeile hinzugefügt, wie Sie es in Listing 14.26 (Zeile 1) sehen können. Weil die Kartennummer 0944583312453477 innerhalb der letzten drei Tage mit mehr als $ 2000 belastet wurde, fügt der Trigger eine Zeile in die Tabelle Credit_Charge _Attempt_Log ein. Listing 14.26: Der Trigger wird ausgelöst, wenn eine Zeile in die Credit_Charge_Log-Tabelle eingefügt wird SQL> insert into Credit_Charge_Log (Card_Number, Amount, Vendor_ID, Transaction_Date) values ('0944583312453477', 128.28, '43214321', '20-JUN-95'); 1 row created. SQL> select * from Credit_Charge_Attempt_Log; CARD_NUMBER AMOUNT VENDOR_I TRANSACTI ------------------ ------ -------- --------0944583312453477 128.28 43214321 20-JUN-95
Sicherheit mit Hilfe eines Triggers implementieren Nachfolgend sehen Sie ein Beispiel, wie Sie mit Hilfe eines Trigger Sicherheitsrichtlinien umsetzen können. Die Datenbank der Acme Corporation ist so angelegt, dass bei einer Auslieferung eine Zeile in die Tabelle Shipment hinzugefügt werden muss. Die Tabelle Shipment enthält eine Spalte Manual_Check, in der angegeben ist, ob der Angestellte im Auslieferungslager telefonisch die Richtigkeit der Lieferungsanforderung bestätigen muss. Zur Reduzierung von Unterschlagungen lautet die Richtlinie der Firma, dass der Angestellte im Auslieferungslager jede Lieferungsanforderung bestätigen muss, die nach Ende der regulären Arbeitszeit um 17.00 Uhr eingeht. Als DBA sind Sie für die Umsetzung dieser Richtlinie verantwortlich. Wie in Listing 14.27 zu sehen ist, können Sie einen Trigger Shipment_Ins_Before erstellen, der vor der Ausführung einer INSERT-Anweisung auf die Tabelle Shipment ausgelöst wird. Der Trigger- Rumpf besteht aus einer einzigen PL/SQL-Anweisung, die den Wert der Spalte Manual_Check auf Y setzt. Zusätzlich entscheiden Sie sich für die Verwendung einer WHENKlausel, damit der Trigger nur nach 17.00 Uhr ausgelöst wird. Listing 14.27: Erzeugung eines Triggers, der eine Bedingung erfüllen muss, bevor er ausgelöst wird SQL> create or replace trigger Shipment_Ins_Before before 2 insert on Shipment 3 for each row 4 when (to_number(to_char(sysdate,'HH24')) > 17) 5 declare 6 begin 7 :new.Manual_Check := 'Y'; 8 end; 9 / Trigger created. Nachdem der Trigger erstellt wurde, können Sie ihn testen. Wie aus Listing 14.28 ersichtlich, ist die aktuelle Zeit nach 17.00 Uhr - es ist 19.00 Uhr. Wird der Tabelle Shipment eine Zeile hinzugefügt, wird die Spalte Manual_Check wie beabsichtigt auf Y gesetzt.
Listing 14.28: Der Trigger prüft die Zeit und wird anschließend ausgelöst SQL> select to_char(sysdate,'HH24') hour from dual; HOUR ---22 SQL> insert into Shipment 2 (Shipment_ID, Product_Code, Quantity, Customer_ID) 3 values 4 ('SHIP1001', 'PROD123', 100, 'CUST999'); 1 row created. SQL> select * from Shipment; SHIPMENT_ID PRODUCT_CODE QUANTITY CUSTOMER_ID M ENTERED_BY ----------- ------------ -------- ----------- - ---------SHIP1001 PROD123 100 CUST999 Y
Automatisches Nummerieren mit einem Trigger Nahezu alle Anwendungen vergeben Nummern wie z.B. Kundennummern und Auftragsnummern. Das es dem Anwender nicht zuzumuten ist, sich eine Nummer auszudenken bzw. die Nummer bestimmten Kriterien genügen muss, werden diese Nummern automatisch generiert. Am besten geschieht dies mit einem BEFORE INSERT Trigger. Das Anwendungsprogramm übergibt keinen Wert für die Nummer, die gleichzeitig der Primärschlüssel ist. Unter Nutzung einer Sequence generiert der Trigger eine Nummer und trägt diese als Primärschlüssel ein. Das Anwendungsprogramm erhält den Wert des Primärschlüssels durch die RETURNING Klausel der insertAnweisung.
Eine Sequence ist ein Datenbankobjekt, das als sehr performanter Nummerngenerator verwendet wird. Sequences liefern aufsteigende Zahlen. Es wird nicht garantiert, dass die Folge der Zahlen lückenlos ist.
Die Syntax für das Anlegen einer Sequence ist: CREATE SEQUENCE sequence_name [START WITH start_value] [INCREMENT BY increment] [MAXVALUE max] [CYCLE] [CACHE cache_size] [ORDER | NOORDER]; Die Bedeutungen der Platzhalter sind: ● ● ● ● ●
sequence_name ist der Name einer Sequenz. start_value ist die erste Nummer, die generiert wird. increment ist der Wert der zur aktuellen Nummer addiert wird, um die nächste Nummer zu generieren. max ist die höchste Nummer, die generiert wird. cache_size ist die Anzahl der vorgenerierten Nummern, die zur Durchsatzerhöhung in einem Puffer bereitgehalten werden.
Sie sollten auf keinen Fall selbst Nummern generieren, indem Sie z.B. Variablen in einer Tabelle verwalten und deren Werte inkrementieren. Diese Vorgehensweise führt wegen des Wettbewerbs mehrerer Datenbanksitzungen um Änderungssperren zu einer schweren Beeinträchtigung des Durchsatzes Ihrer
Anwendung. Die schnellste Möglichkeit Nummern zu generieren ist eine Sequence zu verwenden, nicht zuletzt weil das RDBMS in einem Cache (Puffer) eine Anzahl bereits generierter Nummern vorhält. Die eigentliche Generierung einer Nummer erfolgt über eine select-Anweisung. Eine Sequenz hat zwei Pseudospalten: NEXTVAL und CURRVAL. Wenn Sie in einer select-Anweisung die Pseudospalte NEXTVAL abfragen, wird eine neue Nummer generiert. Die Abfrage der Pseudospalte CURRVAL liefert die aktuelle Nummer. CURRVAL kann nur verwendet werden, wenn in der Datenbanksitzung mindestens einmal auf NEXTVAL zugegriffen wurde. Listing 14.29 zeigt, wie eine Sequenz angelegt, ein Trigger für die automatische Vergabe einer Instructor_ID für einen neuen Dozenten angelegt und ein neuer Dozent mit einer insertAnweisung eingefügt wird. Beachten Sie, wie das Anwendungsprogramm, in diesem Fall SQL*Plus, durch das INSERT ... RETURNING die neu vergebene Instructor_ID und die ROWID, über die schnellstmöglich auf den neuen Datensatz zugegriffen werden kann, erhält.
Sie finden das Listing in der Datei sql\instructor_insert_trigger.sql auf der CD-ROM. Listing 14.29: Verwendung eines Triggers für die Automatische Nummerierung SQL> create sequence seq_instructor_id start with 1000 cache 50; Sequence created. SQL> create or replace trigger insert_instructor before insert on instructor for each row declare v_instructor_id number(5); begin select seq_instructor_id.nextval into v_instructor_id from dual; :new.instructor_id:=v_instructor_id; end; / Trigger created. SQL> variable instructor_id number SQL> variable instructor_rowid varchar2(18) SQL> insert into instructor (department_id, last_name, first_name) values (1004,'Panksepp','Jaak') returning instructor_id, rowid into :instructor_id, :instructor_rowid; SQL> select instructor_id, department_id, last_name, first_name from instructor where instructor_id=:instructor_id; INSTRUCTOR_ID DEPARTMENT_ID LAST_NAME FIRST_NAME ------------- ------------- ------------------------- ----------1000 1004 Panksepp Jaak SQL> update instructor set telephone=8055554456 where rowid=:instructor_rowid; 1 row updated. SQL> select instructor_id, department_id, last_name, first_name, telephone from instructor where instructor_id=:instructor_id INSTRUCTOR_ID DEPARTMENT_ID LAST_NAME FIRST_NAME TELEPHONE ------------- ------------- --------- ---------- ---------1000 1004 Panksepp Jaak 8055554456
Kaskadierende Trigger Das Zusammenwirken von Triggern kann recht komplex sein. So können Sie z.B. einen Trigger erstellen, der bei seiner Auslösung einen weiteren Trigger auslöst. Derartige Trigger werden als kaskadierende Trigger
bezeichnet. Um das Konzept kaskadierender Trigger zu erläutern, schauen Sie sich die folgenden drei einfachen Tabellen in Listing 14.30 an - tab1, tab2 und tab3. Zu Beginn hat jede Tabelle eine einzige Zeile. Listing 14.30: Auslösen des Triggers mit einer insert-Anweisung create table tab1 (col1 number); create table tab2 (col2 number); create table tab3 (col3 number); SQL> select * from tab1; COL1 ------7 SQL> select * from tab2; COL2 ------10 SQL> select * from tab3; COL3 ------13 Erstellen Sie für Tabelle tab1 einen BEFORE-UPDATE-Trigger auf Zeilenebene, der den alten Wert der Spalte col1 aus der Tabelle tab1 in die Tabelle tab2 einfügt, wie es in Listing 14.31 gezeigt wird. Für die Tabelle tab2 erstellen Sie einen BEFORE-INSERT-Trigger auf Zeilenebene, der die Tabelle tab3 aktualisiert und den Wert der Spalte col3 auf den neuen Wert der Spalte col2 setzt. Schließlich erstellen Sie für die Tabelle tab3 einen AFTERUPDATE-Trigger auf Anweisungsebene, der in Tabelle tab3 eine Zeile mit dem Wert 27 der Spalte col3 setzt. Listing 14.31: Erzeugung eines Triggers, der einen anderen Trigger auslöst SQL> create or replace trigger tab1_Update_Before before 2 update on tab1 3 for each row 4 4 declare 5 5 begin 6 6 insert into tab2 7 (col2) 8 values 9 (:old.col1); 10 10 end; 11 / Trigger created. SQL> create or replace trigger tab2_Insert_Before before 2 insert on tab2 3 for each row 4 4 declare 5 5 begin 6 6 update tab3
7 set 8 col3 = :new.col2; 9 9 end; 10 / Trigger created. SQL> create or replace trigger tab3_Update_After after 2 update on tab3 3 3 declare 4 4 begin 5 5 insert into tab3 6 (col3) 7 values 8 (27); 9 9 end; 10 / Trigger created.
Eine Tabelle ändert sich, wenn ihr Inhalt durch eine INSERT-, UPDATE- oder DELETE-Anweisung verändert wird. Ein Trigger auf Zeilenebene oder ein Trigger auf Tabellenebene, der als Folge eines DELETE CASCADE ausgeführt wird, kann Inhalte einer sich verändernden Tabelle weder lesen noch verändern, weil sich eine ändernde Tabelle in einem nicht definierten Zustand befindet. Um mehr über sich verändernde Tabellen zu erfahren (mutating tables), lesen Sie bitte Kapitel 15 im Handbuch »Oracle9i Application Developer's Guide - Fundamentals«. Was geschieht nun, wenn in tab1 eine Zeile aktualisiert wird? Wie in Listing 14.32 zu sehen ist, finden die folgenden Veränderungen statt: ● ● ●
In tab1 ist der Wert von col1 auf 8 aktualisiert worden. In tab2 hat der Trigger Tab1_Update_Before eine Zeile mit dem alten Wert 7 von col1 eingefügt. In tab3 wurde der Trigger Tab2_Insert_Before infolge der neuen Zeile in tab2 ausgelöst und der Wert von col3 auf den in tab2 eingefügten Wert 7 gesetzt. Als tab3 aktualisiert wurde, hat der Trigger Tab_Update_Before eine weitere Zeile in tab3 mit dem Wert 27 eingefügt.
Listing 14.32: Testen kaskadierender Trigger SQL> update tab1 2 set col1 = 8; row updated. SQL> select * from tab1; COL1 ------8 SQL> select * from tab2; COL2 ------10 7
SQL> select * from tab3; COL3 ------7 27
Standardmäßig ist die Anzahl kaskadierender Trigger auf 32 begrenzt. Denken Sie jedoch immer daran, dass Ihr Verständnis für die Auswirkungen der Anweisungen INSERT, UPDATE und DELETE in einem umgekehrt proportionalen Verhältnis zur Anzahl der ausgelösten kaskadierenden Trigger steht. Mit anderen Worten, machen Sie es nicht unnötig kompliziert.
Commit- und Rollback-Anweisungen in Triggern In einem Datenbanktrigger können Sie keine COMMIT- oder ROLLBACK-Anweisung ausführen, es sei denn Sie lassen den Trigger durch die Anweisung PRAGMA AUTONOMOUS_ TRANSACTION in einem eigenen Transaktionskontext laufen. Außerdem kann ein Trigger, der nicht in einem eigenen Transaktionskontext ausgeführt wird, keine gespeicherte Prozedur, Funktion oder ein Paket aufrufen, das eine ROLLBACK- oder COMMIT-Anweisung ausführt. Oracle besitzt diese Einschränkung aus einem wichtigen Grund. Trifft ein Trigger auf einen Fehler, sollten alle von diesem Trigger verursachten Änderungen an der Datenbank rückgängig gemacht werden. Hätte der Trigger aber einen Teil der Änderungen an der Datenbank festgeschrieben, wäre Oracle nicht in der Lage, die gesamte Transaktion rückgängig zu machen.
Trigger auf Datenbankereignisse Das Oracle RDBMS kennt zusätzlich zu Triggern auf Tabellen auch Trigger, die bei bestimmten Ereignissen ausgeführt werden. Diese Ereignisse sind der Aufbau einer Datenbanksitzung (LOGON), das Beenden einer Datenbanksitzung (LOGOFF), das Auftreten eines Fehlers (SERVERERROR), das Öffnen der Datenbank (STARTUP), das Schließen der Datenbank (SHUTDOWN) und das Pausieren einer Transaktion (SUSPEND) aufgrund unzureichenden Speicherplatzes. Oracle9i verfügt über das Merkmal Resumable Space Allocation. Dieses Merkmal erlaubt es Transaktionen, zu pausieren, wenn nicht genug Speicherplatz in einem Tablespace vorhanden ist. Ein Trigger auf das Ereignis SUSPEND wird ausgeführt, wenn eine fortsetzbare Transaktion keinen Speicherplatz mehr erhält. Der Administrator kann zusätzlichen Speicherplatz zuweisen und die Transaktion anschließend fortsetzen. Ohne dieses Merkmal würde die Transaktion vollständig zurückgerollt. Mehr Informationen zu diesem Thema finden Sie im »Oracle9i Database Administrator's Guide«.
Die Syntax für das Anlegen eines Triggers auf ein Datenbankereignis ist: CREATE [OR REPLACE] TRIGGER trigger_name {BEFORE | AFTER} database_event ON {DATABASE | schema_name.SCHEMA} PL/SQL-block Die Bedeutungen der Platzhalter sind: ● ● ●
trigger_name ist der Name eines Triggers. database_event ist eines der oben erwähnten Datenbankereignisse. schema_name ist der Name eines Schemas, für das der Trigger angelegt wird. ON DATABASE bedeutet,
dass der Trigger für alle Schemata gilt. Administratoren stehen immer wieder vor der Schwierigkeit, dass Anwender von Fehlern berichten, jedoch nicht genau mitteilen können, um welchen Fehler es sich handelte oder wann der Fehler genau auftrat. Wenn Sie einen Trigger auf das Ereignis SERVERERROR anlegen, kann das nicht mehr passieren. Listing 14.33 zeigt auf, wie ein Trigger angelegt werden kann, der jeden Fehler des RDBMS in eine Tabelle errorlog einträgt. Zu diesem Zweck wird die Ereignisattributfunktion ora_server_error_msg, die den Zugriff auf den Fehlerstapel (Errorstack) ermöglicht, verwendet. Der Trigger liest die obersten fünf Einträge aus dem Errorstack. Weitere Ereignisattributfunktionen finden Sie im Kapitel 16 des Oracle-Handbuchs »Application Developer's Guide Fundamentals«. Listing 14.33: Anlegen eines Triggers auf ein Datenbankereignis SQL> connect system/manager Connected. SQL> alter user flugle quota 1m on tools; User altered. SQL> connect flugle/flugle Connected. create table errorlog ( username varchar2(30), time timestamp, error varchar2(4000) ) tablespace tools; Table created. CREATE OR REPLACE TRIGGER log_errors AFTER SERVERERROR ON FLUGLE.SCHEMA DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN insert into errorlog (USERNAME, TIME, ERROR) values (user, systimestamp, ora_server_error_msg(1)||chr(10)|| ora_server_error_msg(2)||chr(10)|| ora_server_error_msg(3)||chr(10)|| ora_server_error_msg(4)||chr(10)|| ora_server_error_msg(5)); commit; END; / Trigger created. Beachten Sie, dass der Trigger durch das PRAGMA AUTONOMOUS_TRANSACTION in einem eigenen Transaktionskontext läuft. Starten Sie SQL*Plus und öffnen Sie eine zweite Datenbanksitzung als Benutzer system. Setzen Sie den Tablespace USERS mit alter tablespace users offline in den Zustand Offline.
Sie finden den Quellcode des Triggers in der Datei sql\servererror_trigger.sql auf der CD-ROM. Sie werden jetzt einen Fehler provozieren, indem Sie versuchen, die Tabelle Student im Tablespace USERS zu verändern. Listing 14.34 zeigt, wie nach dem Auftreten des Fehlers ein Datensatz in der Tabelle errorlog im Tablespace TOOLS vorhanden ist.
Listing 14.34: Aufzeichnung eines Datenbankfehlers SQL> delete from student; delete from student * ERROR at line 1: ORA-00376: file 7 cannot be read at this time ORA-01110: data file 7: 'D:\ORADATA\ORCL\USERS01.DBF' SQL> select * from errorlog; USERNAME TIME ERROR --------- ------------------------- --------------------------FLUGLE 08-NOV-01 12.56.06.000 AM ORA-00376: file 7 cannot be read at this time ORA-01110: data file 7: 'D:\ORADATA\ORCL\USERS01.DBF'
Löschen, Aktivieren und Deaktivieren von Triggern
Haben Sie sich entschlossen, dass Sie einen bestimmten Trigger nicht einsetzen wollen, können Sie ihn mit der folgenden Anweisung löschen: DROP TRIGGER trigger_name; Der Platzhalter trigger_name steht für den Namen des zu löschenden Triggers. Manchmal ist das Löschen eines Triggers eine zu drastische Maßnahme. Stattdessen möchten Sie den Trigger vielleicht nur zeitweilig außer Kraft setzen. Sie können einen Trigger solange deaktivieren, bis dessen Einsatz wieder sinnvoll ist. Um einen Trigger vorübergehend zu deaktivieren, verwenden Sie die ALTER-TRIGGERAnweisung:
ALTER TRIGGER trigger_name DISABLE; Der Platzhalter trigger_name steht für den Namen des zu deaktivierenden Triggers. Das folgende Beispiel deaktiviert den Trigger log_errors. Listing 14.35: Deaktivieren eines Triggers SQL> alter trigger log_errors disable; Trigger altered.
Um einen deaktivierten Trigger zu aktivieren, verwenden Sie die Syntax:
ALTER TRIGGER trigger_name ENABLE; Der Platzhalter trigger_name steht für den Namen des zu aktivierenden Triggers. Sie können den Trigger log_errors durch folgenden Befehl aktivieren: Listing 14.36: Aktivieren eines Triggers SQL> alter trigger log_errors enable; Trigger altered.
14.4 Zusammenfassung Sie haben in dieser Lektion eine Menge an Stoff bearbeitet. Die wichtigsten Erkenntnisse dieser Lektion waren: ● ● ●
● ● ● ●
● ● ● ●
● ● ● ● ●
● ● ●
● ●
●
●
●
● ● ●
●
Unter PL/SQL wird ein Fehler oder eine Meldung Exception genannt. Eine Anzahl vordefinierter Exceptions sind SQL-Fehlern zugeordnet. Ein Exception-Handler führt eine Anzahl PL/SQL-Anweisungen als Reaktion auf eine ausgelöste Exception aus. Sie können anwendungsspezifische Exceptions deklarieren und für diese Exception- Handler bestimmen. Ein PL/SQL-Block kann einen Exception-Block mit einem oder mehreren Exception- Handlern enthalten. Sie verwenden die RAISE-Anweisung zum Auslösen einer benutzerdefinierten Exception. Oracle stellt eine Prozedur namens RAISE_APPLICATION_ERROR zur Verfügung, um einem aufrufenden SQL-Unterprogramm einen anwendungsspezifischen Fehler zu übergeben. Könnte eine Abfrage mehr als eine Zeile zurückgeben, müssen Sie einen Cursor verwenden. Ein Cursor ist ein Fenster auf die Menge der von einer Abfrage abgerufenen Zeilen. Sie müssen jeden expliziten Cursor im Deklarationsabschnitt eines PL/SQL- Unterprogramms deklarieren. Sie können einen Cursor mit Parametern und optionalen Standardwerten deklarieren. Die Werte eines jeden Parameters können beim Öffnen des Cursors angegeben werden. Die einem Cursor zugeordnete Abfrage wird beim Öffnen des Cursors auf Korrektheit überprüft. Die Ergebnisse einer Abfrage erhalten Sie mit FETCH-Anweisungen auf den Cursor. Jeder Cursor besitzt vier Attribute: %NOTFOUND, %FOUND, %ISOPEN und %ROWCOUNT. Ein PL/SQL-Unterprogramm kann gleichzeitig mehrere Cursors geöffnet haben. Ein Datenbanktrigger auf einer Tabelle ist ein PL/SQL-Block, der beim Eintreten eines triggerauslösenden Ereignisses (INSERT, UPDATE, DELETE) ausgeführt wird. Trigger können auch für bestimmte Datenbankereignisse festgelegt werden. Sie definieren einen Trigger mit der CREATE TRIGGER-Anweisung. Sie können Trigger zu ganz unterschiedlichen Zwecken, wie z.B. Implementierung von Geschäftsregeln, komplexe Validierungen von Spaltenwerten oder Berechnung der Standardwerte von Spalten, einsetzen. Ein Trigger auf Zeilenebene wird für jede von einer Änderung betroffene Zeile ausgelöst. Ein Trigger auf Anweisungsebene wird nur einmal ausgelöst und hat keinen Zugang zu den Daten in den Zeilen einer Tabelle. Ein BEFORE-Trigger wird vor der eigentlichen Ausführung der auslösenden SQL- Anweisung ausgeführt. Ein AFTER-Trigger wird nach der Ausführung der auslösenden SQL-Anweisung ausgeführt. Sie sollten gepufferte Sequenzen (Cached Sequences) zusammen mit einem Trigger einsetzen, um performant Datensätze automatisch zu nummerieren. Ein Trigger kann eine optionale Klausel besitzen, die die Bedingung bestimmt, die zu seiner Auslösung erfüllt sein muss. Sie können einen Trigger mit der DROP TRIGGER-Anweisung löschen. Sie können einen Trigger mit der ALTER TRIGGER-Anweisung aktivieren oder deaktivieren. Der Rumpf eines Triggers kann keine COMMIT- oder ROLLBACK-Anweisung enthalten, es sei denn Sie lassen den Trigger durch die Anweisung PRAGMA AUTONOMOUS_TRANSACTION in einem eigenen Transaktionskontext laufen. Sie können eine gespeicherte Prozedur oder Funktion aus dem Trigger-Rumpf aufrufen.
14.5 Wie geht es weiter?
Nachdem Sie nun mit SQL und PL/SQL vertraut sind, wird es Zeit zum nächsten Thema überzugehen. Am Tag 15 lernen Sie mit Oracle Forms ein Werkzeug für die Erstellung von Client-Server-Anwendungen kennen.
14.6 Fragen und Antworten Frage: Ist es möglich, einen Trigger zu erstellen, der ausgelöst wird, wenn während einer Abfrage eine Zeile gelesen wird? Antwort: Nein, ein Trigger auf einer Tabelle wird nur von INSERT, UPDATE oder DELETE ausgelöst. Sie können die Auditing-Möglichkeiten verwenden, um jede von einem Benutzer abgesetzte Abfrage aufzuzeichnen. Frage: Worin liegt der Vorteil bei der Verwendung eines Triggers, um eine Bedingung zu überprüfen, die bereits durch die referentielle Integrität erzwungen wird? Antwort: Einige Entity-Relationship-Entwurfswerkzeuge generieren scheinbar überflüssige Trigger. Das heißt, die generierten Trigger suchen nach Verstößen gegen die referentielle Integrität und geben bestimmte Fehlermeldungen zurück. Ohne einen solchen Trigger würde das Oracle-RDBMS eine Fehlermeldung zurückgeben, die besagt, dass die referentielle Integrität verletzt wurde, ohne aber die entsprechenden Spalten oder die Beziehung anzugeben. Diese generierten Trigger erleichtern es sowohl dem Entwickler als auch dem Benutzer, festzustellen, warum der Vorgang nicht zugelassen wurde. Frage: Ist es immer erforderlich, einen Cursor zu schließen? Antwort: Es ist erforderlich, wenn Sie den Wert eines Cursor-Parameters ändern und den Cursor wieder öffnen wollen. Im Allgemeinen ist es eine empfehlenswerte Programmierpraxis, einen nicht mehr verwendeten Cursor zu schließen.
14.7 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. 2. 3. 4.
Wie werden SQLCODE und SQLERRM in PL/SQL-Unterprogrammen verwendet? Richtig oder falsch? Ein PL/SQL-Unterprogramm kann gleichzeitig mehrere Cursors geöffnet haben. Welche sind die Vorteile bei der Verwendung von Datenbanktriggern? Welchen Datenbanktrigger würden Sie verwenden, um den in einer Spalte zu speichernden Wert zu verändern, wenn der Tabelle eine neue Zeile hinzugefügt wird?
Übungen 1. Erstellen Sie eine gespeicherte Funktion Teaching_Load mit einem einzigen Argument - einer Abteilungsnummer (Department_ID). Teaching_Load soll die durchschnittliche Anzahl an Stunden zurückgeben, die Ausbilder in den entsprechenden Abteilungen zurzeit unterrichten. 2. Erstellen Sie eine gespeicherte Prozedur Suitable_Locations mit einem einzigen Argument - Anzahl der Sitzplätze. Suitable_Locations soll die ersten drei Gebäude und Räume ausdrucken, die die angegebene
Anzahl Sitzplätze überschreiten. 3. Schreiben Sie einen Before-Delete-Trigger für die Tabelle Instructor, der eine Exception auslöst, wenn ein Ausbilder laut Plan eine Klasse unterrichtet.
Programmentwicklung mit PL/SQL Während der letzten Lektion haben Sie etwas über Programmkonstrukte von PL/SQL gelernt. In dieser Lektion lernen Sie, wie man PL/SQL-Module erstellt - Prozeduren, Funktionen und Pakete - die in einer OracleDatenbank gespeichert werden. Sie lernen auch etwas über PL/SQL-Datentypen, die in SQL nicht verfügbar sind. Eine gespeicherte Prozedur oder Funktion ist ein PL/SQL-Programm, das in einer Oracle- Datenbank gespeichert ist und von einem Benutzer aufgerufen wird, entweder direkt oder indirekt. Die Vorteile der Verwendung gespeicherter Prozeduren und Funktionen sind: ●
●
●
●
Effizienz: In einer Client-Server-Architektur schickt der Client SQL-Anforderungen an einen DatenbankServer. Mit der Zahl der Benutzer nimmt auch die Zahl der Anforderungen zu und das Netzwerk kann zum Flaschenhals für die Systemleistung werden. Die Verwendung gespeicherter Prozeduren bietet eine erhebliche Verbesserung der Systemleistung, da ein einziger Aufruf einer gespeicherten Prozedur mehrere SQL-Anweisungen aufrufen kann, die auf dem Server ausgeführt werden, so dass der Netzwerkverkehr reduziert wird. Wiederverwendbarkeit: Ein PL/SQL-Programm wird einmal geschrieben und in einer Vielzahl von Situationen verwendet - in SQL-Skripten, in Datenbanktriggern und in der Anwendungslogik von Clients. Portabilität: Sie können eine gespeicherte Prozedur in jeder Oracle-Datenbank verwenden, unabhängig von der Plattform. Als Folge müssen Sie sich nicht um Kompatibilitätsfragen wie das Betriebssystem oder Compiler-Versionen kümmern. Wenn die Plattform Oracle unterstützt, kann die gespeicherte Prozedur ohne zusätzliche Änderungen auf dieser ausgeführt werden. Wenn die gespeicherte Prozedur Verweise auf Datei- und Verzeichnisnamen enthält, müssen Sie natürlich eventuell zusätzliche Änderungen vornehmen. Wartungsfreundlichkeit: Eine gespeicherte Prozedur ist für eine bestimmte Tätigkeit entworfen. Diese Tätigkeit kann von einem Datenbanktrigger, einem SQL*Plus- Skript, einem Anwendungsprogramm oder einer anderen gespeicherten Prozedur benötigt werden. Indem aus allen diesen Komponenten die gespeicherte Prozedur aufgerufen wird, können Sie die Kosten für die Wartung der Software reduzieren.
Sie können verschiedene Werkzeuge verwenden, um gespeicherte Prozeduren, Funktionen und Pakete zu erzeugen und zu warten. In dieser Lektion lernen Sie, wie man dafür SQL*Plus oder SQL Worksheet verwendet. Ein Werkzeug wie Oracle Procedure Builder, eine Komponente von Developer, hat jedoch mehrere Vorteile. Zuerst einmal besitzt Procedure Builder einen Editor, der es ermöglicht, PL/SQL-Code komfortabel zu entwickeln. Zweitens können Sie Ihr gespeichertes Programm übersetzen, indem Sie einfach einen Knopf im Editor drücken. Drittens werden alle Übersetzungsfehler bequem in einem separaten Fenster angezeigt. Es gibt auch Editoren von Drittherstellern, die Sie testen können, bevor Sie ein größeres PL/SQL-Projekt beginnen.
13.1 Erstellen einer gespeicherten Prozedur oder Funktion
Die Syntax für das Erzeugen einer gespeicherten Prozedur ist folgendermaßen: CREATE [OR REPLACE] PROCEDURE procedure-name [(argument1 ... [, argumentN ] ) ] IS [local-variable-declarations] BEGIN executable-section [exception-section] END [procedure-name]; Die Bedeutungen der Platzhalter sind: ●
●
●
● ●
procedure-name ist der Name der Prozedur, entsprechend den Einschränkungen von Oracle für die Benennung von Objekten. argument1 bis argumentN sind optionale Deklarationen von Argumenten, sie bestehen aus: argument-name [IN | OUT | IN OUT] [NOCOPY] datatype [ {:= | DEFAULT} value] Die Schlüsselwörter haben dieselben Bedeutungen wie für temporäre Prozeduren (siehe Tag 12). local-variable-declarations sind optionale Deklarationen von Variablen, Konstanten und anderen Prozeduren und Funktionen, die für procedure-name lokal sind. executable-section sind die PL/SQL-Anweisungen, aus denen sich die Prozedur zusammensetzt. exception-section ist der optionale Teil für die Behandlung von Ausnahmen in der Prozedur.
Als Beispiel enthält Listing 13.1 eine gespeicherte Prozedur, die ein einzelnes Argument hat, das von der deleteAnweisung verwendet wird, um zu bestimmen, welche Klasse aus der Tabelle Course entfernt wird. Listing 13.1: Erstellen einer gespeicherten Prozedur mit einem einzigen Argument SQL> create or replace procedure Delete_Specified_Course 2 (Description_Phrase varchar2) is 3 3 begin 4 4 delete from Course 5 where 6 upper(Description) like Description_Phrase; 7 7 end; 8 / Procedure created. Die Syntax für das Erzeugen einer gespeicherten Funktion ist sehr ähnlich wie die Syntax für das Erzeugen einer gespeicherten Prozedur. Natürlich muss eine gespeicherte Funktion einen Wert zurückgeben.
Die Syntax lautet: CREATE [OR REPLACE] FUNCTION function-name [(argument1 ... [, argumentN] ) ] RETURN function-datatype IS
[local-variable-declarations] BEGIN executable-section [exception-section] RETURN result; END [function-name]; Die Bedeutungen der Platzhalter sind. ●
●
function-name ist der Name der Funktion entsprechend den Einschränkungen von Oracle für die Benennung von Objekten. argument1 bis argumentN sind optionale Deklarationen von Argumenten, sie bestehen aus: argument-name [IN | OUT] datatype [ {:= | DEFAULT} value]
● ●
● ● ●
function-datatype ist der Datentyp des von der Funktion zurückgegebenen Werts. local-variable-declarations sind optionale Deklarationen von Variablen, Konstanten und anderen Prozeduren und Funktionen, die für function-name lokal sind. executable-section sind die PL/SQL-Anweisungen, aus denen sich die Funktion zusammensetzt. exception-section ist der optionale Teil für die Behandlung von Exceptions in der Funktion. result ist das Ergebnis, das die Funktion an den Aufrufer zurückgibt.
Der Unterschied zwischen einer gespeicherten Prozedur und einer gespeicherten Funktion ist, dass die gespeicherte Prozedur keinen Wert zurückgibt, während eine gespeicherte Funktion einen Wert zurückliefert. Als Folge kann eine gespeicherte Funktion in einer SQL-Anweisung auf die gleiche Weise wie eine integrierte Funktion aufgerufen werden. Bei einer gespeicherten Prozedur ist das nicht möglich. Gespeicherte Prozeduren und Funktionen können jedoch beide einen geänderten Argumentwert zurückliefern, wenn das Argument als out oder in out deklariert wurde. Listing 13.2 zeigt, wie man eine gespeicherte Funktion erzeugt, die den Punktedurchschnitt eines Studenten liefert. Listing 13.2: Eine gespeicherte Funktion mit einem einzigen Argument create or replace function student_GPA (arg_student_ID IN number) return number is GPA number; begin select avg(decode(grade, 'A+', 4.25, 'A', 4, 'A-', 3.75, 'B+', 3.25, 'B', 3, 'B-', 2.75, 'C+', 2.25, 'C', 2, 'C-', 1.75, 'D+', 1.25, 'D', 1, 'D-', 0.75, 'F', 0)) into GPA from student_schedule where student_id = arg_student_id; return GPA; end; /
Ermitteln von Fehlermeldungen bei Erstellen gespeicherter Prozeduren
Wenn Oracle beim Erzeugen eines gespeicherten PL/SQL-Programms Fehler entdeckt, wird eine Meldung ausgegeben, die lediglich aussagt, dass Fehler aufgetreten sind - ohne irgendwelche zusätzlichen Details anzugeben. Als Beispiel zeigt Listing 13.3, was geschieht, wenn Sie eine gespeicherte Prozedur mit einem Syntaxfehler erzeugen wollen. Um die Fehler zu sehen, die bei dem Versuch entstehen, diesen PL/SQL-Code zu kompilieren, können Sie den SQL*Plus-Befehl show errors verwenden, dieser zeigt die PL/SQLÜbersetzungsfehler. Listing 13.3: Eine während der Kompilierung einer gespeicherten Prozedur von Oracle zurückgegebene Fehlermeldung SQL> create or replace procedure show_inserts IS 2 2 max_records constant int := 100; 3 i int := 1; 4 4 begin 5 6 for i in 1..max_records loop 7 7 if (mod(i,10) = 0) then 8 insert into test_table 9 (record_number, current_date) 10 values 11 (i, SYSDATE) 12 dbms_output.put_line('The value of i is ' || to_char(i)); 13 13 else 14 null; 15 15 end if; 16 16 end loop; 17 17 end; 18 / Warning: Procedure created with compilation errors. SQL> show errors Errors for PROCEDURE SHOW_INSERTS: LINE/COL ERROR -------- ------------------------------12/5 PLS-00103: Encountered the symbol "DBMS_OUTPUT" when expecting one of the following: ; ; was inserted before "DBMS_OUTPUT" to continue.
Beim Versuch, die Prozedur show_inserts zu erstellen oder zu ersetzen, gibt Oracle direkt nach Zeile 18 eine Fehlermeldung zurück, die anzeigt, dass es Übersetzungsfehler gab. Um den genauen Fehler zu sehen, geben Sie den Befehl show errors ein. Wie Sie in Zeile 11 sehen können, endet die insert-Anweisung nicht mit einem Semikolon.
13.2 PL/SQL-Quellcode und Datenbankkatalog
Nachdem eine gespeicherte Prozedur erzeugt wurde, möchten Sie vielleicht einen Blick auf den Quellcode des PL/SQL-Programms werfen. Auch wenn das SQL-Skript, das für das Erstellen der gespeicherten Prozedur verwendet wurde, nicht verfügbar ist, können Sie den Code einer gespeicherten Prozedur immer noch durch eine Abfrage einer Datensicht des Datenbankkatalogs (Data Dictionary) erhalten. Das Data Dictionary von Oracle ist eine Gruppe von Tabellen, die Informationen über die Oracle-Datenbank selbst enthalten. Weil die Tabellen dieses Data Dictionary eine etwas kryptische Struktur haben, definiert Oracle eine Menge von Datensichten, die einen kohärenteren Blick auf das Data Dictionary bieten. Weitere Informationen über Datensichten erhielten Sie am Tag 9, »Fortgeschrittene Abfragen mit SQL«. Eine dieser Datensichten hat den Namen USER_SOURCE und enthält die folgenden vier Spalten: Stellen Sie sich als Beispiel vor, dass Sie eine gespeicherte Prozedur namens drop_class erstellen. Wenn Sie den Code von drop_class sehen wollen, fragen Sie die Datensicht user_source des Data Dictionary ab, wie dies in Listing 13.4 gezeigt wird. Listing 13.4: Abrufen des Codes für eine gespeicherte Prozedur SQL> select line, text from user_source where name = 'DROP_CLASS' order by line; LINE TEXT ---- --------------------------------------------------------------1 procedure drop_class (arg_student_ID IN varchar2, 2 arg_class_ID IN varchar2, 3 status OUT number) is 4 5 counter number; 6 7 begin 8 9 status := 0; 10 11 - Verify that this class really is part of the student's schedule. 12 13 select count(*) into counter 14 from student_schedule 15 where 16 student_id = arg_student_id and 17 class_id = arg_class_id; 18 19 if counter = 1 then 20 delete from student_schedule 21 where 22 student_id = arg_student_id and 23 class_id = arg_class_id; 24 status := -1; 25 end if; 26 27 end; 27 rows selected.
Ermitteln einer Liste von Prozeduren, Funktionen, Paketen und Paketrümpfen
Sie können USER_OBJECTS abfragen, um eine Liste gespeicherter Prozeduren, Funktionen, Pakete und Paketrümpfe zu erhalten, die zu dem Oracle-Account gehört, mit dem Sie gerade verbunden sind. Wenn Sie alle diese Objekte, unabhängig von ihrem Eigentümer, sehen wollen, müssen Sie DBA_OBJECTS anstelle von USER_OBJECTS abfragen. Die Spalte OBJECT_TYPE von DBA_OBJECTS gibt den Typ des Objekts an: Tabelle, Datensicht, Prozedur usw. Um eine Liste der Typen von Datenbankobjekten zu erhalten, die einem Benutzer gehören, verwenden Sie die in Listing 13.5 gezeigte Abfrage. Listing 13.5: Bestimmung der im Besitz des aktuellen Benutzers befindlichen Objekttypen SQL> select distinct object_type from user_objects; OBJECT_TYPE ---------------FUNCTION INDEX PACKAGE PACKAGE BODY PROCEDURE TABLE VIEW 7 rows selected.
13.3 Vorwärtsdeklaration für Prozeduren und Funktionen In PL/SQL muss jeder Bezeichner deklariert werden - Konstanten, Variablen, Cursor oder Funktionen -, der an irgendeiner Stelle eines PL/SQL-Unterprogramms verwendet wird. Diese Anforderung kann ein Problem verursachen, wenn zwei Unterprogramme aufeinander verweisen, wie es in Listing 13.6 gezeigt wird. Listing 13.6: Aufruf einer Funktion vor ihrer Deklaration SQL> set serveroutput on SQL> SQL> declare 2 2 function Medicare_Patient (Patient_ID IN varchar2) 3 return number is 4 4 status number; 5 Pat_ID varchar2(6); 6 6 begin 7 7 if Insurable_Patient (Pat_ID) = 2 then 8 status := 1; 9 end if; 10 10 return status; 11 11 end Medicare_Patient; 12 12 12 function Insurable_Patient (Patient_ID IN varchar2) 13 return number is 14
14 status number; 15 Pat_ID varchar2(6); 16 16 begin 17 17 if Medicare_Patient (Pat_ID) = 2 then 18 status := 1; 19 end if; 20 20 return status; 21 21 end Insurable_Patient; 22 22 - Executable portion of anonymous block. 23 23 begin 24 null; 25 end; 26 / declare * ERROR at line 1: ORA-06550: line 7, column 4: PLS-00313: 'INSURABLE_PATIENT' not declared in this scope ORA-06550: line 7, column 1: PL/SQL: Statement ignored
Wie Sie in Listing 13.6 sehen können, akzeptiert PL/SQL den Aufruf von Insurable_Patient in der Funktion Medicare_Patient nicht, weil die Deklaration von Insurable_Patient nach der Deklaration von Medicare_Patient erfolgt (Zeile 2). Um dieses Dilemma zu umgehen, nehmen Sie eine Vorwärtsdeklaration des Unterprogramms in den Deklarationsteil auf. Eine Vorwärtsdeklaration ist eine Deklaration eines Unterprogramms, seiner Argumente und seines Rückgabewerts. Listing 13.7 zeigt, wie man für das vorhergehende Beispiel eine Vorwärtsdeklaration für Insurable_Patient (Zeile 2) angibt. Listing 13.7: Bereitstellung einer Vorwärtsdeklaration für eine Funktion SQL> declare 2 2 function Insurable_Patient (Patient_ID IN varchar2) return number; 3 3 function Medicare_Patient (Patient_ID IN varchar2) 4 return number is 5 5 status number; 6 Pat_ID varchar2(6); 7 7 begin 8 8 if Insurable_Patient (Pat_ID) = 2 then 9 status := 1; 10 end if;
11 11 return status; 12 12 end Medicare_Patient; 13 13 13 function Insurable_Patient (Patient_ID IN varchar2) 14 return number is 15 15 status number; 16 Pat_ID varchar2(6); 17 17 begin 18 18 if Medicare_Patient (Pat_ID) = 2 then 19 status := 1; 20 end if; 21 21 return status; 22 22 end Insurable_Patient; 23 23 - Executable portion of anonymous block. 24 24 begin 25 25 null; 26 26 end; 27 / PL/SQL procedure successfully completed.
13.4 Gespeicherte Funktionen in einer SQL- Anweisung verwenden Gespeicherte Funktionen können in SQL-Anweisungen in derselben Weise wie integrierte Funktionen verwendet werden. Die Funktionalität einer einzelnen SQL-Anweisung kann um die Logik einer gespeicherten Funktion erweitert werden. Lassen Sie uns ein elementares Beispiel dafür betrachten, wie man diese Funktionalität nutzt. Weil es in Oracle keine integrierte Funktion für die Umwandlung einer Temperatur von Fahrenheit nach Grad Celsius gibt, erstellen Sie eine gespeicherte Funktion, die diese Umwandlung durchführt, wie dies in Listing 13.8 gezeigt wird. Nach erfolgreicher Erstellung können Sie die gespeicherte Prozedur in einer select-Anweisung verwenden. Listing 13.8: Verwendung einer gespeicherten Funktion in einer select-Anweisung SQL> create or replace function DegF_to_DegC (Deg_F IN number) return number is Deg_C number; begin Deg_C := (5.0/9.0)*(Deg_F-32); return Deg_C; end DegF_to_DegC; / Function created. SQL> select body_temp, degf_to_degc(body_temp)
from patient; BODY_TEMP DEGF_TO_DEGC(BODY_TEMP) --------- ----------------------99.2 37.333333 100.2 37.888889 103.8 39.888889 Wenn Sie eine gespeicherte Funktion erzeugt haben, kann sie jederzeit verwendet werden.
13.5 Speichern von Rückgabewerten in einer Tabelle Obwohl PL/SQL keine integrierte Unterstützung für die Kommunikation mit dem Benutzer besitzt, können Sie dennoch PL/SQL verwenden, um einem Benutzer oder einem anderen Programm Ergebnisse zu liefern, indem Sie ● ●
Informationen in eine Zwischentabelle schreiben, die ein Benutzer oder ein Programm abfragen kann; die Prozeduren und Funktionen des von Oracle zur Verfügung gestellten Pakets dbms_output verwenden.
Sie haben bereits ein Beispiel dafür gesehen, wie man mit PL/SQL in eine Zwischentabelle schreiben kann. Wenn gespeicherte Prozeduren und Funktionen in PL/ SQL kompiliert werden, schreibt die PL/SQL-Engine selbst Fehlermeldungen in eine Tabelle des Data Dictionary, die vom Benutzer abgefragt werden kann. Wenn Sie über SQL*Plus Ausgaben zur Verfügung stellen wollen, ist die Verwendung von dbms_output eine gute Strategie. Wenn Sie viele Werte an einen Benutzer oder ein Programm übergeben wollen, ist eine Tabelle sinnvoller. Für Dateioperationen steht Ihnen darüber hinaus das PL/SQL-Paket UTL_FILE zur Verfügung.
13.6 Aufrufen einer gespeicherten Prozedur oder Funktion Die Methode für den Aufruf einer gespeicherten Prozedur hängt vom Umfeld ab. In SQL*Plus verwenden Sie den Befehl execute. Wenn Sie eine gespeicherte Funktion aufrufen, müssen Sie den Rückgabewert der Funktion in einer SQL*Plus Variablen speichern. SQL*Plus Variablen können in PL/SQLBlöcken verwendet werden. Ein Doppelpunkt vor dem Variablennamen ist erforderlich, damit SQL*Plus eigene Variablen von PL/SQL-Variablen unterscheiden kann.
Die Syntax für die Vereinbarung einer SQL*Plus Variablen ist: VAR[IABLE] [variable [NUMBER|CHAR|CHAR (n)|NCHAR|NCHAR (n) |VARCHAR2 (n)|NVARCHAR2 (n)|CLOB|NCLOB]] Argumente können als Literale oder als SQL*Plus Variablen übergeben werden.
Die Syntax der execute-Anweisung ist: EXEC[UTE] statement; Der Platzhalter statement steht für eine PL/SQL Anweisung.
Listing 13.9 zeigt die Verwendung der execute-Anweisung zusammen mit SQL*Plus Variablen. Listing 13.9: Aufruf einer gespeicherten Funktion mit dem SQL*Plus Befehl execute SQL> variable DegCelsius number SQL> variable DegF number SQL> begin :DegF:=98.7; end; / PL/SQL procedure successfully completed. SQL> execute :DegCelsius:=degf_to_degc(:DegF); PL/SQL procedure successfully completed. SQL> print DegCelsius DEGCELSIUS ---------37.0555556 Aus einem PL/SQL-Unterprogramm rufen Sie die gespeicherte Prozedur oder Funktion mit allen benötigten Argumenten auf. Am Tag 17, »Anwendungsentwicklung mit Forms Developer«, werden Sie lernen, wie man eine gespeicherte Prozedur oder Funktion aus einer Oracle-Forms-Anwendung aufruft.
13.7 Pakete Ein Paket ist eine Gruppe verwandter Prozeduren und Funktionen in PL/SQL. Wie ein Paket der Programmiersprache Ada besteht ein PL/SQL-Paket aus einer Paketspezifikation und einem Paketrumpf. Sie können Pakete erstellen, die anwendungsabhängig sind - beispielsweise könnte ein Paket namens patient_data Prozeduren und Funktionen enthalten, die mit der Behandlung und Beschaffung von Informationen über die Patienten eines Krankenhauses zu tun haben. Außerdem könnte ein Paket Prozeduren und Funktionen enthalten, die häufig vorkommende Aufgaben wie die Umwandlung einer Ortsangabe von einem Koordinatensystem in ein anderes ausführen.
Ein Paket ist eine Sammlung verwandter PL/SQL-Prozeduren und Funktionen, die in einer OracleDatenbank gespeichert wird. Um ein Paket zu erzeugen, müssen Sie eine Paketspezifikation und einen Paketrumpf angeben.
Deklarieren eines Pakets
Die allgemeine Syntax für das Erstellen einer Paketspezifikation ist: CREATE [OR REPLACE] PACKAGE package-name IS declaration-section END package-name; Die Bedeutungen der Platzhalter sind: ●
package-name ist der Name des zu erstellenden Pakets entsprechend den Einschränkungen von Oracle
●
für die Benennung von Objekten. declaration-section besteht aus Deklarationen von Typen, Variablen, Cursors, Prozeduren und Funktionen. Auf Komponenten des Pakets, die in der declaration- section aufgeführt sind, kann von außerhalb des Pakets zugegriffen werden.
Als Beispiel enthält Listing 13.10 die Paketspezifikation für das Informationssystem des Flugle-College.
Sie finden den Quellcode der Paketspezifikation in der Datei sql\flugle_ package.sql auf der CDROM. Listing 13.10: Deklaration einer Paketspezifikation create or replace package Flugle is function register_for_class (arg_student_ID IN number, arg_class_ID IN number) return number; function schedule_conflict (arg_student_ID IN number, arg_class_ID IN number) return number; procedure drop_class (arg_student_ID IN number, arg_class_ID IN number, status OUT number); procedure assign_instructor (arg_class_ID IN number, arg_instructor_ID IN number, status OUT number); procedure assign_grade (arg_student_ID IN number, arg_class_ID IN number, arg_grade IN varchar2, status OUT number); function student_GPA (arg_student_ID IN number) return number; END; /
Das Flugle-Paket enthält sechs Teile: drei Prozeduren und drei Funktionen: ● ● ● ● ● ●
Die Prozedur drop_class Die Prozedur assign_instructor Die Prozedur assign_grade Die Funktion register_for_class Die Funktion schedule_conflict Die Funktion student_GPA
Wenn Sie Paketspezifikationen oder Paketrümpfe mit einem Skript erzeugen, verwenden Sie die or
replace-Klausel. Oracle bietet auch die Anweisungen drop package und drop package body, aber die or replace-Klausel erspart Ihnen den Ärger, sich zu merken, ob Sie ein Paket gelöscht haben, bevor Sie versuchen, es zu erzeugen.
Deklarieren eines Paketrumpfs Ein Paketrumpf enthält die privaten Elemente eines Pakets. Er versteckt Details, z.B. wie Cursor, Prozeduren und Funktionen konkret implementiert sind. Details, die vor den Nutzern des Pakets verborgen werden sollten.
Ein Paketrumpf wird mit der folgenden Syntax vereinbart: CREATE PACKAGE BODY package-name IS [declaration-section] [procedure-bodies] [function-bodies] [initialization-section] END package-name; Die Bedeutungen der Platzhalter sind: ●
●
●
●
●
package-name ist der Name des zu erstellenden Pakets, entsprechend den Einschränkungen von Oracle für die Benennung von Objekten. declaration-section besteht aus Deklarationen von Typen, Variablen und Cursors, auf die von allen Prozeduren und Funktionen zugegriffen werden kann. procedure-bodies besteht aus den ausführbaren Teilen der in der Paketspezifikation vereinbarten Prozeduren. function-bodies besteht aus den ausführbaren Teilen der in der Paketspezifikation vereinbarten Funktionen. initialization-section ist ein optionaler Abschnitt, der einmal beim ersten Aufruf des Pakets ausgeführt wird.
Listing 13.11 listet den Inhalt des Flugle-Paketrumpfs auf, der die Details jeder Prozedur und Funktion enthält, die Teil dieses Pakets ist.
Sie finden den Quellcode des Paketrumpfs in der Datei sql\flugle_package_ body.sql auf der CDROM. Listing 13.11: Deklaration eines Paketrumpfs create or replace package body flugle is -- Declare some exceptions. schedule_conflict_exists exception; already_registered exception; not_registered exception; -- Declare values for status. conflicting_classes number := -2; unsuccessful number := -1; normal number := 0; -- *******************************************************
-- Function register_for_class -function register_for_class (arg_student_ID IN number, arg_class_ID IN number) return number is status number; counter number; begin -- Determine if the student isn't already registered for this class. select count(*) into counter from student_schedule where student_id = arg_student_id and class_id = arg_class_id; if counter > 0 then --- The student is already registered for this class. -raise already_registered; else --- The student isn't registered for this class. -- Determine if there is a schedule conflict. -if schedule_conflict(arg_student_id, arg_class_id) = 0 then insert into student_schedule (student_id, class_id) values (arg_student_id, arg_class_id); else raise schedule_conflict_exists; end if; end if; status := normal; return status; exception when schedule_conflict_exists then raise_application_error (-20001, 'Schedule conflict exists'); when already_registered then raise_application_error (-20002, 'Student is already registered for class'); when others then null; end; -- ******************************************************* -- Function schedule_conflict -function schedule_conflict (arg_student_ID IN number, arg_class_ID IN number) return number is -- Declare a cursor to look for other classes with the same schedule -- as this one. cursor get_other_classes is select SS.Class_ID from Student_Schedule SS, Class C where SS.Class_ID = C.Class_ID and
(C.Semester, C.School_Year, C.Schedule_ID) = (select Semester, School_Year, Schedule_ID from Class where Class_ID = arg_class_ID); Conflicting_Class_ID Class.Class_ID%type; status number; begin -- Need to look at the other classes in the student's schedule -- for the same semester and school year. for get_other_classes_Rec in get_other_classes loop fetch get_other_classes into Conflicting_Class_ID; exit when get_other_classes%notfound; end loop; close get_other_classes; if get_other_classes%rowcount > 0 then status := conflicting_classes; else status := normal; end if; return status; end; -- ******************************************************* -- Procedure drop_class -procedure drop_class (arg_student_ID IN number, arg_class_ID IN number, status OUT number) is counter number; begin -- Verify that this class really is part of the student's schedule. select count(*) into counter from student_schedule where student_id = arg_student_id and class_id = arg_class_id; if counter = 1 then delete from student_schedule where student_id = arg_student_id and class_id = arg_class_id; end if; end; -- ******************************************************* -- Procedure assign_instructor -procedure assign_instructor (arg_class_ID IN number, arg_instructor_ID IN number, status OUT number) is counter number; begin --- Assign this instructor to this class. -update class set Instructor_ID = arg_instructor_ID
where Class_ID = arg_class_ID; status:=normal; end; -- ******************************************************* -- Procedure assign_grade -procedure assign_grade (arg_student_ID IN number, arg_class_ID IN number, arg_grade IN varchar2, status OUT number) is counter number; begin status:=unsuccessful; -- Determine if the student is registered for this class. select count(*) into counter from student_schedule where student_id = arg_student_id and class_id = arg_class_id; if counter = 0 then --- The student is not taking this class. -raise not_registered; else --- Assign the grade for this class. -update student_schedule set grade = arg_grade, date_grade_assigned=sysdate where student_id = arg_student_id and class_id = arg_class_id; end if; commit; status:=normal; exception when not_registered then raise_application_error (-20003, 'Student not registered for class'); when others then null; end; -- ******************************************************* -- Function student_GPA -function student_GPA (arg_student_ID IN number) return number is GPA number; begin --- Calculate the average grade point for this student based on all -- classes for which a grade has been assigned. select avg(decode(grade, 'A+', 4.25, 'A', 4, 'A-', 3.75, 'B+', 3.25, 'B', 3, 'B-', 2.75,
'C+', 2.25, 'C', 2, 'C-', 1.75, 'D+', 1.25, 'D', 1, 'D-', 0.75, 'F', 0)) into GPA from student_schedule where student_id = arg_student_id; return GPA; end; end; / Wenn der Flugle-Paketrumpf erstellt ist, können Sie die Funktionen aus dem Paket aufrufen. Werfen Sie einen Blick auf Listing 13.12. Die update-Anweisung wird verwendet, um den Lehrer für Class 104200 auf NULL zu setzen. Die nachfolgende select-Anweisung beweist, dass dieser Klasse kein Lehrer zugewiesen ist. Als nächstes wird ein anonymer Block verwendet, in dem Assign_Instructor aufgerufen wird, um den Dozenten mit der Instructor_ID 491 der Klasse 104200 zuzuweisen. Der Status ist 0, dies zeigt an, dass die Prozedur erfolgreich war. Schließlich zeigt die letzte select-Anweisung an, dass der Lehrer in der Tat der Klasse zugewiesen wurde. Listing 13.12: Aufruf einer Prozedur in einem Paket aus einem anonymen PL/SQL-Block heraus SQL> set serveroutput on SQL> SQL> update class 2 set 3 Instructor_ID = null 4 where 5 class_ID = 104200; 1 row updated. SQL> select Instructor_ID 2 from Class 3 where 4 Class_ID = 104200; INSTRUCTOR_ID ---------------SQL> declare 2 status number; 3 begin 4 5 Flugle.Assign_Instructor (104200, 491, status); 6 dbms_output.put_line('Status: ' || to_char(status)); 7 end; 8 / Status: 0 PL/SQL procedure successfully completed. SQL> select Instructor_ID 2 from Class 3 where 4 Class_ID = 104200; INSTRUCTOR_ID ------------491
Entwerfen eines Pakets für Datenbanktrigger Die Prozeduren und Funktionen eines Pakets können aus SQL*Plus-Skripten, aus PL/ SQL-Unterprogrammen,
aus Skripten von Client-Anwendungen (wie Oracle Forms oder Power Builder) aufgerufen werden - sowie aus Datenbanktriggern. Ein Datenbanktrigger kann jedoch keine gespeicherte Prozedur, Funktion und kein Unterprogramm eines Pakets aufrufen, das eine commit-, rollback- oder savepoint-Anweisung enthält. Wenn Sie die Flexibilität benötigen, dass die Unterprogramme eines Pakets von einem Datenbanktrigger aufgerufen werden können, stellen Sie deshalb sicher, dass keine der Prozeduren und Funktionen im Paket Transaktionen festschreiben oder rückgängig machen. Weitere Kenntnisse über Trigger werden Sie am Tag 14 »Weitere PL/SQL Programmiertechniken erwerben.
13.8 Zusätzliche PL/SQL-Datentypen Am Tag 12, »Programmierung einer Oracle-Datenbank mit PL/SQL«, haben Sie Grundzüge der Programmierung von PL/SQL kennen gelernt. Wie Sie gesehen haben, unterstützt PL/SQL alle Datentypen, die in SQL verfügbar sind. PL/SQL bietet jedoch die folgenden zusätzlichen Datentypen, die in gewöhnlichen SQL-Anweisungen nicht zur Verfügung stehen: ● ● ● ● ● ●
Boolean binary_integer, natural und positive %type %rowtype die PL/SQL-Tabelle (oder den Array) den benutzerdefinierten Datensatz RECORD
Der Datentyp Boolean Einer der zusätzlichen Datentypen, die PL/SQL unterstützt, ist Boolean. Listing 13.13 zeigt, wie Sie eine BooleanVariable deklarieren. Sie können eine Boolean-Variable mit true oder false initialisieren. Listing 13.13: Deklaration einer Variablen vom Datentyp Boolean und Initialisierung dieser Variablen SQL> set serveroutput on SQL> declare 2 Payment_Is_Late Boolean := TRUE; 3 begin 4 5 if Payment_Is_Late then 6 dbms_output.put_line('The payment is late!'); 7 end if; 8 8 end; 9 / The payment is late! PL/SQL procedure successfully completed.
In Zeile 2 wird die Boolean-Variable Payment_Is_Late mit true initialisiert. In Zeile 5 wird Payment_Is_Late ausgewertet; weil Zeile 5 true ergibt, wird Zeile 6 ausgeführt. Bis Sie ihr einen Wert zuweisen, hat eine Boolean-Variable den Wert NULL. In Listing 13.14 wird der BooleanVariablen Payment_Is_Late der Boolean-Ausdruck Day_of_Month > 5 zugewiesen. Listing 13.14: Zuweisung eines Ausdrucks vom Datentyp Boolean an eine Variable desselben Datentyps
SQL> set serveroutput on SQL> declare Payment_Is_Late Boolean; Day_of_Month integer; begin select to_number(to_char(sysdate,'DD')) into Day_of_Month from dual; Payment_Is_Late := Day_of_Month > 5; if Payment_Is_Late then dbms_output.put_line('The payment is late!'); end if; end; / The payment is late! PL/SQL procedure successfully completed.
Der Datentyp binary_integer Der Datentyp binary_integer speichert vorzeichenbehaftete ganze Zahlen im Bereich von -2147483647 bis 2147483647. PL/SQL bietet auch zwei andere Datentypen, die Untertypen von binary_integer sind. ● ●
natural kann ganze Zahlen im Bereich von 0 bis 2147483647 speichern. positive kann ganze Zahlen im Bereich von 1 bis 2147483647 speichern.
Zahlen, die niemals einen Nachkommateil haben, wie etwa Schleifenzähler, können Sie mit den Datentypen natural oder positive deklarieren. Wenn Sie eine reelle Zahl einer als binary_integer, natural oder positive deklarierten Variablen zuweisen, werden die Nachkommastellen abgeschnitten. Listing 13.15 zeigt ein Beispiel dafür. Listing 13.15: Eine reelle Zahl wird abgeschnitten, wenn sie einer Variablen eines ganzzahligen PL/SQLDatentyps zugewiesen wird SQL> declare 2 Counter natural; 3 begin 4 5 Counter := 103.2; 6 dbms_output.put_line('Counter: ' || to_char(Counter,'999.999')); 7 end; 8 / Counter: 103.000
Benutzerdefinierte Datensätze Sie haben bereits das Konstrukt %rowtype kennen gelernt, um einen Datensatz auf Basis einer Tabelle zu deklarieren. Falls Sie einen Datensatz benötigen, dessen Elemente nicht mit den Spalten einer Tabelle übereinstimmen, verwenden Sie eine Typdeklaration mit folgender Syntax:
TYPE type_name IS RECORD (field_declaration...[,field_declaration]);
Die Bedeutungen der Platzhalter sind: ● ●
type_name ist der Name des Datentyps, der einen benutzerdefinierten Datensatz beschreibt. field_declaration ist der Name eines Elements des benutzerdefinierten Datensatzes. Dabei ist field_declaration wie folgt aufgebaut: field_name field_type [[NOT NULL] {:= | DEFAULT} expression] field_name ist ein gültiger Oracle-Objektname, field_type ist ein vordefinierter oder benutzerdefinierter Datentyp, expression ein Ausdruck dessen Auswertung den Standardwert ergibt.
Listing 13.16 zeigt ein Beispiel für einen benutzerdefinierten Datensatz. Listing 13.16: Vereinbarung eines benutzerdefinierten Datentyps DECLARE TYPE SickDays IS RECORD ( instructor_id instructor.instructor_id%TYPE, sick_days_cumulative integer); BEGIN null; END; Ein benutzerdefinierter Datensatz bietet mehr Flexibilität als die Bezeichnung %rowtype. Sie sollten die Verwendung eines benutzerdefinierten Datensatzes in Betracht ziehen, wenn eine der folgenden Bedingungen erfüllt ist: ● ●
Sie benötigen nur eine Teilmenge der Spalten einer Tabelle. Sie wollen auch verknüpfte oder abgeleitete Informationen im Datensatz speichern.
PL/SQL Collections PL/SQL unterstützt drei Arten von Collections (Ansammlungen): Index-by Tables (indizierte Tabellen), Nested Tables (verschachtelte Tabellen) und Varrays (Variable-Size Arrays - Felder variabler Größe). Nested Tables und Varrays haben Sie am Tag 11 »Tabellen und Indizes für Fortgeschrittene« kennen gelernt. Ein Index-by Table fasst mehrere gleichartige Datenelemente unter einer PL/SQL-Tabelle als übergeordnete Struktur zusammen und erlaubt den Zugriff auf einzelne Datenelemente über die Position innerhalb der Ansammlung. Collections können auch als Argumente an Prozeduren oder Funktionen verwendet werden. Index-by Tables werden in PL/SQL Programmen wie Arrays in Programmiersprachen wie C verwendet. Ein weiteres Einsatzgebiet ist die Manipulation großer Datenmengen über die so genannte Bulk Schnittstelle. Mit Bulk Operationen ist es möglich, in einer select- Anweisung einen Index-By Table oder ein ganzes Array in einer Programmiersprache wie C mit mehreren Hundert Zeilen aus einer Datenbanktabelle zu füllen. Bulk Operationen sind wesentlich effizienter als die Standardmethode, die jede Zeile einzeln vom Datenbankserver holt.
Wenn Sie in PL/SQL eine select-Anweisung als Bulk Operation ausführen möchten, verwenden Sie folgende Syntax: SELECT column_name1 ... [, column_nameN] BULK COLLECT INTO collection_name1 ... [, collection_nameN] FROM table_name;
Die Platzhalter sind wie folgt definiert: ● ● ●
column_name1 bis column_nameN ist eine Liste von Spaltennamen. collection_name1 ... collection_nameN sind Variablennamen von PL/SQL Index-By Tabellen. table_name ist ein Tabellenname.
Dem BULK COLLECT bei select-Anweisungen entspricht FORALL bei insert-, update- und delete-Anweisungen. Mehr Informationen zu Bulk Operationen finden Sie im »PL/SQL User's Guide and Reference« und im »Pro*C/C++ Precompiler Programmer's Guide«. Nested Tables in PL/SQL entsprechen Nested Tables in der Datenbank. Nested Tables in der Datenbank haben Sie am Tag 11 kennen gelernt. PL/SQL Nested Tables können unmittelbar für SQL-Anweisungen auf Nested Tables in der Datenbank verwendet werden und sind daher die einfachste und komfortabelste Möglichkeit auf Nested Tables in der Datenbank zuzugreifen. Im Gegensatz zu Index-By Tables und Nested Tables haben Varrays eine Größenbegrenzung. Jenseits einer bestimmten Positionsnummer enthält ein Varray keine Datenelemente. Ein Varray kann erweitert werden, jedoch gilt grundsätzlich, dass bei Zugriff auf eine zu große Positionsnummer ein Fehler auftritt. Varrays sind immer geordnet, Nested Tables nicht. Der folgende Abschnitt beschäftigt sich mit der Verwendung von PL/SQL Index-By Tables.
Deklarieren von PL/SQL Index-By Tabellen
Der Typ für eine PL/SQL Index-By Tabelle wird mit der folgenden Syntax vereinbart: TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY BINARY_INTEGER; Die Bedeutungen der Platzhalter sind: ● ●
type_name ist der Name des deklarierten Typs. element_type ist der Name eines vordefinierten oder benutzerdefinierten Datentyps. Sie können auch %type verwenden.
Nachdem Sie den PL/SQL-Tabellentyp angelegt haben, deklarieren Sie Variablen auf der Basis dieses Typs. Beispielsweise wird in Listing 13.17, das einen anonymen PL/SQL-Block enthält, Class_ID_Tab als Tabelle der Spalte Class_ID in der Tabelle Class deklariert. Eine select-Anweisung mit BULK COLLECT liest alle Zeilen aus der Tabelle Class. Listing 13.17: Verwendung einer PL/SQL-Tabelle mit SELECT ... BULK COLLECT SQL> set serveroutput on SQL> declare type Class_ID_Tab_Type is table of class.class_id%type index by binary_integer; type Class_Building_Tab_Type is table of class.Class_Building%type index by binary_integer; Class_ID_Tab Class_ID_Tab_Type; Class_Building_Tab Class_Building_Tab_Type; begin SELECT class_id, Class_Building BULK COLLECT INTO
Class_ID_Tab, Class_Building_Tab FROM class; for i in 1..Class_ID_Tab.COUNT loop dbms_output.put_line(Class_ID_Tab(i)||' '||Class_Building_Tab(i)); end loop; end; / 108600 103400 103600 PL/SQL
VILING TOWER FLUGLE HALL POCO HALL procedure successfully completed.
Sie können eine PL/SQL-Tabelle als Argument an eine Prozedur oder Funktion übergeben. Zusammen mit der PL/SQL-Tabelle werden Sie wahrscheinlich auch eine binary_integer-Variable übergeben, die die Anzahl der Elemente in der PL/SQL-Tabelle angibt. Listing 13.18 zeigt ein Beispiel für eine Prozedur, die eine PL/SQLTabelle zurückgibt, die diejenigen Course_IDs enthält, für welche die zusätzlichen Gebühren mehr als 50 betragen. Listing 13.18: Rückgabe einer PL/SQL-Tabelle declare type Course_ID_Type is table of Course.Course_ID%TYPE index by binary_integer; Course_ID_Tab Course_ID_Type; i binary_integer := 0; Total_Number binary_integer; procedure Get_Course_IDs (Num_Rows out binary_integer, Course_ID_Table out Course_ID_Type) is i binary_integer := 0; begin for Course_ID_Rec in (select Course_ID from Course where Additional_Fees > 50) loop i := i + 1; Course_ID_Table(i) := Course_ID_Rec.Course_ID; end loop; Num_Rows := i; end Get_Course_IDs; -- Main block. begin Get_Course_IDs (Total_Number, Course_ID_Tab); for i in 1..Total_Number loop exit when Course_ID_Tab(i) = NULL; dbms_output.put_line('Course_ID_Tab(' || to_char(i) || ') = ' || Course_ID_Tab(i)); end loop; end; / PL/SQL procedure successfully completed. Course_ID_Tab(1) = 10021 Course_ID_Tab(2) = 10012 Course_ID_Tab(3) = 10081
Die erste Zeile in Listing 13.18 beginnt mit dem Deklarationsteil eines anonymen Blocks. Der Typ Course_ID_Type wird in Zeile 3 vereinbart. Die Deklaration der Prozedur Get_Course_IDs beginnt in Zeile 10. In Zeile 13 wird der Index der PL/SQL-Tabelle Course_ID_Table auf 0 initialisiert. Der Ausführungsteil des anonymen Blocks beginnt in Zeile 31. In Zeile 35 wird im Ausführungsteil des anonymen Blocks die Prozedur Get_Course_IDs aufgerufen.
PL/SQL schränkt den Bereich des Index einer PL/SQL-Tabelle auf den Wertebereich eines BINARY_INTEGER ein. Sie könnten -100, 0 oder 1 als Index verwenden, je nachdem, wie es gerade nötig ist.
13.9 Zusammenfassung Diese Lektion hat sich auf die folgenden Konzepte konzentriert: ●
●
●
●
●
●
●
●
● ●
●
●
●
●
Sie können die Oracle-DDL-Anweisungen create procedure und create function verwenden, um gespeicherte Prozeduren bzw. Funktionen zu erstellen. Eine gespeicherte Prozedur oder Funktion besteht aus Deklarationen und ausführbaren Anweisungen in PL/SQL, die übersetzt und in der Datenbank gespeichert werden. Sie können für eine gespeicherte Prozedur oder Funktion Argumente definieren, die nur übergeben werden, die nur zurückgegeben werden, oder die übergeben und zurückgegeben werden. Oracle stellt ein vordefiniertes Paket, dbms_output, zur Verfügung, das Routinen für das Anzeigen von Informationen aus einem PL/SQL-Unterprogramm heraus enthält. Eine gespeicherte Funktion kann in einer Oracle-SQL-Anweisung aufgerufen werden. Der Datentyp der übergebenen Werte muss dem zugehörigen Argument entsprechen. Ein Paket ist eine Gruppe verwandter Prozeduren und Funktionen. Ein Paket hat eine Spezifikation und einen Rumpf. Eine Paketspezifikation wird mit der Anweisung create package erzeugt. Ein Paketrumpf wird mit Hilfe der Anweisung create package body erzeugt. PL/SQL unterstützt mehrere Datentypen, die in SQL-Anweisungen von Oracle nicht zur Verfügung stehen: Boolean, binary_integer, natural und positive. Eine Variable vom Typ Boolean kann die Werte true, false oder NULL haben. Indem Sie %type verwenden, können Sie Variablen deklarieren, die denselben Datentyp wie eine bestimmte Spalte einer Tabelle haben. Verwenden Sie den Bezeichner %rowtype, um einen Datensatz zu deklarieren, dessen Struktur einer bestimmten Tabelle entspricht. Sie können %rowtype auch verwenden, um einen Datensatz zu deklarieren, der dieselbe Struktur wie ein Cursor hat. Eine PL/SQL-Tabelle ist ein benutzerdefinierter Datentyp, den Sie sich als Ansammlung einzelner Elemente vorstellen können. Um eine PL/SQL-Tabelle zu deklarieren, müssen Sie zuerst einen Datentyp für die Tabelle deklarieren. Effiziente Operationen auf großen Datenmengen werden durch PL/SQL-Tabellen und BULK COLLECT oder FORALL unterstützt. Ein benutzerdefinierter Datensatz besteht aus einem oder mehreren Feldern. Der Datentyp jedes Felds kann unter Verwendung eines Datentyps von Oracle SQL oder PL/SQL explizit deklariert werden oder es wird der Bezeichner %type verwendet, um auf den Datentyp einer Datenbankspalte zu verweisen. Ein Feld kann als not null deklariert und bei seiner Deklaration initialisiert werden.
13.10 Wie geht es weiter? An Tag 14 werden Sie etwas über einige bedeutende Aspekte von PL/SQL lernen - wie man Fehler behandelt, wie man mehrere Zeilen mit einem Cursor abfragt, und wie man einen Trigger für eine Tabelle schreibt.
13.11 Fragen und Antworten Frage: Muss ein Paket erst gelöscht werden, um eine neue Version des Pakets anzulegen? Antwort: Nein, die alte Version wird überschrieben, wenn statt mit CREATE mit CREATE OR REPLACE gearbeitet wird. Frage: Ist es besser, anstatt einzelner Prozeduren und Funktionen ein Paket mit Prozeduren und Funktionen zu erstellen? Antwort: Aus der Perspektive der Software-Entwicklung ist es besser, ein Paket zu erstellen. Indem Sie eine Paketspezifikation erstellen, wird die Schnittstelle - die Namen aller Prozeduren und Funktionen und ihrer Argumente - vom PL/SQL-Code getrennt, der die Schnittstelle implementiert.
13.12 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Eine gespeicherte Prozedur kann eine gespeicherte Funktion aufrufen, eine gespeicherte Funktion jedoch keine gespeicherte Prozedur. 2. Nennen Sie drei Gründe für das Speichern von Prozeduren, Funktionen und Paketen in einer Anwendung. 3. Welche Werkzeuge können verwendet werden, um PL/SQL-Unterprogramme zu erstellen? 4. Wenn der Ausdruck x > 32 einer PL/SQL-Variablen zugewiesen wird, wie ist dann der Datentyp der Variablen?
Übungen 1. Erzeugen Sie einen anonymen PL/SQL-Block, der die Prozedur Assign_Grade aus dem Flugle-Paket aufruft und Anna Anastatia für den Kurs »INTRO TO BIOLOGY« am Lehrstuhl für Biologie die Note B zuweist. 2. Der Student Jackson Smythe hat eine gespeicherte Funktion namens Change_My_Grade mit zwei Argumenten erstellt: Student_ID und Class_ID. Die Funktion ändert die Note für jeden beliebigen Studenten und eine beliebige Klasse auf A+. Schreiben Sie diese Funktion
Programmierung einer Oracle-Datenbank mit PL/SQL SQL ist eine Sprache ohne prozedurale Fähigkeiten. Oracle hat mit der Sprache PL/SQL eine leistungsfähige Programmiersprache geschaffen, die das Einbetten von SQL Aufrufen ermöglicht. PL/SQL ist die Basis für die folgenden Elemente von Anwendungen: ● ●
●
●
●
SQL-Skripte: Ein SQL-Skript kann PL/SQL-Unterprogrammblöcke enthalten. Gespeicherte Prozeduren oder Funktionen: Eine gespeicherte Prozedur oder Funktion ist ein PL/SQL-Unterprogramm, das durch eine Client-Anwendung, einen Datenbanktrigger oder einen Anwendungstrigger eines Oracle-Werkzeugs wie z.B. Forms aufgerufen werden kann. Datenbanktrigger: Ein Datenbanktrigger ist ein PL/SQL-Block, der aufgrund der Ausführung einer DML-Anweisung - wie insert, update oder delete - auf einer Datenbanktabelle ausgeführt wird. Package (Paket): Eine Menge von PL/SQL-Prozeduren, Funktionen, Cursors und Variablen, die in einem Paket gebündelt sind. Anwendungstrigger: Entwicklungswerkzeuge für Oracle-Anwendungen wie Oracle Forms und Oracle Reports sind mit einer PL/SQL-Engine ausgestattet, so dass Entwickler mit PL/SQL Anwendungstrigger erstellen können.
Der Zweck der heutigen Lektion ist es, die fundamentalen Elemente von PL/SQL einzuführen. Sie sollten mit der Syntax und dem Gebrauch von PL/SQL vertraut sein, bevor Sie am Tag 13, »Programmentwicklung mit PL/SQL« versuchen, gespeicherte Prozeduren zu entwerfen.
Verwenden Sie nicht SQL*Plus, um die einzelnen Zeilen eines PL/SQLSkripts einzugeben. Wenn Ihnen ein Tippfehler unterläuft, gibt Ihnen SQL*Plus keine Rückmeldung, bis es den Schrägstrich (/) liest, das den PL/SQL-Block abschließt - SQL*Plus übergibt den gesamten Block erst an die PL/SQL-Engine, wenn der Block vollständig ist. Verwenden Sie stattdessen
einen Texteditor wie Notepad, um Ihre PL/SQL-Skripte zu entwickeln. Sie können das Skript mit Kopieren und Einfügen oder mit den Befehlen start bzw. @ von SQL*Plus abarbeiten lassen. Am Tag 19 werden Sie etwas über Procedure Builder lernen, der SQL*Plus bei der Entwicklung von Programmen weit überlegen ist. Es gibt Produkte von Drittherstellern mit ähnlichen Fähigkeiten; Sie können z.B. den SQL Station Coder von Platinum testen (www.platinum.com) oder das auf dem WEB*PL-Browser basierende Entwicklungswerkzeug von DBCorp (www.dbcorp.ab.ca).
12.1 Die Blockstruktur von PL/SQL PL/SQL ist eine blockstrukturierte Sprache. Zusätzlich zu eingebetteten SQLAnweisungen bietet PL/SQL Standardkonstrukte für das Programmieren wie Deklarationen von Variablen, Prozeduren und Funktionen, Kontrollanweisungen wie ifthen-else und loop. Ein PL/SQL-Programm besteht aus Prozeduren, Funktionen oder anonymen Blöcken. Ein anonymer Block ist ein unbenannter PL/SQL-Block, der keine Argumente besitzt und keine Werte zurückgibt. Anonyme Blöcke kommen häufig in Skripten vor, die in einer SQL*Plus-Sitzung ausgeführt werden. Abbildung 12.1 zeigt die oberste Ebene der Struktur eines SQL-Blocks. Dieser enthält einen optionalen Abschnitt mit Deklarationen, einen ausführbaren Abschnitt und einen optionalen Abschnitt für die Behandlung von Ausnahmen (exceptions) und Fehlern von PL/SQL und SQL.
Abbildung 12.1: Gliederungsstruktur eines PL/SQL-Blocks Lassen Sie uns einen einfachen anonymen PL/SQL-Block betrachten, der einige Testdaten erzeugt. Listing 12.1 enthält einen anonymen Block, der durch ein SQL*PlusSkript ausgeführt wird und 100 Zeilen in TEST_TABLE einfügt. Listing 12.1: Ausführung eines anonymen PL/SQL-Blocks in SQL*Plus SQL> create table test_table ( record_number int, current_date date);
Table created. SQL> DECLARE 2 2 max_records CONSTANT int := 100; 3 i int := 1; 4 4 BEGIN 5 5 FOR i IN 1..max_records LOOP 6 6 INSERT INTO test_table 7 (record_number, current_date) 8 VALUES 9 (i, SYSDATE); 10 10 END LOOP; 11 11 COMMIT; 12 END; / PL/SQL procedure successfully completed.
Werfen Sie einen Blick auf einige Elemente dieses PL/SQL-Skripts. Dieses Skript ist ein anonymer PL/SQL-Block, da es keinen Namen hat - es wird nicht als Prozedur, Funktion oder Paket deklariert. Alle Zeilen dieses Skripts sind in einem einzigen SQL*Plus-Skript enthalten. Der PL/SQL-Block beginnt mit dem Wort declare. Der Deklarationsabschnitt enthält eine Konstante max_records (in Zeile 2) und eine Variable i (in Zeile 3), diese dient als Zähler. Der Anfang des ausführbaren Teils des Blocks wird durch begin gekennzeichnet (in Zeile 4). Der Block enthält eine einzelne for loop (in Zeile 5), die eine Zeile in TEST_TABLE einfügt, solange i kleiner oder gleich max_records ist. Wenn die for loop beendet ist, wird die Transaktion festgeschrieben (in Zeile 11). Die letzte Zeile des Skripts ist ein /, dies veranlasst SQL*Plus dazu, den PL/SQL-Block an die PL/SQL-Engine zu schicken. Wenn kein PL/SQL-Übersetzungsfehler auftritt, ist die einzige Rückmeldung von SQL*Plus die Meldung: PL/SQL procedure successfully completed. In der nächsten Lektion werden Sie sehen, wie man in PL/SQL Diagnosemeldungen erzeugt, so dass man die Fortschritte bei der Ausführung eines PL/SQL-Unterprogramms beobachten kann.
Der Deklarationsteil und Standardwerte Der Deklarationsteil eines PL/SQL-Blocks ist optional. Sie müssen jedoch alle Variablen und Konstanten deklarieren, die in den PL/SQL-Anweisungen verwendet werden. Um einen Deklarationsteil in einen PL/SQL-Block aufzunehmen, beginnen Sie den PL/SQLBlock mit dem Wort declare. Jede Deklaration einer Variablen oder Konstanten besteht aus ihrem Namen, dem Datentyp und einem optionalen Standardwert.
Die Syntax ist: variable_name datatype := initial_value; oder variable_name datatype DEFAULT initial_value; Die Bedeutungen der Platzhalter sind: ● ● ●
variable_name ist ein Variablenname. datatype ist ein PL/SQL Datentyp. initial_value ist ein Standardwert.
Als Standard werden beim Eintritt in eine Prozedur, eine Funktion oder einen anonymen Block alle Variablen mit NULL initialisiert. Listing 12.2 enthält einige Beispiele für Deklarationen von Variablen und Konstanten. Listing 12.2: Beispiele deklarierter Variablen und Konstanten SQL> declare Fax_Number VARCHAR2(10); i natural := 33; mystring varchar2(30) default 'SIEGEL'; begin null; end; / PL/SQL procedure sucessfully completed. Ein Grund, einen Standardwert für eine Variable anzugeben, ist, dass der Code oft besser zu verstehen und zu warten ist.
Der Ausführungsteil Der Ausführungsteil eines PL/SQL-Blocks folgt auf das Schlüsselwort begin. Jede PL/SQL- Anweisung wird durch ein Semikolon beendet. Diese Anweisungen können wie folgt aufgeteilt werden: ● ● ● ●
Zuweisungsanweisungen Anweisungen, die den Programmablauf steuern SQL-Anweisungen Cursoranweisungen
Der Exception-Block Eine Exception ist eine Fehlerbedingung, die während der Ausführung eines PL/SQLProgramms auftritt. Eine Exception kann vordefiniert sein - wenn beispielsweise eine insert-Anweisung eine Primärschlüssel-Integritätsbedingung verletzt, wird eine dup_val_on_ index-Exception ausgelöst. Sie können auch Ihre eigenen, für die Anwendung spezifischen Exceptions definieren. Der Exception-Block definiert die Exception-Handler, die sowohl für vordefinierte, als auch für benutzerdefinierte Exceptions aufgerufen werden. Jeder Exception-Handler besteht aus einer oder mehreren PL/SQL-Anweisungen.
12.2 Variablendeklaration mit PL/SQL Eine weitere Fähigkeit von PL/SQL - jedoch nicht von SQL - sind zusätzliche Datentypen. Zusätzlich zu den normalen Datentypen von Oracle-SQL können Sie mit PL/ SQL Variablen der folgenden Datentypen deklarieren: Zusätzlich bietet PL/SQL zwei zusammengesetzte Datentypen: table und record. Sie werden an Tag 13 mehr darüber lernen.
Deklaration einer Variablen mit Hilfe von %type
Die Syntax für die Deklaration einer Variablen mit %type ist: variable_name table_name.column_name%TYPE; Die Bedeutungen der Platzhalter sind:
● ●
●
variable_name ist die Variable, die Sie deklarieren. table_name ist eine Tabelle, die eine Spalte enthält, deren Typ die Variable annehmen soll. column_name ist eine Spalte, die in table_name definiert ist.
Eine Variable, die den Namen eines Servicetechnikers speichert, deklarieren Sie beispielsweise auf folgende Weise: Tech_Name Depot_Estimate.Technician%TYPE; Der Vorteil der Verwendung von %type in einer Variablendeklaration ist, dass der PL/SQLCode von der Definition der Spalte Technician in der Tabelle Depot_Estimate abhängt.
Deklaration einer Variablen mit %rowtype Mit %rowtype sind Sie in der Lage eine Variable zu deklarieren, die einen vollständigen Datensatz (Record) aus einer Tabelle enthält.
Die Syntax für die Deklaration einer Variablen mit %rowtype ist: variable_name table_name%ROWTYPE; Die Bedeutungen der Platzhalter sind: ●
●
variable_name ist der Name der Variable, die einen Datensatz aus table_name abbildet. table_name ist eine Tabelle. Jeder Spalte in table_name entspricht ein Attribut des Datensatzes. Der Zugriff auf ein Attribut von variable_name geschieht durch das Suffix ».column_name«, wobei column_name eine Spalte von table_name ist.
Eine Variable, die eine Zeile aus der Tabelle Instructor speichert, wird beispielsweise folgendermaßen deklariert: v_instructor_row Instructor%ROWTYPE; Auf ein Attribut von v_instructor_row kann folgendermaßen zugegriffen werden: v_instructor_row.last_name := 'RICHARDSON';
Obwohl Sie in einer select-Anweisung auf einen Datensatz verweisen können, der mit %rowtype deklariert wurde, können Sie nicht mit einer insertAnweisung auf den gesamten Datensatz verweisen. PL/SQL weist beispielsweise die folgende insert-Anweisung zurück: SQL> declare 2 Patient_Rec Patient%rowtype; 3 3 begin 4 4 Patient_Rec.Patient_ID := 'HHH111'; 5 Patient_Rec.Body_Temp_Deg_F := 102.7; 6 6 insert into Patient 7 (Patient_ID, Body_Temp_Deg_F) 8 values 9 Patient_Rec; 10 end; / Patient_Rec; * ERROR at line 9: ORA-06550: line 9, column 1: PLS-00103: Encountered the symbol "PATIENT_REC" when expecting one of the follow an aggregate Resuming parse at line 9, column 12. Statt dessen müssen Sie jede Komponente des Datensatzes Patient_Rec angeben, die den in der insert-Anweisung angegebenen Spalten entspricht. Sie können auch eine Variable einer anderen Variablen zuweisen, wenn beide mit der Bezeichnung %rowtype für dieselbe Tabelle deklariert wurden. Listing 12.3 zeigt dieses Konzept, indem New_Patient an ER_Patient zugewiesen wird. Listing 12.3: Zuweisungen an PL/SQL-Variablen, die auf %rowtype basieren SQL> declare 2
2 New_Patient Patient%ROWTYPE; 3 ER_Patient Patient%ROWTYPE; 4 begin 5 6 select * 7 into New_Patient 8 from Patient 9 where 10 Patient_ID = 'ZZ0123'; 11 11 ER_Patient := New_Patient; 12 12 dbms_output.put_line('ER_Patient.Body_Temp_Deg_F: ' || 13 to_char(ER_Patient.Body_Temp_Deg_F)); 14 end; 15 / ER_Patient.Body_Temp_Deg_F: 98.6 Sie können jedoch eine %rowtype-Variable nicht einer anderen %rowtype-Variablen zuweisen, wenn beide nicht auf dieselbe Tabelle verweisen, auch wenn die Tabellen identische Spalten haben. Listing 12.4: %rowtype-Variablen, die auf unterschiedlichen Tabellen basieren, können einander nicht zugewiesen werden SQL> create table Identical_Patient as select * from Patient; Table created. SQL> set serveroutput on SQL> declare New_Patient Patient%ROWTYPE; ER_Patient Identical_Patient%ROWTYPE; begin select * into New_Patient from Patient where Patient_ID = 'ZZ0123'; ER_Patient := New_Patient; dbms_output.put_line('ER_Patient.Body_Temp_Deg_F: ' || to_char(ER_Patient.Body_Temp_Deg_F)); end; / declare *
ERROR at line 1: ORA-06550: line 11, column 15: PLS-00382: expression is of wrong type ORA-06550: line 11, column 1: PL/SQL: Statement ignored
12.3 Einige bekannte Kontrollstrukturen Verschiedene PL/SQL-Kontrollanweisungen steuern den Programmablauf in einem PL/ SQL-Unterprogramm. ● ● ● ● ● ● ●
if then elsif end if loop exit while loop for loop goto null
Bevor Sie gespeicherte Prozeduren oder Trigger programmieren, sollten Sie mit den Grundlagen der Programmierung in PL/SQL vertraut sein. Der nächste Teil dieser Lektion zeigt den Gebrauch dieser Anweisungen im Detail.
Die if-Anweisung Die Syntax der if-then-elsif-Anweisung von PL/SQL unterscheidet sich etwas von der vergleichbaren Anweisung in der Programmiersprache C.
Die PL/SQL-Syntax ist: IF condition THEN statement; ... [statement;] [ELSIF condition THEN statement; ... [statement;]] ... [ELSIF condition THEN statement; ... [statement;]] [ELSE condition THEN statement; ... [statement;]] END IF;
Die Bedeutungen der Platzhalter sind: ● ●
condition ist eine gültige PL/SQL-Bedingung. statement ist eine gültige PL/SQL-Anweisung.
Was die if-then-elsif-Anweisung angeht, beachten Sie bitte folgende Tatsachen: ● ● ●
Die elsif- und else-Klauseln sind optional. Eine if-Anweisung kann mehrere elsif-Klauseln, aber nur eine else-Klausel haben. Beachten Sie die Schreibweise: elsif, nicht elseif.
Listing 12.5 zeigt ein einfaches Beispiel für die if-then-elsif-Anweisung. Listing 12.5: Verwendung der if-then-elsif-Anweisung if MOD(i,5) = 0 then rec_number := 5; elsif MOD(i,7) = 0 then rec_number := 7; else rec_number := i; end if;
Die einfache loop-Anweisung Der einfachste Schleifentyp ist die loop-Anweisung ohne zusätzliche Kennzeichner.
Die Syntax lautet: LOOP statement; ... [statement;] END LOOP Die Variable ist folgendermaßen definiert: ●
statement ist eine gültige PL/SQL-Anweisung, die auch eine weitere Schleife enthalten kann.
Diese Schleife ist offensichtlich eine Endlosschleife. Um diese Schleife zu verlassen,
wenn eine bestimmte Bedingung erfüllt ist, verwenden Sie die exit-Anweisung.
Die exit-Anweisung Die exit-Anweisung hat zwei Formen: ● ●
exit ohne andere Klauseln - ein unbedingtes Exit exit [label_name] when condition
Die erste Form von exit sorgt dafür, dass der Programmablauf die Schleife verlässt, in der die exit-Anweisung eingeschlossen ist. Die zweite Form von exit sorgt dafür, dass der Programmablauf die einschließende Schleife verlässt, wenn die angegebene Bedingung erfüllt ist, wie in Listing 12.6 gezeigt wird. Listing 12.6: Verwendung der exit-Anweisung SQL> declare i positive := 1; max_loops constant positive := 100; begin loop i := i + 1; exit when i > max_loops; end loop; end; / PL/SQL procedure successfully completed.
Die while-loop-Anweisung Die while-loop-Anweisung fügt eine Bedingung zu einer Schleife hinzu.
Die Syntax lautet: WHILE condition LOOP statement; ... [statement;] END LOOP;
Die Bedeutungen der Platzhalter sind: ● ●
condition ist eine gültige PL/SQL-Bedingung. statement ist eine gültige PL/SQL-Anweisung.
Listing 12.7 zeigt die Verwendung der while-loop-Anweisung. Listing 12.7: Verwendung der while-loop-Anweisung WHILE I < 100 LOOP I := I + 1; insert into temp_table (rec_number) values (I); END LOOP;
Die for-loop-Anweisung Die for-loop-Anweisung ist der while-loop-Anweisung recht ähnlich.
Hier ist die Syntax: FOR loop_variable IN [REVERSE] lower_bound .. upper_bound LOOP statement; ... [statement;] END LOOP; Die Platzhalter sind folgendermaßen definiert: ● ● ● ●
loop_variable ist eine Integervariable, die als Schleifenzähler dient. lower_bound ist der Startwert des Schleifenzählers. upper_bound ist der Endwert des Schleifenzählers. statement ist eine PL/SQL Anweisung.
REVERSE ist ein optionales Schlüsselwort, das dafür sorgt, dass die Schleife von upper_bound bis lower_bound abwärts gezählt wird. Listing 12.8 zeigt die Verwendung der for-loop-Anweisung. Listing 12.8: Verwendung einer for-loop-Anweisung
SQL> set serveroutput on SQL> for i in 1..max_loops loop j := j + j; dbms_output.put_line('j: ' || to_char(j)); end loop;
Viele der Beispiele in dieser Lektion rufen ein Paket namens dbms_output auf. Dieses Paket enthält Prozeduren, die nützlich sind, um Bildschirmausgaben aus einem PL/SQL-Block zu implementieren. Das Paket dbms_output legt die übergebenen Argumente in einem Pufferbereich ab. Der Pufferbereich hat eine maximale Größe von 1000000 Bytes. Der Befehl set serveroutput on gibt SQL*Plus die Anweisung, den Inhalt des Pufferbereiches nach der Abarbeitung eines PL/SQL Blocks auszugeben. Die Syntax lautet: set serveroutput on [ size integer], wobei integer die Größe des Pufferbereichs angibt.
Die goto-Anweisung PL/SQL erlaubt es Ihnen, die potentiell gefährliche goto-Anweisung zu verwenden. Um goto zu verwenden, müssen Sie natürlich ein Label angeben, zu dem der Programmablauf springen kann. In PL/SQL wird ein Label mit dem Namen label_name folgendermaßen definiert: <> Listing 12.9 zeigt ein Beispiel einer goto-Anweisung, ein Label kann als Alternative zu einer exit-Anweisung verwendet werden. Listing 12.9: Verwendung der goto-Anweisung SQL> declare 2 2 i positive := 1; 3 max_loops constant positive := 100; 4 4 begin 5 5 i := 1; 6 6 loop
7 7 i := i + 1; 8 if i > max_loops then 9 goto more_processing; 10 end if; 11 11 end loop; 12 12 <<more_processing>> 13 i := 1; 14 14 end; 15 / PL/SQL procedure successfully completed.
Die null-Anweisung In bestimmten Situationen sollten Sie PL/SQL anzeigen, dass keine Aktion durchgeführt werden soll. In einem Exception-Handler kann es beispielsweise vorkommen, dass Sie nichts tun wollen, wenn eine bestimmte Exception auftritt. Um Klarheit zu schaffen, verwenden Sie die null-Anweisung in einem if-then-elsif, um anzuzeigen, dass für eine bestimmte elsif-Klausel keine Aktion durchgeführt werden soll. Unglücklicherweise hat Oracle dieser Anweisung den Namen null gegeben, obwohl sie nichts mit dem NULL-Wert zu tun hat. Listing 12.10 zeigt, wie die null-Anweisung eingesetzt werden kann. Listing 12.10: Verwendung der null-Anweisung if (mod(i,10) = 0) then i := i + 1; else NULL; end if;
Die Zuweisungsanweisung Wie Sie bereits gesehen haben, verwendet PL/SQL ein :=, um einer PL/SQL-Variablen einen Wert zuzuweisen. Sie können den Standardwert einer Konstanten oder Variablen im Deklarationsteil angeben. Eine Bemerkung: Sie können einer Variablen, die mit der Notation %type deklariert wurde, nicht null zuweisen, wenn die referenzierte Spalte als not null definiert ist.
Einfügen von Kommentaren in ein PL/SQL-Unterprogramm
PL/SQL bietet Ihnen zwei Wege, Ihren Quellcode zu dokumentieren. Zum einen können Sie in jeder Zeile einen Kommentar angeben, indem Sie zwei Bindestriche (--), gefolgt vom Kommentar, angeben, wie hier gezeigt wird: Depot_Est_Row.Technician := Last_Tech_Name; -- Assign the name of the last technician involved Sie können auch Kommentare im C-Stil verwenden - indem Sie diese zwischen /* und */ einschließen. Diese Methode ist am besten für mehrzeilige Kommentare geeignet, wie in Listing 12.11 gezeigt wird. Listing 12.11: Kommentieren des PL/SQL-Codes j:= j + 1; /* The next section inserts a row into the Utility_Audit table to record the name of the current Oracle user and the current date and time (SYSDATE). */ insert into Utility_Audit ...
12.4 SQL-Anweisungen in einem PL/SQL-Programm verwenden Sie können SQL-Anweisungen in einem anonymen Block, in einer Prozedur oder in Funktionen, wie Sie in SQL*Plus angewandt werden, mit nur wenigen Abweichungen verwenden. Wie andere PL/SQL-Anweisungen wird jede SQL-Anweisung mit einem Semikolon beendet. Sie können jedoch in PL/SQL in einer SQL-Anweisung auf deklarierte Variablen zugreifen. Listing 12.12 zeigt an einem Beispiel, wie man in einer SQLAnweisung auf deklarierte Variablen zugreift. Listing 12.12: Zugriff auf Variablen in einer SQL-Anweisung DECLARE max_records CONSTANT int := 100; i int := 1; BEGIN FOR i IN 1..max_records LOOP if (mod(i,10) = 0) then INSERT INTO test_table (record_number, current_date) VALUES (i, SYSDATE);
else NULL; end if; END LOOP; COMMIT; END; / In diesem Beispiel verwendet die insert-Anweisung die numerische Variable i und die Pseudospalte sysdate, um die Werte in den Spalten Record_Number und Current_Date zu platzieren.
PL/SQL und die select-Anweisung In einem PL/SQL-Unterprogramm verwendet die select-Anweisung die into-Klausel, um die PL/SQL-Variablen zu identifizieren, in die Spaltenwerte eingetragen werden sollen. Die into-Klausel steht zwischen der Auswahlliste und der from-Klausel. Listing 12.13 enthält ein Beispiel für einen anonymen PL/SQL-Block, der eine select-Anweisung enthält. Listing 12.13: Verwendung einer select-Anweisung in einem PL/SQL-Block SQL> set serveroutput on SQL> declare Average_Body_Temp Patient.Body_Temp_Deg_F%type; begin select avg(Body_Temp_Deg_F) into Average_Body_Temp from Patient; dbms_output.put_line('Average body temp in Deg. F: ' ||to_char(Average_Body_Temp,'999.99')); end; / Average body temp in Deg. F: 99.80 PL/SQL procedure successfully completed.
12.5 PL/SQL-Unterprogramme PL/SQL unterstützt auch die Verwendung von Unterprogrammen - benannte Prozeduren und Funktionen. Eine PL/SQL-Prozedur führt diverse Aktionen durch und kann optionale Parameter haben. Eine PL/SQL-Funktion gibt einen Wert mit einem bestimmten Datentyp zurück und kann ebenfalls optionale Parameter haben.
12.6 Verwenden von Unterblöcken PL/SQL ermöglicht es Ihnen, in einen Block Unterblöcke einzuschließen. Listing 12.14 zeigt beispielsweise einen anonymen Block, der einen anderen anonymen Unterblock enthält, der seinen eigenen Deklarationsteil hat. Listing 12.14: Beispiel für einen Unterblock SQL> declare 2 2 max_i constant int := 100; 3 i int := 1; 4 rec_number int; 5 5 begin 6 6 for i in 1..max_i loop 7 7 if mod(i,5) = 0 then 8 rec_number := 5; 9 elsif mod(i,7) = 0 then 10 rec_number := 7; 11 else 12 rec_number := i; 13 end if; 14 14 insert into test_table 15 (record_number, current_date) 16 values 17 (rec_number, sysdate); 18 18 - Here is a sub-block: 19 19 declare 20 max_j constant int := 20; 21 j int := 1; 22 22 begin 23 23 for j in 1..max_j loop 24 24 rec_number := rec_number * j; 25 25 insert into test_table
Die erste Zeile von Listing 12.14 startet den Deklarationsteil des anonymen Blocks. In Zeile 5 beginnt der Ausführungsteil des Hauptblocks. Der Deklarationsteil des Unterblocks beginnt in Zeile 19; Zeile 22 markiert den Beginn des Ausführungsteils des Unterblocks.
Obwohl PL/SQL die Möglichkeit unterstützt, Blöcke ineinander einzubetten, ist diese Praxis aus zwei Gründen nicht wünschenswert. Zum einen reduziert sie die Lesbarkeit - und damit die Wartbarkeit - Ihres Codes, zum anderen können eingebettete Blöcke nicht von anderen PL/SQL-Unterprogrammen verwendet werden. Sie sollten darauf achten, Prozeduren und Funktionen so zu entwerfen, dass die Wiederverwendbarkeit des Codes und seine Wartbarkeit erhöht werden.
12.7 Deklarieren einer Prozedur Zusätzlich zu anonymen Blöcken können Sie in PL/SQL auch Prozeduren und Funktionen deklarieren.
Die Syntax für die Deklaration einer Prozedur ist: PROCEDURE procedure-name [(argument1 ... [, argumentN]) ] IS [local-variable-declarations] BEGIN executable-section [exception-section] END [procedure-name]; Die Bedeutungen der Platzhalter sind: ●
●
●
●
●
procedure-name ist der Name der Prozedur, er unterliegt den Einschränkungen von Oracle-Datenbanken bezüglich der Namensgebung von Objekten. argument1 bis argumentN sind optionale Deklarationen von Argumenten, diese sind folgendermaßen aufgebaut: argument-name [IN | OUT | IN OUT] [NOCOPY] datatype [ {:= | DEFAULT} value] local-variable-declarations sind optionale Deklarationen von Variablen, Konstanten und anderen Prozeduren, die lokal für procedure-name sind. executable-section sind die PL/SQL-Anweisungen, aus denen sich die Prozedur zusammensetzt. exception-section ist der optionale Abschnitt zur Behandlung von Exceptions in der Prozedur.
Die Unterscheidung zwischen gespeicherten Prozeduren und Prozeduren, die in anonymen Blöcken deklariert und verwendet werden, ist wichtig. Die Prozeduren, die in anonymen Blöcken deklariert und aufgerufen werden, sind temporär. Wenn die Ausführung der anonymen Blöcke beendet ist, sind sie für Oracle nicht mehr vorhanden. Eine gespeicherte Prozedur, die mit create procedure erzeugt wurde oder in einem Paket enthalten ist, ist persistent in dem Sinn, dass sie von einem SQL*Plus-Skript, einem PL/SQLUnterprogramm oder einem Datenbanktrigger aufgerufen werden kann und im Datenbankkatalog gespeichert wird. Um diese Syntax zu verdeutlichen, enthält Listing 12.15 ein Beispiel für einen anonymen Block, der eine Prozedur namens Record_Patient_Temp_Dec deklariert. Diese Prozedur
hat zwei Argumente: die ID des Patienten und die Körpertemperatur des Patienten, gemessen in Grad Celsius (siehe Zeile 4). In Zeile 3 wird die Variable High_Fever auf 42 initialisiert. In Zeile 17 wird die Prozedur mit zwei Argumenten aufgerufen: New_Patient_ID (mit dem Wert GG9999) und High_Fever. Die select-Anweisung, die auf den anonymen Block folgt, demonstriert, dass die Prozedur ihre Aufgabe erfüllt hat - sie hat 42 Grad Celsius in 107.6 Grad Fahrenheit umgewandelt. Listing 12.15: Beispiel für eine Prozedur SQL> declare 2 2 New_Patient_ID Patient.Patient_ID%type; 3 High_Fever constant real := 42.0; 4 4 procedure Record_Patient_Temp_Deg_C (Patient_ID varchar2, 5 Body_Temp_Deg_C real) is 6 Temp_Deg_F real; 7 7 begin 8 8 Temp_Deg_F := (9.0/5.0)*Body_Temp_Deg_C + 32.0; 9 9 insert into Patient 10 (Patient_ID, Body_Temp_Deg_F) 11 values 12 (Patient_ID, Temp_Deg_F); 13 13 commit; 14 end; 15 15 begin 16 16 New_Patient_ID := 'GG9999'; 17 17 Record_Patient_Temp_Deg_C (New_Patient_ID, High_Fever); 18 18 end; 19 / PL/SQL procedure successfully completed. SQL> select Patient_ID, Body_Temp_Deg_F 2 from Patient 3 where 4 Patient_ID = 'GG9999'; PATIEN BODY_TEMP_DEG_F ------ ---------------
GG9999
107.6
Listing 12.16 zeigt, dass Variablen, die innerhalb einer Prozedur deklariert sind, außerhalb der Prozedur nicht zugänglich sind. Listing 12.16: Beispiel für den Gültigkeitsbereich von Variablen in PL/SQL SQL> declare 2 2 procedure Delete_Patients is 3 3 Temp_Deg_F real; 4 4 begin 5 5 delete from Patient 6 where 7 Patient_ID = 'GG3333'; 8 8 commit; 9 9 end; 10 10 begin 11 11 Temp_Deg_F := 100.0; 12 12 end; 13 / Temp_Deg_F := 100.0; * ERROR at line 11: ORA-06550: line 11, column 1: PLS-00201: identifier 'TEMP_DEG_F' must be declared ORA-06550: line 11, column 1: PL/SQL: Statement ignored
Listing 12.16 beginnt mit dem Deklarationsteil eines anonymen Blocks. In Zeile 2 wird die Prozedur Delete_Patients deklariert; innerhalb dieser Prozedur wird die Variable Temp_Deg_F als real vereinbart. In Zeile 11
referenziert der anonyme Block jedoch Temp_Deg_F, das Ergebnis ist die PL/SQL-Fehlermeldung PLS-00201. Dieses Listing zeigt, dass der Gültigkeitsbereich einer lokalen Variablen in einer Prozedur oder Funktion nicht bis zu einem anderen Unterprogramm reicht, das diese Prozedur oder Funktion aufruft. Weitere Informationen über Prozeduren gibt die Lektion am Tag 13.
12.8 Deklarieren einer Funktion Die Deklaration einer Funktion in PL/SQL ähnelt der Deklaration einer Prozedur - außer, dass die Funktion einen Wert mit einem vordefinierten Datentyp zurückgibt.
Die Syntax für die Deklaration einer Funktion ist: FUNCTION function-name [(argument1 ... [, argumentN]) ] RETURN function-datatype IS [local-variable-declarations] BEGIN executable-section RETURN expression; [exception-section] END [function-name]; Die Bedeutungen der Platzhalter sind: ●
●
● ●
●
●
●
function-name ist der Name der Funktion, er unterliegt den Einschränkungen von Oracle-Datenbanken bezüglich der Namensgebung von Objekten. argument1 bis argumentN sind optionale Deklarationen von Argumenten, diese sind folgendermaßen aufgebaut: argument-name [IN | OUT | IN OUT] [NOCOPY] datatype [ {:= | DEFAULT} value] local-variable-declarations sind optionale Deklarationen von Variablen, Konstanten und anderen Prozeduren, die lokal für function-name sind. executable-section sind die PL/SQL-Anweisungen, aus denen sich die Funktion zusammensetzt. exception-section ist der optionale Abschnitt zur Behandlung von Exceptions in der Funktion. RETURN expression beendet die Ausführung der Funktion und gibt das Ergebnis des Funktionsaufrufes zurück.
Listing 12.17 zeigt ein Beispiel für die Deklaration und Verwendung einer Funktion. Die Funktion Max_Additional_Fees hat ein einziges Argument, Dept_ID (siehe Zeile 3). Die Funktion gibt die Course-ID des Kurses mit den höchsten zusätzlichen Gebühren aller Kurse in der angegebenen Abteilung zurück (die select-Anweisung beginnt in Zeile 9). Der anonyme Block ruft die Funktion mit dem Wert econ für die Dept_ID auf (siehe Zeile 24) und die Funktion gibt die Course_ID 189 zurück. Die Abfrage am Ende von Listing 12.17 zeigt, dass Nummer 189 tatsächlich die höchsten zusätzlichen Gebühren hat. Listing 12.17: Beispiel für eine Funktion SQL> set serveroutput on SQL> declare 2 2 Course_ID Course.Course_ID%type; 3 3 function Max_Additional_Fees (Dept_ID IN varchar2) 4 return varchar2 is 5 5 Additional_Fees Course.Additional_Fees%type; 6 Units Course.Units%type; 7 Course_ID Course.Course_ID%type; 8 8 begin 9 9 select Course_ID 10 into Course_ID 11 from Course 12 where 13 Department_ID = Dept_ID and 14 Additional_Fees = 15 (select max(Additional_Fees) 16 from Course 17 where 18 Department_ID = Dept_ID); 19 19 return Course_ID; 20 20 end; 21 21 - Beginning of executable section of anonymous block. 22 22 begin 23 24 Course_ID := Max_Additional_Fees ('ECON'); 25
25 dbms_output.put_line('Course_ID: ' || Course_ID); 26 26 end; 27 / Course_ID: 189 PL/SQL procedure successfully completed. SQL> select Course_ID, Additional_Fees 2 from Course 3 where 4 Department_ID = 'ECON' 5 order by Course_ID; COURS ADDITIONAL_FEES ----- --------------101 25 189 750 199 0
Prozedur- und Funktionsargumente Jedes Argument einer Prozedur oder Funktion kann optional aus einer der folgenden Möglichkeiten gewählt werden: Listing 12.18 zeigt ein Beispiel dafür, wie alle drei Arten von Argumenten verwendet werden. Listing 12.18: Verwendung unterschiedlicher Arten von Argumenten in, out und in out SQL> declare 2 2 This_Arg1 number; 3 This_Arg2 number; 4 This_Arg3 number; 5 5 procedure Different_Arguments 6 (arg1 IN number, 7 arg2 OUT number, 8 arg3 IN OUT number) is 9 9 begin 10 10 arg2 := arg1; 11 arg3 := arg3 + 1; 12
12.9 Zusammenfassung In dieser Lektion wurden die folgenden grundlegenden Merkmale von PL/SQL besprochen: ●
●
● ●
●
●
PL/SQL ist eine blockstrukturierte Sprache, in der ein Block aus einem optionalen Deklarationsteil, einem Ausführungsteil und einem optionalen Teil zur Behandlung von Exceptions besteht. In den SQL-Erweiterungen von PL/SQL gibt es einige zusätzliche Datentypen: binary_integer, natural, positive, table und record. Mit %type und %rowtype wird die Variablendeklaration vereinfacht. Die ausführbaren PL/SQL-Anweisungen umfassen: if-then-elsif, loop, exit, die whileloop, die for-loop und goto. Ein PL/SQL-Unterprogramm kann SQL-Anweisungen enthalten, die auf PL/SQLVariablen zugreifen. Ein PL/SQL-Unterprogramm kann seine eigenen Prozeduren und Funktionen deklarieren.
12.10 Wie geht es weiter? Am Tag 13 lernen Sie mehr über die Deklaration und Verwendung von Prozeduren, Funktionen und Paketen.
12.11 Fragen und Antworten Frage: Kann eine Funktion andere Prozeduren und Funktionen aufrufen und kann eine Prozedur andere Prozeduren und Funktionen aufrufen? Antwort:
Ja, das wird von PL/SQL unterstützt. PL/SQL unterstützt sogar Rekursion. Frage: Ist es möglich, bei der Übergabe von Parametern an Prozeduren und Funktionen Aufwand zu sparen? Antwort: Ja, Sie können das Schlüsselwort NOCOPY verwenden, um Kopiervorgänge einzusparen.
12.12 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Nennen Sie die drei Teile eines PL/SQL-Unterprogramms. 2. Richtig oder falsch? Eine PL/SQL-Variable, die einen Spaltenwert speichert, muss denselben Namen wie die Spalte haben. 3. Warum ist es eine gute Angewohnheit, bei der Deklaration von Variablen %type zu verwenden?
Übungen Programmieren Sie einen anonymen Block, der eine Prozedur enthält. Die Prozedur soll für jeden Kurs am Flugle College, für den bisher keine zusätzlichen Gebühren zu entrichten waren, eine zusätzliche Gebühr von 50 eintragen. Die Prozedur soll eine Department_ ID als Argument akzeptieren. Eine Änderung darf nur ausgeführt werden, wenn der Wert von Department_ID angegeben wird.
Tabellen und Indizes für Fortgeschrittene Am heutigen Tag werden Sie fortgeschrittene Aspekte von Tabellen und Indizes kennen lernen. Ein Großteil der vorgestellten Funktionalität ist in der Oracle9i Standard Edition enthalten. Wenn Sie Bitmap Indizes oder partitionierte Tabellen nutzen möchten, greifen Sie zur Oracle9i Enterprise Edition.
11.1 Partitioning Option Steigende Datenmengen und wachsende Anforderungen an die Verfügbarkeit von Datenbanken lassen es wünschenswert erscheinen, große Tabellen in kleinere Einheiten, so genannte Partitionen, zu zergliedern. Aus Sicht der Anwendungsentwicklung ist die Partitionierung einer Tabelle oder eines Index transparent. Unter dem Aspekt der Administration ergeben sich immense Vorteile. Neue Daten können in eine neue Partition geladen werden, ohne dass Partitionen mit vorhandenen Daten im geringsten modifiziert werden müssten. Das Löschen alter Daten kann statt durch lang laufende, ressourcenzehrende delete-Anweisungen, die noch dazu zu Fragmentierungen von Tabellen und Indizes führen können, extrem schnell durch das Löschen einer gesamten Partition erfolgen. Der kostenbasierte SQL-Optimierer überspringt beim Durchsuchen einer Tabelle ganze Partitionen, um die Antwortzeit zu verbessern. Parallele Abfragen bzw. Änderungen (Parallel Query und Parallel DML) profitieren ebenfalls von der Partitionierung. Die Partitioning Option war erstmals mit Oracle8 verfügbar und stellte seinerzeit ausschließlich Range Partitioning zur Verfügung. Oracle9i Release 1 bietet Ihnen vier verschiedene Partitionierungsverfahren an: ● ● ● ●
Range Partitioning, Hash Partitioning, Composite Partitioning (zusammengesetzte Partitionierung), List Partitioning.
Jedes Partitionierungsverfahren hat seine eigenen Charakteristika und Anwendungsgebiete. Außerdem sind die administrativen Operationen, zum Hinzufügen, Löschen oder Ändern der Partitionen, abhängig vom jeweiligen Partitionierungsverfahren. In den folgenden Abschnitten werden Sie die Verfahren im Einzelnen kennen lernen.
Beispiele für partitionierte Tabellen finden Sie in der Datei sql\partitioning_ examples.sql auf der CDROM. Allen Partitionierungsverfahren gemeinsam ist das Zuordnen von Daten zu Partitionen gemäß eines Partitionierungskriteriums. Das Partitionierungskriterium besteht aus mindestens einer Spalte einer Tabelle oder eines Index. Es ist denkbar, dass sich der Wert in einer Spalte, die Teil des Partitionierungskriteriums ist, so ändert, dass der Datensatz in eine andere Partition verschoben werden müsste. Das Verschieben in Folge von Änderungen, muss explizit aktiviert werden. Die Beweglichkeit eines Datensatzes zwischen Partitionen wird entweder beim Anlegen einer partitionierten Tabelle mit dem Zusatz ENABLE ROW MOVEMENT oder mit ALTER TABLE table_name ENABLE ROW MOVEMENT ermöglicht. Die Katalogsichten [DBA|ALL|USER]_PART_TABLES, [DBA|ALL|USER]_TAB_PARTITIONS,
[DBA|ALL|USER]_PART_INDEXES und [DBA|ALL|USER]_IND_PARTITIONS geben Auskunft über partitionierte Tabellen und Indizes. Partitionen werden auch in der Detailsicht auf Tabellen und Indizes der Enterprise Manager Console angezeigt.
Überführung nicht partitionierter Tabellen in partitionierte Tabellen Nicht partitionierte Tabellen sind nicht ohne weiteres in partitionierte Tabellen zu überführen. Es existiert keine DDL-Anweisung, um eine nicht partitionierte Tabelle in eine partitionierte Tabelle zu überführen. Drei Wege bieten sich an: 1. Anlegen einer partitionierten Tabelle. Übernahme der Daten aus der nicht partitionierten Tabelle mit INSERT ... SELECT. 2. Anlegen einer partitionierten Tabelle mit CREATE TABLE ... AS SELECT auf die nicht partitionierte Tabelle. 3. Nutzung des Pakets DBMS_REDEFINITION. Die dritte Möglichkeit ist die interessanteste, da sie im laufenden Betrieb unter moderater Last eingesetzt werden kann. Das Paket DBMS_REDEFINITION erzeugt gewissermaßen im Hintergrund in einer Zwischentabelle den Inhalt der ursprünglichen Tabelle. Datentransformationen während des Erstellungsprozesses sind möglich. Periodisch können Änderungen auf der ursprünglichen Tabelle in die Zwischentabelle eingepflegt werden. In jedem Fall werden die während der Erstellungsphase der Zwischentabelle anfallenden Änderungen protokolliert und vor Abschluss der Redefinition in die Zwischentabelle eingepflegt. Eine exklusive Sperre wird nur für den kurzen Moment des Austauschens der Definitionen der ursprünglichen Tabelle und der Zwischentabelle im Datenbankkatalog genommen.
Range Partitioning Range Partitioning ist ein Verfahren zum Partitionieren von Tabellen in aufsteigend sortierte Wertebereiche (ranges). Häufig wird Range Partitioning eingesetzt, um Daten nach einem Zeitkriterium zu partitionieren. Stellen Sie sich ein Data Warehouse vor, das Verkaufsdaten der letzten drei Jahre enthält. Nehmen Sie außerdem an, dass häufig Auswertungen der letzten vier Quartale erfolgen, das heißt die where-Klausel einer selectAnweisung bezieht sich auf ein bestimmtes Quartal. Ist die Tabelle mit den Verkaufsdaten quartalsweise partitioniert, kann der kostenbasierte SQL-Optimierer alle Partitionen bis auf eine eliminieren. Ein Index würde bei dieser Art von Abfrage nicht helfen, weil er viel zu unspezifisch wäre, denn die Abfrage greift auf ca. 1/12 der gesamten Datenmenge zu. Es wäre teurer, dieses Datenvolumen über den Index zu ermitteln als eine gesamte nicht partitionierte Tabelle zu lesen, da Indexzugriffe Einzelblockzugriffe sind und ständig zwischen Index- und Tabellensegment hin- und hergesprungen werden müsste. Das Einlesen der gesamten Tabelle mit Multi-Block Operationen (s.a. Initialisierungsparameter db_file_multiblock_ read_count im Handbuch »Oracle9i Reference«) wäre schneller. Am effizientesten für diese Art von Abfrage ist es, die Tabelle mit einer Granularität zu partitionieren, die das Eliminieren von Partitionen, die einen Großteil der Datenmenge enthalten, erlaubt.
Die vereinfachte Syntax einer mit Range Partitioning partitionierten Tabelle ist: CREATE TABLE table_name ( column_specification1 ... [, column_specificationN] ) [TABLESPACE default_tablespace_name] PARTITION BY RANGE (partitioning_key1 ... [, partitioning_keyN]) ( partition_specification1 ... [, partition_specifiationN] );
Die Bedeutungen der Platzhalter sind: ● ●
●
●
●
table_name ist der Name einer partitionierten Tabelle. column_specification1 bis column_specificationN sind Vereinbarungen für Tabellenspalten (siehe CREATE TABLE am Tag 4). default_tablespace_name ist der Tablespace, in dem Partitionen angelegt werden, denen kein eigener Tablespace zugeordnet wird. partitioning_key1 bis partitioning_keyN sind die Namen der Spalten, die das Partitionierungskriterium bilden. partition_specification1 bis partition_specifiationN haben folgendes Format: PARTITION partition_name VALUES LESS THAN (boundary1 ... [, boundaryN]) [TABLESPACE tablespace_name] partition_name ist der Name einer Partition. Tablespace_name ist der Name des Tablespace, in dem eine Partition angelegt wird. Boundary1 bis boundaryN sind die exklusiven oberen Grenzen einer Partition. Die Partitionsgrenzen haben folgende Syntax: {expression | MAXVALUE}
Expression ist ein Literal, dessen Datentyp zu dem jeweiligen partitioning_key passen muss. Falls es sich um ein Datumsliteral handelt, muss die Funktion to_date mit einer geeigneten Datumsmaske verwendet werden, um sicherzustellen, dass die Datumsliterale unabhängig von der Einstellung des NLS_DATE_FORMAT einer Datenbanksitzung ausgewertet werden können. Das Schlüsselwort MAXVALUE bedeutet, dass der größtmögliche Wert in die Partition mit der obersten Grenze MAXVALUE eingefügt wird. Falls die höchste Partition nicht MAXVALUE verwendet, können Werte, die größer als die höchste boundary sind, nicht in die Tabelle eingefügt werden. Der undefinierte Wert NULL ist in der Sortierreihenfolge höher als alle anderen Werte. Kehren wir zu dem Beispiel der Tabelle mit Verkaufszahlen, die quartalsweise partitioniert ist, zurück. Listing 11.1 zeigt, wie eine solche Tabelle mit Range Partitionen angelegt wird. Listing 11.1: Anlegen einer Tabelle mit Range Partitionen CREATE TABLE sales ( sale_date date NOT NULL, product_id number NOT NULL, region_id number NOT NULL, amount number(7,2) NOT NULL ) PARTITION BY RANGE (sale_date) ( PARTITION q1y01 VALUES LESS PARTITION q2y01 VALUES LESS PARTITION q3y01 VALUES LESS PARTITION q4y01 VALUES LESS );
Beachten Sie, dass die jeweiligen Obergrenzen einer Partition exklusiv sind. Daher muss das vierte Quartal des Jahres 2001 den 1. Januar 2002 als Obergrenze erhalten. Da keine Uhrzeit angegeben ist, wird 00:00 Uhr (Mitternacht) verwendet. Somit ist sichergestellt, dass auch der höchste Wert des Monats Dezember (31.12.2001 23:59 Uhr) in die Partition für das vierte Quartal eingefügt wird.
Hash Partitioning Das englische Verb to hash bedeutet zerhacken. Beim Hash Partitioning verhält es sich so, dass eine Tabelle in eine Anzahl von Partitionen zerhackt wird. Im Gegensatz zum Range Partitioning gibt es keine Obergrenzen für
Partitionen. Allein die Anzahl der Hash Partitionen reicht aus, damit Oracle9i den Datensätzen Partitionen zuordnen kann. Die Partition wird durch Anwendung einer Hash-Funktion auf das Partitionierungskriterium ermittelt.
Die vereinfachte Syntax für eine Tabelle mit Hash Partitionen ist: CREATE TABLE table_name ( column_specification1 ... [, column_specificationN] ) [TABLESPACE default_tablespace_name] PARTITION BY HASH (partitioning_key1 ... [, partitioning_keyN]) ( partition_specification1 ... [, partition_specifiationN] ); Die Bedeutungen der Platzhalter sind: ● ●
●
●
●
table_name ist der Name einer partitionierten Tabelle. column_specification1 bis column_specificationN sind Vereinbarungen für Tabellenspalten (siehe CREATE TABLE am Tag 4). default_tablespace_name ist der Tablespace, in dem Partitionen angelegt werden, denen kein eigener Tablespace zugeordnet wird. partitioning_key1 bis partitioning_keyN sind die Namen der Spalten, die das Partitionierungskriterium bilden. partition_specification1 bis partition_specifiationN haben folgendes Format: PARTITION partition_name [TABLESPACE tablespace_name] partition_name ist der Name einer Partition. Die Anzahl der Hash Partitionen entspricht der Anzahl der Zeilen mit partition_specifications. Tablespace_name ist der Name des Tablespace in dem eine Partition angelegt wird.
Listing 11.2: Anlegen einer Tabelle mit Hash Partitionen CREATE TABLE product ( product_id integer, product_name varchar2(30) ) TABLESPACE users PARTITION BY HASH (product_id) ( PARTITION product_h1, PARTITION product_h2 );
Composite Partitioning Composite Partitioning (Zusammengesetzte Partitionierung) ist eine Kombination aus Range und Hash Partitionierung. Eine zusammengesetzt partitionierte Tabelle wird zunächst in Wertebereiche zerlegt und anschließend wird jeder Wertebereich in Subpartitionen zergliedert. Das Einfügen von Daten in eine Subpartition erfolgt wie beim Hash Partitioning. Composite Partitioning bringt Vorteile beim Join großer Tabellen. Falls ein Join einer Hash partitionierten und einer Composite Partitionierten Tabelle ausgeführt werden soll, kann dieser Join optimiert werden, wenn die Hash-(Sub)Partitionierungskriterien und die Anzahl der Hash Partitionen in der Hash partitionierten Tabelle mit
der Anzahl der Hash Subpartitionen in der zusammengesetzt partitionierten Tabelle übereinstimmen. Außerdem hat das Merkmal Parallel DML, das die parallele Änderung von Tabellen ermöglicht, die Eigenschaft, dass genau ein Prozess auf einer Partition arbeitet. Durch Einführung von Hash Subpartitionen kann der Parallelitätsgrad gesteigert werden. Die Anzahl der Subpartitionen innerhalb der Range Partitionen muss nicht identisch sein.
Die vereinfachte Syntax für eine Tabelle mit Composite Partitioning ist: CREATE TABLE table_name ( column_specification1 ... [, column_specificationN] ) [TABLESPACE default_tablespace_name] PARTITION BY RANGE (partitioning_key1 ... [, partitioning_keyN]) SUBPARTITION BY HASH (subpartitioning_key1 ... [, subpartitioning_keyN]) SUBPARTITIONS subpartition_count ( partition_specification1 ... [, partition_specifiationN] ); Die Bedeutungen der nicht gesondert aufgeführten Platzhalter entsprechen denjenigen einer Range partitionierten Tabelle. Die Bedeutungen der zusätzlichen Platzhalter sind: ●
●
subpartitioning_key1 bis subpartitioning_keyN sind die Namen der Spalten, nach denen Hash partitioniert wird. subpartition_count ist die Anzahl der Hash Subpartitionen
Mit der oben angegebenen Syntax erhält jede Range Partition dieselbe Anzahl von Hash Subpartitionen. Wenn Sie unterschiedliche Anzahlen von Hash Subpartitionen verwenden möchten, geben Sie die Hash Subpartitionen bei der Aufzählung der einzelnen Range Partitionen an. Letzteres ist auch erforderlich, wenn Sie den Hash Subpartitionen statt der vom System generierten Namen SYS_SUBPN einen eigenen Namen geben möchten. Die vollständige Syntax finden Sie im Kapitel 14 des Handbuchs »Oracle9i SQL Reference«. Listing 11.3: Anlegen einer Tabelle mit Composite Partitioning CREATE TABLE sales_composite ( sale_date date NOT NULL, product_id number NOT NULL, region_id number NOT NULL, cust_id number NOT NULL, amount number(7,2) NOT NULL ) PARTITION BY RANGE (sale_date) SUBPARTITION BY HASH (product_id) SUBPARTITIONS 4 ( PARTITION q1y01 VALUES LESS THAN (to_date('01.04.2001','dd.mm.yyyy')), PARTITION q2y01 VALUES LESS THAN (to_date('01.07.2001','dd.mm.yyyy')), PARTITION q3y01 VALUES LESS THAN (to_date('01.10.2001','dd.mm.yyyy')), PARTITION q4y01 VALUES LESS THAN (to_date('01.01.2002','dd.mm.yyyy')) ); Die Katalogsichten [DBA|ALL|USER]_TAB_SUBPARTITIONS geben Auskunft über zusammengesetzt partitionierte Tabellen. Folgende Abfrage zeigt, welche zusammengesetzt partitionierten Tabellen angelegt sind und wie viele Subpartitionen eine Range Partition hat.
Listing 11.4: Anzeige von Composite Partitionierten Tabellen im Datenbankkatalog select TABLE_NAME, PARTITION_NAME, count(SUBPARTITION_POSITION) subpartitions from user_tab_subpartitions group by table_name, partition_name; TABLE_NAME PARTITION SUBPARTITIONS --------------- --------- ------------SALES_COMPOSITE Q1Y01 4 SALES_COMPOSITE Q2Y01 4 SALES_COMPOSITE Q3Y01 4 SALES_COMPOSITE Q4Y01 4 Oracle9i Release 2 wird zusätzlich zu Range Partitioning mit Hash Subpartitionen auch Range Partitioning mit List Subpartitionen enthalten.
List Partitioning List Partitionen sind optimal für die Partitionierung nach disjunkten Werten geeignet. Das Partitionierungskriterium besteht aus genau einer Spalte. Eine List Partition kann Datensätze für mehrere Werte enthalten. Im Gegensatz zu Range Partitionen sind List Partitionen nicht sortiert. Index-organisierte Tabellen können nicht List partitioniert werden.
Die vereinfachte Syntax für eine List partitionierte Tabelle ist: CREATE TABLE table_name ( column_specification1 ... [, column_specificationN] ) [TABLESPACE default_tablespace_name] PARTITION BY LIST (partitioning_key) ( partition_specification1 ... [, partition_specifiationN] ); Die Bedeutungen der Platzhalter sind: ● ●
●
● ●
table_name ist der Name einer partitionierten Tabelle. column_specification1 bis column_specificationN sind Vereinbarungen für Tabellenspalten (siehe CREATE TABLE am Tag 4). default_tablespace_name ist der Tablespace, in dem Partitionen angelegt werden, denen kein eigener Tablespace zugeordnet wird. partitioning_key ist der Name der Spalte, die das Partitionierungskriterium bildet. partition_specification1 bis partition_specifiationN haben folgendes Format: PARTITION partition_name VALUES (literal1 ... [, literalN]) [TABLESPACE tablespace_name] partition_name ist der Name einer Partition. Tablespace_name ist der Name des Tablespace, in dem eine Partition angelegt wird. Literal1 bis literalN sind Literale, deren Datentyp dem partitioning_key entspricht. Literal kann NULL sein.
Nehmen wir an, Daten sollen nach Ländern partitioniert werden. Jedes Land hat eine eigene Abkürzung, die als Partitionierungskriterium dienen soll. Es ist mit List Partitioning sehr einfach, diese Partitionierung vorzunehmen. Mehrere kleinere Länder könnten in einer Partition zusammengefasst werden, große Länder eine eigene Partition erhalten.
Listing 11.5: Anlegen einer Tabelle mit List Partitioning CREATE TABLE sales_list ( region varchar2(3), city varchar2(80), revenue number(9,2) ) PARTITION BY LIST (region) ( PARTITION d_a_ch VALUES ('D','A','CH'), PARTITION benelux VALUES ('LU','NL'), PARTITION ost VALUES ('PL') );
Eliminierung von Partitionen Eines der interessantesten Merkmale der Partitioning Option ist die Eliminierung des Zugriffs auf Partitionen zur Leistungssteigerung (Partition Pruning bzw. Partition Elimination). Der kostenbasierte SQL-Optimierer berechnet einen Ausführungsplan, der nur auf diejenigen Partitionen zugreift, die einen Beitrag zur Ergebnismenge leisten können. Die Eliminierung von Partitionen wird durch die Erstellung eines Ausführungsplans, z.B. mit EXPLAIN PLAN oder mit der Enterprise Manager Console überprüft. Nehmen wir die Tabelle sales_list und folgende Abfrage als Beispiel: Listing 11.6: Abfrage einer partitionierten Tabelle. Alle Partitionen bis auf eine (Partition d_a_ch) können im Ausführungsplan eliminiert werden select city, sum(revenue) from sales_list where region='D' group by city;
Abbildung 11.1: Grafischer Ausführungsplan in der Enterprise Manager Console mit Eliminierung von Partitionen durch den kostenbasierten SQL-Optimierer Führen Sie obige Abfrage als Benutzer FLUGLE in SQL*Plus aus und öffnen Sie anschließend die Enterprise Manager Console als SYSTEM. Wählen Sie Ihre Datenbank, dann Instance und Sessions. Klicken Sie auf FLUGLE, dann auf den Kartenreiter SQL. Wählen Sie den ersten Ausführungsschritt TABLE ACCESS (FULL) aus. Wenn Sie jetzt am rechten Fensterrand auf das Piktogramm für View Details klicken (viertes von oben), erscheint ein weiteres Fenster. Dieses Fenster zeigt, dass nur auf Partition 1 zugegriffen wurde, indem Starting Partition: 1 und Stopping Partition: 1 angezeigt wird. Der kostenbasierte SQL-Optimierer hat wie erwartet alle Partitionen bis auf eine aus dem Ausführungsplan eliminiert. Falls eine Abfrage eine Funktion auf eines der Partitionierungskriterien anwendet, ist der SQL-Optimierer nicht in der Lage, Partitionen zu eliminieren, weil das Ergebnis der Funktion zur Eliminierung nötig wäre, aber bei Erstellung des Ausführungsplan nicht zur Verfügung steht. Die einzige Ausnahme bildet die Funktion to_date. Sie verhindert die Eliminierung von Partitionen nicht.
Operationen auf partitionierten Tabellen Änderungen an den Partitionen einer Tabelle werden mit der Anweisung ALTER TABLE vorgenommen. Grundsätzlich gilt, dass Partitionen, die von dieser Anweisung nicht betroffen sind, voll verfügbar bleiben. Die meisten Operationen beschäftigen sich mit dem Hinzufügen (ADD) und Entfernen (DROP) von Partitionen.
Die Datei sql\partitioning_operations.sql auf der CD-ROM enthält Beispiele für viele Operationen auf partitionierten Tabellen.
Hinzufügen von Partitionen Tabelle 11.1 enthält eine Übersicht der Operationen zum Hinzufügen einer Partition. Die Tabelle Sales aus dem Abschnitt über Range Partitioning hat vier Partitionen für jedes Quartal des Jahres 2001. Nehmen wir an, dass es erforderlich ist, Daten für das vierte Quartal des Jahres 2000 zu speichern. Ein Partition für diese Daten kann nicht mit ADD PARTITION hinzugefügt werden, da diese Daten kleinere Werte als die derzeit höchste Partition haben. Es ist erforderlich, auf SPLIT PARTITION zurückzugreifen. Die Partition q1y01 für das erste Quartal 2001, muss an der Grenze 1. Januar 2001 aufgespalten werden, damit Daten vor dem 1. Januar 2001 in die neu hinzugefügte Partition gelangen. Listing 11.7 zeigt die erforderliche SQLAnweisung. Listing 11.7: Aufspalten einer Partition in zwei Teile ALTER TABLE sales SPLIT PARTITION q1y01 AT (to_date('01.01.2001','dd.mm.yyyy')) INTO ( PARTITION q4y00, PARTITION q1y01 ); Die Abfrage des Datenbankkatalogs in Listing 11.8 würde zeigen, dass die neue Partition an der korrekten Stelle eingefügt wurde. Listing 11.8: Abfrage der Katalogsicht USER_TAB_PARTITIONS
select table_name, partition_name, partition_position from user_tab_partitions where table_name='SALES' order by partition_position; TABLE_NAME PARTITION_NAME PARTITION_POSITION ------------ --------------- -----------------SALES Q4Y00 1 SALES Q1Y01 2 SALES Q2Y01 3 SALES Q3Y01 4 SALES Q4Y01 5
Entfernen von Partitionen Tabelle 11.2 enthält eine Auflistung der Operationen zum Entfernen von Partitionen. Die DROP PARTITION Anweisungen löschen Partitionen einschließlich der Daten. Die COALESCE und MERGE Operationen erhalten die Daten und ordnen diese einer verbleibenden Partition zu.
Verändern von List Partitionen Die zulässigen Werte einer List Partition können verändert werden. Sie können jederzeit die Aufnahme zusätzlicher Werte in eine vorhandene Partition gestatten oder Werte aus der Liste der zulässigen Werte entfernen. Dazu verwenden Sie die Anweisung ALTER TABLE mit folgender Syntax:
ALTER TABLE table_name MODIFY PARTITION partition_name [ADD|DROP] VALUES (literal1 ... [, literalN]); Die Bedeutungen der Platzhalter sind analog zum Anlegen einer Tabelle mit List Partitionen. Zum Hinzufügen eines oder mehrerer zulässiger Werte verwenden Sie ADD VALUES. Sie entfernen zulässige Werte mit DROP VALUES. DROP VALUES wird nur dann erfolgreich sein, wenn Sie vorher mit TRUNCATE alle Daten der Partition entfernt haben, oder zumindest mit DELETE diejenigen Daten, die nicht mehr zulässig sein sollen. Betrachten wir ein Beispiel. Wenn die Partition benelux der Tabelle sales_list auch Zeilen mit dem Wert 'BE' für region enthalten soll, verwenden Sie folgende Anweisung: alter table sales_list modify partition benelux add values ('BE');
Entfernen der Daten einer Partition Zusätzlich zum Hinzufügen und Entfernen von Partitionen gibt es weitere Operationen auf partitionierten Tabellen. Das Entfernen der gesamten Daten einer Partition, ohne die Partition selbst zu löschen, geschieht mit TRUNCATE. TRUNCATE kann nicht zurückgerollt werden. Falls Sie beabsichtigen, die Partition wieder mit Daten zu füllen, haben Sie die Möglichkeit, den belegten Speicherplatz wieder zu verwenden (REUSE STORAGE). Oracle9i bietet erstmals die Möglichkeit globale Indizes als Teil der truncate-Anweisung zu aktualisieren. Dies geschieht, wenn die Schlüsselwörter UPDATE GLOBAL INDEXES die truncate-Anweisung abschließen. Den Status einer Indexpartition, ob verwendbar (USABLE) oder nicht verwendbar (UNUSABLE), erhalten Sie durch Abfrage der Spalte STATUS einer der Katalogsichten [DBA|ALL|USER]_IND_PARTITIONS.
Umbenennen von Partitionen
Partitionen können ebenso wie Tabellen umbenannt werden. Die Anweisung ALTER TABLE mit der Klausel RENAME wird für diesen Zweck verwendet.
Die Syntax lautet: ALTER TABLE table_name RENAME [SUB]PARTITION partition_name TO new_partition_name; Die Bedeutungen der Platzhalter sind: ● ● ●
table_name ist ein Tabellenname. partition_name ist der bisherige Name einer Partition. new_partition_name ist der neue Name für die Partition partition_name.
Wollen Sie eine Range, List oder Hash-Partition umbenennen, verwenden Sie das Schlüsselwort PARTITION. Wenn Sie eine Hash-Subpartition einer zusammengesetzt partitionierten Tabelle umbenennen wollen, verwenden Sie das Schlüsselwort SUBPARTITION.
Verschieben von Partitionen Jeder Partition kann ein eigener Tablespace zugeordnet werden. Es ist möglich, eine Partition von einem Tablespace in einen anderen zu verschieben. Dies kann aus organisatorischen Gründen oder als Reorganisation sinnvoll sein.
Die Syntax lautet: ALTER TABLE table_name MOVE PARTITION partition_name TABLESPACE tablespace_name; Die Bedeutungen der Platzhalter sind: ● ● ●
table_name ist der Name einer Tabelle. partition_name ist der Name einer Partition. tablespace_name ist der Name des Tablespace, in den die Partition partition_name verschoben werden soll.
Austauschen von Partitionen und Tabellen Im Reigen der Operationen auf Partitionen ist das Austauschen die letzte, aber sicher nicht die uninteressanteste Operation, die Sie kennen lernen werden. Das Haupteinsatzgebiet für das Austauschen liegt im Bereich Data Warehousing. Stellen Sie sich vor, Sie erhalten monatlich Verkaufsdaten verschiedener Filialen als nichtpartitionierte Tabelle, die in einer Export-Datei liegt. Wenn wir annehmen, dass die Tabelle der Verkäufe nach Monaten partitioniert ist und jeder Monat eine Hash-Subpartition für jede Filiale enthält, entsprechen die Daten einer nicht-partitionierten Tabelle exakt einer Hash-Subpartition. Die Funktionalität, eine Partition mit einer nicht-partitionierten Tabelle auszutauschen, ermöglicht Ihnen, die Daten jeder Filiale als nicht-partitionierte Tabelle zu laden und anschließend diese Daten mit einer leeren Partition der zusammengesetzt partitionierten Verkaufstabelle auszutauschen. Der Austausch selbst ändert nur
Einträge im Datenbankkatalog. Es erfolgt keine Datenbewegung. Folglich ist der Austausch sehr schnell.
Die Syntax lautet: ALTER TABLE part_table_name EXCHANGE [SUB]PARTITION partition_name WITH TABLE nonpart_table_name; Die Bedeutungen der Platzhalter sind: ● ●
●
part_table_name ist der Name einer partitionierten Tabelle. partition_name ist der Name einer Partition (oder Subpartition) der Tabelle part_table_name. Wenn Sie eine Subpartition einer zusammengesetzt partitionierten Tabelle austauschen möchten, verwenden Sie das Schlüsselwort SUBPARTITION. Bei Range, List und Hash Partitionen verwenden Sie das Schlüsselwort PARTITION. nonpart_table_name ist der Name einer nicht partitionierten Tabelle, deren Spaltennamen und Reihenfolge exakt mit part_table_name übereinstimmen.
Noch eleganter lässt sich die Aufgabe mit transportierbaren Tablespaces lösen.
Transportierbare Tablespaces Transportierbare Tablespaces sind schreibgeschützte (read only) Tablespaces, die von einer Datenbank in eine andere übernommen werden. Man spricht vom Exportieren und Importieren eines transportierbaren Tablespace (transportable tablespace). Das Exportieren kann nur mit der Enterprise Edition oder Personal Oracle durchgeführt werden, das Importieren auch mit Standard Edition. Zum Zeitpunkt des Exportierens muss der Tablespace schreibgeschützt sein (ALTER TABLESPACE tablespace_name READ ONLY). Das Exportieren erfolgt mit dem Werkzeug Export (exp). Export schreibt die Katalogeinträge der Datenbankobjekte in eine separate Datei, einen so genannten Export Dump. Beim Importieren des Tablespace in eine andere Datenbank mit dem Werkzeug Import (imp), wird die Kataloginformation aus dem Export Dump in den Datenbankkatalog übernommen. Nach Abschluss des Imports kann der Schreibschutz des Tablespace entfernt werden (ALTER TABLESPACE tablespace_name READ WRITE). Die Blockgröße des Tablespace muss nicht mit der Datenbankblockgröße (db_block_size) der Datenbank, in die importiert wird, übereinstimmen. Transportierbare Tablespaces sind die effizienteste Möglichkeit, Daten zwischen Oracle Datenbanken zu transportieren. Indizes auf Tabellen in einem transportierbaren Tablespace bleiben gültig. Die Kosten des Imports hängen nur von der Anzahl und Komplexität der Datenbankobjekte im Tablespace, nicht jedoch vom Datenvolumen ab. Tablespaces können nur zwischen Datenbanken auf identischen Plattformen (Prozessorarchitektur und Betriebssystem) transportiert werden. Unter dem Gesichtspunkt des Austauschens von Partitionen sind transportierbare Tablespaces insofern interessant, als eine nicht partitionierte Tabelle über diesen Mechanismus Teil des Datenbestandes einer Datenbank werden kann. Anschließend erfolgt der Austausch der nicht-partitionierten Tabelle mit einer Partition einer partitionierten Tabelle.
Partitionierte Indizes Analog zu partitionierten Tabellen vereinfachen partitionierte Indizes die Verwaltung großer Datenmengen, die Verfügbarkeit und die Skalierbarkeit. Partitionierte Indizes werden in zwei Kategorien eingeteilt: ● ●
lokale Indizes übernehmen die Partitionierungsstrategie von der Tabelle, die sie indizieren, globale Indizes haben eine eigene Partitionierungsstrategie. Globale Indizes können nur Range partitioniert werden.
Lokale Indizes sind einfacher zu verwalten als globale Indizes. Jede Operation auf der partitionierten Tabelle wird automatisch auf dem partitionierten Index ausgeführt. Auch die Verfügbarkeit ist höher als bei globalen Indizes, denn eine Indexpartition verweist auf genau eine Tabellenpartition. Falls eine Indexpartition nicht verfügbar ist, sind die Auswirkungen auf eine Tabellenpartition begrenzt. Globale Indizes bieten, da die Partitionierungsstrategie nicht von der indizierten Tabelle übernommen wird, mehr Flexibilität als lokale. Da sichergestellt werden muss, dass jede Zeile, die in eine Tabellenpartition eingefügt wird, auch in eine Indexpartition eingefügt werden kann, müssen alle Partitionsgrenzen der höchsten Partition eines Range partitionierten globalen Index mit dem Wert MAXVALUE belegt werden. Die administrativen Nachteile der globalen Indizes treten zu Tage, wenn Sie die Partitionierungsstrategie der indizierten Tabelle ändern. In diesem Fall werden alle Partitionen des globalen Index als nicht benutzbar markiert, es sei denn, Sie fügen der SQL-Anweisung zur Änderung der Tabellenpartitionierung die Schlüsselwörter UPDATE GLOBAL INDEXES hinzu. Allerdings steht diese Funktionalität nur für heaporganisierte Tabellen zur Verfügung. Bei Verwendung von index-organisierten Tabellen muss der globale Index neu aufgebaut werden. Ein Szenario, das die Verwendung globaler oder nicht partitionierter Indizes erfordert, ist die Erstellung eines eindeutigen Index, dessen Spalten nicht mit dem Partitionierungskriterium der indizierten Tabelle übereinstimmen. Wenn Sie einen partitionierten Index verwenden wollen, muss dieser global sein. Nehmen wir an, Sie haben eine Tabelle mit Kunden in Postleitzahlregionen (postal_code) partitioniert. Sie möchten einen Index auf die eindeutige Kundennummer cust_id der Kunden anlegen. Listing 11.9 zeigt, dass das Anlegen eines lokalen, eindeutigen, partitionierten Index scheitert, weil das Partitionierungskriterium postal_code nicht von diesem Index indiziert wird. Das Anlegen eines globalen, eindeutigen, partitionierten Index ist erfolgreich. Beachten Sie, dass cust_id gleichzeitig der Primärschlüssel der Tabelle customer ist. Die Eindeutigkeit des Primärschlüssels wird durch einen eindeutigen Index erzwungen. Der Primärschlüssel wird durch eine ALTER TABLE Anweisung angelegt. Da der eindeutige Index auf cust_id gleichzeitig den Primärschlüssel implementiert, kann er nicht mit CREATE INDEX angelegt werden. Stattdessen wird ALTER TABLE mit einer Klausel für einen globalen partitionierten Index verwendet. Listing 11.9: Range partitionierte Tabelle, deren Primärschlüsselspalten nicht mit dem Partitionierungskriterium übereinstimmen CREATE TABLE customer ( cust_id number, first_name varchar2(30), last_name varchar2(30), postal_code number(5), marital_status char, constraint ck_marital_status CHECK (marital_status in ('S','M','D','W')) ) PARTITION BY RANGE (postal_code) ( partition area0 VALUES LESS THAN (10000), partition area1 VALUES LESS THAN (20000), partition area2 VALUES LESS THAN (30000), partition area3 VALUES LESS THAN (40000), partition area4 VALUES LESS THAN (50000), partition area5 VALUES LESS THAN (60000), partition area6 VALUES LESS THAN (70000), partition area7 VALUES LESS THAN (80000), partition area8 VALUES LESS THAN (90000), partition area9 VALUES LESS THAN (MAXVALUE) ); Table created. ALTER TABLE customer ADD CONSTRAINT pk_customer
PRIMARY KEY (cust_id) USING INDEX LOCAL; * ERROR at line 1: ORA-14039: partitioning columns must form a subset of key columns of a UNIQUE index ALTER TABLE customer ADD CONSTRAINT pk_customer PRIMARY KEY (cust_id) USING INDEX GLOBAL PARTITION BY RANGE (cust_id) ( PARTITION cust_99999 VALUES LESS THAN (100000), PARTITION cust_199999 VALUES LESS THAN (200000), PARTITION cust_299999 VALUES LESS THAN (300000), PARTITION cust_max VALUES LESS THAN (MAXVALUE) ); Table altered. SELECT INDEX_NAME, TABLE_NAME, PARTITIONING_TYPE AS "TYPE", PARTITION_COUNT AS PARTITIONS, LOCALITY FROM user_part_indexes; INDEX_NAME TABLE_NAME TYPE PARTITIONS LOCALITY ----------- ---------- ------- ---------- -------PK_CUSTOMER CUSTOMER RANGE 4 GLOBAL
11.2 Externe Tabellen Externe Tabellen gestatten es Ihnen Daten, die Sie in einem geeigneten Format von anderen Datenbanken oder Tabellenkalkulationsprogrammen empfangen, in eine Oracle- Datenbank einzubinden, ohne die Daten physisch mit einem Werkzeug wie SQL*Loader in die Datenbank zu laden. Sie können z.B. die Daten weiterhin mit einem Tabellenkalkulationsprogramm verändern und sehen bei jedem Zugriff über die Datenbank sofort die Änderungen. Dies ist eine gute Möglichkeit, wenn die externen Daten zu Tabellen in der Datenbank in Bezug gesetzt werden sollen. Externe Tabellen sind außerdem sinnvoll, wenn die Daten innerhalb der Datenbank weiter transformiert werden sollen, z.B. um ein Data Warehouse zu laden. Eine weitere Möglichkeit besteht darin, ein CREATE TABLE ... AS SELECT auf Basis der externen Tabelle auszuführen, um die Daten in die Datenbank zu laden. Ein CREATE TABLE AS SELECT kann genutzt werden, um die Daten geeignet zu transformieren. Sie sind so in der Lage Speicherplatz zu sparen, weil Sie die Daten vor der Transformation nicht im Rohzustand in der Datenbank ablegen müssen. Bevor Sie eine externe Tabelle anlegen, teilen Sie der Datenbank das Verzeichnis, in dem sich die Daten der externen Tabelle befinden, unter Verwendung eines Aliasnamens für das Verzeichnis mit. Dies geschieht, wie am Tag 3 besprochen, mit der Anweisung CREATE DIRECTORY.
oracle_directory_name ist der Name eines Directory Objekts in der Datenbank. file_name ist der Name einer Datei, die eine externe Tabelle enthält. Oracle nimmt an, dass die Spalten der externen Tabelle in der Datei file_name durch Kommata getrennt sind. Verwenden Sie ein anderes Trennzeichen oder haben die Spalten eines feste Breite, stehen Ihnen alle Möglichkeiten des Oracle SQL*Loader offen, um mit der in der vereinfachten Syntax nicht gezeigten Klausel ACCESS PARAMETERS den Aufbau der Datei exakt zu beschreiben. Weitergehende Informationen zur Syntax finden Sie im Oracle-Handbuch »Oracle9i Database Utilities«.
Die Datei department.csv befindet sich im Verzeichnis \data der CD-ROM. Nehmen wir an, die Daten über die Fachbereiche des Flugle College lägen in einer externen Tabelle vor. Die Datei mit der externen Tabelle hat den in Listing 11.10 gezeigten Inhalt und soll department.csv heißen. Listing 11.10: Inhalt einer externen Tabelle 1000,PHILOSOPHY 1001,ECONOMICS 1002,BIOLOGY 1003,ANTHROPOLOGY 1004,PSYCHOLOGY 1005,MATHEMATICS 1006,HISTORY 1007,ENGLISH 1008,ENGINEERING Um auf die externe Tabelle zuzugreifen, legen Sie zunächst ein Datenbankobjekt vom Typ Directory an und erzeugen anschließend die externe Tabelle. Listing 11.11: Anlegen eines Directory Objekts und einer externen Tabelle create directory ext_tab_dir as 'd:\data'; Directory created. create table Department_External ( Department_ID number(5), Department_Name Varchar2(25) ) organization external ( DEFAULT DIRECTORY ext_tab_dir LOCATION ('department.csv') ); Table created. Mit einer SELECT Anweisung können Sie sich davon überzeugen, dass Oracle9i die externe Tabelle korrekt verarbeitet: Listing 11.12: SELECT auf eine externe Tabelle select department_name from department_external; DEPARTMENT_NAME
------------------------PHILOSOPHY ECONOMICS BIOLOGY ANTHROPOLOGY PSYCHOLOGY MATHEMATICS HISTORY ENGLISH ENGINEERING 9 rows selected.
11.3 Objektrelationale Merkmale Alle Varianten der Oracle9i Datenbank besitzen objektrelationale Merkmale. Genau genommen ist Oracle9i nicht nur ein relationales Datenbankmanagementsystem (RDBMS), sondern ein objektrelationales DBMS (ORDBMS). Die O-RDBMS Merkmale beinhalten u.a. folgende Funktionalitäten: ● ● ●
Wir werden in diesem Abschnitt den Objekt-Datentyp, verschachtelte Tabellen und den VARRAY-Datentyp untersuchen. Um zu beginnen, lassen Sie uns den Objekt-Datentyp OBJECT betrachten.
Der Datentyp OBJECT Die Anweisung CREATE TYPE erzeugt einen neuen Datentyp, der anschließend benutzt werden kann, um eine Tabelle anzulegen, den Datentyp der Spalte einer Tabelle festzulegen oder einen weiteren Datentyp zu erstellen. Wenn Sie zum Beispiel eine Tabelle erstellen wollen, welche die verfügbaren Räumlichkeiten enthält, können Sie einen neuen Datentyp mit Namen Available_Room für diesen Zweck erzeugen (siehe Listing 11.13). Ist der Datentyp erstellt, haben Sie die Möglichkeit, eine Tabelle zu erzeugen, die diesen Datentyp benutzt. Listing 11.13: Erstellen eines neuen Datentyps SQL> create type Available_Room as object (building varchar2(20), room varchar2(6)); Type created. Haben Sie den neuen Datentyp definiert, können Sie ihn für eine Spalte festlegen, wie in Listing 11.14 demonstriert wird. Listing 11.14: Erstellen einer Tabelle, die einen Objektdatentyp enthält SQL> create table Seminar_ID Instructor_ID Location Table created.
Ein Objekt besteht aus Attributen (Spalten) und Methoden (die Funktionen und Prozeduren, die das Objekt manipulieren und Informationen darüber zurückgeben). Als Standard erstellt Oracle eine Konstruktor-Methode für jeden Objekttyp. Diese Konstruktor- Methode erzeugt eine Instanz des Objekts. Wenn Sie einen Wert für die Objektspalte festlegen wollen, müssen Sie die Konstruktor-Methode benutzen, die aus dem Namen des Objekttyps besteht, gefolgt von den in runde Klammern eingeschlossenen Attributwerten. Listing 11.15 enthält
ein Beispiel. Listing 11.15: Festlegen eines Wertes für einen Objektdatentyp mit Hilfe eines Konstruktors SQL> insert into Seminar (Seminar_ID, Instructor_ID, Location) values ('1001', 101, Available_Room('NARROW HALL', 'B200')); 1 row processed. Wenn Sie die Tabelle erstellt haben, die eine Objektspalte enthält, können Sie eine SELECT-Anweisung benutzen, um die Werte aus der Objektspalte abzurufen. Mit der Notation Objektspaltenname.Attributname können Sie auf einzelne Attribute zugreifen. Listing 11.16: Kennzeichnung eines Attributs mit dem Namen der Objektspalte SQL> select Location.Building, Location.Room from Seminar; LOCATION.BUILDING LOCATION ------------------- -------NARROW HALL B200 1 row selected.
Verschachtelte Tabellen Oracle9i unterstützt die Verwendung verschachtelter Tabellen. Nehmen Sie einmal an, Sie erzeugen einen Objekt-Datentyp mit der Bezeichnung Test_Score, der dafür gedacht ist, eine Studenten-ID und die Ergebnisliste eines bestimmten Tests zu speichern (siehe Listing 11.17). Sie können außerdem einen anderen Datentyp mit der Bezeichnung Test_Score_Table erstellen, bei dem es sich um eine Tabelle handelt, die auf dem Test_ScoreDatentyp basiert. Abschließend können Sie den Test_Score_Table-Datentyp benutzen, um den Datentyp für eine Spalte festzulegen, wenn Sie eine Tabelle erzeugen. Listing 11.17: Erstellen einer Tabelle, die eine Tabelle als Spalte enthält SQL> create type Test_Score as object ( Student_ID varchar2(6), Score number); Statement processed. SQL> create type Test_Score_Table as table of Test_Score; Statement processed. SQL> create table Test_Results ( Instructor_ID varchar2(6), Class_ID varchar2(6), Test_Name varchar2(30), Scores Test_Score_Table) nested table Scores store as Test_Scores; Table created.
Die letzte SQL-Anweisung in Listing 11.17 ist eine CREATE TABLE-Anweisung. Wenn Sie eine Tabelle erstellen, die eine Spalte enthält, bei der es sich um eine geschachtelte Tabelle handelt, müssen Sie eine Speichertabelle festlegen, die für die verschachtelte Tabelle bestimmt ist. Wenn Sie einen Satz von Werten für eine verschachtelte Tabelle in einer DML- Anweisung bestimmen, müssen
Sie die Konstruktor-Methode für den Datentyp festlegen, auf dem die verschachtelte Tabelle basiert. Listing 11.18 zeigt ein Beispiel. Eine einzelne Zeile wird in die Tabelle Test_Results eingefügt. Obwohl nur eine einzelne Zeile in die Tabelle Test_Results eingefügt wurde, werden drei Testergebnisse in der Spalte Scores gespeichert, bei der es sich um eine verschachtelte Tabelle handelt. Listing 11.18: Einfügen einer Zeile in eine Tabelle, die eine Tabelle als Spalte enthält SQL> insert into Test_Results 2 (Instructor_ID, Class_ID, Test_Name, Scores) 3 values 4 ('E101', '123456', 'Final exam', 5 Test_Score_Table 6 (Test_Score ('A12345', 98), 7 Test_Score ('E13111', 87), 8 Test_Score ('F13999', 84))); 1 row created.
In Zeile 5 wird die Konstruktor-Methode für die verschachtelte Tabelle vom Typ Test_Score_Table verwendet. In den Zeilen 6, 7 und 8 werden drei Datensätze an die Konstruktor-Methode für Test_Score übergeben.
Der Datentyp Varray Ein seit Oracle8 zur Verfügung stehender Datentyp ist VARRAY (varying array). Dieser Datentyp repräsentiert einen sortierten Satz von Elementen desselben Datentyps.
Hier ist eine vereinfachte Version der VARRAY-Syntax: CREATE TYPE type_name AS VARRAY (limit) OF datatype; Die Variablen sind wie folgt definiert: ● ●
●
type_name ist der Name des zu erstellenden VARRAY-Typs. limit ist die maximale Zahl von Elementen in dem Array. Limit muss ein Integer- Literal sein. Sie können für die Festlegung der Obergrenze keinen Ausdruck verwenden. datatype ist entweder ein vordefinierter oder ein benutzerdefinierter Datentyp.
In Listing 11.19 sehen Sie, wie ein VARRAY als Datentyp für eine Spalte benutzt wird. Listing 11.19: Erstellen einer Tabelle, die eine Spalte vom Typ varray enthält SQL> create type Available_Rooms as varray (100) of Available_Room; Type created. SQL> create table Class_Scheduling ( Administrator_ID varchar2(6), Semester varchar2(6), Year number, Room_Assignments Available_Rooms);
Table created. Lassen Sie uns einen Blick darauf werfen, wie Sie Werte für eine VARRAY-Spalte in einer INSERT-Anweisung festlegen können. Wenn Sie keinen Wert für die VARRAY-Spalte einfügen wollen, können Sie die KonstruktorMethode für den VARRAY-Datentyp mit dem Argument NULL ausführen (siehe Listing 11.20). Listing 11.20: insert-Anweisung, in der für eine Spalte vom Typ varray der Wert NULL angegeben ist SQL> insert into Class_Scheduling (Administrator_ID, Semester, Year, Room_Assignments) values ('101', 'FALL', '1998', Available_Rooms(NULL)); 1 row created. Wenn Sie einen Wert für eine Objektspalte in einer DML-Anweisung festlegen wollen, müssen Sie die Konstruktor-Methode für den Basis-Objekttyp VARRAY, eingeschlossen in die Konstruktor-Methode für den VARRAY-Typ, festlegen. Listing 11.21 enthält zum Beispiel eine INSERT-Anweisung, die versucht, einen zur Verfügung stehenden Unterrichtsraum in der Spalte Room_Assignments zu speichern. Oracle weist die erste INSERT-Anweisung ab, weil Available_Rooms ein VARRAY ist. Die zweite INSERT-Anweisung benutzt die Konstruktor-Methode für Available_Room, eingeschlossen in die Konstruktor-Methode für Available_Rooms. Listing 11.21: Festlegen eines Wertes für eine Spalte vom Typ varray mit Hilfe einer insert-Anweisung SQL> insert into Class_Scheduling 2 (Administrator_ID, Semester, Year, Room_Assignments) 3 values 4 ('101', 'FALL', '1998', Available_Rooms('FLUGLE HALL', 100)); ('101', 'FALL', '1998', Available_Rooms('FLUGLE HALL', 100)) * ERROR at line 4: ORA-00932: inconsistent datatypes SQL> insert into Class_Scheduling 2 (Administrator_ID, Semester, Year, Room_Assignments) 3 values 4 ('101', 'FALL', '1998', 5 Available_Rooms(Available_Room('FLUGLE HALL', '100'))); 1 row created.
Katalogsichten für benutzerdefinierte Datentypen Mehrere Katalogsichten stellen Informationen über benutzerdefinierte Datentypen zur Verfügung: ●
●
USER_TYPES präsentiert grundlegende Informationen über Datentypen, die vom Benutzer erstellt wurden - mit anderen Worten, über andere Datentypen als die vordefinierten. USER_TYPE_ATTRS bietet detaillierte Informationen über jeden der in USER_TYPES aufgelisteten Datentypen.
Listing 11.22 enthält eine Beschreibung der Katalogsicht USER_TYPES und eine Abfrage auf USER_TYPES. Listing 11.22: Betrachtung des Inhalts von user_types SQL> desc USER_TYPES Column Name ----------------------------TYPE_NAME TYPE_OID
Wie Sie in der letzten Abfrage sehen konnten, gibt die TYPECODE-Spalte OBJECT zurück, wenn ein Typ als Objekt definiert ist, oder COLLECTION, wenn ein Typ als VARRAY oder verschachtelte Tabelle definiert ist. AVAILABLE_ROOM z.B. war als Objekt definiert, deshalb ist sein TYPECODE OBJECT, wohingegen AVAILABE_ROOMS als VARRAY von AVAILABLE_ROOM definiert war, so dass der TYPECODE COLLECTION ist. Wenn Sie die Attribute einsehen wollen, die für einen Typ definiert wurden, fragen Sie die Katalogsicht USER_TYPE_ATTRS ab. Listing 11.23 enthält eine Beschreibung der Katalogsicht USER_TYPE_ATTRS und eine Abfrage, welche die Attribute des Typs TEST_SCORE zurückgibt. Listing 11.23: Betrachtung des Inhalts von user_type_attrs SQL> desc user_type_attrs Column Name Null? Type ----------------------------- -------- -----------TYPE_NAME NOT NULL VARCHAR2(30) ATTR_NAME NOT NULL VARCHAR2(30) ATTR_TYPE_MOD VARCHAR2(7) ATTR_TYPE_OWNER VARCHAR2(30) ATTR_TYPE_NAME VARCHAR2(30) LENGTH NUMBER PRECISION NUMBER SCALE NUMBER CHARACTER_SET_NAME VARCHAR2(44) SQL> select attr_name, attr_type_name, length from user_type_attrs where type_name = 'TEST_SCORE'; ATTR_NAME ATTR_TYPE_NAME ----------------------------- ----------------------------STUDENT_ID VARCHAR2 SCORE NUMBER 2 rows selected.
LENGTH -----6
11.4 Indizes für Fortgeschrittene Mit dem Erscheinen von Oracle 7.2 und Oracle Text Server gab es erstmals neben den B- Baum Indizes einen
weiteren Indextyp - den Text Index. Oracle 7.3 führte den Bitmap Index ein. Heute besitzt Oracle9i fünf verschiedene Indextypen: ● ● ● ● ●
Mit dem B-Baum Index sind Sie bereits vertraut. Die übrigen Indextypen werden Sie in den folgenden Abschnitten kennen lernen.
Funktionsbasierte Indizes Die SQL-Optimierer können einen gewöhnlichen Index nicht verwenden, wenn in der where-Bedingung einer Abfrage eine Funktion auf die indizierte Spalte angewendet wird. Bei einem funktionsbasierten Index (Function Based Index) verhält es sich genau umgekehrt. Der Index wird auf das Resultat eines Ausdrucks, meist einer Funktion, angelegt und kann nur für diejenigen Abfragen verwendet werden, welche in der where- Bedingung denselben Ausdruck nutzen. Der Ausdruck bezieht sich auf eine oder mehrere Spalten einer Tabelle. Vordefinierte oder benutzerdefinierte Funktionen sind im Ausdruck zugelassen. Sie benötigen das Privileg QUERY REWRITE, um einen funktionsbasierten Index auf einer Tabelle in Ihrem eigenen Schema anzulegen. Die Funktionalität eines funktionsbasierten Index könnte simuliert werden, indem das Resultat des verwendeten Ausdrucks in einer Spalte der Tabelle gespeichert wird. Diese Spalte könnte über einen Trigger gepflegt werden. Nachteilig bei diesem Ansatz ist die Notwendigkeit, bei jeder Änderung der Tabelle einen Trigger auszuführen. Darüber hinaus ist es bei dieser Variante nicht möglich, einen Bitmap Index anzulegen. Der Speicherbedarf für den simulierten funktionsbasierten Index wäre durch die zusätzliche Spalte der Tabelle wesentlich höher. Funktionsbasierte Indizes stehen seit der Version Oracle8i zur Verfügung und sind aus den genannten Gründen einem simulierten funktionsbasierten Index vorzuziehen.
Die Syntax lautet: CREATE [BITMAP] INDEX index_name ON table_name(expression); Die Bedeutungen der Platzhalter sind: ● ● ●
index_name ist der Name eines Index. table_name ist der Name der indizierten Tabelle. expression ist ein Ausdruck, der auf Spalten der Tabelle table_name zugreift. Damit der Index zur Beschleunigung einer Abfrage genutzt werden kann, muss die where- Bedingung der Abfrage denselben Ausdruck enthalten.
Angenommen, Sie möchten unabhängig von der Groß/Kleinschreibung das Nachnamens nach einem Dozenten am Flugle College suchen. Ein funktionsbasierter Index für diese Anforderung wird wie folgt angelegt: Listing 11.24: Anlegen eines funktionsbasierten Index create index instructor_name_idx on instructor(upper(last_name)); Index created.
Bitmap Indizes
Ein Bitmap Index wird gebildet, indem für jeden in einer Spalte auftretenden Wert eine Folge von Bits erzeugt wird. Für jeden der vorhandenen unterschiedlichen Werte wird eine eigene Bitfolge generiert. Für jede Zeile der Tabelle wird ein Bit benutzt, um zu beschreiben, ob ein bestimmter Wert in der Zeile auftritt oder nicht. Tritt der Wert in der Zeile auf, wird das zugehörige Bit auf 1 gesetzt, andernfalls auf 0. Nehmen wir an, eine Tabelle enthielte die Spalten Sozialversicherungsnummer und Familienstand. Familienstand kann drei Werte annehmen: ledig, verheiratet und geschieden. Folgende Zeilen sind in der Tabelle enthalten: Da drei unterschiedliche Werte in der Spalte Familienstand auftreten, werden drei verschiedene Bitfolgen erzeugt: Die erste Zeile in der Spalte Familienstand=ledig in Tabelle 11.5 hat den Wert 1, weil die erste Zeile der Tabelle 11.4 den Wert ledig in der Spalte Familienstand enthält. Die nächsten beiden Zeilen in Tabelle 11.4 haben andere Werte als ledig. Daher sind dort die Bits auf 0 gesetzt.
Die Syntax für die Erstellung eines Bitmap Index lautet: CREATE BITMAP INDEX index_name ON table_name (column_name1, ... [, column_nameN]); Die Definition der Platzhalter lautet wie folgt: ●
● ●
index_name ist der Name des Index (entsprechend der Namenskonventionen für OracleDatenbankobjekte). table_name ist der Name der Tabelle, die indiziert wird. column_name bis column_nameN sind die indizierten Spalten.
Bitte beachten Sie, dass Bitmap Indizes grundsätzlich nicht eindeutig sind. Mit anderen Worten, das Schlüsselwort UNIQUE kann nicht verwendet werden. Wenn Sie einen eindeutigen Index benötigen, müssen Sie einen B-Baum Index verwenden. Eine Eigenschaft, die Bitmap Indizes sehr interessant macht, ist die Verknüpfung von Bitfolgen verschiedener Bitmap Indizes einer Tabelle mit Operatoren wie UND bzw. ODER. Als Resultat erhält man Verweise für den schnellen Zugriff auf diejenigen Zeilen, die alle Bedingungen erfüllen. Stellen Sie sich vor, unsere Beispieltabelle hätte zusätzlich die Spalte Altersgruppe mit fünf möglichen Werten (1..5) und einen Bitmap Index auf dieser Spalte. Die Frage nach Geschiedenen in Altersgruppe drei wäre durch Verknüpfung der Bitfolgen für Familienstand=geschieden und Altersgruppe=3 sehr schnell zu beantworten. Beachten Sie bei Bitmap Indizes folgende Punkte: ● ● ●
Ein Bitmap Index hat maximal 30 Spalten. Bitmap Indizes indizieren - anders als B-Baum Indizes - Zeilen mit dem Wert NULL. Bitmap Indizes sind für den Bereich Data Warehousing gedacht. Für OLTP Anwendungen sind diese Indizes nicht geeignet.
Bitmap Join Indizes Bitmap Join Indizes sind quasi vorberechnete Joins zwischen mindestens zwei Tabellen. Durch die Komprimierung des Bitmap Join Index werden Abfragen, denen dieselbe Join- Bedingung zugrunde liegt wie dem Bitmap Join Index, beschleunigt. Bitmap Join Indizes sind für den Bereich Data Warehousing gedacht. Für OLTP Anwendungen sind diese Indizes nicht geeignet.
Ausgangspunkt für die Überlegung, einen Bitmap Join Index anzulegen, ist eine Abfrage oder Gruppe von Abfragen mit einem Join zwischen einer Faktentabelle und einer oder mehreren Dimensionstabellen (siehe Tag 3, Star Schema), deren Antwortzeit verbessert werden soll. Die where-Bedingung des Join bildet den im Index vorberechneten Join ab. Es muss sich um einen Equi-Join (Gleichheitsbedingung in der WHERE-Klausel) handeln. Zwischen Dimensions- und Faktentabelle muss eine Fremdschlüsselbeziehung bestehen. Ein Fremdschlüssel in der Faktentabelle referenziert einen Primärschlüssel in der Dimensionstabelle. Fehlt der Primärschlüssel auf der Dimensionstabelle, kann kein Bitmap Join Index angelegt werden (ORA-25954). Ein Bitmap Join Index wird auf einer Faktentabelle angelegt. Als indizierte Spalte wird nicht etwa wie bei einem gewöhnlichen Index eine Spalte der Faktentabelle angegeben, sondern eine Spalte (oder mehrere Spalten) einer Dimensionstabelle (oder mehrerer Dimensionstabellen). Sinnvoll ist die Verwendung von Spalten, die in der where- Bedingung der zu beschleunigenden Abfrage das aus der Dimensionstabelle ausgewählte Datenvolumen begrenzen.
Die Syntax für einen Bitmap Join Index zwischen zwei Tabellen lautet: CREATE BITMAP INDEX index_name ON fact_table_name(dimension_table_name.column_name) FROM fact_table_name, dimension_table_name WHERE fact_table_name.fk_column_name = dimension_table_name.pk_column_name; Die Bedeutungen der Platzhalter sind: ● ● ●
●
●
●
index_name ist der Name des Index. fact_table_name ist der Name der indizierten Faktentabelle. dimension_table_name ist der Name einer Dimensionstabelle. Mehrere Dimensionstabellen können verwendet werden, wenn der Index den Join zwischen mehr als zwei Tabellen ersetzen soll. column_name ist der Name einer Spalte, die in der select-Liste oder der where- Bedingung einer zu beschleunigenden Abfrage enthalten ist. Ein Bitmap Join Index kann auch mehrere Spalten indizieren. fk_column_name ist der Name der Fremdschlüsselspalte der Faktentabelle, die sich auf die Dimensionstabelle bezieht. pk_column_name ist der Name der Primärschlüsselspalte einer Dimensionstabelle. Zusammengesetzte Primärschlüssel sind möglich.
Mit den oben vorgestellten Tabellen sales_composite und customer als Fakten- bzw. Dimensionstabelle, lässt sich folgender Bitmap Join Index anlegen: Listing 11.25: Anlegen eines Bitmap Join Index CREATE BITMAP INDEX sales_cust_bji ON sales_composite(customer.cust_id) FROM sales_composite, customer WHERE sales_composite.cust_id = customer.cust_id LOCAL; Beachten Sie, dass der Bitmap Join Index durch das Schlüsselwort LOCAL die Partitionierung von der Tabelle sales_composite übernimmt. Globale partitionierte Bitmap Join Indizes können nicht angelegt werden.
Domänenindizes Domänenindizes sind auf ein Aufgabengebiet spezialisierte Indizes. Am weitesten verbreitet sind Indizes für geographische Daten (Oracle Spatial) und Volltextrecherche- Indizes. Oracle9i Text beinhaltet mehrere
Indextypen für das Indizieren von Texten innerhalb und außerhalb der Datenbank. Ein Domänenindex beinhaltet einen Operator, der den Index für SQL-Anweisungen nutzbar macht. Oracle9i Text enthält unter anderem die Indextypen CONTEXT und CTXCAT mit den Operatoren CONTAINS und CATSEARCH. Stellen Sie sich die Operatoren wie eine Funktion vor, der Sie genau wie bei einer Suchmaschine im World Wide Web mitteilen, welche Begriffe Sie suchen, nur dass Sie bei Oracle Text wesentlich mehr Funktionalität zur Verfügung haben. Von manchen Suchmaschinen kennen Sie wahrscheinlich Ausdrücke wie AND und OR, mit denen Sie Suchbegriffe verknüpfen können. Oracle Text hat z.B. zusätzlich die Möglichkeit nach Begriffen zu suchen, die nahe beieinander stehen (NEAR), mit dem Jokerzeichen % zu arbeiten, Begriffe anzugeben, die nicht in einem Dokument stehen dürfen (MINUS), Wortstammsuche ($), unscharfe Suche (FUZZY) u.v.a.m. Außerdem können Sie zu jedem Dokument eine Bewertungszahl (SCORE), die ausdrückt wie gut der Suchausdruck auf das Dokument zutrifft, erhalten. Auch diese Funktionalität kennen Sie von einigen Suchmaschinen, die neben jedes Dokument eine Prozentzahl setzen.
Die Syntax für das Anlegen eines CONTEXT Index lautet: CREATE INDEX text_index ON table_name (text_column) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS (index_parameters); Die Bedeutungen der Platzhalter sind: ● ● ● ●
text_index ist der Indexname. table_name ist der Name einer Tabelle, die Texte oder Verweise auf Texte enthält. text_column ist eine Spalte vom Typ varchar2, char, BLOB, CLOB oder BFILE. index_parameters sind u.a. STOPLIST, LEXER und MEMORY. Eine Stopliste enthält Wörter, die nicht indiziert werden sollen, z.B. im Deutschen die Wörter der, die, das. Weitergehende Informationen zu Oracle Text finden Sie im Handbuch »Oracle Text Application Developer's Guide«.
Die Tabelle Course im Schema FLUGLE enthält zu jedem Kurs einen Beschreibungstext, der maximal 2000 Zeichen lang ist. Da es sich um eine varchar2 Spalte handelt, ist es zwar möglich, mit LIKE in dieser Spalte nach Begriffen zu Suchen, jedoch ist diese Suche bei größeren Datenmengen ineffizient und die Funktionalität äußerst rudimentär. Sinnvoller ist es, einen CONTEXT Index auf diese Spalte anzulegen, um den reichen Funktionsumfang des Operators CONTAINS zu nutzen. Nehmen wir an, Sie möchten an einer Einführung in Englische Literatur teilnehmen. Da der CONTEXT Index Groß-/Kleinschreibung ignoriert, es sei denn Sie setzen das Attribut MIXED_CASE auf den Wert YES, müssen Sie sich darum keine Sorgen machen. Ihnen ist nicht bekannt, ob die Beschreibung etwa lautet 'Introduction ...' oder 'This course introduces ...'. Ihre Suche soll aber beides finden. Daher verwenden Sie das Jokerzeichen % und suchen nach introduc%. Die Reihenfolge der Wörter in der Beschreibung ist ebenfalls offen. Der Operator AND verlangt, dass alle Suchbegriffe unabhängig von der Reihenfolge enthalten sein müssen, um einen Treffer zu liefern. Es ergibt sich der in Listing 11.26 wiedergegebene Suchausdruck 'English and literature and introduc%'. Die Beschreibung eines Kurses passt auf diesen Suchausdruck. Listing 11.26: Anlegen eines CONTEXT Index und Volltextsuche mit dem Operator CONTAINS CREATE INDEX course_desc ON course(description) INDEXTYPE IS CTXSYS.CONTEXT; Index created. select course_id, title, description from course where CONTAINS(description,'English and literature and introduc%') >0; COURSE_ID TITLE DESCRIPTION ---------- -------------------- ---------------------------------
10071
INTRO TO ENGLISH LIT
A general introduction to classic English literature including Shakespeare, Milton, Yeats, Hawthorne, and others.
11.5 Zusammenfassung Diese Lektion behandelte die folgenden fortgeschrittenen Aspekte von Tabellen und Indizes: ● ●
● ●
●
● ●
●
●
Oracle9i stellt vier Partitionierungstechniken zur Verfügung: Range, Hash, Composite und List. Die Partitioning Option vereinfacht die Verwaltung großer Datenmengen, erhöht die Verfügbarkeit und die Leistungsfähigkeit. Tabellen und Indizes können partitioniert werden. Jede Partitionierungsstrategie hat einen Satz an Anweisungen, um Partitionen hinzuzufügen, aufzuspalten, zu modifizieren oder zu löschen. Der kostenbasierte SQL-Optimierer (CBO) erstellt Ausführungspläne, die den Zugriff auf Partitionen, die nicht zum gesuchten Ergebnis beitragen können, unterdrückt (Partition Pruning). Externe Tabellen sind Daten außerhalb der Datenbank, auf die mit SELECT zugegriffen werden kann. Mit den objektrelationalen Merkmalen des Datenbankmanagementsystems Oracle9i können Sie Ihre eigenen Datentypen, geschachtelte Tabellen (nested tables) und VARRAYs erzeugen. Oracle9i besitzt fünf verschiedene Indexarten: B-Baum, Bitmap, Bitmap Join, Funktionsbasierte und Domain Indizes. Die Domain Indizes CONTEXT und CTXCAT sind in allen Varianten von Oracle9i enthalten. Ihr Einsatzgebiet ist die Volltextrecherche.
11.6 Wie geht es weiter? Am Tag 12 befassen Sie sich mit den Grundlagen von PL/SQL wie Deklarationen, Kontrollstrukturen und Ausnahmebehandlung.
11.7 Fragen und Antworten Frage: Welche Vorteile bringt der Einsatz der Partitioning Option mit sich? Antwort: Bessere Handhabung großer Datenmengen, bessere Performance bei Abfragen, deren Ergebnis durch Eliminieren des Zugriffs auf eine oder mehrere Partitionen schneller ermittelt werden kann und höhere Verfügbarkeit der Daten. Frage: Welcher Indextyp implementiert einen vorberechneten Join? Antwort: Der Bitmap Join Index.
11.8 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test Frage:
Richtig oder falsch? Eine gewöhnliche Tabelle kann mit ALTER TABLE in eine partitionierte Tabelle überführt werden. Frage: Was ist an dieser Anweisung falsch? TRUNCATE PARTITION q102; Frage: Richtig oder falsch? Der CONTEXT Index unterscheidet in der Standardeinstellung zwischen Groß- und Kleinschreibung.
Übungen Legen Sie einen CONTEXT Index auf der Spalte DESCRIPTION der Tabelle COURSE an. Erstellen Sie eine Abfrage, die Kursidentifikation, Kursbezeichnung und Beschreibung aller Kurse ausgibt, deren Beschreibung die Namen Shakespeare oder Milton enthält.
Datenbanksicherheit und Optimierung Die heutige Lektion behandelt zwei Themen, die während der Entwicklung von OracleDatenbankanwendungen nicht ignoriert werden dürfen - Sicherheit und Optimierung. Viele der Aufgaben, die in dieser Lektion zur Sprache kommen, werden üblicherweise vom zuständigen Datenbankadministrator durchgeführt. Auch wenn Sie niemals für die Sicherheit und die Optimierung einer Oracle-Datenbank verantwortlich sein sollten, ist es sinnvoll, einige der damit verbundenen Sachverhalte verstanden zu haben. Diese Lektion beginnt mit einer Beschreibung von Benutzern, Rollen und der an sie zu vergebenden Berechtigungen.
10.1 Benutzer und Rollen Am Tag 3, »Logischer Datenbankentwurf«, haben Sie einen neuen Datenbankbenutzer angelegt. Datenbankbenutzer besitzen ein eigenes Schema. Ein Grundprinzip im Bereich Datenbanksicherheit besteht darin, ein eigenes Schema für die Speicherung der Datenbankobjekte einer Anwendung zu verwenden. Für die Benutzer einer Anwendung werden eigene Datenbankbenutzer angelegt, deren Schemata keine Daten enthalten. Im Abschnitt 10.2 über Synonyme werden Sie erfahren, wie der Sachverhalt, dass Datenbankobjekte in einem fremden Schema liegen, transparent gemacht wird. Die Aufteilung in Schemata für die Datenspeicherung und Schemata für die eigentlichen Benutzer ist entscheidend für die Umsetzung der in den nächsten Abschnitten vorgestellten Sicherheitskonzepte. Oracle verwaltet nicht nur Datenbankbenutzer sondern auch Datenbank-Rollen. Eine Rolle repräsentiert eine Anzahl von Berechtigungen, die mit der funktionalen Rolle eines Mitarbeiters in einem Unternehmen korrespondieren können. Sie können zum Beispiel einem Datenerfasser die Aufgabe des Einfügens und Änderns der Datensätze in einer bestimmten Tabelle übertragen, ihn oder sie jedoch am Löschen von Daten hindern. Um dieses Sicherheitsschema zu erzwingen, würden Sie eine Datenbankrolle namens DATA_ENTRY erstellen, welche diese Berechtigungen hat. Auf der anderen Seite sollte der Dateneingabe-Manager in der Lage sein, Datensätze in die Tabellen einzufügen, sie zu ändern oder zu löschen. Aus diesem Grund würden Sie eine separate Rolle namens DATA_ENTRY_MANAGER erstellen, welche diesen Sicherheitsgrundsatz erfüllt. Ist eine Datenbankrolle erstellt, kann sie bei Bedarf einem Datenbankbenutzer zugewiesen werden. Zusätzlich kann eine Rolle auch einer anderen Rolle zugewiesen werden. Datenbankrollen helfen nicht nur, die entsprechende Sicherheit zu bieten, sie vereinfachen auch die Datenbankadministration. Für eine Anwendungsumgebung mit vielen Tabellen, Benutzern und funktional klaren Rollen ist die Einführung und Wartung eines Sicherheitsschemas eine Hauptaufgabe. Wenn der Datenbankadministrator Berechtigungen direkt an einzelne Benutzer vergibt, ist es sehr leicht möglich, dass einige Dinge im Laufe der Zeit übersehen werden. Der DBA könnte vergessen, einigen Mitarbeitern die Berechtigungen zu erteilen, die sie für ihre Arbeit benötigen, oder er könnte anderen eine überflüssige Berechtigung erteilen. Falls eine Änderung der Datenbank-Anwendung ebenso eine Änderung der Benutzerberechtigungen notwendig macht, wird der DBA ohne die Verwendung von Rollen weit mehr
Arbeit zu erledigen haben. Indem Sie eine Datenbankrolle für jede funktionale Rolle definieren, die in Ihrem Unternehmen existiert, reduzieren Sie die Aufgabe des Verwaltens von Benutzern und deren Berechtigungen deutlich. Nehmen Sie beispielsweise an, es gäbe zehn Datenbankbenutzer - drei in der Marketing-Abteilung und sieben in der Entwicklungsabteilung. Sie würden zwei Datenbankrollen erstellen, Marketer und Engineer, und die entsprechenden Berechtigungen jeder Rolle zuweisen. Die Marketer- Rolle benötigt den Zugriff auf einen Satz von Tabellen, während die Engineer-Rolle den Zugriff auf einen anderen Satz benötigt. Anschließend würden Sie jedem Benutzer die entsprechende Rolle zuweisen. Wenn sich die Datenbank ändert, z.B. durch Hinzufügen einer Tabelle, erneuern Sie einfach nur die Berechtigungen der Rolle, nicht die der Benutzer. Wenn Sie prüfen möchten, welche Systemberechtigungen für eine Datenbanksitzung gelten, fragen Sie die Sicht SESSION_PRIVS ab. Objektberechtigungen erhalten Sie z.B. über die Katalogsicht ALL_TAB_PRIVS_RECD.
Vordefinierte Benutzer SYS und SYSTEM Jede Oracle-Datenbank besitzt zwei vordefinierte Datenbankbenutzer, die einen speziellen Zweck erfüllen - SYS und SYSTEM. Das Schema SYS umfasst die Oracle Data Dictionary- Tabellen und die damit verbundenen Datenbankobjekte. Das Schema SYSTEM umfasst Tabellen, die von den Oracle Anwendungsentwicklungs-Werkzeugen wie Oracle Forms und Reports benutzt werden. Das StandardPasswort von SYS ist change_on_install, dasjenige von SYSTEM ist manager. Sie sollten keinerlei Datenbankobjekte wie Tabellen oder Indizes erzeugen, solange Sie als SYS oder SYSTEM angemeldet sind, mit Ausnahme der von Oracle ausgelieferten SQL-Skripte, die speziell festlegen, dass sie als SYS oder SYSTEM installiert werden sollten. Oracle9i gestattet es nicht wie Vorgängerversionen über CONNECT INTERNAL eine Datenbankverbindung als Benutzer SYS aufzubauen. Wenn Sie eine Instanz starten oder stoppen wollen bzw. administrative Aufgaben am Datenbankkatalog vornehmen wollen, müssen Sie sich als SYSDBA verbinden. Mit dem Standard-Passwort sähe der connect- Befehl wie folgt aus: connect sys/change_on_install as sysdba Auf UNIX Systemen können UNIX Benutzer, die in der DBA Gruppe sind, folgende Anweisung am Shell Prompt verwenden: sqlplus "/ as sysdba"
Vordefinierte Rollen Wenn eine Oracle-Datenbank installiert ist, sind u.a. folgende Datenbankrollen bereits erstellt: ●
●
CONNECT beinhaltet die Berechtigung für den Aufbau einer Datenbanksitzung (mittels der Systemberechtigung CREATE SESSION). Außerdem verbergen sich hinter der Rolle CONNECT folgende Berechtigungen: ALTER SESSION, CREATE TABLE, CREATE CLUSTER, CREATE SYNONYM, CREATE VIEW, CREATE SEQUENCE, CREATE DATABASE LINK. RESOURCE besitzt die Möglichkeit zur Erstellung von Tabellen, Indizes, Datenansichten und anderer Oracle-Objekte. Außerdem beinhaltet RESOURCE die Systemberechtigung UNLIMITED TABLESPACE, die es einem Benutzer gestattet, in beliebigen Tablespaces unbegrenzt Speicherplatz zu verbrauchen. RESOURCE ist ein Relikt aus der Zeit von Oracle6. Sie sollten die Rolle RESOURCE nicht verwenden, da Sie Gefahr laufen, dass ein böswilliger Benutzer mit diesem Recht durch das Erzeugen von wenigen Tabellen ohne Inhalt aber mit großen Extents den gesamten Speicherplatz der Datenbank verbraucht. Stattdessen sollten sie mit Speicherplatzquoten
●
●
●
●
(alter user username quota amountM on tablespace tablespace_name) arbeiten. Die folgenden Berechtigungen von RESOURCE sind nicht auch Berechtigungen der Rolle CONNECT: CREATE PROCEDURE, CREATE TRIGGER, CREATE TYPE, CREATE OPERATOR, CREATE INDEXTYPE. Falls ein Benutzer diese Berechtigungen benötigt, sollten Sie diese auf anderem Weg als über die Rolle RESOURCE zuteilen, z.B. über eine eigene Rolle. DBA besteht aus allen Systemberechtigungen, die zur Erstellung, Änderung oder dem Ausschluss von Benutzern und der Verwaltung der zugehörigen Datenbankobjekte benötigt werden. IMP_FULL_DATABASE besteht aus allen Systemberechtigungen, die zur Durchführung eines vollständigen Datenbankimports benötigt werden. EXP_FULL_DATABASE besteht aus allen Systemberechtigungen, die zur Durchführung eines vollständigen Datenbankexports benötigt werden. SELECT_CATALOG_ROLE erlaubt den lesenden Zugriff auf den Datenbankkatalog, d.h. auf Sichten, die mit dem Präfix DBA beginnen, wie z.B. DBA_ROLES. Ohne diese Berechtigung kann ein Benutzer nur auf Katalogsichten, die mit dem Präfix USER oder ALL beginnen, zugreifen.
Systemrechte und Objektrechte Eine Rolle ist nicht von Nutzen, solange sie nicht mindestens eine Berechtigung erteilt. Berechtigungen werden in zwei Kategorien unterteilt: ●
●
System-Berechtigungen erlauben einem Oracle-Benutzer die Ausführung von Anweisungen wie CREATE TABLE, CREATE USER oder ALTER INDEX. Eine Liste der Systemprivilegien erhalten Sie durch Abfrage der Katalogsicht SYSTEM_PRIVILEGE_MAP. Objekt-Berechtigungen stehen in Zusammenhang mit einer bestimmten Operation (so wie SELECT oder UPDATE) an einem Datenbankobjekt (z.B. einer Tabelle oder einem Index). Eine Liste der Objektprivilegien erhalten Sie durch Abfrage der Katalogsicht TABLE_PRIVILEGE_MAP.
Sie können einer Rolle oder einem Benutzer viele verschiedene Berechtigungen zuweisen - tatsächlich besitzt Oracle9i 140 Systemberechtigungen. Eine Systemberechtigung, die nahezu jeder Oracle-Benutzer braucht, ist CREATE SESSION - die Möglichkeit, eine Datenbanksitzung aufzubauen. Die SQLAnweisung GRANT wird verwendet, um Berechtigungen zu vergeben.
Erstellen und Verwenden einer Rolle Um diese Konzepte zu konkretisieren, geht dieser Abschnitt durch den Prozess des Erstellens einer Rolle und ihrer Zuweisung an eine bestimmte Person. Sie werden der Rolle außerdem Systemberechtigungen zuweisen. Dazu werden Sie einen Account benötigen, dem die Rolle DBA erteilt wurde. 1. Starten Sie die Oracle Enterprise Manager Console über das Start Menü oder mit dem Befehl oemapp console. 2. Wählen Sie Launch Standalone. 3. Bringen Sie die Datenbanken zur Anzeige. Evtl. müssen Sie auf Network und dann auf Databases klicken. Falls Ihre Datenbank nicht erscheint, fügen Sie diese mit dem Menüpunkt Navigator | Add Database to Tree hinzu. 4. Klicken Sie mit der rechten Maustaste auf die Datenbank und wählen Sie Connect. 5. Geben Sie den Benutzernamen und das Kennwort, z.B. Benutzername system und Kennwort manager, ein. 6. Klicken Sie auf das Pluszeichen vor Security, dann auf das Pluszeichen vor Roles. Sie sehen eine Übersicht vorhandener Rollen. 7. Klicken Sie mit der rechten Maustaste auf Roles und wählen Sie Create. 8. Geben Sie 'Developer' als Rollenname ein. Belassen Sie den Wert None für Authentication. Dies bedeutet, dass ein Benutzer, der diese Rolle verwenden möchte, sich nicht durch ein Passwort o.ä.
authentifizieren muss. 9. Vergeben Sie unter dem Kartenreiter Role die Rolle Connect, indem Sie Connect auswählen und den Pfeil nach unten betätigen. 10. Vergeben Sie unter dem Kartenreiter System Privileges die Rechte CREATE PROCEDURE und CREATE TRIGGER. 11. Klicken Sie auf Create, um die Rolle anzulegen.
Abbildung 10.1: Anzeige von Rollen mit der Oracle Enterprise Manager Console
Abbildung 10.2: Zuweisen von Systemrechten an eine Rolle Enterprise Manager erzeugt die Anweisung CREATE ROLE, um die Rolle anzulegen. Für jede Berechtigung, die der Rolle zugeteilt wird, erzeugt Enterprise Manager eine grant- Anweisung. Wenn Sie die generierten SQL-Anweisungen sehen möchten, klicken Sie auf Show SQL. Sie können jetzt, wie am Tag 3 gezeigt, mit der Enterprise Manager Console einen neuen Benutzer anlegen und diesem Benutzer die Rolle Developer zuteilen.
10.2 Synonyme Ein Synonym ist ein anderer Name für eine Tabelle. Eigentlich ist der Begriff Tabellen- Synonym genauer. Synonyme gibt es in zwei Gültigkeitsbereichen: privat und öffentlich. Ein privates Synonym ist nur für den Oracle-Benutzer sichtbar, der es erstellt hat. Ein öffentliches Synonym ist für alle Oracle-Benutzer sichtbar. Jeder Oracle-Benutzer, dem die Rolle CONNECT zugeteilt wurde, kann ein privates Synonym erzeugen. Auf der anderen Seite können nur Oracle-Benutzer, denen die Rolle DBA oder die Systemberechtigung Create Public Synonym zugeteilt wurde, ein öffentliches Synonym erstellen.
Ein Synonym ist ein anderer Name für eine Tabelle, Datensicht, Prozedur, Funktion oder ein Paket. Ein Synonym, das nur für seinen Erzeuger sichtbar ist, wird als privates Synonym bezeichnet. Ein Synonym, das für alle Oracle-Benutzer sichtbar ist, wird als öffentliches Synonym bezeichnet.
In einer Hinsicht ist ein Synonym einer Datensicht ähnlich: beide Objekte erlauben es einer Tabelle, unter einem anderen Namen referenziert zu werden. Ein Synonym ermöglicht es Ihnen jedoch nicht, den Zugriff auf Spalten zu beschränken oder sie neu zu benennen. Ein Synonym ist ein zusätzlicher Name für die Referenzierung einer Tabelle. Sie können zum Beispiel eine Tabelle nicht neu benennen, weil existierende Anwendungen sich auf den gegenwärtigen Tabellennamen beziehen. Ein Synonym, das einen etwas intuitiveren und sinnvollen Namen bietet, könnte ideal für die Nutzung in einem ad-hoc- Abfragewerkzeug sein.
Die Syntax der Anweisung CREATE SYNONYM Synonyme legen Sie über die grafische Benutzeroberfläche der Oracle Enterprise Manager Console oder über die Anweisung CREATE SYNONYM an.
Die Syntax für das Erzeugen eines Synonyms lautet: CREATE [PUBLIC] SYNONYM synonym_name FOR owner.object_name; Die Bedeutungen der Platzhalter sind: ●
● ●
synonym_name ist der Name des Synonyms und muss den Anforderungen der Namensvergabe an Objekte einer Oracle-Datenbank genügen. owner ist der Name des Schemas, in dem die referenzierte Tabelle oder Sicht abgelegt ist. object_name ist der Name eines Datenbankobjekts (Tabelle, Datensicht, Prozedur, Funktion, Paket), auf das sich das Synonym bezieht.
Ein Synonym kann auf eine Tabelle verweisen, die im Besitz eines anderen Oracle- Benutzers ist. Nehmen Sie einmal an, der Benutzer RJOHNSON hat die Projekttabelle erstellt und will es dem Benutzer KCHOW ermöglichen, diese Tabelle ebenfalls zu lesen. Als erstes erteilt RJOHNSON die SelectBerechtigung für die Projekttabelle an KCHOW. Jedes Mal jedoch, wenn KCHOW sich die Tabelle ansehen will, muss sie daran denken, den Tabellennamen mit dem Schemanamen RJOHNSON, dem Besitzer der Tabelle, zu qualifizieren. Sie erstellt deshalb konsequenterweise ein privates Synonym, das es ihr erlaubt, die Tabelle allein mit dem Tabellennamen zu referenzieren, wie in Listing 10.1 gezeigt wird. Listing 10.1: Verwendung eines privaten Synonyms SQL> select Project_Number from Project; from Project * ERROR at line 2: ORA-00942: table or view does not exist SQL> create synonym Project for RJOHNSON.Project; Synonym created. SQL> select Project_Number from Project; PROJECT_NUMBER
-------------1201 2143 4310
Löschen von Synonymen Oracle bietet eine Anweisung für das Löschen eines Synonyms - die DROP SYNONYM- Anweisung.
Die Syntax lautet wie folgt: DROP [PUBLIC] SYNONYM synonym_name; Der Platzhalter ist wie folgt definiert: ●
synonym_name ist der Name des Synonyms, das Sie löschen wollen.
Verbergen des Besitzes einer Tabelle mit Synonymen Betrachten Sie das folgende Beispiel. Sie haben eine projektbezogene Anwendung in Ihrem Unternehmen entwickelt. Sie müssen jetzt zwei Gruppen von Benutzern unterstützen: diejenigen, die Version 1.0 benutzen, und diejenigen, die Version 2.0 benutzen. Die Version 2.0 erfordert jedoch einige Änderungen an der Datenbank; die von Version 1.0 benutzte Datenbank kann nicht von der Version 2.0 verwendet werden. Die von beiden Versionen benutzten Tabellen können in derselben Oracle-Datenbank gespeichert werden, unter der Voraussetzung, dass sie im Besitz von verschiedenen Oracle- Accounts sind. Angenommen, dass die Tabellen der Version 1.0 im Besitz eines Oracle-Accounts namens PAV10 sind. Die Tabellen der Version 2.0 sind im Besitz eines anderen Oracle-Accounts mit Namen PAV20. Falls Sie eine Gruppe von Software-Testern unterstützen wollen, die zwischen den beiden Versionen hin- und herwechseln müssen, können Sie zwei SQL*Plus-Skripte erstellen. Das erste Skript löscht die existierenden Synonyme und erstellt neue, die auf die Tabellen der Version 1.0 verweisen, wie in Listing 10.2 gezeigt wird. Listing 10.2: Verweis auf die erste Version der Tabellen mit Hilfe von Synonymen drop synonym Account_Number; ... drop synonym Task_Header; drop synonym Task_Detail; ... ... create synonym Account_Number for PAV10.Account_Number; ... create synonym Task_Header for PAV10.Task_Header; create synonym Task_Detail for PAV10.Task_Detail; Das zweite Skript, gezeigt in Listing 10.3, löscht ebenfalls die existierenden Synonyme, die neu erstellten
verweisen jedoch auf die Tabellen der Version 2.0. Listing 10.3: Verweis auf die zweite Version der Tabellen mit Hilfe von Synonymen drop synonym Account_Number; ... drop synonym Task_Header; drop synonym Task_Detail; ... ... create synonym Account_Number for PAV20.Account_Number; ... create synonym Task_Header for PAV20.Task_Header; create synonym Task_Detail for PAV20.Task_Detail; Mit diesen Skripten kann ein Benutzer zwischen den beiden projektbezogenen Versionen hin- und herwechseln.
Anlegen eines Synonyms mit der Oracle Enterprise Manager Console Folgen Sie diesen Schritten, um mit der Oracle Enterprise Manager Console ein Synonym zu erstellen: 1. Verbinden Sie sich mit einer Datenbank. 2. Klicken Sie auf den Menüpunkt Object | Create und wählen Sie Synonym.
Abbildung 10.3: Anlegen eines Synonyms mit der Enterprise Manager Console 3. Geben Sie den Namen des Synonyms ein. Wählen Sie das Schema, in dem das Synonym angelegt werden soll, aus. Entscheiden Sie sich für den Objekttyp, auf den das Synonym verweisen soll, z.B.
Tabelle oder View. Wählen Sie das Schema, in dem das referenzierte Objekt liegt. Das Dialogfenster bietet Ihnen eine Liste der Objektnamen an. Wählen Sie eines der Objekte aus. 4. Klicken Sie auf Create, um das Synonym anzulegen.
10.3 Virtual Private Database und Label Security. Mit den Komponenten Virtual Private Database (VPD) und Label Security sind Sie in der Lage, erhöhte Sicherheitsanforderungen zu erfüllen, die mit den bisher besprochenen Mitteln nicht zu implementieren sind. Bei Einsatz von Virtual Private Database läuft jede Datenbanksitzung in einem Anwendungskontext, der beim Aufbau der Datenbanksitzung erzeugt wird. Datenbankzugriffe werden durch das Hinzufügen zusätzlicher Bedingungen (where- Klausel), so abgewandelt, dass geschützte Daten nicht zugänglich sind. Die Funktion SYS_CONTEXT und die Pakete DBMS_SESSION und DBMS_RLS (RLS steht für Row Level Security) spielen bei der Implementierung eine Rolle. Oracle Label Security stützt die Zugriffsrechtesteuerung auf einem Label als eigener Spalte einer Tabelle ab. Abhängig vom Wert des Label einer konkreten Zeile und den Berechtigungen des Datenbankbenutzers wird der Zugriff erlaubt oder verweigert. Oracle Label Security ist eine schlüsselfertige Lösung, die auf der Technologie von Virtual Private Database aufsetzt. Label Security erfüllt die Sicherheitsanforderungen von Militärorganisationen und Regierungen. Oracle Label Security wird mit dem Oracle Policy Manager, einem grafischen Administrationswerkzeug, konfiguriert. Oracle publiziert die neuesten sicherheitsrelevanten Erkenntnisse auf den Seiten des Oracle Technology Network im Internet. Wenn Sie hohe Sicherheitsanforderungen haben, sollten Sie von Zeit zu Zeit sicherheitsrelevante Informationen vom URL http:// otn.oracle.com/ abrufen.
10.4 Datenbankoptimierung Benutzer sind die besten Richter über die Performance eines Informationssystems. Sie messen die Leistung in Begriffen wie Antwortzeit, Bearbeitungszeit eines Berichts und Datenübertragungsgeschwindigkeit. Ein sehr ineffizientes System kann von Benutzern als ein System mit exzellenter Bearbeitungszeit wahrgenommen werden. Umgekehrt könnte eine Gruppe von Benutzern die Performance eines sehr effizienten Informationssystems für unzureichend halten. Sie sollten Beschwerden der Benutzer ernst nehmen. Nehmen Sie an, Sie müssen ein System analysieren, das Gegenstand von Beschwerden über schlechte Antwortzeiten ist. Sie werden vier Elemente einer Client-Server-Architektur untersuchen müssen: ● ● ● ●
das Netzwerk den Server die Oracle-Datenbank die Anwendung
Das Ziel einer Leistungssteigerung sollte darin bestehen, die größtmögliche, unmittelbare Verbesserung mit den geringsten Störungen für die vorhandene Software, Hardware und Geschäftsprozesse zu erreichen. Die folgenden Abschnitte behandeln jedes der vier Elemente etwas ausführlicher.
Netzwerk Das in einer Client-Server-Architektur benutzte Netzwerk kann ein LAN, WAN oder eine Kombination aus
beiden sein. Ziehen Sie einen Netzwerk-Spezialisten zu Rate, um den Auslastungsgrad des Netzwerks zu bestimmen. Charakterisieren Sie die Netzwerkanforderungen eines einzelnen Clients. Falls sich das Netzwerk als der Engpass herausstellen sollte, untersuchen Sie die Client-Anwendungssoftware, um zu erforschen, ob man die Zahl der SQL-Anforderungen an den Server reduzieren kann. Durch effiziente Programmierung wie z.B. Array Fetch, ist es möglich, statt sehr vieler kleiner Netzwerkpakete eine geringere Anzahl größerer Pakete zu erzeugen, um die Antwortzeit zu verbessern. Eine Änderung der Client-Software kann weitreichende Auswirkungen haben, jedoch steckt meistens das größte Potential zur Leistungsverbesserung in der Anwendung. Wenn Sie gerade mit dem Design einer Anwendung beginnen, können Sie den Datenverkehr auf dem Netzwerk reduzieren, indem Sie Funktionalitäten ermitteln, die mittels in der Datenbank gespeicherter Prozeduren und Funktionen realisiert werden können.
Anwendung Bemühungen um eine Leistungssteigerung auf Seiten der Anwendung können in drei Gruppen eingeordnet werden: ●
●
●
Maßnahmen, die anwendungsunabhängig sind. Sie müssen zum Beispiel keinerlei Software modifizieren oder neu aufbauen, um den globalen Systembereich (SGA) zu optimieren. Maßnahmen, die weitgehend anwendungsunabhängig sind. Sie könnten beispielsweise bei der Untersuchung der Abfragen einer Anwendung herausfinden, dass Sie Indizes zur Verbesserung der Abfrageleistung erstellen sollten. Maßnahmen, welche die Anwendungssoftware mit einbeziehen. Sie könnten herausfinden, dass das grundlegende Datenbankdesign ineffizient ist oder dass die Software unnötige SQL-Abfragen ausführt. Die Durchführung solcher Änderungen ist sehr teuer in Bezug auf das Budget und den Zeitplan. Der Versuch, ein System zu restrukturieren, nachdem es konstruiert wurde, ist sehr schwierig!
Optimierung von SQL Anweisungen Oracle9i hat zwei SQL-Optimierer - den regelbasierten (rule based optimizer, RBO) und den kostenbasierten Optimierer (cost based optimizer, CBO). Der regelbasierte Optimierer betrachtet die Struktur der SQL-Anweisung, die es zu optimieren gilt, ermittelt, welche Indizes für eine Tabelle existieren, und erstellt einen Ausführungsplan gemäß einer festen Anzahl von Regeln.
Ein Ausführungsplan ist die Abfolge der Grundoperationen, die das Oracle-RDBMS benutzt, um eine SQL-Anweisung auszuführen. Falls eine Tabelle zum Beispiel keine Indizes besitzt, wird der Ausführungsplan der Tabelle es erfordern, dass jede Zeile der Tabelle gelesen wird; dies ist der so genannte vollständige Tabellen-Scan. Statistiken über die Inhalte der abzufragenden Tabellen oder ihrer Indizes werden vom RBO nicht verwendet.
Der regelbasierte Optimierer berücksichtigt einen festen Satz von Regeln, um den effizientesten Plan zur Durchführung der in der SQL-Anweisung festgelegten Aufgaben zu ermitteln. Der kostenbasierte Optimierer ist eine Alternative zum regelbasierten. Der CBO benutzt zuvor errechnete Statistiken der Tabellen- und Indexinhalte, um den effizientesten Plan für die Ausführung der Aufgaben zu bestimmen, die von der SQL-Anweisung festgelegt wurden. Der kostenbasierte Optimizer betrachtet Statistiken, die sich auf die Tabelle, ihre Spalten und ihre Indizes beziehen, um daraus einen Ausführungsplan mit größtmöglicher Verringerung des hauptsächlichen Kostenfaktors zu berechnen - der Zahl der Datenbankblöcke, die gelesen werden müssen, um die Abfrageergebnisse zu ermitteln. Wenn eine Analyse der Tabellen oder Indizes ausgeführt wird, speichert Oracle die berechneten Statistiken in den Data-Dictionary-Tabellen. Statistiken werden entweder mit der SQL-Anweisung ANALYZE oder mit dem Paket DBMS_STATS (auch parallel) berechnet. Ein Initialisierungsparameter namens OPTIMIZER_MODE steuert die Wahl zwischen dem regelbasierten und dem kostenbasierten Optimierer. Dieser Parameter besitzt drei mögliche Werte: ●
● ●
CHOOSE: falls mindestens eine Tabelle in der zu optimierenden SQL-Anweisung Statistiken hat, wird der kostenbasierte Optimierer verwendet. Der Standardwert für OPTIMIZER_MODE ist CHOOSE. RULE: Oracle verwendet immer den regelbasierten Optimierer. FIRST_ROWS, ALL_ROWS: Oracle verwendet den kostenbasierten Optimierer. Zusätzlich zu FIRST_ROWS gibt es noch FIRST_ROWS_1000, FIRST_ROWS_100, FIRST_ROWS_10, FIRST_ROWS_1, um genauer anzugeben, wie optimiert werden soll. FIRST_ROWS optimiert auf möglichst schnelle Antwortzeit, d.h. darauf, den ersten Datensatz des Ergebnisses möglichst schnell zur Verfügung zu stellen.
Die Messung der Performance einer Abfrage ist nicht so einfach, wie es sich anhört. Wenn Sie dieselbe Abfrage zweimal auf eine größere Tabelle anwenden, wird die zweite Abfrage die Ergebnisse wahrscheinlich schneller liefern, weil die SGA im schnellen Hauptspeicher bereits die Datenbankblöcke enthält, welche die Abfrage lesen muss. Es werden weniger langsame Festplattenzugriffe benötigt, so dass die zweite Abfrage weniger Zeit zur Durchführung benötigt.
Ausführungspläne Eines der grundlegenden Merkmale von SQL ist die Abstraktion vom physischen Zugriff auf die Daten. Die Berechnung des physischen Zugriffspfades ist Aufgabe des SQL- Optimierers, nicht des Anwendungsentwicklers. Wenn Sie keine Tabellen und Indizes analysieren, wird Oracle den regelbasierten Optimierer verwenden, um den besten Ausführungsplan für jede Abfrage zu ermitteln. Verschiedene Möglichkeiten, um berechnete Ausführungspläne anzuzeigen, stehen zur Verfügung. Voraussetzung für die meisten Vorgehensweisen ist das Vorhandensein einer Tabelle PLAN_TABLE in Ihrem Schema. Zum Anlegen der PLAN_TABLE führen Sie ein SQL-Skript aus, das die Tabelle PLAN_TABLE in Ihrem Schema anlegt. Dies ist in Listing 10.4 dargestellt. Listing 10.4: Erstellen der Tabelle PLAN_TABLE SQL> @?\rdbms\admin\utlxplan.sql
Table created. SQL> desc plan_table Name Null? ----------------------------------------- -------STATEMENT_ID TIMESTAMP REMARKS OPERATION OPTIONS OBJECT_NODE OBJECT_OWNER OBJECT_NAME OBJECT_INSTANCE OBJECT_TYPE OPTIMIZER SEARCH_COLUMNS ID PARENT_ID POSITION COST CARDINALITY BYTES OTHER_TAG PARTITION_START PARTITION_STOP PARTITION_ID OTHER DISTRIBUTION CPU_COST IO_COST TEMP_SPACE
Type ------------VARCHAR2(30) DATE VARCHAR2(80) VARCHAR2(30) VARCHAR2(255) VARCHAR2(128) VARCHAR2(30) VARCHAR2(30) NUMBER(38) VARCHAR2(30) VARCHAR2(255) NUMBER NUMBER(38) NUMBER(38) NUMBER(38) NUMBER(38) NUMBER(38) NUMBER(38) VARCHAR2(255) VARCHAR2(255) VARCHAR2(255) NUMBER(38) LONG VARCHAR2(30) NUMBER(38) NUMBER(38) NUMBER(38)
Die folgenden Abschnitte zeigen die einzelnen Möglichkeiten Ausführungspläne zu erhalten auf.
Autotrace Am einfachsten erhalten Sie einen Ausführungsplan, indem Sie das Merkmal Autotrace von SQL*Plus bzw. SQL*Plus Worksheet nutzen. Die Syntax lautet: SET AUTOT[RACE] {ON|OFF|TRACE[ONLY]} [EXP[LAIN]] [STAT[ISTICS]] Mit SET AUTOTRACE ON erhalten Sie den Ausführungsplan und Ausführungsstatistiken, da die Optionen EXPLAIN und STATISTICS standardmäßig eingeschaltet sind. Die Anzeige der Statistiken erfordert die Rolle PLUSTRACE. Diese Rolle muss vom Benutzer SYS mit dem Skript \sqlplus\admin\plustrce.sql angelegt werden und kann anschließend als Benutzer SYSTEM mit der GRANT Anweisung an Anwendungsentwickler vergeben werden. Möchten Sie keine Ausführungsstatistiken, verwenden Sie SET AUTOTRACE ON EXPLAIN. Mit SET AUTOTRACE TRACEONLY steht eine Möglichkeit zur Verfügung, analog zum EXPLAIN PLAN Befehl im nächsten Abschnitt, nur einen Ausführungsplan zu erzeugen. Die eigentliche Ausführung findet nicht statt. Möchten Sie Ausführungsstatistiken, jedoch nicht das Ergebnis der SQL-Anweisung erhalten, verwenden Sie SET AUTOTRACE TRACEONLY STATISTICS. Mit dieser Option wird die Anweisung ausgeführt, weil nur so die Ausführungsstatistik ermittelt werden kann, jedoch wird die Ausgabe des
Ergebnisses unterdrückt. Listing 10.5: SQL*Plus Autotrace set autotrace traceonly select department_name, last_name from department natural join instructor; 18 rows selected. Execution Plan ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=18 Bytes=414) 1 0 HASH JOIN (Cost=3 Card=18 Bytes=414) 2 1 TABLE ACCESS (FULL) OF 'DEPARTMENT' (Cost=1 Card=9 Bytes=117) 3 1 TABLE ACCESS (FULL) OF 'INSTRUCTOR' (Cost=1 Card=18 Bytes=180)Statistics ---------------------------------------------------------0 recursive calls 4 db block gets 4 consistent gets 0 physical reads 0 redo size 1491 bytes sent via SQL*Net to client 634 bytes received via SQL*Net from client 3 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 18 rows processed Autotrace zeigt nur einen geringen Teil der Informationen aus dem PLAN_TABLE an. Es fehlen z.B. Informationen, die anzeigen, auf welche Partitionen einer Tabelle (siehe Tag 11) zugegriffen wurde.
EXPLAIN PLAN Sie können die EXPLAIN PLAN-Anweisung benutzen, um den Ausführungsplan für eine SQL-Anweisung zu erhalten.
Die Syntax für EXPLAIN PLAN lautet wie folgt: EXPLAIN PLAN FOR sql_statement; Der Platzhalter ist wie folgt definiert: ●
sql_statement ist die SQL-Anweisung, für die ein Ausführungsplan generiert werden soll.
Bevor Sie die EXPLAIN PLAN-Anweisung benutzen können, benötigen Sie die im vorigen Abschnitt beschriebene Tabelle PLAN_TABLE. Wann immer diese Anweisung ausgeführt wird, fügt Oracle Zeilen in die Tabelle PLAN_TABLE ein, deshalb müssen Sie den Inhalt der Tabelle vor jeder Benutzung von EXPLAIN PLAN löschen. Listing 10.6 enthält die Schritte, die erforderlich sind, um den Ausführungsplan
einer Anweisung zu ermitteln. Listing 10.6: Verwendung der explain plan-Anweisung SQL> delete from plan_table; 0 rows deleted. SQL> explain plan for select department_name, last_name, first_name from department natural join instructor; Explained. SQL> @?\rdbms\admin\utlxpls Plan Table -------------------------------------------------------------|Operation | Name |Rows|Bytes|Cost |Pstart|Pstop| -------------------------------------------------------------|SELECT STATEMENT | | 18| 522 | 3 | | | | HASH JOIN | | 18| 522 | 3 | | | | TABLE ACCESS FULL|DEPARTMENT| 9| 117 | 1 | | | | TABLE ACCESS FULL|INSTRUCTOR| 18| 288 | 1 | | | -------------------------------------------------------------Das Skript utlxpls.sql wurde verwendet, um einen gut lesbar formatierten Ausführungsplan zu erzeugen. Utlxpls.sql ist Teil jeder Oracle-Distribution und führt ein hierarchisches SELECT auf die Tabelle PLAN_TABLE aus. Falls Sie SQL-Anweisungen mit Parallel Query bzw. Parallel DML parallel ausführen lassen, steht Ihnen das Skript utlxplp.sql zur Verfügung, um die Parallelisierungsstrategie des Optimierers anzuzeigen. Die Spalten Pstart und Pstop im Ausführungsplan haben nur im Zusammenhang mit der Partitioning Option eine Bedeutung. Der CBO teilt über diese beiden Spalten mit, auf welche Partitionen die Ausführung der Anweisung zugreifen würde. Die Partitioning Option werden Sie am Tag 11 »Tabellen und Indizes für Fortgeschrittene« kennen lernen. Der kostenbasierte Optimierer hat sich auf Grund der sehr kleinen Tabellen entschieden, einen Full Table Scan durchzuführen, obwohl ein Index auf der Spalte DEPARTMENT_ID, über die der Equi-Join erfolgt, vorhanden ist. Der regelbasierte Optimierer würde diesen Index verwenden. Funktionalitäten wie Hash Join und Star Transformation, die für ein Data Warehouse unerlässlich sind, kennt der regelbasierte Optimierer nicht. Oracle empfiehlt, nur noch den kostenbasierten Optimierer einzusetzen. Für OLTP Anwendungen ist der regelbasierte Optimierer meistens ausreichend, jedoch nicht für komplexe Abfragen auf umfangreiche Datenmengen, wie sie z.B. bei einer Data Warehouse Anwendung auftreten.
Ausführungspläne und Enterprise Manager Console Oracle9i hat eine zweite Möglichkeit, Ausführungspläne zu erzeugen. Es gibt eine Sicht mit dem Namen v$sql_plan, in der die Instanz die internen Ausführungspläne zugänglich macht. Wenn Sie mit Enterprise Manager 2.2 gegen eine Oracle9i Instanz arbeiten, wird nicht EXPLAIN PLAN sondern v$sql_plan verwendet. Bei Verwendung von v$sql_plan können Sie sicher sein, den wirklich verwendeten Plan zu sehen. Bei EXPLAIN PLAN müssen Sie sicherstellen, dass die Sitzung in der EXPLAIN PLAN ausgeführt wird, die gleichen Einstellungen relevanter Parameter wie z.B. sort_area_size, hash_join_enabled, query_rewrite_ enabled u.a.m. hat, wie die Datenbanksitzung, die Sie untersuchen möchten.
Abbildung 10.4: Grafische Anzeige und Animation eines Ausführungsplans in der Enterprise Manager Console Die Enterprise Manager Console ist in der Lage, den Ausführungsplan der letzten SQL- Anweisung jeder Datenbanksitzung grafisch anzuzeigen. Benötigen Sie weitergehende Funktionalitäten wie z.B. Vorschläge zu leistungssteigernden Änderungen an der SQL- Anweisung oder am Datenbankschema, sollten Sie das Enterprise Manager Tuning Pack einsetzen.
Analyse einer Anweisung mit Hilfe von TKPROF Das Werkzeug TKPROF konvertiert Protokolldateien, die zuvor mit alter session set sql_trace=true erzeugt wurden, in Berichte mit Angaben zu Antwortzeiten, CPU-Zeit Verbrauch, Plattenzugriffen und Ausführungsplänen. Ein TKPROF-Bericht ist aus folgenden Gründen nützlich für Leistungsanalysen und Leistungssteigerungen: ●
●
Er enthält die SQL-Anweisungen, die von einem bestimmten Benutzer während einer Datenbanksitzung ausgeführt wurden. Wenn der Initialisierungsparameter TIMED_STATISTICS gesetzt ist, enthält die Protokolldatei Informationen über die CPU- und I/O-Kosten, die bei der Ausführung der SQL-Anweisung angefallen sind.
Standardmäßig erzeugt eine Datenbanksitzung keine Protokolldateien. Falls Sie die Abarbeitung einer Gruppe von SQL-Anweisungen analysieren wollen, setzen Sie sql_trace und timed_statistics auf true, führen die SQL-Anweisungen aus, setzen sql_trace auf false und erzeugen mit TKPROF einen Bericht. Falls Sie eine fertige Anwendung verwenden, die sql_trace nicht einschalten kann, verwenden Sie als Benutzer SYS die Funktion SET_SQL_TRACE_IN_SESSION aus dem Paket DBMS_SYSTEM oder
einen LOGON Trigger, um sql_trace einzuschalten. SQL> alter session set timed_statistics=true; Session altered. SQL> alter session set sql_trace=true; Session altered. SQL> select department_name, last_name, first_name from department natural join instructor; SQL> alter session set sql_trace=false; Session altered. SQL> show parameter user_dump_dest NAME TYPE VALUE ------------------ ----------- -------------------------user_dump_dest string d:\ora901\admin\ORCL\udump Wechseln Sie in das Verzeichnis, auf das der Parameter user_dump_dest verweist. Die neuste Datei in diesem Verzeichnis ist Ihre Protokolldatei. Verwenden Sie TKPROF, um einen Bericht zu erzeugen: tkprof Protokolldatei Berichtdatei Protokolldatei ist der Name der Protokolldatei (z.B. ORA00466.TRC), Berichtdatei ist der Name der Datei, die den erzeugten Bericht enthält. Der Ausschnitt mit den Angaben zu der oben ausgeführten SQLAnweisung aus der Berichtdatei ist unten wiedergegeben. select department_name, last_name, first_name from department natural join instructor call count cpu elapsed disk query current rows ------- ----- ------ ------- ---- ----- ------- ---Parse 1 200.29 300.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 3 0.00 0.00 0 4 4 18 ------- ----- ------ ------- ---- ----- ------- ---total 5 200.29 300.00 0 4 4 18 Misses in library cache during parse: 1 Optimizer goal: CHOOSE Parsing user id: 27 Rows Row Source Operation ------- --------------------------------------------18 HASH JOIN 9 TABLE ACCESS FULL DEPARTMENT 18 TABLE ACCESS FULL INSTRUCTOR Wie Sie sehen können, bietet die Protokolldatei eine Fülle an statistischen Informationen über die SQLAnweisungen, die während der Datenbanksitzung ausgeführt wurden. Dieses Werkzeug ist insbesondere bei der Lösung so genannter 80-20 Probleme eine große Hilfe: 80% des Leistungsgewinns wird durch Verbesserung der 20% teuersten SQL- Anweisungen erzielt.
Optimierung der Oracle-Instanz Die in den vorhergehenden Abschnitten beschriebenen Methoden können Sie einsetzen, wenn Sie eine Anwendung kennen bzw. selbst entwickeln und dadurch bereits wissen, welche SQL-Anweisungen Sie untersuchen müssen. Fehlt Ihnen dieses Vorwissen bzw. möchten Sie eine Oracle-Instanz umfassend
optimieren, sollten Sie auf das Paket Statspack zurückgreifen. Statspack ist seit Oracle 8.1.6 Bestandteil der Oracle Software-Distribution.
Die Datei links.html auf der Begleit-CD enthält einen URL, unter dem Sie Statspack für Oracle Versionen vor 8.1.6 herunterladen können. Statspack ist ein ausgereiftes Diagnosewerkzeug. Es ist den gebräuchlichen bstat/estat Skripten, die mit Oracle9i weiterhin ausgeliefert werden, überlegen. Einige der Gründe für die Überlegenheit von Statspack sind: ● ●
● ● ●
Statspack normalisiert die ermittelten Zahlen pro Transaktion und pro Sekunde Statspack speichert die Performancedaten dauerhaft und ermöglicht dadurch die Entwicklung der Performance über einen längeren Zeitraum zu verfolgen Statspack weist die teuersten SQL-Anweisungen aus Statspack kann mit Real Application Clusters und Oracle Parallel Server eingesetzt werden der Abschnitt »Top 5 Wait Events« im Statspack-Bericht macht auf einen Blick deutlich, welche Bereiche in Angriff genommen werden müssen. Uninteressante Wartezustände (idle waits, wie z.B. 'SQL*Net message from client') sind bereits ausgefiltert.
Die Verwendung von Statspack besteht aus den Aktivitäten Installation (einmalig pro Datenbank), Datenerfassung und Berichterstellung. Die Datenerfassung gestaltet sich mit dem Aufruf einer PL/SQLProzedur, z.B. aus SQL*Plus sehr einfach: SQL> EXECUTE statspack.snap; Die Berichterstellung ist ebenfalls sehr einfach. Sie rufen das SQL Skript spreport.sql auf und geben den Zeitraum, für den der Bericht erstellt werden soll, ein. SQL> @?/rdbms/admin/spreport Current Instance ~~~~~~~~~~~~~~~~ DB Id DB Name Inst Num Instance ----------- ------------ -------- -----------2933596068 RACTEST 1 RACTEST1 Instances in this Statspack schema ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DB Id Inst Num DB Name Instance Host ----------- -------- ------------ ------------ ----------2933596068 1 RACTEST RACTEST1 alpham1.myc ompany.com 2933596068 2 RACTEST RACTEST2 alpham2.myc ompany.com Using 2933596068 for database Id Using 1 for instance number Completed Snapshots
Snap Snap Instance DB Name Id Snap Started Level Comment ------------ ------------ ----- ----------------- ----- ---------------------RACTEST1 RACTEST 3 23 Dec 2000 17:41 5 4 23 Dec 2000 17:45 5 Specify the Begin and End Snapshot Ids ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Enter value for begin_snap: 3 Enter value for end_snap: 4 Der erzeugte Bericht ist übersichtlich in verschiedene Abschnitte gegliedert. Der Abschnitt »Instance Efficiency Percentages« gibt Ihnen u.a. einen Überblick darüber, wie effizient die verschiedenen Puffer in der SGA genutzt werden: Instance Efficiency Percentages (Target 100%) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Buffer Nowait %: 97.79 Redo NoWait %: Buffer Hit %: 99.84 In-memory Sort %: Library Hit %: 99.98 Soft Parse %: Execute to Parse %: 0.46 Latch Hit %: Parse CPU to Parse Elapsd %: 49.30 % Non-Parse CPU:
100.00 96.61 99.97 99.65 99.99
Der Abschnitt »Top 5 Wait Events« macht deutlich, worauf die Instanz am häufigsten wartet: Top 5 Wait Events ~~~~~~~~~~~~~~~~~ Wait % Total Event Waits Time (cs) Wt Time ----------------------------------- --------- -----db file scattered read 40656 12066 80,1 db file sequential read 7833 2390 15,8 buffer busy waits 74 281 1,87 SQL*Net break/reset to client 4109 165 1,1 write complete waits 1 33 ,22 Die deutlichste Verbesserung in dem Beispiel oben wäre zu erzielen, wenn das Warteereignis 'db file scattered read', das bei einem Full Table Scan auftritt, seltener auftreten würde oder schneller beendet wäre. Wenn die Summe der Wartezeiten im Vergleich zu der genutzten CPU-Zeit im Abschnitt »Instance Activity Stats« sehr groß ist, verbringt die Instanz zu viel Zeit mit Warten und kann nicht zufriedenstellend von schnellen CPUs profitieren. Instance Activity Stats: Statistic Total per Second per Trans ------------------------ ------ ---------- --------CPU used by this session 79,662 87.6 79,662.0 Erklärungen der Warteereignisse finden Sie im Anhang A der »Oracle9i Database Reference«. Die Optimierung einer Oracle-Instanz ist Thema eigener Bücher, daher kann dieser Themenkomplex hier nur angerissen werden. Weitere Informationen zu Statspack finden Sie im Kapitel 21 des Handbuchs »Oracle 9i Database Performance Guide and Reference«. Beachten Sie auch das Handbuch »Oracle 9i Database Performance Methods«. Es enthält die wichtigsten Richtlinien (z.B. »Top Ten Mistakes Made by Oracle Users«), deren Einhaltung sicherstellt, dass Ihre Anwendung keine schwerwiegenden Engpässe verursacht.
Einschränken der Ressourcen mit Hilfe eines Profils Zusätzlich zur Freigabe eines Speicherkontingents in einem Tablespace bietet Oracle weitere Kontingente, die einem Benutzer zur Verfügung gestellt werden können. Diese Kontingente schließen folgendes ein: ●
●
●
CONNECT_TIME ist die Zahl der abgelaufenen Minuten, seit der Benutzer die Verbindung zur Oracle-Datenbank aufgebaut hat. IDLE_TIME ist die Zahl der abgelaufenen Minuten, seit der Benutzer zum letzten Mal eine Aktion ausgeführt hat, die zu einem Oracle-Aufruf geführt hat. LOGICAL_READS_PER_CALL ist die Zahl der Oracle-Blöcke, die aus dem Speicher oder von einem Laufwerk gelesen wurden, um einen Oracle-Aufruf zu erfüllen.
Der Mechanismus, den Sie für die Spezifizierung dieser Kontingente festlegen, ist das Profil. Sie könnten beispielsweise für einen Gelegenheitsanwender ein Profil erstellen, das seine oder ihre Verbindungszeit auf zwei Stunden oder weniger begrenzt. Darüber hinaus könnten Sie außerdem befürchten, dass ein Gelegenheitsbenutzer - insbesondere jemand, der Zugriff zu einem ad-hoc-Abfragewerkzeug hat versehentlich Abfragen konstruieren kann, die ein kartesisches Produkt aus verschiedenen Tabellen zur Folge haben. Indem Sie LOGICAL_READS_PER_CALL auf einen geeigneten Wert setzen, können Sie verhindern, dass ein Benutzer Ressourcen monopolisiert, die von anderen Benutzern benötigt werden. Um die in einem Benutzerprofil festgelegten Ressourcen-Beschränkungen zu erzwingen, muss der DBA entweder den Initialisierungsparameter resource_limit in der Parameterdatei init.ora auf den Wert TRUE setzen und die Instanz neu starten oder alter system set resource_limit=true ausführen.
Die Leistungsfähigkeit durch Erstellen von Indizes verbessern Ein wesentlicher Teil der Datenbankaktivität besteht aus SELECT-Anweisungen. Entsprechend führt eine Verbesserung der Abfrageleistung zu einer Steigerung der Gesamtleistung. Eine Abfrage wird im Allgemeinen schneller abgearbeitet, wenn ein Index für den Zugriff auf die gewünschten Zeilen benutzt wird. Ein vollständiger Tabellen-Scan ist eine Abfrage, in der alle Zeilen der Tabelle gelesen werden, um die gewünschten Zeilen zu finden. Um zu ermitteln, ob eine Abfrage einen vollständigen Tabellen-Scan durchführt, müssen Sie den Ausführungsplan der Abfrage einsehen. Zeigt ein Ausführungsplan einen vollständigen Tabellen-Scan, erwägen Sie die Erstellung eines Index, den die Abfrage benutzen kann. Seien Sie vor der Möglichkeit einer Überindizierung einer Tabelle gewarnt. Erinnern Sie sich, dass Oracle automatisch die Indizes einer Tabelle bearbeitet, wann immer sich die Inhalte der Tabelle aufgrund einer Anweisung wie INSERT, UPDATE oder DELETE ändern. Ihr Ziel sollte es sein, die häufigsten Abfragen zu optimieren, ohne Oracle zu veranlassen, eine unmäßige Zahl von Indizes zu verwalten.
Die SGA vergrößern Neben ihren anderen Funktionen arbeitet die SGA als Cache für Oracle. Wenn Sie die Zahl der Datenblockpuffer in der SGA vergrößern, erhöhen Sie gleichzeitig die Wahrscheinlichkeit, dass eine SQLAnweisung den benötigten Block im Buffer Cache der SGA findet, wodurch auch die Anzahl der langsamen Festplattenzugriffe reduziert werden. Versichern Sie sich jedoch, dass die SGA nicht so groß wird, dass sie den maximalen Speicher des Betriebssystems überschreitet. Die Größe des Buffer Cache wird mit dem Parameter db_cache_size eingestellt. Oracle9i ist in der Lage, den Buffer Cache zur Laufzeit dynamisch zu verändern. Wenn der Parameter sga_max_size hoch genug gesetzt ist, kann der Buffer Cache vergrößert werden, ohne die Instanz zu stoppen. Weitere Parameter, die Einfluss auf die Leistung und die Größe der SGA haben, sind: log_buffer, shared_pool_size (dynamisch änderbar),
large_pool_size, java_pool_size und db_{2|4|6|8|16}k_ cache.size. Letzterer ist nur relevant, wenn Sie Tablespaces mit verschiedenen Blockgrößen einsetzen.
Konflikte beim Zugriff auf Plattenspeicher verringern Um I/O-Operationen von Laufwerken zu optimieren, müssen Sie zuerst herausfinden, ob diese Operationen gleichmäßig auf alle Laufwerke des Servers verteilt sind. Benutzen Sie Befehle und Werkzeuge des Betriebssystems, um die durchschnittliche Zahl der I/O- Anforderungen an jedes Laufwerk zu ermitteln. Ihre Zielsetzungen sollten folgendes einschließen: ●
●
●
●
Verringern Sie die Gesamtzahl von Laufwerks-I/O-Anforderungen, indem Sie dem Server mehr Arbeitsspeicher zur Verfügung stellen. Durch Hinzufügen von mehr RAM werden Sie in der Lage sein, die Größe der SGA zu vergrößern und damit die Gesamtzahl der I/O-Laufwerkszugriffe zu reduzieren. Verringern Sie die durchschnittliche Zahl der I/O-Anforderungen für jedes Laufwerk, indem Sie dem Server zusätzliche Laufwerke zur Verfügung stellen. Sie können anschließend die Datendateien eines Tablespace auf ein neues Laufwerk verschieben, indem Sie die Anweisung ALTER TABLESPACE mit der Option RENAME benutzen. Verringern Sie die durchschnittliche Anzahl von I/O-Anforderungen für jeden Laufwerkscontroller, indem Sie dem Server einen weiteren Controller hinzufügen. Verteilen Sie die I/O-Anforderungen gleichmäßig zwischen den Laufwerken, so dass jedes Laufwerk annähernd dieselbe Zahl von I/O-Zugriffen verarbeitet. Diese Optimierungsmethode benötigt gleichermaßen Analysen und Experimente, um die optimale Verteilung von OracleDatendateien und Log-Dateien zu ermitteln. Am besten erreichen Sie eine gleichmäßige Verteilung, wenn Sie RAID Hardware (Disk Arrays) oder Software (Volume Manager) einsetzen.
Hindernisse für eine optimale Systemleistung Eines der Ziele dieser Lektion ist die Benennung von Performance-Merkmalen im Zusammenhang mit einer Oracle-Datenbank. Das Material dieser Lektion ist eine Einführung in einige der Punkte, die Ihnen während des Datenbank- Entwicklungsprozesses begegnen werden. Hier sind einige allgemeine Vorschläge. ●
●
●
●
●
●
Kaufen Sie ausreichend Hauptspeicher (RAM), damit das System nicht unnötig Zeit mit dem Einund Auslagern von Hauptspeicher auf Festplatte (Paging, Swapping) verliert. Erwerben Sie Festplatten nicht allein nach Kapazität. Viele Platten mit geringer Kapazität bringen wesentlich mehr Leistung als wenige Platten mit hoher Kapazität, weil viele Schreib-/Leseköpfe simultan arbeiten können. Um einen Prozessor (CPU) auszulasten, benötigen Sie ca. 4-12 Festplatten, denn eine Festplatte ist um Größenordnungen langsamer als ein Prozessor. Setzen Sie Hardware- oder Software-RAID (Redundant Array of Independent Disks) Technologien ein, um eine hohe Bandbreite im Plattensubsystem zu erhalten. Wählen Sie die geeignete Plattform - Hardware und Betriebssystem - für die Anforderungen der Anwendungen. Nutzen Sie Merkmale wie Bind Variablen und Array Fetch (siehe Tag 14) sowie Insert, um zu verhindern, dass die Anwendung der Engpass ist bzw. unnötig Wartezeit wegen des wiederholten Parsens gleichartiger SQL-Anweisungen ohne Bind Variablen erzeugt wird. Vermeiden Sie ständiges An- und Abmelden bei der Oracle-Instanz. Lassen Sie Datenbanksitzungen geöffnet und verwenden Sie diese wieder.
Behalten Sie diese Richtlinien im Gedächtnis, wenn Sie etwas über die Performance- Charakteristiken lesen, die in einem Oracle-Client-Server-Umfeld auftreten können.
10.5 Zusammenfassung Diese Lektion behandelte Themen rund um die Verwaltung von Datenbankbenutzern, Rollen und Berechtigungen. Außerdem wurde auf die Analyse von SQL Anweisungen und Möglichkeiten zur Leistungssteigerung eingegangen. Die Themen im Einzelnen waren: ●
●
●
●
●
●
●
●
●
●
●
●
●
Sie können Benutzer, Rollen und Berechtigungen mit der grafischen Benutzeroberfläche des Oracle Enterprise Manager verwalten. Sie können außerdem SQL-Befehle in SQL*Plus oder SQL*Plus Worksheet benutzen, um Benutzer, Rollen und Berechtigungen zu erstellen und anzupassen. Ein Profil ist ein Satz von Kontingenten auf Systemebene, die einem Benutzer oder einer Rolle zur Verfügung gestellt werden können. Diese Kontingente schließen den Aufwand an Leerlaufzeit und die Zahl der logischen Lesezugriffe mit ein, die für eine einzelne Abfrage ausgeführt werden. Um die Netzwerkbelastung bei Inbetriebnahme einer Anwendung zu reduzieren, planen Sie die Verwendung von gespeicherten Prozeduren und Funktionen, um die Zahl der SQL-Anforderungen auf dem Netzwerk zu reduzieren und um überflüssige SQL-Anfragen von der Client-Anwendung zu minimieren. Um Antwortzeit und Durchsatz einer Anwendung zu verbessern, optimieren Sie bestimmte Parameter der SGA und erstellen geeignete Indizes. Um die Performance des Servers zu verbessern, fügen Sie Arbeitsspeicher hinzu, so dass die Anzahl der Datenblockpuffer in der SGA erhöht werden kann, ohne Paging oder Swapping zu verursachen. Fügen Sie außerdem mehr Laufwerke hinzu, um die durchschnittliche Anzahl von I/OAnforderungen pro Laufwerk zu reduzieren. Oracle stellt zwei SQL Optimierer zur Verfügung: den regelbasierten und den kostenbasierten Optimierer. Der regelbasierte Optimizer nutzt eine geringe Anzahl (ca. 15) fest eingebauter Regeln, um die effizienteste Methode zur Abfrage der gewünschten Zeilen zu ermitteln. Der kostenbasierte Optimizer betrachtet Statistiken von Tabellen und Indizes, um die effizienteste Methode zum Abruf der gewünschten Zeilen zu ermitteln. Die Anweisung EXPLAIN PLAN kann verwendet werden, um den Ausführungsplan einer Abfrage zu ermitteln. Mit der Oracle Enterprise Manager Console können Sie einen animierten, grafisch aufbereiteten Ausführungsplan betrachten. Sie können eine Protokolldatei einer Datenbanksitzung generieren, indem Sie ALTER SESSION SET SQL_TRACE=TRUE ausführen. TKPROF ist ein Werkzeug, das eine Protokolldatei einer Datenbanksitzung in einen Bericht konvertiert, der jede SQL-Anweisung enthält, die während einer Datenbanksitzung ausgeführt wurde. Verwenden Sie das Paket Statspack, um Performancediagnose-Daten zu sammeln und eine Oracle-Instanz zu optimieren.
10.6 Wie geht es weiter? Am Tag 11 lernen Sie die Partitioning Option und zusätzliche Indizierungsmöglichkeiten kennen. Sie verschaffen sich einen Überblick über objektrelationale Merkmale von Oracle9i.
10.7 Fragen und Antworten Frage: Kann eine Datenbankrolle einer anderen Datenbankrolle zugewiesen werden?
Antwort: Ja. Sie können eine Rolle einer anderen Rolle oder einem Oracle-Benutzer zuweisen. Sie sollten es jedoch vermeiden, einen unnötig komplexen Satz von Rollen zu erstellen. Die Benutzung von Rollen sollte das Verwalten von Datenbankberechtigungen vereinfachen und nicht komplizieren.
10.8 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Eine Datenbankrolle kann Benutzern und anderen Datenbankrollen zugeteilt werden. 2. Richtig oder falsch? Das Hinzufügen eines Index verbessert immer die Leistung einer Anwendung. 3. Richtig oder falsch? Jeder Oracle-Benutzer kann ein öffentliches Synonym erstellen, solange dieses auf eine Tabelle verweist, die sich im Besitz des Benutzers befindet.
Übungen Sie können diese Fragen allgemein beantworten, ohne die Verwendung von SQL- Anweisungen: Wenn Sie der DBA am Flugle College wären, welche Datenbankrolle würden Sie erzeugen? Welche Berechtigungen würden Sie dieser Rolle gewähren?
Fortgeschrittene Abfragen mit SQL Sie haben bereits gesehen, wie die select-Anweisung Datensätze aus einer einzelnen Tabelle abfragt. Diese Lektion untersucht die komplexeren Merkmale der select- Anweisung: die group byKlausel, die having-Klausel und die Join-Operation. Sie werden sehen, wie leistungsfähig die selectAnweisung ist. Die Syntax der select-Anweisung ist recht schwer zu verstehen und Sie können die Verwendung ihrer Klauseln nicht wirklich verstehen, indem Sie lediglich die in der Dokumentation gezeigten Syntaxdiagramme studieren. Darum sollten Sie sich die vielen Beispiele in dieser Lektion anschauen. Sie werden erkennen, was funktioniert und was nicht.
9.1 SELECT FOR UPDATE Viele Anwendungen müssen sicherstellen, dass ein Datensatz, der in der Benutzeroberfläche einer Anwendung durch einen Anwender markiert wurde, nicht von einer Transaktion eines weiteren Anwenders verändert wird. Die for update-Klausel der select-Anweisung sperrt einen Datensatz gegen Änderungen durch andere Transaktionen, bis die Sperre durch COMMIT freigegeben wird. Während ein Anwender die Sperre hält, kann er sicher sein, den Datensatz ohne Wartezeit ändern zu können. Stellen Sie sich vor, Sie möchten Konzertkarten kaufen. Die Verkäuferin hat zwei Plätze nach Ihrem Wunsch ausgewählt und bittet Sie um Ihre Kreditkarte, um die Transaktion abzuwickeln. Wären die Plätze nicht mit SELECT .. FOR UPDATE gesperrt, könnte eine andere Verkäuferin, während Sie Ihre Kreditkarte aus dem Portemonnaie nehmen, die Plätze verkaufen und Sie wären sehr verärgert. Die erweiterte Syntax der select-Anweisung mit FOR UPDATE ist: SELECT select_list FROM table_name [WHERE condition] [FOR UPDATE [ NOWAIT | WAIT wait_time ] ]; Die Bedeutungen der Platzhalter sind: ● ● ● ●
select_list ist eine Liste von Spalten aus table_name. table_name ist der Name einer Tabelle, aus der Zeilen abgefragt werden. condition ist eine gemäß der SQL-Syntax zulässige Bedingung. durch FOR UPDATE werden die Zeilen in der Ergebnismenge des SELECT gesperrt. Wenn Sie zusätzlich NOWAIT angeben, gibt Oracle einen Fehler zurück, falls eine der Zeilen bereits
gesperrt ist. Benutzen Sie WAIT wait_time, um wait_time Sekunden auf das Sperren zu warten, falls eine Sperre nicht sofort erworben werden kann. Wenn Sie weder NOWAIT noch WAIT verwenden, wartet die Datenbanksitzung unbegrenzt.
9.2 Gruppenfunktionen Der erste Punkt, den wir besprechen, sind die Funktionen von Oracle, die mit Gruppen von Zeilen arbeiten. Seien Sie gewarnt: dieses Thema ist ein gutes Beispiel für merkwürdige Eigenschaften von SQL. Obwohl diese Funktionen Gruppenfunktionen sind, ist bei ihnen die Verwendung der group by-Klausel nicht nötig. ● ● ● ●
count zählt die Anzahl der Zeilen, die Oracle aufgrund der angegebenen Kriterien liefert. max und min geben die Minimal- und Maximalwerte für die angegebene Spalte zurück. avg und sum berechnen den Durchschnitt und die Summe über eine Spalte. stddev und variance berechnen Standardabweichung und Varianz für eine Spalte.
Jede dieser Funktionen gibt einen einzelnen Wert zurück. Es folgen einige ausführliche Beispiele.
Die Funktion count Die Funktion count gibt es in zwei Versionen: count(*), die alle Zeilen in einer Tabelle zählt, die ein gewisses Kriterium erfüllen, und count(column_name), die alle Zeilen in einer Tabelle zählt, die einen Wert ungleich NULL für column_name haben und den angegebenen Kriterien entsprechen. Wenn Sie die Anzahl der Studenten zählen wollen, die eine Faxnummer besitzen, geben Sie, wie in Listing 9.1 gezeigt, Fax als Argument für die Funktion count an. Listing 9.1: Verwendung der Funktion count SQL> select count(*) from Student; COUNT(*) --------31 SQL> select count(Fax) from Student; COUNT(FAX) ---------3 Sie sollten beachten, dass die Funktion count eine Zeile zurückgibt, auch wenn die Zählung den Wert Null (0) ergab. Das Ergebnis des zweiten select ist 3, weil nur bei drei Studenten eine Faxnummer gespeichert ist. Bei den übrigen Studenten hat die Spalte Fax den Wert NULL.
Die Funktionen avg und sum Die Funktionen avg und sum berechnen Durchschnitt bzw. Summe der Werte einer Spalte. Listing 9.2 zeigt ein Beispiel. Eine Abfrage der Tabelle Course liefert den Durchschnitt und die Summe der für Kurse berechneten Gebühren. Listing 9.2: Verwendung der Funktionen avg und sum
SQL> select avg(Additional_Fees), sum(Additional_Fees) from Course; AVG(ADDITIONAL_FEES) SUM(ADDITIONAL_FEES) -------------------- -------------------46.171875 1477.5 Beachten Sie, dass die Funktion avg bei ihren Berechnungen Zeilen, die in der verwendeten Spalte den Wert NULL haben, ignoriert.
Kombinieren von Gruppenfunktionen mit anderen Spalten Sie können Gruppenfunktionen und Spalten in der Auswahlliste einer select-Anweisung nicht ohne eine group by-Klausel kombinieren. Listing 9.3 bietet ein Beispiel einer Abfrage, in der die Auswahlliste aus zwei Teilen zusammengesetzt wird - Department_ID und sum(Additional_Fees). Oracle gibt hier jedoch einen Fehler zurück. Um zu verstehen warum, hilft es, in folgenden Begriffen zu denken: Eine Gruppenfunktion gibt eine einzelne Zeile zurück, Department_ID gibt dagegen alle Zeilen der Tabelle zurück. Es ist einfach nicht sinnvoll, diese beiden Werte gleichzeitig zurückzugeben. Oracle gibt stattdessen einen Laufzeitfehler zurück. Indem Sie jedoch eine group by-Klausel verwenden, können Sie Gruppenfunktionen und Spalten in der Auswahlliste kombinieren. Die nächsten Abschnitte betrachten die Verwendung der group by-Klausel genauer. Listing 9.3: Fehlerhafte Syntax in einer select-Anweisung SQL> select Department_ID, sum(Additional_Fees) from Course; select Department_ID, sum(Additional_Fees) * ERROR at line 1: ORA-00937: not a single-group group function
9.3 Gruppieren von Zeilen Die group by-Klausel ist ein weiterer Abschnitt der select-Anweisung. Diese optionale Klausel teilt Oracle mit, dass ein Abfrageergebnis nach den unterschiedlichen Werten, die für die angegebenen Spalten existieren, gruppiert werden soll. Zusätzlich kann die Klausel having in Verbindung mit der group by-Klausel verwendet werden, um das Ergebnis weiter einzuschränken.
Obwohl dieses Thema am besten durch ein Beispiel erklärt wird, werfen wir zuerst einen kurzen Blick auf die Syntax. SELECT select_list FROM table_name [WHERE condition] [GROUP BY column1, ... [, columnN] ] [HAVING condition] [ORDER BY sort_column1, ... [, sort_columnK];
Die Bedeutungen der Platzhalter sind: ●
● ● ● ●
select_list ist eine Menge von Spalten und Ausdrücken aus den in table_list aufgelisteten Tabellen. table_name ist der Name einer Tabelle, aus der Zeilen abgefragt werden. condition ist eine gemäß der SQL-Syntax zulässige Bedingung. column1 bis columnN sind Spalten der in table_list enthaltenen Tabellen. sort_column1 bis sort_columnK sind Spalten, nach denen das Ergebnis sortiert wird.
Es ist einfach, diese Elemente - Spalten, Ausdrücke, Tabellen und Bedingungen - in einer SQL-Anweisung so zu mischen, dass Oracle diese zurückweist. Außerdem können Sie Anweisungen konstruieren, die Oracle ohne Fehler bearbeitet, deren Resultate aber schwierig zu interpretieren sind. Wenn diese Dinge passieren, ist es am besten, zu den Grundlagen zurückzugehen. Studieren Sie Ihre select-Anweisung. Wenn sie keinen Sinn mehr ergibt, verwenden Sie SQL*Plus oder ein ähnliches Werkzeug, um sie Element für Element auseinander zu nehmen, bis wieder ein Sinn sichtbar wird. Analysieren Sie Ihre Verwendung von Gruppenfunktionen, die group by- und havingKlauseln sowie alle Join-Bedingungen. Listing 9.4 zeigt, wie man die Anzahl der Kurse ermittelt, die mit jeder Department_ID assoziiert sind. Listing 9.4: Verwendung der Funktion count zusammen mit einer group by-Klausel SQL> select Department_ID, count(*) from Course group by Department_ID; DEPARTMENT_ID COUNT(*) ------------- ---------1003 3 1004 5 1005 4 Als nächstes betrachten wir die gleichzeitige Verwendung der Klauseln group by und having. Sie können die having-Klausel verwenden, um die Abteilungen zu bekommen, die genau vier Kurse haben, wie in Listing 9.5 gezeigt wird. Listing 9.5: Verwendung der group by- und having-Klauseln SQL> select Department_ID, count(*) from Course group by Department_ID having count(*) = 4; DEPARTMENT_ID COUNT(*) ------------- ----------
1005
4
Gruppenfunktionen können in der Auswahlliste ohne die group by-Klausel nicht mit Spalten kombiniert werden. Sie können beispielsweise den Durchschnittswert für zusätzliche Gebühren pro Abteilung erhalten, wie es in Listing 9.6 gezeigt wird. Listing 9.6: Kombination der Funktion avg mit der group by-Klausel SQL> select Department_ID, avg(Additional_Fees) from Course group by Department_ID; DEPARTMENT_ID AVG(ADDITIONAL_FEES) ------------- -------------------1000 0 1001 258.333333 1002 63.3333333 Im nächsten Beispiel werden Sie eine weitere Anwendung für Datenbanken kennen lernen: ein Informationssystem für die Patienten eines Krankenhauses. Eine weitere Verwendung der group byund having-Klauseln ist die Identifikation doppelt vorhandener Zeilen. Es kann beispielsweise vorkommen, dass Sie keinen Primärschlüssel zu einer Tabelle hinzufügen können, weil die Spalte für den Primärschlüssel doppelte Werte enthält, wie in Listing 9.7 gezeigt wird. Eine Abfrage, welche die Klauseln group by und having verwendet, stellt fest, welche Patient_IDs mehr als einmal auftreten. Eine zweite Abfrage bestimmt, welche Zeile gelöscht werden soll. Nachdem sie gelöscht wurde, wird der Primärschlüssel erfolgreich zur Tabelle hinzugefügt. Listing 9.7: Ermittlung doppelter Zeilen SQL> alter table Patient add constraint Patient_PK primary key (Patient_ID); alter table Patient add * ERROR at line 1: ORA-02299: cannot add or enable constraint (TYO.PATIENT_PK)-duplicate keys found SQL> select Patient_ID from Patient having count(*) > 1 group by Patient_ID; PATIEN -----GG9999 SQL> select * from Patient where Patient_ID = 'GG9999'; PATIEN BODY_TEMP_DEG_F FEVER_CLASS ------ --------------- -----------------GG9999 107.6 LETHAL FEVER
GG9999 107 SQL> delete from Patient where Patient_ID = 'GG9999' and Body_Temp_Deg_F = 107; 1 row deleted. SQL> alter table Patient add constraint Patient_PK primary key (Patient_ID); Table altered.
9.4 Nach eindeutigen Zeilen suchen Die select-Anweisung besitzt ein optionales Schlüsselwort, das wir bis jetzt noch nicht besprochen haben: distinct. Dieses Schlüsselwort weist Oracle an, nur Zeilen zurückzugeben, die eindeutige Werte für die angegebenen Spalten haben. Das Eliminieren der Duplikate bedingt einen Sortiervorgang, der die Abfrage langsamer macht. Als Beispiel verwendet Listing 9.8 eine Abfrage, um alle Städte zu erhalten, in denen Studenten leben. Listing 9.8: Abrufen einer Menge voneinander verschiedener Werte mit einer Abfrage SQL> select distinct City from Student; CITY ----------DOVER SPRINGFIELD Die Option distinct ist sehr nützlich, um die Wertemenge einer Spalte zu bestimmen. Sie bietet eine Methode, mit der man schnell feststellen kann, welche Cluster die Werte in einer Tabelle bilden. Wenn Sie die Option distinct nicht angeben, liefert Oracle alle Zeilen, die den in der where-Klausel angegebenen Kriterien entsprechen. Als Standard verwendet Oracle die Option all, um alle Spalten zurückzugeben. Die beiden folgenden select-Anweisungen sind deswegen äquivalent: select City from Student; und select all City from Student;
9.5 Unterabfrage und FROM-Klausel Eine Unterabfrage kann in der FROM-Klausel einer SELECT-Anweisung anstelle einer Tabelle oder Datensicht verwendet werden. Listing 9.9 gibt ein Beispiel wieder. Listing 9.9: Verwenden einer Unterabfrage in der from-Klausel SQLWKS> 2> 3> 4> 5>
select Operating_Room, Surgeon_ID, Last_Name from (select Patient_ID, Last_Name from Patient where Status != 'MORGUE') P, Surgery_Schedule S where P.Patient_ID = S.Patient_ID;
In Zeile 2 von Listing 9.9 ist eine Unterabfrage der Patient-Tabelle in runde Klammern eingeschlossen. Sie gibt Patient_ID und Last_Name nur für diejenigen Patienten zurück, deren Wert für Status nicht gleich 'Morgue' ist. In Zeile 3 wird die Unterabfrage mit der Surgery_Schedule-Tabelle verbunden, basierend auf dem Verknüpfungskriterium in Zeile 5.
9.6 Der Umgang mit hierarchischen Informationen Um Verwirrung zu vermeiden, habe ich das Thema der hierarchischen Informationen in diese und nicht in eine der früheren Lektionen über SQL aufgenommen. Ein Beispiel für hierarchische Daten ist eine Bill of Materials (BOM), die alle Teile, Teilmontagen und Montagen zusammenfasst, aus denen ein fertiges Produkt besteht; ein Flugzeughersteller kann eine BOM haben, die Tausende von Ebenen und Millionen von Teilen umfasst.
Hierarchische Daten liegen vor, wenn eine Tabelle eine reflexive Beziehung, d.h. eine Beziehung zu sich selbst hat. Der Fremdschlüssel einer reflexiven Beziehung referenziert die Tabelle selbst. Können relationale Datenbanken im Allgemeinen und SQL im Besonderen diese Arten von Hierarchien unterstützen? Die Antwort ist: Jein. Die gute Nachricht ist, dass SQL durch die connect by-Klausel Unterstützung für hierarchische Daten bietet. Die schlechte Nachricht ist, dass diese Unterstützung recht begrenzt ist und die Syntax nicht intuitiv. Ich zeige Ihnen trotzdem an einem Beispiel, wie man SQL verwendet, um durch hierarchische Daten zu navigieren. Das Design der Tabelle basiert auf dem Konzept, dass jedes Produkt zum Endprodukt oder einer Teilmontage gehört - außer dem Endprodukt selbst. Tabelle 9.1 erläutert die Spalten der Tabelle Product_Assembly. Die Zeilen in der Tabelle Product_Assembly zeigen dieses Konzept. Eine Kamera, die X1000, wird aus mehreren Teilen und einer Teilmontage, B200, zusammengesetzt. Listing 9.10 zeigt den Inhalt der Tabelle Product_Assembly. Listing 9.10: Inhalt der Product_Assembly-Tabelle SQL> select Assembly_ID, Product_ID, Description from Product_Assembly
order by Assembly_ID, Product_ID; ASSEMBLY_ID PRODUCT_ID DESCRIPTION ----------- ----------- -----------------------------------------B200 I101 Titanium alloy, teflon-coated iris B200 S42 Variable-speed shutter, standard B200 W123 Manual film advance, winder X1000 B200 Black body, stainless steel frame camera body X1000 F100 Blue filter - standard X1000 F55 Xenon flash unit X1000 L100 100mm lens - standard X1000 S04 4 foot camera strap, leather X1000 Complete camera 9 rows selected. Aufgrund der geringen Menge der hier gezeigten Daten können Sie sich die Organisation der Teile leicht klar machen. Der Sinn dieses Beispiels ist, Ihnen zu zeigen, wie man den Aufbau einer Hierarchie abfragt. Listing 9.11 zeigt ein Beispiel dafür, wie man hierarchische Daten mit der select- Anweisung abfragt. Die select-Anweisung enthält zwei neue Klauseln: start with und connect by. Die start with-Klausel identifiziert die Zeile der obersten Ebene, für die Oracle untergeordnete Zeilen abfragen soll - in diesem Beispiel wollen Sie die untergeordneten Zeilen für die X1000 abfragen. Die connect byKlausel teilt Oracle mit, wie die Zeilen präsentiert werden sollen, und der Operator prior weist Oracle an, die Zeilen so darzustellen, dass die Assembly_ID jeder untergeordneten Zeile gleich der Product_ID ihrer übergeordneten Zeile ist. Listing 9.11: Abrufen von hierarchischen Informationen aus einer Tabelle und Einrücken mit lpad und level SQL> select lpad(' ',level-1) || Assembly_ID Assembly_ID, Product_ID, Description from Product_Assembly start with Product_ID = 'X1000' connect by prior Product_ID = Assembly_ID; ASSEMBLY_ID PRODUCT_ID DESCRIPTION ----------- ---------- --------------------------------------------X1000 Complete camera X1000 L100 100mm lens - standard X1000 B200 Black body, stainless steel frame camera body B200 S42 Variable-speed shutter, standard B200 I101 Titanium alloy, teflon-coated iris B200 W123 Manual film advance, winder X1000 S04 4 foot camera strap, leather X1000 F100 Blue filter - standard X1000 F55 Xenon flash unit 9 rows selected.
Um jede Zeile auf ihre hierarchische Ebene einzurücken, bette ich LEVEL in die Funktion lpad ein, so dass die Anzahl der von lpad zurückgegebenen Leerezeichen der hierarchischen Ebene entspricht. (LEVEL ist eine Pseudospalte, welche die hierarchische Ebene für jede Zeile zurückgibt - von 1 für die oberste bis N für die detaillierteste Ebene.). lpad wird mit Assembly_ID verkettet.
Seien Sie bei der Konstruktion Ihres Datenmodells vorsichtig. Seien Sie sich wirklich sicher, dass Sie hierarchische Informationen unterstützen müssen, bevor Sie connect by und start with verwenden. Um eine bestimmte Teilmontage zu betrachten, geben Sie in der start with-Klausel die Assembly_ID an. Die select-Anweisung in Listing 9.12 zeigt diese Prozedur; sie zeigt auch den Wert von LEVEL für jede Zeile. Listing 9.12: Einrücken der Ausgabe einer Hierarchieabfrage mit Hilfe von LEVEL SQL> select lpad(' ',2*(level-1)) || Assembly_ID as Ass_ID, Product_ID as Prod_ID, LEVEL, Description from Product_Assembly start with Product_ID = 'B200' connect by prior Product_ID = Assembly_ID order siblings by assembly_id, product_id; ASS_ID PROD LEVEL DESCRIPTION ------ ---- ----- --------------------------------------------X1000 B200 1 Black body, stainless steel frame camera body B200 I101 2 Titanium alloy, teflon-coated iris B200 S42 2 Variable-speed shutter, standard B200 W123 2 Manual film advance, winder 4 rows selected. Sie sollten sich über eine Reihe von Dingen im Klaren sein, wenn Sie hierarchische selectAnweisungen verwenden. Als erstes kann die hierarchische select-Anweisung auch für einen Join von zwei oder mehr Tabellen verwendet werden. Zweitens, wenn Sie eine order by-Klausel angeben, zerstören Sie die hierarchische Ordnung der von der Abfrage zurückgegebenen Zeilen, es sei denn Sie verwenden order siblings by, wie in Listing 9.12 gezeigt.
Das oben verwendete Beispiel für hierarchische Daten finden Sie in der Datei sql\ hierarchical_select.sql auf der CD-ROM.
9.7 Verwenden des exists-Operators
Zusätzlich zu den anderen SQL-Operatoren sollten Sie auch mit dem exists-Operator vertraut sein. Dieser Operator arbeitet mit einer Unterabfrage und gibt einen Booleschen Wert zurück: ● ●
true, wenn die Unterabfrage mindestens eine Zeile zurückgibt false, wenn die Unterabfrage keine Zeilen zurückgibt
Beachten Sie das in Listing 9.13 gezeigte Beispiel, das auf den Tabellen Class und Instructor basiert. Stellen Sie sich vor, dass Sie die Namen der Lehrer feststellen wollen, die für einen Kurs eingeteilt sind. Dafür verwenden Sie eine Abfrage mit dem exists- Operator. Listing 9.13: Verwendung des exists-Operators SQL> select Last_Name, First_Name, Position 2 from Instructor I 3 where 4 exists 5 (select * from Class C 6 where 7 I.Instructor_ID = C.Instructor_ID); LAST_NAME FIRST_NAME ------------------------ -----------------------CHANG ROGER JASON JERROLD TORRES PETER RICHARDSON NANCY PARKER WILLIAM CHERNOW BESS CHU STEVEN WEISS ROBERTA RESTON RAPHAEL 9 rows selected.
POSITION ------------------ASSISTANT PROFESSOR ASSOCIATE PROFESSOR PROFESSOR ASSISTANT PROFESSOR PROFESSOR ASSOCIATE PROFESSOR PROFESSOR PROFESSOR ASSOCIATE PROFESSOR
Der exists-Operator ist in Situationen nützlich, in denen Sie sich nicht für die von der Unterabfrage zurückgegebenen Spaltenwerte interessieren. Beachten Sie, wie Aliasnamen einer Tabelle - C für Class und I für Instructor - verwendet wurden, um den Tippaufwand zu verringern und die Lesbarkeit der Abfrage zu verbessern. Sie können dem exists- Operator auch den logischen Operator not voranstellen. Als Beispiel zeigt Listing 9.14, wie Sie eine Liste von Lehrern erhalten, die nicht für eine Klasse eingeteilt sind. Listing 9.14: Ein weiteres Beispiel für die Verwendung des exists-Operators SQL> select Last_Name, First_Name, Position 2 from Instructor I 3 where 4 not exists 5 (select * from Class C 6 where 7 I.Instructor_ID = C.Instructor_ID); LAST_NAME FIRST_NAME
-----------------------BORIS LAURA SAMANTHA ARTHUR BENJAMIN ALLAN MARTINA JOSEPH RANIER
------------------PROFESSOR ASSOCIATE PROFESSOR PROFESSOR ASSOCIATE PROFESSOR ASSISTANT PROFESSOR ASSOCIATE PROFESSOR ASSOCIATE PROFESSOR ASSISTANT PROFESSOR PROFESSOR
Wie Sie in Zeile 5 sehen können, fragt eine Unterabfrage für jede Klasse die Spalten aus der Tabelle Class ab, die ein Lehrer mit einer bestimmten Instructor_ID unterrichtet. Der Operator not exists führt dazu, dass die Abfrage Last_Name, First_Name und Position genau dann nicht zurückgibt, wenn die Unterabfrage die entsprechenden Zeilen zurückgibt.
9.8 Die Join-Operation Nun, da Sie weitergehende Kenntnisse von SQL besitzen, sind Sie soweit, dass Sie in die Welt der Joins eintauchen können. Ein Join liefert Zeilen aus zwei oder mehr Tabellen, die eine gemeinsame Menge von Merkmalen teilen. Ein relationale Datenbank hätte ohne die Join-Operation wenig Wert.
Ein Join ist eine Abfrage, die Werte aus zwei oder mehr Tabellen abfragt, in denen eine oder mehrere Spalten der einen Tabelle identische Werte besitzen wie eine oder mehrere Spalten der anderen Tabelle. Die Join-Operation ist der Mechanismus, der es möglich macht, Tabellen miteinander zu verknüpfen. Eine Join-Operation fragt Spalten aus zwei oder mehr Tabellen ab. Um beispielsweise zwei Tabellen zu verknüpfen, gibt das Abfragekriterium typischerweise die Bedingung an, dass eine Spalte der ersten Tabelle - die als Fremdschlüssel definiert ist - gleich einer Spalte der zweiten Tabelle ist - die ein Primärschlüssel ist, auf den der Fremdschlüssel verweist. Die where-Klausel eines Joins kann zusätzliche Bedingungen enthalten. Dieser Typ von Join-Operation wird als EquiJoin bezeichnet. In der Beispieldatenbank können Sie z.B. die Tabellen Instructor und Department verknüpfen, so dass Sie zusammenhängende Informationen aus beiden Tabellen mit einer einzigen SQL-Anweisung erhalten.
Die allgemeine Syntax für die select-Anweisung ermöglicht es Ihnen, mehr als zwei Tabellen zu verknüpfen. SELECT select-list FROM table1, table2 ... [, tableN] WHERE table1.column1 = table2.column2 [AND table2.column3 = tableN.columnN] ... additional-conditions Die Bedeutungen der Platzhalter sind: ● ● ●
●
select_list ist die Menge der Spalten und Ausdrücke von table1 bis tableN. table1 bis tableN sind die Tabellen, aus denen die Spaltenwerte abgefragt werden. column1 bis columnN sind Fremd- bzw. Primärschlüssel der Tabellen table1 bis tableN, welche die Bedingungen für die Verknüpfung der Tabellen ausdrücken. additional-conditions sind die optionalen Abfragekriterien.
Beachten Sie bitte, dass Sie column1 bis columnN nicht in der select_list angeben müssen. Oracle9i unterstützt eine zweite einfachere Möglichkeit, Joins auszudrücken. Es handelt sich um die ANSI SQL:99 Join Syntax. Oracle nutzt in beiden Tabellen namensgleiche Spalten, um die Verknüpfung auszuführen, ohne dass Sie explizit die Namen der Spalten, die für die Verknüpfung verwendet werden sollen, angeben müssen.
SELECT select_list FROM table-name1 [ NATURAL [ {LEFT | RIGHT | FULL } OUTER ] JOIN table-name2 ]; Weitere Vorteile der ANSI Join Syntax, gegenüber der traditionellen Art Joins auszudrücken, sind die Unterstützung des FULL OUTER JOIN, auf den wir später zurückkommen werden, sowie die Möglichkeit die Join Bedingung von den übrigen Bedingungen getrennt zu formulieren, wodurch übersichtlichere SELECT Anweisungen entstehen. Außerdem entfällt die Notwendigkeit, mit Aliasnamen für Tabellen zu arbeiten, wenn die Spalten aus den verknüpften Tabellen denselben Namen besitzen.
Ein einfacher Join mit zwei Tabellen Die herkömmliche Join-Syntax kann durch einen einfachen Verbund von zwei Tabellen demonstriert werden. Die Tabelle Class kennzeichnet den Lehrer einer Klasse durch die Instructor_ID. Die Tabelle Instructor enthält ausführliche Informationen über jeden Lehrer. Durch einen Verbund der Tabellen Class und Instructor mittels der Spalte Instructor_ID können Sie zusätzliche Daten über den Lehrer abfragen. Listing 9.15: Ein einfacher Join mit zwei Tabellen
SQL> select Class_ID, Last_Name, Position from Class C, Instructor I where C.Instructor_ID = I.Instructor_ID order by Class_ID; CLASS_ID LAST_NAME POSITION ---------- ------------------------- ------------------------103400 JASON ASSOCIATE PROFESSOR 103600 CHANG ASSISTANT PROFESSOR 104200 RICHARDSON ASSISTANT PROFESSOR Sehen Sie sich die Elemente der select-Anweisung an. Die Auswahlliste besteht aus den Spalten Class_ID, Last_Name und Position. Die Tabellen in der from-Klausel sind Class und Instructor. Die where-Klausel weist das Oracle-RDBMS an, nur Zeilen zurückzugeben, in denen die Instructor_ID der Tabelle Class mit einer Zeile in der Tabelle Instructor übereinstimmt, die denselben Wert für Instructor_ID aufweist. Sie erreichen dasselbe Resultat wie in Listing Listing 9.15, wenn Sie die ANSI Join Syntax wie in Listing Listing 9.16 verwenden. Listing 9.16: Verknüpfung zweier Tabellen mit ANSI Join Syntax SQL> select Class_ID, Last_Name, Position from Class natural join Instructor order by Class_ID; CLASS_ID LAST_NAME POSITION ---------- ------------------------- ------------------------103400 JASON ASSOCIATE PROFESSOR 103600 CHANG ASSISTANT PROFESSOR 104200 RICHARDSON ASSISTANT PROFESSOR Es gibt keine Einschränkung für die Reihenfolge der Spalten in der select_list. Es ist auch nicht nötig, dass überhaupt eine oder mehrere Zeilen aus jeder der verknüpften Tabellen ausgewählt werden. Als Beispiel zeigt Listing 9.17 eine zulässige Verknüpfung, in der die Auswahlliste keine Spalten aus der Tabelle Class enthält. Listing 9.17: Bildung eines Joins über eine Auswahlliste, die Spalten einer einzigen Tabelle enthält SQL> select Last_Name, First_Name, Position 2 from Class C, Instructor I 3 where 4 C.Instructor_ID = I.Instructor_ID 5 order by Class_ID; LAST_NAME FIRST_NAME POSITION -------------- -------------- -----------------------------------JASON JERROLD ASSOCIATE PROFESSOR CHANG ROGER ASSISTANT PROFESSOR RICHARDSON NANCY ASSISTANT PROFESSOR
RESTON RAPHAEL CHERNOW BESS TORRES PETER JASON JERROLD WEISS ROBERTA CHU STEVEN PARKER WILLIAM 10 rows selected.
ASSOCIATE PROFESSOR ASSOCIATE PROFESSOR PROFESSOR ASSOCIATE PROFESSOR PROFESSOR PROFESSOR PROFESSOR
Mehrdeutige Spalten Jeder Verweis auf eine Spalte in einer Verknüpfung mit herkömmlicher Join-Syntax muss eindeutig sein. In diesem Zusammenhang bedeutet eindeutig, dass der Spaltenname durch den Tabellennamen oder einen Aliasnamen gekennzeichnet werden muss, wenn eine Spalte in mehr als einer Tabelle der Verknüpfung vorhanden ist. Oracle gibt eine Fehlermeldung zurück, wenn Sie mehrdeutig auf eine Spalte verweisen. Listing 9.18 zeigt eine Verknüpfung, die eine mehrdeutige Spalte enthält - Instructor_ID. Listing 9.18: Mehrdeutige Spalte in einem Join SQL> select Instructor_ID, Position 2 from Class C, Instructor I 3 where 4 C.Instructor_ID = I.Instructor_ID 5 order by Class_ID; select Instructor_ID, Position * ERROR at line 1: ORA-00918: column ambiguously defined Oracle gibt eine Fehlermeldung zurück, weil die Instructor_ID, die Teil der Auswahlliste ist, in den Tabellen Class und Instructor auftritt. Um dieses Problem zu korrigieren, müssen Sie die Spalte Instructor_ID mit dem Namen oder dem Alias der Tabelle kennzeichnen. Listing 9.19 zeigt, wie eine mehrdeutige Spalte in einer Auswahlliste gekennzeichnet werden kann. Listing 9.19: Kennzeichnung einer mehrdeutigen Spalte in der Auswahlliste SQL> select I.Instructor_ID, Position from Class C, Instructor I where C.Instructor_ID = I.Instructor_ID order by Class_ID; INSTRUCTOR_ID POSITION ------------- ------------------505 ASSOCIATE PROFESSOR 405 ASSISTANT PROFESSOR 491 ASSISTANT PROFESSOR
Vorsicht vor dem kartesischen Produkt
Ein Fehler, der gelegentlich bei der Verknüpfung mehrerer Tabellen auftritt, ist, dass vergessen wird, eine vollständige where-Klausel als Verknüpfungsbedingung anzugeben. Wenn die Verknüpfungsbedingung fehlt oder unvollständig ist, werden Sie zwei Dinge feststellen: die Abfrage braucht erheblich länger für ihre Ausführung, und die Anzahl der gelieferten Datensätze ist erheblich größer als Sie erwartet haben.
Der technische Ausdruck für eine Verknüpfung ohne Verknüpfungsbedingung ist kartesisches Produkt. Beachten Sie das in Listing 9.20 gezeigte Beispiel. Zwei Tabellen, Class und Instructor, werden in der from-Klausel ohne Verknüpfungsbedingung aufgelistet. Die Tabelle Class hat 10 Zeilen, die Tabelle Location hat 13 Zeilen. Die Abfrage hat keine Verknüpfungsbedingung, die Oracle anweisen würde, die Spalten jeder Tabelle aufgrund von Class_Building und Class_Room abzufragen. Als Ergebnis gibt Oracle alle möglichen Kombinationen der zwei Tabellen zurück, dies sind 10 mal 13 oder 130 Zeilen. Listing 9.20: Erzeugung eines kartesischen Produkts durch Weglassen einer Join-Bedingung SQL> select Class_ID, Seating_Capacity from Class, Class_Location order by Class_ID; CLASS_ID SEATING_CAPACITY --------- ---------------103400 200 103400 120 ... 110300 35 120200 80 130 rows selected.
Verknüpfung von mehr als zwei Tabellen Wie Sie sehen, schränkt die Syntax der select-Anweisung die Zahl der Tabellen nicht ein, die miteinander verknüpft werden können. Als Beispiel betrachten Sie Listing 9.21, in dem drei Tabellen verknüpft werden - Class, Course und Instructor - um detaillierte Informationen über jede Klasse abzufragen. Listing 9.21: Ein Join mit drei Tabellen SQL> select Class_ID, CL.Course_ID, I.Department_ID Dept, Title, Last_Name from Class CL, Course CO, Instructor I where CL.Course_ID = CO.Course_ID and CL.Instructor_ID = I.Instructor_ID order by Class_ID;
CLASS_ID COURSE_ID DEPT TITLE ---------- ---------- ----- ----------------------103400 10043 1004 WORKSHOP ON NORMALITY 103600 10051 1005 PRE-CALCULUS 104200 10003 1000 MODERN PHILOSOPHY 104500 10062 1006 MODERN EUROPEAN HISTORY 108300 10071 1007 INTRO TO ENGLISH LIT 108400 10061 1006 EARLY AMERICAN HISTORY 108600 10043 1004 WORKSHOP ON NORMALITY 109100 10021 1002 INTRO TO BIOLOGY 110300 10084 1008 SEMINAR ON CHAOS 120200 10013 1001 WORKSHOP ON MARX 10 rows selected.
Beachten Sie, dass für jede der drei Tabellen ein Aliasname angegeben wurde: ● ● ●
CL für Class CO für Course I für Instructor
Obwohl Aliasnamen für Tabellen optional sind, sollten Sie Aliasnamen bei der Verknüpfung mehrerer Tabellen verwenden, weil sie die Größe der select-Anweisung reduzieren und sie leichter lesbar machen. Studieren Sie jede der Verknüpfungsbedingungen in Listing 9.21. Die erste Verknüpfungsbedingung wird verwendet, um die Tabellen Class und Course über Course_ID zu verbinden. Die zweite Verknüpfungsbedingung wird verwendet, um die Tabellen Class und Instructor über Instructor_ID zu verknüpfen. Listing 9.22 zeigt, wie Sie denselben Join wiederum einfacher in ANSI SQL Join Syntax ausdrücken können. Listing 9.22: Verknüpfung dreier Tabellen mittels ANSI SQL Join Syntax SQL> select Class_ID, Course_ID, Department_ID Dept, Title, Last_Name from Class natural join Course natural join Instructor order by Class_ID;
Self-Joins Eine andere Form der Verknüpfung ist der Self-Join. Diese Art der Verknüpfung wird verwendet, wenn eine Tabelle eine reflexive Beziehung hat, d.h. der Fremdschlüssel bezieht sich auf den eigenen Primärschlüssel. Ein gutes Beispiel für diesen Typ ist die Tabelle Product_Assembly, die zuvor in dieser Lektion besprochen wurde. Der Primärschlüssel der Tabelle Product_Assembly ist Product_ID. Jedes Produkt - außer dem Endprodukt - gehört zu einer Montage. Deshalb ist Assembly_ID ein Fremdschlüssel, der auf Product_ID verweist (siehe Abbildung 9.1).
Abbildung 9.1: Beispiel für eine reflexive Beziehung. Der Fremdschlüssel in der Tabelle Assembly_ID verweist auf den Primärschlüssel derselben Tabelle Als Beispiel für einen Self-Join stellen Sie sich vor, dass Sie die Beschreibung für jedes Produkt und die Beschreibung der Montage, zu der es gehört, abfragen wollen. Wie Listing 9.23 zeigt, muss die Join-Anweisung ein Alias für beide Kopien der Tabelle definieren. Die Verknüpfungsbedingung P1.Assembly_ID = P2.Assembly_ID - veranlasst Oracle, Produktinformation aus P1 und Montageinformation aus P2 abzufragen. Listing 9.23: Beispiel für einen Self-Join SQL> col "Assembly Breakdown" format a60 word_wrapped SQL> select P1.Description || ' is part of ' || P2.Description "Assembly Breakdown" from Product_Assembly P1, Product_Assembly P2 where P1.Assembly_ID = P2.Product_ID order by P1.Assembly_ID, P1.Product_ID; Assembly Breakdown -----------------------------------------------------------Titanium alloy, teflon-coated iris is part of Black body, stainless steel frame camera body Variable-speed shutter, standard is part of Black body, stainless steel frame camera body Manual film advance, winder is part of Black body, stainless steel frame camera body Black body, stainless steel frame camera body is part of Complete camera Blue filter - standard is part of Complete camera Xenon flash unit is part of Complete camera 100mm lens - standard is part of Complete camera 4 foot camera strap, leather is part of Complete camera
Outer-Join In diesem Abschnitt werden Sie die Verwendung eines weiteren Typs von Join sehen - des OuterJoin. Um den Unterschied zwischen einem gewöhnlichen Join (also einem Equi- Join) und einem Outer-Join zu verstehen, lassen Sie uns einen weiteren Blick auf gewöhnliche Joins werfen. Ein Join über mehrere Tabellen gibt Zeilen aus allen Tabellen zurück, in denen die Verknüpfungsbedingungen zutreffen. Beachten Sie das in Listing 9.24 gezeigte Beispiel. Die Lehrer am Flugle-College können während eines Semesters für eine bestimmte Klasse eingeteilt sein oder nicht. Zusätzlich zu ihrer Lehrtätigkeit wird von den Mitarbeitern auch erwartet, dass sie sich an der
Forschung auf ihrem Gebiet beteiligen. Listing 9.24 enthält einen Join der Tabellen Class und Instructor. Beachten Sie, dass die Abfrage nur Lehrer liefert, die eine Klasse unterrichten. Listing 9.24: Equi-Join der Instructor- und Class-Tabellen SQL> select I.Department_ID, Last_Name, First_Name, Class_ID from Instructor I, Class C where I.Instructor_ID = C.Instructor_ID order by I.Department_ID, Last_Name; DEPARTME LAST_NAME FIRST_NAME CLASS_ID -------- -------------- -------------- --------------------------BIO WEISS ROBERTA 109100 ECON PARKER WILLIAM 120200 ENG CHU STEVEN 110300 ENGL CHERNOW BESS 108300 HIST RESTON RAPHAEL 104500 HIST TORRES PETER 108400 MATH CHANG ROGER 103600 PHILO RICHARDSON NANCY 104200 PSYCH JASON JERROLD 108600 PSYCH JASON JERROLD 103400 10 rows selected. Wenn Sie Informationen über Lehrer abfragen wollen, beispielsweise ob ein Lehrer eine Klasse unterrichtet oder nicht, müssen Sie den Outer-Join-Operator verwenden, indem Sie ein (+) (ein Pluszeichen in Klammern) an die optionale Seite der Verknüpfungsbedingung anhängen. Listing 9.25 zeigt die Verwendung des Outer-Join-Operators. Listing 9.25: Ein Outer-Join SQL> select I.Department_ID, Last_Name, First_Name, Class_ID from Instructor I, Class C where I.Instructor_ID = C.Instructor_ID (+) order by I.Department_ID, Last_Name; DEPARTMENT_ID LAST_NAME FIRST_NAME CLASS_ID ------------- ---------- ---------- ---------1000 ANGELO ARTHUR 1000 BILLINGS BENJAMIN 1000 RICHARDSON NANCY 104200 Mit ANSI SQL Join Syntax lässt sich das Ergebnis aus Listing 9.25 wie folgt erzielen: Listing 9.26: OUTER JOIN in ANSI SQL Syntax select Department_ID, Last_Name, First_Name from Instructor natural left outer join Class order by Department_ID, Last_Name;
In Listing 9.26 wird LEFT OUTER JOIN verwendet, weil die Tabelle, deren Zeilen auch dann ausgewählt werden sollen, falls die Verknüpfungsbedingung nicht zutrifft, in der from- Klausel links steht. Würde in der from-Klausel zuerst Class und dann Instructor stehen, müssten Sie RIGHT OUTER JOIN verwenden. Mit der traditionellen Join Syntax von Oracle ist es nicht möglich, eine vollständige äußere Verknüpfung (Full Outer Join) in einer select-Anweisung auszudrücken. Die ANSI SQL Syntax hat diese Funktionalität. Nehmen wir an, es gäbe eine Klasse, aber es stünde noch nicht fest, welcher Dozent diese Klasse unterrichten wird. Mit einer insert Anweisung erzeugen wir eine solche Klasse. Ein LEFT OUTER JOIN zwischen Instructor und Class liefert alle Dozenten, auch diejenigen, die keinen Kurs halten. Ein RIGHT OUTER JOIN liefert alle Klassen, auch Klassen, denen noch kein Dozent zugeordnet ist. Das Ergebnis eines FULL OUTER JOIN ist die Vereinigungsmenge eines LEFT OUTER JOIN und eines RIGHT OUTER JOIN. Listing 9.27: Full Outer Join SQL> insert into class (class_id, instructor_id) values (103700,NULL); SQL> select Department_ID, Last_Name, First_Name, Class_ID from Instructor natural full outer join Class order by Department_ID, Last_Name; DEPARTMENT_ID LAST_NAME FIRST_NAME CLASS_ID ------------- ---------- ---------- ---------1000 ANGELO ARTHUR 1000 BILLINGS BENJAMIN 1000 RICHARDSON NANCY 104200 103700 Während bei Verwendung des traditionellen Outer Join Operators (+) ein UNION (siehe nächster Abschnitt) eines Left Outer Join und eines Right Outer Join mit einer erweiterten where-Bedingung erforderlich ist, um einen Full Outer Join zu realisieren, reicht bei Verwendung der ANSI SQL Syntax ein einziges SELECT. Wenn ein Lehrer keine Klasse unterrichtet, gibt Oracle eine NULL für Class_ID - oder jede andere Spalte, die Sie aus der Tabelle Class auswählen - zurück. Listing 9.28 zeigt, wie Sie mit herkömmlicher Join Syntax diejenigen Dozenten auswählen, die keine Klasse unterrichten. Listing 9.28: Ein weiterer Outer-Join SQL> select I.Department_ID, Last_Name, First_Name from Instructor I, Class C where I.Instructor_ID = C.Instructor_ID (+) and Class_ID is null order by I.Department_ID, Last_Name; DEPARTMENT_ID LAST_NAME FIRST_NAME CLASS_ID ------------- ---------- ---------- ---------1000 ANGELO ARTHUR 1000 BILLINGS BENJAMIN
9.9 Verwendung von Mengenoperatoren in einer select-Anweisung Die Sprache SQL ist eine teilweise Implementierung des relationalen Modells, wie es von Codd, dem Vater der relationalen Theorie, entworfen wurde. Intersect, union und minus sind Mengenoperatoren, mit denen Sie sich in den folgenden Abschnitten beschäftigen werden.
Der intersect-Operator Der intersect-Operator gibt die Zeilen zurück, die zwei Mengen von Zeilen gemeinsam haben.
Die Syntax für die Verwendung des intersect-Operators ist: select_stmt1 INTERSECT select_stmt2 [order_by_clause]; Die Bedeutungen der Platzhalter sind: ● ●
select_stmt1 und select_stmt2 sind zulässige select-Anweisungen. order_by_clause ist eine order by-Klausel, die durch Zahlen und nicht durch den Namen auf eine Spalte verweist.
Bei der Benutzung des intersect Operators sind einige Punkte zu berücksichtigen: ●
●
●
● ●
Die zwei select-Anweisungen dürfen keine order by-Klausel enthalten, Sie können jedoch das Ergebnis der gesamten intersect-Operation sortieren. Die Anzahl der Spalten im select-stmt1 muss gleich der Anzahl der Spalten im select- stmt2 sein. Die Datentypen der von select-stmt1 abgefragten Spalten müssen gleich den Datentypen der von select-stmt2 abgefragten Spalten sein. Die Verwendung von FOR UPDATE zusammen mit Mengenoperatoren ist nicht möglich. Die optionale order_by_clause unterscheidet sich von der üblichen order by-Klausel einer select-Anweisung, weil die zum Ordnen verwendeten Spalten über Nummern statt Namen angesprochen werden müssen. Der Grund dafür, dass die Spalten in der order_by_clause über Zahlen statt über Namen angesprochen werden müssen, ist, dass es in SQL nicht notwendig ist, dass die in select-stmt1 abgefragten Spaltennamen mit den in select-stmt2 abgefragten Spaltennamen identisch sind. Deshalb müssen Sie die Spalten, die zum Ordnen verwendet werden sollen, über ihre Position in der Auswahlliste angeben.
Betrachten Sie das folgende typische Szenario für die Verwendung des intersect- Operators. Als Dekan des Flugle-College haben Sie den Verdacht, dass einige der Lehrer am Flugle-College auch am Hoover-College unterrichten. Eine Liste der Lehrer am Hoover-College, dem Erzrivalen des Flugle-College, ist in Ihren Besitz gelangt. Sie wird in Listing 9.29 gezeigt. Die erste Abfrage liefert
die Lehrer am Flugle-College. Die zweite Abfrage liefert die Lehrer am Hoover-College. Die dritte Abfrage verwendet den intersect- Operator, um die Lehrer zu finden, die beiden Tabellen gemeinsam sind. Listing 9.29: Inhalt der Instructor-Tabelle für das Hoover-College und das Flugle-College SQL> select Last_Name, First_Name, MI, Position from Instructor; LAST_NAME FIRST_NAME M POSITION -------------- ---------- - --------------------------------------HITCHCOCK BORIS PROFESSOR DANIELS LAURA ASSOCIATE PROFESSOR EDWARDS SAMANTHA PROFESSOR CHANG ROGER ASSISTANT PROFESSOR JASON JERROLD ASSOCIATE PROFESSOR TORRES PETER PROFESSOR ANGELO ARTHUR ASSOCIATE PROFESSOR RICHARDSON NANCY ASSISTANT PROFESSOR PARKER WILLIAM PROFESSOR CHERNOW BESS ASSOCIATE PROFESSOR CHU STEVEN PROFESSOR WEISS ROBERTA PROFESSOR RESTON RAPHAEL ASSOCIATE PROFESSOR BILLINGS BENJAMIN ASSISTANT PROFESSOR YOUNG ALLAN ASSOCIATE PROFESSOR NILAND MARTINA ASSOCIATE PROFESSOR BATES JOSEPH ASSISTANT PROFESSOR POULSON RANIER PROFESSOR 18 rows selected. SQL> select Last_Name, First_Name, MI, Position from Hoover_Instructor; LAST_NAME FIRST_NAME M POSITION ------------------------ --------------- - ----------------------CHANG ROGER ASSISTANT PROFESSOR JASON JERROLD ASSOCIATE PROFESSOR TORRES PETER PROFESSOR CHERNOW BESS ASSOCIATE PROFESSOR CHU STEVEN PROFESSOR RESTON RAPHAEL ASSOCIATE PROFESSOR YOUNG ALLAN ASSOCIATE PROFESSOR NILAND MARTINA ASSOCIATE PROFESSOR BATES JOSEPH ASSISTANT PROFESSOR HUSTON MYRON T PROFESSOR RABINOWITZ KERMIT A LECTURER CHRISTIANSON PAUL V PROFESSOR 12 rows selected. SQL> select Last_Name, First_Name, MI, Position 2 from Instructor 3 intersect
4 select Last_Name, First_Name, MI, Position 5 from Hoover_Instructor; LAST_NAME FIRST_NAME M ------------------------ ---------------- BATES JOSEPH CHANG ROGER CHERNOW BESS CHU STEVEN JASON JERROLD NILAND MARTINA RESTON RAPHAEL TORRES PETER YOUNG ALLAN 9 rows selected.
POSITION ---------------------ASSISTANT PROFESSOR ASSISTANT PROFESSOR ASSOCIATE PROFESSOR PROFESSOR ASSOCIATE PROFESSOR ASSOCIATE PROFESSOR ASSOCIATE PROFESSOR PROFESSOR ASSOCIATE PROFESSOR
Wenn Sie die Zeilen jeder Tabelle untersuchen, stellen Sie fest, dass die beiden Tabellen in der Tat neun Zeilen gemeinsam haben.
Der union-Operator Früher oder später werden Sie sich in einer Situation befinden, in der Sie die Zeilen ähnlicher Tabellen kombinieren müssen, um einen Bericht oder eine Tabelle für die Analyse zu erzeugen. Obwohl die Tabellen ähnliche Informationen repräsentieren, können sie sich beträchtlich unterscheiden. Um die Aufgabe auszuführen, sollten Sie den union-Operator verwenden.
Die Syntax für diese Operation ist: select_stmt1 UNION [ ALL ] select_stmt2 [order_by_clause]; Die Bedeutungen der Platzhalter sind: ● ●
select_stmt1 und select_stmt2 sind zulässige select-Anweisungen. order_by_clause ist eine optionale order by-Klausel, die durch Zahlen und nicht durch den Namen auf eine Spalte verweist.
Der union-Operator kombiniert die von der ersten select-Anweisung gelieferten Zeilen mit den von der zweiten select-Anweisung gelieferten Zeilen. Sie können auch mehr als zwei selectAnweisungen mit UNION verbinden. Fehlt das Schlüsselwort ALL, werden nur unterschiedliche Ergebniszeilen geliefert, während mit ALL möglicherweise auch Duplikate im Ergebnis sind. Für den union-Operator gelten die selben Anforderungen wie für INTERSECT. Um die Verwendung von union zu illustrieren, stellen Sie sich vor, dass Sie die Aufgabe hätten,
Informationen aus zwei seismologischen Labors zu vereinen. Die erste Tabelle stammt aus der Abteilung für Geophysik der Universität von Northern South Dakota, die andere Tabelle stammt aus einem privaten Forschungsinstitut, RIND. Listing 9.30 beschreibt die Struktur der beiden Tabellen. Listing 9.30: Beschreibung zweier Tabellen für seismische Ereignisse SQL> desc UNSD_Event Name -----------------------------EVENT_NO EPICENTER_LATITUDE EPICENTER_LONGITUDE EVENT_MAGNITUDE EVENT_TIME EVENT_WAVE INSTRUMENT_NAME SQL> desc RIND_Event Name -----------------------------LOCATION_LAT LOCATION_LON RICHTER_NUMBER DATE_TIME WAVE_TYPE
Null? -------
Type --------------------------NUMBER NUMBER NUMBER NUMBER DATE CHAR(1) VARCHAR2(30)
Null? -------
Type --------------------------NUMBER NUMBER NUMBER VARCHAR2(30) CHAR(1)
Halten Sie für einen Moment inne, um die Ähnlichkeiten und Unterschiede zwischen UNSD_Event und RIND_Event zu untersuchen. Zuerst einmal speichern beide Tabellen Informationen über seismische Ereignisse. Die Tabelle UNSD_Event hat jedoch zwei zusätzliche Spalten: Event_No und Instrument_Name. Beide Tabellen speichern Breiten- und Längengrad des Epizentrums, die Stärke und den Wellentyp (P oder S). UNSD_Event definiert Event_Time jedoch als date, während RIND_Event varchar2 verwendet, um Datum und Uhrzeit in Date_Time zu speichern. Listing 9.31 verdeutlicht, was geschieht, wenn Sie ein union ausführen, ohne Event_Time in UNSD_Event und Date_Time und RIND_Event in einen gemeinsamen Datentyp zu konvertieren. Listing 9.31: Anwendung des union-Operators auf zwei Tabellen mit unterschiedlichen Datentypen SQL> select Epicenter_Latitude, Epicenter_Longitude, Event_Magnitude, Event_Time, Event_Wave 2 from UNSD_Event 3 UNION 4 select Location_Lat, Location_Lon, Richter_Number, Date_Time, Wave_Type 5 from RIND_Event; select Epicenter_Latitude, Epicenter_Longitude, Event_Magnitude, Event_Time, Event_Wave * ERROR at line 1: ORA-01790: expression must have same datatype as corresponding expression Sie können erzwingen, dass beide Spalten denselben Datentyp haben, indem Sie Date_Time in der
Tabelle RIND_Event mit der Funktion to_date in einen Datumswert umwandeln, wie dies in Listing 9.32 gezeigt wird. Listing 9.32: Erzwingen desselben Datentyps für beide Tabellen mit Hilfe einer Funktion SQL> select Epicenter_Latitude, Epicenter_Longitude, Event_Magnitude, Event_Time, Event_Wave 2 from UNSD_Event 3 UNION 4 select Location_Lat, Location_Lon, Richter_Number, TO_DATE(Date_Time,'MM-DD-YY HH:MI:SS'), Wave_Type 5 from RIND_Event; EPICENTER_LATITUDE EPICENTER_LONGITUDE EVENT_MAGNITUDE EVENT_TIM E ------------------ ------------------- --------------- --------- 12.83 189.85 5.8 25-APR-95 P 22.33 233.31 5.9 03-FEB-95 P 23.33 179.11 5.3 10-JAN-95 P 29.84 238.41 6.2 22-MAR-95 S 31.17 208.33 6.6 19-APR-95 S 31.84 241.21 6.1 12-MAR-95 S 37.81 211.84 6.4 11-JAN-95 S 7 rows selected. Wenn Sie das Ergebnis nach Event_Time ordnen wollen, können Sie die order by-Klausel verwenden. Anstatt jedoch über den Namen auf die Spalte zu verweisen, müssen Sie diese über die Reihenfolge in der Auswahlliste ansprechen. Listing 9.33 zeigt, wie die von der union abgefragten Zeilen nach Event_Time geordnet werden, dem vierten Eintrag in der Auswahlliste. Listing 9.33: Sortieren der durch union gebildeten Zeilen SQL> select Epicenter_Latitude, Epicenter_Longitude, Event_Magnitude, Event_Time, Event_Wave 2 from UNSD_Event 3 UNION 4 select Location_Lat, Location_Lon, Richter_Number, TO_DATE(Date_Time,'MM-DD-YY HH:MI:SS'), Wave_Type 5 from RIND_Event 6 order by 4; EPICENTER_LATITUDE EPICENTER_LONGITUDE EVENT_MAGNITUDE EVENT_TIM E ------------------ ------------------- --------------- --------- --23.33 179.11 5.3 10-JAN-95 P 37.81 211.84 6.4 11-JAN-95 S 22.33 233.31 5.9 03-FEB-95 P 31.84 241.21 6.1 12-MAR-95 S 29.84 238.41 6.2 22-MAR-95 S 31.17 208.33 6.6 19-APR-95 S 12.83 189.85 5.8 25-APR-95 P 7 rows selected.
Der minus-Operator Zusätzlich zu den Operatoren intersect und union bietet Oracle für den Vergleich einer Menge von Zeilen mit einer anderen Menge auch den minus-Operator.
Die Syntax für die Verwendung des minus-Operators ähnelt der Syntax des unionOperators: select_stmt1 MINUS select_stmt2 [order_by_clause] Die Bedeutungen der Platzhalter sind: ● ●
select_stmt1 und select_stmt2 sind zulässige select-Anweisungen. order_by_clause ist eine optionale order by-Klausel, die durch Zahlen und nicht durch den Namen auf eine Spalte verweist.
Die Anforderungen und Besonderheiten bei der Verwendung des minus-Operators sind dieselben wie bei den Operatoren intersect und union. Um die Verwendung des minus- Operators zu verdeutlichen, betrachten Sie erneut die Tabellen Instructor und Hoover_Instructor. Sie wollen feststellen, welche Lehrer am Flugle-College nicht auch am Hoover-College unterrichten. Listing 9.34 zeigt ein Beispiel dafür, wie dies mit dem minus- Operator erreicht werden kann. Die Abfrage liefert neun Zeilen, diese enthalten die Lehrer am Flugle-College, die nicht am Hoover-College lehren. Listing 9.34: Verwendung des minus-Operators SQL> select Last_Name, First_Name, MI, Position 2 from Instructor 3 minus 4 select Last_Name, First_Name, MI, Position 5 from Hoover_Instructor; LAST_NAME FIRST_NAME M POSITION -------------- ---------- - --------------------------------------ANGELO ARTHUR ASSOCIATE PROFESSOR BILLINGS BENJAMIN ASSISTANT PROFESSOR DANIELS LAURA ASSOCIATE PROFESSOR EDWARDS SAMANTHA PROFESSOR HITCHCOCK BORIS PROFESSOR PARKER WILLIAM PROFESSOR POULSON RANIER PROFESSOR RICHARDSON NANCY ASSISTANT PROFESSOR WEISS ROBERTA PROFESSOR
9 rows selected. Die Abfrage in Listing 9.34 sagt jedoch nichts über die Lehrer am Hoover-College aus, die nicht am Flugle-College unterrichten. Um diese Information zu sehen, müssen die zwei select-Anweisungen vertauscht werden, wie es in Listing 9.35 gezeigt wird. Listing 9.35: Vertauschen der über den minus-Operator miteinander verknüpften selectAnweisungen SQL> select Last_Name, First_Name, MI, Position from Hoover_Instructor minus select Last_Name, First_Name, MI, Position from Instructor; LAST_NAME FIRST_NAME M POSITION -------------- ---------- - --------------------------------------CHRISTIANSON PAUL V PROFESSOR HUSTON MYRON T PROFESSOR RABINOWITZ KERMIT A LECTURER Dieses Beispiel zeigt, dass der minus-Operator nicht alle Unterschiede zwischen den beiden Tabellen auflistet - er gibt nur die Zeilen der ersten Tabelle zurück, die in der zweiten Tabelle nicht gefunden werden. Sie können in den vom minus-Operator verwendeten select-Anweisungen auch eine where-Klausel angeben.
9.10 Datensichten verwenden Eine Datensicht ist eine Abfrage einer oder mehrerer Tabellen, die einen weiteren Weg zur Darstellung von Informationen bietet. Eine Datensicht enthält oder speichert nicht wirklich eigene Daten. In der Tat können Sie sich eine Datensicht als virtuelle Tabelle vorstellen. Der einzige Speicherplatz, den eine Datensicht wirklich benötigt, ist der für die select-Anweisung, durch die sie definiert ist.
Eine Datensicht ist eine gespeicherte Abfrage, die auf einer Abfrage einer oder mehrerer Tabellen basiert. Sie können eine Datensicht für die folgenden Aufgaben einsetzen: ● ● ●
Implementierung von Zugriffsrechten Komplexität vor einem Entwickler oder Benutzer verstecken Spalten umbenennen
Die Syntax einer Datensicht
Um eine Datensicht zu definieren, verwenden Sie die folgende Syntax:: CREATE [OR REPLACE] VIEW view_name [(column_name1, ... ,column_nameN)] AS select_statement [WITH CHECK OPTION [CONSTRAINT constraint_name] ]; Die Bedeutungen der Platzhalter sind: ●
●
● ●
view_name ist der Name der Datensicht (er unterliegt denselben Vorschriften wie andere Namen für Objekte in Oracle). Die Schlüsselwörter OR REPLACE bewirken, dass ein View, der bereits existiert, ersetzt wird. column_name1 bis column_nameN sind optionale Spaltennamen der Datensicht, die den in select_statement referenzierten Spalten entsprechen. Spaltennamen müssen angegeben werden, wenn Sie im select_statement Funktionen verwenden. select_statement ist eine zulässige select-Anweisung. constraint_name ist der Name der check option-Integritätsbedingung. Ein View mit check option lässt nur insert und update-Anweisungen zu, deren Resultate durch den View abgefragt werden können. Der Benutzer einer Datensicht mit check option, kann folglich keine Daten erzeugen, die er anschließend nicht lesen kann.
Die folgende einfache Datensicht basiert auf einer einzelnen Tabelle. Nehmen Sie an, dass mehrere Verwalter am Flugle-College um eine sehr einfache Methode gebeten haben, um die aktuellen Kosten für jeden Kurs abzufragen. Die aktuellen Kosten für den Kurs sind die Anzahl der Einheiten multipliziert mit 250$ pro Einheit zuzüglich eventueller zusätzlicher Gebühren. Obwohl Sie den Verwaltern gezeigt haben, wie man eine einfache Abfrage erstellt, wollen diese, dass Sie den Prozess verbessern. Listing 9.36 zeigt die Datensicht, die Sie zu diesem Zweck entwerfen. Hier ist eine Beschreibung jeder Zeile der SQL-Anweisung, aus der die Datensicht besteht. Die erste Zeile gibt den Namen der Datensicht an: Course_Cost. Die zweite Zeile listet die fünf Spalten auf, aus denen sich die Datensicht zusammensetzt: Course_ID, Department_ID, Title, Description und Cost. Die dritte Zeile gibt an, dass die Datensicht aus einer Abfrage erstellt werden soll. Die vierte Zeile gibt die ersten vier Zeilen an, die von der Datensicht geliefert werden sollen. Course_ID, Department_ID, Title und Description. Die fünfte Zeile gibt an, dass die fünfte Spalte der Datensicht - Cost - berechnet wird, indem die Einheiten mit $250 pro Einheit multipliziert werden und die zusätzlichen Gebühren addiert werden. Listing 9.36: Erzeugung einer einfachen Datensicht SQL> create view Course_Cost (Course_ID, Department_ID, Title, Description, Cost) as select Course_ID, Department_ID, Title, Description, Units*250 + Additional_Fees
from Course; View created. SQL> select Department_ID, Course_ID, Title, Cost from Course_Cost order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID TITLE COST ------------- ---------- ------------------------------ ---------1000 10001 INTRO TO PHILOSOPHY 750 1000 10002 ANCIENT GREEK PHILOSOPHERS 750 1001 10011 INTRO TO ECONOMICS 775
Den Zugriff auf Daten mit einer Datensicht einschränken Oracle bietet mehrere Mechanismen für die Einschränkung des Zugriffs auf Daten: Datensichten, Datenbanktrigger, Zugriffsberechtigungen für Tabellen und Spalten, Rollen, Virtual Private Database und Label Security. Mehr zu den beiden letztgenannten Punkten erfahren Sie am Tag 10. Privilegien für Datenbankobjekte können an Datenbankrollen und einzelne Benutzer vergeben werden. Um festzulegen, welche Methode am passendsten ist, müssen Sie die Sicherheitsanforderungen Ihrer Anwendung feststellen. Wenn Sie eine Datensicht verwenden, um den Zugriff auf Daten einzuschränken, stehen Ihnen mehrere Methoden zur Verfügung. Jede Methode bietet eine eigene Granularität der Kontrolle über den Zugriff auf Daten, von grob bis sehr fein. Eine grobe Kontrolle ermöglicht es Ihnen, eine Datensicht zu definieren, die eine Teilmenge ihrer Basistabelle ist. Zum Beispiel enthält die Tabelle Student Adressinformationen, auf die nicht alle Benutzer zugreifen sollen, die Zugriff auf die Tabelle haben. Listing 9.37 zeigt, wie Sie eine Datensicht der Tabelle Student erzeugen, die diese persönlichen Informationen nicht enthält. Listing 9.37: Erzeugung einer auf einer Teilmenge von Tabellenspalten basierenden Datensicht SQL> create view Student_No_Personal 2 as 3 select Student_ID, Last_Name, First_Name, MI, Year, Email 4 from Student; View created. SQL> describe Student_No_Personal Name Null? Type ------------------------------ -------- --------------------------STUDENT_ID NOT NULL VARCHAR2(20) LAST_NAME NOT NULL VARCHAR2(25) FIRST_NAME NOT NULL VARCHAR2(25) MI VARCHAR2(1) YEAR VARCHAR2(25) EMAIL VARCHAR2(100) Wie Sie in Listing 9.37 sehen, gibt der describe-Befehl von SQL*Plus nicht an, ob die Objektbeschreibung zu einer Tabelle oder einer Datensicht gehört. Auch wenn der Benutzer weiß,
dass die Adresse eines Studenten in der Tabelle Student gespeichert wird, kann die Datensicht die Spalte nicht liefern, da diese nicht in der Definition der Datensicht enthalten ist.
Komplexität mit einer Datensicht verbergen Während der Anwendungsentwicklung haben Sie es oft mit Entwicklern oder Benutzern zu tun, die verschiedene organisatorische Perspektiven und einen unterschiedlichen Grad an technischer Erfahrung besitzen. Darum sollten Sie die Mechanismen verwenden, die Oracle zur Anpassung der Umgebung an Entwickler und Benutzer bietet. Ich schließe Entwickler in diese Kategorie ein, weil ihre Kenntnisse von SQL im Allgemeinen und Oracle im Besonderen variieren. Beispielsweise verlangen viele Formulare und Berichte das Verknüpfen mehrerer Tabellen. Das Verwenden von Datensichten vereinfacht diesen Prozess, weil Sie als Architekt der Anwendung die Datensichten festlegen können, mit denen sich die Entwickler befassen müssen. Datensichten sind auch eine exzellente Möglichkeit, eine Datenbankumgebung für Endanwender anzupassen. Dies gilt besonders für große Organisationen, in denen für verschiedene Aufgaben auf dieselben Informationen zugegriffen wird. Typischerweise hat jede Gruppe einen eigenen Namen für dieselben Informationen. Aufgrund der weiten Verbreitung der Werkzeuge von Drittanbietern wie Business Objects, IQ Objects und Oracle Discoverer, sollte der Name einer Spalte exakt beschreiben, welche Information diese enthält. Die DDL Anweisung COMMENT ermöglicht Ihnen außerdem, zu Tabellen und Spalten einen Kommentartext von bis zu 4000 Zeichen im Oracle Datenbankkatalog zu hinterlegen. Die Kommentare sind über die Katalogsichten {DBA|USER|ALL}_TAB_COMMENTS und {DBA|USER|ALL}_COL_COMMENTS zugänglich. So bekommt ein Entwickler, Administrator oder Anwender eine Hilfestellung bei der Frage, welche Spalten er abfragen soll. Indem Sie eine Datensicht erzeugen, können Sie die Spaltennamen anpassen, die eine Gruppe von Benutzern zu sehen bekommt. Eine Datensicht kann die Komplexität einer Verknüpfung mehrerer Tabellen verbergen. Durch Definieren einer Datensicht werden die Benutzer davon befreit, die Eigenarten der selectAnweisung lernen zu müssen. Wenn Sie beispielsweise eine zusammenfassende Datensicht der Klassenpläne am Flugle-College zusammenstellen wollen, können Sie Informationen aus den Tabellen Class, Course und Instructor verknüpfen. Listing 9.38 enthält die create view-Anweisung, die für eine Datensicht auf der Basis des Joins von drei Tabellen erstellt wird. Listing 9.38: Eine auf einem Join mit mehreren Tabellen basierende Datensicht SQL> create view Class_Summary as select Class_ID, Class_Building, Class_Room, CL.Course_ID, CO.Title, Last_Name, First_Name from Class CL, Course CO, Instructor I where CL.Course_ID = CO.Course_ID and CL.Instructor_ID = I.Instructor_ID; View created. Durch Verwenden der Datensicht Class_Summary kann der Benutzer die gewünschten Spalten einfach auswählen, ohne irgendwelche Verknüpfungsbedingungen anzugeben. Listing 9.39 zeigt,
wie der Benutzer die Zeilen der Datensicht Class_Summary auswählen kann, deren Spalte Title die Zeichenkette INTRO enthält. Listing 9.39: Auswahl aus einer Datensicht SQL> select Class_ID, Class_Building, Class_Room, Course_ID, Title, Last_Name from Class_Summary where Title like '%INTRO%'; CLASS_ID CLASS_BUILD CLASS_ROOM COURSE_ID TITLE ---------- ----------- ---------- ---------- -----------109100 FLUGLE HALL 180 10021 INTRO TO BIOLOGY 108300 FLUGLE HALL 150 10071 INTRO TO ENGLISH LIT
LAST_NAME --------WEISS CHERNOW
9.11 Zusammenfassung Diese Lektion hat sich auf die folgenden Merkmale von SQL konzentriert: ●
●
●
●
● ●
● ●
●
●
Oracle bietet Gruppenfunktionen wie avg und sum, die einen einzelnen Wert für eine Menge von Zeilen zurückgeben. Die select-Anweisung hat zwei optionale Klauseln - group by und having - die Zeilen zurückgeben, welche nach einem gemeinsamen Wert für die angegebenen Spalten gruppiert sind. Hierarchische Daten können durch die connect by-Klausel organisiert werden. Sie müssen mit der start with-Klausel eine Startbedingung angeben. Eine Join-Operation ist eine select-Anweisung, deren from-Klausel eine oder mehrere Tabellen und eine oder mehrere Verknüpfungsbedingungen enthält, die typischerweise Fremdund Primärschlüssel vergleichen. Oracle9i unterstützt die ANSI Join Syntax und den Full Outer Join. Der intersect-Operator gibt eine Menge von gemeinsamen Zeilen zurück, die in zwei Mengen von Zeilen gefunden wurden. Der union-Operator gibt alle eindeutigen Zeilen aus zwei Mengen von Zeilen zurück. Der union all-Operator gibt alle Zeilen (inklusive Duplikate) aus zwei Mengen von Zeilen zurück. Der minus-Operator gibt Zeilen zurück, die in einer Menge, aber nicht in der anderen gefunden wurden. Datensichten (Views) werden verwendet, um komplexere Abfragen vorgefertigt bereitzustellen oder Zugriffsbeschränkungen zu implementieren.
9.12 Wie geht es weiter? Am Tag 10 lernen Sie die Sicherheitskonzepte der Oracle9i Datenbank kennen. Außerdem verwenden Sie Tuning Werkzeuge wie EXPLAIN und TKPROF.
9.13 Fragen und Antworten Frage:
Können zwei Tabellen A und B durch Spalten verknüpft werden, wenn der Datentyp der einen Spalte in Tabelle A nicht der gleiche ist wie der Datentyp der anderen Spalte in Tabelle B? Antwort: Ja, das ist möglich. Sie können beispielsweise eine Umwandlungsfunktion wie to_char, to_date oder to_number verwenden, um die eine Spalte so umzuwandeln, dass der von der Funktion zurückgegebene Datentyp dem Datentyp der anderen Spalte entspricht.
9.14 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Erstellen Sie eine SQL-Anweisung, die den Nachnamen eines Lehrers liefert, der einen Kurs mit zusätzlichen Gebühren über $50 unterrichtet. 2. Erstellen Sie eine SQL-Anweisung, die eine Liste von Städten, in denen Studenten wohnen, sowie die Zahl der Studenten in jeder Stadt liefert. 3. Erstellen Sie eine Datensicht, die jede Klasse auflistet - ihre Class_ID, Course_ID und den Raum - , die am Montag zusammentrifft.
Übungen Die Anzahl der Lehrer am Flugle College ist 18, die Anzahl der angebotenen Klassen ist 10. Trotzdem ist die Zahl der eindeutigen Instructor_IDs in der Tabelle Class gleich 9. Geben Sie unter Verwendung dieser Tabelle und von SQL eine vollständige Erklärung.
SQL-Funktionen Diese Lektion untersucht die Bearbeitung von Zeichenketten, Datumsangaben und Zahlen mit SQLFunktionen. Diese Funktionen sind in den folgenden Situationen nützlich: ●
● ● ●
Beim Übertragen von Zeichenfolgen innerhalb einer Datenbankanwendung von einer Form in eine andere Beim Erzeugen von Berichten Beim Durchführen von Ad-hoc-Abfragen Beim Konvertieren von Daten von einer Datenbank in eine andere
Oracle bietet eine große Menge integrierter Funktionen und Operatoren für die Umwandlung und Bearbeitung von Zeichenketten, Datumsangaben und Zahlen. Der volle Leistungsumfang dieser Funktionen und Operatoren wird wirksam, wenn man sie ineinander verschachtelt. Diese Lektion zeigt mehrere Beispiele für diese Technik. Es würde den Rahmen dieses Buches sprengen, alle SQLFunktionen zu behandeln. Das Kapitel 6 des Handbuchs »Oracle9i SQL Reference«, enthält eine vollständige Auflistung aller SQL-Funktionen.
8.1 Bearbeiten von Zeichenfolgen Oracle bietet mehrere nützliche integrierte Funktionen, mit denen Zeichenfolgen bearbeitet werden können. Sie sehen auf den folgenden Seiten ein Reihe von Beispielen.
Die Länge einer Zeichenkette ermitteln Oracle9i kennt im Umgang mit der Länge von Zeichenketten zwei verschiedene Semantiken. Rufen Sie sich zunächst in Erinnerung, dass Oracle eine Vielzahl von Zeichensätzen, darunter Unicode, unterstützt. Viele dieser Zeichensätze verwenden mehrere Bytes, um ein einzelnes Schriftzeichen zu speichern. Die deutschen Umlaute benötigen in Unicode Kodierung z.B. zwei Bytes, während dieselben Zeichen im Zeichensatz WE8ISO8859P1 durch ein Byte dargestellt werden. Sie erkennen, dass die Anzahl der Bytes von der Anzahl der Schriftzeichen abweichen kann. Oracle9i gibt Ihnen bei der Arbeit mit char, varchar2 und long Spalten über den Initialisierungsparameter NLS_LENGTH_SEMANTICS die Möglichkeit, mit der Länge in Bytes oder der Länge in Schriftzeichen zu arbeiten. Die Datentypen nchar und nvarchar2 arbeiten immer mit der Länge in Schriftzeichen. Relevant ist der Parameter nur, wenn der Datenbankzeichensatz ein Multi-Byte Zeichensatz variabler Länge wie Unicode in UTF8 Kodierung, Japanisch (z.B. JA16EUC) oder Chinesisch (z.B. ZHT32EUC) ist, denn bei 8bit Zeichensätzen wie WE8ISO8859P1 entspricht die Länge in Bytes der Anzahl der Schriftzeichen. Der Standardwert von NLS_LENGTH_SEMANTICS ist BYTE, um die Abwärtskompatibilität zu gewährleisten. Falls Ihr Datenbankzeichensatz ein ASCII basierter Multi-Byte Zeichensatz ist, haben Schriftzeichen in verschiedenen Sprachen unterschiedliche Längen. In eine varchar2(10) Spalte würden 10 Schriftzeichen aus dem ASCII Repertoire passen. Sobald jedoch ein deutscher Umlaut, ein Buchstabe mit Akzent aus dem Französischen, Portugiesischen, usw. in die Spalte eingefügt werden, haben nur noch neun
Schriftzeichen Platz, weil Zeichen außerhalb des ASCII Repertoires mehr als ein Byte benötigen. Das Problem ist, dass Sie nicht vorhersehen können, wie viele Zeichen außerhalb des ASCII Repertoires in eine Spalte eingefügt werden. Setzen Sie NLS_LENGTH_SEMANTICS auf CHAR, entfällt diese Problematik vollständig. Oracle9i fasst die Länge der Spalten als die Länge in Schriftzeichen auf und garantiert Ihnen, dass immer dieselbe Anzahl Schriftzeichen in der Spalte Platz finden, unabhängig davon wie viele Bytes der Datenbankzeichensatz benötigt, um ein Schriftzeichen zu kodieren.
Die Funktion length Sie können die length-Funktion verwenden, um die Länge einer Zeichenfolgenspalte zu ermitteln. Der Rückgabewert von length ist die Anzahl der Schriftzeichen in der als Parameter übergebenen Zeichenkette, wie in Listing 8.1 gezeigt wird. Listing 8.1: Verwendung der Funktion length SQL> select Last_Name, length(Last_Name) from instructor order by Last_Name; LAST_NAME LENGTH(LAST_NAME) ------------------------ ----------------ANGELO 6 BATES 5 BILLINGS 8 Wenn Sie die Anzahl der Bytes in einer Zeichenkette ermitteln möchten, verwenden Sie die Funktion lengthb. Wie zuvor besprochen, benötigen Multi-Byte-Zeichensätze wie Unicode mehrere Bytes, um bestimmte Schriftzeichen darzustellen. Der Rückgabewert von lengthb ist bei Zeichenketten, die in einem Multi-Byte Zeichensatz kodiert sind, unter Umständen größer als derjenige von length.
Eine Teilzeichenkette aus einer Zeichenkette gewinnen Die Funktion substr extrahiert eine Teilzeichenkette aus einer Zeichenkette.
Die Funktion substr wird folgendermaßen verwendet: SUBSTR (string, starting_character, number_of_characters) Die Bedeutungen der Platzhalter sind: ● ● ●
string ist eine Spalte einer Tabelle, die eine Zeichenkette speichert, oder ein Zeichenkettenliteral. starting_character ist die Startposition der Teilzeichenkette. Die Position des ersten Zeichens ist 1. number_of_characters ist die Länge der Teilzeichenkette, die extrahiert werden soll.
Listing 8.2 zeigt, wie die Funktion substr verwendet wird, um die ersten vier Zeichen des Nachnamens eines Lehrers zurückzugeben. Listing 8.2: Verwendung der Funktion substr SQL> select Last_Name, substr(Last_Name,1,4)
from Instructor order by Last_Name; LAST_NAME -----------------------ANGELO BATES BILLINGS
SUBS ---ANGE BATE BILL
Zusätzlich zur Verwendung von Literalen können Sie auch eine Funktion als Argument der Funktion von substr angeben. Listing 8.3 zeigt ein gutes, wenn auch nicht sehr nützliches Beispiel. Nehmen Sie an, dass Sie die drei letzten Zeichen des Nachnamens eines Lehrers erhalten wollen. Sie würden die Funktion length verwenden, um die Position des letzten Zeichens zu finden. Um das richtige Startzeichen für die Funktion substr zu finden, ziehen Sie n - 1 vom Resultat des Aufrufs der Funktion length ab, wobei n die Zahl der Zeichen ist, die Sie erhalten wollen - in Listing 8.3 substrahieren Sie 3 - 1 = 2. Listing 8.3: Verwendung der Funktion length in einem Aufruf der Funktion substr SQL> select substr(Last_Name,length(Last_Name)-2,3) 2 from Instructor 3 order by Last_Name; SUB --ELO TES NGS
Muster in einer Zeichenkette finden Die Funktion instr sucht in einer Zeichenkette nach dem Auftreten einer bestimmten Zeichenfolge. Der Rückgabewert ist die Position, an der die gesuchte Zeichenfolge beginnt. instr (string, substring [, position [, occurrence] ] ) Die Bedeutungen der Platzhalter sind: ● ● ● ●
string ist eine Zeichenkette, die durchsucht werden soll. substring ist die gesuchte Zeichenfolge. position ist die Startposition, bei der die Suche in string beginnt. occurrence gibt an ob das erste, zweite dritte, etc. Auftreten der Zeichenfolge substring gesucht werden soll.
Listing 8.4 zeigt, wie in einer Zeichenkette ab Position fünf nach dem zweiten Auftreten der Zeichenfolge 'the' gesucht wird. Listing 8.4: Verwendung von instr zur Suche nach Zeichenfolgen innerhalb einer Zeichenkette select instr( 'Oracle Corporation is the market share leader in the RDBMS market' ,'the',5,2) result from dual; RESULT ---------50
Einen Teil einer Zeichenkette ersetzen Eine häufige Aufgabe bei der Bearbeitung von Daten ist, innerhalb einer Spalte ein Muster in ein anderes umzuwandeln. Die SQL-Funktion replace verändert den Inhalt einer Spalte so, dass eine Zeichenkette durch eine andere ersetzt wird.
Die Syntax der Funktion replace ist: REPLACE (string, existing_string, [replacement_string]) Die Bedeutungen der Platzhalter sind: ● ● ●
string ist eine Zeichenfolge. existing_string ist eine Zeichenfolge, die in string auftreten könnte. replacement_string ist eine optionale Zeichenfolge, durch die existing_string ersetzt werden soll.
Listing 8.5 zeigt, wie replace verwendet werden kann, um den Namen eines Kurses in der Tabelle Course zu ändern. Zuerst zeigt eine Abfrage die aktuellen Namen. Dann wird eine update-Anweisung mit der replace-Funktion verwendet, um jedes Auftreten des Wortes SEMINAR in DISCUSSION zu ändern. Eine weitere Abfrage zeigt die Auswirkungen der update-Anweisung. Listing 8.5: Verwendung der Funktion replace SQL> select Title 2 from Course 3 order by Title; TITLE ------------------------------PSYCH IN FILM SEMINAR ON CHAOS SEMINAR ON NACIMERA SQL> update Course 2 set Title = replace(Title,'SEMINAR','DISCUSSION'); 2 rows updated. SQL> select Title 2 from Course 3 order by Title; TITLE ------------------------------PSYCH IN FILM SEMINAR ON CHAOS SEMINAR ON NACIMERA
Wenn Sie in der Funktion replace keine Ersatzzeichenfolge angeben, wird die vorhandene Zeichenfolge aus der Spalte entfernt.
Entfernen führender und nachfolgender Leerzeichen in einer Zeichenfolge Wenn eine Spalte führende oder nachfolgende Leerzeichen enthält, kann eine Abfrage, die auf einem bestimmten Spaltenwert basiert, irreführende Resultate zurückgeben. Um dies zu verdeutlichen, werfen Sie einen Blick auf Listing 8.6. Zuerst wird eine update- Anweisung verwendet, um ein Leerzeichen an die Spalte First_Name anzufügen. Eine Abfrage der Tabelle Instructor nach dem Vornamen 'BORIS' liefert keinen Datensatz, aber eine zweite Abfrage, die nach einem Vornamen sucht, der gleich 'BORIS ' ist, zeigt, dass die Spalte geändert wurde. Listing 8.6: Der Umgang mit nachfolgenden Leerzeichen SQL> update Instructor 2 set First_Name = First_Name || ' '; 18 rows updated. SQL> select Instructor_ID, Last_Name, First_Name 2 from Instructor 3 where 4 First_Name = 'BORIS'; no rows selected SQL> select Instructor_ID, Last_Name, First_Name 2 from Instructor 3 where 4 First_Name = 'BORIS '; INSTRUCTOR_ID LAST_NAME FIRST_NAME ------------------------------------M101 HITCHCOCK BORIS Wenn Sie nichts von den nachfolgenden Leerzeichen gewusst hätten, hätten Sie sich natürlich über die Tatsache gewundert, dass die Abfrage keine Zeile liefert.
Um irreführende Ergebnisse bei einer Abfrage zu vermeiden, entfernen Sie führende oder nachfolgende Leerzeichen, bevor eine Zeile zu einer Tabelle hinzugefügt oder bearbeitet wird. Nachfolgende Leerzeichen sind hier häufiger ein Problem als führende Leerzeichen, weil sie weniger gut sichtbar sind. Oracle bietet drei Funktionen, um Leerzeichen zu entfernen: ltrim, rtrim und trim. ltrim entfernt führende Leerzeichen einer Zeichenfolge, rtrim entfernt nachfolgende Leerzeichen und trim sowohl führende als auch nachfolgende Leerzeichen. Wenn es keine führenden oder nachfolgenden Leerzeichen gibt, verändern ltrim, rtrim und trim die vorhandene Zeichenfolge nicht. Beachten Sie, wie die Funktion rtrim in Listing 8.7 verwendet wird. Listing 8.7: Abschneiden nachfolgender Leerzeichen mit Hilfe der Funktion rtrim SQL> update Instructor 2 set First_Name = rtrim(First_Name)
3 where 4 First_Name like '% '; 18 rows updated. Beachten Sie, wie in Listing 8.7 die where-Klausel verwendet wird, um nur die Zeilen zu aktualisieren, in denen First_Name ein nachfolgendes Leerzeichen enthält. Wie Sie sehen können, haben ltrim und rtrim ein einziges Argument: Die Zeichenfolge, von der die Leerzeichen entfernt werden sollen. Sie können bei beiden Funktionen auch ein optionales zweites Argument angeben: Eine alternative Menge von Zeichen, die von der Zeichenkette entfernt werden sollen. Als Beispiel betrachten Sie bitte Listing 8.8. Stellen Sie sich vor, dass Sie eine Tabelle namens Test_Trim haben, mit einer einzigen Spalte namens MY_COL vom Typ varchar2. Sie wissen, dass einige Zeilen führende Zeichen aufweisen, die Sie entfernen wollen: x, y und z. Die Funktion ltrim wird in einer updateAnweisung verwendet, um die störenden Zeichen zu entfernen. Listing 8.8: Löschen führender Zeichen mit Hilfe der Funktion ltrim SQL> select my_col from test_trim; MY_COL -----------------------------yzzxHello, world zyxGoodbye, cruel world SQL> update test_trim set my_col = ltrim(my_col,'xyz'); 2 rows updated. SQL> select my_col from test_trim; MY_COL -----------------------------Hello, world Goodbye, cruel world Sie können nachfolgende Zeichen entfernen, indem Sie dieselbe Technik mit rtrim verwenden.
Auffüllen einer Zeichenkette Gelegentlich stehen Sie vor der Aufgabe, eine Zeichenkette mit führenden oder nachfolgenden Zeichen auffüllen zu müssen. Oracle bietet zu diesem Zweck die Funktionen: lpad und rpad an.
Lpad Um eine Zeichenfolge von links mit Zeichen aufzufüllen, verwenden Sie die Funktion lpad.
Die Syntax ist: LPAD (string, n [, pad_string]) Die Bedeutungen der Platzhalter sind:
●
● ●
string ist ein Zeichenkettenliteral oder der Name einer Zeichenfolgenspalte, die von links aufgefüllt werden soll. n ist die Gesamtlänge der von lpad zurückgegebenen Zeichenfolge. pad_string ist eine optionale Zeichenfolge, mit der string von links aufgefüllt werden soll. Falls pad_string nicht angegeben wird, wird mit Leerzeichen aufgefüllt.
Lassen Sie uns sehen, wie das funktioniert. Listing 8.9 zeigt, wie man mit lpad eine Zeichenfolge mit führenden Leerzeichen auffüllt. Listing 8.9: Hinzufügen führender Leerzeichen zu einer Spalte mit Hilfe der Funktion lpad SQL> select lpad(my_col,20) from test_trim; LPAD(MY_COL,20) -------------------Hello, world Goodbye, cruel world Sie können ein Zeichenkettenliteral angeben, das lpad verwendet, um die Zeichenkette von links aufzufüllen. Die Anzahl der Zeichen, mit denen die Zeichenkette aufgefüllt wird, hängt vom Wert von n ab. Listing 8.10 zeigt, wie man einen festen Betrag zum Rückgabewert der Funktion length addieren kann, die hier als Argument für lpad dient. Im Beispiel ist die zu length(my_col) hinzuaddierte Zahl ein Vielfaches der Länge der Zeichenkette 'You say ' (acht Zeichen). Dadurch wird string zweimal mit pad_string aufgefüllt, wie in der zweiten Abfrage gezeigt wird. Listing 8.10: Verwendung der Funktion length als Argument für die Funktion lpad SQL> select lpad(my_col,length(my_col)+8,'You say ') from test_trim; LPAD(MY_COL,LENGTH(MY_COL)+8,'YOUSAY') -----------------------------------You say Hello, world You say Goodbye, cruel world SQL> select lpad(my_col,length(my_col)+16,'You say ') from test_trim; LPAD(MY_COL,LENGTH(MY_COL)+16,'YOUSAY') -----------------------------------You say You say Hello, world You say You say Goodbye, cruel world Sie können eine Zeichenfolge auch mit dem Inhalt einer anderen Spalte von links auffüllen. Wie Sie bei der zweiten Abfrage in Listing 8.11 sehen können, wird my_col von links mit my_col2 aufgefüllt. Wie oft dieser Schritt ausgeführt wird, hängt von der Länge der Werte in beiden Spalten ab. Wenn my_col den Wert California hat und my_col2 den Wert Los Angeles, dann gibt lpad eine Zeichenfolge zurück, in der California (10 Zeichen) von links mit Los Angeles (11 Zeichen) aufgefüllt wird, so dass Los Angeles 40 Zeichen ausfüllt. Listing 8.11: Verkettung zweier Spalten mit Hilfe der Funktion lpad SQL> select * from test_trim; MY_COL --------------California Michigan Washington Oregon
MY_COL2 --------------Los Angeles Jackson Seattle Portland
SQL> select lpad(my_col,50,my_col2) from test_trim; LPAD(MY_COL,50,MY_COL2) -------------------------------------------------Los AngelesLos AngelesLos AngelesLos AngelesCalifornia JacksonJacksonJacksonJacksonJacksonJacksonMichigan SeattleSeattleSeattleSeattleSeattleSeattleWashington PortlandPortlandPortlandPortlandPortlandPortOregon Durch Kombinieren dieser integrierten Funktionen können Sie komplexe Ausdrücke erstellen. Listing 8.12 verwendet die Längen von my_col und my_col2 als Argumente, um zu garantieren, dass das Auffüllen von links nur einmal stattfindet. Lassen Sie uns die select-Anweisung zerlegen. Das erste Argument von lpad ist my_col2. Für das zweite Argument addieren Sie die Länge von my_col2 zur Länge von my_col und addieren eine 2 für das ', ', das zwischen my_col und my_col2 eingefügt wird. Schließlich geben Sie ', ' als pad_string und drittes Argument von lpad an, was letztlich zur Konkatenation von my_col und my_col2 führt. Listing 8.12: Verwendung der Funktion length als Argument für die Funktion lpad SQL> select lpad (my_col, length(my_col)+length(my_col2)+2, my_col2 || ', ') 2 from test_trim; LPAD(MY_COL,LENGTH(MY_COL)+LENGTH(MY_COL2)+2,MY_COL2||', ') ----------------------------------------------------------Los Angeles, California Jackson, Michigan Seattle, Washington Portland, Oregon
Rpad
Rpad funktioniert genauso wie lpad. Verwenden Sie die folgende Syntax: RPAD (string, n [, pad_string]) Die Bedeutungen der Platzhalter sind: ● ● ●
string ist das Zeichenkettenliteral oder die Zeichenkettenspalte, die von rechts aufgefüllt werden soll. n gibt an, wie oft von rechts mit pad_string aufgefüllt werden soll. pad_string ist eine Zeichenfolge, mit der string von rechts aufgefüllt werden soll. Wenn pad_string nicht angegeben wird, wird mit Leerzeichen aufgefüllt.
Ändern der Groß- und Kleinschreibung Oracle bietet drei Funktionen, mit denen Sie die Groß- und Kleinschreibung einer Zeichenfolge ändern können: ●
initcap ändert das erste Zeichen jedes Worts in Großschreibung.
● ●
lower ändert alle Zeichen einer Zeichenfolge in Kleinschreibung. upper ändert alle Zeichen einer Zeichenfolge in Großschreibung.
Alle drei Funktionen haben ein einziges Argument: die Zeichenfolge, die bearbeitet werden soll. Listing 8.13 zeigt die Verwendung der Funktionen upper und lower. Listing 8.13: Verwendung der Funktionen upper und lower SQL> select lower(Title), upper(Title) 2 from Course 3 order by Title; LOWER(TITLE) UPPER(TITLE) ---------------------------------- -------------------------------abnormal psychology ABNORMAL PSYCHOLOGY advanced arithmetic ADVANCED ARITHMETIC ancient Greek philosophers ANCIENT GREEK PHILOSOPHERS Listing 8.14 zeigt, wie die Funktion initcap alle Zeichen in Kleinschreibung ändert und den ersten Buchstaben jedes Worts groß schreibt. Listing 8.14: Verwendung der Funktion initcap SQL> select initcap(Title) 2 from Course 3 order by Title; INITCAP(TITLE) -----------------------------Abnormal Psychology Advanced Arithmetic Ancient Greek Philosophers
Die Funktion decode Viele Datenbankanwendungen verwenden Spalten, die verschlüsselte Informationen enthalten. Manchmal erzeugt ein Datenbankdesigner eine Tabelle, um einen Code und seine Beschreibung zu speichern, besonders wenn der Designer erwartet, dass die Codes sich ändern. In anderen Situationen steht die Spalte mit dem Code für sich alleine, ohne dass weitere Informationen in der Datenbank verfügbar wären. Tag 3, »Logischer Datenbankentwurf«, besprach die Notwendigkeit für eine Tabelle, die Details eines Zeitplans enthält - an welchen Tagen Lehrveranstaltungen stattfinden und deren Dauer. Als Ergebnis haben Sie die Tabelle Schedule_Type_Details erzeugt. Eine der Spalten in dieser Tabelle, Day, speichert den Wochentag als ganze Zahl, wobei der Sonntag durch eine 1 repräsentiert wird, der Montag durch eine 2 usw. Die meisten Benutzer finden es umständlich, die Inhalte dieser Tabelle zu entziffern. Glücklicherweise kann die Funktion decode kryptische Codes so transformieren, dass Benutzer das Ergebnis problemlos interpretieren können.
Die Syntax ist:
DECODE (expression, value1, returned_value1 ... [, valueN, returned_valueN] [, default_returned_value]) Die Bedeutungen der Platzhalter sind: ● ● ●
●
expression ist ein gültiger Oracle-Ausdruck. valueN ist der Wert, der gleich expression sein könnte. returned_valueN ist der Wert, der von decode zurückgegeben wird, wenn expression gleich valueN ist. default_returned_value ist ein optionaler Wert, der von decode zurückgegeben wird, wenn expression keinem der Werte value1 bis valueN entspricht.
Listing 8.15 zeigt, wie die Funktion decode die numerische Angabe der Wochentage in den Wochentag transformiert. Listing 8.15: Umwandlung eines numerischen Wertes in eine Zeichenfolge mit Hilfe der Funktion decode SQL> select Schedule_ID, Day, 2 decode (Day, 1, 'SUN', 2, 'MON', 3, 'TUE', 4, 'WED', 3 5, 'THU', 6, 'FRI', 7, 'SAT') 4 from Schedule_Type_Details 5 order by Schedule_ID, Day; SCHEDULE_ID DAY DEC --------------- --S180 6 FRI T10 2 MON T10 4 WED T10 6 FRI T13 2 MON T13 4 WED T13 6 FRI T15 2 MON T15 4 WED T15 6 FRI TT9 3 TUE TT9 5 THU 12 rows selected.
Dezimal- und Hexadezimaldarstellung von Zeichenketten Die Funktion dump gibt Ihnen die Möglichkeit, die tatsächliche Kodierung einer Zeichenkette in einer Spalte als Dezimal- oder Hexadezimaldarstellung anzuzeigen. Wenn Anwender Ihnen z.B. melden, dass deutsche Umlaute nicht korrekt verarbeitet werden, ist es sinnvoll, die Kodierung der Daten in der Datenbank zu prüfen. Sie benötigen dazu eine Codetabelle des Datenbankzeichensatzes oder des nationalen Zeichensatzes, die zu jedem Schriftzeichen die Dezimal- oder Hexadezimaldarstellung enthält. Prüfen Sie mit Hilfe der dump-Funktion, ob die Kodierung der Daten in der Datenbank mit der Codetabelle übereinstimmt. Sollte eine Abweichung vorliegen, prüfen Sie als nächstes, ob die Einstellung des Zeichensatzes auf dem Client-Rechner durch den Registry-Eintrag bzw. die Umgebungsvariable NLS_LANG korrekt ist.
Verwendet der Client-Rechner z.B. fälschlich die Einstellung NLS_LANG=German_ Germany.US7ASCII anstelle von NLS_LANG=German_Germany.WE8ISO8859P15, dann wird ein scharfes S (ß) als Unterstrich in der Datenbank abgelegt, weil die Behandlung eines Byte als Schriftzeichen aus dem Repertoire des Zeichensatzes US7ASCII das achte Bit in der Darstellung des scharfen S auf 0 setzt. Dadurch entsteht die Kodierung für das Schriftzeichen Unterstrich (_). Oracle9i verhält sich völlig korrekt, dennoch geht die richtige Darstellung des scharfen S verloren, weil NLS_LANG auf dem Client-Rechner falsch eingestellt ist. Listing 8.16: Verwendung der Funktion dump SQL> select last_name, dump(last_name) dec, dump(last_name,16) hex from instructor order by last_name; LAST_NAME DEC HEX --------- --------------------------- --------------------------WEISS Typ=1 Len=5: 87,69,73,83,83 Typ=1 Len=5: 57,45,49,53,53 YOUNG Typ=1 Len=5: 89,79,85,78,71 Typ=1 Len=5: 59,4f,55,4e,47 Übergeben Sie nur eine Zeichenkette an die Funktion dump, wird die Dezimalkodierung ausgegeben. Wenn Sie zusätzlich die Zahl 16 als zweites Argument übergeben, wird die Hexadezimaldarstellung ausgegeben. Die letzte Ergebniszeile besagt, dass der Buchstabe Y im Namen Young durch ein Byte mit dem Hexadezimalwert 59 (dezimal 89) dargestellt wird.
8.2 Bearbeiten von Datumsangaben Es gibt eine unglaubliche Vielfalt in der Art und Weise, in der Datenbanksysteme Datums- und Zeitangaben behandeln. Glücklicherweise besitzt Oracle den Datentyp date, um Datums- und Zeitangaben zu behandeln. Dieser Datentyp hat sein eigenes internes Format, um das Sie sich nicht zu kümmern brauchen. Sie müssen nur wissen, dass er für die Speicherung von Jahrhundert, Jahr, Monat, Tag, Stunde, Minute und Sekunde zuständig ist. Wie dieser Abschnitt zeigt, hat es viele Vorteile, diesen Datentyp an den richtigen Stellen zu verwenden. Wenn Sie eine Spalte als date definieren, können Sie alle integrierten Funktionen von Oracle zur Bearbeitung von Datums- und Zeitangaben verwenden. Der Oracle-Datentyp date ist zudem sehr praktisch für Anwendungsentwickler. Man kann darüber streiten, ob die von Oracle verwendeten Algorithmen optimal sind. Sie könnten der Meinung sein, dass andere Methoden zur Speicherung von Datums- und Zeitangaben effizienter sind. Ohne Frage kann Ihnen jedoch die Verwendung des Oracle-Datentyps date eine Menge Zeit und Aufwand bei der Anwendungsentwicklung sparen. In der Tat, da die Vorteile dieses Datentyps so klar sind, sollten Sie unbedingt den Oracle-Datentyp date verwenden, wenn Sie Datumsoder Zeitinformationen speichern müssen. Stellen Sie sich vor, dass Sie das Einstellungsdatum und ein eventuelles Kündigungsdatum jedes Lehrers speichern müssen. Wenn Sie eine falsche Entscheidung fällen und sich dafür entscheiden, das Einstellungsdatum des Lehrers als numerischen Wert der Form yymmdd zu speichern, würde das Einstellungsdatum für einen Angestellten, der am 9. Mai 1957 eingestellt wurde, als 570509 gespeichert werden. Listing 8.17 enthält eine Beschreibung der Tabelle Instructor, so wie sie aussehen würde, wenn sie eine derartige Spalte zur Speicherung des Einstellungsdatums enthielte. Listing 8.17: Instructor-Tabelle, in der das Datum der Anstellung als Zahl gespeichert ist SQL> desc Instructor Name
Null?
Type
---------------INSTRUCTOR_ID DEPARTMENT_ID LAST_NAME FIRST_NAME MI POSITION TELEPHONE FAX EMAIL HIRE_DATE
-------- -----------VARCHAR2(20) NOT NULL VARCHAR2(20) NOT NULL VARCHAR2(25) VARCHAR2(25) VARCHAR2(1) VARCHAR2(25) VARCHAR2(10) VARCHAR2(10) VARCHAR2(100) NUMBER(6)
Schlauerweise (das dachten Sie zumindest) haben Sie sich entschieden, das Format yymmdd zu verwenden, so dass das Einstellungsdatum entweder in aufsteigender oder in absteigender Reihenfolge geordnet werden könnte. Sie könnten eine select-Anweisung verwenden, um die Zeilen aus der Tabelle Instructor nach Einstellungsdatum geordnet zu erhalten. Dieser Ansatz hat jedoch einige Probleme, nämlich: ●
●
●
Eine irrige Annahme. Wenn ein Lehrer nach 1999 eingestellt wird, gibt die select- Anweisung nicht das richtige Ergebnis zurück. Ein Lehrer, der im Jahr 2000 oder danach eingestellt wurde, taucht am Anfang der Liste auf, da das Format nur die zwei letzten Ziffern des Jahres speichert, in dem der Lehrer eingestellt wurde. Mehr Arbeit und geringere Zuverlässigkeit. Weil Sie sich nicht auf die Gültigkeitsprüfung von Oracle für die Daten verlassen können, ist es Ihre Aufgabe, einen Algorithmus zur Validierung des Einstellungsdatums zu entwickeln. Eingeschränkte Funktionalität. Wenn Sie eine Tabelle auf der Grundlage von Datenarithmetik abfragen müssen, erfordern Dinge, die mit einer einzigen select- Anweisung hätten erreicht werden können, zusätzliche Bearbeitung. Sie können keine select-Anweisung schreiben, die alle Angestellten liefert, deren Einstellungsdatum nach dem durchschnittlichen Einstellungsdatum liegt.
Wenn Sie stattdessen den Oracle-Datentyp date verwenden, können Sei sicher sein, dass jede Anwendung, die Sie entwickeln, auf andere Plattformen portiert werden kann. Datumsformate werden auf jeder Plattform unterstützt, auf der Oracle läuft. Wenn Sie planen, eine Anwendung auf einer Vielzahl von Betriebssystemen laufen zu lassen, werden Sie feststellen, dass die Verwendung des Oracle-Datentyps date einfacher ist, als für jedes Betriebssystem eine Vielzahl von Datums- und Zeitformaten zu unterstützen.
Das aktuelle Datum und die aktuelle Uhrzeit Oracle9i stellet Ihnen die Funktion sysdate zur Verfügung, um das aktuelle Datum und die aktuelle Uhrzeit abzufragen. Wie der Datentyp date ist sysdate bis auf eine Sekunde genau. Sysdate ist ein extrem nützliches Konstrukt, um Zeilen während einer Einfüge- oder Aktualisierungsoperation mit einem Zeitstempel zu versehen. Viele Beispiele in dieser Lektion verwenden sysdate. Sollte Ihnen die Genauigkeit von einer Sekunde nicht ausreichen, sind Sie in der Lage, mit Spalten des Datentyps timestamp zu arbeiten, um eine Genauigkeit von einer Milliardstel-Sekunde (9 Nachkommastellen für Sekundenbruchteile) zu erreichen. Das aktuelle Datum und die aktuelle Zeit mit einer Hunderttausendstel-Sekunde Genauigkeit erhalten Sie über die Funktion systimestamp. Zusätzlich gibt systimestamp die Zeitzone, in der sich die Datenbank befindet, zurück. Der Rückgabewert von systimestamp hat den Datentyp TIMESTAMP WITH TIME ZONE.
Datumsformate
Weil der Datumsdatentyp die Werte für Jahrhundert, Jahr, Monat, Stunde, Minute und Sekunde speichert, kann jeder dieser Werte unabhängig voneinander extrahiert und formatiert werden. Die Datums- und Zeitelemente können zudem abgekürzt oder voll ausgeschrieben werden, je nachdem, welches Format Sie bevorzugen. Sie haben auch die Option, ein Datums- oder Zeitelement in verschiedenen Formaten zu wiederholen. Wie Sie sich erinnern, enthält die Tabelle Schedule_Type_Details eine Startzeit für jeden Tag einer bestimmten Schedule_ID. Es kann beispielsweise sein, dass ein Zeitplan einen Montag/Mittwoch/Freitag-Zeitplan beschreibt, der um 10 Uhr beginnt und 50 Minuten dauert. Die Spalte Starting Time enthält Informationen über die Startstunde und -minute. Wenn Sie nur die Uhrzeit mit einer Genauigkeit von Stunden und Minuten ausgeben möchten, müssen Sie die Funktion to_char mit einem Zeitformat verwenden, um den internen Datumswert in einen externen Zeichenwert zu übersetzen. Listing 8.18 zeigt, wie Sie den Inhalt der Tabelle Schedule_Type_Details anzeigen können. Listing 8.18: Umwandlung einer Datums- und Zeitangabe in eine Zeichenfolge SQL> select Schedule_ID, Day, Starting_Time, to_char(Starting_Time,'HH:MI PM') 2 from Schedule_Type_Details 3 order by Schedule_ID, Day; SCHED DAY STARTING_ TO_CHAR(STARTING_TIME,'HH:MIPM') ------- --------- ------------------------------S180 6 01-MAR-97 09:00 AM T10 2 01-MAR-97 10:00 AM T10 4 01-MAR-97 10:00 AM
Sie können sysdate (systimestamp) und die Tabelle DUAL verwenden, um mit verschiedenen Datums- und Zeitformaten zu experimentieren. Sie können sysdate aus der Tabelle DUAL auswählen, jedoch keine Zeilen in die Tabelle DUAL einfügen - sie darf nur eine Zeile haben, damit einige Oracle-Werkzeuge richtig funktionieren. Tabelle 8.1 enthält die gültigen Elemente der Datumsformate.
Das Standard-Datumsformat in Oracle Das Standard-Datumsformat von Oracle ist DD-MON-YY. Zum Beispiel ist 01-JAN-98 eine Datumsangabe gemäß dem Standard-Datumsformat von Oracle. Sie können ein Datum in diesem Format angeben, ohne irgendwelche Funktionen oder Datentypumwandlungen zu verwenden. Wenn Sie Datumsangaben jedoch in einem anderen Format anzeigen oder angeben müssen, verwenden Sie NLS_DATE_FORMAT oder eine der integrierten Funktionen, um das Datumsformat anzugeben, das Sie verwenden wollen. Sie können NLS_DATE_FORMAT entweder als Umgebungsvariable setzen (siehe Tag 3) oder mit alter session modifizieren. Ein als Umgebungsvariable eingetragenes Datumsformat gilt für alle Anwendungen auf einem Rechner, während mit alter session das Datumsformat für eine einzelne Datenbanksitzung verändert werden kann. Listing 8.19: Änderung des Standarddatumsformats für eine Datenbanksitzung SQL> select sysdate from dual;
SYSDATE --------02-Nov-01 SQL> alter session set nls_date_format='dd.mm.yyyy hh24:mi:ss'; Session altered. SQL> select sysdate from dual; SYSDATE ------------------02.11.2001 09:34:42 Wenn Sie versuchen, einer Datumsspalte eine Zeichenfolge zuzuweisen, die nicht dem Standardformat entspricht, gibt Oracle wahrscheinlich eine Fehlermeldung zurück. Wenn beispielsweise die beiden ersten Ziffern größer als 31 sind, gibt Oracle immer den Fehlercode ORA-01847 zurück. Wenn, eine englischsprachige Umgebung vorausgesetzt, die Abkürzung für den Monat nicht jan, feb, mar, may, jun, jul, aug, sep, oct, nov oder dec ist, gibt Oracle den Fehlercode ORA-01843 zurück. Wenn der Tag des Monats nicht innerhalb des gültigen Bereichs für den jeweiligen Monat ist, gibt Oracle den Fehlercode ORA01839 zurück. Tabelle 8.2 enthält eine Liste der Fehlercodes, die im Zusammenhang mit der Bearbeitung von Datumswerten stehen.
Umwandlung von Datumsangaben in Zeichenketten Sie müssen sich merken, dass eine Datumsspalte ein Datumswert bleibt, es sei denn, Sie wandeln den Wert mit einer SQL-Funktion in einen anderen Datentyp um. Wenn Sie den Wert einer Datumsspalte in eine Zeichenkette umwandeln wollen, verwenden Sie die integrierte Funktion to_char.
Die Syntax ist: TO_CHAR(date_value [, date_format [,'nlsparam']]) Die Bedeutungen der Platzhalter sind: ●
●
●
date_value ist ein Datumsliteral, ein Datumswert aus einer Spalte oder ein von einer integrierten Funktion zurückgegebener Datumswert. date_format ist ein gültiges Datumsformat von Oracle. Wenn Sie kein Datumsformat angeben, wird das Standard-Datumsformat der Datenbanksitzung verwendet. nlsparam steuert den NLS Kontext. Monatsnamen hängen von der eingestellten Sprache ab. Wenn Sie nlsparam z.B. den Wert 'NLS_DATE_LANGUAGE=German' geben, und im date_format mit Mon arbeiten, werden deutsche Monatsnamen zurückgegeben.
Listing 8.20 zeigt, wie eine Abfrage die Funktion to_char verwendet, um das Einstellungsdatum eines Angestellten im Format month dd, yyyy zurückzugeben. Listing 8.20: Umwandlung einer Datumsangabe in eine Zeichenfolge SQL> select Last_Name, First_Name, to_char(Hire_Date,'MONTH DD, YYYY') H_DATE from Employee order by Hire_Date; LAST_NAME FIRST_NAME
H_DATE
--------------SMITH HERNANDEZ GLEASON
---------JEAN RANDY PAUL
-----------------APRIL 10, 1982 NOVEMBER 18, 1983 APRIL 05, 1984
Umwandlung von Zeichenfolgen in Datumsangaben Es ist nicht überraschend, dass die Umwandlung von Zeichenfolgen in Datumsangaben der Umwandlung von Datumsangaben in Zeichenfolgen ähnelt. Anstatt der integrierten Funktion to_char verwenden Sie die integrierten Funktionen to_date oder to_timestamp, wenn Sie einen Datumswert unter Verwendung eines angegebenen Formats angeben wollen.
Die Syntax der Funktionen to_date und to_timestamp ist: TO_DATE (string_value [, format [, 'nlsparam']]) TO_TIMESTAMP (string_value [, format [, 'nlsparam']]) Die Bedeutungen der Platzhalter sind: ●
●
●
string_value ist ein Zeichenkettenliteral, eine Zeichenkette aus einer Spalte oder eine von einer Funktion zurückgegebene Zeichenkette. format ist eine gültige Formatangabe für den Datentyp date oder timestamp. Wenn Sie kein Format angeben, wird das Standard-Datumsformat der Datenbanksitzung verwendet. nlsparam steuert den NLS Kontext. Monatsnamen hängen von der eingestellten Sprache ab. Wenn Sie nlsparam z.B. den Wert 'NLS_DATE_LANGUAGE=German' geben, und im Format mit Mon arbeiten, werden deutsche Monatsnamen erwartet.
Wenn Sie beispielsweise eine Zeichenfolge konvertieren wollen, die nicht das Standarddatumsformat von Oracle verwendet (dd-mon-yy), verwenden Sie die Funktion to_date. Listing 8.21 zeigt, wie man eine Abfrage verwendet, um die Zahl der Tage zu bestimmen, die seit der amerikanischen Zweihundertjahrfeier vergangen sind. Listing 8.21: Ein Beispiel für eine Datumsberechnung SQL> select SYSDATE - TO_DATE('07-04-1976','MM-DD-YYYY') 2 from DUAL; SYSDATE-TO_DATE('07-04-1976','MM-DD-YYYY') -----------------------------------------6878.9465
Datums- und Zeitangaben Denken Sie daran, dass jede Spalte, die unter Verwendung des Datentyps date definiert wird, sowohl das Datum als auch die Uhrzeit enthält. Wenn Sie nur die Uhrzeit angeben, wird das Datum auf den Ersten des aktuellen Monats gesetzt. Wenn Sie nur das Datum angeben, wird die Uhrzeit auf 00:00 Uhr gesetzt. Listing 8.22 zeigt die Verwendung eines gebräuchlichen Formats für Zeitangaben - hh:mi:ss. Listing 8.22: Verwendung eines Datumsformats in einem Aufruf der Funktion to_char
SQL> select Employee_ID, to_char(Time_Clocked_In,'HH:MI:SS') Time_Clocked_In 2 from Time_Clock 3 order by Employee_ID; EMPLOYEE_ID TIME_CLOCKED_IN ----------- --------------1002 09:02:03 1003 08:51:12 1004 08:59:33 1005 09:22:12 Denken Sie daran, dass der Oracle-Datentyp date die Uhrzeit bis auf eine Sekunde genau speichern kann. Wenn Sie ein 24-Stunden-Format für die Zeit verwenden wollen, sollten Sie das Format 'HH24:MI:SS' verwenden.
Es ist leicht, Monate und Minuten in Datums- und Zeitformaten durcheinander zubringen. Oracle akzeptiert beispielsweise die folgende insert-Anweisung, obwohl es nicht das ist, was Sie im Sinn hatten (MM anstatt MI): INSERT INTO EMPLOYEE (EMPLOYEE_ID, START_TIME) VALUES (1033,to_date('08:05','HH24:MM')); Oracle wird diese Anweisung folgendermaßen interpretieren: Die Startzeit für den Angestellten Nummer 1033 wird auf 8:00 morgens gesetzt, der Monat auf Mai. Da MM immer zwischen 1 und 12 liegt, akzeptiert Oracle den übergebenen Wert immer, auch wenn es nicht das ist, was Sie vorhatten.
Berechnen des Zeitraums zwischen zwei Datumsangaben Ein weiterer Vorteil der Verwendung des Oracle-Datentyps date ist, dass er Datumsarithmetik unterstützt. Sie können beispielsweise Tage zu einem existierenden Datum hinzuaddieren oder davon abziehen: select sysdate + 7 from dual; Indem Sie 7 zu sysdate hinzuaddieren, erhalten Sie ein Datum eine Woche nach dem aktuellen Datum. In ähnlicher Weise können Sie Tage von einem Datum abziehen, um ein früheres Datum zu berechnen.
Speicherung von Zahlen in Oracle Oracle speichert Zahlen nicht in derselben Weise wie Programmiersprachen, etwa C oder Fortran. In C benötigt beispielsweise eine Fließkommazahl unabhängig von ihrem Wert immer denselben Speicherplatz, während in Oracle die Anzahl der Bytes, die für die Speicherung einer Zahl verwendet werden, von ihrer Gesamtstellenzahl abhängt. Um diese Tatsache zu verdeutlichen, können Sie die in Oracle integrierte Funktion vsize verwenden, welche die Anzahl der von ihrem Argument belegten Bytes zurückgibt. Listing 8.23 enthält die Abfrage einer Tabelle, die eine numerische Spalte namens Num_Value besitzt. Rechts vom Spaltenwert ist vsize(Num_Value) angegeben - diese Funktion liefert die Anzahl der Bytes, die
verwendet werden, um Num_Value zu speichern. Oracle kann zwei Ziffern einer Zahl in einem Byte speichern. Ein weiteres Byte wird verwendet, um das Vorzeichen und den Exponenten zu speichern. Listing 8.23: Bestimmung des von einer Zahl benötigten Speicherplatzes SQL> select Num_Value, vsize(Num_Value) 2 from Number_Demo; NUM_VALUE VSIZE(NUM_VALUE) --------- ---------------123 3 1234 3 12345 4 123456 4 1234567 5 12345678 5 123456789 6 12345.679 6
Umwandlung einer Zahl in eine Zeichenfolge Es gibt zwei typische Gründe, einen numerischen Wert in eine Zeichenfolge umzuwandeln: ● ●
Um das Anzeigeformat einer Zahl in einem Formular oder einem Bericht zu ändern Um einen numerischen Ausdruck in einem Formular oder einem Bericht mit einer Zeichenfolge zu verketten
Umwandlung einer Zahl in eine Zeichenfolge mit Hilfe der Funktion to_char Die Funktion to_char wird verwendet, um eine Zahl explizit in eine Zeichenfolge umzuwandeln.
Die Syntax ist: TO_CHAR (number [, format [, 'nlsparam']]) Die Bedeutungen der Platzhalter sind: ● ● ●
number ist ein numerischer Ausdruck, der umgewandelt werden soll. format ist ein optionales Formatmodell, das von to_char verwendet werden soll. nlsparam bestimmt die nationale Sprachumgebung. In Europa wird ein Komma als Dezimaltrennzeichen verwendet, in den USA ein Punkt. Durch Übergabe des Parameters NLS_NUMERIC_CHARACTERS können Sie das Zeichen für den Gruppenseparator (hier der Punkt), der bei 1.000, 1.000.000 usw. eingefügt wird, und das Dezimaltrennzeichen, das die Nachkommastellen abtrennt, explizit angeben. Darüber hinaus werden die Parameter NLS_CURRENCY und NLS_ISO_CURRENCY akzeptiert.
Listing 8.24 enthält ein Beispiel für die Verwendung der Funktion to_char ohne Format. Beachten Sie, dass die erste Spalte - Real_Value - in SQL*Plus rechtsbündig ist, während die zweite Spalte -
to_char(Real_Value) - in SQL*Plus linksbündig ist, da sie eine Zeichenspalte ist. Listing 8.24: Umwandlung einer Zahl in eine Zeichenfolge mit Hilfe der Funktion to_char ohne Formatangabe SQL> select Real_Value, to_char(Real_Value) 2 from Number_Demo; REAL_VALUE TO_CHAR(REAL_VALUE) ---------- ------------------3.1 3.1 Jetzt ist der richtige Zeitpunkt, das Formatmodell von Oracle für Zahlen zu besprechen. Listing 8.25 bis Listing 8.27 zeigen die wichtigsten Elemente der Zahlenformate. Um die Zahl der anzuzeigenden Ziffern anzugeben, verwenden Sie 9 für jede Ziffer. Sie können auch ein Komma und einen Dezimalpunkt zum angegebenen Format hinzufügen. Beachten Sie als Beispiel bitte Listing 8.25. Listing 8.25: Verwendung einer numerischen Formatmaske SQL> select Course_ID, Title, to_char(Additional_Fees, '9,999.99') 2 from Course 3 where 4 Department_ID = 'BIO' 5 order by Course_ID; COURSE TITLE TO_CHAR(A ----- --------------------------------------------------- --------101 INTRO TO BIOLOGY 55.00 177 INVERTEBRATE ANATOMY 65.00 178 MAMMALIAN ANATOMY 70.00 Um eine Zahl mit führenden Nullen anzuzeigen, verwenden Sie 0 am Anfang des Formats, wie in Listing 8.26 gezeigt wird. Listing 8.26: Verwendung einer führenden Null in einer Formatmaske SQL> select Course_ID, Title, to_char(Additional_Fees, '099.99') 2 from Course 3 where 4 Department_ID = 'ECON' 5 order by Course_ID; COURSE TITLE TO_CHAR ----- ----------------------------------------------------- ------101 INTRO TO ECONOMICS 025.00 189 MONETARY POLICY 750.00 199 WORKSHOP ON MARX 000.00 Um ein führendes Dollarzeichen anzuzeigen, beginnen Sie das Format mit $ (siehe Listing 8.27). Listing 8.27: Angabe eines Dollarzeichens in einer Formatmaske SQL> select Course_ID, Title, to_char(Additional_Fees, '$999.99') 2 from Course
3 where 4 Department_ID = 'ECON' 5 order by Course_ID; COURS TITLE ----- ---------------------------------------------------101 INTRO TO ECONOMICS 189 MONETARY POLICY 199 WORKSHOP ON MARX
TO_CHAR( -------$25.00 $750.00 $.00
Wenn Sie wollen, dass eine Zahl in wissenschaftlicher Schreibweise erscheint, geben Sie nach der Stellenzahl eeee an (siehe Listing 8.28). Listing 8.28: Festlegung der wissenschaftlichen Notation in einer Formatmaske SQL> select Num_Value, to_char(Num_Value,'9.9999EEEE') 2 from Number_Demo 3 order by Num_Value; NUM_VALUE TO_CHAR(NUM_ --------- -----------123 1.2300E+02 1234 1.2340E+03 12345 1.2345E+04 12345.679 1.2346E+04
Umwandlung einer Zeichenfolge in eine Zahl Die Funktion to_number ist das Gegenstück zu to_char: Sie konvertiert einen Zeichenausdruck in eine Zahl mit dem angegebenen Format.
Die Syntax für to_number ist TO_NUMBER (string [, format [, nlsparam]]) Die Bedeutungen der Platzhalter sind: ● ● ●
string ist ein Zeichenausdruck, der konvertiert werden soll. format ist ein optionales Formatmodell, das to_number verwenden soll. nlsparam ist identisch wie bei to_char für die Konvertierung von Zahlen.
To_number verwendet dasselbe Formatmodell wie to_char. Listing 8.29 zeigt, wie man einen Zeichenwert, der Dividenden repräsentiert, in eine Zahl umwandelt. Listing 8.29: Verwendung der Funktion to_number SQL> update Security_Price set Last_Qtr_EPS = to_number('$0.14','$9.99') where Symbol = 'ORCL'; 1 row updated. SQL> select Symbol, Last_Qtr_EPS
from Security_Price where Symbol = 'ORCL'; SYMBOL LAST_QTR_EPS ------ -----------ORCL 0.14
Runden und Abschneiden von Zahlen Oracle bietet vier integrierte Funktionen für das Runden und Abschneiden der Nachkommastellen von Kommazahlen.
ROUND(value,[scale]) TRUNC(value,[scale]) FLOOR(value) CEIL(value) Bei diesen Funktionen ist value ein numerischer Ausdruck, scale ist ein optionales Argument, das die Anzahl der Nachkommastellen angibt, die beim Runden oder Abschneiden erhalten bleiben soll. (Die Voreinstellung ist 0). Scale kann negativ sein, um links vom Dezimalkomma zu runden. Die folgenden Beispiele zeigen Ihnen die Verwendung jeder Funktion.
Round Die Funktion round hat zwei Argumente: einen numerischen Ausdruck und eine optionale Anzahl von Nachkommastellen, die das Rundungsergebnis haben soll. Wenn das zweite Argument nicht angegeben wird, gibt round den Wert seines Arguments auf die nächste ganze Zahl gerundet zurück. Round kann mit numerischen Literalen verwendet werden, wie in Listing 8.30 gezeigt wird. Listing 8.30: Verwendung der Funktion round SQL> select round(123.2) from dual; ROUND(123.2) -----------123 SQL> select round(123.27,1) from dual; ROUND(123.27,1) --------------123.3 SQL> select round(101.8,-1) from dual; ROUND(101.8,-1) -----------100
Trunc Die Funktion trunc ähnelt der Funktion round. Anstatt auf die nächste ganze Zahl zu runden, beschneidet trunc auf die angegebene Anzahl Nachkommastellen. Sie können trunc mit einem numerischen Literal verwenden, wie in Listing 8.31 gezeigt wird.
Listing 8.31: Abschneiden einer Zahl SQL> select trunc(123.33), trunc(123.567,2) from dual; TRUNC(123.33) TRUNC(123.567,2) ------------- ---------------123 123.56
Floor Die Funktion floor ist fast identisch mit der Funktion trunc, nur kann floor nicht auf eine Kommazahl abschneiden. Die Funktion floor gibt die ganze Zahl zurück, die kleiner oder gleich ihrem numerischen Argument ist, wie in Listing 8.32 gezeigt wird. Listing 8.32: Verwendung der Funktion floor SQL> select floor(128.3), floor(129.8) from dual; FLOOR(128.3) FLOOR(129.8) ------------ -----------128 129
Ceil Die Funktion ceil gibt die Aufrundungszahl für ihr numerisches Argument zurück - die kleinste ganze Zahl, die größer oder gleich ihrem Argument ist. Betrachten Sie das Beispiel in Listing 8.33. Listing 8.33: Verwendung der Funktion ceil SQL> select ceil(128.3), ceil(129.8) from dual; CEIL(128.3) CEIL(129.8) ----------- ----------129 130
Ermitteln des größten und des kleinsten Werts Sie können die Funktionen max und min verwenden, um den größten und den kleinsten Wert für eine bestimmte Spalte einer Tabelle zu erhalten. Technisch gesehen sind max und min Gruppenfunktionen. Sie müssen jedoch nicht die group by-Klausel einer select- Anweisung verwenden, um diese Funktionen zu verwenden. Als Beispiel zeigt Listing 8.34, wie man den größten und den kleinsten Wert für Additional_Fees aus der Tabelle Course erhält. Listing 8.34: Verwendung der Funktionen min und max SQL> select min(Additional_Fees), max(Additional_Fees) from Course; MIN(ADDITIONAL_FEES) MAX(ADDITIONAL_FEES) -------------------- -------------------0 750
Ermitteln, ob ein Wert NULL ist Wenn Sie eine Oracle-Anwendung entwickeln, kommen Sie in Situationen, in denen der Bildschirm oder ein Bericht Informationen über eine Spalte zeigt, die NULL sein kann. Wenn Sie anstatt der NULL einen
bestimmten Wert zurückgeben wollen, können Sie die Funktion nvl verwenden, um die Ersetzung vorzunehmen.
Hier ist die Syntax: NVL (column_value, substitute_value) Die Bedeutungen der Platzhalter sind. ● ●
column_value ist der Spaltenwert, der ausgewertet werden soll. substitute_value ist der Wert, den die Funktion nvl zurückgeben soll, wenn column_value den Wert NULL hat.
Listing 8.35 zeigt ein Beispiel. Stellen Sie sich vor, wir hätten die Tabelle Course so modifiziert, dass Additional_Fees auf NULL gesetzt wird, wenn es den Wert numerisch Null (0) hat. Als nächstes fragen Sie die Tabelle Course ab und sehen, dass viele Zeilen keinen Wert für Additional_Fees haben. Danach fragen Sie die Tabelle ab, wobei Sie die Funktion nvl so verwenden, dass sie den Betrag Null zurückgibt, wenn Additional_Fees den Wert NULL hat. Listing 8.35: Verwendung der Funktion nvl SQL> update course set additional_fees = null where additional_fees = 0; SQL> set null 'n/a' SQL> select Department_ID, Course_ID, Additional_Fees from Course order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID ADDITIONAL_FEES ------------- ---------- --------------1000 10002 n/a 1000 10003 n/a 1001 10011 25 SQL> select Department_ID, Course_ID, nvl(Additional_Fees,0) from Course order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID NVL(ADDITIONAL_FEES,0) ------------- ---------- ---------------------1000 10002 0 1000 10003 0 1001 10011 25
8.3 Zusammenfassung Diese Lektion hat Ihnen gezeigt, wie man eine Reihe von Operatoren und integrierten Funktionen mit Zeichenfolgen, numerischen Werten und Datums-/Zeitwerten verwendet. Einige der integrierten Funktionen für Zeichenfolgen sind: ●
substr gibt den angegebenen Teil einer Zeichenfolge zurück.
● ● ●
● ● ● ● ● ●
length gibt die Länge einer Zeichenfolge zurück. lpad und rpad füllen Zeichenfolgen von links bzw. rechts mit Zeichenfolgen auf. ltrim und rtrim entfernen führende und nachfolgende Leerzeichen und andere Zeichen. Trim entfernt führende und nachfolgende Zeichen. replace ersetzt eine Zeichenfolge durch eine andere. lower, upper und initcap verändern die Groß- und Kleinschreibung in einer Zeichenfolge. decode übersetzt Spaltenwerte. dump gibt die Kodierung einer Zeichenkette im Datenbankzeichensatz zurück. instr liefert die Zeichenposition, an der ein Muster in einer Zeichenkette gefunden wird. substr extrahiert eine Anzahl von Zeichen aus einer Zeichenkette
Zusätzlich gab es in dieser Lektion die folgenden Informationen über Datums- und Zeitwerte: ●
●
● ● ● ●
sysdate ist eine Funktion, die das aktuelle Datum und die aktuelle Uhrzeit bis auf eine Sekunde genau zurückgibt. systimestamp ist eine Funktion, die das aktuelle Datum und die aktuelle Uhrzeit bis auf eine Hunderttausendstel-Sekunde genau zurückgibt. Das Standarddatumsformat von Oracle ist DD-Mon-YY, z.B. 01-Jan-02 für den ersten Januar 2002. Die Funktion to_char konvertiert Datumswerte in Zeichenfolgen. Die Funktion to_date konvertiert Zeichenketten in Datumswerte. Die Funktion to_timestamp konvertiert Zeichenketten in den Datentyp timestamp.
Denken Sie an die folgenden Konzepte, wenn Sie SQL-Anweisungen für Ihre Anwendung erstellen. ● ● ●
●
Sie können numerische Ausdrücke mit der Funktion to_char in Zeichenfolgen umwandeln. Sie können Zeichenfolgen mit der Funktion to_number in Zahlen umwandeln. max und min sind Gruppenfunktionen, die den Maximal- und Minimalwert für eine bestimmte Spalte oder einen bestimmten Ausdruck aus einer Menge von Zeilen liefern. Die Funktion nvl gibt einen anderen Wert - wie 0 oder 'n/a' - für den NULL-Wert zurück.
8.4 Wie geht es weiter? Am Tag 9 üben Sie das Verknüpfen von Tabellen mittels der Join Operation sowie das Sortieren und Gruppieren von Datensätzen.
8.5 Fragen und Antworten Frage: Können die integrierten Funktionen von Oracle nur mit SQL*Plus oder SQL*Plus Worksheet verwendet werden? Antwort: Nein. Sie können die integrierten Funktionen mit jedem Werkzeug verwenden, das es Ihnen ermöglicht, SQL-Anweisungen einzugeben - z.B Oracle Forms oder PowerBuilder.
8.6 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test
1. Erstellen Sie eine SQL-Anweisung, die Zeilen der Tabelle Instructor wie im folgenden Beispiel gezeigt liefert: Professor Parker 2. Erstellen Sie eine SQL-Anweisung, die den Instructor liefert, dessen Nachname der erste in der alphabetischen Reihenfolge ist.
Übungen Erzeugen Sie eine Tabelle mit Namen employee mit den Spalten name (Zeichenkette) und hiredate (Datum). Erzeugen Sie eine Zeile in der Tabelle, wobei Sie in die Spalte Name das Literal 'Damasio' und in die Spalte hiredate das geeignet konvertierte Literal '01 März 2001' einfügen. Machen Sie die Änderung an der Tabelle dauerhaft.
Tag 8 SQL-Funktionen 229 Tag 9 Fortgeschrittene Abfragen mit SQL 263 Tag 10 Datenbanksicherheit und Optimierung 301 Tag 11 Tabellen und Indizes für Fortgeschrittene 329 Tag 12 Programmierung einer Oracle-Datenbank mit PL/SQL 367 Tag 13 Programmentwicklung mit PL/SQL 395 Tag 14 Weitere PL/SQL-Programmiertechniken 429 In der zweiten Woche werden Sie näher in SQL und seine Funktionen eingeführt. Sie erhalten eine Einführung in die Sicherheitskonzepte von Oracle und die Optimierung von SQL. Die Verwendung von PL/ SQL, Oracles prozeduraler Spracherweiterung von SQL, wird in dieser Woche ebenfalls besprochen. Hier die 2. Woche im Überblick: ●
●
●
●
●
Tag 8, »SQL-Funktionen« Diese Lektion beschreibt viele der integrierten Funktionen, die in einer SQLAnweisung verwendet werden können. Sie werden viele Beispiele für Funktionen, die Zeichenketten, numerische Werte und Datumswerte verarbeiten, kennen lernen. Diese Funktionen sorgen für bemerkenswerte Flexibilität. Tag 9, »Fortgeschrittene Abfragen mit SQL« In dieser Lektion lernen Sie, wie man Informationen aus zwei oder mehr verknüpften Tabellen mittels SQL-Operationen abfragt. Tag 10, »Datenbanksicherheit und Optimierung« Erfassen Sie die Sicherheitskonzepte der Oracle9i Datenbank. Verwenden Sie Tuning Werkzeuge wie EXPLAIN und TKPROF. Tag 11, »Tabellen und Indizes für Fortgeschrittene« In dieser Lektion lernen Sie die Partitioning Option und zusätzliche Indizierungsmöglichkeiten kennen. Sie verschaffen sich einen Überblick über objektrelationale Merkmale. Tag 12, »Programmierung einer Oracle-Datenbank mit PL/SQL«
●
●
Dies ist die erste von drei Lektionen, die sich auf die Verwendung von PL/SQL, Oracles programmorientierte Erweiterung von SQL, konzentrieren. Sie lernen etwas über die grundlegenden Elemente eines PL/SQL-Unterprogramms und einige der Anweisungen zur Steuerung des Kontrollflusses, die in PL/SQL verwendet werden. Tag 13, »Programmentwicklung mit PL/SQL« Als Fortsetzung des Tag 9 bespricht diese Lektion zusätzliche Konstrukte von PL/SQL wie %type und %rowtype. Sie lernen zudem, wie man ein PL/SQLPaket erzeugt. Tag 14, »Weitere PL/SQL-Programmiertechniken« In dieser Lektion werden viele wichtige Themen angesprochen, unter anderem die Verwendung eines Cursors beim Abfragen von Zeilen, die Verwendung von PL/SQL in Datenbanktriggern und die Behandlung von Ausnahmen (exceptions).
Daten mit SQL ändern Am Tag 6, »Einführung in SQL«, haben wir kurz über den DML (Datenmanipulationssprachen)-Aspekt von SQL gesprochen und uns ausführlich mit der Anweisung select beschäftigt. Die Anweisung select kann die Inhalte von Tabellen nur lesen, sie besitzt nicht die Fähigkeit, Daten zu erzeugen oder zu verändern. Diese Lektion untersucht die Verwendung der drei verbleibenden DML-Anweisungen - insert, update und delete. Wenn Sie eine Anwendung für Oracle entwickeln, werden Sie vielleicht ein Front-End- Werkzeug wie Oracle Forms, Visual Basic oder PowerBuilder verwenden. Diese Umgebungen zur Applikationsentwicklung erzeugen intern viele insert-, update- und delete-Anweisungen. Trotzdem ist es bei fast jeder Anwendung erforderlich, Skripte mit SQL-Anweisungen zu entwickeln. Es kann auch nötig sein, Prozeduren und Funktionen in PL/SQL zu schreiben, die solche Anweisungen enthalten. Ein weiterer Grund zu lernen, wie man Daten mittels SQL verändert, ist das Übertragen und Umwandeln von Daten aus anderen Datenbanken in eine Oracle-Datenbank. Um Fremddaten zu portieren, müssen Sie einen Satz von Skripten entwickeln, die SQL- Anweisungen enthalten, mit denen die Daten »gereinigt« werden - um beispielsweise falschen Code oder Werte zu korrigieren - bevor die Daten in eine Oracle-Datenbank eingefügt werden.
7.1 SQL als Datenmanipulationssprache Am Tag 3, »Logischer Datenbankentwurf«, haben Sie die drei Perspektiven einer Datenbank kennen gelernt: Die Benutzerperspektive, die konzeptionelle Perspektive und die physische Perspektive. Der beste Weg, vollständig zu verstehen, wie SQL Daten modifiziert, ist, sich auf die konzeptionelle Perspektive zu konzentrieren. Denken Sie nur an Tabellen, Spalten und Zeilen, und Sie werden SQL und Oracle schneller beherrschen. Sie sollten sich am Anfang nicht darum kümmern, wie Oracle eine SQLAnweisung ausführt. Konzentrieren Sie sich stattdessen auf die Aufgabe der SQL-Anweisung. Hier ist ein weiterer nützlicher Hinweis für die Verwendung von DML. Wenn Sie über die Auswirkungen einer SQL-Anweisung (insert, update oder delete) nachdenken, versuchen Sie sich vorzustellen, dass eine Gruppe von Zeilen und nicht nur eine einzelne Zeile betroffen ist. Das Erste, was viele Programmierer über Oracle lernen wollen, sind die Interna der Dateiformate und spezielle Codes. Dieser Ansatz ist jedoch falsch! Merken Sie sich folgendes: ● ● ●
Das Format der Datenbankdateien von Oracle ist ein proprietäres Format der Oracle Corporation. Diese Formate und Codes ändern sich möglicherweise mit jeder neuen Version von Oracle. Ihre Anwendung ist leichter zu warten, wenn Sie sich auf einen Industriestandard wie SQL verlassen anstatt auf Ihre Kenntnisse über die Dateiformate von Oracle. Das erleichtert auch das Portieren einer Oracle-Anwendung zu einem anderen RDBMS.
Hinzufügen von Zeilen mit Hilfe der insert-Anweisung
Die insert-Anweisung fügt Zeilen zu einer Tabelle hinzu. Sie können Literale oder Ausdrücke für die Speicherung in einer Tabellenzeile angeben.
Die Bezeichnung insert verleitet manche SQL-Anfänger dazu anzunehmen, dass sie kontrollieren können, wo eine Zeile in eine Tabelle eingefügt wird. Denken Sie daran, dass einer der wichtigen Gründe für die Verwendung relationaler Datenbanken die logische Unabhängigkeit der Daten ist - mit anderen Worten: eine Tabelle hat keine implizite Ordnung. Eine neu eingefügte Zeile wird an einer beliebigen Stelle in eine Tabelle eingefügt.
Die Syntax der insert-Anweisung lautet: INSERT INTO table_name [(column_name1...[, column_nameN])] VALUES (column_value1...[, column_valueN]) Die Variablen sind wie folgt definiert: ● ● ●
table_name ist der Name der Tabelle, in die eine Zeile eingefügt werden soll. column_name1 bis column_nameN sind Spalten der Tabelle table_name. column_value1 bis column_valueN sind Literale oder Ausdrücke. Der Wert von column_valueK wird der Spalte column_nameK (K=1..N) zugewiesen.
Stellen Sie sich beispielsweise vor, Sie wollten zur Kurstabelle einen neuen Kurs hinzufügen. Listing 7.1 enthält die insert-Anweisung, die das bewerkstelligt. Listing 7.1: Beispiel für eine insert-Anweisung SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units, Additional_Fees) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3, 25); 1 row created. Beachten Sie, dass die Anzahl der Spalten in der Liste der Spaltennamen der Anzahl der Literale oder Ausdrücke in den Klammern nach dem Schlüsselwort VALUES entsprechen muss. Listing 7.2 enthält ein Beispiel dafür, was passiert, wenn sechs Spalten, aber nur fünf Literale angegeben werden. Wenn Sie mehr Spaltennamen als Werte angeben, gibt Oracle eine Fehlermeldung zurück. Oracle kann natürlich nicht wissen, welcher Spaltenwert fehlt. Listing 7.2: Es sind mehr Spalten definiert, als in der insert-Anweisung Werte geliefert werden
SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units, Additional_Fees) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 3, 25); values * ERROR at line 3: ORA-00947: not enough values Umgekehrt zeigt Listing 7.3, was geschieht, wenn Sie weniger Spaltennamen als Werte angeben. Listing 7.3: Es sind weniger Spalten definiert, als in der insert-Anweisung Werte geliefert werden SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units) 3 value 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3, 25); values * ERROR at line 3: ORA-00913: too many values Wenn ein Spaltenname in der insert-Anweisung falsch geschrieben wird, gibt Oracle eine Fehlermeldung zurück. In Listing 7.4 wird die Spalte Units fälschlicherweise als Unit geschrieben. Listing 7.4: Ein falscher Spaltenname in einer insert-Anweisung SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Unit) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3); (Course_ID, Department_ID, Title, Description, Unit) * ERROR at line 2: ORA-00904: invalid column name
Wenn Sie insert mit einer langen Liste von Spaltennamen ausführen und Oracle ORA-00947 oder ORA-00913 zurückgibt, ist es Ihre Aufgabe, die Zuordnung durchzuführen und die Liste der Spaltennamen mit der Liste der Wertausdrücke abzustimmen. Wenn Sie das Problem bei der Prüfung immer noch nicht finden können, versuchen Sie die Zahl der Spalten zu verringern und das Problem zu isolieren.
Angabe von Werten in der insert-Anweisung
Jeder Spaltenwert, der in einer insert-Anweisung angegeben wird, muss einen der folgenden Werte aufweisen: ● ●
●
den Wert »nicht verfügbar« bzw. in SQL-Syntax NULL ein numerisches Literal wie 3.14159 oder ein Zeichenkettenliteral in einfachen Anführungszeichen wie 'Goethestraße' einen Ausdruck, der aus Operatoren und Funktionen besteht, wie beispielsweise SUBSTR(Last_Name, 1,4)
In der insert-Anweisung können Sie Literale mit Ausdrücken mischen. Listing 7.5 demonstriert die Verwendung der insert-Anweisung, bei der eine zusätzliche Gebühr für einen Kurs in Höhe von 10 % der Anzahl der Einheiten multipliziert mit 250$ angegeben wird. Listing 7.5: Verwendung eines Ausdrucks in einer insert-Anweisung SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units, Additional_Fees) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3, 0.10 * 3 * 250); 1 row created. SQL> select Additional_Fees 2 from Course 3 where 4 Course_ID = 10045 and 5 Department_ID = 1004; ADDITIONAL_FEES --------------75
Der Datentyp einer Spalte und eines Werts müssen übereinstimmen Mit wenigen Ausnahmen (die wir besprechen werden) müssen der Datentyp für eine Spalte und den dazugehörigen Wert identisch sein. Es ist beispielsweise nicht sinnvoll, eine Zeichenfolge in eine numerische Spalte einzufügen, wie in Listing 7.6 gezeigt wird. Listing 7.6: In einer insert-Anweisung stimmen die Datentypen von Spalten und Werten nicht überein SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units, Additional_Fees) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3, 'Seventy-five dollars'); 3, 'Seventy-five dollars') * ERROR at line 6: ORA-01722: invalid number Im vorhergehenden Beispiel gibt Oracle den Fehlercode ORA-01722 zurück, da die Zeichenfolge
'Seventy-five dollars' nicht in einer als Zahl definierten Spalte gespeichert werden kann. Wenn die Zeichenfolge jedoch eine zulässige Zahl ist, wird die insert- Anweisung erfolgreich ausgeführt, wie in Listing 7.7 gezeigt wird. Dies ist eine Ausnahme von der Regel, dass eine Spalte und der zugewiesene Wert denselben Datentyp haben müssen. Listing 7.7: Automatische Umwandlung einer Zeichenkette, die einen numerischen Wert darstellt, in einen Wert vom Datentyp number innerhalb einer insert-Anweisung SQL> insert into Course 2 (Course_ID, Department_ID, Title, Description, Units, Additional_Fees) 3 values 4 (10045, 1004, 'PSYCH IN FILM', 5 'Seminar on the portrayal of psychologists and psychiatrists in film', 6 3, '75'); 1 row created. Eine andere Ausnahme von dieser Regel betrifft Zeichenfolgen und Datumsangaben. Listing 7.8 zeigt, wie eine Zeichenkette, die dem voreingestellten Datumsformat von Oracle entspricht (DD-Mon-YY), in eine Datumsspalte eingefügt werden kann. Listing 7.8: Einfügen einer Datumsangabe mit dem Standard-Datumsformat SQL> insert into Student_Schedule 2 (Student_ID, Class_ID, Grade, Date_Grade_Assigned) 3 values 4 (10231311, 104200, 'B', '02-JUN-97'); 1 row created. Obwohl 02-JUN-97 ein Zeichenkettenliteral und kein Datumsliteral ist, kann es der Spalte Date_Grade_Assigned zugewiesen werden, weil es das voreingestellte Datumsformat DD- Mon-YY von Oracle verwendet.
INSERT und SQL-Funktionen Oracle9i enthält parameterlose SQL-Funktionen, die gerne bei insert-Anweisungen verwendet werden, da der Zeitpunkt des Einfügens und der Datenbankbenutzer, der die insert-Anweisung ausgeführt hat, mit diesen Funktionen bequem in einer Tabelle festgehalten werden können. Listing 7.9 zeigt die Verwendung dieser Funktionen. ● ●
user - der Oracle-Benutzer, der momentan mit der Oracle-Datenbank verbunden ist sysdate - die aktuelle Zeit und das aktuelle Datum
Sie können den Rückgabewert der Funktion user nur einer Zeichenkettenspalte, den Rückgabewert von sysdate nur einer Datumsspalte zuweisen. Listing 7.9: Verwendung von user und sysdate in einer insert-Anweisung SQL> insert into Student_Schedule 2 (Student_ID, Class_ID, Grade, Date_Grade_Assigned, 3 Created_Username, Created_Timestamp) 4 values 5 (10231311, 104200, 'B', '02-JUN-97',
6 USER, SYSDATE); 1 row created.
Warum Spalten in einer insert-Anweisung angegeben werden sollten Wenn Sie die Syntax für die insert-Anweisung genau betrachten, sehen Sie, dass die Spaltenliste ein optionales Element ist. Darum verwendet Oracle per Voreinstellung alle Spalten, wenn Sie die Namen der Spalten, denen Werte zugewiesen werden sollen, nicht angeben. Außerdem ist die Reihenfolge der Spalten, die Oracle verwendet, die Reihenfolge, in der die Spalten beim Erzeugen der Tabelle angegeben wurden; diese Reihenfolge ist dieselbe, die Sie sehen, wenn Sie in SQL*Plus den Befehl describe auf eine Tabelle anwenden. Wie in Listing 7.10 gezeigt wird, erwartet Oracle, dass die erste Spalte das Gebäude mit dem Klassenraum enthält, wenn der Spaltenname nicht in der insert-Anweisung angegeben wird. Listing 7.10: Probleme, die auftreten, wenn Spaltennamen in einer insert-Anweisung weggelassen werden SQL> desc class_location Name Null? Type ----------- ---CLASS_BUILDING NOT NULL VARCHAR2(25) CLASS_ROOM NOT NULL VARCHAR2(25) SEATING_CAPACITY NUMBER(38) SQL> insert into Class_Location 2 values 3 ('250', 'MACLAREN COMPLEX', 500); 1 row created. SQL> select Class_Building, Class_Room, Seating_Capacity 2 from Class_Location 3 where 4 Class_Building = '250'; CLASS_BUILDING CLASS_ROOM SEATING_CAPACITY --------------------------------------------250 MACLAREN COMPLEX 500 Weil die Spaltennamen in der insert-Anweisung nicht angegeben wurden, sieht der Wert 250, der in Class_Room eingefügt werden sollte, einwandfrei aus - die Zeile wurde erfolgreich in die Tabelle eingefügt. Diese Verwendung der Syntax kann jedoch gefährlich sein. ●
●
●
Die Definition der Tabelle kann sich ändern; die Anzahl der Spalten kann ab- oder zunehmen; als Folge davon schlägt die insert-Anweisung fehl. Die insert-Anweisung kann schwierig zu verstehen sein. Die Werte können nicht voneinander unterscheidbar sein oder aus Versehen verschoben werden. Diese Probleme zurückzuverfolgen, wenn man nicht in der Lage ist, die Spalten visuell mit den entsprechenden Werten abzustimmen, ist schwierig. Die insert-Anweisung kann erfolgreich sein, es können jedoch die falschen Daten in die Tabelle eingefügt werden. Diese Situation kann auftreten, wenn zwei benachbarte Werte mit demselben Datentyp aus Versehen verschoben werden. Dieses Szenario ist die größte der potentiellen Gefahren, weil Ihnen Oracle eventuell keine Fehlermeldung zurückgibt, die darauf hinweisen könnte, dass etwas schiefgelaufen ist.
Eine Unterabfrage in einer insert-Anweisung verwenden Bis jetzt haben die Beispiele gezeigt, wie jede Ausführung der insert-Anweisung eine einzelne Zeile zu einer Tabelle hinzufügen kann. Die folgenden Beispiele zeigen, wie man ein insert durchführt, ohne Literale anzugeben.
Die Syntax der insert-Anweisung mit einer Unterabfrage
Die alternative Form der insert-Anweisung ersetzt die Liste der Spaltenwerte auf folgende Weise mit Hilfe einer select-Anweisung: INSERT INTO table_name [(column_name1... [, column_nameN])] select_statement; Die Bedeutungen der Platzhalter sind: ● ● ●
table_name ist der Name der Tabelle, in die Zeilen eingefügt werden sollen. column_name1 ... column_nameN sind Spaltennamen der Tabelle table_name. select_statement ist eine zulässige select-Anweisung.
Nehmen wir an, dass eine Tabelle verwendet wird, um Informationen über Lehrer zu speichern, die nicht mehr am Flugle-College unterrichten. Im Rahmen der Feier zum hundertjährigen Bestehen des FlugleCollege werden alle früheren Lehrer geehrt, indem man sie zu aktiven Lehrern macht.
Beachten Sie bitte, dass die Tabelle Inactive_Instructor in der Beispieldatenbank nicht vorhanden ist. Um die nichtaktiven Lehrer zur Instructor-Tabelle hinzuzufügen, verwenden Sie eine Unterabfrage, um die Zeilen der Tabelle Inactive_Instructor auszuwählen und sie in die Tabelle Instructor einzufügen, wie dies in Listing 7.11 gezeigt wird. Listing 7.11: Einfügen von Zeilen mit einer Unterabfrage SQL> insert into Instructor 2 (Instructor_ID, Last_Name, First_Name, Department_ID) 3 select Instructor_ID, Last_Name, First_Name, Department_ID 4 from Inactive_Instructor; 5 rows created. Um eine Unterabfrage mit einer insert-Anweisung zu verwenden, muss die Zahl der in der insertAnweisung angegebenen Spalten der Anzahl der Spalten in der select- Anweisung entsprechen.
Erzeugen von Testdaten Wenn Sie eine Datenbankanwendung erzeugen, benötigen Sie Daten, um die Software zu testen. Eine
größere Menge von Testdaten zu erzeugen, kann eine schwierige Aufgabe sein. Zum Glück können Sie die insert-Anweisung verwenden, um die Testdaten zu duplizieren und so die Datenmenge zu vergrößern. Listing 7.12 zeigt, wie Sie eine Unterabfrage verwenden können, um die in einer Tabelle vorhandenen Zeilen in dieselbe Tabelle zu kopieren. Nehmen Sie an, dass die Tabelle Instructor ursprünglich 17 Zeilen enthält. Wenn Sie ein insert mit einer Unterabfrage durchführen, die diese 17 Zeilen liefert, wächst Ihre Tabelle auf 34 Zeilen. Natürlich können Sie dieses Einfügen nur durchführen, wenn Sie den Primärschlüssel zeitweise löschen. Wenn Sie dasselbe insert ein weiteres Mal durchführen, hat die entstehende Tabelle 68 Zeilen. Wie Sie sehen können, verdoppelt sich die Zahl der Zeilen jedes Mal, wenn Sie ein insert ... select durchführen. Listing 7.12: Einfügen von Zeilen in eine Tabelle, indem die bereits vorhandenen Zeilen in dieselbe Tabelle kopiert werden SQL> insert into Instructor 2 (Instructor_ID, Department_ID, Last_Name) 3 select Instructor_ID, Department_ID, Last_Name 4 from Instructor; 17 rows created.
Wenn Sie die Kopiertechnik verwenden, um Testdaten zu erzeugen, wächst die Zahl der Zeilen exponentiell. Wenn Sie mit 100 Zeilen beginnen, enthält die Tabelle 12.800 Zeilen, wenn insert siebenmal durchgeführt wurde. Wenn Sie nicht nach jedem insert ein commit durchführen, kann es sein, dass die Undo-Segmente die nicht festgeschriebenen Transaktionen nicht speichern können und Oracle eine Fehlermeldung zurückgibt. Der Tablespace, in dem die Undo-Segmente vergeblich versucht haben, mehr Speicherplatz zu belegen, wird in der Fehlermeldung angegeben.
Die commit-Anweisung wird verwendet, um alle Veränderungen dauerhaft zu speichern, die der Benutzer seit der letzten commit-Anweisung oder seit dem Beginn der Arbeitssitzung an der Datenbank vorgenommen hat, je nachdem, was weniger weit zurückliegt.
Die rollback-Anweisung wird verwendet, um alle Veränderungen rückgängig zu machen, die der Benutzer seit der letzten commit-Anweisung oder seit dem Beginn der Arbeitssitzung an der Datenbank vorgenommen hat, je nachdem, was weniger weit zurückliegt.
Ändern der Daten mit Hilfe der update-Anweisung Wenn Sie vorhandene Daten in einer Oracle-Datenbank verändern wollen, müssen Sie die update-
Anweisung verwenden. Mit dieser Anwendung können Sie eine oder mehrere Zeilen in einer Tabelle aktualisieren.
Grundlegende Syntax der update-Anweisung Wie bei der insert-Anweisung ist die Syntax der update-Anweisung wesentlich einfacher als die Syntax der select-Anweisung.
Die update-Anweisung hat die folgende Syntax: UPDATE table_name SET column_name1 = expression1 ... [, column_nameN = expressionN] [WHERE condition]; Die Bedeutungen der Platzhalter sind: ● ● ●
●
table_name ist die Tabelle, die aktualisiert werden soll. column_name1 ... column_nameN sind Spaltennamen der zu aktualisierenden Tabelle. expression1 ... expressionN sind gültige SQL-Ausdrücke, deren Werte den Spalten column_name1 ... column_nameN zugewiesen werden. condition steht für einen Ausdruck aus einer oder mehreren gültigen SQL- Bedingungen.
Wie Sie sehen können, verweist die update-Anweisung auf eine einzelne Tabelle und weist mindestens einer Spalte einen Ausdruck zu. Die where-Klausel ist optional. Wenn eine update-Anweisung keine where-Klausel enthält, wird die Änderung an allen Zeilen der Tabelle vorgenommen.
Ändern der Werte von mehreren Spalten Wie die Syntax der update-Anweisung zeigt, kann eine update-Anweisung den Wert für mehr als eine Spalte in einer Tabelle ändern. Listing 7.13 zeigt, wie eine update- Anweisung Werte an zwei Spalten zuweist: Position und Telephone. Listing 7.13: Änderung des Werts in mehreren Spalten durch eine update-Anweisung SQL> update Instructor set Position = 'PROFESSOR', Telephone = '8055551212' where instructor_id=612; 1 row updated.
In Mengen denken, nicht in Datensätzen Ein Weg, zu demonstrieren, dass SQL mengenorientiert ist, ist eine update-Anweisung, die einen Wert zwischen zwei Spalten vertauscht. Listing 7.14 enthält eine Abfrage, die aktuelle Werte für Instructor_ID, Telephone und Fax aus der Tabelle Instructor auswählt. Sie können die Telefon- und Faxnummern in der Tabelle Instructor mit einer einzigen
update-Anweisung vertauschen. Sie müssen die Telefon- und Faxnummern nicht in temporären Variablen speichern, wie Sie das tun müssten, wenn Sie eine konventionelle Programmiersprache verwenden würden, um diese Spalten zu vertauschen. Die zweite Abfrage der Tabelle Instructor zeigt, dass das Vertauschen der Spalten erfolgreich war. Listing 7.14: Veranschaulichung der Ausrichtung von SQL auf Mengen SQL> set null 'k.A.' SQL> select Instructor_ID, Telephone, Fax from Instructor; INSTRUCTOR_ID TELEPHONE FAX ------------- ---------- ---------101 8055550123 8055550321 131 k.A. k.A. 149 k.A. k.A. 201 8055550131 8055559444 SQL> update Instructor set Telephone = Fax, Fax = Telephone; SQL> select Instructor_ID, Telephone, Fax from Instructor; INSTRUCTOR_ID TELEPHONE FAX ------------- ---------- ---------101 8055550321 8055550123 131 k.A. k.A. 149 k.A. k.A. 201 8055559444 8055550131
Eine Unterabfrage in einer update-Anweisung verwenden Bis jetzt wurde in diesem Kapitel gezeigt, wie man Bedingungen in einer where-Klausel verwendet, um die Zahl der von einer update-Anweisung betroffenen Spalten einzuschränken. Nun sehen wir, wie eine Unterabfrage mit der update-Anweisung verwendet wird. Stellen Sie sich beispielsweise vor, Sie wollten die zusätzlichen Gebühren für einen Kurs um 10$ vermindern, wenn die zusätzliche Gebühr den Durchschnitt der zusätzlichen Gebühren übersteigt. Dies kann, wie in Listing 7.15 gezeigt, mit einer einzigen update- Anweisung geschehen. Beachten Sie, dass die Unterabfrage in Klammern eingeschlossen werden muss. Listing 7.15: Verwendung einer Unterabfrage in einer update-Anweisung SQL> select Department_ID, Course_ID, Additional_Fees from Course order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID ADDITIONAL_FEES ------------- ---------- --------------1002 10021 55 1003 10032 55 1004 10043 45 1008 10082 45 1008 10084 45 SQL> update Course set Additional_Fees = Additional_Fees - 10
where Additional_Fees > (select avg(Additional_Fees) from Course); 2 rows updated. SQL> select Department_ID, Course_ID, Additional_Fees from Course where course_id in (10021,10032,10043,10082,10084) order by Department_ID, Course_ID; DEPARTMENT_ID COURSE_ID ADDITIONAL_FEES ------------- ---------- --------------1002 10021 45 1003 10032 45 1004 10043 45 1008 10082 45 1008 10084 45
Abgleichen von Daten mit der merge-Anweisung Bisweilen sehen Sie sich der Aufgabe gegenüber, Datenbestände in mehreren Tabellen in einer einzigen Tabelle zu konsolidieren. Anstatt Sie zu zwingen, in einer Programmiersprache Schleifen zu kodieren, die prüfen ob Daten bereits vorhanden sind und dann die vorhandenen Daten ändern und neue Daten einfügen, gibt Ihnen Oracle9i die merge-Anweisung an die Hand, mit der die Aufgabe wesentlich einfacher zu bewerkstelligen ist. Mit merge wählen Sie in einer einzigen SQL Anweisung im ersten Schritt Daten aus einer Tabelle aus, und vergleichen mit einer zweiten Tabelle. Im zweiten Schritt der mergeVerarbeitung wird für die übereinstimmenden Zeilen eine update-Anweisung, für die abweichenden Zeilen eine insert-Anweisung ausgeführt.
Die Syntax der merge-Anweisung ist: MERGE INTO target_table_name [target_table_alias] USING (select_from_source) [source_table_alias] ON (condition) WHEN MATCHED THEN update_target_table WHEN NOT MATCHED THEN insert_target_table; Die Bedeutungen der Platzhalter sind: ● ● ●
● ● ● ●
target_table_name ist der Name der Zieltabelle, die verändert werden soll target_table_alias ist ein optionaler Aliasname für die Zieltabelle select_from_source ist eine select-Anweisung auf die Quelltabelle, welche die mit der Zieltabelle abzugleichenden Daten liefert. source_table_alias ist ein optionaler Aliasname für die Quelltabelle condition ist ein Vergleich zwischen Quell- und Zieltabelle update_target_table ist eine update-Anweisung für die Zieltabelle insert_target_table ist eine insert-Anweisung für die Zieltabelle
Trifft die Bedingung condition zu, wird die update-Klausel ausgeführt. Ist condition falsch, wird die insertKlausel ausgeführt. In der condition dürfen nur Spalten verwendet werden, die auch in
select_from_source enthalten sind. Nehmen wir an, Sie erhalten eine Datei mit aktualisierten Telefonnummern der Professoren, die im nächsten Semester am Flugle College tätig sein werden. Darüber hinaus enthält die Datei auch Professoren, die bisher nicht am Flugle College tätig waren. Sie werden gebeten, die Telefonnummern der vorhandenen Professoren zu aktualisieren und neu eingestellte Professoren in die Tabelle instructor aufzunehmen. Verwenden Sie die merge-Anweisung, gelingt Ihnen diese Aufgabe, ohne dass Sie auf PL/SQL oder eine andere Programmiersprache zurückgreifen müssen. Die Daten liegen in der Tabelle instructor_changes vor. Listing 7.16 zeigt den Inhalt der Tabelle instructor_changes.
Die Tabelle instructor_changes steht Ihnen als Export-Datei \flugle\ instructor_changes.dmp auf der CD-ROM zur Verfügung. Die MERGE Anweisung finden Sie in der Datei sql\merge.sql. Listing 7.16: Daten in der Tabelle instructor_changes SQL> select * from instructor_changes; FIRST_NAME LAST_NAME TELEPHONE DEPARTMENT_ID ---------- ---------- ---------- ------------BESS CHERNOW 805555125 1007 STEVEN CHU 805555127 1008 JAAK PANKSEPP 805555126 1004 Wenn Sie die Inhalte der Tabellen instructor_changes (Listing 7.16) und instructor (Listing 7.17) vergleichen, erkennen Sie, dass instructor_changes die Telefonnummern von zwei Professoren enthält, deren Telefonnummer bisher fehlte, und einen neu eingestellten Professor. Listing 7.17: Auswahl der Professoren aus instructor, die ebenfalls in instructor_changes enthalten sind SQL> select first_name, last_name, telephone, department_id from instructor where last_name in ('CHERNOW','CHU','PANKSEPP'); FIRST_NAME LAST_NAME TELEPHONE DEPARTMENT_ID ---------- ---------- ---------- ------------BESS CHERNOW 1007 STEVEN CHU 1008 Mit der merge-Anweisung in Listing 7.18 konsolidieren Sie die Datenbestände. Listing 7.18: Merge-Anweisung zum Konsolidieren der Daten in zwei Tabellen SQL> MERGE INTO instructor i USING (SELECT first_name, last_name, department_id, telephone FROM instructor_changes) c ON (i.first_name=c.first_name and i.last_name = c.last_name and
i.department_id=c.department_id) WHEN MATCHED THEN UPDATE SET i.telephone = c.telephone WHEN NOT MATCHED THEN INSERT (first_name, last_name, department_id, telephone) VALUES (c.first_name, c.last_name, c.department_id, c.telephone); 3 rows merged. Sie verifizieren die Änderungen wie in Listing 7.19 gezeigt. Listing 7.19: Die merge-Anweisung hat zwei Datensätze in der Tabelle instructor geändert und einen Datensatz eingefügt SQL> select * from instructor_changes; FIRST_NAME LAST_NAME TELEPHONE DEPARTMENT_ID ---------- ---------- ---------- ------------BESS CHERNOW 805555125 1007 STEVEN CHU 805555127 1008 JAAK PANKSEPP 805555126 1004 Das Einfügen in instructor war möglich, weil ein Trigger auf der Tabelle instructor dafür gesorgt hat, dass dem neuen Professor automatisch eine instructor_id zugeordnet wird. Wie Sie einen solchen Trigger erstellen, erfahren Sie am Tag 14 »Weitere PL/SQL Programmiertechniken«.
Einen Spaltenwert auf NULL setzen Hier folgen die verschiedenen Methoden, mit denen der Wert einer Spalte auf NULL gesetzt werden kann: ●
● ●
Die Spalte bekommt während eines insert keinen Wert zugewiesen, und sie hat keinen Standardwert. Die Spalte bekommt während eines insert explizit den Wert NULL zugewiesen. Die Spalte wird bei einem update explizit auf NULL gesetzt.
Zuweisen von NULL in einer insert-Anweisung Sie können eine Spalte in einer insert-Anweisung explizit auf NULL setzen, wie in Listing 7.20 gezeigt wird. Listing 7.20: Ein Spaltenwert wird in einer insert-Anweisung auf NULL gesetzt SQL> insert into Instructor 2 (Instructor_ID, Department_ID, Last_Name, Telephone) 3 values 4 (331, 1006, 'TORRES', NULL); 1 row created.
Einen Spaltenwert mit Hilfe einer update-Anweisung auf NULL setzen Sie können die update-Anweisung verwenden, um den Wert einer Spalte auf NULL zu setzen. Listing 7.21 zeigt ein Beispiel. Nachdem Sie die Telefonnummer für den Lehrer mit der ID 101 auf NULL gesetzt haben, können Sie überprüfen, dass Oracle die Änderung in der Tat ausführt. Listing 7.21: Ein Spaltenwert wird in einer update-Anweisung auf NULL gesetzt
SQL> select Last_Name, Telephone from Instructor where Instructor_ID = 101; LAST_NAME TELEPHONE ---------------------HITCHCOCK 8055550123 SQL> update Instructor 2 set 3 Telephone = NULL 4 where 5 Instructor_ID = 101; 1 row updated. SQL> select Last_Name, Telephone from Instructor where Instructor_ID = 101; LAST_NAME TELEPHONE ---------------------HITCHCOCK
Standardwerte und NULL Wenn Sie eine Tabelle erzeugen, können Sie eine Spalte als erforderlich angeben, indem Sie nach dem Datentyp der Spalte NOT NULL angeben. Eine erforderliche Spalte muss jedes Mal, wenn eine Zeile in die Tabelle eingefügt wird, einen Wert zugewiesen bekommen. Wenn Sie eine Zeile einfügen, ohne einen Wert für eine erforderliche Spalte anzugeben, gibt Oracle eine Fehlermeldung zurück. Beispielsweise muss jeder Kurs am Flugle- College eine Kurs-ID haben, einer bestimmten Abteilung zugewiesen sein und einen Titel aufweisen. Listing 7.22 enthält eine insert-Anweisung, die versucht, eine Zeile in die Tabelle Course einzufügen, ohne einen Wert für die Spalte Title anzugeben, diese ist als NOT NULL definiert. Listing 7.22: In einer insert-Anweisung ist für eine erforderliche Spalte kein Wert angegeben SQL> insert into Course 2 (Course_ID, Department_ID) 3 values 4 (10032, 1003); insert into Course * ERROR at line 1: ORA-01400: cannot insert NULL into ("FLUGLE"."COURSE"."TITLE")
Löschen von Daten mit Hilfe der delete-Anweisung Die delete-Anweisung entfernt Zeilen aus einer Tabelle. Oracle9i verwendet die Kriterien in der whereKlausel, um zu bestimmen, welche Zeilen gelöscht werden sollen.
Die Syntax der delete-Anweisung
Die delete-Anweisung hat die einfachste Syntax der fünf DML-Anweisungen: DELETE FROM table_name [WHERE condition]; Die Bedeutungen der Platzhalter sind: ● ●
table_name ist die Tabelle, aus der Zeilen gelöscht werden sollen. condition ist eine gültige SQL-Bedingung.
Wenn Sie denken, dass die Syntax von SQL inkonsistent ist, haben Sie Recht. Beispielsweise unterscheidet sich die Syntax der update-Anweisung (update table_name) von der Syntax der deleteAnweisung (delete from table_name). SQL hat viele andere Eigenarten. Wenn Sie die Leistungsfähigkeit von SQL ausnutzen wollen, konzentrieren Sie sich darauf, die Syntax zu erlernen, und arbeiten Sie viele Beispiele durch. Listing 7.23 zeigt ein Beispiel für eine einfache delete-Anweisung. Listing 7.23: Beispiel für eine delete-Anweisung SQL> delete from class where instructor_id=405; 1 row deleted.
Eine delete-Anweisung wird auch erfolgreich ausgeführt, wenn keine Zeilen aus der Tabelle gelöscht werden. SQL> delete from class where instructor_id=999; 0 rows deleted
Entfernen aller Zeilen mit Hilfe der truncate table-Anweisung Wenn Sie eine Anwendung entwerfen, kann es sein, dass Sie alle Zeilen in einer Tabelle entfernen müssen. Wenn die Tabelle viele Zeilen hat, kann es ziemlich ineffizient sein, diese Aufgabe mit der deleteAnweisung zu erledigen, da Oracle die vorherigen Werte in ein Undo Segment schreibt. Als Alternative sollten Sie die truncate table-Anweisung verwenden. Die Anweisung truncate table entfernt Zeilen viel schneller als die delete- Anweisung. Jede Tabelle hat einen so genanntenFüllstandsanzeiger (High Water Mark). Mit dem Füllstandsanzeiger verwaltet Oracle den höchsten jemals erreichten Füllstand einer Tabelle. Die truncate-Anweisung setzt diesen Füllstandsanzeiger auf den Wert für eine leere Tabelle zurück. Damit sind alle Daten entfernt.
Die truncate table-Anweisung wird folgendermaßen verwendet:
TRUNCATE TABLE table_name; Der Platzhalter table_name steht für den Namen der Tabelle, deren Daten gelöscht werden sollen. Vorsicht: Die truncate table-Anweisung ist keine DML-Anweisung, sondern eine DDL- Anweisung. Wenn Sie eine truncate table-Anweisung abschicken, können Sie nicht Ihre Meinung ändern und einen Rollback ausführen, um die verlorenen Zeilen zurückzubekommen, denn Oracle führt bei DDL-Anweisungen implizit ein commit aus. Die einzigen Möglichkeiten, Daten, die mit truncate entfernt wurden, wiederherzustellen, sind entweder die gesamte Datenbank auf einen alten Stand zurückzusetzen oder den Tablespace mit der betroffenen Tabelle auf einen Zeitpunkt vor dem truncate zurückzusetzen (Tablespace Point In Time Recovery).
7.2 Transaktionen Ein anderes leistungsfähiges Konzept, mit dem Sie vertraut sein müssen, ist die Transaktion.
Eine Transaktion ist definiert als eine atomare Arbeitseinheit - entweder alle Änderungen in der Transaktion sind dauerhaft oder keine einzige. Eine Transaktion beginnt mit der ersten Änderung nach dem Aufbau einer Datenbanksitzung. Eine Transaktion endet, wenn eines der folgenden Ereignisse eintritt: ● ●
Eine commit-Anweisung wird ausgeführt. Eine rollback-Anweisung wird ausgeführt.
Mit der ersten Änderung nach einer commit- oder rollback-Anweisung beginnt erneut eine neue Transaktion. Einige Werkzeuge wie SQL*Plus und SQL*Plus Worksheet führen beim Abbau der Datenbankverbindung implizit ein Commit durch.
Sichern der Arbeit mit Hilfe der commit-Anweisung Sie können sich eine Transaktion wie eine Veränderung an einem Dokument vorstellen, die Sie mit Ihrer bevorzugten Textverarbeitung vornehmen. Sie können mehrere Änderungen vornehmen und diese dann rückgängig machen oder das Programm verlassen, ohne die Änderungen zu speichern. Wenn Sie die Textverarbeitung anweisen, die Datei zu speichern, verändern Sie die auf der Festplatte gespeicherte Datei dauerhaft. Das Festschreiben einer Transaktion ähnelt dem Speichern einer Datei in einem Texteditor. Allerdings garantiert Oracle9i, dass Sie bei Verlust der Datei mit einer geeigneten Datensicherung den neuesten Stand wiederherstellen können. Dies ist bei einem Texteditor nicht der Fall. Die commit-Anweisung schreibt eine Transaktion fest. Die commit-Anweisung macht alle Änderungen, die an der Datenbank seit Beginn der Transaktion erfolgt sind, dauerhaft. Sie können ein commit nur für die Änderungen an der Datenbank verwenden, die Sie selbst vorgenommen haben. Die commit-Anweisung, die ein Benutzer eingibt, hat keine Auswirkungen auf Änderungen durch andere Benutzer.
Rückgängig machen von Änderungen mit der rollback- Anweisung
In ähnlicher Weise wie der Befehl »Rückgängig machen« bei einer Textverarbeitung arbeitet eine rollbackAnweisung, mit einer großen Ausnahme: die rollback-Anweisung macht alle Änderungen an der Datenbank rückgängig, die der Benutzer seit dem letzten Commit oder seit dem Beginn der Sitzung vorgenommen hat. Betrachten Sie das Wechselspiel zwischen commit und rollback. Listing 7.24 illustriert dieses Konzept. Eine einfache Abfrage aus table_1 gibt vier Zeilen zurück. Wenn Sie die vier Zeilen aus table_1 löschen und nach dem Löschen eine Abfrage durchführen, gibt diese Abfrage keine Zeilen zurück. Wenn Sie ein rollback durchführen und die Tabelle erneut abfragen, sehen Sie, dass der Zustand der Tabelle vor der delete-Anweisung wiederhergestellt wurde. Listing 7.24: Veranschaulichung von commit und rollback SQL> select * from table_1; TABLE_1_COL ----------99 99 99 99 SQL> delete from table_1; 4 rows deleted. SQL> select * from table_1; no rows selected SQL> rollback; Rollback complete. SQL> select * from table_1; TABLE_1_COL ----------99 99 99 99
Oracle führt für DDL-Anweisungen wie create table ein automatisches Commit durch. Alle Änderungen, die Sie vor einer DDL-Anweisung vorgenommen haben, werden ebenfalls automatisch festgeschrieben. Eine Tabelle, die mit einer create table-Anweisung erzeugt wurde, wird durch eine rollback-Anweisung nicht gelöscht. Wenn Sie eine Tabelle löschen wollen, müssen Sie die Anweisung drop table verwenden.
Savepoints Für Transaktionen, die mehrere SQL-Anweisungen umfassen, können Sie Savepoints als Zwischenschritte für die Transaktion verwenden. Ein Savepoint ist eine Markierung innerhalb einer Transaktion, die eine Untermenge der im Verlauf der Transaktion vorgenommenen Änderungen enthält. Listing 7.25 zeigt, wie ein Savepoint deklariert wird; er bekommt den Namen null_fax_numbers.
Sie können sich einen Savepoint als eine Markierung innerhalb einer Transaktion vorstellen, die auf eine Untermenge der in der Transaktion vorgenommenen Änderungen verweist. Listing 7.25: Vereinbarung eines Savepoints SQL> savepoint null_fax_numbers; Savepoint created. Sie können sich bei einer rollback-Anweisung auf einen Savepoint beziehen. Der Savepoint gibt Ihnen die Option, eine Transaktion bis zu einem Zwischenpunkt (einem Savepoint) rückgängig zu machen.
Die Syntax der rollback-Anweisung ist: ROLLBACK [TO savepoint]; Der Platzhalter savepoint steht für einen vorher benannten Savepoint. Betrachten Sie das in Listing 7.26 gezeigte Beispiel. Eine neue Abteilung 'COMPUTER SCIENCE' wird eingefügt. Sie ändern anschließend den Namen der Abteilung fälschlich auf 'COMPUTING'. Der Savepoint nach der insert-Anweisung ermöglicht es Ihnen im Gegensatz zu einem Rollback die Auswirkungen der insert-Anweisung zu erhalten und nur die update-Anweisung rückgängig zu machen. Hätten Sie ein Rollback durchgeführt, wären alle Anweisungen in der Transaktion, also auch die insertAnweisung, rückgängig gemacht worden. Listing 7.26: Beispiel für die Verwendung eines Savepoints SQL> insert into department (department_id, department_name) values (1010,'COMPUTER SCIENCE'); 1 row created. SQL> savepoint after_Insert; Savepoint created. SQL> update department set department_name='COMPUTING' where department_id=1010; 1 row updated. SQL> rollback to after_insert; Rollback complete. SQL> select department_id, department_name from department where department_id=1010; DEPARTMENT_ID DEPARTMENT_NAME ------------- ------------------------1010 COMPUTER SCIENCE Sie sollten Savepoints jedoch mit Vorsicht verwenden, weil Anwendungen dadurch um eine weitere Ebene komplexer werden. Stellen Sie sicher, dass Ihre Transaktionen wohldefiniert sind, bevor Sie sich
dafür entscheiden, Savepoints zu implementieren. Tritt bei der Ausführung einer insert-, update- oder delete-Anweisung ein Fehler auf, werden die Auswirkungen der Anweisung zurückgerollt, d.h. es wird automatisch ein Rollback auf einen impliziten Savepoint vor Beginn der fehlgeschlagenen Anweisung ausgeführt. Die Auswirkungen von Anweisungen in derselben Transaktion, die vor dem Fehler ausgeführt wurden, bleiben erhalten.
7.3 Nebenläufigkeit Nebenläufigkeit bezeichnet in der Informatik das gleichzeitige Arbeiten verschiedener Prozesse auf einem Rechnersystem. Ein Datenbanksystem wie Oracle9i bietet gleichzeitigen Zugang für mehrere Benutzer (Mehrbenutzerbetrieb). Natürlich wollen Sie auch sicher sein, dass ein Benutzer nicht die Änderungen eines anderen Benutzers überschreiben kann. Wenn beispielsweise ein Administrator im Fachbereich für Anthropologie die Beschreibung eines Kurses ändert, sollte ein anderer Administrator nicht in der Lage sein, irgendeine Information über denselben Kurs zu ändern, bis der erste Administrator seine Änderungen festgeschrieben hat. Andererseits sollte die Tatsache, dass ein Administrator in einer Abteilung die Informationen über die von dieser Abteilung angebotenen Kurse ändert, niemanden daran hindern, die Informationen über diese Kurse zu betrachten oder Informationen über einen anderen Kurs zu ändern.
Lesekonsistenz und Nur-Lese-Transaktionen In einer Oracle-Umgebung mit mehreren Benutzern bietet Oracle auf der Ebene der SQL- Anweisungen das, was man »Lesekonsistenz« nennt. Lesekonsistenz bedeutet, dass eine einzelne SQL-Anweisung keine Ergebnisse zurückliefern kann, die widersprüchlich oder inkonsistent sind. Bei Einsatz des Oracle RDBMS ist es unmöglich, Daten zu lesen, die nicht durch COMMIT festgeschrieben wurden. Da schreibende Prozesse lesende Prozesse nicht blockieren, besteht keine Erfordernis dies tun. Außerdem stünde das Lesen nicht festgeschriebener Änderungen im krassen Widerspruch zur Theorie relationaler Datenbanken. Konsequenterweise lässt es Oracle9i nicht zu. Nehmen wir an, dass der oder die Vorsitzende der Abteilung für Biologie die Anzahl der Einheiten für jeden der von dieser Abteilung angebotenen Kurse wissen will. Direkt nachdem die Abfrage des Vorsitzenden an die Datenbank abgeschickt wurde, änderte der Archivar des College alle Kurse mit drei Einheiten auf vier Einheiten. Konsistenz auf der Ebene der Anweisungen bedeutet, dass die Abfrage keine Zeile zurückgeben kann, in der die Zahl der Einheiten auf vier aktualisiert wurde. Je nachdem, wann der Archivar des College seine Änderungen festschreibt, sieht sie der Vorsitzende der Abteilung für Biologie oder er sieht sie nicht. Entscheidend ist der Zeitpunkt, zu dem die Abfrage gestartet wurde. Das Ergebnis jeder Abfrage ist eine lesekonsistente Sicht auf die Daten. Die lesekonsistente Sicht wird mit den Informationen in den Undo Segmenten generiert. Wenn Sie eine Tabelle zweimal abfragen, kann es sein, dass Sie beim zweiten Mal andere Resultate erhalten, nämlich dann, wenn ein anderer Benutzer inzwischen Änderungen mit commit festgeschrieben hat. In einer einzelnen SQL-Anweisung sind niemals teilweise durchgeführte Änderungen sichtbar. Oracle-Datenbanksysteme haben gegenüber anderen RDBMS-Systemen die Besonderheit, dass zum Lesen von Daten keine Sperren gesetzt werden. Dadurch ist Oracle den anderen Systemen im Mehrbenutzerbetrieb überlegen, denn lesende Prozesse behindern schreibende Prozesse nicht.
Sie können in eine Situation kommen, in der Sie Lesekonsistenz für mehr als eine Anweisung benötigen. Es kann in der Tat sein, dass Sie Lesekonsistenz für eine bestimmte Transaktion brauchen. Zu diesem Zweck geben Sie die folgende Anweisung ein:
set transaction read only; Sie erhalten fortan bei jeder Abfrage eine lesekonsistente Sicht für den Zeitpunkt des Absetzens der Anweisung set transaction read only, anstatt für den Startzeitpunkt der folgenden select-Anweisungen. Durch die commit-Anweisung kehren Sie zum Standardverhalten von Oracle zurück.
7.4 Zusammenfassung Die heutige Lektion hat die Verwendung der folgenden grundlegenden SQL-Anweisungen vorgestellt: ● ● ● ● ● ● ● ●
insert fügt eine Zeile zu einer Tabelle hinzu. insert ... select fügt das Ergebnis einer select-Anweisung in eine Tabelle ein. update modifiziert bestehende Zeilen. delete löscht Zeilen aus einer Tabelle. In Verbindung mit diesen Anweisungen können Sie auch Unterabfragen verwenden. merge konsolidiert die Datenbestände zweier Tabellen. Die truncate-Anweisung entfernt alle Zeilen einer Tabelle. Eine Datenbanktransaktion ist eine Menge von Veränderungen an einer oder mehreren Tabellen einer Datenbank, die eine Arbeitseinheit darstellen. Mit Hilfe der commit-Anweisung wird eine Transaktion dauerhaft gemacht. Alternativ können Sie eine rollback-Anweisung verwenden, um Änderungen rückgängig zu machen.
7.5 Wie geht es weiter? Am Tag 8, »SQL Funktionen«, lernen Sie Funktionen für die Bearbeitung von Zahlen, Zeichenketten und Datumswerten kennen.
7.6 Fragen und Antworten Frage: Welche Syntax kennzeichnet den Anfang einer Transaktion? Antwort: Normalerweise keine. Normalerweise beginnt eine Transaktion mit einer beliebigen Änderung an einer Tabelle seit der letzten bestätigten Transaktion oder seit dem Beginn der Datenbanksitzung. Sie müssen auf jeden Fall mit SET TRANSACTION READ ONLY den Anfang einer Read-Only-Transaktion kennzeichnen.
7.7 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Konstruieren Sie eine SQL-Anweisung, die einen Kurs mit den folgenden Eigenschaften hinzufügt: Department_ID = 1002 (BIOLOGY), Course_ID = 137, Title = 'INSECT BEHAVIOR', Description = 'In-depth study of insect societies and their behavior patterns', Units = 3, keine zusätzlichen Gebühren. 2. Konstruieren Sie eine SQL-Anweisung, die zusätzliche Gebühren in Höhe von $50 für alle Kurse in der Abteilung Philosophie einträgt. 3. Konstruieren Sie eine SQL-Anweisung, die eine geplante Klasse löscht, wenn diese in Viling Tower abgehalten werden soll.
Übungen Einige der Lehrer in Flugle-College haben sich entschieden, eine neue Abteilung namens Integrated Studies einzurichten. Als Folge davon werden die Abteilungen für Englisch, Geschichte und Philosophie zusammengelegt, um die Abteilung für Integrated Studies zu bilden. Die Department ID für diese neue Abteilung wird 1009 sein. Erzeugen Sie in der Datenbank eine Abteilung für Integrated Studies (ohne die existierenden Abteilungen zu löschen). Verändern Sie zudem die Tabelle Instructor so, dass die Lehrer in den Abteilungen für Englisch, Geschichte und Philosophie der Abteilung für Integrated Studies zugewiesen werden.
Einführung in SQL Für die Entwicklung einer Oracle-Datenbankanwendung ist es wichtig, dass Sie sich ausreichende Kenntnisse in der strukturierten Abfragesprache SQL aneignen. SQL ist eine leistungsfähige Sprache, die sich von traditionellen Sprachen der dritten Generation (3GLs), wie z.B. C und Pascal, unterscheidet: ●
●
●
Bei SQL handelt es sich um eine nichtprozedurale Sprache. Sie setzen SQL ein, um Oracle mitzuteilen, welche Daten abgerufen oder geändert werden sollen, ohne dabei anzugeben, wie das zu geschehen hat. SQL enthält keine Programmierkonstrukte für den Kontrollfluss, Funktionsdefinitionen, Schleifen oder if-then-else-Anweisungen. Wie Sie am Tag 12, »Programmierung einer OracleDatenbank mit PL/SQL«, sehen werden, stellt Oracle allerdings mit einem Produkt namens PL/SQL prozedurale Spracherweiterungen für SQL bereit. SQL verfügt über eine feste Anzahl Datentypen. Aufbauend auf diesen Datentypen können Sie mit der Oracle Objects Option, die Bestandteil aller Varianten von Oracle9i ist, eigene anwendungsspezifische Datentypen definieren.
Eine Ähnlichkeit zwischen SQL und einer traditionellen Programmiersprache besteht darin, dass Sie im Allgemeinen bei beiden mehrere Möglichkeiten haben, ein und dieselbe Aufgabe zu erledigen besonders im Bereich des Datenabrufs. Unterschiedliche SQL-Anweisungen können zu demselben Ergebnis führen (sich allerdings in Bezug auf Leistungsfähigkeit und Verständlichkeit unterscheiden).
6.1 Abrufen und Ändern von Daten Auf höchster Ebene können SQL-Anweisungen in die drei folgenden Kategorien eingeteilt werden: ●
●
●
Data Manipulation Language (DML, Datenmanipulationssprache), mit der Daten abgerufen oder geändert werden Data Definition Language (DDL, Datendefinitionssprache), mit der Datenbankobjekte definiert werden Data Control Language (DCL, Datenkontrollsprache), mit der die den Datenbankbenutzern erteilten Berechtigungen definiert werden
Die Kategorie DML enthält fünf Grundanweisungen: ● ● ● ●
select, mit der Zeilen einer Tabelle abgerufen werden insert, mit der Zeilen in eine Tabelle eingefügt werden update, mit der vorhandene Zeilen einer Tabelle geändert werden delete, mit der Zeilen aus einer Tabelle entfernt werden
●
merge, zum Ändern einer Tabelle abhängig von Daten in einer zweiten Tabelle. Merge ist eine Kombination aus select, update und insert, die das Zusammenführen von Datenbeständen vereinfacht.
Diese Anweisungen werden meist von Anwendungsentwicklern verwendet. DDL- und DCLAnweisungen werden hauptsächlich von Datenbankentwicklern und Administratoren bei der Erstellung der von einer Anwendung benutzten Datenbankobjekte eingesetzt.
6.2 SQL-Syntax Es folgen einige syntaktische Erfordernisse, die Sie bei der Arbeit mit SQL berücksichtigen müssen. ● ●
●
Jede SQL-Anweisung endet mit einem Semikolon. Eine SQL-Anweisung kann in eine Zeile eingegeben oder der Deutlichkeit halber auf mehrere Zeilen aufgeteilt werden. In den meisten Beispielen dieses Buchs sind die Anweisungen in gut lesbare Abschnitte unterteilt. SQL ignoriert Groß- und Kleinschreibung. Sie können Groß- und Kleinbuchstaben mischen, wenn Sie SQL-Schlüsselwörter (wie z.B. select und insert) sowie Tabellen- und Spaltennamen verwenden.
Bei dem Inhalt einer Spalte wird die Groß- und Kleinschreibung berücksichtigt. Wenn Sie alle Kunden abfragen, deren Nachname mit a beginnt, und alle Namen in Großbuchstaben gespeichert sind, werden überhaupt keine Zeilen abgerufen.
Die meisten Beispiele in diesem Kapitel wurden mit SQL*Plus erstellt. Natürlich können Sie auch jedes andere von Ihnen bevorzugte Werkzeug für die Eingabe von SQLAnweisungen verwenden - z.B. SQL*Plus Worksheet.
6.3 Syntax der select-Anweisung Von den vier DML-Anweisungen wird die select-Anweisung in einer realen Anwendung am häufigsten ausgeführt, da Datensätze im Allgemeinen häufiger gelesen als geändert werden. Eine select-Anweisung kann auch als Unterabfrage in einer update-, insert- oder delete-Anweisung vorkommen, darauf werden wir am Tag 7, »Daten mit SQL ändern«, noch eingehen. Die select-Anweisung ist ein äußerst leistungsfähiges Werkzeug, und ihre Syntax ist aufgrund der zahlreichen Möglichkeiten, Tabellen, Spalten, Funktionen und Operatoren in zulässigen Anweisungen zu kombinieren, kompliziert. Anstatt uns die vollständige Syntax der select-Anweisung anzusehen, beginnen wir in diesem Abschnitt mit einigen grundlegenden Beispielen für eine selectAnweisung. Eine select-Anweisung enthält mindestens zwei Elemente: ● ●
die select-Liste, d.h. eine Liste der abzurufenden Spalten oder Ausdrücke die from-Liste, d.h. die Tabelle(n), aus der Zeilen abgerufen werden sollen
Eine einfache select-Anweisung
Wenn Sie die Beispiele dieser Lektion durcharbeiten möchten, befolgen Sie bitte die Anweisungen im Anhang B. Dort wird die Installation der Beispieldatenbank beschrieben. Eine einfache select-Anweisung - eine Abfrage, mit der nur die Student ID aus der Student-Tabelle abgerufen wird - ist in Listing 6.1 dargestellt. Listing 6.1: Eine erste select-Anweisung SQL> select Student_ID from Student; STUDENT_ID ---------10231300 10231301 10231302 Wenn Sie sowohl den Bezeichner (Student_ID) als auch den Nachnamen (Last_Name) des Studenten abrufen wollen, führen Sie die Spalten einfach in der gewünschten Reihenfolge auf, wie in Listing 6.2 dargestellt. Listing 6.2: Abfragen mehrerer Spalten SQL> select Student_ID, Last_Name from Student; STUDENT_ID LAST_NAME ---------------------10231300 SMYTHE 10231301 HAN 10231302 GORDON Wenn Sie alle Spalten einer Tabelle abrufen möchten, können Sie dazu wie in Abb. 6.1 dargestellt, die Kurzform '(*)' verwenden.
Abbildung 6.1: Abfragen aller Spalten in einer Tabelle mit SQL*Plus
Die select-Liste Falls die select-Liste mehrere Spalten enthält, müssen diese Spalten durch Kommata getrennt werden. Die select-Liste kann außerdem zulässige Ausdrücke enthalten, die wiederum Spalten enthalten können, aber nicht müssen. Außerdem können Sie eine Spalte in einer select-Liste mehrfach verwenden. Die Abfrage in Listing 6.3 ist vollständig zulässig. Listing 6.3: Mehrfaches Abfragen einer Spalte SQL> select Student_ID, Student_ID 2 from Student; STUDENT_ID STUDENT_ID ------------------10231300 10231300 10231301 10231301 10231302 10231302
Sie können arithmetische Berechnungen durchführen, indem Sie den arithmetischen Ausdruck in ein SELECT auf die Tabelle dual einbetten, wie z.B. in select 3.14159*20 from dual; Da dual genau eine Zeile enthält, erfolgt die Ausgabe: 62.8318
Von select zurückgelieferte Werte
Die von der select-Anweisung zurückgelieferten Ergebnisse werden von SQL*Plus oder SQL*Plus Worksheet tabellarisch dargestellt. Jeder abgerufene Datensatz stellt eine Zeile in dieser tabellarischen Ausgabe dar. Die Elemente der select-Liste sind die Spalten der tabellarischen Ausgabe.
Verwendung von Ausdrücken in der select-Liste Zusätzlich zu der Angabe von Spalten können Sie auch Ausdrücke in der select-Liste verwenden. Ausdrücke gehören zu denselben Datentypen wie Spalten - Zeichen, numerische Werte und Datumsinformationen. Durch die Verwendung von Operatoren, integrierten Funktionen und Konstanten können Sie den Bedürfnissen Ihrer Anwendung entsprechende komplexe Ausdrücke konstruieren. Denken Sie daran, dass Oracle jedes Element in der select-Liste als separate Spalte betrachtet, selbst wenn der Ausdruck auf mehrere Spalten verweist.
Arithmetische und logische Operatoren Die arithmetischen und logischen Operatoren von SQL ähneln den Operatoren der Programmiersprache C (siehe Tabelle 6.1). Die Course-Tabelle enthält zum Beispiel die Lerneinheiten (Units) aller Kurse. Wenn die aktuellen Kosten pro Einheit 250$ betragen, erhält man die Kosten für den Kurs mit der in Listing 6.4 gezeigten select-Anweisung. Listing 6.4: Verwendung eines arithmetischen Operators in einer select-Anweisung SQL> select Title, Units*250 2 from Course; TITLE --------------------------------------INTRO TO ANTHROPOLOGY PRE-CALCULUS GENERAL CALCULUS
UNITS*250 --------750 750 750
Anstatt sich die Prioritätsregeln für die in der SQL-Anweisung verwendeten arithmetischen Operatoren zu merken, sollten Sie stets Klammern einsetzen, wenn Sie sich hinsichtlich der richtigen Auswertung eines Ausdrucks unsicher sind.
Zeichenfolgenoperatoren Einer der wichtigsten Zeichenfolgenoperatoren in SQL ist der Verkettungsoperator | |. Die Grammatik von SQL verlangt, dass Zeichenkonstanten in einfache Anführungszeichen zu setzen sind. Mit diesem Operator können Sie zwei oder mehrere Zeichenfolgen verketten, wie in Listing 6.5 dargestellt.
Listing 6.5: Verwendung eines Operators für Zeichenfolgen SQL> select Last_Name || ': ' || Position from Instructor; LAST_NAME||':'||POSITION --------------------------------HITCHCOCK: PROFESSOR DANIELS: ASSOCIATE PROFESSOR EDWARDS: PROFESSOR Es folgen verschiedene Gründe für das Verketten von Zeichenfolgen: ●
Einbetten von Zeichenfolgen in die von einer Abfrage zurückgegebenen Werte. Sie möchten einen Formbrief an Ihre Kunden (Customer) erstellen und darin die Anrede (Salutation) mit einem Leerzeichen und dem Nachnamen des Kunden kombinieren. select Salutation || ' ' || Last_Name from Customer order by Last_Name;
●
●
Kombinieren von Zeichenfolgen. Zur Darstellung für den Benutzer muss Ihre Anwendung möglicherweise eine Teilzeichenkette aus einer Spalte mit einer Teilzeichenkette einer anderen Spalte kombinieren. Erstellen neuer Werte, die einer Spalte zugeordnet werden können. Bei der Implementierung Ihrer Anwendung müssen Sie eventuell vorhandene Daten von einem Format in ein anderes konvertieren.
Sie können den Verkettungsoperator für mehr als zwei Zeichenfolgen verwenden, wie in Listing 6.6 dargestellt. Listing 6.6: Mehrere Verkettungen SQL> select Last_Name || ', ' || Position || ', can be contacted at ' || 2 Email 3 from Instructor; LAST_NAME||','||POSITION||',CANBECONTACTEDAT'||EMAIL -------------------------------------------------------------------HITCHCOCK, PROFESSOR, can be contacted at [email protected] DANIELS, ASSOCIATE PROFESSOR, can be contacted at [email protected] EDWARDS, PROFESSOR, can be contacted at [email protected]
6.4 Festlegen von Kriterien in der where-Klausel Normalerweise rufen Sie nicht alle Zeilen einer Tabelle ab, besonders dann nicht, wenn die Tabelle viele Zeilen enthält. SQL verfügt über eine where-Klausel, mit der Sie nur diejenigen Datensätze abrufen, die bestimmte Kriterien erfüllen. Wenn Sie zum Beispiel eine Liste der Erstsemester (FRESHMAN) des Flugle-Colleges haben möchten, würden Sie Year = 'FRESHMAN' in die whereKlausel einfügen, wie in Listing 6.7 dargestellt.
Listing 6.7: Festlegen eines Kriteriums in einer where-Klausel SQL> select Last_Name, First_Name, Street_Address 2 from Student 3 where 4 Year = 'FRESHMAN'; LAST_NAME FIRST_NAME ------------------------ -----------------------SMYTHE JACKSON REYNOLDS PAULA TANAKA JEFFREY
STREET_ADDRESS ---------------144 OLIVE AVE. 7493 MAPLE ST. 838 PECAN RD.
Kombinieren von Bedingungen mit AND und OR Sie können die Schlüsselwörter AND und OR verwenden, um mehrere von einer Abfrage zu erfüllende Bedingungen zu kombinieren. Um zum Beispiel eine Liste der Studenten im zweiten Jahr (SOPHOMORE) zu erhalten, deren Straße (Street_Address) die Zeichenfolge ash enthält, würden Sie beide Bedingungen in der where-Klausel angeben, wie in Listing 6.8 gezeigt. Listing 6.8: Kombination von Abfragekriterien mit Hilfe von and SQL> select Last_Name, First_Name, Street_Address 2 from Student 3 where 4 Year = 'SOPHOMORE' and 5 Street_Address like '%ASH%'; LAST_NAME FIRST_NAME ------------------------ -----------------------GORDON IVY JACKEL LINDA DEURRE PIERRE PINKWATER PETER
STREET_ADDRESS ---------------544 ASH ST. 493 ASH ST. 555 ASH ST. 533 ASH ST.
Bitte beachten Sie das Wort like in der vierten Zeile des obigen Beispiels. Dieser Operator ist eines der leistungsfähigsten Werkzeuge von SQL.
Die Grundsyntax für die Verwendung des Operators like gestaltet sich folgendermaßen: column_name LIKE 'pattern' Die Bedeutungen der Platzhalter sind wie folgt: ● ●
column_name ist eine gültige Spalte in der von der from-Klausel referenzierten Tabelle. pattern ist ein Muster, nach dem Sie suchen.
Das Zeichen % dient in diesem Zusammenhang als Jokerzeichen; es entspricht keinem oder mehreren Zeichen. Das Zeichen _ (Unterstrich) wird als Jokerzeichen für ein einzelnes Zeichen verwendet.
6.5 Sortieren von Daten in der order by-Klausel Die order by-Klausel legt fest, welche Spalten für das Sortieren eines Abfrageergebnisses verwendet werden sollen. Die order by-Klausel ist optional, aber bitte bedenken Sie: die Reihenfolge, in der Zeilen von einer Abfrage ohne Sortiervorschrift zurückgegeben werden, ist beliebig (ob es sich um eine indizierte Tabelle handelt oder nicht). Daher sollten Sie im Allgemeinen eine order by-Klausel in der select-Anweisung angeben. Eine order by-Klausel in einer selectAnweisung kann jede Spalte einer Tabelle referenzieren. Falls die Spalte geeignet indiziert ist, kann der Sortiervorgang durch Nutzung des Index erheblich beschleunigt werden. Sie brauchen zum Beispiel eine Liste der Kursleiter (Instructor) am Flugle-College, die zuerst nach dem Fachbereichsbezeichner (Department ID) und dann nach dem Nach- (Last_Name) und Vornamen (First_Name) des Kursleiters sortiert ist, wie in Listing 6.9 dargestellt. Listing 6.9: Sortierung abgefragter Zeilen nach mehreren Spalten SQL> select Department_ID, Last_Name, First_Name from Instructor order by Department_ID, Last_Name, First_Name; DEPARTMENT_ID LAST_NAME FIRST_NAME ------------- ------------------------- ------------------------1000 ANGELO ARTHUR 1000 BILLINGS BENJAMIN 1000 RICHARDSON NANCY 1001 PARKER WILLIAM 1002 EDWARDS SAMANTHA 1002 WEISS ROBERTA 1003 DANIELS LAURA
Sie können Spalten in einer order by-Klausel angeben, selbst wenn diese nicht aus der Tabelle ausgewählt werden, wie in diesem Beispiel gezeigt: select Last_Name from Instructor order by Position; Sie können in der order by-Klausel auch Spalten unabhängig davon angeben, ob sie Teil eines Indexes oder einer Tabelle sind. Standardmäßig sortiert Oracle die Zeilen in aufsteigender Reihenfolge. Um die Zeilen in absteigender Reihenfolge zu sortieren, müssen Sie das Schlüsselwort desc (für descending, engl.
absteigend) nach dem Spaltennamen eingeben. Sie können auf- und absteigende Spalten in derselben order by-Klausel nennen, wie in Listing 6.10 dargestellt. Listing 6.10: Sortierung in absteigender Reihenfolge SQL> select Department_ID, Last_Name, First_Name from Instructor order by Department_ID desc, Last_Name, First_Name DEPARTMENT_ID LAST_NAME FIRST_NAME ------------- ------------------------- ------------------------1008 CHU STEVEN 1007 CHERNOW BESS 1006 RESTON RAPHAEL 1006 TORRES PETER 1005 CHANG ROGER
6.6 Zählen von Zeilen in einer Tabelle Wenn Sie wissen möchten, wie viele Zeilen einer Tabelle dem angegebenen Kriterium entsprechen, aber die Zeilen selber nicht abrufen wollen, können Sie die count-Funktion verwenden. count gibt eine einzelne Zeile zurück, welche die Anzahl der dem genannten Kriterium entsprechenden Zeilen nennt. Listing 6.11 zeigt ein Beispiel. Listing 6.11: Zählen der Zeilen in einer Tabelle SQL> select count(*) 2 from Instructor 3 where 4 Position = 'ASSOCIATE PROFESSOR'; COUNT(*) -------7
Bei count handelt es sich um eine Aggregatfunktion (mehr darüber erfahren Sie am Tag 9, »Fortgeschrittener Abfragen mit SQL«). Das Sternchen weist Oracle an, alle dem Kriterium entsprechenden Zeilen zurückzugeben. Statt des Sternchens können Sie auch einen Spaltennamen angeben. Wenn Sie das tun, gibt Oracle allerdings nur jene Zeilen zurück, in denen dem angegebenen Spaltennamen ein Wert zugewiesen wurde (mit anderen Worten, Zeilen, deren Spaltenwert nicht NULL ist).
6.7 Ermitteln von Zeilen mit Spaltenwert NULL Ein großer Unterschied zwischen einem RDBMS und älteren DBMS (Datenbankmanagementsystemen) ist das Konzept des NULL-Werts. Wenn Sie Datensätze aus einer Tabelle abrufen möchten, für die kein Spaltenwert definiert ist, können Sie das Kriterium IS
NULL in der where-Klausel verwenden. Bei Listing 6.12 handelt es sich um eine Abfrage, welche die Namen der Studenten ohne Faxnummer zurückgibt. Listing 6.12: Abfrage der Zeilen, für die eine Spalte den Wert NULL enthält SQL> select Last_Name, First_Name 2 from Student 3 where 4 Fax is null; LAST_NAME FIRST_NAME ------------------------ ------------SMYTHE JACKSON HAN COREY REYNOLDS PAULA Sie können auch den Operator not zum Abrufen von Zeilen verwenden, deren Spalten einen Wert ungleich NULL enthalten. Mit der Abfrage aus Listing 6.13 können Sie zum Beispiel die Anzahl der Studenten mit Faxnummer zählen. Listing 6.13: Abfrage der Zeilen, für die eine Spalte einen Wert ungleich null enthält SQL> select Last_Name, First_Name from Student where Fax is not null; LAST_NAME FIRST_NAME ------------------------ ------------GORDON IVY CLAUSEN THOMAS JACKSON ROBERT Sie sollten sich darüber im Klaren sein, wie NULL-Werte in arithmetischen Operationen verarbeitet werden. Damit Sie sehen können, wie ein NULL-Wert von dem Wert 0 abweicht, betrachten Sie bitte Listing 6.14. Nehmen wir an, Sie haben eine Tabelle mit dem Namen Intelligence (Intelligenz), die zwei Spalten enthält - Last_Name und IQ. Listing 6.14: Abfrage aller Werte für Last_Name und IQ SQL> select Last_Name, IQ from Intelligence; LAST_NAME IQ ---------------------------- ----SMITH 100 GORDON 125 JONES 150 WILSON RICHARDS In diesem Beispiel ist der IQ für Wilson und Richards gleich NULL. SQL*Plus stellt NULL standardmäßig als Leerzeichen dar. Sie können NULL durch eine andere Zeichenkette darstellen, indem Sie den SQL*Plus Befehl set verwenden, z.B. set null 'k.A.' für 'keine Angabe'. Wenn Sie den Durchschnitts-IQ [avg(IQ)] aus den Datensätzen der Intelligence-Tabelle wissen möchten, geben Sie die in Listing 6.15 gezeigte Abfrage ein.
Listing 6.15: Bestimmung des durchschnittlichen IQs aller Zeilen, bei denen der Wert von IQ von NULL verschieden ist SQL> select avg(IQ) from Intelligence; AVG(IQ) --------125 Wie Sie sehen können, wurden die Zeilen, bei denen der Wert von IQ gleich NULL ist, nicht für die Berechnung des Durchschnitts einbezogen. Oracle hat den durchschnittlichen IQ mit der Formel (100 + 125 + 150) / 3 errechnet. Wenn Sie die NULL-Werte in 0 ändern, wird ein anderes Ergebnis erzielt, wie in Listing 6.16 dargestellt.
Um die Spaltenwerte in einer Tabelle zu ändern, verwenden Sie die update-Anweisung. Mehr darüber erfahren Sie am Tag 7. Listing 6.16: IQ wird überall dort auf 0 gesetzt, wo der aktuelle Wert null ist SQL> update Intelligence 2 set IQ = 0 3 where 4 IQ is NULL; 2 rows updated. SQL> select Last_Name, IQ 2 from Intelligence; LAST_NAME IQ ---------------------------- ----SMITH 100 GORDON 125 JONES 150 WILSON 0 RICHARDS 0 SQL> select avg(IQ) from Intelligence; AVG(IQ) --------75
6.8 Suche nach Zeilen mit Hilfe des like-Operators Ein Beispiel für die Verwendung des like-Operators haben Sie bereits gesehen. Oracle- Benutzer setzen den like-Operator zum Durchsuchen einer Tabelle ein, wenn Sie sich hinsichtlich der Schreibweise der gesuchten Daten nicht sicher sind. Nehmen wir an, Sie wurden vom College-Präsidenten gebeten, eine Liste von Kursen
zusammenzustellen, deren Beschreibung das Wort workshop enthält. Sie starten die in Listing 6.17 dargestellte Abfrage. Listing 6.17: Bestimmung der Kurse, bei denen die Beschreibung das Muster workshop enthält SQL> col title format a22 col description format a30 word_wrapped select Title, Description from Course where Description like '%workshop%'; TITLE DESCRIPTION ---------------------- -----------------------------MONETARY POLICY An intense workshop on various monetary policies conducted by modern nations in the 19th and 20th centuries. Includes a field trip to the Federal Reserve Board. MODERN PHILOSOPHY A workshop on recent philosophical debates including bioethics, communication theory, social net theory, and philosophical relativism. Sie bemerken, dass diese Abfrage nicht die Kurse umfasst, deren Beschreibung (Description) mit dem Wort Workshop beginnt, da in diesem Fall der erste Buchstabe ein großes W ist. Aus diesem Grund formulieren Sie die Abfrage, wie in Listing 6.18 dargestellt, um. Listing 6.18: Zeichenfolgenvergleich mit Hilfe der Funktion upper SQL> select Title, Description from Course where upper(Description) like '%WORKSHOP%'; TITLE DESCRIPTION ---------------------- -----------------------------MONETARY POLICY An intense workshop on various monetary policies conducted by modern nations in the 19th and 20th centuries. Includes a field trip to the Federal Reserve Board. MODERN PHILOSOPHY A workshop on recent philosophical debates including bioethics, communication theory, social net theory, and philosophical
relativism. SEMINAR ON THEME ANALY Workshop on the automation of SIS modern critical techniques. Use of algorithms for the detection of theme failure will be introduced. Die Abfrage liefert nun einen zusätzlichen Datensatz zurück. Durch die Anwendung der Funktion upper auf die Spalte Description sind Sie in der Lage, den Inhalt der Spalte zu vergleichen, ohne sich darüber Gedanken machen zu müssen, ob das Muster nun in Groß- oder Kleinbuchstaben zu schreiben ist. Sie können Zeichenkettenmuster durch das Jokerzeichen (Wildcard) % trennen, wie in Listing 6.19 gezeigt. Listing 6.19: Verwendung mehrerer Platzhalter SQL> select Description 2 from Course 3 where 4 upper(Description) like '%STUDY%BEHAVIOR%'; DESCRIPTION -------------------------------------------------------------------------Discussion and field study of the strange behavior of the Nacimera culture Manchmal kommt es allerdings vor, dass Sie eine Zeichenfolge suchen, die tatsächlich das Zeichen % enthält. Normalerweise wird das %-Zeichen in einfachen Anführungszeichen vom Oracle-RDBMS als Jokerzeichen interpretiert. Wenn das Oracle-RDBMS ein Zeichen als Literal interpretieren soll, setzen Sie das Code-Umschaltzeichen vor das betreffende Zeichen. Wie in Listing 6.20 gezeigt, wollen Sie Kursbeschreibungen abrufen, die das Zeichen % enthalten; daher setzen Sie \% zwischen zwei Jokerzeichen. Listing 6.20: Behandlung des Prozentzeichens als Literal mit Hilfe eines CodeUmschaltzeichens SQL> select Description 2 from Course 3 where 4 Description like '%\%%' escape '\'; DESCRIPTION -------------------------------------------------------------------Learn about advanced operations such as % and logarithms. In dieser Abfrage wird der umgekehrte Schrägstrich (\) verwendet, um Oracle mitzuteilen, dass das %-Zeichen nach dem \ als Literal interpretiert werden soll. Die gleiche Methode können Sie auch einsetzen, wenn Sie nach einem Unterstrich suchen wollen, anstatt diesen als Jokerzeichen für ein einzelnes Zeichen einzusetzen.
6.9 Suche nach Zeilen mit dem between-Operator
In dieser Lektion habe ich bereits erläutert, dass select-Anweisungen unterschiedlich strukturiert sein können und dennoch zum selben Ergebnis führen. Der between-Operator ist ein gutes Beispiel für diese Flexibilität. Der between-Operator wählt Werte innerhalb eines Bereichs mit einer unteren und oberen Grenze aus. Obere und untere Grenze sind inklusiv, d.h. Teil das angegebenen Wertebereichs. Der between-Operator funktioniert mit numerischen Werten, Zeichenfolgen und Datumswerten. Nehmen wir zum Beispiel an, dass die aktuellen Kosten einer Lerneinheit am Flugle-College 250$ betragen. Da sich die Kosten für einen Kurs aus der Summe der Kosten pro Einheit und der anfallenden Zusatzkosten zusammensetzt, kann die Liste der Kurse, deren Kosten zwischen 760$ und 800$ liegen, aus der in Listing 6.21 gezeigten Abfrage abgeleitet werden. Listing 6.21: Verwendung des between-Operators SQL> select Title, Units*250 + Additional_Fees from Course where (Units*250 + Additional_Fees) between 760 and 800; TITLE UNITS*250+ADDITIONAL_FEES --------------------------------------- ------------------------ADVANCED ARITHMETIC 760 INTRO TO PSYCHOLOGY 775 ABNORMAL PSYCHOLOGY 770 INTRO TO ECONOMICS 775 INTRO TO CIRCUIT THEORY 795 INTRO TO DYNAMICS 785 6 rows selected. Bei der vorangegangenen Abfrage handelt es sich semantisch um die gleiche wie in Listing 6.22. Listing 6.22: Eine Alternative zur Verwendung des between-Operators SQL> select Title, Units*250 + Additional_Fees from Course where (Units*250 + Additional_Fees) >= 760 and (Units*250 + Additional_Fees) <= 800; TITLE UNITS*250+ADDITIONAL_FEES --------------------------------------- ------------------------ADVANCED ARITHMETIC 760 INTRO TO PSYCHOLOGY 775 ABNORMAL PSYCHOLOGY 770 INTRO TO ECONOMICS 775 INTRO TO CIRCUIT THEORY 795 INTRO TO DYNAMICS 785 6 rows selected. Wie Sie sehen, handelt es sich bei dem between-Operator um das Äquivalent zweier durch AND verbundener Bedingungen. Bei richtiger Anwendung vereinfacht der between-Operator eine
Abfrage.
6.10 Der in-Operator Ein weiterer Operator, der den Wert einer Spalte oder eines Ausdrucks mit einer Liste möglicher Werte vergleicht, ist der in-Operator.
Die Syntax für den in-Operator sieht folgendermaßen aus: expression IN (expression1 ... [, expressionN]) Die Bedeutungen der Platzhalter sind: expression ist ein gültiger SQL-Ausdruck. expression1 bis expressionN stellen eine Liste gültiger SQL-Ausdrücke dar. Der in-Operator gibt einen Booleschen Wert zurück, der entweder true oder false ist: ● ●
true, wenn der Ausdruck einem der Werte in der Liste der Ausdrücke entspricht false, wenn der Ausdruck keinem der Werte in der Liste der Ausdrücke entspricht
Als Beispiel für die Verwendung des in-Operators nehmen wir an, Sie wollten nur die Kursleiter der Fachabteilungen Mathematik, Anthropologie und Psychologie abrufen. Falls die Liste der Werte lang ist, erspart der in-Operator Ihnen eine Reihe von Eingaben sowie dem Oracle-Parser für SQLAnweisungen Verarbeitungszeit. Listing 6.23 zeigt den Code. Listing 6.23: Verwendung des in-Operators SQL> select Last_Name, First_Name, Department_ID from Instructor where Department_ID in (1000,1001,1002); LAST_NAME FIRST_NAME DEPARTMENT_ID ---------------------- ------------- ------------EDWARDS SAMANTHA 1002 ANGELO ARTHUR 1000 RICHARDSON NANCY 1000 PARKER WILLIAM 1001 Eine Alternative zum in-Operator ist in Listing 6.24 dargestellt. Listing 6.24: Eine Alternative zur Verwendung des in-Operators SQL> select Last_Name, First_Name, Department_ID
from Instructor where Department_ID = 1000 or Department_ID = 1001 or Department_ID= 1002; LAST_NAME FIRST_NAME DEPARTMENT_ID ---------------------- ------------- ------------EDWARDS SAMANTHA 1002 ANGELO ARTHUR 1000 RICHARDSON NANCY 1000 PARKER WILLIAM 1001 Sie können das Schlüsselwort not mit dem in-Operator kombinieren, so dass eine Bedingung als true angesehen wird, wenn ein Ausdruck keinem der in der Liste enthaltenen Ausdrücke entspricht. Siehe hierzu Listing 6.25. Listing 6.25: Verwendung des not in-Operators SQL> select Last_Name, First_Name, Department_ID from Instructor where Department_ID not in (1000, 1001, 1002); LAST_NAME FIRST_NAME DEPARTMENT_ID -------------------- ---------------- ------------HITCHCOCK BORIS 1003 DANIELS LAURA 1003 CHANG ROGER 1005 Wenn Sie den not in-Operator nicht verwenden wollen, müssen Sie alle Werte, denen der Ausdruck oder die Spalte nicht entsprechen soll, mit dem logischen Operator and »verketten«. Listing 6.26 zeigt ein Beispiel. Listing 6.26: Eine Alternative zur Verwendung des not in-Operators SQL> select Last_Name, First_Name, Department_ID from Instructor where Department_ID != 1000 and Department_ID != 1001 and Department_ID != 1002; LAST_NAME FIRST_NAME DEPARTMENT_ID -------------------- ---------------- ------------HITCHCOCK BORIS 1003 DANIELS LAURA 1003 CHANG ROGER 1005
6.11 Aliasnamen für Spalten Bei der Angabe eines komplexen Ausdrucks in einer select-Liste können Sie durch Zuordnung eines
Aliasnamens dokumentieren, was der Ausdruck darstellt.
Die Syntax für ein Element der select-Liste gestaltet sich folgendermaßen: expression_name [ [AS] alias_name] Die Bedeutungen der Platzhalter sind wie folgt: ●
●
expression_name ist ein SQL-Ausdruck; im einfachsten Fall ist expression_name ein Spaltenname. alias_name ist ein für die Referenzierung von expression_name in anderen Teilen der selectAnweisung verwendeter Aliasname.
Bitte beachten Sie, dass es sich bei AS um ein optionales Schlüsselwort handelt. Sie können das Beispiel in Listing 6.27 verwenden, um den Einsatz eines Aliasnamens zu üben. Lassen Sie uns noch einmal wiederholen: Die aktuellen Kosten eines Kurses am Flugle-College betragen 250$ pro Einheit plus zusätzliche Kosten, was mit der Abfrage in Listing 6.27 ausgedrückt wird. Listing 6.27: Verwendung eines Aliasnamens in einer select-Anweisung SQL> select Title, Units*250 + Additional_Fees as Course_Cost 2 from Course 3 where 4 Department_ID = 'ECON'; TITLE COURSE_COST --------------------------------------- ----------INTRO TO ECONOMICS 775 MONETARY POLICY 1500 WORKSHOP ON MARX 750 Indem Sie dem Ausdruck Units*250 + Additional_Fees den Alias Course_Cost zuordnen, gewinnen Sie zwei Vorteile: ● ●
Sie haben nun einen Namen, der den Ausdruck genau beschreibt. Sie können in der order by-Klausel auf einen Alias verweisen.
6.12 Eine Unterabfrage verwenden Eine Unterabfrage (Subselect) wird als select-Anweisung definiert, die in einer anderen DMLAnweisung erscheint - einer anderen select-, update-, delete- oder insert- Anweisung. In einer selectAnweisung ist die Unterabfrage Teil einer Bedingung in der where-Klausel. Die folgende Abfrage wählt den Titel aller Kurse, deren zusätzliche Kosten niedriger sind oder den durchschnittlichen Zusatzkosten aller Kurse entsprechen:
Listing 6.28: Eine Unterabfrage select Title from Course where Additional_Fees <= (select avg(Additional_Fees) from Course) Bei der Verwendung von Unterabfragen sollten Sie einige Dinge beachten: ● ●
●
●
Die Unterabfrage muss in Klammern gesetzt werden. Die Anzahl der von der Unterabfrage zurückzuliefernden Zeilen muss der Anzahl der von der Funktion oder dem Operator erwarteten Werte entsprechen. Im vorangegangenen Beispiel erwartet der Operator <= einen zu vergleichenden Einzelwert, und die Funktion avg (bei der es sich um eine Gruppenfunktion handelt) gibt einen Einzelwert zurück. Die Anzahl der von der Unterabfrage zurückgegebenen Spalten muss mit der Anzahl der von der Funktion oder dem Operator erwarteten Spalten übereinstimmen. Die order by-Klausel wird nicht innerhalb einer Unterabfrage eingesetzt.
Erstellen einer neuen Tabelle mit Hilfe der select-Anweisung Betrachten wir eine Form der create table-Anweisung, bei der eine Unterabfrage eingesetzt wird, um die Struktur der zu erstellenden Tabelle anzugeben. Diese Anweisung wird Ihnen bei der Entwicklung und Prüfung Ihrer Anwendung sicherlich als sehr hilfreich erscheinen. Wenn Sie mit dem Inhalt einer Tabelle experimentieren möchten - Hinzufügen, Löschen und Aktualisieren mehrerer Zeilen -, wären Sie gut beraten, eine Kopie der Tabelle anzufertigen, mit der Sie experimentieren möchten. Nehmen wir an, dass es sich bei der vorhandenen Tabelle um eine Liste der Kunden der letzten zehn Jahre handelt, die viele Datensätze - sagen wir 100.000 - enthält. Für Ihre Versuche möchten Sie aber nur eine Teilmenge dieser Zeilen verwenden. Besonders interessiert sind Sie an den in Kalifornien ansässigen Kunden. Sie können eine Tabelle erstellen, die eine Teilmenge aller Kunden (Customer_Subset) enthält, indem Sie die create table-Anweisung mit der select- Anweisung kombinieren, wie in Listing 6.29 dargestellt. Listing 6.29: Erstellung einer Tabelle aus einer Teilmenge von Zeilen einer anderen Tabelle SQL> create table Customer_Subset 2 as 3 select * 4 from Customer 5 where 6 State = 'CA'; Table created.
Beachten Sie, dass Oracle Ihnen nicht mitteilt, wie viele Zeilen in die neue Tabelle eingefügt wurden. Betrachten wir nun die Syntax genau: CREATE TABLE new_table_name AS select_stmt; Die Bedeutungen der Platzhalter sind: new_table_name steht für den Namen der zu erstellenden Tabelle. select_stmt ist eine gültige select-Anweisung. Zusätzlich können Sie die create table- und die select-Anweisung noch für einen anderen Zweck gemeinsam nutzen. Wenn Sie eine weitere Tabelle erstellen wollen, welche dieselbe Struktur wie eine vorhandene Tabelle aufweisen soll - mit denselben Spaltendefinitionen, jedoch ohne die Daten können Sie folgende Anweisung einsetzen: CREATE TABLE my_new_empty_table AS SELECT * FROM existing_table WHERE 1 = 2; Nun werden Sie wahrscheinlich sagen: »1 ist niemals gleich 2.« Das ist richtig. Und aus diesem Grund werden keine der Zeilen aus existing_table in my_new_empty_table kopiert. Die neue Tabelle verfügt über dieselben Spaltendefinitionen wie existing_table, aber nicht über Daten. Sie könnten auch jeden anderen Ausdruck, der immer das Ergebnis false liefert, verwenden, um das Gleiche zu erreichen.
Oracle weist die create table-Anweisung zurück, wenn die Unterabfrage auf eine Spalte vom Typ long oder long raw verweist. Wenn Sie Zeilen aus einer long-Spalte von einer Tabelle in eine andere kopieren müssen, verwenden Sie den Befehl copy von SQL*Plus. Er unterstützt das Kopieren von Spaltenwerten vom Typ long.
Beim Erzeugen einer Tabelle mit CREATE TABLE ... AS SELECT werden nur NOT NULL Integritätsregeln von der Ausgangstabelle übernommen. Wenn Sie wollen, dass die neue Tabelle alle existierenden Integritätsregeln der Ausgangstabelle aufweist, müssen Sie diese mit einer alter table-Anweisung hinzufügen.
6.13 Zusammenfassung In der heutigen Lektion wurden einige Grundlagen von SQL vorgestellt: ●
●
●
●
Bei der Datenmanipulationssprache (DML) handelt es sich um eine Kategorie von SQL, die aus der select-, insert-, update- und der delete-Anweisung besteht. Jede select-Anweisung enthält eine select-Liste und eine from-Klausel, welche die Tabelle bezeichnet, aus der die Datensätze abgerufen werden sollen. Normalerweise enthält die select-Anweisung auch eine where-Klausel, in der die Kriterien angegeben werden, denen die von der select-Anweisung zurückgegebenen Zeilen entsprechen müssen. Die order by-Klausel wird zur Angabe der Spalten eingesetzt, die verwendet werden, um die Sortierreihenfolge festzulegen. Mit dem Operator LIKE können Sie nach Mustern in Zeichenketten suchen.
6.14 Wie geht es weiter? Am Tag 7 lernen Sie, wie Sie andere DML-Anweisungen - insert, update und delete - einsetzen, um den Inhalt einer Tabelle zu ändern. Sie werden erfahren, wie eine select- Anweisung in den updateund delete-Anweisungen referenziert werden kann, um die betreffenden Zeilen zu bestimmen.
6.15 Fragen und Antworten Frage: Kann der between-Operator sowohl für Ausdrücke mit numerischen Werten als auch Zeichenketten verwendet werden? Antwort: Ja. Sie haben bereits ein Beispiel dafür gesehen, wie der between-Operator mit einem numerischen Ausdruck verwendet wird. Listing 6.30 zeigt ein Beispiel für den Einsatz des between-Operators mit einer Zeichenfolge, um eine Liste der Kurstitel zu bekommen, die mit einem Zeichen zwischen S und W beginnen. Bitte beachten Sie, dass die Abfrage in Listing 6.30 keine Titel zurückgeben würde, die mit dem Wort Workshop beginnen, weil diese in der Sortierreihenfolge nach W kommen. Listing 6.30: Anwendung des between-Operators auf Zeichenfolgen SQL> select Title 2 from Course 3 where 4 Title between 'S' and 'W'; TITLE ------------------------------------SEMINAR ON CHAOS SEMINAR ON NACIMERA SEMINAR ON THEME ANALYSIS Frage: Welchen internen Wert verwendet Oracle zur Speicherung eines NULL-Werts für einen Datensatz?
Antwort: Im Allgemeinen können Sie den Wert nicht »sehen«. Außerdem brauchen Sie den Wert eigentlich nicht zu kennen, da Sie eine Spalte nur unter Verwendung eines Vergleichs auf IS NULL oder IS NOT NULL überprüfen können.
6.16 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Versuchen Sie die Fragen des Tests zu beantworten, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Sie müssen eine Spalte in die select-Liste aufnehmen, wenn Sie das Ergebnis der select-Anweisung nach den Werten dieser Spalte Zeilen sortieren möchten. 2. Was ist an dieser Anweisung falsch? select First_Name from Student order by Last_Name where Last_Name like '%IN%'; 3. Richtig oder falsch? Eine Spalte muss zuerst indiziert werden, bevor sie in der order byKlausel angegeben werden kann.
Übungen Konstruieren Sie unter Verwendung der in dieser Lektion behandelten Course-Tabelle eine selectAnweisung, welche Department ID, Course ID und Course Title sortiert nach Department ID und Course ID zurückgibt, und zwar für alle Kurse, deren Beschreibung die Zeichenkette introduc, unabhängig von der Groß- und Kleinschreibung, enthält.
Oracle Designer in der Anwendungsentwicklung Der Oracle Designer ist eine umfassende Suite von Werkzeugen, welche die Analyse, das Design und die Realisierungsphase von großen Projekten unterstützt. Sie können sich den Oracle Designer als einen Werkzeugkasten vorstellen, der für jede Phase eines Projektes das passende Werkzeug enthält. Diese Werkzeuge können einem ganzen Team helfen, die Anforderungen für eine Anwendung auf vielen Ebenen zu erfassen, zu dokumentieren und zu verifizieren - Datenmodell, Prozessmodell und Datenfluss, um nur einige zu nennen. Diese Werkzeuge arbeiten so zusammen, dass die Ausgabedaten des einen die Eingabewerte eines anderen Werkzeugs sind. Die Basis des Oracle Designer ist der Oracle SCM (Oracle Software Configuration Manager), ein Oracle-basiertes Repository, das eine strukturierte Beschreibung aller Objekte der Anwendung und beliebige Anwendungsdateien (z.B. Quell-Code, Konfigurationsdateien) enthält. Der Oracle SCM unterstützt das Konfigurations- Management in Projekten durch folgende Funktionen: ● ● ● ● ● ● ●
Entwicklung auf der Basis isolierter Arbeitsbereiche (Workareas) Versionierung von Objekten/Dateien (Check-In, Check-Out) parallele Entwicklung (Branching) Vergleichen unterschiedlicher Versionen von Objekten/Dateien (Compare) Zusammenführen unterschiedlicher Versionen von Objekten/Dateien (Merge) Konfigurationen (Configurations) Analyse der Abhängigkeiten (Dependency Analysis)
Am besten lässt sich der Oracle SCM mit einem Source-Code-Verwaltungssystem (wie z.B. PVCS oder ClearCase) vergleichen, wobei der wesentliche Unterschied zu den genannten Systemen darin besteht, dass die Definitionen und Dateien in einer Oracle- Datenbank abgelegt werden. Während der Oracle Designer die Installation des Oracle SCM zwingend voraussetzt, können der Oracle Jdeveloper (siehe Tag 20), Oracle Forms (siehe Tag 15) und Oracle Reports (siehe Tag 18) den Oracle SCM optional als Source-Code-Verwaltungssystem benutzen.
Abbildung 5.1: Oracle Designer und Oracle SCM
Ein Repository ist ein Satz von Datenbankobjekten, welche Metadaten enthalten, d.h. Daten über Daten. Der Oracle SCM enthält als Repository zum Beispiel Informationen über das Datenmodell, das Prozessmodell und viele andere Design-Aspekte einer Anwendung. Der Einsatz des Oracle Designer bietet sich an: ● ●
● ●
für Unternehmen, die große oder eine Vielzahl kleiner und mittlerer Projekte realisieren; für Projekte, in denen eine Vielzahl von Einzelpersonen - Analytiker, Designer, Entwickler, Tester, Verfasser von Dokumentationen, Dozenten - in Teams zusammenarbeiten; für Projekte, in denen die Wiederverwendbarkeit von Entwicklungsergebnissen angestrebt wird; für Anwendungen, die im Hinblick auf Umfang, Komplexität und eingesetzte Technologien sehr ambitioniert sind.
Um von dieser Lektion profitieren zu können, sollten Sie eine Kopie des Oracle Designer besitzen, damit Sie die einzelnen Schritte jeder Übung nachvollziehen können. Bitte erwarten Sie nicht, wenn Sie noch nicht mit dem Oracle Designer gearbeitet haben, die Software am Montag zu erhalten und mit der Erstellung vollständiger Anwendungen am Dienstag beginnen zu können. Dies ist eine komplexe Suite von Werkzeugen, deren Nutzung einen bedeutenden Aufwand an Training erfordert. Erwarten Sie ebenfalls nicht von einer Einzelperson, dass sie bis ins Detail mit jeder Möglichkeit in jeder Komponente vertraut sein kann; das ist schlicht unrealistisch. Stattdessen
macht es für jedes Mitglied des Entwicklungsteams viel mehr Sinn, sich auf die Komponenten zu konzentrieren, die mit seiner oder ihrer Rolle innerhalb des Entwicklungsprozesses korrespondieren. In dieser Lektion erhalten Sie nur eine kurze Einführung in einige Komponenten des Oracle Designer; eine umfassende Beschäftigung mit dem Oracle Designer und dem Oracle SCM würde ein eigenes Buch erfordern.
5.1 Installation des Oracle SCM und Einrichten von Benutzern Da der Oracle Designer das Vorhandensein des Oracle SCM (Oracle Software Configuration Manager) zwingend voraussetzt, müssen wir uns zunächst kurz mit dessen Installation und der Einrichtung von Benutzern beschäftigen. Die Installation des Oracle SCM besteht aus: ● ●
einer client-seitigen Installation der Installation eines Repository in der Oracle-Datenbank
Nach der erfolgreichen Installation der Client-Software auf dem PC steht Ihnen eine detaillierte OnlineDokumentation über die Installation des Repository in der Datenbank zur Verfügung. Da die Installation aus einer Reihe von Einzelschritten besteht, sollten Sie diese an Hand der Online-Dokumentation abarbeiten. Da sich die einzelnen Installationsschritte von Version zu Version unterscheiden können, wird in diesem Buch nicht im Detail darauf eingegangen.
Bitte beachten Sie, dass die Installation des Oracle SCM immer für bestimmte Versionen der Datenbank zertifiziert wird. Vor der Installation sollten Sie sich daher über den aktuellen Zertifizierungs-Status in den Installationshinweisen oder auf den Oracle-Seiten im Internet informieren. Die im Buch dargestellten Beispiele wurden mit dem Oracle Designer/Oracle SCM 6i auf Basis einer Oracle-Datenbank 8.1.7 erstellt. Die für Anfang des Jahres 2002 geplante Version des Oracle Designer/Oracle SCM 9i wird in Bezug auf diese Beispiele keine Unterschiede aufweisen. Für beide Versionen des Oracle Designer ist eine Zertifizierung für die Datenbank Oracle 9i in Vorbereitung. Die Installation des Repository wird aus dem Repository Administration Utility (RAU) angestoßen (Start | Programs | Oracle Repository 6i | Repository Administration Utility). Der Benutzer, unter dem die Installation des Repository in der Datenbank erfolgt (in unserem Beispiel der Benutzer REPOS_OWNER), ist als Eigentümer des Repository ein privilegierter Benutzer, der später weitere Benutzer zum Zugriff auf das Repository berechtigen kann. Bitte achten Sie darauf, bei der Auswahl der Installations- Optionen für das Repository die Unterstützung für die Objekte des Oracle Designer zu aktivieren.
Abbildung 5.2: Installationsoptionen des Repository Nach der Installation des Repository in der Datenbank sollten Sie die Protokolldateien auf eventuell aufgetretene Fehler überprüfen. Zu diesem Zeitpunkt haben Sie ein funktionsfähiges Repository mit nur einem einzigen Benutzer, dem Repository-Eigentümer. Deshalb werden Sie im nächsten Schritt einen weiteren Benutzer einrichten, der auf das Repository zugreifen darf. Bitte rufen Sie erneut das Repository Administration Utility (RAU) auf und melden Sie sich als Repository- Eigentümer REPOS_OWNER an. Danach wählen Sie bitte die Funktion Maintain Users aus. Die Liste der Benutzer enthält zu diesem Zeitpunkt nur den Eigentümer des Repository. Fügen Sie jetzt mit der Befehlsschaltfläche Add User den Benutzer FLUGLE hinzu. In der oberen Werteliste erhalten Sie eine Auswahl aller gültigen Datenbank-Benutzer, die noch nicht als Repository-Benutzer eingetragen sind. Die voreingestellten Privilegien können Sie für die folgenden Beispiele mit OK akzeptieren.
Abbildung 5.3: Einrichten eines Benutzers für das Repository Jeder Benutzer des Repository benötigt bestimmte Rechte und Synonyme, um auf die Objekte des Repository in der Datenbank zuzugreifen. Während der Installation des Repository haben Sie die Möglichkeit, zwischen einem Satz von privaten Synonymen pro Benutzer oder gemeinsamen (public) Synonymen für alle Benutzer zu wählen. Aus Gründen der einfacheren Administration empfiehlt sich die Verwendung gemeinsamer Synonyme. Haben Sie sich für private Synonyme entschieden, so müssen diese nach Auswahl und Bestätigung des Benutzers mit der Befehlsschaltfläche Reconcile angelegt werden. Haben Sie sich dagegen für gemeinsame (public) Synonyme entschieden, so entfällt dieser Schritt.
5.2 Anlegen eines Application System und Vergabe von Zugriffsrechten für den Benutzer Um mit den Werkzeugen des Oracle Designer arbeiten zu können, benötigt jeder Benutzer Zugriff auf 1. mindestens eine Workarea: Sie können sich eine Workarea als Schreibtisch vorstellen, an dem Sie allein (Private) oder im Team (Shared) das Projekt bearbeiten können. 2. mindestens ein Application System: Ein Application System fungiert als Container, der fachlich zusammengehörige Objekte enthält. Mit der Installation des Oracle SCM wird eine gemeinsame Workarea GLOBAL_SHARED_ WORKAREA automatisch angelegt. Wir benötigen daher noch ein Application System, welches die Tabellen-Definitionen enthalten soll. Um die Administration zu vereinfachen, werden wir das Application System durch den Repository-Eigentümer einrichten und danach dem Benutzer FLUGLE Zugriffsrechte für das Application System geben. Bitte rufen Sie zunächst den Repository Object Navigator (RON) aus dem Menü mit Start | Programs | Oracle Repository 6i | Repository Object Navigator auf. Melden Sie sich bei der Verbindungsaufforderung als Repository-Eigentümer REPOS_OWNER an. Wählen Sie im WelcomeBildschirm des RON die Option Open Navigator aus. Im Auswahl-Fenster Open Navigator doppelklicken Sie auf Shared Workareas, selektieren Sie die GLOBAL_SHARED_WORKAREA und bestätigen Sie Ihre Auswahl mit OK.
Abbildung 5.4: Auswahl der GLOBAL_SHARED_WORKAREA im RON Der Repository Object Navigator (RON) ist ein Werkzeug, das allen Benutzern zur Verfügung steht, um sich über den Inhalt des Repository zu informieren und Aufgaben im Zusammenhang mit der Versionsverwaltung auszuführen. Wir können ihn u.a. auch verwenden, um ein Application System anzulegen sowie Zugriffsrechte dafür zu vergeben. Das Anlegen eines neues Application System geschieht am einfachsten, indem Sie mit der rechten Maustaste das Kontext-Menü für die GLOBAL_SHARED_WORKAREA im Object Navigator des RON aufrufen und die Option Create Child auswählen. Wählen Sie in der Auswahlliste als Typ Application Systems aus. Geben Sie einen Namen für das Application System an (FLUGLE_APP); optional können Sie noch beschreibende Informationen hinzufügen. Durch einfaches Klicken auf das DiskettenSymbol wird das Application System im Repository angelegt.
Abbildung 5.5: Anlegen eines Application System Damit der Benutzer FLUGLE dieses Application System benutzen darf, müssen wir ihm noch einige Zugriffsrechte gewähren. Wir tun dies wiederum über das Kontext-Menü, indem wir das Application System markieren und die Option Grant Access Rights über die rechte Maustaste auswählen. Wählen Sie im linken Teil des Fensters den Benutzer FLUGLE aus und markieren Sie im rechten Teil alle angebotenen Zugriffsrechte. Bestätigen Sie abschließend Ihre Auswahl mit Grant.
Abbildung 5.6: Zugriffsrechte für Application System einrichten
Da Sie damit alle notwendigen Voraussetzungen für die weitere Arbeit eingerichtet haben, können Sie den RON verlassen.
5.3 Erstellen eines Server-Modell-Diagramms mit dem Design Editor des Oracle Designer Es gibt viele Vorteile bei der Anwendung eines Werkzeugs wie des Design Editor anstelle der Verwendung von Skripten mit SQL-Anweisungen. Es ist viel unwahrscheinlicher, mit dem Design Editor syntaktische Fehler zu machen; er prüft beispielsweise, ob die von einem Fremdschlüssel referenzierte Spalte selbst ein Primärschlüssel ist. Sie können das Diagramm des Server-Modells ausdrucken und in einer Konferenz präsentieren; dagegen ist es viel schwieriger, das in einem Skript repräsentierte Datenmodell zu visualisieren. Lassen Sie uns die Schritte betrachten, die notwendig sind, um ein neues Server-Modell- Diagramm anzufertigen. Um den Oracle Designer aufzurufen, wählen Sie zunächst aus dem Menü Start | Programs | Oracle Designer 6i | oracle Designer. Bitte melden Sie sich bei der Verbindungsaufforderung mit dem vorher eingerichteten Benutzer FLUGLE und seinem Passwort an. Im nächsten Schritt werden Sie aufgefordert, eine Workarea auszuwählen. Wählen Sie bitte die GLOBAL_SHARED_WORKAREA aus, die Ihnen zu diesem Zeitpunkt als einzige Auswahlmöglichkeit angeboten wird.
Abbildung 5.7: Auswahl der GLOBAL_SHARED_ WORKAREA im Designer Nachdem der Kontext für die folgenden Arbeitsschritte bestimmt ist, können wir jetzt aus dem Startfenster des Oracle Designer den Design Editor aufrufen. Bitte wählen Sie unter den angegebenen Optionen als Startpunkt das Server Model.
Abbildung 5.8: Auswahl des Server Model im Design Editor Im Design Editor wird der Object Navigator mit dem Server Model als aktiver Registerkarte gestartet. In der freien Arbeitsfläche des Design Editor können Sie ausgewählte Objekte bearbeiten, Diagramme erzeugen etc. Im oberen Teil des Design Editor sehen Sie ein Menü und eine Symbolleiste. Um ein neues Server-Modell- Diagramm zu erstellen, wählen Sie den Menüeintrag File | New | Server Model Diagram. Vor dem Öffnen des leeren Diagramms müssen Sie als Container noch das Application System auswählen, zu dem die Server-Objekte gehören sollen.
Abbildung 5.9: Anlegen eines neuen Server-Modell-Diagramms
5.4 Eine Tabelle dem Diagramm hinzufügen Lassen Sie uns zu Beginn dem Diagramm die Tabelle Student hinzufügen. In der linken Symbolleiste des Diagramms befinden sich u.a. folgende Befehlsschaltflächen, um Datenbank-Objekte im Diagramm anzulegen.: ● ● ● ● ● ●
Um zum Beispiel eine Tabelle zu erstellen, klicken Sie die Schaltfläche Table auf der Symbolleiste an, positionieren den Mauszeiger auf dem Diagramm dort, wo Sie die Tabelle platzieren wollen, und klicken mit der linken Maustaste. Es öffnet sich ein Dialogfenster mit einem Wizard, der Sie durch die Schritte beim Anlegen der Tabelle führt.
Abbildung 5.10: Anlegen einer Tabelle Wie in Abbildung 5.10 gezeigt wird, füllen Sie die Felder im Tabellenregister folgendermaßen aus:
Sollte sich beim Anlegen der Tabelle im Diagramm statt des Dialogfensters eine Tabelle mit Eigenschaften öffnen, müssen Sie im Hauptmenü des Design Editor die Menüoption Options | Use Property Dialogs aktivieren. Im nächsten Schritt können Sie die Tabelle mit einer bereits existierenden Tabelle über einen Fremdschlüssel verknüpfen. Da es jedoch im Application System noch keine weitere Tabelle gibt, auf die wir uns beziehen könnten, überspringen wir den zweiten Schritt.
5.5 Festlegen der Spalten für eine Tabelle Im dritten Schritt können Sie die Spalten der Tabelle festlegen. Die Registerkarte enthält Eigenschaften, die Sie für jede Spalte der Tabelle festlegen können (siehe Abbildung 5.11).
Abbildung 5.11: Definition der Tabellenspalten Die Schaltflächen Add und Delete dienen dem Hinzufügen bzw. Entfernen einzelner Spalten. Wenn Sie die Schaltfläche Allowable Values betätigen, können Sie für die vorher ausgewählte Spalte zulässige Werte definieren (z.B. W oder M für die Spalte Geschlecht). Über die Schaltfläche Advanced erreichen Sie weitere Eigenschaften der markierten Spalte, die hier jedoch nicht weiter vorgestellt werden. Über die beiden Schaltflächen mit den Pfeilsymbolen können Sie die Reihenfolge der Spalten innerhalb der Tabelle verändern.
5.6 Den Primärschlüssel für die Tabelle festlegen
Im vierten Schritt können Sie einen Primärschlüssel für die Tabelle festlegen. Im oberen Teil des Dialogfensters legen Sie den Namen des Primärschlüssel-Constraints fest, wobei der Oracle Designer einen Namensvorschlag aus dem Alias-Namen der Tabelle und einem Suffix _PK erzeugt. Im linken Teil des Dialogfensters werden alle obligatorischen Spalten angeboten, aus denen der Primärschlüssel der Tabelle gebildet werden kann. Sie können einen oder mehrere (max. 16) Spalten auswählen und mit den Pfeil-Schaltflächen in das rechte Auswahlfenster transportieren. Besteht der Primärschlüssel aus mehreren Spalten, so können Sie die Reihenfolge der Spalten innerhalb des Constraints mit den beiden Schaltfächen (Pfeil nach oben / Pfeil nach unten) modifizieren (siehe Abbildung 5.12).
Abbildung 5.12: Definition des Primärschlüssels für die Tabelle Mit diesen vier Schritten haben wir die Tabelle Student im Repository definiert. Im Object Navigator des Design Editor ist die Struktur der Tabelle in hierarchischer Form dargestellt. Die TabellenDefinition ist zusätzlich im Server-Modell-Diagramm sichtbar und kann als grafische Abbildung mit dem Menüeintrag File | save Diagram As im Repository abgespeichert werden. Wie Sie in der Abbildung 5.13 sehen können, wird das Symbol # links von der Student_ID angezeigt, was die Spalte als Primärschlüssel kennzeichnet. Ebenso wird links von allen obligatorischen Spalten das Symbol * angezeigt.
Abbildung 5.13: Object Navigator und Server-Modell-Diagramm
Sie können mit der linken Maustaste das Tabellensymbol im Diagramm vergrößern, so dass alle Spalten sichtbar sind. Im unteren Teil des Tabellensymbols können zusätzliche Informationen zu den Integritätsregeln (Constraints), Triggern, Indizes etc. der Tabelle angezeigt werden. Dazu müssen Sie die entsprechenden Schaltflächen im oberen Teil des Tabellensymbols betätigen. Um die Tabelle weiter zu bearbeiten, können Sie sie wahlweise im Object Navigator oder im Diagramm mit Doppelklick öffnen. Ein Fenster mit dem Titel Edit Table wird erscheinen und sechs Registerkarten enthalten: ● ● ● ●
●
●
Name Columns Display (Festlegung, welche Spalten in Anwendungen angezeigt werden sollen) Controls (Eigenschaften, die von den Anwendungsgeneratoren des Oracle Designer benutzt werden, um die Anzeigecharakteristiken der Tabellenspalten zu bestimmen) UI (Eigenschaften, die von den Anwendungsgeneratoren des Oracle Designer benutzt werden, um die Anzeigecharakteristiken der Tabellenspalten zu bestimmen) Advanced (zusätzliche Eigenschaften der Tabelle).
Wenn Sie die Integritätsregeln (Constraints), Indizes oder Trigger der Tabelle bearbeiten möchten, können Sie diese ebenso durch einen Doppelklick auf das gewünschte Objekt im Object Navigator
oder im Diagramm öffnen.
5.7 Einen Fremdschlüssel festlegen Um den Schritten zu folgen, die zur Erzeugung eines Fremdschlüssels erforderlich sind, lassen Sie uns ein bereits existierendes Diagramm betrachten, das zwei Tabellen besitzt: Department und Instructor. Wie Sie in Abbildung 5.14 sehen können, enthält die Tabelle Instructor nicht die Department_ID, welche den Fachbereich anzeigt, zu dem der Dozent gehört.
Abbildung 5.14: Server-Modell-Diagramm mit zwei Tabellen ohne Fremdschlüssel Um einen obligatorischen Fremdschlüssel in der Tabelle Instructor zu erzeugen, wählen Sie die Befehlsschaltfläche Mandatory Foreign Key auf der Symbolleiste (wird im Allgemeinen als Krähenfuß bezeichnet). Beachten Sie, dass der Mauszeiger sich jetzt in einen Krähenfuß verwandelt hat. Bewegen Sie ihn über die Tabelle Instructor. Klicken Sie mit der linken Maustaste auf die Tabelle; ziehen Sie den Krähenfuß über die Tabelle Department, und klicken Sie erneut mit der linken Maustaste. Sie sollten jetzt zwei Dinge bemerken (wie in Abbildung 5.15 gezeigt wird): ●
●
Ein obligatorischer Fremdschlüssel mit der Bezeichnung INR_DET_FK verbindet die Tabellen Department und Instructor. Das Fremdschlüssel-Constraint ist Teil der Tabelle Instructor. Der Tabelle Instructor wurde eine obligatorische Spalte Det_Department_ID hinzugefügt.
Abbildung 5.15: Server-Modell-Diagramm mit zwei Tabellen mit Fremdschlüssel Um den Fremdschlüssel zu bearbeiten, doppelklicken Sie auf das Symbol, welches die beiden Tabellen verbindet. Ein Fenster mit dem Titel Edit Foreign Key erscheint (siehe Abbildung 5.16). Für diesen Fremdschlüssel müssen die Standardwerte nicht geändert werden.
Abbildung 5.16: Eigenschaften des Fremdschlüssels
5.8 Die DDL-Anweisungen für die Tabellen erstellen Der Oracle Designer enthält einen Server Generator, mit dessen Hilfe Sie aus den Tabellendefinitionen im Design Editor ein Datenbankschema erzeugen können. Um den Server-Generator aufzurufen, wählen Sie den Menüeintrag Generate | Generate Database From Server Model im Hauptmenü des Design Editor.
Abbildung 5.17: Generierung des Server-Modells Die vom Server-Generator angezeigte Registerkarte Target bietet Ihnen drei Auswahlmöglichkeiten (siehe Abbildung 5.17): ● ● ●
Generierung der Objektdefinitionen nur in Dateien (DDL Files only) Generierung der Objektdefinitionen über Dateien in eine Oracle-Datenbank (Database) Generierung der Objektdefinitionen über Dateien in Datenbanken anderer Hersteller (ODBC)
Mit dem File Prefix legen Sie den Dateinamen, mit Directory das Verzeichnis für die zu erzeugenden DDL-Dateien fest. Falls Sie nur DDL-Dateien erzeugen wollen, müssen Sie den Typ der ZielDatenbank aus der Werteliste Type auswählen. Wenn Sie die Objekte in eine Oracle-Datenbank generieren wollen, müssen Sie zusätzlich Username, Password und Connect-Information des Zielschemas angeben. Über die Schaltfläche Options können Sie den Umfang der Generierung
einschränken. Standardmäßig sind jedoch alle Generierungs-Optionen aktiviert (siehe Abbildung 5.18).
Abbildung 5.18: Optionen für die Server-Generierung Die zweite Registerkarte Objects ermöglicht Ihnen die Auswahl der zu generierenden Objekte. Sie können eine Auswahl aus den im linken Fenster angezeigten Objekten treffen und diese mit den PfeilSchaltflächen in das rechte Fenster transportieren (siehe Abbildung 5.19). Wenn Sie vor dem Aufruf des Server-Generators bereits die zu generierenden Objekte im Object Navigator oder Diagramm markiert haben, kann dieser Auswahlschritt entfallen.
Abbildung 5.19: Auswahl der zu generierenden Objekte An diesem Punkt können Sie auf Start klicken, um die Generierung zu beginnen. Im Meldungs-Fenster des Design Editor werden neben einer Fortschrittsanzeige auftretende Probleme durch Warnungen oder Fehlermeldungen signalisiert. Durch Betätigen der Schaltfläche List Actions wird ein weiteres Fenster mit einer Liste der zur Auswahl stehenden Aktionen angezeigt. Sie können aus dieser Liste eine oder mehrere Aktionen auswählen (z.B. die generierten Skripte anschauen oder ausführen) und mittels der Schaltfläche Run die Aktionen anstoßen (siehe Abbildung 5.20).
Abbildung 5.20: Meldungs-Fenster und Aktions-Liste Der Server-Generator erzeugt pro Generierungslauf eine Master-Datei mit der Dateiendung .SQL, aus der heraus die anderen generierten Skripte in der korrekten Reihenfolge aufgerufen werden. Für jeden Typ von Datenbank-Objekt wird eine eigene Datei mit einer speziellen Dateiendung erzeugt, z.B.: ● ● ●
TAB für Tabellen CON für Integritätsregeln (Constraints) VW für Datensichten (Views)
Wenn Sie die Objektdefinitionen in ein Datenbank-Schema generieren, wird automatisch ein Abgleich mit dem Inhalt des Schemas vorgenommen, d.h. bereits existierende Objekte werden nicht noch einmal generiert. Der Server-Generator erzeugt in diesem Falle Befehle, die nur noch die Differenz (Delta) zwischen den Definitionen im Oracle Designer und dem aktuellen Inhalt des Schemas implementieren. Da auch in diesem Falle Skripte erzeugt und diese nicht sofort ausgeführt werden, haben Sie die Möglichkeit, diese Änderungen zu begutachten und ggf. vor der Ausführung zu modifizieren. Listing 5.1: Vom Server-Generator erstellte SQL-Dateien D:\User\Oracle9i\flugle.sql --- Generated for Oracle 8.1 on Thu Dec 27
13:10:26 2001 by Server Generator
6.5.52.1.0 SPOOL flugle.lst @@flugle.tab @@flugle.con SPOOL OFF -- D:\User\Oracle9i\flugle.tab --- Generated for Oracle 8.1 on Thu Dec 27 13:10:25 2001 by Server Generator 6.5.52.1.0 PROMPT Creating Table 'INSTRUCTOR' CREATE TABLE INSTRUCTOR (INSTRUCTOR_ID NUMBER(5) NOT NULL ,LAST_NAME VARCHAR2(25) NOT NULL ,FIRST_NAME VARCHAR2(25) ,MI VARCHAR2(1) ,POSITION VARCHAR2(25) ,TELEPHONE VARCHAR2(10) ,FAX VARCHAR2(10) ,EMAIL VARCHAR2(100) ,DET_DEPARTMENT_ID NUMBER(5) NOT NULL ) / PROMPT Creating Table 'DEPARTMENT' CREATE TABLE DEPARTMENT (DEPARTMENT_ID NUMBER(5) NOT NULL ,DEPARTMENT_NAME VARCHAR2(25) NOT NULL ) / PROMPT Creating Table 'STUDENT' CREATE TABLE STUDENT (STUDENT_ID NUMBER(8) NOT NULL ,LAST_NAME VARCHAR2(25) NOT NULL ,FIRST_NAME VARCHAR2(25) NOT NULL ,MI VARCHAR2(1) ,YEAR VARCHAR2(25) ,STREET_ADDRESS VARCHAR2(25) ,CITY VARCHAR2(25) ,STATE VARCHAR2(2) ,ZIPCODE VARCHAR2(9) ,TELEPHONE VARCHAR2(10) ,FAX VARCHAR2(10) ,EMAIL VARCHAR2(100) ) / -- D:\User\Oracle9i\flugle.con --- Generated for Oracle 8.1 on Thu Dec 27 13:10:25 2001 by Server Generator 6.5.52.1.0 PROMPT Creating Primary Key on 'INSTRUCTOR' ALTER TABLE INSTRUCTOR ADD (CONSTRAINT INR_PK PRIMARY KEY (INSTRUCTOR_ID))
/ PROMPT Creating Primary Key on 'DEPARTMENT' ALTER TABLE DEPARTMENT ADD (CONSTRAINT DET_PK PRIMARY KEY (DEPARTMENT_ID)) / PROMPT Creating Primary Key on 'STUDENT' ALTER TABLE STUDENT ADD (CONSTRAINT STT_PK PRIMARY KEY (STUDENT_ID)) / PROMPT Creating Foreign Key on 'INSTRUCTOR' ALTER TABLE INSTRUCTOR ADD (CONSTRAINT INR_DET_FK FOREIGN KEY (DET_DEPARTMENT_ID) REFERENCES DEPARTMENT (DEPARTMENT_ID)) /
5.9 Ein Datenmodell mittels »Reverse Engineering« aus einem existierenden Datenbank-Schema erzeugen Der Oracle Designer ist auch in der Lage, den umgekehrten Weg zu gehen, d.h. aus einem existierenden Datenbank-Schema ein Datenmodell zu extrahieren und die entsprechenden Definitionen im Repository abzuspeichern. Dieser Prozess wird allgemein als »Reverse Engineering« bezeichnet; im Oracle Designer wird dagegen für diese Funktionalität die Bezeichnung »Capture Design« verwendet. Wann lässt sich diese Funktionalität sinnvoll nutzen? Als Entwickler können Sie beispielsweise an eine bestehende Datenbank ohne begleitende Dokumentation geraten. Indem Sie diese Datenbank mittels »Reverse Engineering« rückübersetzen, kann der Design Editor ein Diagramm konstruieren, das Ihnen beim Verständnis des Datenmodells helfen wird. Sie sind dann in der Lage, das Modell weiterzuentwickeln - vielleicht unter Hinzufügung von Tabellen, Spalten u.a. Datenbank-Objekten -, indem Sie das bestehende Datenmodell als Grundlage benutzen. Das »Reverse Engineering« eines Server-Modells ist aus folgenden Quellen möglich: ● ● ●
Um das »Reverse Engineering« aufzurufen, wählen Sie im Design Editor den Menüeintrag Generate | Capture Design Of | Server Model. In dem dann erscheinenden Fenster können Sie u.a.: ● ● ●
die Quelle für die Rückübersetzung wählen den Umfang der Rückübersetzung bestimmen festlegen, dass automatisch ein Server-Modell-Diagramm erzeugt werden soll.
Nach einer Auswahl der Objekte aus dem Ursprungs-Schema klicken Sie auf Start, um den Prozess auszulösen. Im Meldungs-Fenster werden neben einer Fortschrittsanzeige auftretende Probleme durch Warnungen oder Fehlermeldungen signalisiert.
Die Definitionen der rückübersetzten Objekte werden im Repository eingetragen, aber noch nicht mit Commit dauerhaft abgespeichert. Sie haben anschließend die Möglichkeit, die vorläufig eingetragenen Definitionen zu begutachten, ggf. zu modifizieren und sich dann erst endgültig für die dauerhafte Speicherung oder das Löschen der Definitionen zu entscheiden. In der Abbildung 5.21 sehen Sie ein Server-Modell-Diagramm, das im Ergebnis eines »Reverse Engineering« entstanden ist.
Abbildung 5.21: Server-Modell im Ergebnis eines »Reverse Engineering«
5.10 Die Grundlagen von Oracle Designer Die Komponenten des Oracle Designer können in diese Kategorien eingeteilt werden: ● ●
●
●
Prozessmodellierung: Besteht aus dem Process Modeller Systemmodellierung: Umfasst den Entity Relationship Diagrammer, Function Hierarchy Diagrammer und Dataflow Diagrammer Design Transformer: Umfasst den Database Design Transformer und den Application Design Transformer System Design und Generierung: Umfasst den Design Editor mit Object Navigator, verschiedenen Diagrammern, dem Logic Editor und den Generatoren (Server-, Forms-, Reports, Visual Basic-, Webserver- und MS-Help-Generator).
Der Oracle SCM (Oracle Software Configuration Manager ) enthält eine Reihe weiterer universeller Programme:
Lassen Sie uns kurz die Rolle diskutieren, die einige dieser Komponenten innerhalb des Entwicklungsprozesses einer Anwendung spielen. Sie haben bereits zu Beginn des Kapitels kennen gelernt, wie Sie im Repository Object Navigator (RON) ein neues Application System anlegen können5.2.
Process Modeller Der Process Modeller ist ein Werkzeug, das Ihnen die grafische Beschreibung der Geschäftsprozesse ermöglicht, für die Sie Anwendungen entwickeln wollen. Der Process Modeller wird in erster Linie benutzt, um die Prozesse in ihrer gegenwärtig existierenden Form zu verstehen. Das Werkzeug ist in der Lage, einen Prozess zu animieren, was die Dynamik leichter verständlich macht. Ausgehend vom Verständnis der gegenwärtigen Prozesse können diese neu konzipiert werden, um Zykluszeiten zu reduzieren oder um unnötige Arbeitsschritte zu eliminieren.
Entity Relationship Diagrammer Der Entity Relationship Diagrammer bietet die Möglichkeit, ein konzeptuelles Datenmodell von den fachlichen Anforderungen ausgehend zu modellieren. Ein Entity- Relationship-Diagramm ist aus Entitäten, Attributen, Domänen und Beziehungen zusammengesetzt und stellt diese Objekte grafisch dar. Mit Hilfe des Datenbank Design Transformer kann aus dem Entity-Relationship-Modell automatisch ein erster Entwurf für die Datenbank im Repository erzeugt werden.
Function Hierarchy Diagrammer Sie verwenden den Function Hierarchy Diagrammer, um die Hierarchie von Geschäftsfunktionen, die in einem Unternehmen ausgeführt werden, grafisch zu beschreiben. Sie könnten zum Beispiel auf der obersten Ebene eine Funktion mit dem Namen Manage Human Resources beschreiben. Wenn Sie diese Funktion in ihre Teilfunktionen zerlegen, ergeben sich auf der niedrigeren Ebene weitere Funktionen, wie Recruit New Instructors, Hire New Instructors usw. Mit dem Function Hierarchy Diagrammer können Sie die Verwendung von Entitäten und Attributen aus dem Entity- RelationshipModell durch die Funktion identifizieren. Abbildung 5.22 veranschaulicht als ein Beispiel eine einfache Funktionshierarchie für die administrative Funktion am Flugle College.
Abbildung 5.22: Funktionshierarchie-Diagramm
Transformer für das Datenbank-Design und das AnwendungsDesign Der Database Design Transformer ist ein Werkzeug, das aus einem Entity- Relationship-Modell einen ersten Entwurf für die Datenbank im Repository erzeugt. Analog generiert der Application Design Transformer einen ersten Modulentwurf einschließlich der Datenzugriffe aus der Funktionshierarchie.
Modulentwurf im Design Editor Im Design Editor können Sie Ihre Anwendungen als Module beschreiben und daraus komplette Anwendungssysteme zusammensetzen. Aus diesen Modulen lassen sich mittels der AnwendungsGeneratoren lauffähige Anwendungen erzeugen. Die Abbildung 5.23 zeigt zum Beispiel das Modul-Diagramm eines Bildschirm-Moduls, mit dem die Tabellen Class und Course bearbeitet werden können.
Abbildung 5.23: Modul-Diagramm
5.11 Zusammenfassung Die Schlüsselkonzepte in dieser Lektion, die man sich merken sollte, waren die folgenden: ●
●
●
●
●
Der Oracle Designer ist eine Suite von Werkzeugen, die die Analyse, das Design und die Realisierungsphase von großen Projekten unterstützt. Im Design Editor des Oracle Designer können Sie Ihr Datenbank-Design, z.B. Tabellen, Spalten, Integritätsregeln (Constraints), Indizes auf grafischem Wege beschreiben. Mit Hilfe des Server-Generators können Sie aus dem Datenbank-Design im Design Editor ein Datenbank-Schema (für Oracle-Datenbanken oder Datenbanken anderer Hersteller) generieren. Umgekehrt können Sie aus einer existierenden Datenbank mittels »Reverse Engineering« ein Datenmodell im Repository erzeugen. Der Oracle Designer speichert alle Informationen im Oracle SCM (Oracle Software Configuration Manager), einem Oracle-basierten Repository, das umfangreiche Funktionen für das Konfigurations-Management enthält.
5.12 Wie geht es weiter?
Am Tag 6, »Einführung in SQL«, lernen Sie Data Manipulation Language, einen weiteren Bestandteil von SQL, kennen, um mit SELECT-Anweisungen auf Daten zuzugreifen.
5.13 Workshop Der Zweck dieses Workshops ist es, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Ein Nachteil des Oracle Designer ist, dass er nur Oracle- Datenbanken unterstützt. 2. Nennen Sie drei Vorteile der Verwendung des Oracle Designer anstelle eines Texteditors zur Erstellung der DDL-Anweisungen, die Sie zur Erzeugung einer Datenbank benötigen.
Implementierung des logischen Modells: physischer Datenbankentwurf Die vorhergehende Lektion, Tag 3, »Logischer Datenbankentwurf«, befasste sich mit den Elementen eines Datenmodells, wobei die Implementierung dieses Modells in eine »reale« Datenbank nicht berücksichtigt wurde. In dieser Lektion werden Sie sehen, wie ein logisches Datenmodell tatsächlich in einer Oracle-Datenbank implementiert wird. Diese Lektion behandelt einen Aspekt von SQL, der als Data Definition Language (DDL, Datendefinitionssprache) bezeichnet wird. DDL besteht aus SQL-Anweisungen zum Erzeugen, Ändern und Löschen von Datenbankobjekten.
DDL steht für Data Definition Language. DDL besteht aus SQL-Anweisungen, die zum Erzeugen, Ändern oder Löschen von Datenbankstrukturen, wie z.B. Tabellen, Datensichten und Indizes, eingesetzt werden. Wenn Sie häufig mit der Entwicklung von Datenbankanwendungen betraut sind, sollten Sie unbedingt den Einsatz eines Werkzeugs für den Datenbankentwurf unter Windows erwägen, wie z.B. Oracle Designer, ERwin von Logic Works oder S-Designer von Sybase. Diese Produkte erlauben Ihnen, ein logisches Modell grafisch zu definieren und die richtigen SQL-Anweisungen zur Erstellung einer Datenbank zu erzeugen. Selbst wenn Sie ein Werkzeug für den Datenbankentwurf verwenden, sollten Sie sich ausreichende Kenntnisse über die Werkzeuge von Oracle aneignen. Es gibt viele Werkzeuge, die eine Oracle-Datenbank mit einer SQLBenutzeroberfläche ausstatten. Zwei Beispiele hierfür sind SQL*Plus und SQL*Plus Worksheet, eine Komponente des Oracle Enterprise Managers. Diese Werkzeuge ermöglichen Ihnen die Eingabe von SQLAnweisungen zum Erzeugen von Tabellen, Indizes und anderen Datenbankobjekten. Bei den meisten Beispielen in dieser Lektion wird SQL*Plus verwendet. Abbildung 4.1 zeigt, wie Sie eine SQL-Anweisung mit Hilfe von SQL*Plus Worksheet ausführen.
Abbildung 4.1: Ausführung einer SQL-Anweisung mit Hilfe von SQL*Plus Worksheet Eine weitere Alternative ist der Einsatz eines Werkzeugs wie Oracle Enterprise Manager (OEM) Console, das eine grafische Benutzeroberfläche für die Administration aller Aspekte einer Oracle-Datenbank bereitstellt. Im Gegensatz zu SQL*Plus oder SQL*Plus Worksheet erfordert die Enterprise Manager Console keine SQLKenntnisse. Die Console setzt Ihre Eingaben, wie z.B. zum Anlegen einer Tabelle, in SQL um und übergibt die SQL-Anweisungen an die Oracle-Instanz. Dieses Werkzeug ist ideal für Benutzer, die nicht mit SQL vertraut sind und die Sprache auch nicht lernen wollen.
Abbildung 4.2: Erstellen und Ändern von Tabellen mit Hilfe der Oracle Enterprise Manager Console
4.1 Grundlagen der create table-Anweisung Aufgrund ihrer vielen Optionen und Klauseln kann die SQL-Anweisung create table recht komplex ausfallen. Es folgt eine vereinfachte Form der Syntax:
CREATE TABLE table_name ( column_name1 datatype [NOT NULL], ... column_nameN datatype [NOT NULL]) [ORGANIZATION HEAP | INDEX | EXTERNAL (external_table_parameters)]; Die Definition der Variablen lautet wie folgt: ● ● ● ●
table_name steht für den Namen der Tabelle. column_name1 bis column_nameN sind gültige Spaltennamen. datatype ist die Bezeichnung eines Oracle-Datentyps. external_table_parameters sind weitere Angaben für den Zugriff auf externe Tabellen.
Die ORGANIZATON Klausel gibt an, wie die Tabelle gespeichert wird. Fehlt die ORGANIZATION Klausel, wird eine heap-organisierte Tabelle angelegt, bei der die Daten nicht sortiert in verschiedenen Abschnitten, so genannten Extents, in einem Tablespace der Datenbank gespeichert werden. Eine index-organisierte Tabelle (IOT) wird wie ein Index als sortierte Baumstruktur in der Datenbank gespeichert. Eine externe Datei ist eine Datei außerhalb der Datenbank, die in einem Format vorliegt, das von dem Werkzeug SQL*Loader verarbeitet werden kann. Auf externe Tabellen kann nur lesend zugegriffen werden. Bis auf wenige Ausnahmen werden Sie immer mit heap-organisierten Tabellen arbeiten. Die vollständige Syntax von CREATE TABLE finden Sie in dem Handbuch »Oracle9i SQL Reference«. Die create table-Anweisung kann direkt aus SQL*Plus oder SQL*Plus Worksheet aufgerufen werden. Die Beispiele in dieser Lektion wurden mit SQL*Plus erstellt.
Benennen einer Tabelle Oracle stellt die folgenden Anforderungen an Tabellennamen: ● ● ● ●
Jede Tabelle muss einen eindeutigen Namen haben. Ein Tabellenname darf nicht länger als 30 Zeichen sein. Tabellennamen sollten mit einem Buchstaben beginnen. Ein Tabellenname kann die Buchstaben A bis Z, die Zahlen 0 bis 9 sowie die Zeichen $, # und _ (Unterstrich) enthalten.
●
●
●
Bei der Benennung von Tabellen können Sie Groß- und Kleinbuchstaben verwenden. Oracle ignoriert die Groß- und Kleinschreibung, sofern die Tabellen- oder Spaltennamen nicht in doppelten Anführungszeichen stehen. Bei der Verwendung von doppelten Anführungszeichen handelt es sich um eine von Oracle unterstützte Syntax. Sie werden allerdings nie einen Grund haben, doppelte Anführungszeichen direkt verwenden zu müssen. Einige Fremdwerkzeuge (wie z.B. PowerBuilder) machen sich diese Syntax zunutze, dies ist aber für den Entwickler erkennbar. Wenn Sie den Tabellennamen in doppelte Anführungszeichen (") setzen, darf auch ein von SQL reserviertes Wort verwendet werden, der Anfang des Namens darf dann eines der Zeichen $, #, _ (Unterstrich) oder ein Leerzeichen sein. Vermeiden Sie derartige Tabellennamen, da diese schwieriger zu handhaben sind. Der Name einer Tabelle sollte aussagekräftig sein. Benutzen Sie bei der Benennung von Tabellen und anderen Datenobjekten keine übertriebenen Abkürzungen. Viele Werkzeuge für die Anwendungsentwicklung, wie z.B. PowerBuilder oder Oracle Forms, stellen für die Auswahl einer Tabelle eine intuitive Oberfläche bereit. Da keine Eingaben erforderlich sind, ist ein langer Tabellenname genauso einfach auszuwählen wie ein kurzer Tabellenname.
Falls Sie eine Tabelle umbenennen möchten, steht Ihnen die Anweisung RENAME TABLE zur Verfügung.
RENAME TABLE old_table_name TO new_table_name; Der Platzhalter old_table_name steht für den bisherigen Tabellennamen, new_table_name für den neuen Tabellennamen.
Benennen einer Spalte Es folgen einige Gedanken zur Benennung von Spalten: ●
● ●
●
Innerhalb einer Tabelle muss der Spaltenname eindeutig sein. Sie können aber denselben Spaltennamen in unterschiedlichen Tabellen verwenden. Es gelten die selben Namenskonventionen wie für Tabellennamen. Wie bei Tabellen sollten Sie auch für Spalten einen aussagekräftigen Namen wählen. Aussagekräftige Namen lassen die Definition der einzelnen Spalten für den Benutzer deutlicher erscheinen. Eine Oracle-Tabelle kann aus bis zu 1000 Spalten bestehen.
Beispiele für die Erstellung von Tabellen Dieser Abschnitt beginnt mit einem einfachen Beispiel. Sie werden eine create table- Anweisung schreiben, um eine Tabelle mit den am Flugle-College vorhandenen Fachbereichen (Department) zu erstellen, wie in Listing 4.2 dargestellt. Listing 4.1: Erstellung der Department-Tabelle SQL> Create table Department 2 (Department_ID number(5) NOT NULL, 3 Department_Name Varchar2(25)); Table created. Einige Aspekte hinsichtlich der Syntax von Anweisungen sind besonders zu erwähnen: ● ● ●
Klammern schließen die Liste der Spalten ein. In erforderlichen Spalten folgt der Ausdruck NOT NULL auf die Nennung des Datentyps der Spalte. Wenn nötig, wird die Spaltenbreite nach dem Datentyp in Klammern gesetzt.
Sie haben die Möglichkeit, die Eigenschaft einer Spalte ausdrücklich als optional festzulegen, indem Sie nach der Nennung des Datentyps NULL eingeben, wie in Listing 4.2 dargestellt. Listing 4.2: Erstellung der Student-Tabelle SQL> create table Student 2 (Student_ID number(8) NOT NULL, 3 Last_Name Varchar2(25) NOT NULL, 4 First_Name Varchar2(25) NOT NULL, 5 MI Varchar2(1) NULL, 6 Year Varchar2(25) NULL, 7 Street_Address Varchar2(25) NULL, 8 City Varchar2(25) NULL, 9 State Varchar2(2) NULL, 10 Zipcode Varchar2(9) NULL, 11 Telephone Varchar2(10) NULL, 12 Fax Varchar2(10) NULL, 13 E-Mail Varchar2(100) NULL); Table created. Wie ich bereits erwähnt habe, ignoriert Oracle bei der Auswertung von SQL-Anweisungen die Groß- und Kleinschreibung (was nicht bei allen Herstellern der Fall ist), solange die Tabellen- und Spaltennamen nicht in doppelten Anführungszeichen stehen. Listing 4.3 zeigt ein Beispiel für die Student-Tabelle, in dem die Tabellenund Spaltennamen in doppelten Anführungszeichen stehen. Allerdings erkennt Oracle diese Namen bei der anschließenden Verwendung nur dann, wenn sie ebenfalls in doppelte Anführungszeichen gesetzt werden. Listing 4.3: Tabellen- und Spaltennamen in Anführungszeichen SQL> create table "student" 2 ("student_id" number(8) NOT NULL, 3 "last_name" Varchar2(25) NOT NULL, 4 "first_name" Varchar2(25) NOT NULL, 5 "mi" Varchar2(1), 6 "year" Varchar2(25), 7 "street_address" Varchar2(25), 8 "city" Varchar2(25), 9 "state" Varchar2(2), 10 "zipcode" Varchar2(10), 11 "telephone" Varchar2(10), 12 "fax" Varchar2(10), 13 "email" Varchar2(100)); Table created. SQL> describe student Object does not exist. SQL> describe "student" Name Null? Type ------------------------------ -------- -----------student_id NOT NULL NUMBER(5) last_name NOT NULL VARCHAR2(25) first_name NOT NULL VARCHAR2(25) mi VARCHAR2(1) year VARCHAR2(25) street_address VARCHAR2(25) city VARCHAR2(25) state VARCHAR2(2)
Wenn Sie wissen möchten, welche Tabellen in Ihrem Datenbankschema liegen, fragen Sie die Katalogsicht USER_TABLES ab.
Katalogsichten haben die Präfixe USER, ALL und DBA. Die Sichten, die mit USER beginnen, liefern Informationen über Datenbankobjekte, die dem verwendeten Datenbankbenutzer gehören. Die Ansichten, die mit ALL beginnen, zeigen Informationen über alle Objekte, für die der verwendete Benutzer Zugriffsrechte hat. Sichten, die mit DBA beginnen, liefern Informationen über alle in der Datenbank benutzten Objekte. Der Zugriff auf Sichten mit dem Präfix DBA erfordert eine der Rollen DBA oder SELECT_CATALOG_ROLE.
Festlegen des Primärschlüssels Bei dem Primärschlüssel einer Tabelle handelt es sich um die Gruppe von Spalten, welche die einzelnen Zeilen der Tabelle eindeutig bezeichnen.
table_name steht für den Namen der Tabelle. column_name1 bis column_nameN sind gültige Spaltennamen. datatype ist die Angabe eines gültigen Oracle-Datentyps. constraint_name ist der Name der Primärschlüssel-Integritätsbedingung. pk_column_name1 bis pk_column_nameN bezeichnen die Tabellenspalten, die den Primärschlüssel bilden.
Obwohl die Klausel für den Primärschlüssel in der create table-Anweisung einen wichtigen Bestandteil der Theorie relationaler Datenbanken darstellt, erfolgt ihre Anwendung in der Praxis optional. Aus folgenden Gründen sollten Sie sicherstellen, dass Sie einen Primärschlüssel für jede Tabelle in Ihrer Datenbank definieren. ●
●
Ein Primärschlüssel verhindert, dass Zeilen doppelt in eine Tabelle eingefügt werden. Je nach den jeweiligen Gegebenheiten können doppelte Zeilen stören oder eine Anwendung zum Absturz bringen. Es würde Ihnen zum Beispiel nicht gefallen, wenn zwei Fachbereiche denselben Bezeichner hätten. Wenn Sie die department_id als Primärschlüssel deklarieren, kann das nicht passieren. Eine Tabelle muss über einen Primärschlüssel verfügen, wenn ein Fremdschlüssel in einer anderen Tabelle auf sie verweist.
Oracle9i implementiert Primärschlüssel durch eindeutige Indizes (UNIQUE INDEX). Wenn Sie einen Primärschlüssel anlegen, erzeugt Oracle9i automatisch einen eindeutigen Index und benennt den Index mit dem Namen der Integritätsbedingung. Durch Abfrage der Katalogsicht USER_INDEXES, können Sie sich davon überzeugen. Listing 4.4: Indizes, die Primärschlüssel implementieren, erhalten den Namen der Integritätsbedingung als Indexnamen SQL> select index_name, table_name from user_indexes; INDEX_NAME TABLE_NAME ------------------------------ ----------------------PK_CLASS CLASS PK_CLASS_LOCATION CLASS_LOCATION PK_COURSE COURSE PK_DEPARTMENT DEPARTMENT Unter nochmaliger Verwendung der Department-Tabelle zeigt Listing 4.5, wie der Primärschlüssel bei der Erstellung der Tabelle deklariert wird. Listing 4.5: Deklaration des Primärschlüssels bei Erstellung der Tabelle SQL> create table Department 2 (Department_ID number(5), 3 Department_Name Varchar2(25), 4 Constraint PK_Department 5 Primary Key (Department_ID)); Table created.
Primärschlüssel unterliegen gewissen Einschränkungen. Erstens kann einer Spalte, die Teil des Primärschlüssels ist, nicht NULL zugewiesen werden. Zweitens kann eine als long, long raw, blob, clob oder bfile definierte Spalte nicht Teil des Primärschlüssels sein. Drittens beträgt die maximale Anzahl von Spalten im Primärschlüssel 33. Sie können letztere Einschränkung durch die Verwendung eines Ersatzschlüssels umgehen - eines künstlichen Werts, der garantiert alle Zeilen einer Tabelle eindeutig identifiziert. Listing 4.7 zeigt, wie der Primärschlüssel das Einfügen von doppelten Zeilen in die Tabelle verhindert. Die Einhaltung der Primärschlüssel-Integritätsbedingung wird erzwungen, selbst wenn die Auswirkung der ersten insert-Anweisung nicht mit COMMIT (siehe Tag 7, Abschnitt 7.2 »Transaktionen«) festgeschrieben wurde.
Als doppelte Zeile wird eine Zeile bezeichnet, deren Primärschlüsselspalten über dieselben Werte verfügen wie die Primärschlüsselspalten einer anderen Zeile. Listing 4.6: Erzwingen der Eindeutigkeit eines Primärschlüssels SQL> insert into Department (Department_ID, Department_Name) values
(1004, 'PSYCHOLOGY'); 1 row created. SQL> insert into Department (Department_ID, Department_Name) values (1004, 'PSYCHIATRY'); insert into Department ERROR at line 1: ORA-00001: unique constraint (FLUGLE.PK_DEPARTMENT) violated
Festlegen von Fremdschlüsseln Nun, da Sie wissen, wie der Primärschlüssel in der create table-Anweisung angegeben wird, sind Sie bereit, Ihre Fremdschlüssel zu deklarieren.
CREATE TABLE table_name ( column_specification1, ... column_specificationN, [CONSTRAINT constraint_name FOREIGN KEY (fk_column_name1,..., fk_column_nameN) REFERENCES referenced_table (pk_column_name1,..., pk_column_nameN)]; Die Definition der Platzhalter lautet wie folgt: ● ●
● ● ● ●
table_name steht für den Namen der Tabelle. column_specification1 bis column_specificationN sind gültige Spaltenangaben (wie unten im Einzelnen beschrieben). constraint_name ist der Name der Integritätsbedingung, die Sie dem Fremdschlüssel zuordnen möchten. referenced_table ist der Name der Tabelle, die der Fremdschlüssel referenziert. fk_column_name1 bis fk_column_nameN sind die Spalten, die den Fremdschlüssel bilden. pk_column_name1 bis pk_column_nameN sind die Spalten, die den Primärschlüssel der referenzierten Tabelle referenced_table bilden.
Es folgt die Syntax für column_specification.
column_name datatype [DEFAULT default value] [CONSTRAINT constraint_name] [NULL]|[NOT NULL]|[UNIQUE]|[CHECK (condition)] Die Definition der Platzhalter lautet wie folgt: ● ● ●
● ●
column_name steht für einen gültigen Oracle-Spaltennamen. datatype ist die gültige Angabe eines Oracle-Datentyps. default_value ist ein zulässiger Standardwert, welcher der Spalte beim Einfügen zugeordnet wird, falls die Einfügeoperation keinen Wert für die Spalte übergibt. constraint_name ist der Name der Integritätsbedingung. condition ist eine gültige Boolesche Bedingung, die auf den Wert der Spalte zutreffen muss.
Eine Integritätsbedingung ist ein Mechanismus, der gewährleistet, dass die Werte einer Spalte oder einer Spaltengruppe einer Bedingung entsprechen. Integritätsbedingungen werden im Datenbankkatalog (Data Dictionary) gespeichert. Für die Course-Tabelle müssen Sie einen Primär- und einen Fremdschlüssel deklarieren, wie in Listing 4.7 gezeigt. Listing 4.7: Deklaration von Primär- und Fremdschlüsseln bei Erstellung der Tabelle SQL> create table Course (Course_ID number(5), Department_ID number(5) NOT NULL, Title Varchar2(60) NOT NULL, Description Varchar2(2000), Units Number, Additional_Fees Number, Constraint PK_Course Primary key (Course_ID, Department_ID), Constraint FK_Course_Department_ID Foreign key (Department_ID) references Department (Department_ID)); Table created. Die Spalten der referenzierten Tabelle müssen den Primärschlüssel der referenzierten Tabelle bilden und die Datentypen und Längen der Fremdschlüssel-Spalten und der referenzierten Primärschlüssel-Spalten müssen exakt übereinstimmen. Ist dies nicht der Fall, erstellt Oracle den Fremdschlüssel nicht. Im folgenden Beispiel gehen wir davon aus, dass Sie den Fremdschlüssel für die Course-Tabelle noch nicht erzeugt haben. Löschen wir zuerst den Primärschlüssel der Department-Tabelle. Wenn Sie versuchen, die Course- Tabelle zu erstellen, wird Oracle feststellen, dass Department_ID nicht als Primärschlüssel für die Department-Tabelle definiert wurde. Die create table-Anweisung führt nicht zum Erfolg, wie Listing 4.8 zeigt. Listing 4.8: Ein Fremdschlüssel kann nicht deklariert werden, weil es keinen zugehörigen Primärschlüssel gibt SQL> alter table Department drop primary key; Table altered. SQL> create table Course (Course_ID number(5), Department_ID number(5) NOT NULL, Title Varchar2(60) NOT NULL, Description Varchar2(2000), Units Number, Additional_Fees Number, Constraint PK_Course Primary key (Course_ID, Department_ID), Constraint FK_Course_Department_ID Foreign key (Department_ID) references Department (Department_ID)); Foreign key (Department_ID) references Department (Department_ID)) * ERROR at line 11: ORA-02270: no matching unique or primary key for this column-list
4.2 Einschränken eines Spaltenwerts
Ein leistungsfähiges Merkmal von SQL ist die Möglichkeit, bei der Erstellung einer Tabelle die Validierung der elementaren Daten einer Tabelle festzulegen. Diese Aufgabe erledigt SQL mit einer optionalen check-Klausel, die für jede Spalte angegeben werden kann. Bei der check-Klausel handelt es sich um eine Boolesche Bedingung, die entweder true (wahr) oder false (falsch) lautet. Erweist sich die Bedingung als true, wird der Spaltenwert von Oracle akzeptiert; erweist sich die Bedingung als false, gibt Oracle einen Fehlercode aus. Listing 4.9 zeigt ein Beispiel, in dem Sie die Definition der Instructor-Tabelle so verändern werden, dass die Position eines Kursleiters mit einem der drei Werte - assistant professor (Lehrbeauftragter), associate professor (planmäßiger außerordentlicher Professor) oder professor - wiedergegeben wird. Listing 4.9: Festlegung einer check-Integritätsbedingung für eine Spalte SQL> create table Instructor 2 (Instructor_ID number(5), 3 Department_ID number(5) NOT NULL, 4 Last_Name varchar2(25) NOT NULL, 5 First_Name varchar2(25), 6 MI varchar2(1), 7 Position varchar2(25) constraint CK_Instructor_Position 8 check (Position in ('ASSISTANT PROFESSOR', 'ASSOCIATE PROFESSOR', 9 'PROFESSOR')), 10 Telephone varchar2(10), 11 Fax varchar2(10), 12 E-Mail varchar2(100), 13 constraint PK_Instructor Primary Key (Instructor_ID), 14 constraint FK_Instructor_Department_ID 15 Foreign Key (Department_ID) references Department (Department_ID)); Table created. Wenn Sie versuchen, der Tabelle einen Kursleiter hinzuzufügen, dessen Position keinem der drei zulässigen Werte entspricht, gibt Oracle einen Fehlercode aus, welcher aussagt, dass eine check-Integritätsbedingung verletzt wurde, wie in Listing 4.10 dargestellt. Listing 4.10: Die check-Integritätsbedingung wird beachtet SQL> insert into Instructor 2 (Instructor_ID, Department_ID, Last_Name, First_Name, Position) 3 values 4 (612, 1005, 'NILAND','MARTINA', 'CURMUDGEON'); insert into Instructor * ERROR at line 1: ORA-02290: check constraint (FLUGLE.CK_INSTRUCTOR_POSITION) violated
Da es sich bei curmudgeon (Brummbär) um keine gültige Kursleiterposition handelt, schlägt der Versuch, die Zeile einzufügen, fehl. Dies ist ein Beispiel dafür, wie eine check-Integritätsbedingung die Datenintegrität sicherstellt.
Wertebereiche und die Check-Integritätsbedingung Bei der check-Integritätsbedingung handelt es sich um eine Integritätsbedingung auf Spaltenebene, die
mindestens zwei Zwecken dient. ●
●
Einige Spalten - häufig numerischer Natur - haben Wertebereiche, die nicht über Fremdschlüssel eingeschränkt werden können. Dennoch müssen Sie den Wert einer solchen Spalte auf einen bestimmten Bereich beschränken. Der zulässige Wertebereich einer Spalte ist nicht konstant; er muss beim Einfügen oder bei der Aktualisierung des Spaltenwerts berechnet werden.
Eine check-Integritätsbedingung wird entweder beim create table oder beim alter table .. add constraint angegeben.
Die Syntax lautet wie folgt: column_name datatype [CONSTRAINT constraint_name] [CHECK (condition)] Die Definition der Variablen lautet wie folgt: ● ● ●
●
column_name steht für den Spaltennamen. datatype ist der Datentyp der Spalte. constraint_name stellt den Namen der Integritätsbedingung dar und unterliegt den Einschränkungen für die Benennung von Oracle-Datenbankobjekten. condition ist eine gemäß der SQL-Syntax zulässige Bedingung in Oracle, die einen Booleschen Wert zurückgibt.
Um dieses Konzept zu verdeutlichen, zeigt Listing 4.11, wie Sie eine Tabelle erstellen können, die in einer Krankenhausdatenbank für die Speicherung von Patientendaten eingesetzt wird. Eine der Spalten in dieser Tabelle enthält die Körpertemperatur des Patienten in Grad Fahrenheit (Body_Temp_Deg_F). Sie sollten die möglichen Werte dieser Spalten einschränken, indem Sie diese als number(4,1) definieren. Allerdings akzeptiert diese Spalte nach wie vor Zahlen von 0,0 bis 999,9 - einschließlich einiger für die Körpertemperatur offensichtlich unsinniger Werte. Sie können eine check- Integritätsbedingung verwenden, um den Wertebereich auf 60,0 (für Patienten, die an Unterkühlung leiden) bis 110,0 einzuschränken. Listing 4.11: Erstellung einer check-Integritätsbedingung SQL> create table Patient ( Patient_ID varchar2(6) primary key, Body_Temp_Deg_F number(4,1) constraint CK_Patient_Body_Temp Check (Body_Temp_Deg_F >= 60.0 and Body_Temp_Deg_F <= 110.0)); Table created. SQL> insert into Patient (Patient_ID, Body_Temp_Deg_F) values ('A1001', 98.6); 1 row created. SQL> insert into Patient (Patient_ID, Body_Temp_Deg_F) values ('Q7777', 111.2); ('Q7777', 111.2) * ERROR at line 4:
ORA-02290: check constraint (TYO.CK_PATIENT_BODY_TEMP) violated Als Beispiel zeigt Listing 4.12 die Definition einer check-Integritätsbedingung, welche überprüft, ob der Versicherungsstatus (Insurance_Status) eines Patienten Ja (y) oder Nein (n) lautet. Listing 4.12: Verwendung des in-Operators in einer check-Integritätsbedingung SQL> create table Patient ( Patient_ID varchar2(6) primary key, Body_Temp_Deg_F number(4,1) constraint CK_Patient_Body_Temp Check (Body_Temp_Deg_F >= 60.0 and Body_Temp_Deg_F <= 110.0), Insurance_Status Char(1) constraint CK_Patient_Insurance_Status Check (Insurance_Status in ('Y','N'))); Table created. SQL> insert into Patient (Patient_ID, Insurance_Status) values ('R4321','Y'); 1 row created. SQL> insert into Patient (Patient_ID, Insurance_Status) values ('U3030','U'); ('U3030','U') * ERROR at line 4: ORA-02290: check constraint (TYO.CK_PATIENT_INSURANCE_STATUS) violated
Wie Sie sehen, wird die check-Integritätsbedingung verletzt, wenn Sie versuchen, den Wert 'U' unter Insurance_Status (Versicherungsstatus) einzugeben.
Zu einer Spalte kann es mehr als eine Check- Integritätsbedingung geben Oracle schränkt die Anzahl der auf einer Spalte oder einer Tabelle definierten check- Integritätsbedingungen nicht ein. Kommen wir auf das Beispiel des Flugle-College zurück. Listing 4.13 zeigt, wie zu einer einzelnen Spalte - Amount_Approved (genehmigter Betrag) - zwei check-Integritätsbedingungen definiert werden. Listing 4.13: Eine Spalte mit mehr als einer check-Integritätsbedingung SQL> 2 3 4 5 6 7 8 9 10 11 12
Table created. SQL> insert into Loan_Application 2 (Loan_Application_No, Borrower_Last_Name, Borrower_First_Name, 3 Amount_Requested, Amount_Approved) 4 values 5 (2001, 'RUBRIK', 'STANLEY', 1000000, 999950); insert into Loan_Application * ERROR at line 1: ORA-02290: check constraint (TYO.CK_AMOUNT_APPROVED_INTERVAL) violated SQL> insert into Loan_Application 2 (Loan_Application_No, Borrower_Last_Name, Borrower_First_Name, 3 Amount_Requested, Amount_Approved) 4 values 5 (2001, 'RUBRIK', 'STANLEY', 1000000, 999000); 1 row created. SQL> insert into Loan_Application 2 (Loan_Application_No, Borrower_Last_Name, Borrower_First_Name, 3 Amount_Requested, Amount_Approved) 4 values 5 (2001, 'RUBRIK', 'STANLEY', 1000000, 1001000); insert into Loan_Application * ERROR at line 1: ORA-02290: check constraint (TYO.CK_AMOUNT_APPROVED_LIMIT) violated
In dem Beispiel aus Listing 4.13 werden für die Tabelle Loan_Application (Darlehensantrag) zwei Integritätsbedingungen definiert. Die erste Integritätsbedingung (Zeilen 8 und 9) begrenzt den Bewilligungsbetrag auf eine Million Dollar, während die zweite Integritätsbedingung (Zeilen 10 und 11) sicherstellt, dass der Bewilligungsbetrag in Schritten von tausend Dollar vorliegt. Sie können beide Integritätsbedingungen auf einfache Weise in einer einzigen Integritätsbedingung kombinieren. Sie sollten allerdings separate Integritätsbedingungen definieren, wenn Sie glauben, dass es erforderlich werden könnte, eine einzelne Integritätsbedingung zu deaktivieren, während andere Integritätsbedingungen weiterhin aktiviert bleiben sollen.
In einer Check-Integritätsbedingung auf andere Spalten verweisen Eine der Einschränkungen einer check-Integritätsbedingung für eine Spalte ist, dass sie nicht auf andere Spalten derselben Tabelle verweisen kann. Nehmen wir an, Sie sollen eine Tabelle definieren, in der Informationen über Darlehensanträge gespeichert werden sollen. In dieser Tabelle enthält Amount_Requested den vom Darlehensnehmer beantragten Betrag. Bei Amount_Approved handelt es sich um den vom Darlehensausschuss genehmigten Betrag. Der Darlehensgeber genehmigt niemals einen Betrag, der die beantragte Summe übersteigt. Listing 4.14 zeigt diese Integritätsbedingung. Die check-Integritätsbedingung einer Spalte kann nicht auf andere Spalten verweisen. Sie können jedoch eine Integritätsbedingung auf Tabellenebene verwenden, um eine andere Spalte in derselben Tabelle zu referenzieren. Durch Einfügen eines Kommas nach der Definition von Amount_Approved, wird aus der Integritätsbedingung auf Spaltenebene eine Integritätsbedingung auf Tabellenebene. Listing 4.14: Verwendung einer check-Integritätsbedingung auf Tabellenebene
Pseudospalten und SQL-Funktionen in einer check- Integritätsbedingung verwenden Eine check-Integritätsbedingung kann nicht auf die Pseudospalten LEVEL und ROWNUM sowie auf die SQL Funktionen sysdate, systimestamp, user, uid und userenv zugreifen. Wenn Sie eine Geschäftsregel definieren möchten, die diese Funktionalität benötigt, sollten Sie sich bei der Einschränkung von Spaltenwerten eines Datenbanktriggers bedienen. Am Tag 14, »Weitere PL/SQL Programmiertechniken«, werden Sie dieses Thema kennen lernen.
Eine Pseudospalte kann in SQL-Anweisungen wie eine normale Spalte einer Tabelle verwendet werden. Jede Tabelle hat implizit die Pseudospalten LEVEL, ROWID und ROWNUM. Pseudospalten
belegen keinen Speicherplatz. Die Pseudospalte ROWNUM kann bei einem SELECT dazu genutzt werden, die ausgewählten Zeilen zu nummerieren. Listing 4.15: Nummerieren der Ergebniszeilen einer SELECT-Anweisung durch die Pseudospalte ROWNUM SQL> select rownum, department_id, department_name from department; ROWNUM DEPARTMENT_ID DEPARTMENT_NAME ---------- ------------- ------------------------1 1000 PHILOSOPHY 2 1001 ECONOMICS 3 1002 BIOLOGY Die Pseudospalte LEVEL wird am Tag 9 »Fortgeschrittene Abfragen mit SQL« besprochen, wenn wir auf hierarchische SQL-Abfragen eingehen.
Verzögerte Prüfung von Integritätsbedingungen Das Standardverhalten von Oracle9i in Bezug auf Integritätsbedingungen besteht darin, bei Abschluss einer Änderung durch INSERT, UPDATE oder DELETE sofort die Integritätsbedingung zu prüfen. Dieses Verhalten kann dahingehend modifiziert werden, dass die Prüfung am Ende der Transaktion erfolgt. Integritätsbedingungen, die bei Abschluss einer Transaktion mit COMMIT geprüft werden, heissen Deferred Constraints. Deferred Constraints geben Ihnen z.B. die Möglichkeit, Datensätze in eine Detailtabelle, die eine FremdschlüsselIntegritätsbedingung hat, einzufügen, ohne dass sich ein referenzierter Datensatz in der übergeordneten Tabelle befindet. Solange Sie vor Abschluss der Transaktion den Datensatz in die übergeordnete Tabelle einfügen, wird kein Fehler verursacht. Sie nutzen Deferred Constraints, indem Sie an die Klausel für eine Integritätsbedingung Schlüsselwörter gemäß folgender Syntax anhängen:
[DEFERRABLE] [INITIALLY DEFERRED] Das Schlüsselwort DEFERRABLE gestattet die Prüfung der Integritätsbedingung bei Abschluss einer Transaktion. Die Schlüsselwörter INITIALLY DEFERRED bewirken, dass die Prüfung der Integritätsbedingung tatsächlich erst bei Abschluss einer Transaktion erfolgt. Ohne diese beiden Schlüsselwörter würde die Prüfung der Integritätsbedingung wie gewöhnlich im Rahmen einer Änderung erfolgen, es sei denn die Datenbanksitzung hätte mit SET CONSTRAINTS ALL DEFERRED die Prüfung bei Abschluss einer Transaktion gefordert.
Die Syntax von SET CONTRAINT[S] ist: SET CONTRAINT[S] {ALL | constraint_name1 ... [, constraint_nameN]} {IMMEDIATE | DEFERRED}; Die Platzhalter constraint_name1 bis constraint_nameN stehen für Namen von Integritätsbedingungen.
Mit SET CONSTRAINT[S] sind Sie in der Lage, das Standardverhalten von Integritätsbedingungen zu ändern, sofern diese als DEFERRABLE angelegt wurden. Verwenden Sie ALL und DEFERRED, so werden alle Deferred Constraints beim Beenden der Transaktion geprüft bzw. mit IMMEDIATE sofort bei Durchführung der Änderung. Durch Angabe einzelner Namen von Integritätsbedingungen wird bewirkt, dass nur das Verhalten der erwähnten Integritätsbedingungen verändert wird. Eine Integritätsbedingung kann nicht nachträglich mit ALTER TABLE auf verzögerte Prüfung umgestellt werden. Löschen und erneutes Anlegen der Integritätsbedingung sind erforderlich.
Integritätsbedingungen abrufen Wenn Sie eine Spalte als not null deklarieren, behandelt Oracle die Eigenschaft »erforderlich« als Integritätsbedingung. Diese Integritätsbedingung ist die einzige, die Sie mit dem SQL*Plus-Befehl describe betrachten können. Listing 4.16 zeigt ein Beispiel. Wenn Sie mit SQL*Plus den Aufbau der Instructor-Tabelle abfragen, gibt Oracle an, dass die Instructor-Tabelle über drei erforderliche Spalten verfügt. Diese Anzahl ist etwas irreführend, da Instructor_ID als Primärschlüssel der Tabelle definiert wurde - dessen Wert ist automatisch not null. Listing 4.16: Betrachtung von not null-Integritätsbedingungen mit der describe-Anweisung von SQL*Plus SQL> describe Instructor Name -----------------------------INSTRUCTOR_ID DEPARTMENT_ID LAST_NAME FIRST_NAME MI POSITION TELEPHONE FAX EMAIL
Null? Type -------- -------------------------NOT NULL VARCHAR2(20) NOT NULL VARCHAR2(20) NOT NULL VARCHAR2(25) VARCHAR2(25) VARCHAR2(1) VARCHAR2(25) VARCHAR2(10) VARCHAR2(10) VARCHAR2(100)
Sie können die Sicht USER_CONSTRAINTS des Datenbankkatalogs verwenden, um alle mit einer Tabelle verbundenen Integritätsbedingungen zu betrachten. USER_CONSTRAINTS enthält folgende Spalten: ● ● ● ● ● ●
Bei Fremdschlüsseln finden Sie in der Spalte R_OWNER den Eigentümer der referenzierten Tabelle und in R_CONSTRAINT_NAME den Namen der Primärschlüssel-Integritätsbedingung der referenzierten Tabelle. Listing 4.17 zeigt die Ergebnisse einer Abfrage von user_constraints nach allen mit der Instructor-Tabelle verbundenen Integritätsbedingungen: zwei not null- Integritätsbedingungen, eine check-Integritätsbedingung auf der Spalte Position, einen Primärschlüssel, der durch einen Wert P für Constraint_Type angegeben wird, und einen Fremdschlüssel (constraint_type R) auf die Department ID der Tabelle Department.Sie werden feststellen, dass ich bei Tabellen- und Spaltennamen nur den Anfangsbuchstaben groß schreibe, während ich im Code den gesamten Namen groß schreibe. In Listing 4.17 muss zum Beispiel das Wort INSTRUCTOR in Zeile 4
vollständig in Großbuchstaben eingegeben werden, andernfalls wird die Tabelle nicht im Data Dictionary gefunden. Das liegt daran, dass die Namen der Datenbankobjekte in Großbuchstaben im Data Dictionary stehen. Listing 4.17: Betrachtung von Integritätsbedingungen für die Instructor-Tabelle SQL> select constraint_name, constraint_type, search_condition 2 from user_constraints 3 where 4 table_name = 'INSTRUCTOR'; CONSTRAINT_NAME C SEARCH_CONDITION ----------------------------- - ----------------------------------NN_INSTRUCTOR_DEPT_ID C DEPARTMENT_ID IS NOT NULL NN_INSTRUCTOR_LAST_NAME C LAST_NAME IS NOT NULL CK_INSTRUCTOR_POSITION C Position in ('ASSISTANT PROFES SOR', 'ASSOCIATE PROFESSOR', 'PROFESSOR ') PK_INSTRUCTOR P FK_INSTRUCTOR_DEPARTMENT_ID R
4.3 Festlegen eines Standardwerts für eine Spalte Durch die Verwendung der default-Klausel bei der Definition einer Spalte können Sie einen Standardwert für diese Spalte festlegen. Dieser Standardwert wird immer dann auf eine Spalte angewendet, wenn eine Zeile in die Tabelle eingefügt wird, ohne die Spalte in der insert-Anweisung zu nennen. Nehmen wir zum Beispiel an, dass die meisten Studenten in Springfield leben. Aus diesem Grund möchten Sie sicherstellen, dass beim Einfügen eines Studenten oder einer Studentin in die Student-Tabelle der Standardwert SPRINGFIELD eingesetzt wird, wenn kein Ort angegeben ist. Listing 4.18 verdeutlicht, wie Sie die create tableAnweisung ändern würden, um zu diesem Ergebnis zu gelangen. Nach Festlegung eines Standardwerts wird eine in die Student-Tabelle eingefügte Zeile auf den Standardwert SPRINGFIELD gesetzt, sofern sie keinen Wert für City (Ort) enthält. Listing 4.18: Festlegung eines Standardwerts für eine Spalte SQL> create table Student 2 (Student_ID number(8), 3 Last_Name Varchar2(25) NOT NULL, 4 First_Name Varchar2(25) NOT NULL, 5 MI Varchar2(1), 6 Year Varchar2(25), 7 Street_Address Varchar2(25), 8 City Varchar2(25) Default 'SPRINGFIELD', 9 State Varchar2(2), 10 Zipcode Varchar2(9), 11 Telephone Varchar2(10), 12 Fax Varchar2(10), 13 E-Mail Varchar2(100), 14 Constraint PK_Student Primary Key (Student_ID)); Table created. SQL> insert into student 2 (Student_ID, Last_Name, First_Name, Year) 3 values 4 (10231313, 'JACKSON', 'ROBERT', 'JUNIOR'); 1 row created.
SQL> select Student_ID, Last_Name, City 2 from Student 3 where 4 Student_ID = 10231313; STUDENT_ID LAST_NAME CITY ---------- ------------------------ ----------10231313 JACKSON SPRINGFIELD
4.4 Verwendung von ALTER TABLE zur Änderung der Tabellendefinition Manchmal ist es notwendig, die Definition einer Tabelle zu ändern. Diesem Zweck dient die alter tableAnweisung. Diese Anweisung verändert zwar die Struktur einer Tabelle, nicht aber deren Inhalt. Mit alter table können Sie eine Tabelle wie folgt ändern: ● ● ● ● ● ● ●
eine neue Spalte in eine vorhandene Tabelle einfügen eine Spalte aus einer Tabelle löschen die Kapazität einer vorhandenen Spalte vergrößern oder verkleinern die Eigenschaft einer bestehenden Spalte von erforderlich in optional oder umgekehrt ändern den Datentyp einer Spalte ändern einen Standardwert für eine vorhandene Spalte bestimmen andere Integritätsbedingungen für eine bestehende Spalte festlegen
Es folgen die fünf Formen einer alter table-Anweisung:
ALTER TABLE table_name ADD (column_specification | constraint ,..., column_specification | constraint); ALTER TABLE DROP column_name [CASCADE CONSTRAINTS]; ALTER TABLE table_name MODIFY (column_specification | constraint); ALTER TABLE table_name DROP PRIMARY KEY; ALTER TABLE table_name DROP CONSTRAINT constraint_name; Die Platzhalter sind wie folgt definiert: ● ● ● ● ●
table_name ist der Name einer Tabelle. column_specification ist die gültige Spezifikation einer Spalte (Spaltenname und Datentyp). column_name ist der Name einer Spalte. constraint steht für eine vollständige Integritätsbedingung. constraint_name ist der Name einer Integritätsbedingung.
Die erste Form der Anweisung wird verwendet, um eine Spalte hinzuzufügen oder um Primärschlüssel oder Fremdschlüssel festzulegen. Die zweite Form nutzen Sie, um eine Spalte einer Tabelle, die Sie nicht mehr benötigen, zu löschen. Die Verwendung von CASCADE CONSTRAINTS bewirkt, dass Integritätsbedingungen, die sich auf die zu löschende Spalte beziehen, ebenfalls gelöscht werden.
Die dritte Form wird zum Ändern einer vorhandenen Spalte benutzt. Unter anderem können Sie eine Spalte vergrößern oder eine erforderliche in eine optionale Spalte umwandeln. Die vierte bzw. fünfte Form der alter table-Anweisung wird zum Löschen des Primärschlüssels einer Tabelle bzw. anderer Integritätsbedingungen verwendet. Die nächsten Seiten werden Ihnen die Verwendung dieser Anweisung anhand mehrerer Beispiele näher bringen.
Definieren eines Primärschlüssels nach Erstellen der Tabelle Sie haben auch die Möglichkeit, die Integritätsbedingung eines Primärschlüssels nach Erstellen einer Tabelle zu definieren. Natürlich darf die Tabelle noch nicht über einen Primärschlüssel verfügen. Um eine Integritätsbedingung für den Primärschlüssel einer vorhandenen Tabelle zu definieren, verwenden Sie die alter table-Anweisung, wie in Listing 4.19 dargestellt. Listing 4.19: Hinzufügen eines Primärschlüssels SQL> create table Department 2 (Department_ID Varchar2(20), 3 Department_Name Varchar2(25) 4 constraint NN_Department_Name NOT NULL); Table created. SQL> alter table Department add 2 constraint PK_Department primary key (Department_ID); Table altered. Die Primärschlüssel-Integritätsbedingung wird unabhängig davon gewahrt, ob der Primärschlüssel bei der Erstellung der Tabelle oder nachträglich definiert wurde.
Änderung einer Spaltendefinition von NOT NULL in NULL Obwohl Sie die Eigenschaft einer Spalte von erforderlich in optional ändern können, sollten Sie darüber nachdenken, warum diese Änderung notwendig ist. Ist dieses Attribut wirklich optional, oder arbeiten Sie mit einer Reihe von Testdaten, die für die realistischen Anwendungsdaten nicht repräsentativ sind? Listing 4.20 zeigt eine Tabelle namens demo_table. Die alter table-Anweisung wird dazu verwendet, die Definition der Spalte Record_No in erforderlich zu ändern. Listing 4.20: Änderung der Eigenschaft einer Spalte von optional in erforderlich SQL> describe demo_table Name Null? Type ------------------------------ -------- --------------------------RECORD_NO NOT NULL NUMBER(38) DESCRIPTION VARCHAR2(40) CURRENT_VALUE NOT NULL NUMBER SQL> alter table demo_table modify (current_value number null); Table altered. SQL> describe demo_table Name Null? Type ------------------------------ -------- --------------------------RECORD_NO NOT NULL NUMBER(38) DESCRIPTION VARCHAR2(40) CURRENT_VALUE NUMBER
Änderung einer Spaltendefinition von NULL in NOT NULL Ist eine Tabelle leer, können Sie einer Spalte die Definition NOT NULL zuweisen. Wenn die Tabelle aber nicht leer ist, können Sie die Eigenschaft einer Spalte nur dann in NOT NULL ändern, wenn alle Zeilen der Tabelle einen Wert für die betreffende Spalte aufweisen. Listing 4.21 zeigt, wie Oracle reagiert, wenn Sie versuchen, die Eigenschaft der Spalte current_value in erforderlich zu ändern. Wenn Sie jedoch sicherstellen, dass current_value keinen NULL-Wert in den einzelnen Zeilen der Tabelle aufweist, kann der Wert von current_value auf not null gesetzt werden. Listing 4.21: Versuch, die Eigenschaft einer Spalte in erforderlich zu ändern SQL> alter table demo_table modify (current_value number not null); alter table demo_table modify (current_value number not null) * ERROR at line 1: ORA-01449: column contains NULL values; cannot alter to NOT NULL SQL> update demo_table set current_value = record_no + 100; 4 rows updated. SQL> select current_value from demo_table; CURRENT_VALUE ------------121 122 123 124 SQL> commit; Commit complete. SQL> alter table demo_table modify (current_value number not null); Table altered.
Anfangs verfügen alle Zeilen der Tabelle über einen NULL-Wert für die Spalte current_value; daher kann die Eigenschaft der Spalte nicht in erforderlich geändert werden. Um die Spalte in erforderlich zu ändern, müssen alle Zeilen einen Wert für current_value haben. Aktualisieren Sie alle Zeilen in der Tabelle, so dass der Spalte current_value der Wert der Spalte record_no plus 100 zugeordnet wird. Anschließend kann die Definition der Spalte in not null geändert werden.
Erweitern der Kapazität einer Spalte Sie können die alter table-Anweisung verwenden, um die Kapazität einer Zeichenspalte zu verändern. Das in Listing 4.22 dargestellte Beispiel zeigt die Verwendung dieser Anweisung. Die ursprüngliche Kapazität der Description-Spalte beträgt 40 Zeichen. Um die Kapazität der Description-Spalte von 40 auf 50 Zeichen zu erhöhen, wird die Spaltendefinition mit der alter table-Anweisung geändert. Listing 4.22: Vergrößerung der Kapazität einer Spalte SQL> describe demo_table Name Null? Type ------------------------------ -------- --------------------------RECORD_NO NOT NULL NUMBER(38) DESCRIPTION VARCHAR2(40) CURRENT_VALUE NOT NULL NUMBER SQL> alter table demo_table modify (description varchar2(50));
Table altered. SQL> describe demo_table Name -----------------------------RECORD_NO DESCRIPTION CURRENT_VALUE
Null? Type -------- --------------------------NOT NULL NUMBER(38) VARCHAR2(50) NOT NULL NUMBER
Verringern der Kapazität einer Spalte Während des Entwurfs einer Datenbankanwendung könnten Sie feststellen, dass Sie sich bei der Kapazität einer Spalte geirrt haben: Sie haben die Spalte mit einer höheren Kapazität angelegt als notwendig. Anfangs können Sie sich vielleicht nicht vorstellen, dass es problematisch sein könnte, wenn eine Spalte größer als nötig ist, da trotzdem alle Daten in dieser Spalte Platz finden. Dennoch stellt dies ein Problem dar, weil es darauf hindeutet, dass Ihr Datenmodell ungenau ist. Wenn Sie »das Richtige« tun wollen, müssen Sie die Spalte verkleinern, so dass die Spaltendefinition den für die Daten reservierten Platz widerspiegelt. Sie können die ALTER TABLE-Anweisung auch dazu verwenden, die Kapazität einer Spalte zu verringern. Die Spalte year der Tabelle Student hat den Datentyp varchar2(25). Nehmen wir an, dass varchar2(8) ebenfalls ausreichend ist. Falls es keine Zeile mit mehr als acht Zeichen in der Spalte year gibt, können Sie die Kapazität der Spalte mit ALTER TABLE verringern. Listing 4.23: Verringern der Kapazität einer Spalte SQL> ALTER TABLE student MODIFY (year varchar2(8)); Table altered. Die vorgestellten ALTER TABLE Anweisungen sind nur ein kleiner Ausschnitt der mit Oracle9i verfügbaren Möglichkeiten. Sie sind z.B. mit ALTER TABLE MOVE in der Lage, index-organisierte Tabellen im laufenden Betrieb (online) zu reorganisieren. Tiefgreifende Veränderungen an der Struktur einer Tabelle sind mit dem Paket DBMS_REDEFINITION ebenfalls im laufenden Betrieb möglich. Am Tag 11 werden Sie die ALTER TABLE Anweisung für partitionierte Tabellen kennen lernen.
4.5 Der Fremdschlüssel und referentielle Integrität Primär- und Fremdschlüssel wirken bei der Durchsetzung der referentiellen Integrität zusammen. Ein Fremdschlüssel in einer Tabelle ist eine Spalte oder eine Gruppe von Spalten, deren Werte auf die Werte des Primärschlüssels einer anderen Tabelle beschränkt sind. Sie sollten so oft wie möglich Fremdschlüssel definieren. Bei Client-Server- Anwendungen ist die Anwendungssoftware des Client die erste Verteidigungslinie. Die letzte Verteidigungslinie der referentiellen Integrität bilden die in der Datenbank definierten Primär- und Fremdschlüssel. Wie ein Primärschlüssel kann auch ein Fremdschlüssel gleich beim Erstellen einer Tabelle definiert werden.
Prüfung des Fremdschlüssels beim Einfügen in eine Tabelle Um die Definition eines Fremdschlüssels zu verdeutlichen, wollen wir uns den Department- und InstructorTabellen der Beispieldatenbank des Flugle-Colleges zuwenden. Jeder Fachbereich verfügt über einen eindeutigen Bezeichner, welcher in der Spalte Department_ID abgelegt ist. Jeder Kursleiter darf nur einem einzigen Fachbereich angehören. Oracle gewährleistet die referentielle Integrität, indem jegliche Operationen untersagt werden, durch die ein Fremdschlüssel einen Wert bekommen könnte, der in der Tabelle mit dem referenzierten Primärschlüssel nicht existiert. Listing 4.25 zeigt zum Beispiel, dass Sie keinen Datensatz einfügen können, der einen Kursleiter beschreibt, der zu einem Fachbereich mit Department_ID 0 gehört, da diese Fachbereichsnummer in der Department-Tabelle nicht existiert.
Listing 4.24: Erzwingen referentieller Integrität SQL> insert into Instructor 2 (Instructor_ID, Department_ID, Last_Name) 3 values 4 (301, 0, 'EDWARDS'); insert into Instructor * ERROR at line 1: ORA-02291: integrity constraint (FLUGLE.FK_INSTRUCTOR_DEPARTMENT_ID) violated parent key not found Es ist nicht erforderlich, den Datentyp einer Fremdschlüssel-Spalte anzugeben. Listing 4.25 liefert ein Beispiel, in dem die Deklaration der Department_ID in der Course-Tabelle keinen Datentyp enthält. Stattdessen verwendet Oracle den Datentyp und die Breite der Spalte Department_ID in der Department-Tabelle und verwendet diese Definitionen bei der Erstellung der Course-Tabelle. Listing 4.25: Der Datentyp einer Fremdschlüssel-Spalte wird anhand der referenzierten Spalte bestimmt SQL> create table Course 2 (Course_ID number(5), 3 Department_ID constraint NN_Course_Department_ID NOT NULL, 4 Title Varchar2(60) 5 constraint NN_Course_Title NOT NULL, 6 Description Varchar2(2000), 7 Units Number, 8 Additional_Fees Number, 9 Constraint PK_Course 10 Primary key (Course_ID, Department_ID), 11 Constraint FK_Course_Department_ID 12 Foreign key (Department_ID) references Department (Department_ID)); Table created. SQL> describe Course Name Null? Type ------------------------------ -------- -------------------------COURSE_ID NOT NULL NUMBER(5) DEPARTMENT_ID NOT NULL NUMBER(5) TITLE NOT NULL VARCHAR2(60) DESCRIPTION VARCHAR2(2000) UNITS NUMBER ADDITIONAL_FEES NUMBER Der Vorteil dabei, den Datentyp einer Fremdschlüssel-Spalte nicht anzugeben, liegt darin, dass die betreffende Spalte garantiert über dieselbe Datentypdefinition verfügen wird wie der referenzierte Primärschlüssel. Der Nachteil ist, dass Sie den Datentyp der Spalte mit Fremdschlüssel nicht durch das Betrachten der create tableAnweisung bestimmen können.
Vereinbaren eines Fremdschlüssels nach Erstellen der Tabelle Alternativ zur Deklaration eines Fremdschlüssels beim Erstellen einer Tabelle haben Sie die Möglichkeit, mit der alter table-Anweisung einen Fremdschlüssel für eine bereits vorhandene Tabelle zu deklarieren. Listing 4.26 zeigt ein Beispiel für die Deklaration eines Fremdschlüssels mit Hilfe der alter table-Anweisung. Listing 4.26: Deklaration eines Fremdschlüssels SQL> create table Course
2 (Course_ID Varchar2(5), 3 Department_ID Varchar2(20) 4 constraint NN_Course_Department_ID NOT NULL, 5 Title Varchar2(60) 6 constraint NN_Course_Title NOT NULL, 7 Description Varchar2(2000), 8 Units Number, 9 Additional_Fees Number, 10 Constraint PK_Course 11 Primary key (Course_ID, Department_ID)); Table created. SQL> alter table Course add constraint FK_Course_Department_ID 2 foreign key (Department_ID) references Department (Department_ID); Table altered.
Löschen eines referenzierten Primärschlüssels Im Rahmen einer Revision der Tabellenstruktur oder dem Laden von Daten kann es notwendig sein, den Primärschlüssel einer Tabelle vorübergehend zu löschen. Zur Veranschaulichung nehmen wir an, dass Sie eine Tabelle AP_Header für die Speicherung der Verbindlichkeitsdaten (AP) erstellen und als Primärschlüssel Vendor_Invoice_Number (Rechnungsnummer des Verkäufers) verwenden. Details der Verbindlichkeitsdaten speichern Sie in AP_Detail, wie in Listing 4.27 dargestellt. AP_Detail enthält einen Fremdschlüssel, der AP_Header referenziert. Listing 4.27: Löschen eines Primärschlüssels SQL> create table AP_Header ( 2 Bill_Number NUMBER(4) NOT NULL, 3 Vendor_Invoice_Number VARCHAR2(10), 4 Vendor_ID VARCHAR2(6) NOT NULL, 5 Date_Received DATE NOT NULL, 6 Bill_Status VARCHAR2(5), 7 primary key (Vendor_Invoice_Number)); Table created. SQL> create table AP_Detail ( 2 Bill_Number NUMBER(4) NOT NULL, 3 Vendor_Invoice_Number VARCHAR2(10) NOT NULL, 4 Item_Number NUMBER(3) NOT NULL, 5 Billed_Amount NUMBER(8,2) NOT NULL, 6 Approved_Amount NUMBER(8,2), 7 Paid_Amount NUMBER(8,2), 8 Constraint AP_Detail_FK Foreign Key (Vendor_Invoice_Number) 9 References AP_Header); Table created. Nach Validierung einiger Daten und Überlegungen zu dieser Definition erkennen Sie, dass es leicht passieren könnte, dass zwei unterschiedliche Lieferanten dieselbe Rechnungsnummer verwenden. Aus diesem Grund versuchen Sie, den Primärschlüssel der Tabelle AP_Header zu löschen. Oracle gestattet Ihnen nicht, den Primärschlüssel von AP_Header zu löschen. Listing 4.28 verdeutlicht dies. Listing 4.28: Das Löschen eines Primärschlüssels ist nicht möglich SQL> alter table AP_Header drop primary key; alter table AP_Header drop primary key * ERROR at line 1:
ORA-02273: this unique/primary key is referenced by some foreign keys Es gibt eine Option zu der drop primary key-Klausel; Sie können das Schlüsselwort cascade verwenden. Wenden Sie diese Funktion mit Vorsicht an! Die Option cascade löscht den Primärschlüssel sowie alle sich darauf beziehenden Fremdschlüssel. Listing 4.29 zeigt, wie der Primärschlüssel mit Hilfe der cascade-Klausel erfolgreich gelöscht wird. Listing 4.29: Löschen eines Primärschlüssels mit der cascade-Klausel SQL> alter table AP_Header drop primary key cascade; Table altered.
Löschen eines Fremdschlüssels Während der Phase des Datenbankentwurfs könnten Sie feststellen, dass Sie irrtümlich eine Spalte als Fremdschlüssel definiert haben. Das Löschen eines Fremdschlüssels funktioniert etwas anders als das Löschen eines Primärschlüssels. Da eine Tabelle mehr als einen Fremdschlüssel enthalten kann, verlangt die alter table- Anweisung die Angabe des Namens der mit dem Fremdschlüssel verbundenen Integritätsbedingung, und zwar unter Verwendung der folgenden Syntax:
ALTER TABLE table_name DROP CONSTRAINT constraint_name; Die Definition der Platzhalter ist wie folgt: ● ●
table_name ist der Name der Tabelle, auf der die Integritätsbedingung definiert ist. constraint_name ist der Name der Fremdschlüssel-Integritätsbedingung.
Nehmen wir zum Beispiel an, dass die AP_Header-Tabelle über eine zusätzliche Spalte namens Vendor_Status (Verkäufer_Status) verfügt, welche zufällig denselben Datentyp und dieselbe Breite aufweist wie Vendor_ID (Verkäufer-Bezeichner). Wie in Listing 4.30 dargestellt, erstellen Sie irrtümlicherweise einen Fremdschlüssel für Vendor_Status, welcher auf den Primärschlüssel der Vendor-Tabelle verweist. Bei dem Versuch, einen Wert in die Spalte Vendor_Status einzufügen, merken Sie Ihren Fehler schnell und löschen den der Spalte zugeordneten Fremdschlüssel. Listing 4.30: Löschen eines Fremdschlüssels SQL> alter table AP_Header add constraint FK_AP_Header_Vendor_Status foreign key (Vendor_Status) references Vendor; Table altered. SQL> alter table AP_Header drop constraint FK_AP_Header_Vendor_Status; Table altered.
Deklaration von Unique-Integritätsbedingungen Zusätzlich zu Primär- und Fremdschlüsseln können Sie bei Oracle angeben, dass eine Spalte eindeutige Werte haben soll. Eine Eindeutigkeits-Integritätsbedingung (unique constraint) ist kein Ersatz für eine PrimärschlüsselIntegritätsbedingung. Zum Beispiel enthält die Patient-Tabelle eine Liste der Patienten eines Krankenhauses. Jedem Patienten wird eine Patient ID (Patientenbezeichnung) zugeordnet, die als Primärschlüssel verwendet wird. Jedoch verfügt jeder Patient auch über eine eindeutige Sozialversicherungsnummer (Social_Security_Number). Listing 4.31 zeigt ein Beispiel, wie eine eindeutige Sozialversicherungsnummer mit
Hilfe einer Integritätsbedingung für Eindeutigkeit gewährleistet wird. Listing 4.31: Erstellung einer Integritätsbedingung für Eindeutigkeit SQL> create table Patient ( 2 Patient_ID varchar2(6) primary key, 3 Last_Name varchar2(30) not null, 4 First_Name varchar2(20) not null, 5 Middle_Name varchar2(20), 6 Social_Security_Number varchar2(9) CONSTRAINT UQ_Patient_SocSec Nr unique, 7 Insurance_Carrier_Code varchar2(4)); Table created. SQL> insert into Patient 2 (Patient_ID, Last_Name, First_Name) 3 values 4 ('A901', 'NORTON', 'ED'); 1 row created. SQL> insert into Patient 2 (Patient_ID, Last_Name, First_Name, Social_Security_Number) 3 values 4 ('A902', 'KRAMDEN', 'RALPH', '123456789'); 1 row created. SQL> insert into Patient 2 (Patient_ID, Last_Name, First_Name, Social_Security_Number) 3 values 4 ('A903', 'NORTON', 'TRIXIE', '123456789'); insert into Patient * ERROR at line 1: ORA-00001: unique constraint (TYO.UQ_Patient_SocSec Nr) violated
Auch wenn die Verwendung von Namen für Integritätsbedingungen optional ist, empfehle ich, diese zu benennen, wenn Sie eine Unique-oder Check-Integritätsbedingung, einen Primär- oder Fremdschlüssel anlegen. Wenn Sie für die Integritätsbedingung keinen Namen angeben, erzeugt Oracle automatisch einen recht verschlüsselten Namen (SYS_C[0-9]*). Wollen Sie eine Integritätsbedingung löschen, müssen Sie den von Oracle erzeugten Namen der Integritätsbedingung in der Katalogsicht USER_CONSTRAINTS suchen. Sie ersparen sich selbst einige Mühe, wenn Sie einen sprechenden Namen angeben. Sie könnten zum Beispiel die Integritätsbedingungen für Primärschlüssel als PK_tablename und die Integritätsbedingungen für Fremdschlüssel als FK_tablename_column bezeichnen, solange Sie damit innerhalb der für die Namen von Oracle-Objekten gezogenen Grenze von 30 Zeichen bleiben. Sie sollten außerdem Namen für Check- und Unique- Integritätsbedingungen vergeben, z.B. CK_tablename_column oder UQ_tablename_column.
Unterschiede zwischen Primary Key- und Unique- Integritätsbedingungen Zwei Unterschiede zwischen den Integritätsbedingungen für Primärschlüssel und Eindeutigkeit sind besonders zu erwähnen. Erstens kann eine Tabelle nur einen Primärschlüssel, aber viele Integritätsbedingungen für Eindeutigkeit haben. Zweitens werden die Spalten, die den Primärschlüssel bilden, bei der Definition des Primärschlüssels automatisch erforderlich (NOT NULL). Bei der Deklaration einer Integritätsbedingung für Eindeutigkeit werden die betroffenen Spalten nicht automatisch als erforderlich definiert.
4.6 Tabellenindizes Bei der Betrachtung der Integritätsbedingungen für Primär- und Fremdschlüssel muss man auch Indizes erwähnen - diese Themen sind eng miteinander verwandt. Dieser Abschnitt erläutert, was Tabellenindizes sind und wie sie sowohl vom Anwendungsentwickler als auch von Oracle eingesetzt werden.
Ein Index ist ein Datenbankobjekt, das zu jeder Zeile einer Tabelle die Werte der indizierten Tabellenspalten und einen Verweis auf den Speicherort der Zeile (sog. ROWID) enthält. Der Verweis ermöglicht den schnellstmöglichen Zugriff auf die jeweilige Zeile der Tabelle.
Die ROWID, der Verweis vom Index auf die Tabelle, ist ein eigener Oracle-Datentyp. Die ROWID ist der exakte Speicherort einer Zeile innerhalb einer Tabelle. Die ROWID setzt sich aus vier Komponenten zusammen: Objektnummer, Dateinummer relativ zum Tablespace, in dem das Objekt gespeichert ist, Datenblocknummer in der Datei und Zeile im Datenblock. Oracle9i garantiert, dass sich die ROWID einer Zeile in einer gegebenen Datenbank nicht ändert. Wenn Sie jedoch Daten aus einer Datenbank exportieren (mit dem Programm exp) und in eine andere Datenbank importieren (imp), ändern sich die ROWIDs der Zeilen. Somit ist es im Allgemeinen nicht sinnvoll, den ROWID Datentyp für Anwendungen zu verwenden. Oracle9i stellt das Paket DBMS_ROWID zur Verfügung, um ROWIDs, die in Basis64 Kodierung vorliegen, in die einzelnen Komponenten zerlegt anzuzeigen. Listing 4.32 zeigt die Verwendung des Pakets DBMS_ROWID. Listing 4.32: Abfrage der ROWID SQL>select department_name, rowid, dbms_rowid.rowid_object(rowid) as obj_nr, dbms_rowid.rowid_relative_fno(rowid) as file_nr, dbms_rowid.rowid_row_number(rowid) as row_nr from department; DEPARTMENT_NAME ROWID OBJ_NR FILE_NR ROW_NR --------------- ------------------ ---------- ---------- ---------PHILOSOPHY AAABUNAAHAAAABKAAA 5389 7 0 ECONOMICS AAABUNAAHAAAABKAAB 5389 7 1 BIOLOGY AAABUNAAHAAAABKAAC 5389 7 2 Sie haben die Möglichkeit, auf die Katalogsicht USER_OBJECTS zuzugreifen, um sich zu vergewissern, dass die Objektnummer 5389, die in der ROWID der Zeilen der Tabelle Department gespeichert ist, tatsächlich mit der Objektnummer der Tabelle Department übereinstimmt. Listing 4.33: Vergleich der Objektnummer aus einer ROWID mit der Objektnummer im Datenbankkatalog SQL> select object_id, object_name from user_objects where object_id=5389; OBJECT_ID OBJECT_NAME ---------- -----------5389 DEPARTMENT
Nachdem Sie nun einiges Hintergrundwissen über Indizes erworben haben, kehren wir nun zum Index selbst zurück. Ein Index umfasst eine oder mehrere Spalten einer Tabelle. Ein Index kann nur für eine Tabelle, jedoch nicht für eine Datensicht erstellt werden. Oracle9i stellt mehrere Arten von Indizes zur Verfügung: ● ● ● ● ●
Alle Indextypen sind persistent, d.h. sie werden permanent in einem Tablespace gespeichert und nicht zur Laufzeit aufgebaut. Ähnlich wie der Index eines Buches dazu dient, schnell Begriffe in einem Buch zu finden, werden Indizes im Datenbankbereich zur Beschleunigung des Zugriffs auf Tabellen eingesetzt. Am heutigen Tag beschäftigen wir uns lediglich mit dem gebräuchlichsten Indextyp - dem B-Baum Index. Die übrigen Indextypen lernen Sie am Tag 11 »Tabellen und Indizes für Fortgeschrittene« kennen. Die Ausführungen zum Thema ROWID im vorherigen Abschnitt gelten ohne Einschränkung für B-Baum Indizes. Bitmap Indizes speichern die ROWID nicht für jeden Eintrag im Index separat und sparen dadurch sehr viel Speicherplatz.
B-Baum Indizes B-Baum Indizes sind der Standard-Indextyp und dementsprechend der am häufigsten verwendete Indextyp. Indizes, die Primärschlüssel oder Integritätsbedingungen für Eindeutigkeit (unique constraint) implementieren, nutzen diesen Indextyp. Eine B- Baumstruktur wird grafisch als symmetrischer, invertierter Baum dargestellt, bei dem jedes Blatt eine Reihe von sortierten Indexwerten darstellt. ● ●
●
Ein B-Baum Index kann bis zu 33 Spalten haben. Ein B-Baum Index kann eindeutig (unique) oder mehrdeutig (nonunique) sein. Eindeutige Indizes implementieren Primärschlüssel und die Integritätsbedingungen für Eindeutigkeit. Ein B-Baum Index speichert keine Zeilen, die in allen indizierten Spalten den Wert NULL enthalten. Folglich können SQL Abfragen, die count(*) verwenden, nur dann durch einen B-Baum Index beschleunigt abgearbeitet werden, wenn mindestens eine der indizierten Spalten als NOT NULL definiert ist.
Die Kardinalität einer Spalte (Gruppe von Spalten) bezeichnet den Quotient aus der Anzahl unterschiedlicher Werte der Spalte (Gruppe von Spalten) und der Anzahl der Zeilen der zugrunde liegenden Tabelle (bzw. des Index). Mitunter wird auch von der Kardinalität eines Index gesprochen. Damit ist die Kardinalität der indizierten Spalten gemeint. Ein eindeutiger Index hat eine Kardinalität von 1. Ein Index mit wenigen unterschiedlichen Werten hat eine Kardinalität nahe bei 0. B-Baum Indizes sind gut geeignet, um Spalten mit hoher Kardinalität zu indizieren. Bitmap Indizes werden aufgrund ihrer Kompaktheit, die einerseits aus den Bitmaps an sich und andererseits aus einer zusätzlichen Komprimierung resultiert, eingesetzt, um Abfragen auf Spalten mit geringer Kardinalität zu beschleunigen.
Erstellen eines Index Es folgt die Syntax für die Erstellung eines B-Baum Index:
CREATE [UNIQUE] INDEX index_name ON table_name (column_name1, ..., column_nameN); Die Definition der Platzhalter lautet wie folgt: ●
● ●
index_name ist der Name des Index (entsprechend der Namenskonventionen für OracleDatenbankobjekte). table_name ist der Name der Tabelle, die indiziert wird. column_name1 bis column_nameN sind die indizierten Spalten.
Bitte beachten Sie, dass das Schlüsselwort unique optional ist. Wenn Sie unique nicht eingeben, wird ein nicht eindeutiger Index erstellt. Mit anderen Worten, der nicht eindeutige Index schränkt die Werte einer Spaltengruppe in keiner Weise ein. Wenn Sie das Schlüsselwort unique angeben, verhindert der Index, dass Zeilen mit identischen Spaltenwerten mehrfach in der Tabelle gespeichert werden. Listing 4.34 zeigt ein Beispiel. Der Primärschlüssel der Student-Tabelle lautet Student_ID. Allerdings fragen Sie die Student-Tabelle häufig nach dem Nachnamen eines Studenten ab. Um die Leistung solcher Abfragen zu verbessern, erstellen Sie einen nicht eindeutigen Index. Listing 4.34: Erstellung eines nicht eindeutigen Index SQL> create index Student_Last_Name on Student (Last_Name); Index created.
Warum man keine eindeutigen Indizes erstellen sollte Obwohl das Anlegen eines eindeutigen Index mit create unique index eine zulässige Anweisung ist, sollten Sie diese nicht verwenden; deklarieren Sie stattdessen primary key- und unique-Integritätsbedingungen. Die Hauptgründe für diesen Ratschlag sind: ●
●
Wenn Sie primary key- und unique-Integritätsbedingungen deklarieren, erstellt Oracle automatisch den entsprechenden eindeutigen Index, um diese Integritätsbedingungen durchzusetzen. Sie wären nicht in der Lage, eine foreign key-Integritätsbedingung zu deklarieren, wenn Sie nicht auch eine entsprechende primary key- Integritätsbedingung deklarieren.
4.7 Skript für die Erstellung der Beispieltabellen Da Sie nun die Grundlagen der Tabellenerstellung kennen gelernt haben, ist es an der Zeit, sich mit dem Skript für die Erstellung unserer Beispieltabellen zu befassen, das in Listing 4.35 wiedergegeben ist.
Sie finden dieses Skript unter dem Namen \sql\flugle.sql auf der CD-ROM. Zum Ausführen des Skripts können Sie SQL*Plus oder SQL*Plus Worksheet einsetzen. Geben Sie @D:\sql\flugle.sql ein (sofern die CD-ROM Laufwerk D ist), um das Skript auszuführen. Wenn Sie SQL*Plus Worksheet verwenden, können Sie die Datei mit Worksheet | Run Local Script auswählen und ausführen. Listing 4.35: Skript für die Erstellung der Beispieltabellen
constraint FK_Student_Schedule_Class foreign key (Class_ID) references Class (Class_ID)); create index Student_Last_Name on Student (Last_Name);
4.8 Zusammenfassung Bei der Implementierung Ihres logischen Entwurfs sollten Sie Folgendes berücksichtigen: ●
● ● ● ● ●
● ● ● ● ● ●
●
Verwenden Sie die create table-Anweisung mit Hilfe von SQL*Plus oder SQL Worksheet, um die Anweisungsoptionen besser steuern oder ein Skript verarbeiten zu können. Benutzen Sie die alter table-Anweisung, um eine vorhandene Tabelle zu ändern. Definieren Sie gegebenenfalls Standardwerte für Spalten. Jede Tabelle sollte über einen Primärschlüssel verfügen. Verwenden Sie Fremdschlüssel, um die referentielle Integrität zu gewährleisten. Sie können den Primärschlüssel für eine Tabelle mit Hilfe der create table- oder der alter table-Anweisung definieren. Sie können den Primärschlüssel einer Tabelle mit der alter table-Anweisung löschen. Sie können eine Integritätsbedingung mit der alter table-Anweisung deaktivieren oder aktivieren. Sie können eine Integritätsbedingung mit der alter table-Anweisung löschen. Ein Tabellenindex kann für bis zu 33 Spalten definiert werden. Ein Tabellenindex kann eindeutig oder nicht eindeutig sein. Erstellen Sie keine eindeutigen Indizes mit der create unique index-Anweisung; deklarieren Sie stattdessen primary key- und unique-Integritätsbedingungen, und lassen Sie die Indizes von Oracle automatisch erstellen. Verwenden Sie eine check-Integritätsbedingung, um den Wert einer Spalte auf einen festen oder berechneten Bereich zu beschränken.
4.9 Wie geht es weiter? Am Tag 5, »Einsatz von Oracle Designer in der Anwendungsentwicklung«, werden Sie etwas über die Anwendung eines Datenbank-Designwerkzeuges lernen. Oracle Designer wird benutzt, um ein logisches Datenmodell zu entwickeln und die physische Datenbank automatisch zu implementieren, indem SQL-DDLAnweisungen generiert werden, die Tabellen, Indizes und andere Objekte erzeugen. Oracle Designer ist ein umfassendes Mehrbenutzer-Design-Werkzeug, das es Ihnen erlaubt, Datenbanken und Anwendungen für ein großes, komplexes Unternehmen zu entwickeln.
4.10 Fragen und Antworten Frage: Wie hoch ist die ideale Anzahl von Indizes für eine Tabelle? Antwort: Die Antwort lautet: Es kommt darauf an. Zumindest wird ein Index automatisch durch die Deklarierung des Primärschlüssels erstellt. Darüber hinaus können Sie vielleicht einen nicht eindeutigen Index für die einzelnen Fremdschlüssel erstellen. Sie könnten auch einen nicht eindeutigen Index auf allen Spalten erstellen, die häufig Teil einer Abfragebedingung ist und die eine hohe Kardinalität haben. Von zusätzlichen Indizes für Tabellen mit geringer Zeilenanzahl - z.B. einhundert oder weniger - sollten Sie absehen. Frage: Welche Vorteile hat die Deklaration einer check-Integritätsbedingung für eine Spalte? Kann die Integritätsbedingung nicht in einer Anwendung wie Oracle Forms oder Visual Basic implementiert werden? Antwort:
Ja, es ist möglich und manchmal auch wünschenswert, eine Integritätsbedingung auch auf Anwendungsebene durchzusetzen. Sie sollten allerdings die deklarativen Integritätsbedingungen für eine Tabelle und ihre Spalten als erste »Verteidigungslinie« für die Datenintegrität betrachten. Da in der Client-Server- Umgebung eine große Anzahl von Werkzeugen eingesetzt wird, müssen Sie sicherstellen, dass niemand einer Spalte ungültige Werte zuordnen kann, egal welches Werkzeug benutzt wird. Frage: Wie kann man eine Spalte in einer Tabelle löschen oder umbenennen? Antwort: Sie können mit ALTER TABLE DROP COLUMN eine Spalte löschen. Oracle9i gestattet es nicht, eine Spalte umzubenennen. Wenn Sie eine Spalte umbenennen wollen, führen Sie ein CREATE TABLE AS SELECT durch und geben den neuen Spaltennamen als Aliasnamen des alten Spaltennamens an. Anschließend löschen Sie die alte Tabelle (DROP TABLE) und geben der neuen Tabelle den Namen der alten Tabelle (RENAME TABLE). Denken Sie daran, dass die neue Tabelle außer den not null-lntegritätsbedingungen keine weiteren Integritätsbedingungen der alten Tabelle erhält. Sie können auch eine Spalte mit dem gewünschten Namen hinzufügen und die Daten von der Spalte, die Sie umbenennen möchten, in die neue Spalte übernehmen. Abschließend löschen Sie die nicht mehr benötigte Spalte. Frage: Kann in einer check-Integritätsbedingung die SQL-Funktion sysdate verwendet werden, um abhängig von der Uhrzeit Änderungen zu erlauben oder zu verbieten? Antwort: Nein, die SQL-Funktionen sysdate, systimestamp, user, uid und userenv dürfen nicht in checkIntegritätsbedingungen verwendet werden.
4.11 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Was ist an dieser Anweisung falsch? CREATE TABLE new_table ( first_col number, second_col date third_col number default sysdate); 2. Beschreiben Sie eine SQL-Anweisung, welche die folgende Oracle-Fehlermeldung verursachen könnte: ORA-02266: unique/primary keys in table referenced by enabled foreign keys 3. Worin besteht der Unterschied zwischen den check-Integritätsbedingungen für eine Spalte und für eine Tabelle?
Übungen Die Instructor-Tabelle besitzt eine Spalte namens Position. In dem derzeitigen Tabellenentwurf gibt es eine check-Integritätsbedingung für die Spalte Position, welche den Wert auf assistant professor, associate professor und full professor begrenzt. Ändern Sie den Datenbankentwurf so, dass eine zusätzliche Tabelle, INSTRUCTOR_POSITION, verwendet wird, um zulässige Werte für die Position des Kursleiters anzugeben.
Logischer Datenbankentwurf Die Grundlage aller Datenbankanwendungen ist ein logisches Datenmodell. Wie bei allen Modellen handelt es sich bei einem logischen Datenmodell um die Idealform eines echten Systems. Ein Modell ist nur nützlich, wenn es auch genau ist. Wie ein reales Unternehmen verhält sich ein logisches Datenmodell eher dynamisch als statisch. Es muss sich in dem Maße entwickeln, wie sich das ihm zugrundeliegende Unternehmen verändert.
Ein logisches Datenmodell stellt sowohl die von einem Unternehmen verwendeten Datenelemente als auch die Beziehung zwischen diesen Datenelementen dar. Eine der gebräuchlichsten Methoden für die Entwicklung eines logischen Datenmodells ist die Erstellung eines Entity-Relationship-Modells, eines Modells, das Tabellen (Entity) und deren Beziehungen (Relationship) zueinander darstellt. Tabellen können Personen, Orte, Gegenstände oder Begriffe abbilden. Jede Tabelle wird anhand einer Reihe von Attributen beschrieben. In der Beispieldatenbank ist ein Kursleiter durch eine Anzahl von Attributen, wie z.B. Instructor_ID (Kursleiter-Identifikation), Last_Name (Nachname), First_Name (Vorname) und Department (Fachbereich) abgebildet. Der Administrator eines Fachbereichs benötigt eine Reihe weiterer Attribute für denselben Kursleiter, z.B. seine derzeitige Position. Wie Sie sehen, stellen die Anforderungen des Unternehmens den Motor für das Datenmodell dar. Tabellen existieren nicht im leeren Raum. Zwischen den Tabellen gibt es Beziehungen. Diese Beziehungen werden u.a. verwendet, um Integritätsbedingungen darzustellen. In der Beispielanwendung - einem Informationssystem für ein kleines College - muss eine Klasse von einem einzigen Kursleiter unterrichtet werden. Warum soll man ein logisches Datenmodell entwickeln - warum wendet man sich nicht gleich der Implementierung zu? Durch die Entwicklung eines logischen Datenmodells werden Sie gezwungen, sich mit den Daten einer Organisation und deren internen Beziehungen zueinander zu befassen, ohne sich gleich mit den Einzelheiten der Implementierung, wie z.B. dem Datentyp einer Spalte, auseinandersetzen zu müssen. Sie können sich das logische Datenmodell auf einer höheren Abstraktionsebene vorstellen als die Implementierung.
3.1 Theorie relationaler Datenbanken Kein Buch, das sich mit relationalen Datenbanken befasst, kann als vollständig betrachtet werden, wenn es nicht die Grundbegriffe der Theorie relationaler Datenbanken behandelt.
Das Konzept des NULL-Werts Ein großer Unterschied zwischen einer relationalen Datenbank und der Technologie älterer Datenbanken
liegt im Konzept des NULL-Werts. In einer nichtrelationalen Datenbank gibt ein bestimmter Wert das Fehlen eines Werts in einem Attribut an.
Die Kennzeichnung NULL gibt an, dass der Wert einer Spalte oder eines Ausdrucks nicht verfügbar ist oder nicht zugewiesen wurde. In einer relationalen Datenbank stellt der NULL-Wert einer Spalte verschiedene Konzepte dar: ● ●
Ein Wert für diese Spalte ist in der betreffenden Zeile nicht verfügbar. Der Spalte wurde noch kein Wert zugewiesen.
In einer relationalen Datenbank können Sie den Wert einer Spalte auf NULL setzen oder eine Spalte daraufhin prüfen, ob ihr Wert NULL ist. Es folgt eine kurze Zusammenfassung weiterer Begriffe aus dem Bereich relationaler Datenbanken: ●
●
●
● ●
● ● ● ●
Jede Tabelle verfügt über eine Reihe von Attributen, welche die Tabelle beschreiben. Eine Tabelle namens course (Kurs) würde die in einem College angebotenen Kurse beschreiben. Ein Tupel ist eine Einzelinstanz von Attributwerten. Ein Tupel der Tabelle course beschreibt z.B. einen einzelnen vom College angebotenen Kurs. Einige Attribute identifizieren die einzelnen Tupel einer Tabelle eindeutig. Diese Attribute bezeichnet man als Primärschlüssel. Für die Tabelle, welche die Studenten beschreibt, ist das Attribut Student_ID (Studenten-Identifikation) der Primärschlüssel, da es die einzelnen Studenten eindeutig identifiziert. Keines der Attribute, die den Primärschlüssel bilden, kann einen NULL-Wert haben. Ein Fremdschlüssel ist eine Attributgruppe (ein oder mehrere Attribute) einer Tabelle, deren Werte als Primärschlüssel in einer anderen Tabelle vorkommen müssen. Tabellen werden zueinander in Beziehung gesetzt. Die Reihenfolge der Tupel innerhalb einer Tabelle ist beliebig. Die Reihenfolge der Attribute einer Tabelle ist beliebig. Unter Schema versteht man eine Gruppe von Datenbankobjekten wie z.B. Tabellen, Spalten, Primärschlüssel, Fremdschlüssel, die ein logisches Datenmodell implementieren.
Eine Tabelle besteht aus Zeilen und Spalten Aus der Sicht eines Praktikers wird eine Tabelle im Entity-Relationship-Modell als konkretes Datenbankobjekt vom Typ TABLE in einem RDBMS implementiert. Die Attribute einer Tabelle werden als Tabellenspalten implementiert. Tupel entsprechen einem einzelnen Satz Attribute oder Spaltenwerte und werden als Zeile bezeichnet. Die Begriffe Zeile und Datensatz haben dabei dieselbe Bedeutung.
Eine Spalte in einer Datenbanktabelle stellt die Attribute einer Tabelle dar.
Eine Zeile ist eine einzelne Gruppe von Attributen oder Spaltenwerten in einer Datenbanktabelle.
Ein Datensatz ist dasselbe wie eine Zeile - es handelt sich um eine einzelne Gruppe von Attributen oder Spaltenwerten.
Sie können nicht anhand eines Verzeichnisses im Dateisystem feststellen, welche Tabellen in einer Oracle-Datenbank gespeichert sind, da das Oracle-RDBMS die interne Struktur seiner Dateien verwaltet. Bei einem Dateiverwaltungssystem wie dBASE wird jede »Tabelle« als separate Datei in einem Verzeichnis gespeichert.
Die Reihenfolge der Zeilen ist beliebig Ein Grundsatz der Theorie relationaler Datenbanken ist, dass eine Tabelle keine implizite Reihenfolge hat. Die einzige Möglichkeit, die Reihenfolge zu steuern, in der die Zeilen einer Tabelle abgerufen werden, ist es, diese Reihenfolge explizit in der Abfrage vorzugeben. Das Konzept der Unabhängigkeit vom Speicherort eines Tupels ist wirksam, da Sie auf diese Weise die Tabellen abstrakt betrachten und in den meisten Fällen die physische Implementierung von Datenbankstrukturen ignorieren können.
Die Reihenfolge der Spalten ist beliebig Wie die Zeilen einer Tabelle haben auch die Spalten keine implizite Reihenfolge. Wenn Sie für die Ausgabe einer Tabellenbeschreibung SQL*Plus verwenden, gibt SQL*Plus die Spalten in der Reihenfolge zurück, in der sie erstellt wurden. Sie können allerdings eine beliebige Reihenfolge vorgeben, in der die Spalten abgerufen werden sollen. Darüber hinaus können Sie die Definition einer Spalte ändern, ohne dadurch andere Spalten zu beeinträchtigen. So haben Sie zum Beispiel die Möglichkeit, die Kapazität einer Spalte zu vergrößern, ohne auch nur eine der bestehenden Tabellendefinitionen oder SQL- Anweisungen ändern zu müssen. Die Beachtung der mit der Änderung verbundenen physischen Details ist Aufgabe des relationalen Datenbank-Managementsystems (RDBMS) von Oracle. Eine relationale Datenbank soll für logische Datenunabhängigkeit sorgen, da die Definitionen der einzelnen Spalten voneinander unabhängig sind.
3.2 Datenintegrität Nach der relationalen Theorie verfügt jede Tabelle über eine Reihe von Attributen, welche die einzelnen Tupel dieser Tabelle eindeutig identifizieren. Die relationale Theorie sagt auch aus, dass in einer Tabelle keine Tupel doppelt vorhanden sein können, was ganz einfach bedeutet, dass jede Tabelle einen Primärschlüssel haben muss. Dieses Konzept wird als Datenintegrität bezeichnet. Eindeutig ist zum Beispiel die Sozialversicherungsnummer der einzelnen Mitarbeiter.
Primärschlüssel Jede Tabelle verfügt über eine Gruppe von Attributen, die einen Datensatz eindeutig identifizieren. Diese Attributgruppe wird als Primärschlüssel bezeichnet. Der Primärschlüssel kann aus einem einzelnen Attribut - ein Student wird durch Student ID (Studenten-Bezeichnung) eindeutig identifiziert - oder mehreren Attributen bestehen. Manchmal ist es offensichtlich, welche Attribute den Primärschlüssel bilden, manchmal nicht. Um festzustellen, ob Ihre Vorstellungen von einem Primärschlüssel richtig sind, müssen Sie sich vorhandene Daten anschauen. Sie müssen aber auch mit Personen sprechen, welche die Art und Weise kennen, in der die Organisation arbeitet. Verlassen Sie sich bei der Validierung Ihrer Vorstellungen von einem Primärschlüssel nicht nur auf vorhandene Daten.
Ein Primärschlüssel ist eine Gruppe von einem oder mehreren Attributen, die eine Zeile eindeutig identifizieren.
Kein Teil des Primärschlüssels hat einen NULL-Wert Ein Grundsatz der relationalen Theorie ist, dass kein Teil des Primärschlüssels einen NULL-Wert haben kann. Wenn Sie eine Weile darüber nachdenken, erscheint diese Tatsache intuitiv klar. Der Primärschlüssel muss die einzelnen Tupel einer Tabelle eindeutig identifizieren. Wenn der Primärschlüssel (oder ein Teil davon) einen NULL- Wert hätte, könnte der Primärschlüssel nichts identifizieren. Ein Kurs, der über keine Course ID verfügt, kann weder identifiziert noch in irgendeiner Weise verarbeitet werden.
Referentielle Integrität Tabellen werden mit Hilfe von Fremdschlüsseln zueinander in Beziehung gesetzt. Ein Fremdschlüssel besteht aus einer oder mehreren Spalten, für die eine Gruppe möglicher Werte im Primärschlüssel einer zweiten Tabelle zu finden ist. Referentielle Integrität wird erreicht, wenn die Werte in der Spalte eines Fremdschlüssels auf den referenzierten Primärschlüssel oder auf den NULL-Wert eingeschränkt werden. Hat der Datenbankentwickler Primär- und Fremdschlüssel erst einmal deklariert, ist die Gewährleistung der Datenintegrität und der referentiellen Integrität Aufgabe des RDBMS.
Ein Fremdschlüssel besteht aus einer oder mehreren Spalten, deren Werte im Primärschlüssel einer anderen Tabelle wiederzufinden sein müssen.
Beziehungen Die Verbindung zwischen zwei Tabellen wird als Beziehung bezeichnet. In einer Beziehung wird eine Tabelle als übergeordnet und die andere als untergeordnet bezeichnet. Eine Beziehung zwischen den Tabellen A und B kann aus der Sicht der Tabelle A oder aus der Sicht der Tabelle B betrachtet werden. Es ist denkbar, dass je nach Betrachtungsrichtung die eine bzw. die andere Tabelle als untergeordnet gilt. Ein Beispiel für eine Beziehung mit je nach Betrachtungsrichtung unterschiedlicher untergeordneter Tabelle ist die Beziehung zwischen Auftrag und Auftragsposition. Eine Auftragsposition kann nicht ohne
Auftrag existieren und gilt folglich als untergeordnet. Ein Auftrag ohne Auftragspositionen ist nicht sinnvoll, daher kann der Auftrag seinerseits als untergeordnet gelten. Eine Beziehung wird durch folgende Merkmale definiert: ●
●
●
Identifizierend oder nichtidentifizierend: Bei einer nichtidentifizierenden Beziehung bildet der Primärschlüssel der übergeordneten Tabelle einen Teil des Primärschlüssels der untergeordneten Tabelle. In einer identifizierenden Beziehung ist der Primärschlüssel der übergeordneten Tabelle nicht Teil des Primärschlüssels der untergeordneten Tabelle. Kardinalität: Die Kardinalität einer Beziehung bezeichnet die Anzahl der Zeilen einer untergeordneten Tabelle, die sich auf eine einzelne Zeile einer übergeordneten Tabelle beziehen. Die Kardinalität kann die Werte null (0), eins (1) oder mehrere (n) annehmen. Zum Beispiel kann ein Kursleiter, der sich mit Grundlagenforschung beschäftigt, keine oder mehrere Klassen unterrichten. Eine Fachabteilung muss jedoch einen oder mehrere Kurse anbieten, andernfalls wäre sie keine Fachabteilung. Erforderlich oder optional: Eine Beziehung ist erforderlich, wenn sich eine Zeile in einer untergeordneten Tabelle auf eine Zeile in einer übergeordneten Tabelle beziehen muss oder umgekehrt. Die Beziehung ist optional, wenn eine Zeile in einer untergeordneten Tabelle ohne Bezug zu einer Zeile in einer übergeordneten Tabelle auskommt oder umgekehrt. Bei einer optionalen Beziehung darf der Fremdschlüssel der untergeordneten Tabelle den NULL-Wert annehmen. Die oben erwähnte Beziehung zwischen Auftrag und Auftragsposition ist ein Beispiel für eine Beziehung, die von beiden Seiten als erforderlich modelliert werden kann.
●
Integritätsregeln: Diese Regeln definieren, welche Maßnahmen ergriffen werden müssen, wenn eine Zeile in einer übergeordneten oder untergeordneten Tabelle auf irgendeine Weise geändert wird. Es gibt sechs Möglichkeiten: Eine Zeile der übergeordneten Tabelle wird hinzugefügt, geändert oder entfernt, oder eine Zeile der untergeordneten Tabelle wird hinzugefügt, geändert oder entfernt. Für jede dieser Möglichkeiten gibt es mehrere anwendbare Maßnahmen - die Aktion zu verhindern, den Fremdschlüsselwert in der untergeordneten Tabelle auf den NULL-Wert zu setzen, die Operation in Bezug auf die untergeordnete Tabelle zu kaskadieren oder den Schlüsselwert der über- oder untergeordneten Tabelle auf einen Standardwert zu setzen. Am Beispiel des Löschens in der übergeordneten Tabelle bedeutet Kaskadieren, dass in allen untergeordneten Tabellen diejenigen Datensätze, die sich auf den zu löschenden Datensatz in der übergeordneten Tabelle beziehen, ebenfalls gelöscht werden.
Normalisierung Es ist sehr einfach, eine Tabelle in einer Oracle-Datenbank zu erstellen. Sie können dazu aus einer Vielzahl von Werkzeugen auswählen - Enterprise Manager, SQL*Plus, SQL*Plus Worksheet u.a. Am Tag 4, »Implementierung des logischen Modells: physischer Datenbankentwurf«, werden Sie mehr über diese Werkzeuge erfahren. Es ist jedoch wichtig, dass Sie sich die Zeit nehmen, den optimalen Entwurf für die Datenbank Ihrer Anwendung zu prüfen. An dieser Stelle kommt ein Aspekt aus der Theorie relationaler Datenbanken ins Spiel. Bei der Normalisierungstheorie handelt es sich um die Untersuchung von Tabellen, Attributen (Spalten) und der Abhängigkeit der Attribute untereinander. Die Ziele der Normalisierung lauten wie folgt: ● ● ●
Minimieren redundanter Daten Vermeiden von Änderungsanomalien Verhindern inkonsistenter Daten
●
Entwerfen von Datenstrukturen, die eine einfache Pflege erlauben
Da es sich hierbei um die Theorie handelt, müssen Sie einige wichtige Begriffe verstehen. In Tabelle 3.1 sind diese Begriffe in drei Kategorien eingeteilt - Theoretiker, Analytiker und Entwickler. Bei der Lektüre von Unterlagen zum Thema Datenbanken, sowohl wissenschaftlicher als auch kommerzieller Art, begegnen Ihnen viele verschiedene Begriffe. Wenn Sie diese Lektion lesen, werden Sie feststellen, dass einige der Begriffe synonym verwendet werden. Sie können die Begriffe verwenden, die Sie bevorzugen, solange Sie diese richtig anwenden und verstehen, was sie bedeuten. Ein Professor der Informatik schreibt zum Beispiel über die Relation XYZ; ein Anwendungsentwickler könnte dieselbe Sache als Tabelle XYZ bezeichnen. Die Normalisierungstheorie bezeichnet die gewünschte Anordnung von Tabellen und Spalten als Normalformen. Dieses Kapitel befasst sich mit der ersten, zweiten und dritten Normalform, welche auch häufig 1NF, 2NF und 3NF genannt werden. Obwohl diese Begriffe theoretisch und abstrakt klingen, sind sie eigentlich recht intuitiv. Weitere Normalformen - Boyce-Codd, 4. und 5. Normalform - beziehen sich auf komplexere Normalisierungsaspekte. Diese Themen gehen aber über den Rahmen dieses Buches hinaus.
Normalisierungsregel 1: Jede Spalte sollte genau einen Teil der Informationen enthalten Eine Relation (Tabelle) befindet sich in der ersten Normalform (1NF), wenn alle ihre Attribute atomarer Form sind. Der Begriff atomar sagt aus, dass jedes Attribut aus einer einzigen Information über diese Relation besteht. Wenn zum Beispiel eine Relation zum Speichern von Informationen über Mitarbeiter bestimmt ist, würden Sie kein Einzelattribut, Dependents (Untergebene), verwenden, um die Namen der Untergebenen eines Mitarbeiters zu speichern. Einige Mitarbeiter könnten keine, andere dagegen viele Untergebene haben.
Normalisierungsregel 2: Alle Tupel hängen nur vom Primärschlüssel ab Bei einer Relation in der zweiten Normalform (2NF) dürfen alle Spalten nur vom Primärschlüssel abhängen. In einfachen Worten bedeutet diese Regel, dass eine Tabelle keine Fremdinformationen enthalten darf. Zusätzlich zu anderen Informationen bezeichnet die Class-Tabelle zum Beispiel alle Kurse und deren entsprechende Kursleiter. Der Kursleiter wird durch die Spalte Instructor ID (KursleiterBezeichnung) identifiziert. Der Primärschlüssel der Tabelle Class ist die Class ID (Klassen-Bezeichnung). Wenn die Class-Tabelle auch die Position des Kursleiters enthalten würde, hätte die Tabelle nicht die zweite Normalform; die Position des Kursleiters hängt nur von der Instructor ID und nicht vom Primärschlüssel ab.
Normalisierungsregel 3: Alle Spalten hängen einzig und allein vom Primärschlüssel ab Um die dritte Normalform (3NF) einzunehmen, müssen die Spalten einer Tabelle vollständig vom Primärschlüssel abhängen. Das Schlüsselwort in diesem letzten Satz lautet vollständig. Jede Spalte der Tabelle muss vom gesamten Primärschlüssel abhängen, nicht nur von einem Teil. Die Course-Tabelle identifiziert zum Beispiel jeden einzelnen Kurs sowie die Fachabteilung, welche diesen Kursus anbietet. Angenommen, der Primärschlüssel bestünde aus der Department ID und der Course ID, weil die Course ID nur innerhalb eines Fachbereiches eindeutig ist. Beide Spalten wären notwendig, um eine Zeile in der Tabelle eindeutig zu identifizieren. Wenn die Course-Tabelle auch eine
Spalte zur Ablage des Fachbereichsleiters enthielte, würde sich die Tabelle nicht in der dritten Normalform befinden. Der Fachbereichsleiter hängt nur von der Department ID und nicht vom gesamten Primärschlüssel ab. Somit würde der Fachbereichsleiter nur von einem Teil des Primärschlüssels, jedoch nicht vollständig vom Primärschlüssel abhängen. Manchmal wird dieses Konzept auch als abgeleitete Spalte bezeichnet. Im obigen Beispiel kann Department Chairperson (Fachbereichsleiter) von Department ID abgeleitet werden. Nach der relationalen Theorie sollte eine Tabelle keine abgeleiteten Spalten enthalten. In der Praxis enthalten Tabellen allerdings häufig abgeleitete Spalten.
Anwenden der Normalisierung auf den Datenbankentwurf Die Normalisierungstheorie nennt auch Normalformen, die über die dritte Normalform (3NF) hinausgehen, ich möchte dieses Thema an dieser Stelle aber nicht weiter vertiefen. Dagegen würde ich gern über die Anwendung der Normalisierungstheorie sprechen. Wenn Sie Artikel über relationale Datenbanken lesen, treffen Sie auf eine »große Debatte«. Puristen aus dem Bereich der relationalen Theorie meinen, dass alle Tabellen zumindest in der 3NF vorliegen müssten, obwohl Praktiker argumentieren, dass man die Normalform einer Datenbank aufheben - mit anderen Worten, den Datenbankentwurf von der 3NF auf die 2NF reduzieren - muss, um eine annehmbare Leistung zu erzielen. Meine Meinung liegt irgendwo dazwischen, und ich möchte folgendes empfehlen.
3.3 Werkzeuge für die Erstellung von Entity- RelationshipDiagrammen Um Ihnen bei der Entwicklung eines logischen Datenmodells behilflich zu sein, sollten Sie ernsthaft den Einsatz eines Werkzeugs für die Erstellung von Entity-Relationship- Diagrammen, wie z.B. Oracle Designer, ERwin von LogicWorks oder Sybase S-Designer, in Erwägung ziehen. Alle diese Werkzeuge sind für Microsoft Windows Systeme erhältlich. Kurz gesagt ermöglichen Ihnen diese Werkzeuge, ein Entity-Relationship-Diagramm zu erstellen, indem Sie Symbole aus einer Werkzeugleiste auswählen und Beziehungen zwischen diesen Symbolen zeichnen. Diese Werkzeuge haben einige Vorzüge: ● ●
●
Sie vereinfachen den Prozess der Diagrammerstellung. Sie erzeugen automatisch die SQL-Anweisungen für die Erstellung von Datenbankobjekten wie Tabellen, Indizes und Integritätsbedingungen. Sie erlauben Ihnen, die einzelnen Tabellen, Attribute, Beziehungen und Integritätsbedingungen zu dokumentieren. Diese Werkzeuge erzeugen außerdem Berichte, die von anderen Entwicklern und Benutzern verwendet werden können, damit Sie Rückmeldungen über die Genauigkeit des Datenmodells erhalten.
Abbildung 3.1: Ein mit Oracle Designer erstelltes Entity-Relationship-Diagramm
3.4 Star Schema Der Datenbankentwurf für ein Data Warehouse ist gewöhnlich ein so genanntes Star Schema (sternförmiges Entity-Relationship-Modell). Das Star Schema hat seinen Namen von der sternförmigen Anordnung mehrerer Dimensionstabellen rund um eine Faktentabelle. Eine Faktentabelle enthält Maßzahlen für das Geschäftsergebnis eines Unternehmens, z.B. Verkäufe. Jede Maßzahl ist über Fremdschlüsselbeziehungen in Bezug zu Dimensionen gesetzt, welche eine Maßzahl genau charakterisieren. Ein Verkauf ereignet sich z.B. in einem bestimmten Quartal (Zeitraum) in einer bestimmten Region und wird mit einem Kunden getätigt. Zeitraum, Region und Kunde sind in diesem Beispiel Dimensionstabellen. Zwischen einer Dimension und der Faktentabelle besteht jeweils eine 1:n Beziehung, d.h. ein Fremdschlüssel in der Faktentabelle bezieht sich auf einen Primärschlüssel in einer Dimensionstabelle.
Abbildung 3.2: Star Schema Datenmodell eines Data Warehouse
3.5 Das Beispielschema
Dieses Buch verwendet ein Datenbankschema für ein kleines, fiktives College als Beispiel. Im Jahr 1884 stiftete Reginald Flugle, ein wohlhabender Manschettenknopf- und Krawattennadelfabrikant im Norden der südkalifornischen Wüste, einem kleinen, von der Dürre geplagten College einen beträchtlichen Geldbetrag. Bisher erfolgte die Verwaltung des Flugle-College auf Papier. Der Dekan des College hat Ihnen den Auftrag für die Entwicklung eines computergestützten Informationssystems auf der Basis einer Client- Server-Architektur erteilt.
Anforderungen Die Anforderungen an das Informationssystem des Flugle-College umfassen folgende Punkte: ● ● ● ● ● ● ●
Informationen über die Studenten speichern, pflegen und abrufen Informationen über die Kursleiter speichern, pflegen und abrufen Informationen über die einzelnen Fachbereiche speichern, pflegen und abrufen Informationen über angebotene Kurse speichern, pflegen und abrufen Informationen über die Kurstermine speichern, pflegen und abrufen den Studenten ermöglichen, Stundenpläne elektronisch zusammenzustellen pro Semester einen Kurskatalog erstellen
Unter Berücksichtigung dieser Anforderungen werden folgende Tabellen benötigt, um das Informationsbedürfnis des Flugle-College zu befriedigen: ● ● ● ● ●
●
●
●
●
Die Student-Tabelle enthält Informationen über die einzelnen Studenten dieses College. Die Department-Tabelle umfasst Informationen über die einzelnen Fachbereiche des College. Die Instructor-Tabelle enthält Informationen über die einzelnen Kursleiter am College. Die Course-Tabelle nennt die einzelnen vom College angebotenen Kurse. Die Class-Location-Tabelle bezeichnet die einzelnen Klassenräume, die für die Stunden zur Verfügung stehen. Die Schedule-Type-Tabelle enthält die Art des Stundenplans - ein Stundenplan kann zum Beispiel zwei Veranstaltungen pro Woche enthalten. Die Schedule-Type-Details-Tabelle enthält Einzelheiten über eine bestimmte Art von Stundenplänen, z.B. an welchen Tagen und um wie viel Uhr die Veranstaltungen stattfinden. Die Class-Tabelle enthält Informationen über alle geplanten Stunden, wie z.B. Kurs, Kursleiter und Stundenplan. Unter Klasse versteht man das Kursangebot während eines bestimmten Semesters. Die Student-Schedule-Tabelle identifiziert die Klassen, für die sich ein Student eingeschrieben hat.
Im nächsten Abschnitt wenden wir uns den Tabellen im einzelnen zu.
Die Student-Tabelle Der Zweck der Student-Tabelle liegt in der Pflege von Informationen über die einzelnen Studenten am Flugle-College. Jeder Student wird durch eine Student ID identifiziert - die Tabelle ist so definiert, dass zwei Studenten niemals dieselbe Student ID haben können. Zusätzlich zum Namen des Studenten, der Adresse und anderen wichtigen Informationen enthält die Student-Tabelle auch ein Attribut namens Year (Jahr), das den Jahrgang eines Studenten angibt - zum Beispiel Erstsemester. In dieser Hinsicht könnte diese Beispieldatenbank etwas unrealistisch sein; es ist wahrscheinlich wichtiger, die absolvierten Lerneinheiten als den Jahrgang zu verfolgen. Eine weitere Anmerkung: In dieser Tabelle ist der Status der Studenten nicht angegeben - z.B. aktiv, in Urlaub, inaktiv. Es gibt kein Attribut, welches angibt, ob ein Student einen Abschluss erlangt hat. Trotzdem sind die in der folgenden Student-Tabelle angegebenen Attribute für unsere Zwecke realistisch genug.
Die Department-Tabelle Sorgen Sie beim Erstellen der Department-Tabelle dafür, dass andere Tabellen sich in konsistenter Weise auf einen Fachbereich beziehen. Die Liste für die Department-Tabelle zeigt deren einfache Struktur. Der Primärschlüssel für die Department-Tabelle lautet Department ID.
Die Instructor-Tabelle Informationen über die einzelnen Kursleiter werden in der Instructor-Tabelle gespeichert, wie in der folgenden Liste dargestellt. Der Primärschlüssel für diese Tabelle lautet Instructor ID. Außerdem gibt es einen Fremdschlüssel - Department ID - der auf die Department-Tabelle verweist. Im Datenmodell des Flugle College wird jeder Kursleiter einem einzigen Fachbereich zugeordnet, der durch die Department ID gekennzeichnet ist. Für ein echtes College oder eine Universität würden Sie eine derart künstliche Einschränkung wohl nicht vornehmen.
Die Course-Tabelle Jeder Fachbereich bietet eine Reihe von Kursen an. Jeder Kurs hat eine eindeutige Course ID. Weitere Attribute eines Kurses sind Titel, Beschreibung, Anzahl der Lerneinheiten und eventuell erforderliche Zusatzgebühren.
Die Class-Location-Tabelle Anhand dieser Tabelle kann ein College-Administrator den geeigneten Ort für eine Klasse anhand der vorhandenen Sitzplätze ermitteln. Der Primärschlüssel für die Class-Location- Tabelle besteht aus dem Gebäude und dem Klassenraum, wie in der folgenden Liste angegeben. Bei einer realen Anwendung würden Sie vielleicht mehr über den Standort der Klasse erfahren wollen - z.B. andere Ausstattungsmerkmale, wie Projektionswände oder Mikrofone.
Die Schedule-Type-Tabelle Der Zweck dieser Tabelle ist die Beschreibung verschiedener Arten von Stundenplantypen. Ein Beispiel für einen Stundenplantyp sind drei Veranstaltungen pro Woche. Der Primärschlüssel dieser Tabelle lautet Schedule ID (Stundenplan- Bezeichnung). Die folgende Liste enthält eine Beschreibung der Schedule Type-Tabelle.
Die Schedule-Type-Details-Tabelle Die Schedule-Type-Details-Tabelle enthält Einzelheiten über einen bestimmten Stundenplantyp, wie z.B. Tag, Anfangszeit und Dauer der einzelnen Veranstaltungen einer Klasse für diesen Stundenplantyp. Wenn zum Beispiel die Schedule ID T10 drei Veranstaltungen pro Woche um 10 Uhr morgens umfasst, gibt es in der Schedule-Type- Details-Tabelle drei Zeilen. Eine Zeile enthält einen Eintrag für 10 Uhr montags, eine andere einen Eintrag für 10 Uhr mittwochs und die letzte Zeile einen Eintrag für 10 Uhr freitags. Da die Dauer für jede Zeile angegeben wird, kann die Tabelle auch einen Stundenplantyp enthalten, bei der sich die Dauer der Veranstaltung an den einzelnen Tagen unterscheidet. Im Folgenden wird die Struktur der Schedule-Type-Details-Tabelle beschrieben.
Die Class-Tabelle Die Class-Tabelle enthält die Besonderheiten der einzelnen vom College angebotenen Kurse für ein
bestimmtes Semester. Sie können sich eine Klasse als Beispiel für einen Kurs vorstellen - daher muss die Class-Tabelle die Course ID und die Department ID enthalten. In der folgenden Liste ist die Struktur der Class-Tabelle dargestellt. Für jede Klasse muss ein Ort und ein Stundenplan angegeben sein. Und natürlich muss jede Klasse von einem Kursleiter unterrichtet werden.
Die Student-Schedule-Tabelle Die Student-Schedule-Tabelle dient zwei Zwecken. Erstens verzeichnet sie die Zensur, die ein Student in den einzelnen Klassen erreicht hat. Zweitens identifiziert sie für die noch nicht abgeschlossenen Klassen, welcher Kurs Teil des aktuellen Stundenplans eines Studenten ist. In realistischeren Datenbanken würden Sie wahrscheinlich die Klassen, die ein Student bereits abgeschlossen hat, von seinem aktuellen Stundenplan unterscheiden. Der Primärschlüssel der Tabelle setzt sich aus Student ID und Class ID zusammen. Im Folgenden wird die Struktur der Student-Schedule-Tabelle beschrieben.
3.6 Terminologie zu Oracle Bevor Sie am Tag 4 lernen, wie man eine Tabelle erstellt, müssen Sie sich mit der verwendeten Terminologie vertraut machen. Tabellen werden von Oracle in so genannten Tablespaces abgelegt.
Ein Tablespace ist ein Behälter für Datenbankobjekte. Ein Tablespace besteht aus bis zu 1022 Datendateien. Oracle kann Dateien in einem Dateisystem oder nicht mit einem Dateisystem formatierte Plattenbereiche, so genannte Raw Devices, als Datendatei nutzen. Oracle9i kann auf den UNIX und Windows Plattformen Datendateien mit einer Größe von mehr als 4 GB verwalten. Wenn Sie Tablespaces nach dem Muster in Listing 3.1 anlegen, verwendet Oracle Bitmaps, um eine performante Freispeicherverwaltung auf dem Tablespace selbst (Locally Managed Tablespace) und den enthaltenen Datenbankobjekten (Automatic Segment Space Management) zu implementieren. Listing 3.1: Anlegen eines Tablespace CREATE TABLESPACE tablespace_name DATAFILE 'file_spec' SIZE nM [AUTOEXTEND ON [NEXT kM] [MAXSIZE mM]] EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO; Die Bedeutungen der Platzhalter sind: ● ●
●
tablespace_name ist der Name des Tablespace. file_spec ist der absolute Pfad einer Datei oder eines Raw Device, z.B. d:\oradata\users.dbf unter Windows oder /oradata/users.dbf unter UNIX. n ist die Größe der Datei bzw. des Raw Device (abzüglich eines Oracle Blocks) in MB.
Die Autoextend-Klausel kann für Datendateien in Dateisystemen verwendet werden und bewirkt, dass die Datei bei Platzmangel automatisch um k MB vergrößert wird, bis zu einer maximalen Größe von m MB. Einige der wichtigsten Begriffe sind Datenbank, Instanz, Benutzer und Datenbanksitzung. Eine OracleDatenbank setzt sich aus Tablespaces und weiteren Dateien für besondere Aufgaben zusammen. Die Änderungen an der Datenbank werden in Redo Logs protokolliert. Um Änderungen rückgängig machen zu können, hebt Oracle vormals gültige Daten in Undo Segmenten in einem Undo Tablespace auf. Die Kontrolldatei enthält Verwaltungsinformation für die Datenbank. Diese Verwaltungsinformation beinhaltet
z.B. welche Datendateien und Tablespaces zur Datenbank gehören.
Eine Oracle-Datenbank besteht aus dem Datenbankkatalog (Data Dictionary) im SYSTEM Tablespace, Kontrolldatei(en) (Controlfile), Änderungsprotokolldateien (Online Redo Logs) und weiteren Tablespaces für Temporärsegmente (z.B. TEMP), Undo Segmente (z.B. UNDOTBS) und Benutzerspeicherbereiche (z.B. Tablespace USERS). Die maximale Größe einer Oracle-Datenbank liegt bei mehr als einem Petabyte (1024 Terabyte). Online Redo Logs werden periodisch überschrieben. Um die Wiederherstellbarkeit der Datenbank z.B. nach einem Festplattendefekt zu gewährleisten, sollte eine Oracle-Datenbank im ARCHIVELOG-Modus betrieben werden. In diesem Modus werden Offline Redo Logs, das sind exakte Kopien der Online Redo Logs, erstellt. Offline Redo Logs werden nicht von Oracle überschrieben. Die gesamte Änderungshistorie einer Datenbank kann so über Monate und Jahre aufbewahrt werden.
Eine Oracle-Instanz besteht aus Prozessen und Speicherstrukturen. Eine Instanz öffnet genau eine Datenbank. Jeder Zugriff auf eine Oracle-Datenbank wird durch eine OracleInstanz durchgeführt. Jede Oracle-Instanz ist durch die Umgebungsvariable ORACLE_SID (Oracle System IDentifier) eindeutig identifiziert. Eine Oracle-Instanz ist auf Windows Systemen durch das Programm oracle.exe und auf UNIX Systemen durch das Programm oracle implementiert. Die Programme oracle.exe bzw. oracle liegen in einem Verzeichnisbaum, der bei der Installation der Oracle Distribution vom Oracle Installer aufgebaut wird. Die Wurzel dieses Verzeichnisbaums wird als ORACLE_HOME bezeichnet. Auf Windows Systemen wird das ORACLE_HOME in die Registry eingetragen. Auf UNIX Systemen ist ORACLE_HOME eine Umgebungsvariable und das Programm oracle befindet sich in $ORACLE_HOME/bin. Eine Oracle-Instanz kann sich in Bezug auf die Datenbank in verschiedenen Zuständen befinden. Man unterscheidet drei Phasen, die durchlaufen werden müssen, um Änderungen an einer Datenbank durchzuführen. Die drei Phasen und die Befehle, um in die jeweilige Phase zu gelangen, sind: 1. STARTUP NOMOUNT - die Initialisierungsparameter werden gelesen und die Instanz wird gestartet. 2. ALTER DATABASE MOUNT - die Kontrolldatei wird geöffnet und es wird ermittelt, welche Datei den Datenbankkatalog enthält sowie welche weiteren Dateien zur Datenbank gehören. 3. ALTER DATABASE OPEN - die Datendateien der Tablespaces werden geöffnet. Eine Redo Log Datei (Current Redo Log) wird geöffnet, um Änderungen an der Datenbank zu protokollieren. Sobald Phase 3 durchlaufen wurde, können Benutzer (z.B. FLUGLE) mit den Tabellen arbeiten. Zwei weitere Befehle zum Starten einer Instanz sind bedeutsam. Mit STARTUP MOUNT erreichen Sie Phase 2, mit STARTUP unmittelbar Phase 3. Normalerweise wird die Datenbank in Phase 3 zum Schreiben und Lesen geöffnet (OPEN READ WRITE). Es gibt aber auch die Möglichkeit, mit ALTER DATABASE OPEN READ ONLY die Datenbank ausschließlich zum Lesen zu öffnen. Sie sind somit in der Lage, Tablespaces einer Oracle- Datenbank auf nicht beschreibbaren Medien wie z.B. CD-ROM zu legen. Lediglich die Kontrolldatei muss immer auf einem beschreibbaren Medium liegen.
Abbildung 3.3: Architektur des Oracle RDBMS mit Anwendungsprogrammen (oben), der OracleInstanz (Mitte) und der Datenbank (unten). Pfeile stellen einen kleinen Ausschnitt der funktionalen Beziehungen zwischen den Komponenten dar Die Prozesse einer Oracle-Instanz werden weiter in die Kategorien Hintergrundprozess, Serverprozess und Dispatcher untergliedert. Anwendungsprogramme kommunizieren mit einem Dispatcher oder einem dedizierten Serverprozess. Zu den Hintergrundprozessen zählt z.B. der Database Writer (DBW0), der veränderte Datenbankblöcke aus dem als System Global Area (SGA) bezeichneten Speicherbereich der Instanz auf einen Festplattenspeicher schreibt. Der Hintergrundprozess Log Writer (LGWR) schreibt Änderungsprotokolle aus dem Log Buffer in der SGA in die aktuelle Online Redo Log Datei.
Zum Starten einer Oracle-Instanz wird eine Initialisierungsdatei benötigt. Der Name der Datei auf Windows ist \database\init .ora. Auf UNIX Systemen wird die Initialisierungsdatei $ORACLE_HOME/dbs/init$ORACLE_SID.ora verwendet. Zumindest folgende Initialisierungsparameter müssen in der init.ora Datei enthalten sein, damit die Instanz eine Datenbank öffnen kann: ● ●
●
db_name - der Name der Oracle-Datenbank control_files - absoluter Pfad einer oder mehrerer Kontrolldateien. Aus den Kontrolldateien geht hervor, welche weiteren Dateien zur Datenbank gehören. remote_login_passwordfile - gibt an, ob eine Passwortdatei und auf welche Art die Passwortdatei verwendet wird. Auf Windows sollte der Parameter auf exclusive stehen. Auf UNIX ist der Parameter optional und eine Passwortdatei nur dann erforderlich, wenn eine Fernverbindung zur Instanz als Benutzer SYS aufgebaut werden soll.
Bedeutsam ist der Parameter db_block_size, denn es handelt sich um den einzigen Parameter, der nach dem Erzeugen der Datenbank nicht mehr verändert werden kann. Er gibt die Größe eines Datenbankblocks an und kann die Werte 2048, 4096, 8192 und 16384 annehmen. Oracle9i gestattet
Ihnen die Blockgröße jedes Tablespace individuell beim CREATE TABLESPACE anzugeben.
Ein Datenbankbenutzer hat einen Namen, ein Kennwort, ein Schema und Berechtigungen, die im Oracle-Datenbankkatalog vermerkt sind. Legt ein Datenbankbenutzer Datenbankobjekte an, werden diese standardmäßig in dem Datenbankschema erstellt, das denselben Namen hat wie der Benutzer.
Eine Datenbanksitzung ist ein Kommunikationskanal zwischen einem Anwendungsprogramm und einer Oracle-Instanz. Eine Datenbanksitzung hat bestimmte Attribute. Einige der Attribute beeinflussen die Ausführung von SQL-Anweisungen. Um eine Datenbanksitzung aufzubauen, werden Benutzernamen und Kennwort an eine Oracle-Instanz gesendet. Über den Kommunikationskanal werden der Oracle-Instanz SQL-Anweisungen übermittelt und Ergebnisse empfangen. Attribute einer Datenbanksitzung sind z.B. die Sitzungsnummer, Einstellungen für die Sprachumgebung (NLS_LANG), das Datumsformat (nls_date_format) und Nummernformat (nls_numeric_characters) sowie Parameter, die festlegen, welcher SQL-Optimierer (optimizer_mode) eingesetzt werden soll oder wie viel Speicherplatz für Sortiervorgänge (sort_area_size) verwendet werden soll. Der Anwender ist nicht mit diesen Einzelheiten konfrontiert, denn für jede mögliche Einstellung gelten Standardwerte, die vom Datenbankadministrator festgelegt werden.
Datenbanksitzung Eine Datenbanksitzung mit Oracle herzustellen bedeutet, einen gültigen Benutzernamen und ein Kennwort anzugeben, die von der Oracle-Instanz akzeptiert werden. Mit Hilfe eines beliebigen OracleWerkzeugs, das mit einer der Oracle-Programmierschnittstellen erstellt wurde, kann ein Benutzer eine Sitzung zu einer Oracle-Instanz herstellen. Auch mit einem Drittwerkzeug wie PowerBuilder kann sich ein Benutzer bei einer Oracle- Instanz anmelden. Die Begriffe Oracle-Sitzung, Oracle-Verbindung und Datenbanksitzung werden oft gleichbedeutend verwendet. Es ist möglich, eine Datenbanksitzung zu einer Oracle-Instanz aufzubauen, die auf demselben Rechner läuft wie das Anwendungsprogramm oder auf einem anderen Rechner in einem Netzwerk. Eine lokale Verbindung zu einer Oracle-Instanz herzustellen bedeutet, dass das vom Benutzer verwendete Programm auf demselben Computer ausgeführt wird wie die Oracle-Instanz. Der Begriff Fernverbindung bedeutet, dass sich die Oracle-Instanz auf einem anderen Rechner im Netzwerk befindet. Der Kommunikationskanal zwischen dem Anwendungsprogramm und der Oracle-Instanz wird immer von Oracle Net Services realisiert, unabhängig davon, ob mit einer lokalen Verbindung oder einer Fernverbindung gearbeitet wird.
Ein Servicename definiert auf Ebene der Oracle Net Services, wie der Kommunikationskanal für eine Datenbanksitzung mit einer Oracle-Instanz aufgebaut wird. Die Verwendung eines Servicenamens ist zwingend, um eine Fernverbindung aufzubauen. Eine Servicename für eine Fernverbindung definiert z.B., dass das Protokoll TCP/IP verwendet werden soll, um einen Kommunikationskanal zu einer Oracle-Instanz aufzubauen.
Ein Listener ist ein Prozess, der Anforderungen für den Aufbau von Datenbanksitzungen entgegennimmt und den Verbindungsaufbau zwischen dem Anwendungsprogramm und der Oracle-Instanz bewerkstelligt. Listener warten typischerweise auf dem Port 1521 darauf, dass ein Anwendungsprogramm über TCP/IP die Verbindung zu einer Oracle-Instanz nachfragt. Um welche Instanz es sich handelt, wird dem Servicenamen entnommen. Der Listener kennt die Instanz entweder, weil diese in der Listener Konfigurationsdatei listener.ora bekannt gegeben wurde, oder weil die Instanz beim Listener einen Service registriert hat. Der registrierte Service setzt sich aus den Werten der Initialisierungsparameter db_name und db_domain zusammen, oder wird dem Parameter service_names entnommen, falls dieser gesetzt ist. Einen Überblick über die Services und Instanzen, die einem Listener bekannt sind, erhalten Sie mit dem Kommando lsnrctl services. Lokale Verbindungen benötigen keinen Listener. Eine lokale Verbindung wird hergestellt, wenn beim Verbindungsaufbau, z.B. mit dem Befehl connect in SQL*Plus, kein Servicename angegeben wird. In diesem Fall wird durch Auswertung der Variable ORACLE_SID ermittelt, zu welcher Oracle-Instanz eine Datenbanksitzung aufgebaut werden soll.
Datenbankbenutzer Jede Verbindung zu einer Datenbank wird für einen Datenbankbenutzer hergestellt. Die Begriffe Tabelleneigentümer, Oracle-Benutzer und Oracle-Account werden gleichbedeutend verwendet. Ein Tabelleneigentümer ist der Datenbankbenutzer, dem eine Tabelle gehört. Ein Tabelleneigentümer ist immer ein Oracle-Benutzer, aber ein Oracle- Benutzer muss nicht immer eigene Tabellen besitzen. Ein Beispiel soll bei der Erläuterung dieses Konzepts helfen. Betrachten wir die Beispieldatenbank. Ein Oracle-Account namens Flugle besitzt alle Tabellen des College-Informationssystems. Sally Jensen ist die Verwalterin des Fachbereichs Biologie und verfügt über einen Oracle-Account namens sjensen. Sally Jensen hat Zugang zu allen Tabellen des Schemas Flugle, besitzt aber keine eigenen Tabellen. Jede in einer Oracle-Datenbank befindliche Tabelle gehört einem Eigentümer. Ein sinnvoller Ansatz wäre es, einen Eigentümer zu erzeugen, bei dem es sich nicht um eine Einzelperson innerhalb der Organisation, sondern um die gesamte Organisation handelt. Wenn Sie darüber nachdenken, werden Sie feststellen, dass die Daten einer Organisation - Informationen, die von den Mitgliedern der Organisation erstellt, geändert und verwendet werden - nicht nur einer Person gehören. Außerdem ist die Dynamik einer Organisation zu berücksichtigen: Die Mitarbeiter kommen und gehen, aber die Organisation bleibt erhalten. Anstatt nun eine Person zum Eigentümer der Oracle-Tabellen zu ernennen, werden Sie in der Beispielanwendung einen neuen Benutzer namens Flugle anlegen und diesen Oracle- Account als
Eigentümer aller Tabellen innerhalb der Anwendung verwenden.
Externe Prozeduren Oracle9i gibt einem Entwickler die Möglichkeit, Routinen, die in anderen Programmiersprachen geschrieben wurden, von einem PL/SQL-Unterprogramm aus aufzurufen. Diese Routinen, die in einer Programmiersprache der dritten Generation (3GL, z.B. C) geschrieben sind und sich in einer gemeinsam nutzbaren Bibliothek (shared library) befinden, werden als externe Prozeduren bezeichnet. Externe Prozeduren sind sinnvoll, wenn eine bestimmte Aufgabe nicht mit PL/SQL oder der Java Virtual Machine in der Oracle-Instanz realisiert werden kann oder eine externe Prozedur einen deutlichen Geschwindigkeitsvorteil einbringt.
3.7 Oracle Enterprise Manager Oracle Enterprise Manager ist der Oberbegriff für eine Systemmanagementumgebung mit einer Vielzahl von Werkzeugen für die grafische Administration von Oracle-Produkten. Enterprise Manager unterstützt die Oracle9i Datenbank, Oracle9i Application Server, Oracle Applications und SAP/R3. Enterprise Manager setzt sich aus der Enterprise Manager Console und mehreren zum Teil zusätzlich zu lizenzierenden Packs zusammen. Einige der Packs sind Diagnostics Pack, Tuning Pack und Change Management Pack. Enterprise Manager fällt in die Kategorie der Systemmanagement-Werkzeuge. Enterprise Manager ist als mehrbenutzerfähige Drei-Schichten-Architektur realisiert, kann aber mit Einschränkungen auch im ClientServer-Betrieb oder als Web-Anwendung eingesetzt werden. Alle für das Systemmanagement relevanten Informationen werden in einem Repository gehalten., das in einer Oracle-Datenbank abgelegt wird. Das Repository enthält Informationen über Administratoren und deren Arbeitszeiten, die verwalteten Rechnerknoten, Oracle-Instanzen u.v.a.m. Die Anwendungsschicht bildet die Enterprise Manager Console oder eines der Packs. Die mittlere Schicht bildet der Oracle Management Server, der auf das Repository zugreift. Die dritte Schicht bilden so genannte Intelligent Agents, die auf den überwachten Rechnern laufen. Intelligent Agents liefern Statusinformationen über die Oracle-Instanzen an den Management Server, führen Aufgaben (Jobs) auf den Rechnern durch oder alarmieren bei bestimmten Ereignissen über den Management Server einen Administrator. Weiterführende Informationen über Enterprise Manager finden Sie in der Oracle- Dokumentation, z.B. im »Oracle Enterprise Manager Concepts Guide«. Für unsere Zwecke reicht es aus, die Enterprise Manager Console im Client Server Betrieb zu nutzen. Management Server und Intelligen Agent müssen nicht gestartet werden.
Erzeugen eines neuen Benutzers Als ersten Schritt bei der Erstellung der Beispieldatenbank werden Sie einen Oracle- Benutzer namens Flugle erzeugen, dem alle Datenbankobjekte gehören werden. Bevor Sie einen neuen Oracle-Benutzer erzeugen, müssen Sie eine Verbindung zu einer Oracle- Instanz herstellen. Die meisten OracleWerkzeuge stellen ein Dialogfenster bereit, um die Verbindung zu einer Oracle-Instanz herzustellen. Im Dialogfenster werden Sie aufgefordert, drei Eingaben vorzunehmen: ● ● ●
den Oracle-Datenbankbenutzer, den Sie für Ihre Verbindung verwenden werden ein Benutzerkennwort einen Servicenamen (z.T. als 'Host String' gekennzeichnet)
Wenn Sie eine Verbindung zu einer Oracle-Instanz auf einem Server im Netzwerk herstellen möchten, müssen Sie einen Servicenamen angeben. Die Definition von Servicenamen führen Sie mit Oracle Net Manager durch.
Erzeugung eines neuen Benutzers mit der Enterprise Manager Console Rufen Sie unter Windows über das Start Menü die Enterprise Manager Console auf. Auf UNIX Systemen erreichen Sie die Konsole, indem Sie den Befehl oemapp console in einer Shell eingeben. Den Befehl oemapp können Sie auch am MS DOS Command Prompt unter Windows verwenden. Akzeptieren Sie die Standardauswahl Launch Standalone mit OK. Die Enterprise Manager Console erscheint (siehe Abbildung 3.4).
Abbildung 3.4: Hinzufügen einer Datenbank in der Enterprise Manager Console Im linken Bereich des Fensters sehen Sie innerhalb der hierarchischen Darstellung des Netzwerks (Network) den Knoten Databases. Klicken Sie mit der rechten Maustaste auf Databases und wählen Sie Add Database To Tree. Ein Dialogfenster erscheint. Wählen Sie unter Add Selected Databases... einen der Servicenamen aus der Konfigurationsdatei tnsnames.ora aus und bestätigen Sie mit OK. Im Ast Databases erscheint jetzt der ausgewählte Servicename. Klicken Sie mit der rechten Maustaste auf den Servicenamen und wählen Sie Connect. Geben Sie den Benutzernamen 'system' ein. Wenn Sie das Standardpasswort nicht verändert haben, geben Sie 'manager' als Passwort ein. Bestätigen Sie mit OK. Nach erfolgreichem Aufbau einer Datenbanksitzung werden unter dem Servicename die Knoten Instance, Schema, Security, usw. aufgeblendet. Wählen Sie den Menüpunkt Object und anschließend Create. Aus der Liste der Datenbankobjekte wählen Sie nun User und bestätigen mit OK. Das Dialogfenster Create User öffnet sich. Unter dem Kartenreiter General geben Sie Benutzernamen und Passwort ein. Unter Tablespaces können Sie den Standard-Tablespace (Default) und den Tablespace für temporäre Segmente einstellen. Der StandardTablespace ist meistens 'USERS' und der Tablespace für temporäre Segmente entweder 'TEMP' oder <System Assigned>. System Assigned bedeutet, dass der Standard-Tablespace für temporäre
Segmente, der beim Anlegen der Datenbank oder anschließend mit alter database set default temporary tablespace festgelegt wurde, für den Benutzer verwendet werden soll. Wenn eine Oracle9i Datenbank einen Standard-Tablespace für temporäre Segmente besitzt, kann es nicht mehr wie bei den Vorgängerversionen passieren, dass ein Benutzer versehentlich im SYSTEM Tablespace temporäre Segmente anlegt, weil vergessen wurde, der Benutzerdefinition den tatsächlichen Tablespace für temporäre Segmente zuzuweisen.
Abbildung 3.5: Eingabe des Benutzernamens und Passworts Überzeugen Sie sich davon, dass der Benutzer unter dem Kartenreiter Roles die Rolle 'CONNECT' erhält. Hinter der Rolle 'CONNECT' verbergen sich die Systemberechtigungen CREATE SESSION, ALTER SESSION, CREATE DATABASE LINK, CREATE TABLE, CREATE CLUSTER, CREATE SYNONYM, CREATE VIEW und CREATE SEQUENCE. Die Berechtigung CREATE SESSION ist erforderlich, um eine Datenbanksitzung aufzubauen. Wechseln Sie zu dem Kartenreiter System Privileges. Geben Sie dem Benutzer Flugle weitere Berechtigungen, indem Sie auf die Berechtigung klicken und diese mit einem Klick auf den nach unten gerichteten Pfeil in den mit Granted beschrifteten Bereich verschieben.
Abbildung 3.6: Vergabe von Systemberechtigungen Wechseln Sie nun zu dem Kartenreiter Quota. Klicken Sie auf den zuvor unter General ausgewählten Standard-Tablespace und wählen Sie im unteren Bereich des Fensters Unlimited. Sie gestatten hiermit dem Benutzer Flugle unbeschränkt Speicherplatz in seinem Standard-Tablespace zu verbrauchen. Alternativ könnten Sie Value statt Unlimited wählen und dem Benutzer z.B. eine Quote von 50MB an diesem Tablespace einräumen. Wenn Sie auf Show SQL klicken, zeigt Ihnen die Console, welche SQL-Anweisungen generiert werden, um Ihre Eingaben zu den einzelnen Kartenreitern umzusetzen. Sie erkennen die Elemente der Syntax von create user.
Die vereinfachte Syntax von create user ist: CREATE USER user_name IDENTIFIED BY password [DEFAULT TABLESPACE tablespace_name] [TEMPORARY TABLESPACE temp_tablespace_name] [QUOTA {UNLIMITED | amount M} ON tablespace_name]
[PASSWORD EXPIRE] [ACCOUNT {UNLOCK | LOCK}]; Die Bedeutungen der Platzhalter sind: ● ● ●
● ●
user_name ist der Name des neuen Benutzers. password ist das Passwort des neuen Benutzers. tablespace_name ist der Name eines vorhandenen Tablespace, der als Standard- Tablespace des neuen Benutzers verwendet wird. temp_tablespace_name ist der Name eines Tablespace für temporäre Segmente. amount ist die Anzahl Megabytes, die der Benutzer im Standard-Tablespace belegen darf.
Beim Anlegen eines Benutzers besteht die Möglichkeit, das Passwort mit password expire als abgelaufen zu kennzeichnen. Der Benutzer wird dann beim ersten Anmelden aufgefordert, das Passwort zu verändern. ACCOUNT LOCK bewirkt, dass der Zugang zu dem Account gesperrt wird. Nachdem Sie sich mit der Syntax von create user vertraut gemacht haben, klicken Sie abschließend auf 'Create', um den Benutzer Flugle anzulegen.
Sie können den Benutzer Flugle auch mit dem SQL-Skript sql\create_user_ flugle.sql auf der CD-ROM anlegen.
3.8 Datentypen Als Überleitung zu der Lektion des vierten Tags sollten wir nun einige Grundlagen in Bezug auf die in einer Oracle-Datenbank verfügbaren Datentypen erarbeiten. Jede Spalte in einer Oracle-Datenbank muss durch einen dieser Datentypen definiert werden. Mit Oracle Version 8.0 oder höher können Sie zusätzlich zu den vordefinierten Datentypen auch Ihre eigenen Datentypen verwenden. Die vordefinierten Datentypen für vier Datenkategorien werden in den folgenden Abschnitten erläutert: ● ● ● ●
Zahlen Zeichenfolgen Datum und Uhrzeit LOBs (Large OBjects)
Zahlen Oracle bietet verschiedene Datentypen zum Speichern von Zahlen an. Jeder Typ dient einem anderen Zweck: ● ●
●
number speichert allgemeine Zahlen. decimal speichert Festkommazahlen und ermöglicht die Kompatibilität von Oracle zu ANSI SQL und anderen relationalen Datenbanken - besonders SQL/ DS und DB2. Dec und numeric sind gleichbedeutend mit decimal. float speichert Gleitkommazahlen und gewährleistet die Kompatibilität von Oracle zum ANSIDatentyp float. Alternativen sind double precision und real.
Der Datentyp number bietet die größte Flexibilität beim Speichern numerischer Werte. Er akzeptiert positive und negative ganze und reelle Zahlen und verfügt über eine Genauigkeit von bis zu 38 Stellen.
Die Syntax für die Angabe des Datentyps number bei der Definition einer Spalte lautet number (Gesamtstellenzahl, Anzahl Nachkommastellen) Unter Gesamtstellenzahl versteht man die maximale Anzahl der zu speichernden Stellen. Anzahl Nachkommastellen wird verwendet, um die Position der Nachkommastellen rechts (positiv) oder links (negativ) vom Dezimalpunkt anzugeben. Diese Anzahl reicht von -84 bis 127. Oracle kann zwischen einer und 38 Stellen für eine Zahl reservieren. Die Anzahl der zum Speichern einer Zahl in einer Oracle-Datenbank erforderlichen Bytes hängt von der Anzahl der Stellen ab, die verwendet werden, um diese Zahl darzustellen. Wenn Sie die Gesamtstellenzahl begrenzen, begrenzt Oracle die in der Spalte zu speichernden Werte auf die vorgegebene Gesamtstellenzahl. Nehmen wir zum Beispiel an, dass Sie eine Tabelle namens Number_Digits_Demo speichern, die zwei Spalten enthält: ● ●
Record_No definiert als int, Value definiert als number(4).
Wenn Sie in der Spalte Value einen Wert speichern, der die vorgegebene Gesamtstellenzahl überschreitet, gibt Oracle eine Fehlermeldung aus, wie in Listing 3.2 dargestellt. Listing 3.2: Oracle erzwingt numerische Genauigkeit SQL> insert into number_digits_demo (record_no, value) values (101, 9999); 1 row created. SQL> insert into number_digits_demo (record_no, value) values (101, 10000); (101, 10000) * ERROR at line 4: ORA-01438: value larger than specified precision allows for this column. Betrachten wir ein anderes Beispiel für die Gesamtstellenzahl und die Anzahl der Nachkommastellen. Nehmen Sie an, Sie haben eine Tabelle namens Scale_Precision_Demo mit zwei Spalten: ● ●
Record_No definiert als int, Value definiert als number(6,2).
In der Spalte value lautet die Gesamtstellenzahl 6 und die Anzahl der Nachkommastellen 2. Mit anderen Worten, von insgesamt sechs Stellen reserviert Oracle zwei Stellen rechts vom Dezimalpunkt und lässt
maximal vier Stellen links vom Dezimalpunkt übrig. Außerdem können in der Spalte für alle Zahlen nicht mehr als sechs Stellen gespeichert werden. Wie in Listing 3.3 dargestellt, kann die Zahl 1234.5 in der Spalte number gespeichert werden. Die Zahl 12345.1 kann jedoch nicht gespeichert werden, da sie links vom Dezimalpunkt über fünf Stellen verfügt. Ähnlich verhält es sich mit der Zahl 12345, obwohl sie insgesamt nur fünf Stellen hat, kann sie in der Spalte nicht gespeichert werden, da sich diese fünf Stellen links vom Dezimalpunkt befinden. Listing 3.3: Oracle erzwingt die Gesamtstellenzahl und die Anzahl der Nachkommastellen SQL> insert into scale_precision_demo 2 (record_no, value) 3 values 4 (901, 1234.5); 1 row created. SQL> insert into scale_precision_demo 2 (record_no, value) 3 values 4 (901, 12345.1); (901, 12345.1) * ERROR at line 4: ORA-01438: value larger than specified precision allows for this column SQL> insert into scale_precision_demo 2 (record_no, value) 3 values 4 (901, 12345); (901, 12345) * ERROR at line 4: ORA-01438: value larger than specified precision allows for this column
Zeichenfolgen Zeichenfolgen werden grundsätzlich entsprechend der Vorgaben eines bestimmten Zeichensatzes verarbeitet. Zeichensätze sind international standardisiert und legen ein Repertoire an darstellbaren Schriftzeichen fest. Der Zeichensatz, mit dem eine Datenbanksitzung arbeitet, kann auf Windows Systemen dem Registry Value NLS_LANG des Registry Key HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE entnommen werden. Plattformübergreifend wird die Umgebungsvariable NLS_LANG ausgewertet. NLS_LANG hat drei Komponenten: Sprache, Land und Zeichensatz. Die Syntax von NLS_LANG ist: <Sprache>_.. Eine mögliche Einstellung für den deutschen Sprachraum unter Verwendung eines Zeichensatzes, der das Euro-Symbol beinhaltet, ist NLS_LANG= German_Germany.WE8ISO8859P15. Eine umfangreiche Liste der von Oracle9i unterstützten Sprach-, Länder- und Zeichensatzeinstellungen finden Sie im »Oracle9i Globalization Support Guide«. Eine Oracle-Datenbank kennt zwei Zeichensätze - den Datenbank-Zeichensatz (database character set) und den nationalen Zeichensatz (national character set). Der Datenbankzeichensatz ist meist ein 8bit Zeichensatz, der eine Obermenge das bekannten 7bit ASCII Zeichensatzes darstellt. Durch die Erweiterung auf 8bit ist es z.B. mit dem Zeichensatz WE8ISO8859P15 möglich, alle deutschen Umlaute und viele weitere Zeichen darzustellen, die nicht Teil des ASCII Zeichensatzes für englische Texte sind.
Unicode ist ein standardisierter Zeichensatz, der alle gebräuchlichen Sprachen beinhaltet. Der Unicode Standard ist im Internet unter dem URL http://www.unicode.org dokumentiert. Der Datenbankzeichensatz kann auch die Unicode-Implementierung UTF8 sein. Dies ist zwingend, wenn Sie die Datenbank für den LDAP Verzeichnisdienst Oracle Internet Directory nutzen wollen und wünschenswert, wenn die Datenbank-Zeichensätze aus verschiedensten Sprachräumen unterstützen soll. Der Datenbank-Zeichensatz WE8ISO8859P15 bildet z.B. nur westeuropäische Zeichen ab. Kyrillische Zeichen könnten nicht in der Datenbank gespeichert werden. Wenn Sie jedoch UTF8 als Datenbank- Zeichensatz wählen, können westeuropäische und kyrillische Zeichen zusammen mit vielen anderen Zeichensätzen wie Arabisch oder Hebräisch gemeinsam in der Datenbank abgelegt werden. Der Datenbank-Zeichensatz kann sich vom Zeichensatz der Datenbanksitzung (NLS_LANG) unterscheiden. Oracle9i konvertiert dann automatisch zwischen den Zeichensätzen. Zum Speichern von Zeichenfolgen können Sie aus mehreren Datentypen auswählen: ● ● ● ● ● ●
char nchar varchar2 nvarchar2 clob nclob
Spalten vom Typ char, varchar2 und clob werden im Datenbank-Zeichensatz kodiert. Spalten vom Typ nchar, nvarchar2 und nclob werden im nationalen Zeichensatz kodiert. Wenn Sie mit Daten, die im nationalen Zeichensatz kodiert sind arbeiten, müssen Sie die Daten kennzeichnen. In SQL*Plus wird eine Zeichenkette im nationalen Zeichensatz durch Voranstellen des Buchstabens N vor die Zeichenkette (z.B. N'Testdaten') gekennzeichnet. In vielen Datenbanken handelt es sich bei dem Großteil der Daten um Zeichenketten. Die Vor- und Nachteile eines jeden Datentyps werden in den folgenden Absätzen erläutert. Der Datentyp char speichert Zeichenketten fester Länge von bis zu 2000 Zeichen. Wenn Sie keine Länge vorgeben, wird in einer char-Spalte ein Zeichen gespeichert. Wenn die gespeicherte Zeichenkette kürzer als die definierte Länge ist, wird mit Leerzeichen aufgefüllt. Der Datentyp varchar2 speichert bis zu 4000 Zeichen in einer einzigen Spalte. Wenn Sie eine größere Zeichenmenge speichern müssen, sollten Sie die Verwendung eines LOB- Datentyps in Erwägung ziehen. Sie können 4 GB in einer clob-Spalte speichern. Der Datentyp clob fällt in die Kategorie der Large Objects. Programmierschnittstellen für clobs liegen in den Sprachen PL/SQL, Java, C (OCI) und C++ (OCCI) vor. Es wäre möglich, lange Zeichenketten bis zu 2 GB in einer Spalte vom Datentyp long zu speichern, jedoch ist der Datentyp long veraltet und wesentlich schlechter handhabbar. Oracle empfiehlt den Datentyp long nicht mehr einzusetzen. Spalten des Datentyps long können mit der alter table modify Anweisung von Oracle9i auf clob Spalten umgestellt werden. Folgen Sie diesen Richtlinien, wenn Sie den geeigneten Datentyp für eine Spalte auswählen, in der Zeichenketten gespeichert werden sollen.
●
●
●
Sie können char verwenden, um Spalten zu definieren, in denen ein einziges Zeichen gespeichert wird. Verwenden Sie varchar2 zum Speichern von Zeichenfolgen variabler Länge, die bis zu 4000 Zeichen enthalten. Verwenden Sie clob oder nclob, um mehr als 4000 Zeichen in einer Spalte zu speichern.
Den Datentyp char verwenden Weil der Datentyp char Spalten fester Länge speichert, sollten Sie diesen Typ verwenden, wenn Sie Spalten definieren, die ein einziges Zeichen enthalten. Die Verwendung des Datentyps char zum Speichern längerer Zeichenketten ist nicht sinnvoll, da Sie damit Speicherplatz verschwenden.
Den Datentyp varchar2 verwenden Da hiermit Zeichenfolgen variabler Länge gespeichert werden, sollte der Datentyp varchar2 zum Speichern von Zeichenfolgen bevorzugt eingesetzt werden. Dieser Datentyp kann bis zu 4000 Zeichen speichern. Das Oracle-RDBMS weist nur den für die Speicherung der einzelnen Spaltenwerte benötigten Speicherplatz zu. Am Tag 8, »SQL- Funktionen«, werden Sie die Vielzahl der Möglichkeiten kennen lernen, wie Sie SQL- Funktionen und Operatoren zur Verarbeitung von Zeichenketten und anderer Daten verwenden können.
Datums- und Zeitinformationen Oracle9i bietet mehrere Möglichkeiten Datum, Uhrzeit und Zeitintervalle zu speichern. Die verfügbaren Datentypen sind date, interval und verschiedene Varianten von timestamp. Oracle verfügt über integrierte Funktionen, die speziell zum Bearbeiten von Datums- und Zeitinformationen dienen. Am Tag 8 werden Sie einige Beispiele für diese Funktionen kennen lernen.
Der Datentyp date Der Datentyp date ermöglicht es Ihnen, Daten im Zeitraum vom 1. Januar 4712 v. Chr. bis zum 31. Dezember 9999 n. Chr. zu speichern. Oracle weist einer date-Spalte einen Speicherplatz mit einer Länge von 7 Bytes zu. Oracle verwendet zur Datumseingabe und Datumsanzeige standardmäßig das Format DD-Mon-YY. In diesem Format steht DD für den Tag des Monats, Mon für den Monatsnamen in englischer Sprache und YY für das zweistellige Jahr.
Abbildung 3.7: Festlegung des Datums- und Zeitformats als Umgebungsvariable über das Windows Control Panel
Abbildung 3.8: Einstellung des Formats der Datums- und Uhrzeitausgabe mit der Umgebungsvariablen NLS_DATE_ FORMAT Wenn dieses Format für Ihre Zwecke nicht geeignet ist, können Sie die Umgebungsvariable NLS_DATE_FORMAT auf ein anderes Format setzen (siehe Abbildung 3.7). Auf Windows Systemen
verwenden Sie das Start Menü, um über Settings und Control Panel das Piktogramm System auszuwählen. Dort wechseln Sie zu dem Kartenreiter Environment und geben unter Variable NLS_DATE_FORMAT ein. Unter Value geben Sie z.B. dd.mm.yyyy hh24:mi:ss ein, um die Uhrzeit sekundengenau auszugeben. Klicken Sie auf Set und Apply, um die Änderung wirksam zu machen. Wenn Sie jetzt an einem MS DOS Command Prompt oder über das Start Menü SQL*Plus starten, ist die Änderung wirksam.
Der Datentyp timestamp Timestamp ist eine Erweiterung von date, in dem Sinne, dass der Datentyp timestamp zusätzlich Sekundenbruchteile und Zeitzoneninformation speichern kann.
Die Syntax von timestamp ist: TIMESTAMP [(fractional_seconds_precision)] [WITH [LOCAL] TIME ZONE] Der Platzhalter fractional_seconds_precision steht für die Anzahl der Ziffern, die Sekundenbruchteile darstellen. Eine Oracle9i Instanz läuft im Kontext einer bestimmten Zeitzone. Die Zeitzoneneinstellung des Betriebssystems wird von der Oracle-Instanz für diesen Zweck genutzt und als Datenbankzeitzone bezeichnet. Wenn mit local timezone gearbeitet wird, werden alle Zeitinformationen normalisiert auf die Datenbankzeitzone gespeichert. Falls erforderlich, konvertiert Oracle zwischen der Zeitzone, in der das Anwendungsprogramm läuft, und der Datenbankzeitzone. Bei Verwendung von timestamp with time zone wird in der Spalte zusätzlich die Abweichung von UTC (Universal Time Coordinated, früher Greenwich Mean Time, GMT) gespeichert. Datumswerte werden dann inklusive der Abweichung von UTC angegeben. Zum Beispiel stellt die Zeichenkette '01.10.2001 09:26:56.66 +02:00' den ersten Oktober 2001 und eine Abweichung von +2 Stunden von UTC dar.
Der Datentyp interval Aufgabe des Datentyps interval ist die Speicherung von Zeitintervallen und die Durchführung von Berechnungen mit Zeitpunkten und Zeitintervallen. Zwei Varianten stehen zur Verfügung - interval year to month und interval day to second.
Die Syntax des Datentyps interval lautet: INTERVAL YEAR [(year_precision)] TO MONTH INTERVAL DAY [(day_precision)] TO SECOND(fractional_seconds_precision)] Die Bedeutungen der Platzhalter sind wie folgt:
●
●
●
year_precision steht für die Anzahl der Ziffern, die für die Dauer des Intervalls in Jahren verwendet werden können. day_precision steht für die Anzahl der Ziffern, die für die Dauer des Intervalls in Tagen verwendet werden können. fractional_seconds_precision steht für die Anzahl der Ziffern, die für die Dauer des Intervalls in Sekundenbruchteilen verwendet werden können.
Oracle stellt mehrere SQL-Funktionen für den Umgang mit Intervallen zur Verfügung. Am Tag 8, »SQLFunktionen«, werden Sie sich näher mit diesen Funktionen beschäftigen.
Große Objekte Wie Sie wahrscheinlich wissen, erlauben die meisten Datenbanken das Speichern großer Objekte (LOB, Large Object). LOBs umfassen Dokumente, Grafiken, Klänge, Videos - eigentlich alle Arten von Binärdateien, die Sie sich vorstellen können. LOBs werden in vier Kategorien eingeteilt: ● ● ● ● ●
BLOBs, die unstrukturierte binäre Daten in der Datenbank speichern CLOBs für Dokumente im Datenbankzeichensatz NCLOBs für Dokumente im nationalen Zeichensatz temporäre LOBs, d.h. nicht persistente BLOBs, CLOBS oder NCLOBS BFILEs, die binäre Dateien im Dateisystem referenzieren
Die Benutzung von LOBs besitzt mehrere Vorteile. Erstens, ein LOB kann 4 Gbyte oder die doppelte Kapazität der veralteten long raw-Spalte aufnehmen. Zweitens, eine Tabelle kann mehr als eine LOBSpalte enthalten, jedoch nur eine long raw-Spalte. Außerdem können die in einer LOB-Spalte gespeicherten Daten in einem eigenen Segment gespeichert werden, was bei Zugriffscharakteristiken, die selten auf den LOB zugreifen, zu einer besseren Gesamt-Performance führt. LOBs, die größer als 4000 Bytes sind, werden grundsätzlich in einem eigenen LOB-Segment gespeichert.
LOB Locator Die Handhabung eines LOBs erfolgt indirekt über einen so genannten LOB-Locator und das PL/SQL Paket DBMS_LOB. LOB-Spalten enthalten nicht den LOB selbst, sondern einen LOB-Locator. Der LOBLocator dient als Parameter für die Prozeduren und Funktionen des Pakets DBMS_LOB. Eine Anwendung, die mit einem LOB arbeiten möchte, muss zunächst den LOB-Locator aus der Tabelle lesen. Anschließend kann durch Aufruf von DBMS_LOB und Übergabe des LOB-Locator eine der Funktionalitäten aus Tabelle 3.2 verwendet werden. Weitergehende Informationen zum Umgang mit LOBs erhalten Sie in den Handbüchern »Oracle9i Application Developer's Guide - Large Objects (LOBs)« und » Oracle9i Supplied PL/SQL Packages and Types Reference«.
Verwenden von blobs Sie können sich den BLOB-Datentyp als eine neue, verbesserte Version des long raw- Datentyps vorstellen. Anders als eine long raw-Spalte kann eine INSERT-Anweisung mit einer Unterabfrage eine BLOB-Spalte enthalten. Wie die long-Spalten, so weisen auch long raw-Spalten eine Reihe von Einschränkungen auf. Sie können zum Beispiel bei long raw- Spalten keine der integrierten SQLFunktionen verwenden. Oracle empfiehlt long raw- Spalten zu Gunsten von BLOB-Spalten nicht mehr zu verwenden. Listing 3.4 ist ein Beispiel für eine Tabelle, die eine BLOB-Spalte enthält. Diese wird benutzt, um den eingescannten Lebenslaufs eines Dozenten im Pixelformat TIFF zu speichern.
Listing 3.4: Erstellen einer Tabelle, die eine Spalte vom Typ BLOB enthält SQL> create table Instructor_Application ( Application_Number number, Last_Name varchar2(30), First_Name varchar2(30), MI varchar2(1), Resume_TIFF blob, Status varchar2(30)); Table created.
Initialisierung einer LOB-Spalte Es gibt zwei integrierte Funktionen, die benutzt werden, um einen LOB-Locator zu initialisieren: EMPTY_BLOB für BLOBs und EMPTY_CLOB für CLOBs und NCLOBs. Beide Funktionen besitzen keine Argumente. Listing 3.5 veranschaulicht die Benutzung von EMPTY_BLOB in einer INSERT-Anweisung. Listing 3.5: Einfügen einer Zeile in eine Tabelle mit einer Spalte vom Typ BLOB SQL> insert into Instructor_Application 2 (Application_Number, Last_Name, First_Name, MI, Resume_TIFF, Status) 3 values 4 (1001, 'Deevers', 'James', NULL, EMPTY_BLOB(), 'REJECTED'); 1 row created.
Der Datentyp BFILE Eine BFILE-Spalte wird benutzt, um eine binäre Datei zu referenzieren, die in einem Dateisystem, das für eine Oracle-Instanz erreichbar ist, gespeichert ist. Die Binärdatei befindet sich außerhalb der Datenbank. Als Folge dessen stehen die Mechanismen von Oracle für atomare Transaktionen, Schutz vor gleichzeitigen Änderungen, konsistentes Lesen, Ändern der Binärdaten (DBMS_LOB) sowie Datensicherung und Wiederherstellung (Backup und Recovery) nicht zur Verfügung. Bevor Sie sich den BFILEs zuwenden können, benötigen Sie noch Vorkenntnisse über Aliasnamen für Verzeichnisse.
Die create directory-Anweisung Die CREATE DIRECTORY-Anweisung ist für die Arbeit mit BFILE-Spalten gedacht. Die Ausführung dieser Anweisung erfordert die Systemberechtigung CREATE ANY DIRECTORY. Der Zweck von CREATE DIRECTORY ist das Erstellen eines Alias für das Verzeichnis eines Dateisystems. Ist der Verzeichnis-Alias definiert, kann die Berechtigung zum Lesen der Inhalte einer Datei in diesem Verzeichnis einer Rolle oder einem Benutzer zugeteilt werden. Der Eigentümer des Alias hat automatisch Leserechte.
Die Syntax der CREATE DIRECTORY-Anweisung lautet: CREATE DIRECTORY oracle_directory_name AS 'absolute_file_system_path';
Die Bedeutungen der Platzhalter sind: ●
●
oracle_directory_name ist der Name des Directory Objekts in der Datenbank. Oracle_directory_name ist der Name, den Oracle-Benutzer zur Referenzierung des Verzeichnisses benutzen müssen. Wie bei allen Namen von Datenbankobjekten, die nicht in doppelten Anführungszeichen angegeben werden, wird auch der oracle_directory_name in Großbuchstaben im Datenbankkatalog gespeichert. Achten Sie also darauf, den oracle_directory_name in Großbuchstaben zu schreiben, wenn Sie ihn als Argument an Funktionen wie z.B. BFILENAME verwenden. absolute_file_system_path ist der absolute Pfad eines Verzeichnisses, das mit den BetriebssystemZugriffsrechten der Oracle-Instanz erreichbar ist. Auf UNIX Systemen beginnen absolute Pfade mit dem Schrägstrich / (z.B. /home/oracle), auf Windows Systemen mit dem Laufwerksbuchstaben (z.B. C:\home\oracle). Wenn das Betriebssystem Groß-/Kleinschreibung unterscheidet, muss dies beachtet werden.
Listing 3.6 veranschaulicht, wie ein Verzeichnis erzeugt wird. Die Berechtigung zum Lesen von Dateien aus diesem Verzeichnis kann einer Datenbankrolle gewährt werden. Listing 3.6: Erstellen eines Verzeichnisses SQL> create directory PHOTO_DIR as 'C:\Flugle\Photos'; Directory created. Das Gegenstück zur CREATE DIRECTORY-Anweisung ist die DROP DIRECTORY-Anweisung, mit der ein Verzeichnis-Alias gelöscht wird. Als Beispiel demonstriert Listing 3.7, wie eine Tabelle erzeugt wird, die eine BFILE-Spalte enthält. Listing 3.7: Definieren und Verwenden einer Spalte vom Typ BFILE SQL> create table Instructor_Photo (Instructor_ID number(5), Recent_Photo bfile); Table created. SQL> insert into Instructor_Photo (Instructor_ID, Recent_Photo) values (251, BFILENAME ('PHOTO_DIR', '251.jpg')); 1 row created.
Die Instructor_Photo-Tabelle wird mit einer BFILE-Spalte namens Recent_Photo erzeugt. Der Zweck dieser Spalte ist es, das Verzeichnis und den Namen der JPEG-Datei zu bestimmen, die ein Foto des Dozenten enthält. Eine Zeile wird in die Instructor_Photo-Tabelle eingefügt. Wie Sie sehen können, wird die BFILENAME-Funktion zur Bereitstellung eines LOB-Locator für die BFILE-Spalte benutzt. Der Verzeichnisalias PHOTO_DIR und der Dateiname werden in Hochkommata eingeschlossen. Beachten Sie, dass der Verzeichnisalias beim Anlegen mit CREATE DIRECTORY nicht in doppelte Anführungszeichen eingeschlossen war. Daher wurde das Datenbankobjekt PHOTO_DIR in Großbuchstaben angelegt und muss bei der
Verwendung ebenfalls groß geschrieben werden. Der Inhalt der mittels der BFILE-Spalte referenzierten binären Datei kann mit einer SQL-Anweisung nicht geändert werden. Das Verzeichnis und der Dateiname, die in der BFILE-Spalte gespeichert sind, jedoch schon.
Sie finden ein vollständiges Beispiel für die Verwendung von BFILEs in der Datei sql\bfile.sql auf der Begleit-CD zum Buch.
Lange Zeichenfolgen Wie bereits zuvor erwähnt, sollten Sie den Oracle-Datentyp clob verwenden, um mehr als 4000 Zeichen in einer einzelnen Spalte zu speichern. Eine clob-Spalte fasst eine Zeichenmenge von bis zu 4 GB. Wie beim Datentyp varchar2 weist das Oracle-RDBMS bei der Verwendung des Datentyps clob lediglich den für die Speicherung der einzelnen Spaltenwerte erforderlichen Speicherplatz zu. Sie werden allerdings bei der Verwendung von clob-Spalten in SQL auf eine Reihe von Einschränkungen stoßen. Sie können keine SQL-Funktionen oder Operatoren verwenden, um den Inhalt einer clob-Spalte zu durchsuchen oder zu ändern. Sie müssen eine der Programmierschnittstellen für LOBs, z.B. das PL/SQL Paket dbms_lob verwenden.
Verwenden von CLOBs In Listing 3.8 können Sie an einem Beispiel sehen, wie eine clob-Spalte namens OCR_RESUME zu der Tabelle Instructor_Application hinzugefügt wird. Der Zweck der OCR_RESUME-Spalte ist die Speicherung des Textes, der von der Scanner-Software aus dem eingescannten Lebenslauf gewonnen wurde. Listing 3.8: Erweiterung einer Tabelle um eine Spalte vom Typ clob SQL> alter table Instructor_Application add 2 (OCR_Resume clob); Table altered. SQL> update Instructor_Application set OCR_Resume = 'Resume of James R. Deevers - Born in San Jose in 1972'; 1 row updated. SQL> select OCR_Resume from Instructor_Application; OCR_RESUME ---------------------------------------------------Resume of James R. Deevers - Born in San Jose in 1972
Nachdem die clob-Spalte zur Tabelle hinzugefügt wurde, wird eine UPDATE-Anweisung benutzt, um den Wert von OCR_Resume zu setzen. Eine SELECT-Anweisung veranschaulicht, dass die Inhalte von OCR_Resume aus der Tabelle abgefragt werden können.
Der Datentyp raw
Oracle stellt den Datentyp raw bereit, der bis zu 2000 Byte an Binärdaten fassen kann. Aufgrund der Speicherbegrenzung ist eine raw-Spalte weniger nützlich als eine BLOB- Spalte. Der Einsatz von rawSpalten kann sinnvoll sein, wenn Sie kleine Binärobjekte speichern möchten. Da ein LOB 88 Byte Verwaltungsinformation benötigt, sparen Sie Speicherplatz, wenn Sie Binärobjekte mit weniger als 2000 Bytes in raw-Spalten ablegen.
3.9 Zusammenfassung Versuchen Sie, wie in dieser Lektion erläutert, beim Entwerfen eines logischen Datenmodells folgende Schritte zu beachten: 1. Identifizieren Sie jede Tabelle im zu modellierenden System. 2. Benennen Sie einen Primärschlüssel für jede Tabelle. 3. Bestimmen Sie die zu erstellenden Fremdschlüssel. Beachten Sie bei der Arbeit mit den Oracle-Datentypen folgende Grundlagen: ● ● ● ●
●
● ●
● ●
Jede Spalte in einer Tabelle muss einen Datentyp erhalten. Verwenden Sie number zum Speichern numerischer Daten. Verwenden Sie char zum Speichern einzelner Zeichen. Verwenden Sie varchar2 zum Speichern von Zeichenketten, die maximal 4000 Bytes enthalten. In Oracle integrierte Funktionen und Operatoren sind mit varchar2 Spalten verwendbar. Verwenden Sie clob, wenn Sie lange Zeichenfolgen mit mehr als 4000 Bytes, wie z.B. Texte oder HTML Seiten speichern müssen. Verwenden Sie date zum Speichern aller Datums- und Zeitinformationen. Verwenden Sie timestamp zum Speichern von Datum, Uhrzeit und Sekundenbruchteilen. Der Datentyp timestamp kann auch Informationen über Zeitzonen beinhalten. Verwenden Sie BLOB-Spalten zum Speichern von bis zu 4 GB Binärdaten in der Datenbank. Verwenden Sie BFILE-Spalten, um Binärdaten außerhalb der Datenbank zu referenzieren und über Programmierschnittstellen von Oracle zu Lesen.
3.10 Wie geht es weiter? Am Tag 4 lernen Sie, wie Sie Tabellen und andere Strukturen, die ein logisches Datenmodell implementieren, erstellen.
3.11 Fragen und Antworten Frage: Stellt die Leistung nicht ein Problem dar, wenn alle Tabellen einer Anwendung in der dritten Normalform vorliegen? Antwort: Es ist falsch anzunehmen, dass die Leistung einer Datenbankanwendung unter dem gültigen Standard liegt, wenn die Tabellen in der dritten Normalform vorliegen. Es ist besser, mit einem Entwurf in der dritten Normalform - oder höher - zu beginnen und dann Bereiche zu ermitteln, in denen die Leistung ein Problem darstellen könnte. Frage: Wie viele Spalten kann eine Tabelle in einer Oracle-Datenbank enthalten?
Antwort: Eine Tabelle kann bis zu 1000 Spalten enthalten. Frage: Ist es möglich eine Binärdatei, die von einer BFILE-Spalte referenziert wird, mit Mechanismen von Oracle zu ändern? Antwort: Nein, das Ändern von Binärdateien, die über BFILE-Spalten referenziert werden, ist nicht möglich.
3.12 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Wenn ein Attribut, das nicht Teil des Primärschlüssels ist, einen Fremdschlüssel darstellt, ist es unbedingt erforderlich; es lässt keinen NULL-Wert zu. 2. Kann die Student-Schedule-Tabelle verwendet werden, wenn ein Student einen Kurs wiederholen muss? Warum bzw. warum nicht? 3. Welche Eigenschaft einer relationalen Datenbank verhindert, dass eine Klasse aus der ClassTabelle gelöscht wird, wenn in der Student-Schedule-Tabelle Zeilen existieren, welche die zu löschende Class ID enthalten? 4. Es gibt eine Beziehung zwischen der Class- und der Instructor-Tabelle. Handelt es sich hierbei um eine identifizierende oder nichtidentifizierende Beziehung? 5. Richtig oder falsch? Wenn Sie den entsprechenden Index für eine Tabelle erstellen, werden die Zeilen stets in aufsteigender Reihenfolge der indizierten Spalten abgerufen.
Übungen 1. Angenommen, Sie möchten den Kursleiter identifizieren, der auch Bereichsleiter ist. Können Sie mindestens zwei Möglichkeiten nennen, dies herauszufinden? Wie lauten die Stärken und Schwächen der jeweiligen Ansätze? 2. Die Student-Schedule-Tabelle enthält zur Zeit sowohl die derzeitigen Klassen als auch die früheren Klassen, die ein Student besucht hat. Schlagen Sie einen alternativen Entwurf mit zwei Tabellen vor, wobei eine Tabelle den aktuellen Stundenplan und die zweite Tabelle die Klassen enthält, die der Student früher besucht hat. Nennen Sie alle Attribute in den zwei Tabellen. Welche Vorteile hat dieser Entwurf?
Richtlinien für die Entwicklung einer OracleAnwendung Der Aufwand für die Entwicklung einer neuen Oracle-Anwendung hängt von vielen Faktoren ab. Zum Thema Anwendungsentwicklung wurden bereits viele ausgezeichnete Bücher veröffentlicht. In dieser Lektion werden Sie einige der Überlegungen kennen lernen, die bei der Entwicklung einer OracleDatenbankanwendung angestellt werden.
In dieser Lektion umfasst der Begriff Benutzer alle an der Anwendung Beteiligten - Endbenutzer, Führungskräfte, Lieferanten, Kunden, Betriebspersonal und Entwickler.
2.1 Anwendungsentwicklung Schritt für Schritt Traurige Wahrheit bei der Anwendungsentwicklung ist, dass die Fehlerquote erstaunlich hoch liegt; ein nicht unerheblicher Teil der neuen Anwendungen geht nicht in Produktion. Für Sie als Anwendungsentwickler ist es wichtig zu verstehen, warum dies der Fall ist und wie die Auswirkungen auf den Entwicklungsprozess aussehen. Selbst wenn Sie die Vorschläge aus dieser Lektion - oder einer anderen Quelle peinlich genau befolgen, gibt es keine Garantie, dass die Implementierung der neuen Anwendung erfolgreich verlaufen wird. Im Idealfall sind folgende Schritte für die Entwicklung einer neuen Anwendung erforderlich: ● ● ●
Sammeln von Anforderungen Entwerfen der Anwendung Implementieren der Anwendung
● ● ●
Testen der Anwendung Einsetzen der Anwendung Pflege der Anwendung
In der Realität folgen diese Schritte selten so nahtlos aufeinander. Stattdessen werden die Arbeitsschritte im Allgemeinen mehrfach wiederholt, wobei sich die Anwendung der Implementierung der »tatsächlichen Anforderungen« immer weiter nähert (siehe Abbildung 2.1). Lassen Sie uns etwas tiefer in die einzelnen Schritte einsteigen.
Bei jedem Projekt gibt es mehrere Beteiligte, wie z.B.: Projektleiter, Unternehmensanalytiker, Datenbankentwickler, Anwendungsentwickler, Programmierer, Fachautor, Datenbankadministrator, Systemadministrator und Dozenten. Bei einem sehr kleinen Projekt ruhen alle diese Aufgaben möglicherweise auf Ihren Schultern. Bei einem großen Projekt könnte ein ganzes Team für einen bestimmten Bereich, z.B. Schulung, zuständig sein. Man sollte immer daran denken, dass für jeden dieser Bereiche jemand zuständig sein muss, damit das Projekt zum Erfolg führt.
Abbildung 2.1: Zyklus der Anwendungsentwicklung
Sammeln von Anforderungen Der Ausgangspunkt bei der Entwicklung einer neuen Anwendung liegt in der Aufgabe, Anforderungen zu sammeln. Die Aufgabe, die Anforderungen an eine neue
Anwendung zu definieren, wird von allen Beteiligten als notwendig erachtet. Allerdings ist das Sammeln von Anforderungen schwieriger als es den Anschein haben mag. ●
●
●
Zwischenmenschliche Kommunikation: Das Sammeln von Anforderungen erfordert eine gewisse zwischenmenschliche Kommunikation - entweder durch Gespräche, Zuhören, Lesen oder Schreiben. Da die menschliche Sprache ungenau ist, bedarf es der Interpretation. Diese Interpretation gilt sowohl für die anderen als auch für Sie, den Anwendungsentwickler. Politik: Die Entwicklung einer neuen Anwendung steckt voller politischer Gefahren. Eine Benutzergruppe formuliert möglicherweise eine Anforderung, während eine andere Gruppe eventuell eine dazu im Widerspruch stehende Forderung hat. Nun liegt es bei Ihnen, mit diesen Personen zusammenzuarbeiten und herauszufinden, wer Recht hat, wessen Stimme zählt und wer eigentlich letztendlich bei diesem Projekt zufrieden gestellt werden soll. Das Risiko eines Projekts im Bereich der Anwendungsentwicklung verhält sich proportional zu der Anzahl der an der Entwicklung beteiligten Organisationen (siehe Abbildung 2.2). Ständige Veränderungen: Wahrscheinlich entwickeln Sie neue Anwendungen häufig in einer Umgebung, in der sich die Anforderungen ändern. Wenn Sie Glück haben, denkt irgend jemand daran, Sie über die Art der Änderungen, deren Gründe und die Auswirkungen auf die bestehenden Anforderungen zu informieren. Vielleicht sehen Sie es als eine Herausforderung an, eine Architektur zu entwickeln, die diesen sich ändernden Anforderungen gerecht wird.
Abbildung 2.2: Das Verhältnis zwischen Risiko und organisatorischer Komplexität Sie werden sicherlich mehrere Arten von Anforderungen für Ihre Anwendung sammeln wollen:
●
●
●
Anforderungen an die Funktionen: Hierzu gehören sowohl die von der Anwendung zu erfüllenden Anforderungen der höheren als auch der niederen Ebene. Ein Beispiel für eine Anforderung der höheren Ebene wäre: »Das Informationssystem des Flugle- College soll es den Lehrkräften ermöglichen, ihren Studenten Noten zuzuweisen.« Anforderungen an die Daten: Zu diesen Anforderungen zählen die von der Anwendung zu verwaltenden Informationen. Diese Anforderungen sind entscheidend für die Entwicklung eines logischen Datenmodells, das bei der Implementierung der funktionalen Anforderungen hilfreich ist. Ein Beispiel für Anforderungen an die Daten wäre: »Das Informationssystem des Flugle-College soll Daten über die von den einzelnen Fachabteilungen angebotenen Kurse enthalten.« Anforderungen an die Leistung: Diese Anforderungen beschreiben die Leistung, welche das gesamte System, einschließlich der Anwendung, erbringen muss. Eine Anforderung an die Leistung wäre zum Beispiel: »Das Informationssystem des Flugle- College soll in der Lage sein, gleichzeitig 120 Benutzer zu unterstützen, und dabei eine Antwortzeit von weniger als zwei Sekunden für die Registrierung eines Studenten für einen Jahrgang aufweisen.«
Es gibt viele Wege, Anforderungen zu verfolgen. Sie können sich natürlich ausführliche Notizen machen. Sie können auch eine Anforderungsdatenbank erstellen und mit deren Hilfe eine Dokumentation anfertigen. Sie können aber auch Komponenten aus Designer oder andere Werkzeuge für die computergestützte Software-Entwicklung (Computer Aided Software Engineering, CASE) benutzen. Welche Methode Sie verwenden, um Anforderungen zu sammeln und zu dokumentieren, hängt jedoch von mehreren Punkten ab: ●
●
●
●
Komplexität der Anwendung: Wird die Anwendung für eine kleine Benutzergruppe oder eine Hauptabteilung einer großen Organisation entwickelt? Budget: Soll die Anwendung mit geringen Mitteln finanziert werden oder hat die Geschäftsführung die Finanzierung für dieses Großprojekt genehmigt? Anzahl der mit der Anwendungsentwicklung betrauten Personen: Sind Sie die einzige Person, die sich mit der Anwendungsentwicklung beschäftigt oder gibt es einige Dutzend Mitarbeiter, die an der Entwicklung beteiligt sind? Sichtbarkeit: Ist die zu entwickelnde Anwendung außerhalb eines kleinen Teams unbekannt oder gibt es Führungskräfte, die je nach Erfolg der implementierten Anwendung befördert werden oder zurücktreten müssen?
Die Antworten auf diese Fragen entscheiden, wie strukturiert Sie bei der Sammlung von Anforderungen und der Überprüfung Ihrer Vorstellung von diesen Anforderungen mit den Beteiligten - Endbenutzern, Führungskräften und anderen Entwicklern vorgehen sollten. Stehen für Ihr Projekt klare Forderungen, bescheidene Mittel und wenig Personal zur Verfügung, könnte es sein, dass Sie eine informelle Methode bei
der Sammlung und Überprüfung von Anforderungen anwenden möchten: Sie wollen zwar noch alles dokumentieren, allerdings ohne formelle Prüfung und Genehmigungsprozess. Gibt es für Ihr Projekt jedoch komplexe Anforderungen, beträchtliche Mittel und viel Personal, sollten Sie dem Sammeln von Anforderungen, der Analyse, Validierung und Genehmigung einen formellen Prozess vorausschicken; wenn Sie bei sich keinen solchen Prozess erkennen, könnte dies von einem Fehler in der Anfangsphase zeugen.
Entwicklung und Implementierung Irgend jemand hat einmal gesagt, dass bei der Entwicklung eines Systems zehntausend Entscheidungen getroffen und selten aufgezeichnet würden. Es gibt viele wichtige Aspekte bei der Anwendungsentwicklung: ●
●
●
Entwickeln Sie ein logisches Datenmodell: Falls für die Anwendung keine Datenbank existiert, ist ein logisches Datenmodell erforderlich; darüber werden Sie am Tag 3, »Logischer Datenbankentwurf«, noch mehr erfahren. Hierbei handelt es sich um den wahrscheinlich wichtigsten Schritt im Entwicklungsprozess. Unabhängig von der Projektgröße, sollten Sie ein Werkzeug zur Erstellung von Entity Relationship Diagrammen wie z.B. Oracle Designer oder ERwin von Logic Works einsetzen. Am Tag 5 »Oracle Designer in der Anwendungsentwicklung« werden Sie lernen, wie ein Datenmodell mit Oracle Designer erstellt wird. Solche Werkzeuge erlauben es Ihnen, ein grafisches Modell eines Datenbankentwurfs zu erstellen. Der große Vorteil dieser Werkzeuge liegt darin, dass sie aus dem Modell Anweisungen für die Erzeugung von Datenbankobjekten generieren. Außerdem erhalten Sie eine ausführliche Dokumentation des Datenbankentwurfs. Wählen Sie Entwicklungswerkzeuge aus: Sofern nicht andere die Entscheidung für Sie treffen, sollten Sie sich die zu unterstützenden Client-Systeme (z.B. Windows oder Motif) und die Vorlieben anderer Entwickler anschauen. Natürlich sollten Sie auch Ihre Erfahrungen mit verschiedenen Entwicklungswerkzeugen miteinbeziehen. Bei der Auswahl von Entwicklungswerkzeugen sollten Sie außerdem die Marktdurchdringung berücksichtigen. Falls Sie sich entschlossen haben, ein Werkzeug zu verwenden, mit dem Sie noch nie zuvor gearbeitet haben, sollten Sie sich genügend Zeit für die Einarbeitung lassen. Sie möchten etwaige Mängel sicher nicht erst mitten im Projekt entdecken. Implementieren Sie die von den Benutzern geforderten Funktionen: Im Allgemeinen ist es besser, mehrere Versionen der Anwendung für die Benutzer einzuplanen als zu wenige. Mit jeder Version erhöht sich die von den Benutzern erwartete Funktionalität. Jede Version bietet den Benutzern die Gelegenheit, Ihnen entsprechende Rückmeldungen zu geben. Darüber hinaus erhalten Sie die Möglichkeit, diese Rückmeldungen in die nächste Version einzuarbeiten. Durch diesen Prozess bringen sich die Benutzer in das Projekt ein. Mit der Zeit
●
●
●
●
werden sie zu Beteiligten. Dies ist entscheidend, denn falls die Benutzer die endgültige Version der Anwendung nicht akzeptieren, ist sie zum Scheitern verurteilt. Nutzen Sie wieder verwendbare Komponenten: Versuchen Sie möglichst, eine Reihe wieder verwendbarer Komponenten einzusetzen oder zu entwerfen. Man braucht nicht zu erwähnen, dass der Grad der Wiederverwendbarkeit von den benutzten Entwicklungswerkzeugen abhängt. Handelt es sich nicht um objektorientierte Werkzeuge, bestehen die wieder verwendbaren Komponenten aus Formular- und Berichtsvorlagen, Bibliotheken und Header-Dateien. Unterstützen die Werkzeuge eine objektorientierte Entwicklung, bestehen die wieder verwendbaren Komponenten aus Basisklassen, aus denen eine funktionierende Anwendung erstellt werden kann. Obwohl die Wiederverwendbarkeit ein hochgestecktes Ziel ist, sollten Sie gut überlegen, wie viel Zeit und Energie Sie investieren. Verwenden Sie ein Werkzeug für die Konfigurationsverwaltung: Selbst wenn der Aufwand für die Entwicklung Ihrer Anwendung nur gering ist, sollten Sie die Verwendung einer Art von Konfigurationsverwaltung in Erwägung ziehen. Zumindest sollten Sie eine Sicherungskopie aller Versionen Ihrer Anwendung entweder auf Band oder Diskette aufbewahren. Für mittlere oder große Projekte ist dies nicht ausreichend. Planen Sie die Verwendung eines MehrbenutzerWerkzeugs für die Konfigurationsverwaltung ein, welches die Versionssteuerung unterstützt. Die Konfigurationsverwaltung sollte folgendes umfassen: Quellcodedateien, SQL-Skripte, um die Oracle-Datenbank zu erstellen und mit Daten zu füllen, PL/SQL-Quellcode, Entwürfe, Testdatenreihen, Schulungsmaterial und Textdateien für die Erstellung einer Online-Hilfe. Identifizieren Sie Benutzerrollen und -berechtigungen: Identifizieren Sie bei der Entwicklung Ihrer Anwendung die wichtigsten Rollen, die von den Benutzern beim Einsatz des Systems eingenommen werden. Sie sollten diese Rollen auf zwei Ebenen implementieren: auf Datenbankebene und auf Anwendungsebene beides ist wichtig. Sie sollten die Berechtigungen - Erstellen, Lesen, Ändern und Löschen (create, read, update, delete - häufig als CRUD bezeichnet) - festlegen, die den einzelnen Rollen für den Zugriff auf Datenbanktabellen, Sichten oder andere Objekte zugeteilt werden. Sie sollten in der Lage sein einem Benutzer bei der Anmeldung eine Rolle zuzuordnen und in Abhängigkeit dieser Rolle Menüeinträge zu aktivieren bzw. deaktivieren. Bleiben Sie so weit wie möglich plattformunabhängig: Es gibt zwei Plattformen, die Sie während der Entwicklungsphase des Projekts berücksichtigen müssen: den Server und den Client. Was den Server anbelangt, sollten Sie für die Implementierung der Anwendungslogik so oft wie möglich PL/SQL verwenden; dadurch verringert sich der erforderliche Aufwand, um die Anwendung von einer Server-Plattform (z.B. Windows NT) auf eine andere Server-Plattform (z.B. Solaris) zu übertragen. Für den Client sollten Sie herausfinden, welche für das Betriebssystem spezifischen Merkmale Sie bereitstellen müssen. Wenn Sie
●
●
betriebssystemspezifische Merkmale in die Anwendung einbinden (z.B. OLE), kann die Anwendung diese Eigenschaften nicht auf allen Client-Plattformen (wie z.B. Mac und Motif) zur Verfügung stellen. Das bedeutet, Sie würden einige Benutzer mehr erfreuen als andere. Sorgen Sie für eine konsistente Benutzeroberfläche: Alle Bildschirm- und Berichtskategorien sollten ein ähnliches Erscheinungsbild aufweisen. Beispielsweise sollten alle Eingabebildschirme über dieselben Schaltflächen verfügen, die etwa an derselben Stelle liegen und dieselben Funktionen haben. Für Ihre Formulare sollten Sie dieselben Schriftarten, Schriftgrößen und Farben verwenden. Wenn ein grauer Hintergrund auf dem einen Bildschirm bedeutet, dass das betreffende Feld schreibgeschützt ist, sollten alle schreibgeschützten Felder einen grauen Hintergrund aufweisen. Binden Sie eine Diagnosemöglichkeit in die Anwendung ein: Wenn möglich, sollten Sie eine Diagnosemöglichkeit in Ihre Anwendung integrieren. Diese Komponente sollte es ermöglichen, die Ausführung der Anwendung anzuzeigen oder zu protokollieren (oder beides). Dieses Protokoll kann den Namen des ausführenden Moduls oder der Funktion, einen Zeitstempel, signifikante Ereignisse, Fehlercodes und andere Punkte enthalten, die Ihnen in der Entwicklungs- oder Einsatzphase des Projekts beim Aufspüren von Problemen helfen können. Um größtmögliche Flexibilität zu erreichen, sollte die Diagnosefunktion zur Laufzeit konfigurierbar sein (z.B. in einer INI-Datei oder der Windows-Registrierung).
Testen der Anwendung Auf jeder Entwicklungsstufe Ihrer Anwendung sollten Sie eine Testphase einplanen. Wichtig ist zu definieren, was unter Testen zu verstehen ist. Wenn vielleicht auch im weitesten Sinne des Worts, kann Testen folgendes umfassen: ● ●
●
●
Prüfen, ob das logische Datenmodell vollständig, korrekt und konsistent ist. Die Zustimmung der Benutzer zur allgemeinen Benutzeroberfläche, Menüstruktur und zum Kontrollfluss einholen. Repräsentative Daten, entweder bekannte oder neue Daten, in die OracleDatenbank eingeben, um das Datenmodell, Regeln und Größenvoraussetzungen zu validieren. Prüfen, ob die PL/SQL-Programmblöcke wie geplant funktionieren (PL/SQL ist eine Programmerweiterung der Programmiersprache SQL, die Sie in späteren Lektionen noch näher kennen lernen werden).
Zusätzlich zu den oben genannten Punkten müssen Sie spezielle Tests einplanen: ●
Typische (und atypische) Testläufe, um festzustellen, ob die Anwendung richtige Eingaben akzeptiert, ungültige Eingaben und Auswahlvarianten ablehnt und
●
richtige Ergebnisse liefert. Leistungstests, bei denen unterschiedliche Auslastungsstufen simuliert werden, wie zum Beispiel die Anzahl der Benutzer, die gleichzeitig Datensätze der Studenten ändern.
Wenn Sie eine Anwendung erstellen, die auf unterschiedlichen Client-Systemen (wie z.B. Windows 98, Windows 2000 und Mac) eingesetzt werden soll, müssen Sie sicherstellen, dass Ihnen diese Computer für Tests, Einsatz und Pflege zur Verfügung stehen. Andernfalls werden Sie viel Zeit dafür aufbringen müssen, festgestellte Fehler zu reproduzieren, zu diagnostizieren und zu beheben. Außerdem sollten Sie sich vergewissern, dass diese Geräte für die Computer der Benutzer im Hinblick auf die Prozessorgeschwindigkeit, den Speicher und den verfügbaren Plattenplatz repräsentativ sind.
Inbetriebnahme der Anwendung Während der Inbetriebnahme sind mehrere Punkte zu berücksichtigen: ●
●
●
Installieren der Anwendung: Wenn Sie die Anwendung für eine kleine Benutzergruppe installieren, deren Standorte dicht beieinander liegen, können Sie sich vielleicht den Luxus leisten, die Software selbst zu installieren. Ist die Anzahl der Benutzer jedoch groß oder sind deren Standorte weit verteilt, müssen Sie die Installation der Software automatisieren. Es gibt verschiedene Möglichkeiten. Sie könnten eine Reihe von Installationsdisketten (oder CDROMs) herstellen, welche die Installation automatisieren. Sie können einen Datei-Server verwenden, auf den alle Benutzer bei der Installation zugreifen können. Oder Sie können eine Web Site einrichten, von der die Benutzer die Software herunterladen können und dann einer Reihe von Anweisungen folgen müssen. In allen Fällen umfasst die Installation zwei Hauptaufgaben. Die erste ist die Erstellung eines Verzeichnisses auf dem Computer des Benutzers, das Kopieren aller notwendigen Dateien in dieses Verzeichnis und das Konfigurieren der Umgebung. Die zweite Aufgabe besteht aus der Installation und Konfiguration der Oracle Net Services, falls diese noch nicht installiert sind. Benutzerschulung: Es muss in jedem Fall Zeit eingeräumt werden, um die Endbenutzer für den Einsatz der Anwendung zu schulen. Diese Schulung kann durch einen Lehrer oder ein Lernprogramm erfolgen. Manche Führungskräfte könnten einwenden, dass die Endbenutzer nicht genügend Zeit für eine regelmäßige Schulung haben. Aber wie man es auch nimmt, die Benutzer brauchen Zeit, um die Anwendung der Software zu erlernen. Online-Hilfe bereitstellen: Nach erfolgter Schulung werden die Benutzer sicherlich immer noch Fragen zur Verwendung der Software haben. Eine wirksame Möglichkeit, Antworten auf diese Fragen zu erteilen, ist die Bereitstellung einer Hilfedatei im Anwendungsprogramm, um kontextbezogene
Hilfe zu leisten.
Pflege der Anwendung Sie haben einen langen Weg zurückgelegt. Sie haben Anforderungen gesammelt, die Anwendung entwickelt, implementiert, getestet und eingesetzt - was nun? Warten Sie auf die Rückmeldungen der Benutzer - sowohl (hoffentlich) positive als auch negative (falls es Ihnen hilft, betrachten Sie diese als konstruktive Kritik). Unter den Rückmeldungen der Benutzer werden sich wahrscheinlich auch Anfragen nach zusätzlichen Funktionen befinden (z.B. »Wie lange würden Sie brauchen, um einen Bericht zu erstellen, der so aussieht?«) sowie Fehlerberichte (z.B. »Wenn ich F5 drücke, ist der Bildschirm plötzlich leer.«). Wenn Sie keine Rückmeldungen erhalten, ist das ein schlechtes Zeichen - es könnte bedeuten, dass nur wenige die Software tatsächlich einsetzen. Die Rückmeldungen sollten nach Schwere und Umfang geordnet werden. Die Behebung kritischer Fehler kann mit einer Notversion erfolgen, in der vielleicht nur die zu ersetzenden Dateien enthalten sind. Die Beseitigung weniger kritischer Fehler oder die Einbindung neuer Funktionen sollte mit einer regulären Version oder einem Upgrade erfolgen.
2.2 Zusammenfassung In dieser Lektion haben Sie die wichtigsten Phasen der Entwicklung einer OracleAnwendung kennengelernt: ● ● ● ● ● ●
Sammeln von Anforderungen Entwerfen der Anwendung Implementieren der Anwendung Testen der Anwendung Inbetriebnahme der Anwendung Pflege der Anwendung
2.3 Wie geht es weiter? Am Tag 3 werden Sie in die begriffliche Theorie von relationalen Datenbanken einsteigen: Relationen, Attribute, Domänen und Beziehungen. Außerdem erfolgt eine Einführung in die Begriffe der Datenintegrität und der referentiellen Integrität sowie einige der Normalformen.
2.4 Fragen und Antworten
Frage: Mit welchen Punkten sollte ich mich beschäftigen, wenn eine neue Anwendung ein vorhandenes System ersetzen soll? Antwort: Es gibt eine Reihe von politischen und technischen Fragen, die Sie berücksichtigen müssen. Jede vorhandene Anwendung hat ihre Befürworter und Kritiker. Sicherlich sollten Sie mit beiden Gruppen viele Gespräche führen, um die Vor- und Nachteile der vorhandenen Anwendung verstehen zu können. Antwort: Versuchen Sie, sich bei der Entwicklung der neuen Anwendung nicht von den Funktionen einer Altanwendung einengen zu lassen. Sie wollen die Anwendung bestimmt nicht einfach mit Hilfe eines gängigen Software-Entwicklungswerkzeugs »umschreiben«. Antwort: Versuchen Sie Entwickler und Administratoren der Altanwendung so gut wie möglich in die Neuentwicklung einzubeziehen. Beide Personenkreise haben sicherlich viele Ideen für eine Verbesserung des Systems und fühlen sich möglicherweise durch die Entwicklung einer neuen Anwendung bedroht. Stehen Sie der alten Anwendung nicht zu kritisch gegenüber. Deren Entwickler haben wahrscheinlich mit den zur Verfügung stehenden Ressourcen ihr Bestes getan. Wenn Sie den Übergang von der alten zur neuen Anwendung planen, sollten Sie schrittweise vorgehen und einen Zeitraum vorsehen, in dem das alte System weiterhin läuft, aber bereits Funktionselemente des neuen Systems eingesetzt werden. Unterschätzen Sie aber nicht den für die Migration der alten Daten in die neue OracleDatenbank erforderlichen Aufwand.
2.5 Workshop Der Zweck dieses Workshop ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Richtig oder falsch? Wenn Sie ein Werkzeug für die Anwendungsentwicklung benutzen, welches eine objektorientierte Entwicklung unterstützt, ist die Chance größer, eine erfolgreiche Anwendung zu implementieren. 2. Nennen Sie drei Arten von Anforderungen, die bei der Entwicklung eines
Systems erforderlich sind. 3. Richtig oder falsch? Sie sollten erst mit der Softwareentwicklung beginnen, wenn Ihnen die Anforderungen vollständig bekannt sind.
Übungen 1. Welche Arten von Risiken könnten bei der Entwicklung einer Oracle-Anwendung auftreten? 2. Welche Faktoren könnten zu diesen Risikokategorien gehören? 3. Welche Schritte könnten Sie unternehmen, um diese Risiken zu mildern?
Die Welt relationaler Datenbanken erforschen 1970 veröffentlichte »Communications of the ACM«, eine renommierte Zeitschrift der Informatikwelt, einen Aufsatz mit dem Titel »A Relational Model of Data for Large Shared Data Banks«. Der von Dr. E. F. Codd, einem Mitarbeiter des IBM Research Laboratory in San José, verfasste Aufsatz stellte die theoretische und mathematische Grundlage für das Konzept einer relationalen Datenbank dar. Kaum ein anderer einzelner Artikel aus dem Bereich der Informatik hatte einen so großen Einfluss auf Hersteller, Praktiker und Benutzer wie dieser Beitrag.
Bei einer relationalen Datenbank handelt es sich um ein Informationssystem, welches Daten in Form von Zeilen in einer Reihe von Tabellen darstellt, wobei jede Tabelle über eine oder mehrere Spalten verfügt. In seinem Aufsatz beschrieb Codd die Elemente einer relationalen Datenbank: Beziehungen, Attribute, Wertebereiche (Domänen) und die Vergleichsoperatoren. Codds Abhandlung beschrieb ein Datenspeichersystem, das über drei Eigenschaften verfügte, die zu jenem Zeitpunkt dringend benötigt wurden: ●
●
Logische Datenunabhängigkeit: Diese wünschenswerte Eigenschaft bedeutet, dass an einem Attribut (Spalte) vorgenommene Änderungen - zum Beispiel Vergrößerung oder Verkleinerung - keine spürbaren Auswirkungen auf andere Attribute mit derselben Relation (Tabelle) haben. Die logische Datenunabhängigkeit war für Datenverarbeitungsunternehmen reizvoll, da hiermit die Kosten für die Softwarepflege beträchtlich gesenkt werden konnten. Referentielle Integrität und Datenintegrität: Im Gegensatz zu anderen Datenbanksystemen würde eine relationale Datenbank die
●
Anwendungssoftware von der Last befreien, Integritätsregeln durchsetzen zu müssen. Codd schilderte zwei Merkmale, die von einer relationalen Datenbank gewahrt würden - die referentielle Integrität und die Datenintegrität. Diese Eigenschaften werden in diesem Buch noch im Einzelnen erläutert. Ad-hoc-Abfrage: Mit Hilfe dieser Eigenschaft könnte der Benutzer der Datenbank vorgeben, welche Daten abgefragt werden sollen, ohne dabei anzugeben, auf welche Weise dies erfolgen soll.
Es ist wichtig, die Grenzen der seinerzeit vorhandenen Datenbanken zu verstehen. Der Durchschnittsbenutzer - sofern es sich nicht um Programmierer handelte - konnte keine Daten abfragen, die gewissen Kriterien entsprachen, solange kein Programm für diese speziellen Anforderungen geschrieben wurde. Um lediglich die Breite eines vorhandenen Felds, z.B. zipcode (PLZ), von fünf auf neun Zeichen zu erhöhen, mussten die mit der Wartung betrauten Programmierer zahllose Programme ändern, nur um die Feldadressen anzupassen. Viele dieser Programme verfügten möglicherweise noch nicht einmal über einen direkten Verweis auf das Feld zipcode, aber die Auswirkungen durch die Vergrößerung eines Felds machte sich bei diesen älteren Datenbanken im gesamten System bemerkbar. Es verging noch eine gewisse Zeit, bevor einige der Funktionen der von Codd beschriebenen relationalen Datenbank tatsächlich in einem kommerziellen Produkt implementiert wurden. Anfang der achtziger Jahre wurde die relationale Datenbank zur Grundlage von Entscheidungshilfen. Durch die Leistungen der relationalen Datenbank konnten Benutzer in Unternehmen über interaktive Abfragen und Berichte Daten analysieren, ohne dabei die Hilfe von Programmierern in Anspruch nehmen zu müssen. Dank weiterer Fortschritte im Hardware-Bereich und in der relationalen Technik erfuhren relationale Datenbanken in Transaktionsverarbeitungssystemen Ende der achtziger Jahre eine größere Akzeptanz.
Die heutigen relationalen Datenbanken enthalten eine Reihe besonders nützlicher Funktionen, die Codd in seinem damaligen Artikel nicht erwähnt hat. Zum Zeitpunkt der Drucklegung dieses Buches gibt es auf dem Markt jedoch noch keine Datenbank, in der alle von Codd aufgestellten Regeln für relationale Datenbanken implementiert sind. Heutzutage sind relationale Datenbanken das Kernstück der Informationssysteme vieler großer und kleiner Organisationen, sowohl auf dem öffentlichen als auch dem privaten Sektor. Viele Hersteller verkaufen heute relationale Datenbankmanagementsysteme (RDBMS); einige der bekannteren Hersteller sind
Oracle, Sybase, IBM (DB2), Informix, Microsoft (SQL Server), NCR (Teradata) und Computer Associates. Unter diesen Herstellern hat sich Oracle zum Marktführer entwickelt. Das RDBMS von Oracle wurde auf mehr Plattformen portiert als alle anderen Datenbankprodukte. Da Oracle viele Plattformen unterstützt, haben sich viele Hersteller von Anwendungssoftware für Oracle als Datenbankplattform entschieden.
Bei einem RDBMS (Relational Database Management System) handelt es sich um die von einem Hersteller wie Oracle Corporation bereitgestellte Software, mit deren Hilfe eine relationale Datenbank verwaltet wird. Ein RDBMS unterstützt die Verwendung deklarativer Anweisungen, welche die Regeln beschreiben, denen die Daten entsprechen sollen. Diese Funktion wird als deklarative Integrität bezeichnet. Bei einem RDBMS handelt es sich um die Hauptkomponente einer Client-Server-Architektur, auf die wir in dieser Lektion noch zu sprechen kommen.
1.1 Structured Query Language (SQL) Bei der Structured Query Language (SQL, strukturierte Abfragesprache) handelt es sich um eine nichtprozedurale Sprache; im Gegensatz zu C oder Cobol, in denen Sie den Zugriff auf und die Bearbeitung von Daten genau beschreiben müssen, gibt SQL an, was zu tun ist. Oracle ermittelt intern, wie die Abfrage ablaufen soll. SQL entspricht den Normen des American National Standards Institute (ANSI) und der International Standards Organization (ISO) sowie dem Industriestandard. Die Implementierung von SQL durch Oracle entspricht ANSI X3.135-1989/ISO 9075-1989, Stufe 2. Wie andere Datenbankhersteller bietet auch Oracle viele Erweiterungen zur ANSI/ISO SQL.
Die Structured Query Language (SQL) ist die Standardsprache für den Zugriff auf eine relationale Datenbank. Oracle hat die ANSI/ISO Norm SQL:1999 in allen wesentlichen Punkten in seinen Produkten umgesetzt. Detaillierte Informationen zur Umsetzung einzelner Aspekte des SQL:1999 Standards sind im Anhang B des Handbuchs »Oracle9i SQL Reference« nachzulesen. Außerdem entspricht die Implementierung von SQL bei Oracle den
Normen der US-Regierung, wie in der Federal Information Processing Standard Publication (FIPS PUB) 127 unter dem Titel Database Language SQL beschrieben.
1.2 Die Oracle-Produktreihe Als weltweit führender Hersteller von Software für relationale Datenbanken vertreibt die Oracle Corporation ihr Aushängeschild, die Oracle9i Datenbank, auf allen bedeutenden Plattformen wie z.B. Compaq Tru64 UNIX (64bit), Compaq OpenVMS, Fujitsu Siemens PrimePower Solaris (32bit/64bit), Hewlett Packard HP-UX (64bit), IBM AIX (64bit), IBM NumaQ, IBM OS/390, Linux Intel (32bit), Microsoft Windows/NT/2000 (32bit), Sun Microsystems Sparc Solaris (32bit/64bit). Oracle8i und Oracle8 sind auf deutlich mehr Plattformen erhältlich. Der Unterschied zwischen einem 32bit und einem 64bit Oracle Server liegt in der Größe des adressierbaren Speicherbereiches. Der Oracle 64bit Server kann größere Puffer (z.B. für Datenbankblöcke) verwalten als der 32bit Server. Das Oracle- RDBMS steht in den folgenden drei Konfigurationen zur Verfügung: ●
Oracle9i Standard Edition ist ein äußerst hochwertiges Datenbankprodukt, das alle Merkmale für hohen Durchsatz im Bereich Transaktionsverarbeitung (OLTP Online Transaction Processing), Messaging (Advanced Queueing) und Multimedia beinhaltet. Darüber hinaus sind in Oracle9i Text für Volltextrecherche und Dokumentenklassifizierung sowie Grundfunktionalität für die Implementierung von geographischen Informationssystemen enthalten. Oracle Standard Edition kann für Rechnersysteme, die mit maximal vier CPUs ausgestattet werden können, lizenziert werden. Eine Java Virtual Machine ist Teil des RDBMS und verarbeitet gespeicherte Prozeduren oder erzeugt Java Server Pages. Backup und Recovery sind mit allen namhaften Media Management Herstellern wie Legato, Veritas, Tivoli, ArcServe u.s.w. integriert. Grundfunktionalität für die Einrichtung einer Standby Datenbank für Desaster Recovery Szenarien ist enthalten. Weitere Merkmale von Standard Edition sind graphische Administration und Überwachung mit Oracle Enterprise Manager, Unterstützung verteilter Transaktionen, Replikation, Schnittstellen zu anderen Datenbanken (Heterogeneous Services z.B. mit ODBC), Unterstützung mehrerer Blockgrößen in einer Datenbank, Large Objects (LOBs), verschachtelte Tabellen (NESTED TABLES), VARRAYs, Oracle C++ Call Interface (OCCI) und Unterstützung für Objektrelationale Konzepte wie benutzerdefinierte Abstrakte Datentypen, Methoden und Vererbung.
Zusammenfassend ist festzustellen, dass bereits Oracle9i Standard Edition eine Vielzahl von Merkmalen, wie z.B. nichtblockierende Mehrversions-Lesekonsistenz für hochgradig parallelen Datenzugriff, beinhaltet, die in den Enterprise Editions anderer Hersteller nicht zu finden sind. ●
Oracle9i Enterprise Edition enthält alle Merkmale von Oracle9i Standard Edition und viele weitere Merkmale wie Bitmap Indizes, Erstellung transportierbarer Tablespaces, parallele Abfragen (Parallel Query) und Änderungen (Parallel DML), Data Mining, OLAP (Online Analytic Processing), Online Index Build, Online Reorganisation von Tabellen, Advanced Replication, Block Level Media Recovery, Data Guard (erweiterte Funktionalität im Bereich Standby Datenbank), Tablespace Point-in-Time Recovery, Parallel Recovery und Transparent Application Failover (TAF). Oracle9i kann durch die Lizenzierung zusätzlicher Optionen weiter aufgewertet werden. Die wichtigsten Optionen sind in Tabelle 1.1 wiedergegeben. Oracle9i Enterprise Edition ermöglicht die Implementierung hochperformanter und hochverfügbarer Anwendungen in den Bereichen Transaktionsverarbeitung, Messaging, Data Warehousing und Multimedia.
Ein Data Warehouse ist eine Datenbank, die umfangreiche Auswertungen der Geschäftsentwicklung eines Unternehmens ermöglicht. Auf Basis der Auswertungen werden Unternehmensentscheidungen getroffen. ●
Personal Oracle ist als Plattform für einen einzelnen Datenbankentwickler unter Windows NT/2000 gedacht. Bis auf zwei Management Packs und die Option Real Application Clusters, die für Personal Oracle nicht verfügbar sind, entspricht Personal Oracle der Oracle9i Enterprise Edition. Oracle gibt damit Entwicklern die Möglichkeit, im Einzelplatzbetrieb zu einem günstigen Preis alle Funktionalitäten von Oracle9i Enterprise Edition, z.B. zur Entwicklung von Prototypen, zu nutzen.
1.3 Weitere Oracle-Produkte Außer dem Oracle-RDBMS vertreibt die Oracle Corporation weitere Produkte: ●
Internet Application Server (iAS) ist ein Applikationsserver, der Java2 Enterprise Edition und Java Server Pages unterstützt.
●
●
●
●
Bei Internet Developer Suite handelt es sich um eine Werkzeugfamilie, welche die Entwicklung von Client-Server und Web-Anwendungen unterstützt. Jedes dieser Werkzeuge wird von den GUI-Umgebungen Windows und Motif unterstützt. Mehr über Developer erfahren Sie an den Tagen 15 bis 19. Internet Developer Suite umfasst folgende Werkzeuge: Oracle Forms ist ein Entwicklungswerkzeug zur Erstellung formularbezogener Anwendungen, welche eng mit der Oracle-Datenbank integriert sind. Oracle Reports ist ein Berichtsgenerator, der auch den komplexesten Anforderungen gerecht wird. Diese Berichte können vor dem Ausdrucken mit Hilfe der Vorschaufunktion auf dem Bildschirm angezeigt werden. Oracle Procedure Builder wird zur Entwicklung, zur Pflege und zum Testen von Software verwendet, die in einer Sprache namens PL/SQL geschrieben wurde, welche aus einer Oracle-Datenbank heraus gespeichert und ausgeführt werden kann.
Bei PL/SQL handelt es sich um eine von der Oracle Corporation entwickelte Programmiersprache, welche prozedurale Erweiterungen für SQL zur Verfügung stellt. PL/SQL-Module sind in Oracle-Datenbanken enthalten, um Geschäftsregeln zu implementieren, Ausnahmen zu verwalten und die Funktionen bereitzustellen, die von anderen Werkzeugen für die Anwendungsentwicklung aktiviert werden können. ●
●
Designer ist eine Familie von Werkzeugen für die Entwicklung komplexer Anwendungen, die sich über viele Organisationen innerhalb eines Unternehmens erstrecken können. Jedes Werkzeug unterstützt unterschiedliche Aufgaben innerhalb der Entwicklungsorganisation - Analytiker, Datenbankentwickler, Anwendungsentwickler und Programmierer. Die Komponenten von Designer unterstützen die Entwicklung von Datenmodellen mit Entity Relationship Diagrammen, die automatische Erstellung von Datenbanken, die Entwicklung unternehmensspezifischer Funktionen und die Generierung von Anwendungsobjekten, wie z.B. Oracle-Forms-Anwendungen, Visual-Basic-Programme und Web-Anwendungen. Precompiler wie Pro*C/C++ sind Werkzeuge für die Software-Entwicklung, mit denen Sie SQL-Anweisungen in ein Programm einbetten können, das in einer Sprache der dritten Generation (3GL), wie z.B. C, Fortran oder Cobol, geschrieben wurde. Der entsprechende Oracle-Precompiler übersetzt eingebettete SQL- Anweisungen in Aufrufe der Oracle-Bibliotheksfunktionen.
1.4 Was bedeutet Client-ServerDatenverarbeitung? In dieser Lektion erkunden Sie sowohl die Entwicklung der Architektur der ClientServer- Datenverarbeitung als auch deren Zukunftsaussichten. Die meisten Werkzeuge für die Anwendungsentwicklung, über die wir in diesem Buch sprechen, unterstützen die Architektur der Client-Server-Datenverarbeitung. Lassen Sie uns also gleich klären, worum es sich bei der Architektur der Client-Server-Datenverarbeitung handelt.
Die Architektur der Client-Server-Datenverarbeitung besteht aus einem oder mehreren Computern, die als Client-Computer bezeichnet werden, auf denen ein Anwendungsprogramm läuft, welches mit einem entfernten Rechner kommuniziert, der als Server-Computer bezeichnet wird und Anfragen der Client-Systeme bearbeitet.
Beim Grundmodell einer Client-Server-Architektur befindet sich RDBMS auf dem Server-Computer. Das auf dem Client-System befindliche Anwendungsprogramm ist über eine Schnittstelle mit einer weiteren Softwareschicht verbunden, die als Middleware bezeichnet wird und für die Übermittlung von Anfragen und deren Ergebnissen zwischen Anwendungsprogramm und RDBMS zuständig ist. Die Client-Server-Architektur bietet mehrere Vorteile gegenüber früheren Datenverarbeitungsarchitekturen: ●
●
Sie unterstützt eine heterogene Mischung von Client-Systemen. Heutzutage können die Benutzer Windows 98, Windows NT/2000, einen Macintosh oder eine Unix- Workstation verwenden, welche alle vom selben Server unterstützt werden können. Die Aufgaben der Datenverarbeitung werden sinnvoll auf Client und Server verteilt. Der Client-Computer ist für die Steuerung der Benutzeroberfläche zuständig - die Anzeige von Daten, die Validierung von Eingaben und die Bereitstellung aussagefähiger Rückmeldungen -, während die Server-Plattform
●
normalerweise als Datenbank-Server dient. Sie ist unabhängig vom Netzwerkprotokoll, vom Betriebssystem der ServerPlattform und vom Betriebssystem der Client-Plattform. Durch diese Unabhängigkeit ergibt sich eine enorme Flexibilität bei der Auswahl der Hardund Softwarekomponenten für ein neues System oder bei der Implementierung einer Client-Server-Anwendung auf vorhandener Hardware.
Bei einer Client-Server-Architektur lastet die Unterstützung verschiedener Netzwerkprotokolle (wie z.B. TCP/IP, SPX/IPX und Named Pipes) und Betriebssysteme (wie z.B. Windows, MacOS, Unix u.a.) auf den Schultern der einzelnen Software- Hersteller, d.h. der RDBMS-Hersteller, wie z.B. Oracle, Sybase, Microsoft und Informix; sowie der Werkzeughersteller, wie z.B. Oracle, Powersoft und Microsoft und anderer Hersteller, die Komponenten wie ODBC-Treiber für besondere Plattformen und RDBMS liefern.
1.5 Die Ursprünge der Client-ServerDatenverarbeitung Um die Vorteile der Client-Server-Architektur einschätzen zu können, ist es notwendig, sich die frühere Datenverarbeitungsarchitektur vor Augen zu führen. Vor der Einführung des PC setzte sich ein Computersystem normalerweise aus einem Großrechner und vielen darauf zugreifenden »nichtprogrammierbaren« Terminals zusammen. Ein nichtprogrammierbares Terminal verfügte über keinen eigenen Speicher und keine Verarbeitungsmöglichkeiten; jedes auf dem Bildschirm erscheinende Zeichen wurde von einem Programm auf dem Großrechner gesteuert. Diese Architektur brachte einige Begleiterscheinungen mit sich: ●
●
●
Der Großrechner war für alles zuständig: das Speichern und Abfragen von Daten, den Aufbau der Benutzeroberfläche auf allen Terminals und die Validierung der Benutzereingaben. Als sich die Anzahl der aktiven Terminals erhöhte, wuchs auch die Belastung des Großrechners durch Ein- und Ausgaben. Um mit der Arbeitsbelastung fertig zu werden, waren normalerweise Hardware-Lösungen wie Terminalkonzentratoren und Multiplexer erforderlich. Die Benutzer waren vom Großrechner vollkommen abhängig. War der Großrechner abgeschaltet, fand keine Datenverarbeitung statt; ein nichtprogrammierbares Terminal war ohne den Computer, an den es angeschlossen war, wertlos. In den siebziger Jahren wurden jedoch »programmierbare« Terminals eingeführt, welche über eigenen Speicher und begrenzte Verarbeitungsmöglichkeiten verfügten, die von einem SoftwareEntwickler nutzbar gemacht werden konnten. Ein Anwendungsprogramm brachte Farbe auf den Bildschirm, nahm Anfragen von den Benutzern entgegen, z.B. über die Menüauswahl, validierte
Benutzereingaben und verwaltete die Datenspeicherung. Angesichts dieser Aufgabenmenge war ein Anwendungsprogramm normalerweise recht komplex gestaltet. Aufgrund der Hardware-Kosten und der Kenntnisse, die erforderlich waren, um einen Computer zum Laufen zu bringen, wurden die Rechnerressourcen der meisten großen Organisationen zentral durch eine einzige Gruppe gesteuert, die Abteilung für elektronische Datenverarbeitung (EDV). Angesichts der Komplexität der Anwendungsprogramme bestand ein beträchtlicher Rückstand bei der Entwicklung neuer Anwendungen, den man normalerweise in Jahren messen konnte. Mit der Einführung des PC änderte sich das alles schnell.
Die Geburtsstunde des PC Im August 1981 stellte IBM den Personalcomputer IBM 5150 PC vor. Natürlich war der IBM-PC nicht der erste Personalcomputer der Welt, es besteht aber kein Zweifel, dass IBM dem Personalcomputer zum Durchbruch verholfen hat. Verglichen mit dem heutigen Standard war der erste PC mit einem 8088-Prozessor von Intel mit 4,77 MHz, 64 Kbyte RAM, einem Schwarzweißbildschirm und einem 5,25"Diskettenlaufwerk als Speichermedium recht bescheiden ausgerüstet. Eines der ersten für den PC geschriebenen Programme war ein Terminalemulator, mit dem der PC ein Terminal des Typs IBM 3270 oder DEC VT100 simulieren konnte. Im Vergleich zu einem nichtprogrammierbaren Terminal war der erste PC recht teuer. Einige Büroanwendungen, wie z.B. WordStar (Textverarbeitung) und VisiCalc (das erste Tabellenkalkulationsprogramm), gewannen jedoch zunehmend an Beliebtheit. Daher konnte ein PC einen doppelten Zweck erfüllen: ●
●
Er konnte als Terminal verwendet werden und bot damit den Vorteil, dass von der Terminalsitzung ein Protokoll aufgezeichnet, gedruckt oder in ein Dokument eingebunden werden konnte. Mit Hilfe eines Standardprogramms, durch das die Produktivität des Benutzers beträchtlich erhöht wurde, konnte er selbständig eingesetzt werden.
PC-Datenbanken Ebenfalls 1981 stellte Ashton-Tate dBASE II vor, ein Datenbankprogramm für Einzelplatz- PCs. Zu der Zeit waren die meisten EDV-Abteilungen mit der Entwicklung neuer Anwendungen stark im Rückstand. Außerdem war die Entwicklung und der Einsatz einer neuen Anwendung für Großrechner eine teure und komplexe Angelegenheit. Die Entwicklungs- und Betriebsaufwendungen enthielten beträchtliche Kosten für die Betriebsstunden und permanente Datenspeicherung. Der Kauf eines
PC und des dBASE- Programms sowie die Entwicklung eines kundenspezifischen dBASE-Programms, über das er die Kontrolle hatte, stellten eine attraktive Alternative dar.
LAN-Datenbanken Als der PC zunehmend an Popularität gewann, suchten Organisationen mit mehreren PCs eine bequeme, kostengünstige Lösung, mit der die Geräte untereinander kommunizieren und teure Ressourcen, wie z.B. Massenspeicher, Modems und Drucker, gemeinsam nutzen konnten. Das Local Area Network (LAN, lokales Netzwerk) - eine Kombination von Netzwerkkarten, Kabeln und Software-Treibern erfüllte diese Anforderungen. Mit wachsender Akzeptanz des LAN auf dem Markt wünschten sich die Benutzer eine Datenbank, auf die mehrere PCs zugreifen konnten. Bald kamen Mehrbenutzerversionen von dBASE und anderen Datenverwaltungssystemen, wie z.B. Paradox und FoxPro, auf den Markt. Zu jener Zeit wurden sowohl von Software-Herstellern als auch von internen Programmierern viele Einzel- und Mehrbenutzeranwendungen entwickelt. Auf diesen Werkzeugen basierende Anwendungen brachten enorme Vorteile mit sich, darunter auch einen ausgezeichneten Investitionsertrag im Vergleich zu ähnlichen, für Großrechner bestimmten Anwendungen. Es gab allerdings auch einige negative Aspekte dabei: ●
●
●
Ungleichmäßige Qualität der entwickelten Software: Da diese Werkzeuge relativ leicht zu bedienen waren, konnte ein Neuling eine Anwendung erstellen, die mangelhaft entwickelt war. Obwohl sie vielleicht den ursprünglichen Anforderungen gerecht wurde, war die Anwendung so aufgebaut, dass künftige Änderungen einen beträchtlichen Aufwand erforderten. Außerdem war für viele dieser Organisationen das Konzept der Software-Entwicklung neu. Da sie wenig Erfahrung mit der Software- Entwicklung hatten, wurden viele wichtige Schritte, wie z.B. Dokumentationsbedarf und -entwurf, nicht berücksichtigt. Die Pflege des Programms war schwierig, wenn dessen eigentliche Entwickler nicht mehr zur Verfügung standen. Spürbarer Verlust an Einfluss durch die EDV-Abteilung: Die meisten werden sagen, dass der Verlust von Einfluss und Befugnissen seitens der EDVAbteilung ein positiver Aspekt bei der Ausbreitung von PC-Anwendungen war. Einige EDV-Abteilungen waren proaktiv; sie erkannten den Wert der PCRevolution und leiteten ihre Benutzergemeinde in diese Richtung. Viele EDVAbteilungen betrachteten diese Entwicklung jedoch als ernsthafte politische und wirtschaftliche Bedrohung und reagierten mit der Aufstellung von Regeln, die, sofern sie angemessen waren, als taktisches Mittel eingesetzt wurden, um die Kontrolle zurückzuerlangen. Inseln der Automatisierung: Ein berechtigter Einwand seitens der EDV-Abteilung
war, dass wertvolle Unternehmensdaten in separaten PC-Datenbanken stillgelegt würden und die darin enthaltenen Informationen nicht gemeinsam genutzt werden könnten.
Die Entstehung relationaler Datenbanken Als Mitte bis Ende der achtziger Jahre die PC-Datenbanken anwuchsen, verzeichnete sich in der EDV-Abteilung eine weitere dramatische Veränderung. In den ersten Jahren wurden relationale Datenbanken hauptsächlich für den Aufbau einer Entscheidungshilfe (DSS, Decision Support System) verwendet. Die Verkaufsabteilung konnte zum Beispiel eine relationale Datenbank mit Informationen über den Verkauf bestimmter Produktreihen in den einzelnen Verkaufsgebieten füttern. Mit Hilfe von SQL konnte ein Vertriebsanalytiker Anfragen an die Datenbank richten, bei denen nur wenige oder überhaupt keine Programmierungsarbeiten notwendig waren.
Middleware
Mitte der achtziger Jahre erkannten die Hersteller relationaler Datenbanken, wie sinnvoll es ist, die Verarbeitung einer Anwendung auf zwei Geräte aufzuteilen: einen Client-Computer, der für die Steuerung der Benutzeroberfläche zuständig sein sollte, und einen Server-Computer, auf dem das RDBMS läuft. Um dies zu erreichen, wurde eine SoftwareKategorie namens Middleware erfunden. Jeder RDBMS-Hersteller erstellte zwei Middleware-Komponenten: einen proprietären Client-Treiber und einen entsprechenden proprietären Server-Treiber. Jedes Netzwerkprotokoll und jedes Client- oder Server-Betriebssystem erforderte eine besondere Implementierung durch den RDBMS-Hersteller. Allerdings bot die Entwicklung von Middleware einige ernst zu nehmende Vorteile für Anwendungsentwickler: Mit Hilfe der Middleware konnte ein Anwendungsprogramm auf mehreren Client-Computern eingesetzt werden, von denen jeder über ein anderes Betriebssystem verfügte, um mit einer Datenbank auf einem Server-System zu kommunizieren, auf dem wiederum ein anderes Betriebssystem installiert war. Das Middleware-Produkt von Oracle heißt Net Services.
Bei einer Zwei-Ebenen-Architektur handelt es sich um eine Architektur für die Client-Server-Datenverarbeitung, die aus Client-Systemen besteht, die direkt mit dem Datenbank-Server kommunizieren (siehe Abbildung 1.1).
Abbildung 1.1: Zwei-Ebenen-Architektur
Abbildung 1.2: Drei-Ebenen--Architektur
Bei einer Drei-Ebenen-Architektur (three tier architecture) handelt es sich um eine Architektur für die Client-Server-Datenverarbeitung, die aus ClientSystemen besteht, die mit einem Anwendungs-Server kommunizieren. Zwischen Client und Anwendungs-Server werden häufig Protokolle wie HTTP (Hypertext Transfer Protocol) oder IIOP (Internet Inter-Object Request Broker Protocol) eingesetzt. Der Anwendungs-Server steht mit einer Datenbank in Verbindung.
Werkzeuge für die Anwendungsentwicklung Anfangs wurden die meisten Client-Server-Anwendungen in Sprachen der dritten Generation (3GL) erstellt, wie z.B. Fortran, C oder Cobol, mit denen Bibliotheksroutinen der RDBMS aufgerufen wurden. Einige RDBMS-Hersteller, wie z.B. Oracle, sahen allerdings die Notwendigkeit für ein Werkzeug, das die Entwicklung von Datenbankanwendungen reibungsloser gestalten könnte.
Als Ergebnis wurde eine neue Kategorie von Entwicklungssoftware geschaffen: die Sprachen der vierten Generation (4GL), deren Name richtigerweise zu erkennen gibt, dass es sich bei diesen Werkzeugen um eine höhere Abstraktionsebene handelt als bei 3GL. Heute werden 4GLs allgemein als Umgebungen für die Anwendungsentwicklung bezeichnet. Anfangs erzeugten 4GLs eine zeichenorientierte Benutzeroberfläche. Mit wachsender Akzeptanz der Windows-Plattform und der zunehmenden Verbreitung von hochauflösenden Farbgrafikkarten begannen die 4GL-Hersteller, grafische Benutzeroberflächen (GUIs), wie z.B. Windows, Macintosh und Motif, zu unterstützen.
»Thick«-Client und »Thin«-Client 1996 war die Entwicklung von Client-Server-Anwendungen ausgereift. Organisationen verstanden, wie Umgebungen für die Anwendungsentwicklung, z.B. Oracle Forms und PowerBuilder, bei der Erstellung einer Anwendung einzusetzen waren. Für große Organisationen war die Verwaltung einer Client-Server-Anwendung jedoch keine leichte Übung:
●
●
●
Für jede Client-Plattform mussten unterschiedliche Anwendungsversionen erstellt werden. Wenn eine neue Version der Anwendung herauskam, musste die Software an alle Client-Systeme verteilt werden. Wenn ein neuer Benutzer ein Anwendungsprogramm auf seinen Computer aufspielen wollte, musste die richtige Middleware-Version für dieses System installiert und konfiguriert werden.
Eine umfangreiche Studie der Gartner Group stellte fest, dass die jährlichen Durchschnittskosten für die Verwaltung eines PC ca. DM 22.000 betragen. Viele Führungskräfte betrachteten diese Probleme besonders in finanzieller und administrativer Hinsicht als Alptraum und suchten nach einem Ausweg. Gleichzeitig fanden zwei Ereignisse statt, die man nicht außer Acht lassen darf: ● ●
Die außerordentliche Akzeptanz des Internet und des World Wide Web Die Einführung und Übernahme der Programmiersprache Java durch Hardwareund Software-Hersteller
Die explosionsartige Verbreitung des World Wide Web und der Web-Browser veranlasste die RDBMS-Hersteller, wie z.B. Oracle, zur Entwicklung von Werkzeugen, welche die dynamische Darstellung von Datenbankinhalten im Web unterstützen sollten. Gleichzeitig zog Java als plattformunabhängige, objektorientierte, verteilte und sichere Sprache die Software-Hersteller an, die seit langem daran interessiert waren, Software einmal zu schreiben und diese dann auf einer Vielzahl von HardwarePlattformen und unterschiedlichen Betriebssystemen einzusetzen. Angesichts dieser beiden Bewegungen begannen viele Software-Hersteller, das Modell des »dünnen« Client zu befürworten: eine Benutzeroberfläche auf der Basis eines Web-Browsers, auf die ein Java-Zusatzprogramm von einem Server heruntergeladen wird, um dann auf dem Client-Computer ausgeführt zu werden. Die traditionelle Client-ServerArchitektur wurde als »dickes« Client-Modell bezeichnet, da der Client normalerweise eine große ausführbare Datei für das Anwendungsprogramm, mehrere Bibliotheken (z.B. DLLs) sowie die Middleware-Treiber (z.B. Oracle Net Services) benötigte. Die Argumente für die Unterstützung des »dünnen« Client-Modells lauten wie folgt: ● ●
●
●
Die Verwaltungskosten für Client-Systeme werden beträchtlich gesenkt. Anwendungen werden einmal geschrieben und können überall eingesetzt werden. Die Aktualisierung von Anwendungen erfolgt auf nur einem System: dem Server. Übertragbarkeit: Statt an einen bestimmten Client-Computer gebunden zu sein,
»folgt« eine Anwendung dem berechtigten Benutzer. Allerdings gibt es auch Bedenken hinsichtlich der Wirtschaftlichkeit des dünnen ClientModells: ●
●
Da die gesamte Anwendungssoftware von einem Server heruntergeladen wird, erhöht das dünne Client-Modell die Netzauslastung. Da es sich bei Java um eine virtuelle Maschine handelt, die Byte-Code interpretiert, sind Java-Programme nicht so schnell wie C-Programme, die im Maschinencode des Prozessors vorliegen. Leistung könnte ein wichtiger Punkt sein.
1.6 Die Herausforderung an die Client-ServerDatenverarbeitung: der Network Computer Eine Zeitlang hat sich Oracle-Chef Larry Ellison beklagt, dass PCs zu teuer und komplex für den Durchschnittsanwender seien. Sein Wunsch wäre ein Thin Client namens Network Computer, der weniger kosten würde als ein typischer PC und weit einfacher zu verwalten wäre, da die gesamte Software über einen Web-Browser heruntergeladen würde. Eine Vielzahl von Anwendungen wird heute nach dem Thin Client Modell betrieben, obwohl der PC in den meisten Unternehmen erhalten geblieben ist und nicht durch Geräte wie den Network Computer abgelöst wurde. Das Paradigma des Network Computers hat weite Verbreitung gefunden, während sich der Network Computer als Hardware Plattform nicht durchsetzen konnte.
1.7 CORBA 1989 wurde die Object Management Group (OMG) von Vertretern der Hard- und Software-Hersteller, Entwicklern und Software-Praktikern gegründet. Das Ziel der OMG war es, die Verwendung objektorientierter Software zu fördern. Zu diesem Zweck entwickelte die OMG eine begriffliche Grundlage namens Object Management Architecture. Auf dieser Grundlage wurde eine Spezifikation für die Common Object Request Broker Architecture, allgemein als CORBA bezeichnet, entwickelt. Die Spezifikation von CORBA beschreibt mehrere Komponenten: ●
●
Einen Object Request Broker (ORB), der Anfragen und Antworten zwischen Objekten in einer heterogenen, verteilten Umgebung unterstützt. Object Services, bei denen es sich um eine Reihe von Grunddiensten handelt, welche die Verwendung und Implementierung von Objekten unterstützen.
●
●
Common Facilities, bei denen es sich um eine Reihe von Diensten handelt, die von Anwendungen gemeinsam benutzt werden können. Application Objects, bei denen es sich um vom Hersteller oder Entwickler bereitgestellte Objekte mit einer definierten Oberfläche handelt.
CORBA bildet die Infrastruktur, auf der Enterprise Java Beans und die Java2 Enterprise Edition (J2EE) aufsetzen. Oracle unterstützt diese Standards mit den Produkten Oracle9i Datenbank und Oracle9i Application Server.
1.8 Zusammenfassung Die wichtigsten Gedanken aus dem Bereich der Client-Server-Datenverarbeitung lauten wie folgt: ●
●
●
●
●
●
●
●
Die theoretische Grundlage für relationale Datenbanken wurde im Jahr 1970 geschaffen. Die Structured Query Language (SQL) ist die Standardsprache für den Zugriff auf relationale Datenbanken. Es gibt drei Varianten des relationalen Datenbankmanagementsystems (RDBMS) von Oracle: Oracle Standard Edition, Oracle Enterprise Edition und Personal Oracle. Die Client-Server-Architektur unterstützt eine große Anzahl von ClientPlattformen, Server-Plattformen und Netzwerkprotokollen. Die Client-Server-Architektur erfordert die Verwendung einer Softwareschicht namens Middleware, deren Aufgabe es ist, die protokollunabhängige Kommunikation zwischen dem Client- und dem Server-System zu unterstützen. Eine Zwei-Ebenen-Architektur besteht aus einem Client-Computer, der direkt mit einem Datenbank-Server kommuniziert. Eine Drei-Ebenen-Architektur besteht aus einem Client-Computer, der mit einem Anwendungs-Server kommuniziert. Der Anwendungs-Server kommuniziert mit dem Datenbank-Server. Die Popularität des World Wide Web und der Programmiersprache Java haben zur Entwicklung vieler Anwendungen nach dem Thin-Client-Prinzip geführt.: Ein Web- Browser lädt die Java-Zusatzprogramme (Applets) entsprechend ihrer Notwendigkeit herunter.
1.9 Wie geht es weiter?
Am Tag 2, »Richtlinien für die Entwicklung einer Oracle-Anwendung«, lernen Sie Grundlagen über die Vorgehensweise bei der Erstellung einer Anwendung, wie z.B. das Sammeln der Benutzeranforderungen.
1.10 Fragen und Antworten Frage: Oracle Corporation hat Oracle9i auf den Markt gebracht. Werden SQL, PL/SQL und die Oracle-Entwicklungswerkzeuge in Oracle9i weiterhin unterstützt? Antwort: Natürlich. Oracle9i unterstützt SQL, PL/SQL und die Oracle- Entwicklungswerkzeuge. Statt die derzeit im Oracle-RDBMS verwendeten Funktionen zu ersetzen, erweitert Oracle9i schließlich die vorhandenen Fähigkeiten durch benutzerdefinierte Datentypen, Vererbung, eine integrierte Java Virtual Machine und viele andere Funktionen. Frage: Welche Umgebungen für die Anwendungsentwicklung werden in diesem Buch besprochen? Antwort: Sie werden zwei Umgebungen für die Anwendungsentwicklung kennen lernen: Developer, einschließlich Oracle Forms, Oracle Reports und Oracle JDeveloper.
1.11 Workshop Der Zweck dieses Workshops ist es, Ihnen die Möglichkeit zu geben, Ihr Wissen über das in dieser Lektion behandelte Thema zu testen. Schauen Sie, ob Sie die Fragen des Tests richtig beantworten können, und machen Sie die Übungen, bevor Sie mit der morgigen Lektion fortfahren.
Test 1. Nennen Sie zwei Vorteile relationaler Datenbanken gegenüber Dateiverwaltungssystemen. 2. Nennen Sie drei Vorteile der Architektur für die Client-Server-Datenverarbeitung im Vergleich zu der Datenverarbeitungsarchitektur von Großrechnern. 3. Wie heißt das Middleware-Produkt von Oracle? 4. Was ist ein »dicker« Client? Was ist ein »dünner« Client?
Übungen Wie bereits erwähnt, wird in diesem Buch eine Beispieldatenbank eines kleinen College verwendet. Denken Sie darüber nach, welche Art von Informationen Ihrer Meinung nach ein College speichern und abfragen möchte. Fragen Sie sich, welche Fragen ein Student, ein Professor oder ein Administrator an die Datenbank stellen könnte. Notieren Sie, wie Sie diese Informationen anordnen könnten. Sie werden am Tag 3, »Logischer Datenbankentwurf«, auf diese Informationen zurückkommen.
Tag 1 Die Welt relationaler Datenbanken erforschen 29 Tag 2 Richtlinien für die Entwicklung einer Oracle-Anwendung 47 Tag 3 Logischer Datenbankentwurf 59 Tag 4 Implementierung des logischen Modells: physischer Datenbankentwurf 101 Tag 5 Oracle Designer in der Anwendungsentwicklung 145 Tag 6 Einführung in SQL 173 Tag 7 Daten mit SQL ändern 199 In Woche 1 lernen Sie viele der in diesem Buch verwendeten Grundbegriffe kennen. Die Schwerpunkte liegen auf dem Entwurf und der Implementierung von Datenmodellen. Ab dem dritten Tag erlernen Sie Besonderheiten bei der Verwendung der Oracle- Datenbank. Im Überblick: ●
●
●
●
Tag 1, »Die Welt relationaler Datenbanken erforschen« Diese Lektion behandelt die historische Entwicklung relationaler Datenbanken. Wir werden Ihnen einige Produkte der Oracle Corporation vorstellen. Außerdem erfahren Sie etwas über die Architektur der Client-Server-Datenverarbeitung. Tag 2, »Richtlinien für die Entwicklung einer Oracle- Anwendung« Diese Lektion befasst sich schwerpunktmäßig mit der bei der Entwicklung einer Oracle-Anwendung üblichen Vorgehensweise. Neben technischen Aspekten behandelt diese Lektion auch organisatorische Fragen. Tag 3, »Logischer Datenbankentwurf« In dieser Lektion erfahren Sie etwas über die theoretischen Grundlagen relationaler Datenbanken. Außerdem wird der Entwurf der im Buch verwendeten Beispielschemata vorgestellt. Tag 4, »Implementierung des logischen Modells: physischer Datenbankentwurf« Auf der Grundlage der am dritten Tag erworbenen Kenntnisse beschreibt diese Lektion die Erstellung einer Oracle-Datenbank. Sie lernen, wie man Tabellen, Indizes und andere Datenbankobjekte erzeugt.
●
●
●
Tag 5, »Oracle Designer in der Anwendungsentwicklung« Im Verlauf dieser Lektion lernen Sie ein relativ neues Oracle-Werkzeug, den Designer, und seine Verwendung in der Anwendungsentwicklung kennen. Tag 6, »Einführung in SQL« Der Schwerpunkt dieser Lektion liegt auf der Verwendung von SQL, um Daten aus einer Oracle-Datenbank abzurufen. Tag 7, »Daten mit SQL ändern« In dieser Lektion lernen Sie, wie Sie Zeilen einer Tabelle mit Hilfe von SQL ändern können.
Einleitung Willkommen zu »Oracle9i Datenbankentwicklung in 21 Tagen«! Dieses Buch behandelt die Verwendung des Datenbanksystems Oracle9i und den Einsatz mehrerer Entwicklungswerkzeuge. Während einer Zeitdauer von drei Wochen werden Sie mit den unterschiedlichen Aspekten der Datenbankentwicklung vertraut gemacht. Das Buch gliedert sich in die drei Themenkomplexe Datenbankentwurf, Datenbankimplementierung und Anwendungsentwicklung. Der eingelegte Stundenplan verdeutlicht die thematische Gliederung der Lerninhalte und gibt Ihnen einen kompakten Überblick über den Inhalt des Buches. Die ersten Lektionen behandeln den logischen Datenbankentwurf, den physischen Datenbankentwurf und die Verwendung der strukturierten Abfragesprache SQL (Structured Query Language), der Standardsprache für die Kommunikation mit einer relationalen Datenbank. Diese einführenden Lektionen werden durch die Vorstellung von Oracle Designer, einem Produkt für Modellierung von Softwaresystemen und Codegenerierung, abgerundet. In den folgenden Kapiteln widmen Sie sich der Datenmanipulation mit SQL, bevor Sie sich den Sicherheitskonzepten, der Datenbankoptimierung (Tuning) und fortgeschrittenen Aspekten von Tabellen und Indizes zuwenden. Der Themenkomplex Datenbankimplementierung endet mit dem Erlernen von PL/SQL, der in Oracle9i integrierten prozeduralen Spracherweiterung zu SQL. Mit PL/SQL können Sie Anwendungslogik in eine Oracle-Datenbank einbetten. Sobald Sie die Grundlagen von PL/SQL erlernt haben, widmen Sie sich unter dem Themenkomplex Anwendungsentwicklung fünf Lektionen lang der Internet Developer Suite, einer Sammlung von Programmierwerkzeugen für die Anwendungsentwicklung, die Oracle Forms, Reports und Procedure Builder umfasst. Die beiden abschließenden Lektionen steigen in die Bereiche Oracle9i Application Server und Anwendungsentwicklung mit Java und dem Entwicklungswerkzeug JDeveloper ein. Sie erlernen die Implementierung internetfähiger Anwendungen mit Java Server Pages und JDBC. Sie erfahren, wie Sie anspruchsvolle Anwendungen mit den komponentenbasierten Oracle Business Components for Java (BC4J) erstellen, ohne Java Code zu schreiben.
Voraussetzungen Sie ziehen den meisten Nutzen aus diesem Buch, wenn Sie folgende praktische Erfahrungen mitbringen: ●
● ●
Kenntnisse mindestens einer Programmiersprache, wie z.B. Java, BASIC oder C bzw. C++. Sicherheit im Umgang mit Windows- oder UNIX- (auch Linux) Umgebungen. Vorkenntnisse im Umgang mit einer Datenbank.
Die beste Möglichkeit, sich in ein neues Themengebiet einzuarbeiten, besteht darin, viele Dinge auszuprobieren. Um den in jeder Lektion vorgestellten Beispielen folgen sowie jeden Test und jede Übung bearbeiten zu können, benötigen Sie Zugang zu folgenden Programmen: ●
●
● ● ●
Oracle9i Datenbank. Sie sollten Enterprise Edition oder Personal Oracle mit installierter Partitioning Option verwenden, damit Sie die Beispiele zur Partitioning Option nachvollziehen können. Oracle Internet Developer Suite. Fünf Lektionen beschäftigen sich mit den Komponenten der Internet Developer Suite, nämlich mit Oracle Forms, Reports, Oracle Graphics und Procedure Builder. Um die Funktionsweise der in diesen Lektionen vorgestellten Beispielanwendungen nachvollziehen zu können, ist es für Sie zweifellos wünschenswert, die entsprechenden Versionen dieser Programmierwerkzeuge zur Verfügung zu haben, um praktische Erfahrungen damit sammeln zu können. Oracle JDeveloper in der Version 9i Oracle 9i Internet Application Server Sie benötigen weiter einen Rechner, auf dem Windows oder UNIX (bzw. Linux) läuft.
Oracle stellt die benötigte Software registrierten Benutzern des Oracle Technology Network zum Herunterladen zur Verfügung. Sie erreichen das Oracle Technology Network (OTN) unter dem URL http://otn.oracle.com.
Über dieses Buch Dieses Buch ist als 21-Tage-Kurs zum Selbststudium gedacht, der Tests und Übungen am Ende jedes Kapitels sowie Beispiele umfasst, die Sie selbst ausprobieren können. Das Buch ist mehr als eine grundlegende Überarbeitung des Vorgängertitels »Oracle8 Datenbankentwicklung in 21 Tagen«. Gegenüber dem Vorgängertitel wurden mehrere Dutzend Merkmale der Oracle9i Datenbank neu in den Text aufgenommen. Die weite Verbreitung von Java hat sich in den Kapiteln zum
Thema Internet- Anwendungsentwicklung mit Java und JDeveloper niedergeschlagen. Auch wenn Sie noch nicht Oracle 9i, sondern eine der Vorgängerversionen ab Oracle8 einsetzen, wird Ihnen dieses Buch ein wertvoller Begleiter bei der Erweiterung Ihrer Oracle-Kenntnisse sein. Allerdings werden Sie nicht alle Beispiele nachvollziehen können. Das Konzept sieht vor, dass Sie drei Wochen lang an jedem Tag der Woche ein Kapitel durcharbeiten können. Sie sollten sich die Zeit jedoch selbst einteilen. Wenn Sie der Ansicht sind, dass Sie jeden Tag zwei oder mehr Kapitel durcharbeiten können, dann nur zu! Sollten Sie andererseits meinen, dass Sie sich einem Kapitel mehr als einen Tag widmen sollten, so dürfen Sie sich auch gerne so viel Zeit nehmen, wie Sie benötigen. Jede Woche beginnt mit einem Überblick über die Woche. Jeder Tag schließt mit einem Abschnitt, der Fragen und Antworten zu den an diesem Tag angesprochenen Themen enthält. Außerdem gibt es am Ende des Tages jeweils einen Workshop. Ein Test überprüft Ihre Kenntnisse über die am jeweiligen Tag vorgestellten Konzepte, und eine oder mehrere Übungen dienen dazu, dass Sie Ihr Wissen in die Praxis umsetzen. Wir legen Ihnen dringend nahe, diese Abschnitte zu bearbeiten, um das erworbene Wissen zu vertiefen. Folgende Aufstellung enthält die Merkmale von Oracle9i, die im Rahmen der vorliegenden Überarbeitung in den Text aufgenommen wurden: ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●
Abschaffung von CONNECT INTERNAL ANSI Join Syntax und Full Outer Join Automatic Segment Space Management Autonome Transaktionen Bind Variablen und library cache lock Bitmap Join Index BULK COLLECT in PL/SQL Business Components for Java (BC4J) CHECK OPTION bei CREATE VIEW COMMENT DBMS_REDEFINITION Paket DBMS_ROWID Paket DBMS_STATS Paket Domain Indizes (z.B. Oracle9i Text) DROP COLUMN bei ALTER TABLE DUMP Dynamische SGA (sga_max_size, db_cache_size) Enterprise Manager 2.2 Externe Tabellen
FIRST_ROWS_N SQL-Optimierung FORALL in PL/SQL INSERT ... RETURNING Index Organized Tables iAS (Internet Application Server) Java Anwendungsentwicklung JDeveloper Label Security Locally Managed Tablespaces MERGE NLS_LENGTH_SEMANTICS ORA_SERVER_ERROR_MSG ORDER BY SIBLINGS bei hierarchischem SELECT Password Management Resumable Space Allocation ROWID Datentyp SELECT ... FOR UPDATE SELECT_CATALOG_ROLE Sequence Standard-Tablespace für Temporärsegmente Statspack für die Performancediagnose (Ablösung von bstat/estat) systimestamp Funktion timestamp Datentyp Trigger auf Datenbankereignisse trim variable Blockgröße von Tablespaces Virtual Private Database v$sql_plan Zeitzonenunterstützung
In diesem Buch verwendete Konventionen Dieses Buch enthält spezielle Icons, mit denen wichtige Konzepte und Informationen herausgestrichen werden sollen.
Ein Hinweis enthält interessante Informationen zum behandelten Thema.
Ein Tipp gibt Ihnen Ratschläge oder zeigt Ihnen einfachere Wege zur Lösung eines Problems auf.
Ein Achtungszeichen weist Sie auf mögliche Probleme hin und hilft Ihnen, schwierigen Situationen aus dem Wege zu gehen.
Das Symbol »Neuer Begriff« ist den Absätzen hinzugefügt, in denen ein neuer Begriff definiert wird. Der neue Begriff ist kursiv gedruckt, so dass Sie ihn leicht erkennen können.
Dieses Symbol steht dort, wo die Syntax einer Anweisung aufgeführt wird.
Das Symbol »Analyse« kennzeichnet die Erläuterungen und den Zweck des zuvor vorgestellten Listings.
Das Symbol »CD« verweist auf Inhalte der Begleit-CD zum Buch.
Notation für die Darstellung von Syntax Ein erheblicher Teil des Buches beschäftigt sich mit der Syntax formaler Sprachen, vor allem mit SQL. Bei der Vorstellung von Syntax wird folgende Notation verwendet:
Über die Autoren David Lockman David Lockman berät Unternehmen, die Produkte aus dem Hause Oracle einsetzen. Er hat am Entwurf und der Implementierung von Anwendungssystemen für verschiedene Branchen mitgewirkt und ist Autor mehrerer Bücher zum Thema OracleDatenbankentwicklung, u.a. auch des Titels »Oracle8 Datenbankentwicklung«, der die Grundlage des vorliegenden Buches bildet. In seiner früheren Position bei Oracle war David Lockman als Leitender Consultant für eine Gruppe verantwortlich, die Migrations-, Tuning- und Entwicklungsprojekte durchführte. Norbert Debes Norbert Debes ist Diplom-Informatiker. Er arbeitet seit sechs Jahren direkt beim Hersteller intensiv mit den Datenbankprodukten von Oracle. Er ist Referent auf Kongressen zum Thema Oracle-Datenbanken und publiziert in Fachzeitschriften. Er hat sich vor allem auf die Themen Hochverfügbarkeit und Backup & Recovery spezialisiert und bündelt im Hause Oracle Aktivitäten rund um das Produkt Real Application Clusters, die hochverfügbare, parallele Variante des Oracle9i Datenbanksystems. Carsten Czarski Carsten Czarski ist als Systemberater bei Oracle Deutschland tätig. Seine Schwerpunktgebiete sind die Themen Java und Datenbankanwendungen sowie der Oracle Internet Application Server. Jürgen Menge Jürgen Menge ist promovierter Wirtschaftsinformatiker. Er arbeitet seit acht Jahren bei Oracle Deutschland und ist gegenwärtig als Systemberater tätig. Seine Schwerpunkte sind Entwicklungswerkzeuge und Konfigurations-Management.
Wir sind an Ihrer Meinung interessiert! Als Teil unseres Bestrebens, Bücher höchster Qualität zu produzieren, ist SAMS an Ihrer Meinung interessiert. Sie können Ihre Kommentare, Ideen oder Vorschläge zur Verbesserung zukünftiger Ausgaben an die unten angegebene Adresse oder per Fax an 089-46003-330 schicken. Online-Verbindung bekommen Sie über das Internet http://www.mut.de. Schon jetzt vielen Dank – Ihre Kommentare werden uns dabei helfen, weiterhin die besten Computerbücher herauszugeben. SAMS
Fragen zur HTML-Version?
Markt+Technik Erik Franz Hans-Pinsel-Str. 9b 85540 Haar bei München
Markt+Technik Harry Jörg Hans-Pinsel-Str. 9b 85540 Haar bei München