vorheriges KapitelInhaltsverzeichnisIndexInfoseitenächstes Kapitel



10


Sichten und Indizes erstellen

Ziele

Sichten verwenden

Indizes

Zusammenfassung

Fragen & Antworten

Workshop



Ziele

Beginnend mit der heutigen Lektion wenden wir uns Themen zu, die selbst für Programmierer oder Datenbankbenutzer, die schon Kontakt mit SQL hatten, neu sein dürften. Die Tage 1 bis 8 haben fast das gesamte einführende Material behandelt, das für den Einstieg in SQL und relationale Datenbanken erforderlich ist. Tag 9 war dem Entwurf von Datenbanken, der Erzeugung von Tabellen und anderen Befehlen zur Datenmanipulation gewidmet. Bei den bisher besprochenen Objekten - Datenbanken, Tabellen, Datensätzen und Feldern - handelt es sich durchgängig um physikalische Objekte, die auf der Festplatte abgelegt sind. Heute verschiebt sich der Schwerpunkt auf zwei Merkmale von SQL, mit denen man Daten in anderen Formaten ansehen oder präsentieren kann: die Sicht und den Index. In dieser Lektion lernen Sie ...


Eine Sicht (oder View) bezeichnet man auch als virtuelle Tabelle. Sichten erzeugt man mit der Anweisung CREATE VIEW. Danach kann man sich mit den SQL-Befehlen


auf die Sicht beziehen.


Ein Index ist eine weitere Form der Datendarstellung, die von der Speicherorganisation auf dem Datenträger abweicht. Spezielle Arten von Indizes ordnen die physikalische Position innerhalb einer Tabelle neu. Indizes lassen sich auf einer Spalte innerhalb einer Tabelle oder auf einer Kombination von Spalten innerhalb einer Tabelle erzeugen. Wenn man mit einem Index arbeitet, erscheinen die Daten in der Ausgabe in einer sortierten Reihenfolge, die man mit der Anweisung CREATE INDEX steuern kann. Das Indizieren geeigneter Felder führt gewöhnlich zu einer Leistungsverbesserung, was insbesondere auf Felder zutrifft, die zwischen mehreren Tabellen in Beziehung stehen.


Sichten und Indizes sind zwei grundsätzlich verschiedene Objekte, haben aber eines gemeinsam: Beide sind sie mit einer Tabelle in der Datenbank verbunden. Obwohl die Beziehungen der Objekte zu einer Tabelle eindeutig sind, erweitern sie die Tabelle und bieten damit leistungsfähige Möglichkeiten wie vorsortierte Daten und vordefinierte Abfragen.

Die heutigen Beispiele sind mit Oracle8 entstanden. Konsultieren Sie bitte die Dokumentation zu Ihrer konkreten SQL-Implementierung, da sich kleinere Abweichungen in der Syntax zeigen können.


Sichten verwenden

Mit Sichten oder virtuellen Tabellen lassen sich komplexe Abfragen verkapseln. Nachdem eine Sicht auf einer Gruppe von Daten erzeugt wurde, kann man diese Sicht wie jede andere Tabelle verwenden. Allerdings gibt es bestimmte Einschränkungen bei der Modifikation von Daten in Sichten zu beachten. Ändern sich die Daten in der Tabelle, ist eine entsprechende Änderung der Sicht festzustellen. Sichten belegen im Gegensatz zu Tabellen keinen physikalischen Platz in der Datenbank.


Die Syntax für die Anweisung CREATE VIEW lautet wie folgt:


Auf den ersten Blick mag diese Syntax vielleicht nicht ganz klar sein. Die heutigen Beispiele dürften aber ausreichend die Verwendung und die Vorteile von Sichten demonstrieren. Mit dem Befehl CREATE VIEW weist man SQL an, eine Sicht mit dem spezifizierten Namen zu erzeugen. Die Angabe der Spaltennamen ist optional. Eine SQL-SELECT-Anweisung bestimmt die Felder in diesen Spalten und deren Datentypen. Es handelt sich hier um die gleiche SELECT-Anweisung, die Sie schon mehrfach in den letzten neun Tagen verwendet haben.


Bevor Sie etwas Sinnvolles mit Sichten anfangen können, müssen Sie die Datenbank RECHNUNGEN mit ein paar mehr Daten füllen. Machen Sie sich keine Sorgen, wenn Sie sich an Ihrem neu erworbenen Wissen zum Befehl DROP DATABASE berauscht und die Datenbank gelöscht haben. Sie können sie einfach wiederherstellen. (Die Tabellen 10.1 bis 10.3 enthalten dazu Beispieldaten.)


SQL> CREATE DATABASE RECHNUNGEN


(je nach Datenbanksystem)


SQL> CREATE TABLE RECHNUNGEN (
2 NAME CHAR(30) NOT NULL,
3 BETRAG NUMBER,
4 KONTO_ID NUMBER NOT NULL);

Tabelle wurde angelegt.

SQL> CREATE TABLE BANKKONTEN (
2 KONTO_ID NUMBER NOT NULL,
3 TYP CHAR(30),
4 SALDO NUMBER,
5 BANK CHAR(30));

Tabelle wurde angelegt.

SQL> CREATE TABLE FIRMA (
2 NAME CHAR(30) NOT NULL,
3 ADRESSE CHAR(50),
4 STADT CHAR(30),
5 STAAT CHAR(2));

Tabelle wurde angelegt.


Tabelle 10.1: Beispieldaten für die Tabelle RECHNUNGEN

NAME

BETRAG

KONTO_ID

Phone Company

125

1

Power Company

75

1

Record Club

25

2

Software Company

250

1

Cable TV Company

35

3

Joe's Car Palace

350

5

S. C. Student Loan

200

6

Florida Water Company

20

1

U-O-Us Insurance Company

125

5

Debtor's Credit Card

35

4

Tabelle 10.2: Beispieldaten für die Tabelle BANKKONTEN

KONTO_ID

TYP

SALDO

BANK

1

Checking

500

First Federal

2

Money Market

1200

First Investor's

3

Checking

90

Credit Union

4

Savings

400

First Federal

5

Checking

2500

Second Mutual

6

Business

4500

Fidelity

Tabelle 10.3: Beispieldaten für die Tabelle FIRMA

NAME

ADRESSE

STADT

STAAT

Phone Company

111 1st Street

Atlanta

GA

Power Company

222 2nd Street

Jacksonville

FL

Record Club

333 3rd Avenue

Los Angeles

CA

Software Company

444 4th Drive

San Francisco

CA

Cable TV Company

555 5th Drive

Austin

TX

Joe's Car Palace

1000 Govt. Blvd

Miami

FL

S.C. Student Loan

25 College Blvd

Columbia

SC

Florida Water Company

1883 Hwy 87

Navarre

FL

U-O-Us Insurance Company

295 Beltline Hwy

Macon

GA

Debtor's Credit Card

115 2nd Avenue

Newark

NJ

Nachdem Sie diese Informationen mit den Befehlen CREATE DATABASE, CREATE TABLE und INSERT erfolgreich eingegeben haben, können Sie sich einer tiefergehenden Betrachtung der Sichten zuwenden.



Eine einfache Sicht

Beginnen wir mit der einfachsten aller Sichten. Nehmen wir an, daß Sie eine Sicht auf die Tabelle RECHNUNGEN erzeugen möchten, die identisch zur Tabelle ist, aber einen abweichenden Namen hat. (Wir nennen sie hier OFFEN für offene Rechnungen.) Die entsprechende Anweisung lautet:


SQL> CREATE VIEW OFFEN AS
2 SELECT * FROM RECHNUNGEN;

View wurde angelegt.


Um sich von der ordnungsgemäß ausgeführten Operation zu überzeugen, können Sie die Sicht genau wie eine Tabelle behandeln:


SQL> SELECT * FROM OFFEN;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 125 1
Power Company 75 1
Record Club 25 2
Software Company 250 1
Cable TV Company 35 3
Joe's Car Palace 350 5
S.C. Student Loan 200 6
Florida Water Company 20 1
U-O-Us Insurance Company 125 5
Debtor's Credit Card 35 4

10 Zeilen ausgewählt.


Es lassen sich sogar neue Sichten aus vorhandenen Sichten erstellen. Dieses Verfahren ist durchaus zulässig, verkompliziert aber die Wartung. Nehmen wir eine Sicht an, die drei Ebenen unterhalb einer Tabelle liegt, also eine Sicht einer Sicht einer Sicht einer Tabelle. Was passiert wohl, wenn Sie die erste Sicht auf die Tabelle löschen? Die beiden anderen Sichten existieren weiterhin, sind aber nutzlos, da sie einen Teil ihrer Informationen aus der ersten Sicht erhalten. Denken Sie daran, daß eine erstellte Sicht als virtuelle Tabelle fungiert.


SQL> CREATE VIEW KREDITKARTEN_OFFEN AS
2 SELECT * FROM OFFEN
3 WHERE KONTO_ID = 4;

View wurde angelegt.

SQL> SELECT * FROM KREDITKARTEN_OFFEN;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Debtor's Credit Card 35 4

1 Zeile wurde ausgewählt.


Mit CREATE VIEW kann man auch einzelne Spalten aus einer Tabelle selektieren und sie in eine Sicht übernehmen. Das folgende Beispiel selektiert die Felder NAME und STAAT aus der Tabelle FIRMA.


SQL> CREATE VIEW FIRMENINFO (NAME, STAAT) AS
2 SELECT NAME, STAAT FROM FIRMA;

View wurde angelegt.

SQL> SELECT * FROM FIRMENINFO;

NAME ST
------------------------------ --
Phone Company GA
Power Company FL
Record Club CA
Software Company CA
Cable TV Company TX
Joe's Car Palace FL
S.C. Student Loan SC
Florida Water Company FL
U-O-Os Insurance Company GA
Debtor's Credit Card NJ

10 Zeilen ausgewählt.


Für das Abrufen spezieller Daten kann der Benutzer eine Sicht erzeugen. Nehmen wir zum Beispiel eine Tabelle mit 50 Spalten und mehreren tausend Zeilen, aus der Sie lediglich 2 Spalten interessieren. In diesem Fall bietet sich eine Sicht auf die gewünschten zwei Spalten an. Bei Abfragen aus dieser Sicht sind erheblich kürzere Reaktionszeiten bis zur Rückgabe der Ergebnisse zu verzeichnen.


Spalten umbenennen

Sichten vereinfachen die Darstellung der Daten. Neben der Benennung der Sicht erlaubt die SQL-Syntax für die Anweisung CREATE VIEW auch, die ausgewählten Spalten umzubenennen. Sehen Sie sich das vorherige Beispiel etwas näher an. Was wäre, wenn Sie die Felder ADRESSE, STADT und STAAT aus der Tabelle FIRMA zusammen auf einem Briefumschlag drucken möchten? Das nächste Beispiel zeigt eine entsprechende Anweisung. Hier wird mit dem Operator || und der Funktion RTRIM eine lange Adresse aus den Zeichendaten der Adreßfelder in Verbindung mit Kommas und Leerzeichen zusammengebaut.


SQL> CREATE VIEW UMSCHLAG (FIRMA, POSTANSCHRIFT) AS
2 SELECT NAME, RTRIM(ADRESSE) || ' ' || RTRIM(STADT) || ', ' || RTRIM(STAAT)
3 FROM FIRMA;

View wurde angelegt.

SQL> SELECT * FROM UMSCHLAG;

FIRMA POSTANSCHRIFT
------------------------------ ----------------------------------------
Phone Company 111 1st Street Atlanta, GA
Power Company 222 2nd Street Jacksonville, FL
Record Club 333 3rd Avenue Los Angeles, CA
Software Company 444 4th Drive San Francisco, CA
Cable TV Company 555 5th Drive Austin, TX
Joe's Car Palace 1000 Govt. Blvd Miami, FL
S.C. Student Loan 25 College Blvd Columbia, SC
Florida Water Company 1883 Hwy 87 Navarre, FL
U-O-Os Insurance Company 295 Beltline Hwy Macon, GA
Debtor's Credit Card 115 2nd Avenue Newark, NJ

10 Zeilen ausgewählt.


Die SQL-Syntax erfordert es, daß man einen virtuellen Feldnamen bereitstellt, wenn das virtuelle Feld der Sicht mittels einer Berechnung oder SQL-Funktion erstellt wird. Diese Forderung ist berechtigt, da man in der Sicht kaum einen Spaltennamen wie COUNT(*) oder AVG(ZAHLUNG) haben möchte.

Informieren Sie sich in der Dokumentation zu Ihrer Datenbank über die Verwendung des Operators ||.


Verarbeitung von Sichten in SQL

Mit Sichten lassen sich Tabellendaten komfortabler als in der tatsächlichen Tabellenstruktur darstellen. Sichten bieten auch Vorteile, wenn man mehrere komplizierte Abfragen nacheinander ausführen muß (beispielsweise in einer gespeicherten Prozedur oder einem Anwendungsprogramm). Um Ihre Kenntnisse der Sicht und der SELECT-Anweisung zu vertiefen, untersucht der nächste Abschnitt die Art und Weise, in der SQL eine Abfrage gegen eine Sicht ausführt. Nehmen wir zum Beispiel eine Abfrage an, bei der routinemäßig die Tabelle RECHNUNGEN mit der Tabelle BANKKONTEN zu verknüpfen ist, um Informationen über Ihre Zahlungen abzurufen.


SQL> SELECT RECHNUNGEN.NAME, RECHNUNGEN.BETRAG, BANKKONTEN.SALDO,
2 BANKKONTEN.BANK FROM RECHNUNGEN, BANKKONTEN
3 WHERE RECHNUNGEN.KONTO_ID = BANKKONTEN.KONTO_ID;


NAME BETRAG SALDO BANK
------------------------------ --------- --------- ------------------------------
Phone Company 125 500 First Federal
Power Company 75 500 First Federal
Florida Water Company 20 500 First Federal
Software Company 250 500 First Federal
Record Club 25 1200 First Investor's
Cable TV Company 35 90 Credit Union
Debtor's Credit Card 35 400 First Federal
Joe's Car Palace 350 2500 Second Mutual
U-O-Us Insurance Company 125 2500 Second Mutual
S.C. Student Loan 200 4500 Fidelity

10 Zeilen ausgewählt.


Diese Verarbeitungsschritte kann man mit der folgenden Anweisung in eine Sicht umwandeln:


SQL> CREATE VIEW RECH_OFFEN (NAME, BETRAG, KTO_STAND, BANK) AS
2 SELECT RECHNUNGEN.NAME, RECHNUNGEN.BETRAG, BANKKONTEN.SALDO,
3 BANKKONTEN.BANK FROM RECHNUNGEN, BANKKONTEN
4 WHERE RECHNUNGEN.KONTO_ID = BANKKONTEN.KONTO_ID;

View wurde angelegt.


Die Abfrage der Sicht RECH_OFFEN nach einem bestimmten Kriterium läßt sich etwa folgendermaßen formulieren:


SQL> SELECT * FROM RECH_OFFEN
2 WHERE KTO_STAND > 500;

NAME BETRAG KTO_STAND BANK
------------------------------ --------- --------- ------------------------------
Record Club 25 1200 First Investor's
Joe's Car Palace 350 2500 Second Mutual
U-O-Us Insurance Company 125 2500 Second Mutual
S.C. Student Loan 200 4500 Fidelity

4 Zeilen ausgewählt.


SQL arbeitet die obige Anweisung in mehreren Schritten ab. Da RECH_OFFEN eine Sicht und keine eigentliche Tabelle ist, sucht SQL zuerst nach einer Tabelle namens RECH_OFFEN - und findet nichts. Der SQL-Prozessor wird wahrscheinlich (je nach verwendetem Datenbanksystem) aus einer Systemtabelle entnehmen, daß es sich bei RECH_OFFEN um eine Sicht handelt. Dann baut der Prozessor nach dem Plan der Sicht die folgende Abfrage auf:

SQL> SELECT RECHNUNGEN.NAME, RECHNUNGEN.BETRAG, BANKKONTEN.SALDO,
2 BANKKONTEN.BANK FROM RECHNUNGEN, BANKKONTEN
3 WHERE RECHNUNGEN.KONTO_ID = BANKKONTEN.KONTO_ID
4 AND BANKKONTEN.SALDO > 500;


Beispiel 10.1

Konstruieren Sie eine Sicht, die alle Staaten zeigt, an die Rechnungen geschickt wurden. Zeigen Sie auch den Gesamtbetrag und die Gesamtzahl der an jeden Staat versandten Rechnungen an.


Zunächst einmal wissen wir, daß die CREATE VIEW-Komponente der Anweisung wie folgt aufgebaut ist:


CREATE VIEW BEISPIEL (STAAT, RECH_GESAMT, BETRAG_GESAMT) AS...


Jetzt ist zu bestimmen, wie die SELECT-Abfrage aussieht. Wir wissen, daß das Feld STAAT zuerst mit der Syntax SELECT DISTINCT auszuwählen ist, um die Staaten anzuzeigen, an die Rechnungen geschickt wurden. Beispielsweise:


SQL> SELECT DISTINCT STAAT FROM FIRMA;


ST
--
CA
FL
GA
NJ
SC
TX

6 Zeilen ausgewählt.


Neben der Auswahl des Feldes STAAT müssen wir die Gesamtzahl der an diesen Staat geschickten Zahlungen summieren. Demzufolge ist die Tabelle RECHNUNGEN mit der Tabelle FIRMA zu verknüpfen.


SQL> SELECT DISTINCT FIRMA.STAAT, COUNT(RECHNUNGEN.NAME) FROM RECHNUNGEN, FIRMA
2 WHERE RECHNUNGEN.NAME=FIRMA.NAME
3 GROUP BY FIRMA.STAAT;

ST COUNT(RECHNUNGEN.NAME)
-- ----------------------
CA 2
FL 3
GA 2
NJ 1
SC 1
TX 1

6 Zeilen ausgewählt.


Nachdem wir nun zwei Drittel des gewünschten Ergebnisses erhalten haben, können wir den letzten erforderlichen Rückgabewert hinzufügen. Mit Hilfe der SUM-Funktion bilden wir den Gesamtbetrag, der an jeden Staat gegangen ist.


SQL> SELECT DISTINCT FIRMA.STAAT, COUNT(RECHNUNGEN.NAME), SUM(RECHNUNGEN.BETRAG)
2 FROM RECHNUNGEN, FIRMA
3 WHERE RECHNUNGEN.NAME = FIRMA.NAME
4 GROUP BY FIRMA.STAAT;

ST COUNT(RECHNUNGEN.NAME) SUM(RECHNUNGEN.BETRAG)
-- ---------------------- ----------------------
CA 2 275
FL 3 445
GA 2 250
NJ 1 35
SC 1 200
TX 1 35

6 Zeilen ausgewählt.


Als letzten Schritt kombinieren wir diese SELECT-Anweisung mit der Anweisung CREATE VIEW, die wir zu Beginn dieses Projekts erstellt haben:


SQL> CREATE VIEW BEISPIEL (STAAT, RECH_GESAMT, BETRAG_GESAMT) AS
2 SELECT DISTINCT FIRMA.STAAT, COUNT(RECHNUNGEN.NAME), SUM(RECHNUNGEN.BETRAG)
3 FROM RECHNUNGEN, FIRMA
4 WHERE RECHNUNGEN.NAME = FIRMA.NAME
5 GROUP BY FIRMA.STAAT;

View wurde angelegt.


SQL> SELECT * FROM BEISPIEL;

ST RECH_GESAMT BETRAG_GESAMT
-- ----------- -------------
CA 2 275
FL 3 445
GA 2 250
NJ 1 35
SC 1 200
TX 1 35

6 Zeilen ausgewählt.


Das obige Beispiel zeigt, wie man die Anweisungen CREATE VIEW und SELECT plant. Der Code testet zunächst die SELECT-Anweisungen, um festzustellen, ob sie die passenden Ergebnisse generieren, und kombiniert dann die Anweisungen, um die Sicht zu erzeugen.

Beispiel 10.2

Nehmen wir an, daß Ihre Gläubiger einen Aufschlag von 10 Prozent auf alle verspäteten Zahlungen berechnen, und daß Sie diesen Monat leider mit allem in Verzug sind. Nun möchten Sie sich über diese Zuschläge und über die Art der Forderungen informieren.


Die Verknüpfung ist unkompliziert. (Man muß nicht einmal die Funktionen COUNT oder SUM bemühen.) Allerdings werden Sie die wesentlichen Vorteile von Sichten erkennen. Man kann 10 Prozent Mahngebühren addieren und das Ergebnis als Feld innerhalb der Sicht darstellen. Von nun an lassen sich Datensätze aus der Sicht auswählen, wobei der Gesamtbetrag bereits berechnet ist. Die entsprechende Anweisung sieht etwa wie folgt aus:


SQL> CREATE VIEW ZAHL_VERZUG (NAME, GESAMT_NEU, KONTOTYP) AS
2 SELECT RECHNUNGEN.NAME, RECHNUNGEN.BETRAG * 1.10, BANKKONTEN.TYP
3 FROM RECHNUNGEN, BANKKONTEN
4 WHERE RECHNUNGEN.KONTO_ID = BANKKONTEN.KONTO_ID;


View wurde angelegt.


SQL> SELECT * FROM ZAHL_VERZUG;

NAME GESAMT_NEU KONTOTYP
------------------------------ ---------- ------------------------------
Phone Company 137,5 Checking
Power Company 82,5 Checking
Florida Water Company 22 Checking
Software Company 275 Checking
Record Club 27,5 Money Market
Cable TV Company 38,5 Checking
Debtor's Credit Card 38,5 Savings
Joe's Car Palace 385 Checking
U-O-Us Insurance Company 137,5 Checking
S.C. Student Loan 220 Business

10 Zeilen ausgewählt.



Einschränkungen bei SELECT

Wenn man bei der Formulierung einer Sicht die SELECT-Anweisung verwendet, sind die folgenden zwei Regeln zu beachten:



Daten in einer Sicht modifizieren

Wie Sie mittlerweile wissen, kann man eine virtuelle Tabelle für ein SQL-Skript oder eine Datenbankanwendung erzeugen, indem man eine Sicht auf eine oder mehrere physikalische Tabellen innerhalb einer Datenbank erstellt. Nachdem die Sicht mit der Anweisung CREATE VIEW...SELECT erstellt wurde, kann man die Daten der Sicht mit den in Lektion 8 besprochenen Befehlen UPDATE, INSERT und DELETE aktualisieren, einfügen und löschen.


Auf die Einschränkungen hinsichtlich der Datenmodifikation in Sichten gehen wir später genauer ein. Die nächste Gruppe von Beispielen zeigt, wie man Daten in einer Sicht manipuliert.


Um die Arbeit von Beispiel 10.2 fortzusetzen, aktualisieren wir die Tabelle RECHNUNGEN, damit sie die leidigen Verzugsgebühren von 10 Prozent widerspiegelt.


SQL> CREATE VIEW ZAHL_VERZUG AS
2 SELECT * FROM RECHNUNGEN;

View wurde angelegt.

SQL> UPDATE ZAHL_VERZUG
2 SET BETRAG = BETRAG * 1.10;

10 Zeilen wurden aktualisiert.

SQL> SELECT * FROM ZAHL_VERZUG;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 137,5 1
Power Company 82,5 1
Record Club 27,5 2
Software Company 275 1
Cable TV Company 38,5 3
Joe's Car Palace 385 5
S.C. Student Loan 220 6
Florida Water Company 22 1
U-O-Us Insurance Company 137,5 5
Debtor's Credit Card 38,5 4

10 Zeilen ausgewählt.


Um zu überprüfen, ob die UPDATE-Anweisung tatsächlich die zugrundeliegende Tabelle RECHNUNGEN aktualisiert hat, fragen wir die Tabelle RECHNUNGEN ab:


SQL> SELECT * FROM RECHNUNGEN;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 137,5 1
Power Company 82,5 1
Record Club 27,5 2
Software Company 275 1
Cable TV Company 38,5 3
Joe's Car Palace 385 5
S.C. Student Loan 220 6
Florida Water Company 22 1
U-O-Us Insurance Company 137,5 5
Debtor's Credit Card 38,5 4

10 Zeilen ausgewählt.


Nun löschen wir eine Zeile aus der Sicht:


SQL> DELETE FROM ZAHL_VERZUG
2 WHERE KONTO_ID = 4;

1 Zeile wurde gelöscht.

SQL> SELECT * FROM ZAHL_VERZUG;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 137,5 1
Power Company 82,5 1
Record Club 27,5 2
Software Company 275 1
Cable TV Company 38,5 3
Joe's Car Palace 385 5
S.C. Student Loan 220 6
Florida Water Company 22 1
U-O-Us Insurance Company 137,5 5

9 Zeilen ausgewählt.


Als abschließenden Schritt testen wir die UPDATE-Funktion. Bei allen Rechnungen, die einen BETRAG größer als 100 aufweisen, addieren wir zusätzlich 10.


SQL> UPDATE ZAHL_VERZUG
2 SET BETRAG = BETRAG + 10
3 WHERE BETRAG > 100;

5 Zeilen wurden aktualisiert.

SQL> SELECT * FROM ZAHL_VERZUG;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 147,5 1
Power Company 82,5 1
Record Club 27,5 2
Software Company 285 1
Cable TV Company 38,5 3
Joe's Car Palace 395 5
S.C. Student Loan 230 6
Florida Water Company 22 1
U-O-Us Insurance Company 147,5 5

9 Zeilen ausgewählt.



Probleme bei der Änderung von Daten in Sichten

Da eine Sicht eventuell nur einen Ausschnitt aus einer Gruppe von Tabellen widerspiegelt, ist das Modifizieren der Daten in den zugrundeliegenden Tabellen nicht immer so unkompliziert wie in den obigen Beispielen. Die folgende Liste führt einige der häufigsten Einschränkungen auf, die man bei der Arbeit mit Sichten berücksichtigen muß:



Gebräuchliche Einsatzfälle von Sichten

Mit Sichten lassen sich unter anderem folgende Aufgaben realisieren:



Sichten und Sicherheit

Auch wenn Tag 12 eine umfassende Behandlung der Datenbanksicherheit bringt, streifen wir hier kurz dieses Thema und erläutern, wie man mit Sichten Sicherheitsfunktionen realisieren kann.


Alle heutzutage verwendeten relationalen Datenbanksysteme enthalten zahlreiche eingebaute Sicherheitsmechanismen. Die Benutzer des Datenbanksystems lassen sich im allgemeinen auf der Basis ihrer Datenbanknutzung in Gruppen einteilen. Übliche Gruppentypen sind Datenbank-Administratoren, Datenbankentwickler, Personal zur Dateneingabe und öffentliche Benutzer. Diese Benutzergruppen verfügen über verschiedene Privilegstufen. Datenbank-Administratoren obliegt die komplette Steuerung des Systems, so daß sie über die Datenbankprivilegien UPDATE, INSERT, DELETE und ALTER verfügen. Der öffentlichen Gruppe wird man nur SELECT-Privilegien gewähren - und möglicherweise diese Privilegien auf bestimmte Tabellen innerhalb bestimmter Datenbanken einschränken.


In diesem Szenario läßt sich mit Sichten die Art der Informationen festlegen, auf die der Datenbankbenutzer Zugriff hat. Will man zum Beispiel den Benutzern lediglich das Feld NAME der Tabelle RECHNUNGEN freigeben, erzeugt man einfach eine Sicht namens RECH_NAME:


SQL> CREATE VIEW RECH_NAME AS
2 SELECT NAME FROM RECHNUNGEN;

View wurde angelegt.


Ein Benutzer mit Privilegien auf der Ebene eines Systemadministrators könnte der öffentlichen Gruppe die SELECT-Privilegien für die Sicht RECH_NAME gewähren. Diese Gruppe hätte keine Zugriffsrechte auf die zugrundeliegende Tabelle RECHNUNGEN. Wie sich unschwer erraten läßt, gibt es in SQL auch Anweisungen für die Datensicherheit. Denken Sie daran, daß Sichten für die Implementierung von Datenbanksicherheit sehr nützlich sind.



Einheiten mit Sichten umrechnen

Sichten lassen sich auch einsetzen, um dem Benutzer Daten zu präsentieren, die sich von den eigentlichen Daten in der Datenbank unterscheiden. Ist zum Beispiel das Feld BETRAG in US-Dollar gespeichert, und sollen sich die kanadischen Benutzer nicht laufend mit der Umrechnung der Gesamtsumme von BETRAG in kanadische Dollar herumschlagen müssen, kann man eine einfache Sicht namens RECH_KANADA erzeugen:


SQL> CREATE VIEW RECH_KANADA (NAME, BETRAG_KANADA) AS
2 SELECT NAME, BETRAG / 1.10
3 FROM RECHNUNGEN;

View wurde angelegt.
NAME BETRAG_KANADA
------------------------------ -------------
Phone Company 125
Power Company 75
Record Club 25
Software Company 250
Cable TV Company 35
Joe's Car Palace 350
S.C. Student Loan 200
Florida Water Company 20
U-O-Us Insurance Company 125
Debtor's Credit Card 35

10 Zeilen ausgewählt.


Bei derartigen Umrechnungen sollte man an mögliche Probleme denken, die sich aus der Änderung von Daten in der zugrundeliegenden Tabelle ergeben, wenn man eine Berechnung (wie etwa im vorherigen Beispiel) zum Erzeugen von Spalten in der Sicht verwendet. Wie immer sollte man in der Dokumentation des Datenbanksystems nachsehen, wie das System den Befehl CREATE VIEW implementiert.


Mit Sichten komplizierte Abfragen vereinfachen

Auch in den Fällen, wo das endgültige Ergebnis aus einer Folge von Abfragen hervorgeht, erweisen sich Sichten als nützlich. Das folgende Beispiel zeigt eine derartige Situation.


Um den Namen aller Banken auszugeben, die Rechnungen an den Staat Texas mit einem Betrag kleiner als $50 geschickt haben, teilt man die Problemstellung in zwei Teilprobleme auf:


Diese Aufgabe wollen wir mit zwei separaten Sichten lösen: RECH1 und RECH2:


SQL> CREATE VIEW RECH1 AS
2 SELECT * FROM RECHNUNGEN
3 WHERE BETRAG < 50;
View wurde angelegt.

SQL> CREATE VIEW RECH2 (NAME, BETRAG, KONTO_ID) AS
2 SELECT RECHNUNGEN.* FROM RECHNUNGEN, FIRMA
3 WHERE RECHNUNGEN.NAME = FIRMA.NAME AND FIRMA.STAAT = 'TX';

View wurde angelegt.


Da man alle Rechnungen auflisten möchte, die nach Texas gegangen sind und die kleiner als $50 waren, kann man nun mit der IN-Klausel von SQL ermitteln, welche Rechnungen in RECH1 nach Texas geschickt wurden. Mit diesen Informationen erzeugt man eine neue Sicht namens RECH3:

SQL> CREATE VIEW RECH3 AS
2 SELECT * FROM RECH2 WHERE NAME IN
3 (SELECT RECH1.NAME FROM RECH1);

View wurde angelegt.


Nun fassen wir die obige Abfrage mit der Tabelle BANKKONTEN zusammen, um den eingangs gestellten Anforderungen dieses Beispiels gerecht zu werden:


SQL> CREATE VIEW BANKEN_IN_TX (BANK) AS
2 SELECT BANKKONTEN.BANK
3 FROM BANKKONTEN, RECH3
4 WHERE RECH3.KONTO_ID = BANKKONTEN.KONTO_ID;

View wurde angelegt.

SQL> SELECT * FROM BANKEN_IN_TX;

BANK
------------------------------
Credit Union

1 Zeile wurde ausgewählt.


Wie man sieht, ist die abschließende Abfrage ziemlich einfach, nachdem man die Abfragen in zwei separate Sichten zerlegt hat. Außerdem kann man die einzelnen Sichten so oft wie nötig wiederverwenden.


Die Anweisung DROP VIEW

Wie bei jedem anderen SQL-Befehl der Art CREATE... hat auch CREATE VIEW einen korrespondierenden DROP...-Befehl. Die Syntax lautet:


Beim Einsatz des Befehls DROP VIEW muß man lediglich berücksichtigen, daß alle anderen Sichten, die sich auf die gelöschte Sicht beziehen, nunmehr ungültig sind. Manche Datenbanksysteme löschen alle Sichten, die sich auf die gerade gelöschte Sicht beziehen. Wenn Sie mit Personal Oracle8 die Sicht RECH1 löschen, liefert die abschließende Abfrage den folgenden Fehler:


SQL> DROP VIEW RECH1;

View wurde gelöscht.

SQL> SELECT * FROM BANKEN_IN_TX;
SELECT * FROM BANKEN_IN_TX
*
FEHLER in Zeile 1:
ORA-04063: view "PERKINS.BANKEN_IN_TX" hat Fehler


Eine Sicht kann man löschen, ohne daß die eigentlichen Tabellen modifiziert werden. Das erklärt, warum wir Sichten auch als virtuelle Tabellen bezeichnen. (Die gleiche Logik läßt sich auf die Technologie der virtuellen Realität anwenden.)


Indizes

Mit einem Index bietet sich eine weitere Möglichkeit, die Daten in einem von der physikalischen Speicherung auf der Festplatte abweichenden Format darzustellen. Darüber hinaus lassen sich die auf dem Datenträger vorliegenden Daten mit einem Index neu anordnen (was Sichten nicht leisten können).


In einer SQL-Datenbank verwendet man Indizes, um ...



Was sind Indizes?

Daten lassen sich aus einer Datenbank nach zwei Verfahren abrufen. Bei der ersten Methode, die man auch als sequentielle Zugriffsmethode (SAM - Sequential Access Method) bezeichnet, geht SQL jeden Datensatz durch und sucht nach festgelegten Übereinstimmungen. Dieses Suchverfahren ist nicht effizient, aber die einzige Möglichkeit, damit SQL den richtigen Datensatz lokalisieren kann. Denken Sie an die Tage zurück, als in Bibliotheken riesige Kataloge mit Karteikarten standen. Nehmen wir an, daß der Bibliothekar die Karten mit dem alphabetischen Index herausgenommen, die Karten durch die Luft gewirbelt und sie dann zurück in den Karteischrank gestellt hat. Wenn Sie nun den Standort eines Buches im Regal suchen, müssen Sie am Anfang beginnen und jede Karte nacheinander durchgehen, bis Sie die gewünschten Informationen gefunden haben. (Gegebenenfalls können Sie die Suche beenden, sobald Sie irgendein Buch zu diesem Thema entdecken.)


Nehmen wir nun an, daß der Bibliothekar die Buchtitel alphabetisch geordnet hat. Auf diese Buchinformationen kann man nun sehr schnell zugreifen, da eine gezielte Suche über das bekannte Alphabet möglich ist.


Stellen Sie sich nun die flexiblen Suchmöglichkeiten vor, wenn der Bibliothekar die Bücher nicht nur nach dem Titel, sondern in weiteren Katalogen nach Autorennamen und nach Themen sortiert hat. Damit steht Ihnen als Bibliotheksbenutzer ein komfortables Abfragesystem zur Verfügung. Außerdem lassen sich die Informationen in einem Bruchteil der ursprünglich erforderlichen Zeit auffinden.


Wenn man eine Datenbank mit Indizes ausstattet, kann SQL nach der direkten Zugriffsmethode arbeiten. SQL bedient sich einer baumartigen Struktur, um die Indexdaten zu speichern und abzurufen. Am Beginn des Baumes werden Zeiger auf eine Gruppe von Daten gespeichert. Diese Gruppen bezeichnet man als Knoten. Jeder Knoten enthält Zeiger auf andere Knoten. Die nach links zeigenden Knoten enthalten Werte, die kleiner als ihr Elternknoten sind. Die nach rechts weisenden Knoten zeigen auf Werte, die größer als der Elternknoten sind.


Das Datenbanksystem beginnt die Suche beim obersten Knoten und folgt einfach den Zeigern, bis sich ein Erfolg einstellt.


Das Ergebnis einer Abfrage gegen eine nichtindizierte Tabelle bezeichnet man auch als vollständige Suche. Dabei durchsucht der Datenbank-Server alle Zeilen einer Tabelle, bis alle Zeilen mit der/den angegebenen Bedingung(en) zurückgegeben wurden. Dieses Vorgehen ist vergleichbar mit der Suche nach einem Buch in der Bibliothek, wenn man mit dem ersten Buch im ersten Regal beginnt und sich jedes Buch ansieht, bis man das gewünschte entdeckt hat. Um das Buch jedoch schnell zu finden, sieht man zunächst in den (vom Computer erstellten) alphabetischen Titelkatalog. Analog dazu erlaubt ein Index dem Datenbank-Server den schnellen Zugriff auf bestimmte Datenzeilen innerhalb einer Tabelle.

Zum Glück braucht man die eigentliche Baumstruktur nicht selbst einzurichten, wie man auch nicht das Schreiben und Lesen in Tabellen oder Datenbanken realisieren muß.


Einen Index legt man gemäß der folgenden SQL-Syntax an:


SQL> CREATE INDEX Indexname
2 ON Tabellenname(Spaltenname1, [Spaltenname2],...);

Index wurde angelegt.


Wie Sie schon mehrfach bei anderen Anweisungen gesehen haben, kann sich auch bei CREATE INDEX die Syntax zwischen den verschiedenen Datenbanksystemen unterscheiden. Für Oracle8 sieht sie zum Beispiel folgendermaßen aus:


Die Syntax für CREATE INDEX mit SQL Server von Sybase stellt sich wie folgt dar:


Informix SQL implementiert diesen Befehl wie folgt:


Beachten Sie, daß diese Implementierungen mehrere Dinge gemeinsam haben. Die grundlegende Anweisung lautet:


CREATE INDEX Indexname ON Tabellenname (Spaltenname,...)


In SQL Server und Oracle läßt sich ein gruppenbildender (clustered) Index erstellen, auf den wir später eingehen. Oracle und Informix gestatten die Kennzeichnung, ob der Spaltenname in aufsteigender (ASC, englisch ascending) oder absteigender (DESC, englisch descending) Reihenfolge sortiert werden soll. Auch wenn Sie es kaum noch hören können: Sie sollten unbedingt die Dokumentation zu Ihrem Datenbank-Managementsystem konsultieren, bevor Sie mit dem Befehl CREATE INDEX arbeiten.


Mit der folgenden CREATE INDEX-Anweisung erstellen Sie zum Beispiel einen Index auf dem Feld KONTO_ID der Tabelle RECHNUNGEN:


SQL> SELECT * FROM RECHNUNGEN;


NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 125 1
Power Company 75 1
Record Club 25 2
Software Company 250 1
Cable TV Company 35 3
Joe's Car Palace 350 5
S.C. Student Loan 200 6
Florida Water Company 20 1
U-O-Us Insurance Company 125 5
Debtor's Credit Card 35 4

10 Zeilen ausgewählt.


SQL> CREATE INDEX ID_INDEX ON RECHNUNGEN( KONTO_ID );

Index wurde angelegt.

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 125 1
Power Company 75 1
Software Company 250 1
Florida Water Company 20 1
Record Club 25 2
Cable TV Company 35 3
Debtor's Credit Card 35 4
Joe's Car Palace 350 5
U-O-Us Insurance Company 125 5
S.C. Student Loan 200 6

10 Zeilen ausgewählt.


Wenn man den Index löscht, passiert folgendes:


SQL> DROP INDEX ID_INDEX;

Index wurde gelöscht.

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 125 1
Power Company 75 1
Record Club 25 2
Software Company 250 1
Cable TV Company 35 3
Joe's Car Palace 350 5
S.C. Student Loan 200 6
Florida Water Company 20 1
U-O-Us Insurance Company 125 5
Debtor's Credit Card 35 4

10 Zeilen ausgewählt.


Die Tabelle RECHNUNGEN erscheint nun wieder in der ursprünglichen Form. Mit der einfachsten Form der Anweisung CREATE INDEX ändert sich an der physikalischen Speicherung der Tabelle nichts.

Vielleicht fragen Sie sich, warum Datenbanksysteme überhaupt Indizes bereitstellen, wenn man auch mit der Klausel ORDER BY arbeiten könnte.


SQL> SELECT * FROM RECHNUNGEN ORDER BY KONTO_ID;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Phone Company 125 1
Power Company 75 1
Florida Water Company 20 1
Software Company 250 1
Record Club 25 2
Cable TV Company 35 3
Debtor's Credit Card 35 4
Joe's Car Palace 350 5
U-O-Us Insurance Company 125 5
S.C. Student Loan 200 6

10 Zeilen ausgewählt.


Diese SELECT-Anweisung und der Index ID_INDEX auf der Tabelle RECHNUNGEN führen zum gleichen Ergebnis. Der Unterschied besteht darin, daß eine ORDER BY-Klausel die Daten bei jedem Aufruf der entsprechenden SQL-Anweisung neu sortiert und anordnet. Bei einem Index erstellt das Datenbanksystem ein physikalisches Indexobjekt (gemäß der weiter oben erläuterten Baumstruktur) und verwendet für jede Abfrage der Tabelle denselben Index.

Wenn man eine Tabelle löscht, werden ebenso alle der Tabelle zugeordneten Indizes gelöscht.


Tips zu Indizes

Beim Einsatz von Indizes sollte man die folgenden Punkte berücksichtigen:



Mehrere Felder indizieren

Mit SQL lassen sich auch mehrere Felder indizieren. Derartige Indizes bezeichnet man als zusammengesetzte Indizes. Der folgende Code zeigt einen einfachen zusammengesetzten Index. Beachten Sie, daß trotz der Kombination von zwei Feldern nur ein physikalischer Index (namens ID_ZUS_INDEX) erzeugt wird.


SQL> CREATE INDEX ID_ZUS_INDEX ON RECHNUNGEN (KONTO_ID, BETRAG);

Index wurde angelegt.

SQL> SELECT * FROM RECHNUNGEN;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Florida Water Company 20 1
Power Company 75 1
Phone Company 125 1
Software Company 250 1
Record Club 25 2
Cable TV Company 35 3
Debtor's Credit Card 35 4
U-O-Us Insurance Company 125 5
Joe's Car Palace 350 5
S.C. Student Loan 200 6

10 Zeilen ausgewählt.

SQL> DROP INDEX ID_ZUS_INDEX;
Index wurde gelöscht.


Es lassen sich Leistungsgewinne erreichen, indem man die Spalte mit den meisten eindeutigen Werten selektiert. Beispielsweise ist jeder Wert im Feld NAME der Tabelle RECHNUNGEN nur einmal vorhanden. In einem zusammengesetzten Index gibt man das »selektivste« Feld zuerst in der Spaltenliste an. Das heißt, man setzt das Feld, für das man die häufigsten Auswahloperationen erwartet, an den Beginn der Liste. (Die Reihenfolge, in der die Spaltennamen in der CREATE INDEX-Anweisung erscheinen, muß nicht mit der Reihenfolge in der Tabelle übereinstimmen.) Nehmen wir an, daß Sie routinemäßig eine Anweisung wie die folgende verwenden:

SQL> SELECT * FROM RECHNUNGEN WHERE NAME = 'Cable TV Company';


Um Leistungsgewinne zu erzielen, erzeugt man einen Index mit dem Feld NAME als führende Spalte. Dazu zwei Beispiele:


SQL> CREATE INDEX NAME_INDEX ON RECHNUNGEN(NAME, BETRAG);


oder


SQL> CREATE INDEX NAME_INDEX ON RECHNUNGEN(NAME);


Das Feld NAME bildet die äußerst linke Spalte in beiden Indizes, so daß die obige Abfrage für eine Suche auf dem Feld NAME optimiert wird.


Mit zusammengesetzten Indizes kombiniert man auch mehrere Spalten, die selbst nur eine geringe »Selektivität« aufweisen. Als Beispiel für die Selektivität untersuchen wir die Tabelle BANKKONTEN:


KONTO_ID TYP SALDO BANK
--------- ------------------------------ --------- ------------------------
1 Checking 500 First Federal
2 Money Market 1200 First Investor's
3 Checking 90 Credit Union
4 Savings 400 First Federal
5 Checking 2500 Second Mutual
6 Business 4500 Fidelity


Wir stellen fest, daß drei der sechs Datensätze den Wert Checking enthalten. Diese Spalte hat eine geringere Selektivität als das Feld KONTO_ID. Beachten Sie weiterhin, daß jeder Wert des Feldes KONTO_ID eindeutig ist. Um die Selektivität des Index zu verbessern, könnte man die Felder TYP und KONTO_ID in einem neuen Index zusammenfassen. Dieser Schritt erzeugt einen eindeutigen Indexwert (der natürlich die höchste erreichbare Selektivität aufweist).


Einen Index mit mehreren Spalten bezeichnet man auch als zusammengesetzten Index. Die Entscheidung für einspaltigen oder zusammengesetzten Index macht man von Leistungsbetrachtungen abhängig. In Oracle werden Sie sich wahrscheinlich für einen einspaltigen Index entscheiden, wenn die meisten Abfragen nur eine bestimmte Spalte als Teil einer Bedingung einschließen. Andererseits wird man wahrscheinlich einen zusammengesetzten Index erstellen, wenn die Spalten in diesem Index häufig zusammen als Bedingungen für eine Abfrage zum Einsatz kommen. Prüfen Sie die konkrete Implementierung in der Dokumentation, wenn Sie mehrspaltige Indizes erstellen.


Das Schlüsselwort UNIQUE bei CREATE INDEX

Bei zusammengesetzten Indizes läßt sich mit dem Schlüsselwort UNIQUE verhindern, daß mehrere Datensätze mit den gleichen Daten erscheinen. Nehmen wir an, daß die Tabelle RECHNUNGEN der folgenden eingebauten »Regel« entsprechen soll: Jede an eine Firma gezahlte Rechnung muß von einem anderen Bankkonto kommen. Man erzeugt dann einen UNIQUE-Index auf den Feldern NAME und KONTO_ID. Leider unterstützt Oracle8 nicht die UNIQUE-Syntax in diesem Sinne, sondern implementiert mit UNIQUE die Integritätsregel, daß der Wert der Spalte (oder der Kombination von Spalten) der zu indizierenden Tabelle eindeutig sein muß. Das folgende Beispiel zeigt das Schlüsselwort UNIQUE mit CREATE INDEX in der Sprache Transact-SQL von Sybase.


1> create unique index EIND_ID_NAME
2> on RECHNUNGEN(KONTO_ID, NAME)
3> go
1> select * from RECHNUNGEN
2> go


NAME BETRAG KONTO_ID
Florida Water Company 20 1
Power Company 75 1
Phone Company 125 1
Software Company 250 1
Record Club 25 2
Cable TV Company 35 3
Debtor's Credit Card 35 4
U-O-Us Insurance Company 125 5
Joe's Car Palace 350 5
S.C. Student Loan 200 6


Versuchen Sie nun, einen Datensatz mit den gleichen Daten wie in einem bereits bestehenden Datensatz in die Tabelle RECHNUNGEN einzufügen.


1> insert RECHNUNGEN (NAME, BETRAG, KONTO_ID)
2> values('Power Company', 125, 1)
3> go


Es erscheint eine Fehlermeldung mit dem Hinweis, daß der INSERT-Befehl nicht zulässig ist. Man kann derartige Fehlermeldungen in einer Anwendung abfangen und dem Benutzer mitteilen, daß er ungültige Daten einfügen möchte.

Beispiel 10.3

Erstellen Sie einen Index für die Tabelle RECHNUNGEN, der das Feld BETRAG in fallender Reihenfolge sortiert.


SQL> CREATE INDEX BETRAG_ABST
2 ON RECHNUNGEN(BETRAG DESC);

Index wurde angelegt.


Hier haben Sie erstmalig den Operator DESC eingesetzt. Damit zeigt man SQL an, den Index in absteigender Richtung zu sortieren. (Per Vorgabe wird ein numerisches Feld in aufsteigender Richtung sortiert.) Sehen Sie sich nun das Ergebnis an:

SQL> SELECT * FROM RECHNUNGEN;

NAME BETRAG KONTO_ID
------------------------------ --------- ---------
Joe's Car Palace 350 5
Software Company 250 1
S.C. Student Loan 200 6
Phone Company 125 1
U-O-Us Insurance Company 125 5
Power Company 75 1
Cable TV Company 35 3
Debtor's Credit Card 35 4
Record Club 25 2
Florida Water Company 20 1

10 Zeilen ausgewählt.


Dieses Beispiel hat einen Index mit dem Operator DESC auf der Spalte BETRAG erzeugt. Beachten Sie in der Ausgabe, daß die Beträge vom größten zum kleinsten Wert geordnet sind.


Indizes und Verknüpfungen

Wenn man bei Abfragen mit komplizierten Verknüpfungen arbeitet, kann die Ausführungszeit der SELECT-Anweisung - insbesondere bei großen Tabellen - im Sekundenbereich liegen (verglichen mit den sonst üblichen Wartezeiten im Bereich von Millisekunden). In einer Client/Server-Umgebung mit vielen Benutzern kann ein derartiges Leistungsverhalten für die Benutzer Ihrer Anwendung ziemlich frustrierend sein. Die Geschwindigkeit der Abfrage läßt sich steigern, wenn man einen Index für die häufig in Verknüpfungen vorkommenden Felder erstellt. Allerdings erreicht man eher das Gegenteil, wenn man zu viele Indizes erzeugt. Es empfiehlt sich daher, mit Indizes auf verschiedenartigen großen Tabellen (mit einem Umfang von mehreren tausend Datensätzen) zu experimentieren. Aus diesen Experimenten lassen sich dann Rückschlüsse auf die Optimierung von SQL-Anweisungen ziehen.


In den meisten Implementierungen läßt sich die für eine Abfrage benötigte Zeit messen. Oracle bezeichnet diesen Mechanismus als Timing. Konsultieren Sie in diesem Zusammenhang die Dokumentation der jeweiligen Implementierung.

Das folgende Beispiel erzeugt einen Index auf den Feldern KONTO_ID in den Tabellen RECHNUNGEN und BANKKONTEN:


SQL> CREATE INDEX RECH_INDEX ON RECHNUNGEN(KONTO_ID);

Index wurde angelegt.

SQL> CREATE INDEX RECH_INDEX2 ON BANKKONTEN(KONTO_ID);

Index wurde angelegt.

SQL> SELECT RECHNUNGEN.NAME NAME, RECHNUNGEN.BETRAG BETRAG,
2 BANKKONTEN.SALDO KONTOSTAND
3 FROM RECHNUNGEN, BANKKONTEN
4 WHERE RECHNUNGEN.KONTO_ID = BANKKONTEN.KONTO_ID;

NAME BETRAG KONTOSTAND
------------------------------ --------- ----------
Phone Company 125 500
Power Company 75 500
Software Company 250 500
Florida Water Company 20 500
Record Club 25 1200
Cable TV Company 35 90
Debtor's Credit Card 35 400
Joe's Car Palace 350 2500
U-O-Us Insurance Company 125 2500
S.C. Student Loan 200 4500

10 Zeilen ausgewählt.


Dieses Beispiel erzeugt zuerst einen Index für das Feld KONTO_ID auf beiden Tabellen in der zugehörigen Abfrage. Mit einem Index für KONTO_ID für jede Tabelle kann die Verknüpfung schneller auf bestimmte Datenzeilen zugreifen. In der Regel sollte man die Spalten einer Tabelle indizieren, die eindeutig sind oder über die man Tabellen in Abfragen verknüpfen möchte.


Cluster

Auch wenn wir ursprünglich festgestellt haben, daß sich mit Indizes eine Tabellenansicht präsentieren läßt, die sich von der physikalischen Anordnung unterscheidet, ist diese Aussage nicht hundertprozentig korrekt. Viele Datenbanksysteme unterstützen einen speziellen Indextyp, mit dem der Datenbankverwalter oder Entwickler Daten gruppieren - in sogenannten Clustern zusammenfassen - kann. Wenn man mit einem gruppenbildenden Index arbeitet, wird die physikalische Anordnung der Daten innerhalb einer Tabelle modifiziert. Ein gruppenbildender Index bringt gewöhnlich bessere Reaktionszeiten beim Abrufen von Daten als ein herkömmlicher, nichtgruppenbildender Index. Allerdings erlauben viele Datenbanksysteme (wie etwa Sybase SQL Server) nur einen gruppenbildenden Index pro Tabelle. Den gruppenbildenden Index baut man gewöhnlich auf dem Primärschlüsselfeld auf. In der Sprache Transact-SQL von Sybase kann man nach der folgenden Syntax einen eindeutigen gruppenbildenden Index auf dem Feld KONTO_ID der Tabelle BANKKONTEN erstellen:


Oracle behandelt das Cluster-Konzept etwas anders. In einer Oracle-Datenbank stellt ein Cluster ein Datenbankobjekt in der Art einer Datenbank oder Tabelle dar. In einem Cluster speichert man Tabellen mit gemeinsamen Feldern, um die Zugriffsgeschwindigkeit zu erhöhen.


Die Syntax für das Erstellen eines Clusters mit Oracle8 sieht wie folgt aus:


Innerhalb des Clusters sollte man dann einen Index erzeugen, der sich auf die in den Cluster aufzunehmenden Tabellen bezieht. Anschließend kann man die Tabellen hinzufügen. In einen Cluster sollte man nur Tabellen aufnehmen, die man häufig in Verknüpfungen verwendet. In einen Cluster gehören keine Tabellen, auf die man individuell über eine einfache SELECT-Anweisung zugreifen kann.


Die SQL-Implementierung der Cluster hängt vom jeweiligen Datenbankhersteller ab. An dieser Stelle gehen wir nicht näher auf deren Einsatz oder die Syntax für das Erstellen ein. Anhand der Dokumentation des konkreten Datenbanksystems können Sie sich informieren, ob und wie diese nützlichen Objekte unterstützt werden.



Zusammenfassung

Sichten sind virtuelle Tabellen und bieten eine einfache Möglichkeit, die Daten in einem Format zu präsentieren, das sich von der tatsächlichen Speicherorganisation in der Datenbank unterscheidet. Die Syntax der Anweisung CREATE VIEW verwendet (mit einigen Ausnahmen) eine normale SELECT-Anweisung, um die Sicht zu erzeugen. Man kann eine Sicht als normale Tabelle behandeln und darauf Daten einfügen, aktualisieren, löschen und auswählen. In diesem Zusammenhang wurde kurz auf die Datenbanksicherheit eingegangen, und es wurde gezeigt, wie man Sicherheit mit Hilfe von Sichten implementiert. Lektion 12 widmet sich dem Thema Datenbanksicherheit ausführlicher.


Die grundlegende Syntax für das Erstellen einer Sicht lautet:


CREATE VIEW Sichtname AS
SELECT Feldname(n) FROM Tabellenname(n);


Sichten verwendet man unter anderem, um ...


Weitere Hilfsmittel für den Datenbankentwurf und die SQL-Programmierung sind Indizes. Dabei handelt es sich um physikalische Datenbankobjekte, die ein Datenbank-Managementsystem speichert und die sich für die Abfrage von bereits sortierten Daten aus der Datenbank eignen. Außerdem läßt sich dank des Aufbaus von Indizes mit geeignet formulierten Abfragen und indizierten Tabellen eine signifikante Geschwindigkeitssteigerung erreichen.


Die grundlegende Syntax für das Erstellen eines Index sieht wie folgt aus:


CREATE INDEX Indexname
ON Tabellenname(Feldname(n));


Manche Datenbanksysteme bieten zusätzliche Optionen, wie etwa die Schlüsselwörter UNIQUE und CLUSTERED.



Fragen & Antworten

Frage:

Wenn die Daten innerhalb meiner Tabelle bereits in einer sortierten Reihenfolge vorliegen, warum sollte ich dann einen Index für diese Tabelle verwenden?

Antwort:

Ein Index bietet trotzdem noch einen Leistungszuwachs aufgrund der schnellen Suche von Schlüsselwerten in einer Baumstruktur. Gegenüber dem Durchsuchen aller Datensätze im direkten Zugriff lassen sich Datensätze über einen Index schneller lokalisieren. Außerdem kann der SQL-Abfrageprozessor gar nicht wissen, daß die Daten in der Tabelle bereits sortiert sind.

Frage:

Kann ich einen Index erzeugen, der Felder aus mehreren Tabellen enthält?

Antwort:

Leider nicht. Allerdings bietet zum Beispiel Oracle8 die Möglichkeit, einen Cluster - das heißt, einen gruppenbildenden Index - zu erzeugen. In einem Cluster kann man die erforderlichen Tabellen unterbringen und Cluster-Indizes auf Feldern erzeugen, die den Tabellen gemeinsam sind. Diese Implementierung ist die Ausnahme und nicht die Regel. Im konkreten Fall konsultieren Sie bitte die Dokumentation zu Ihrem Datenbank-Managementsystem.


Workshop


Kontrollfragen

1. Was passiert, wenn man einen eindeutigen Index auf einem nichteindeutigen Feld erzeugt?


2. Sind die folgenden Aussagen richtig oder falsch?


3. Ist die folgende CREATE-Anweisung korrekt?


SQL> create view kredit_schulden as
(select all from offen
where KONTO_ID = 4);


4. Ist die folgende CREATE-Anweisung korrekt?


SQL> create unique view offen as
select * from schulden_tbl;


5. Ist die folgende CREATE-Anweisung korrekt?


SQL> drop * from view offen;


6. Ist die folgende CREATE-Anweisung korrekt?


SQL> create index id_index on rechnungen
(konto_id);



Übungen

1. Machen Sie sich mit Ihrem Datenbanksystem vertraut. Unterstützt es Sichten? Welche Optionen sind Ihnen bei der Erstellung von Sichten erlaubt? Schreiben Sie mit der passenden Syntax eine einfache SQL-Anweisung, die eine Sicht erzeugt. Führen Sie einige herkömmliche Operationen wie SELECT oder DELETE aus und löschen Sie dann die Sicht (per DROP).


2. Prüfen Sie, ob Ihr Datenbanksystem Indizes unterstützt. Zweifellos steht Ihnen eine breite Palette von Optionen zur Verfügung. Probieren Sie einige dieser Optionen auf einer Tabelle aus, die innerhalb Ihrer Datenbank existiert. Bestimmen Sie insbesondere, ob Sie eindeutige (UNIQUE) oder gruppenbildende (CLUSTERED) Indizes auf einer Tabelle innerhalb der Datenbank erzeugen können.


3. Suchen Sie sich nach Möglichkeit eine Tabelle, die mehrere tausend Datensätze enthält. Stoppen Sie die Zeit für verschiedene Operationen bezüglich der Datenbank. Fügen Sie Indizes ein und prüfen Sie, ob sich eine Leistungsverbesserung feststellen läßt. Probieren Sie die heute gegebenen Tips aus.



Ein Imprint des Markt&Technik Buch- und Software-Verlag GmbH.
Elektronische Fassung des Titels: SQL in 21 Tagen, ISBN: 3-8272-2020-3


vorheriges KapitelTop Of PageInhaltsverzeichnisIndexInfoseitenächstes Kapitel