Datenbankprogrammierung
 

Axel Kohnert
Lehrstuhl Mathematik 2
WS 2000/2001
 
 
 

Teil 1 SQL

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

1.1 Geschichte

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

SQL wurde Ende der 70er Jahre von IBM entwickelt. Es war als Abfragesprache zur IBM Datenbank DB2 vorgesehen. DB2 ist eine relationale Datenbank die auch heute noch vertrieben wird. SQL ist eine Abkürzung für
 

structured query language
 

Später wurde SQL von der Organisation ANSI standardisiert. Aber fast alle Datenbankanbieter implementieren eine Obermenge, d.h. sie bieten Erweiterungen an. Der aktuelle Standard ist SQL92.  SQL wurde für eine relationale Datenbank geschrieben. Der Erfolg dieser Datenbanken ist auch der Grund dafür, dass SQL der de facto Industriestandard ist.
 
 
 
 
 
 
 
 
 

Mit SQL werden sowohl die 'normalen' Datenbankoperationen
 

·Einfügen

·Löschen

·Suchen

·Anlegen von Tabellen

·Löschen von Tabellen
 
 
 
 
 
 
 
 
 

als auch Aufgaben des Administrators
 

·Datenbanken anlegen

·Benutzerrechte manipulieren

·Benutzer anlegen/löschen

·Systemsicherheit gewährleisten
 
 
 
 
 

erledigt.
 
 
 
 
 

1.2. select Anweisung

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

1.2.1 einfache Form

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Dies ist der Befehl um Daten in der Datenbank zu suchen. Die select Anweisung hat folgende Form

SELECT ... FROM ..........

Die Worte select und from sind Schlüsselworte die vorkommen müssen. Bei Schlüsselworten wird nicht zwischen Groß- und Kleinschreibung unterschieden. Man erkennt auch den Ansatz die Anfrage möglichst natürlichsprachlich zu formulieren. In einer relationalen Datenbank werden alle Daten in Tabellen gehalten. Für unsere Beispiele nehmen wir eine online Buchhandlung. Es gibt folgende Tabelle mit Namen BOOKS

ISBN  AUTOR TITLE PUBLISHER_ID PRICE
0-7895-0052-3
Rob Coronel
Database Systems
bd_fr
29,95
0-521-77571-X
Sheldon M. Ross
Topics in finite and discrete mathematics
cup
39,95
0-201-44787-8
Bowman, Emerson, Darnovsky
The practical SQL Handbook
AW
29,95
.....
 
Will man nun im einfachsten Fall die gesamte Tabelle als Ergebnis der Abfrage erhalten, so lautet der SQL Befehl:

SELECT * FROM BOOKS;

und man erhält die Tabelle inklusive der Kopfzeile mit den Spaltennamen als Ergebnis. Der Strichpunkt dient als Abschluss des SQL Befehls. Die erste Variante ist der Fall, dass man nicht alle Spalten der Tabelle haben will, weil man z. B. mit der ISBN Nummer und der Verlags id nichts anfangen kann. Der Befehl lautet dann:
 
 
 
 
 
 

SELECT AUTOR,TITLE FROM BOOKS;

und man erhält die 2 spaltige Tabelle

AUTOR
TITLE
Rob Coronel
Database Systems
Sheldon M. Ross
Topics in finite and discrete mathematics
Bowman, Emerson, Darnovsky
The practical SQL Handbook
....
Wählt man im obigen Beispiel nur die Preisspalte aus
 
 

SELECT PRICE FROM BOOKS;

erhält man die Ausgabe:
 

PRICE
29,95
39,95
29,95
....
wobei scheinbar identische Zeilen  vorkommen. Dies kann man mit dem Schlüsselwort

DISTINCT

umgehen. Mit der Abfrage

SELECT DISTINCT PRICE FROM BOOKS;

erhält man die Ausgabe:
 

PRICE
29,95
39,95
....
Dies liegt daran, dass default Einstellung das entgegengesetzte Schlüsselwort ALL ist.  Bei der Spaltenauswahl kann man die Spalte nicht nur einfach selektieren, sondern auch Operationen anwenden. Um z. B. den Buchpreis nicht in $, sondern in DM zu bekommen, kann man mit 2.20 (oder was auch immer der Dollarkurs ist multiplizieren:
 
 
 
 
 
 

SELECT AUTOR, TITLE, PRICE, PRICE*2.20 FROM BOOKS;
 
 

ISBN 
AUTOR
PRICE
PRICE*2.20
0-7895-0052-3
Rob Coronel
29,95
65,89
0-521-77571-X
Sheldon M. Ross
39,95
87,89
0-201-44787-8
Bowman, Emerson, Darnovsky
29,95
65,89
.....
 
Wobei noch viel komplizierter Ausdrücke erstellt werden können, wobei auch die Einträge aus verschiedenen Spalten verknüpft werden können.
 
 
 

1.2.2 WHERE Schlüsselwort

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Mit der WHERE Klausel werden Zeilen ausgewählt. Das Konstrukt wirkt als eine Art Filter. Zuerst ein einfaches Beispiel

SELECT AUTOR, TITLE, PRICE FROM BOOKS WHERE PRICE >30;
 
 

ISBN 
AUTOR
PRICE
0-521-77571-X
Sheldon M. Ross
39,95
.....

Folgende Vergleichsoperatoren stehen zur Verfügung:

= Gleichheit
< Kleiner
> Größer
<= Kleiner gleich
>= Größer gleich
<> Verschieden

Das funktioniert nicht nur bei numerischen Einträgen sondern auch beim Datum und Zeichenketten. Natürlich können auch wieder logische Bedingungen aus der Verknüpfung von mehreren Spalten formuliert werden. Es gibt noch weitere Verknüpfungsoperatoren
 
 
 
 

LIKE

Dies dient zur Auswahl mittels regulärer Ausdrücke. Ausgewählt wird, wenn der Zeichenketteneintrag matcht. Mit dem Befehl

SELECT ISBN, AUTOR, PRICE FROM BOOKS WHERE AUTOR LIKE '%R%';
 

ISBN 
AUTOR
PRICE
0-7895-0052-3
Rob Coronel
29,95
0-521-77571-X
Sheldon M. Ross
39,95
.....
 
 
werden alle Zeilen ausgewählt, in denen der Autorenname ein 'R' enthält. Das Zeichen % ist das Sonderzeichen für eine beliebige Zeichenkette, inklusive der Länge 0. Das Sonderzeichen _ entspricht einem beliebigen Zeichen.
 

BETWEEN

Dient zur Auswahl von Intervallen.

SELECT ISBN, AUTOR, PRICE FROM BOOKS WHERE PRICE BETWEEN 35 AND 45;
 

ISBN 
AUTOR
PRICE
0-521-77571-X
Sheldon M. Ross
39,95
.....
 
 
IN

Damit wird getestet ob ein Wert innerhalb einer gegebenen Wertemenge liegt.

SELECT AUTOR, TITLE, PRICE FROM BOOKS WHERE PRICE IN (32,"39,95");

ISBN 
AUTOR
PRICE
0-521-77571-X
Sheldon M. Ross
39,95
.....
 
 
analog gibt es auch noch die WHERE Klausel mit NOT IN. Mit den Bausteinen

AND, OR, NOT

kann man auch kompliziertere Bedingungen aufbauen:
 
 
 
 
 
 

SELECT AUTOR, TITLE, PRICE FROM BOOKS WHERE AUTOR LIKE '%R%' AND PRICE BETWEEN 25 AND 35;
 
 

ISBN 
AUTOR
PRICE
0-7895-0052-3
Rob Coronel
29,95
.....
 
 

 
 
 
 

1.2.3 Zusammenfassen von Zeilen  (Aggregatzeilen)

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Bei der select Anweisung können auch Funktionen angegeben werden, die auf die Ergebniszeilen angewandt werden:

SELECT AVG(PRICE) FROM BOOKS;
 

AVG(PRICE)
35,55

Das Ergebnis ist der Durchschnittswert über alle Einträge in der PRICE Spalte der BOOKS Tabelle. Weitere Funktionen sind

COUNT()                                das ist die Anzahl der Einträge
MAX()
MIN()
SUM()                                      das ist die Summe
 

und verschiedene weitere, meist statistische Funktionen, als eigene Erweiterungen der verschiedenen Datenbankanbieter. Diese Funktionen können verknüpft werden mit dem WHERE Schlüsselwort:

SELECT AVG(PRICE) FROM BOOKS WHERE AUTOR='Goethe';

oder um z. B. die Anzahl der verschiedenen Autoren in der Datenbank zu bestimmen verwendet man das DISTINCT Schlüsselwort.
 

SELECT COUNT(DISTINCT AUTOR) FROM BOOKS;
 
 
 
 

GROUP BY

Bisher wurden die Funktionen auf das Gesamtergebnis der Auswahl angewandt, dies ändert sich mit der GROUP BY Anweisung:

SELECT AUTOR,AVG(PRICE) FROM BOOKS GROUP BY AUTOR;

Damit wird für jeden Autor eine Zeile mit dem Namen und dem Durchschnittspreis seiner Bücher ausgegeben. Will man derartige Anfragen formulieren muss man darauf achten, dass nur solche Spalten ausgewählt werden die für alle Zeilen die mittels GROUP BY zusammengefasst werden den gleichen Eintrag haben. Falsch wäre z. B.

SELECT AUTOR,ISBN,AVG(PRICE) FROM BOOKS GROUP BY AUTOR;

da der Fall passieren kann dass, ein Autor Bücher mit verschiedenen ISBN Nummern hat.

HAVING

Das ist das Schlüsselwort, wenn man Eigenschaften von Blöcken, die mit GROUP BY zusammengefasst wurden, finden will. So z.B. alle Autoren, deren Durchschnittspreis über 100 liegt:

SELECT AUTOR,AVG(PRICE) FROM BOOKS GROUP BY AUTOR HAVING AVG(PRICE)>100;

Der Unterschied zum WHERE Schlüsselwort ist die Anwendung auf die Blöcke geformt durch GROUP BY im Gegensatz zu der einzelnen Zeile wie es beim WHERE Schlüsselwort passiert. Das macht keinen Unterschied, wenn als Auswahlkriterium keine zusammenfassende Funktion genommen wird:

SELECT AUTOR,AVG(PRICE),PUBLISHER_ID FROM BOOKS GROUP BY AUTOR,PUBLISHER_ID HAVING PUBLISHER_ID='AW';

oder aber

SELECT AUTOR,AVG(PRICE),PUBLISHER_ID FROM BOOKS WHERE PUBLISHER_ID='AW' GROUP BY AUTOR,PUBLISHER_ID;

In beiden Fällen wird der Durchschnittspreis für die Bücher eines Autors beim AW Verlag ermittelt. Im ersten Fall wird der AW Verlag am Ende herausgefiltert, im zweiten Fall, bevor die Gruppen zusammengefasst werden.
 
 
 
 
 
 
 

1.2.4 ORDER BY

 
Damit kann die Reihenfolge der Zeilen in der Ausgabe beeinflusst werden. Dieser Teil kommt am Ende der SELECT Anweisung. Um z. B. die Bücher dem Preis nach sortiert auszugeben


SELECT * FROM BOOKS ORDER BY PRICE;
 
 

ISBN 
AUTOR
TITLE
PUBLISHER_ID
PRICE
...
 
 
 
 
0-7895-0052-3
Rob Coronel
Database Systems
bd_fr
29,95
0-201-44787-8
Bowman, Emerson, Darnovsky
The practical SQL Handbook
AW
29,95
...
 
 
 
 
0-521-77571-X
Sheldon M. Ross
Topics in finite and discrete mathematics
cup
39,95
....
 
 
 
 
 
Die normale Sortierreihenfolge ist ansteigend, es sei denn man spezifiziert absteigend mit dem Schlüsselwort DESC. Bei Textfeldern wird die lexikographische Reihenfolge genommen. Will man zuerst nach Autor und innerhalb des Autors nach Preis, dann geht das so:
SELECT * FROM BOOKS ORDER BY AUTOR ASC, PRICE DESC;
 

und beim Autor wird die ansteigende lexikographische Ordnung genommen (ASC ist default und eigentlich überflüssig) und innerhalb der Bücher vom gleichen Autor erscheint das teuerste zuerst. Eine andere Möglichkeit ist statt des Namen die Position der Spalte zu verwenden:
 

SELECT * FROM BOOKS ORDER BY 5 DESC;
 

und es wird wieder nach dem Preis sortiert, diesmal kommt das teuerste Buch zuerst.
 
 
 
 
 

1.2.5 select mit join

Alle bisherigen Fälle waren ein select aus einer Tabelle. Ein wesentlicher Vorteil der relationalen Datenbank ist aber das Verteilen auf verschiedene Tabellen. Daher muss es natürlich auch möglich sein die Daten aus verschiedenen Tabellen gleichzeitig zurück zu holen. Dies geschieht mit der Operation join, dabei werden zwei Tabellen verschmolzen. Dies geschieht nicht physikalisch sondern nur zum Zweck der Abfrage  mittels SELECT. Ein Beispiel ist folgende Situation: Der Buchhändler merkt, dass ihm das Buch Programming in SQL ausgeht. Die Daten zum Buch findet er mit der Abfrage
SELECT * FROM BOOKS WHERE TITLE = 'Programming in SQL';
 
 
ISBN 
AUTOR
TITLE
PUBLISHER_ID
PRICE
0-632-03136-0
John Carter 
Programming in SQL
McGH
24,95

 

Um das Buch nachzubestellen braucht er die Verlagsanschrift, und da diese für alle Bücher des gleichen Verlags gleich ist hat sie ein guter Datenbankdesigner in einer eigenen Tabelle PUBLISHER abgelegt, und dort findet der Buchhändler:
 
 
 
PUBLISHER_ID
NAME
ADRESS
LAND
FAX
FON
McGH
McGRAW-HILL 
Book Company 
Europe
Maidenhead 
Berkshire 
SL62QL
England
01628770224
0162823432
...
 
 
 
 
 

und mit der Abfrage
 

SELECT * FROM BOOKS,PUBLISHER  WHERE BOOKS.PUBLISHER_ID=PUBLISHER.PUBLISHER_ID AND TITLE = 'Programming in SQL';
 

erhält der Buchhändler alle Daten zur Nachbestellung. Es wurden die zwei Tabellen über das gemeinsame Attribut PUBLISHER_ID verschmnolzen, und eine neue Tabelle entstand, aus der wurde dann eine Zeile herausgewählt. Theoretisch (relationale Datenbanktheorie) hat die Tabelle nach dem Join folgende Spalten:
 
 
 

ISBN  AUTOR TITLE PRICE PUBLISHER_ID NAME ADRESS LAND FAX FON

 

Intern wird bei der Abfrage (wie man auch an der Syntax erkennt) ein direktes Produkt gebildet und dann herausgefiltert. Die verwendete Verknüpfung war der sogenannte Equi-Join da die Verschmelzung mit der Gleichheit BOOKS.PUBLISHER_ID = PUBLISHER.PUBLISHER_ID geschah. Als ein weiteres  Beispiel noch eine Abfrage um den Durchschnittspreis je Land des Verlags herauszubekommen:
 
 

SELECT PUBLISHER.LAND HERKUNFTSLAND, AVG(PRICE) DURCHSCHNITTSPREIS FROM BOOKS,PUBLISHER GROUP BY PUBLISHER.LAND;
 
 
 
HERKUNFTSLAND
DURCHSCHNITTSPREIS
...
 
ENGLAND
19,12
....
 

 

und an diesem Beispiel wurde auch noch die Verwendung von Aliasnamen bei den Spaltenüberschriften gezeigt. Eine weitere Verwendung des Joins ergibt sich beim sogenannten Auto-join, d.h. der Join einer Tabelle mit sich selbst:
 

SELECT x.TITLE FROM BOOKS x, BOOKS y WHERE y.TITLE='Programming in SQL' AND x.TITLE LIKE '%SQL%' AND x.PRICE < y.PRICE
 

damit kann man Bücher über SQL finden, die billiger sind als das Buch Programming in SQL. Man kann die SQL Befehle in drei Gruppen einteilen:
 


der bisher besprochene SELECT fällt natürlich in die erste Kategorie.
 
 

1.3 CREATE TABLE, DROP TABLE, ALTER TABLE

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Um etwas aus einer Tabelle auszuwählen muss diese  natürlich zuvor erzeugt  werden.  Die zugehörigen Befehle fallen in den Teil der Data Description Language (Erzeugen) . Zum Erzeugen einer Tabelle dient der Befehl
 
 

CREATE TABLE
 

Um obige Beispieltabelle BOOKS zu erzeugen wäre folgender Aufruf passend:
 
 

CREATE TABLE BOOKS (ISBN CHAR(13), AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10), PRICE NUMBER(7,2));
 
 

Nach den Schlüsselwörtern kommt der Name der Tabelle, danach die Attributnamen zusammen mit dem Attributtyp getrennt durch Kommata. Die Datentypen variieren leicht von Anbieter zu Anbieter. Obige Syntax funktioniert bei Oracle. Die Typen bedeuten folgendes:
 
 
 


 
 

Beim Erzeugen einer Tabelle kann man zusätzliche Bedingungen formulieren, z.B. dass ein Eintrag vorhanden sein muss. So erzeugt der Befehl
 

CREATE TABLE BOOKS (ISBN CHAR(13) NOT NULL , AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10), PRICE NUMBER(7,2));
 

die gleiche Tabelle mit der Nebenbedingung, dass stets ein Eintrag in der Spalte ISBN vorhanden sein muss. Dies wird beim Einfügen oder Ändern überprüft. Bei Schlüsseln (später) sollte dies verlangt werden. Ferner können weitere Bedingungen angegeben werden, die dann beim Einfügen / Ändern überprüft werden. Will man z.B. keine Bücher für unter 2$ verkaufen, so kann man das Einfügen / Ändern  mit dem folgenden Befehl verhindern:
 

CREATE TABLE BOOKS (ISBN CHAR(13)  , AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10), PRICE NUMBER(7,2) CHECK (PRICE > 2) );
 

Um die Tabelle wieder zu Löschen gibt es den Befehl
 

DROP TABLE
 

und im Beispiel kann die Tabelle mit
 

DROP TABLE BOOKS;
 

wieder gelöscht werden. Die andere Möglichkeit ist das Ändern einer bestehenden Tabelle das passende Schlüsselwort ist
 

ALTER TABLE
 

es ist nicht in allen SQL Varianten verfügbar, aber z.B. in Oracle. Es gibt zwei Varianten
 

ALTER TABLE .... ADD ...

ALTER TABLE .... MODIFY .....
 
 

um z.B. in die BOOKS Tabelle noch eine Spalte mit der Anzahl der verkauften Exemplare einzufügen:
 

ALTER TABLE BOOKS ADD (NUMBERS INTEGER);
 

und die Einträge werden mit dem sog. NULL Wert vorbesetzt. Eine nachfolgende Abfrage liefert das Ergebnis:
 

SELECT * FROM BOOKS WHERE TITLE LIKE '%SQL%';
 
 

ISBN 
AUTOR
TITLE
PUBLISHER_ID
PRICE
NUMBERS
...
0-632-03136-0
John Carter 
Programming in SQL
McGH
24,95
...

Eine Spalte mit dem Modifier NOT NULL kann nur zu einer leeren Tabelle hinzugefügt werden. Eine weitere Variante ist das Erzeugen einer neuen Tabelle aus dem Ergebnis einer SELECT Anweisung, damit wird eine neue Tabelle erzeugt und sofort mit dem Ergebnis der SELECT Anweisung gefüllt.
 

CREATE TABLE SQLBOOKS AS SELECT * FROM BOOKS WHERE TITLE LIKE '%SQL%';
 

und die neue Tabelle SQLBOOKS hat die gleichen Spalten wie die Tabelle BOOKS und enthält alle Bücher aus der Tabelle BOOKS in der deren Titel das Wort SQL vorkommt. Ein nachfolgendes
 

SELECT * FROM SQLBOOKS;
 
 

ISBN 
AUTOR
TITLE
PUBLISHER_ID
PRICE
...
0-632-03136-0
John Carter 
Programming in SQL
McGH
24,95
...

Mit dem CREATE Befehl  wird eine neue Tabelle als Kopie angelegt, d.h. spätere Änderungen an BOOKS werden nicht automatisch übernommen. Anders ist das bei VIEWS (später).
 
 
 
 
 

1.4 INSERT, UPDATE, DELETE

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Der nächste Schritt ist das Füllen der Tabelle mit Einträgen. Diese Befehle fallen in den  Bereich der Data Manipulation Language.  Beim Einfügen  werden komplette Zeilen angegeben. Die Schlüsselwörter hierzu sind:
 

INSERT INTO ... VALUES
 

Im Beispiel
 

INSERT  INTO BOOKS VALUES ('0-7895-0052-3','Rob Coronel','Database Systems','bd_fr',29.95);
 

wobei die Hochkommata nötig sind zur Eingabe der Zeichenketten. Ferner ist zu beachten, dass der Preis mit einem Punkt zur Trennung von Vorkomma und Nachkommateil eingegeben wird. Dies Beispiel betrifft auch die noch nicht um die Spalte NUMBER erweiteret Tabelle. Will man nicht alle Werte eingeben, was erlaubt ist wenn nicht der Modifier NOT NULL verwendet wurde muss man folgende Syntax verwenden:
 

INSERT  INTO BOOKS (TITLE) VALUES ('Informatik Band 3');
 

und damit wird eine Zeile eingefügt die nur in der Spalte TITEL einen Eintrag enthält. Im Falle, dass eine der übrigen Spalten als NOT NULL angegeben wurde verursacht eine derartige Anweisung einen Fehler. Will man Löschen so geschieht das ähnlich dem SELECT mit der Anweisung
 

DELETE FROM .... WHERE ....
 

bzw. ohne WHERE, wobei die gesamten Tabelleneinträge gelöscht werden. Will man z.B. obiges Einfügen wieder Löschen:
 

DELETE FROM BOOKS WHERE TITLE='Informatik Band 3';
 
 

Oder man kann alle Zeilen Löschen, in der z.B. der PRICE nicht gesetzt ist:
 

DELETE FROM BOOKS WHERE PRICE IS NULL;
 

Eine andere Möglichkeit ist, die fehlerhafte Zeile zu ändern, dazu gibt es das Schlüssselwort
 

UPDATE .... SET .... WHERE
 

und mittels WHERE wird die Zeile (oder Teiltabelle ausgewählt) und mit SET werden die Spalteneinträge geändert:
 

UPDATE BOOKS SET ISBN='3-446-18699-9' AUTOR='Christian Horn' PUBLISHER_ID='Hanser' PRICE='22' WHERE TITLE='Informatik Band 3';
 

und damit sind die NULL Werte ersetzt worden. Eine andere Anwendung der UPDATE Anweisung ist z.B. eine Preiserhöhung des Hanser Verlags:
 

UPDATE BOOKS SET PRICE=PRICE*1.07 WHERE PUBLISHER_ID='Hanser';
 

und der Preis aller Bücher, die im Hanser Verlag erscheinen werden 7% teurer.
 
 
 
 

1.5. Views

Views (Sichten) sind neben Joins das entscheidende Merkmal einer relationalen Datenbank. Ein Sicht ist eine virtuelle Tabelle, die, basierend auf einer SELECT Anweisung, einen Ausschnitt des Datenbestandes zur Verfügung stellt. Dabei werden in einer Sicht nicht wie bei einer temporären Tabelle Daten gespeichert, sondern die Sicht wird jederzeit dynamisch an die zugrundeliegenden Tabellen angepaßt. Damit läßt sich mit Sichten  ein sehr flexibler Zugriff auf die Daten realisieren, da sie wie ein Filter auf den Datenbestand wirken. Eine Sicht  wird mit der Anweisung
 

CREATE VIEW
 

angelegt. Danach kann mit diesem View genauso wie mit einer realen Tabelle gearbeitet werden. Lediglich beim schreibenden Zugriff gibt es
Einschränkungen. Um mit den Befehlen INSERT, DELETE und UPDATE auf einen View zugreifen zu können, muß ein View
veränderbar (updatable) sein. Ein View ist updatable, wenn alle der folgenden Bedingungen zutreffen:
 
 
 


 

Auch das Definieren von Sichten fällt in den Bereich der Data Definition Language. Um z.B. die Tabelle mit den SQL Büchern anzulegen
 
 

CREATE VIEW SQLBOOKS AS SELECT * FROM BOOKS WHERE TITLE LIKE '%SQL%';
 
 

Der Unterschied zum CREATE TABLE AS ist, dass damit nur eine neue Sicht definiert wird, d.h. alle nachfolgenden Änderungen in der Tabelle BOOKS werden übernommen, da bei jedem Zugriff auf SQLBOOKS die darunter liegende Abfrage neu ausgewertet wird. Nachfolgend kann auch in einen VIEW eingefügt werden, die geschieht mit dem normalen INSERT Befehl
 
 
 
 

INSERT INTO SQLBOOKS VALUES ('0-7895-0052-3','Rob Coronel','Database Systems','bd_fr',29.95);
 
 

und ein nachfolgendes SELECT auf diese Zeile bringt eine Fehlermeldung, denn der Titel matcht nicht auf die VIEW Bedingung. Um schon beim INSERT die Fehlermeldung zu bekommen muss bei der VIEWS Definition die Option
 
 

WITH CHECK OPTION
 
 

angegeben werden. Werden bei der Sicht nicht alle zugrundeliegenden Spalten verwendet, so werden diese beim Einfügen mit NULL Werten besetzt.
 
 

CREATE VIEW SQLBOOKS AS SELECT * FROM BOOKS WHERE TITLE LIKE '%SQL%' WITH CHECK OPTION ;
 
 

Das Löschen einer Sicht geschieht mit dem Befehl
 
 

DROP VIEW ...
 
 

wie das Löschen von Tabellen. Beim Definieren von VIEWS kann man sich neue Spalten Namen definieren:
 
 

CREATE VIEW SQLSHORTLIST (SQLTITEL, AUTOR, VERLAG) AS SELECT BOOKS.TITLE, BOOKS.AUTOR, PUBLISHER.NAME FROM BOOKS, PUBLISHER WHERE BOOKS.PUBLISHER_ID = PUBLISHER.PUBLISHER_ID;
 
 

und natürlich können VIEWS geschachtelt werden, d.h. bei einer Definition eines VIEWS kann auf einen anderen zugegriffen werden. Arbeitet man bei VIEWS mit Joins, so ist zu beachten, dass damit keine Änderung an den zugrunde liegenden Tabellen möglich ist. Ferner dürfen nur SELECT Anweisungen verwendet werden die Tabellen als Ergebnis liefern. Zeilenorientierte SELECT Anweisungen (später) sind nicht erlaubt.
 
 

1.6 Zugriffsrechte


Views sind eine gute Möglichkeit nur Teilmengen der Tabelle der  Öffentlichkeit zugänglich zu machen. Will man als online Buchhändler nicht aller Welt verraten wieviel Bücher man verkauft, so ist es geschickt einen View zu definieren, der die Spalte NUMBERS nicht mit weitergibt:
 

CREATE VIEW ALLEWELT AS SELECT ISBN , AUTOR , TITLE , PUBLISHER_ID , PRICE FROM BOOKS;
 

In einem zweiten Schritt wird mit
 

GRANT SELECT ON ALLEWELT TO WEBUSER;
 

dem Benutzer WEBUSER das Recht mittels SELECT auf den VIEW ALLEWELT zuzugreifen eingeräumt. Hat der Benutzer keine Rechte für die Tabelle BOOKS, kann so der gewünschte Schutzmechanismus erreicht werden. Wird statt dem Benutzer das Schlüsselwort ALL verwendet, so erhalten das Recht alle eingetragenen Benutzer. Wird das Schlüsselwort PUBLIC verwendet so erhalten auch alle zukünfigen neu eingetragenen Benutzer das Recht. Man kann Rechte sowohl für VIEWS als auch für Tabellen definieren. Automatisch hat man die Rechte nach einem CREATE TABLE) Folgende Rechte können vergeben werden (nicht vollständig)
 
 
 


 

Dann gibt es noch das Recht

Man kann auch mehrere Rechte gleichzeitig gewähren:
 

GRANT UPDATE,SELECT  ON ALLEWELT TO CHEF;
 

Man kann das Recht auch spaltenweise definieren:
 

GRANT UPDATE(ISBN,AUTOR)  ON ALLEWELT TO CHEF;
 

Dies ist wieder abhängig von der konkreten SQL Variante, manchmal muss man auch sagen:
 

GRANT UPDATE  ON ALLEWELT(ISBN,AUTOR)  TO CHEF;
 

Es gibt noch eine Variante, mit der eine Benutzer die gewährten Rechte weiterreichen darf, dazu dient die Erweiterung:
 

GRANT ... ON .. TO  ... WITH GRANT OPTION
 

Das Entziehen von Zugriffsrechten (sie müssen vorher mit GRANT vergeben worden sein)  passiert mit dem Befehl
 

REVOKE
 

Die Syntax ist wie beim GRANT Kommando
 

REVOKE UPDATE ON ALLEWELT TO CHEF;
 

Man muss aufpassen (SQL Varianten abhängig) was passiert wenn man Rechte für Gesamtviews/tabellen und einzelne Spalten mischt und was passiert wenn man Anweisungen für  PUBLIC als auch einzelne Nutzer mischt. Eine Erweiterung wie WITH GRANT OPTION gibt es natürlich nicht.
 
 
 

1.7 SCHLÜSSEL, INDEX


Schlüssel in einer Tabelle sind Attributmengen, die Zeilen eindeutig beschreiben. Sie sind Kernstück bei der Entwicklung von Normalformen, d.h. beim Entwurf von Datenbank. Man hat die Möglichkeit bei der Definition die Schlüsseleigenschaft anzugeben:
 

CREATE TABLE BOOKS (ISBN CHAR(13) NOT NULL , AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10), PRICE NUMBER(7,2), PRIMARY KEY (ISBN) );
 

und beim Einfügen wird überprüft ob nicht ein Buch mit der gleichen ISBN schon da ist. Ferner gibt es sogenannt Fremdschlüssel, in unserem Beispiel ist das die Spalte PUBLISHER_ID, die in der Tabelle PUBLISHER als Schlüssel dient. Es ist klar, dass in die Tabelle BOOKS kein Buch eingefügt werden darf, wenn nicht die PUBLISHER_ID bereits in der Tabelle PUBLISHER vergeben ist. Dies wird durch die folgende Definition bei CREATE TABLE erreicht:
 

CREATE TABLE BOOKS (ISBN CHAR(13) NOT NULL , AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10), PRICE NUMBER(7,2), PRIMARY KEY (ISBN) , FOREIGN KEY (PUBLISHER_ID) REFERENCES PUBLISHER(PUBLISHER_ID) );
 

Die Schlüsselbedingungen können auch direkt als Modifier zu den Spaltendefinition hinzugefügt werden:
 

CREATE TABLE BOOKS (ISBN CHAR(13) NOT NULL PRIMARY KEY , AUTOR CHAR(100), TITLE CHAR(200), PUBLISHER_ID CHAR(10) FOREIGN KEY REFERENCES PUBLISHER(PUBLISHER_ID), PRICE NUMBER(7,2)  );
 

was natürlich nur funktioniert wenn der Schlüssel aus einer einzelnen Spalte besteht. Äquivalent zu dem Schlüsselwort PRIMARY KEY kann man auch das Schlüsselwort
 

UNIQUE
 

verwenden (in beiden Varianten).Die Fremdschlüsselbedingung definiert mit REFERENCES kann wie schon gesehen mit GRANT/REVOKE erlaubt oder verboten werden. Ein Problem mit Fremdschlüsseln ist die Frage:

 
Was passiert wenn ein Datensatz gelöscht wird / verändert wird, auf den mittels Fremdschlüssel zugegriffen wird?


Der Normalfall ist, dass es nicht erlaubt ist einen entsprechenden Datensatz zu Löschen/Verändern. Man kann dies durch die erweiterte FOREIGN KEY Syntax ändern:
 

FOREIGN KEY (...) REFERENCES ... ON DELETE SET NULL
 

und damit wird beim Löschen der Wert beim Verweis auf NULL gesetzt. Ferner gibt es noch
 

FOREIGN KEY (...) REFERENCES ... ON DELETE CASCADE
 

und in der Tabelle mit dem Verweis wird die Zeile gelöscht. Es gibt noch die Klausel ON DELETE SET DEFAULT: Dann wird ein sogenannter Default Wert (anstelle von NULL) gesetzt. Diesen kann man bei der CREATE TABLE Anweisung definieren
 

CREATE TABLE BOOKS( ...., char(50) SPRACHE DEFAULT 'deutsch', ..);
 

Das Standardverhalten ist kann auch explizit vorgegeben werden:
 

FOREIGN KEY (...) REFERENCES ... ON DELETE RESTRICT
 

Analog gibt es dann noch die FOREIGN KEY Variante
 

FOREIGN KEY (...) REFERENCES ... ON UPDATE ...
 

Schlüssel (PRIMARY KEY / UNIQUE) werden üblicherweise vom System als Index verwandt. Dies ist eine Methode schneller zu werden. Es wird transparent für den Benutzer ein Verzeichnis angelegt wo die entsprechenden Daten zu finden sind. D.h. um ein Buch mit einer gegebenen ISBN Nummer zu finden muss nicht die ganze Tabelle BOOKS durchlaufen werden. Der Benutzer kann auch explizit einen Index anlegen dazu gibt es den Befehl
 

CREATE INDEX
 

um z.B. schnell nach Autoren zu suchen, legt der Designer der Datenbank einen entsprechenden Index an
 

CREATE INDEX AUTIND on BOOKS(AUTOR);
 

und ein nachfolgendes Suchen nach einen Autor schneller werden.
 

SELECT AUTOR FROM BOOKS WHERE AUTOR='Dick Francis';
 

Man kann auch Indices über mehrere Spalten definieren, dann wird eine Anfrage, in der alle Spalten des Indices vorkommen schneller. Dies ist analog der Tatsache, dass Schlüssel über mehrere Attribut gehen können.  Bei der Definition des Index kann auch der Modfier UNIQUE verwendet werden, das bedeutet, dass kein auf der Indexmenge identischer Datensatz eingefügt werden kann.
 

CREATE UNIQUE INDEX AUTIIND  ON BOOKS(AUTOR,TITLE);
 

was nicht sinnvoll ist, da so jeweils nur eine Ausgabe eines Buchs eines Autor abgespeichert werden kann. Ein PRIMARY KEY wird automatisch als UNIQUE (man kann auch das Schlüsselwort DISTINCT verwenden) angelegt.  FOREIGN KEY werden als Indices verwandt, die doppelte Einträge enthalten dürfen. Eine Spalte darf nicht in mehreren Indices vorkommen. Ein Index kann nur dann zum schnellen Zugriff auf eine Tabelle verwendet werden, wenn die bei der Suche verwendeten Spalten in einem Index geschlossen am Anfang liegen. Im folgenden Beispiel kann der Index zur Suche nicht verwendet werden, da für die Spalte col1 beim SELECT kein Wert angegeben wurde.
 

CREATE TABLE tab
(
        col1 INTEGER,
        col2 INTEGER,
        col3 INTEGER
);

CREATE INDEX idx1 ON tab (col1, col2);

SELECT * FROM tab WHERE col2 = 15;
 

Zum Anlegen von Indices werden meist B-Baum Methoden verwendet. Zum Löschen eines Indexs dient der Befehl

DROP INDEX
 
 
 
 

1.8 DATABASE Befehl


Eine Datenbank bezeichnet eine Menge von Tabellen, die logisch zusammengehören. Jede Tabelle, außer den Systemtabellen (data dictionary), gehört zu genau einer Datenbank. Neben den Tabellen können in einer Datenbank noch weitere Datenbankobjekte angelegt werden: Views, Synonyme, Indizes, Domains, Prozeduren, Funktionen, Trigger und Constraints. Jeder Benutzer muß nach der Anmeldung an ein Datenbanksystem  über die

DATABASE

Anweisung eine Datenbank auswählen, mit der er arbeiten möchte. Bei der DATABASE Anweisung gibt es noch den Modifier EXCLUSIVE, damit man die Datenbank als einziger nutzt. Um z.B. bei der Buchhandlungsdatenbank nachts eine Änderung durchzuführen kann man mit dem Befehl
 

DATABASE 'bookshop' EXCLUSIVE;
 

die Verbindung herstellen. Ein Fehler passiert wenn ein anderer Nutzer schon eine Verbindung hat. Die einzige Alternative ohne DATABASE Anweisung ist der Fall, dass  eine neue Datenbank angelegt wird. Dies geschieht   mit der
 

CREATE DATABASE  ....
 

Anweisung. Für unseren Buchladen wäre der Befehl gewesen:
 

CREATE DATABASE 'bookshop';
 

Dies ist dann seine aktuelle Datenbank, auf die sich alle folgenden
SQL-Anweisungen beziehen. Bei der Auswahl einer neuen Datenbank muß vorher
die aktuelle Datenbank geschlossen werden dazu  die
 

CLOSE DATABASE

Anweisung. Es ist nicht möglich, zwei Datenbanken parallel zu öffnen. Zusammen mit einer Datenbank werden verschiedene Systemfiles (data dictionary) abgespeichert.  Lediglich die Benutzer und die Datenbank (zumindest in den untersuchten Datenbanken) sind systemweit gültige Tabellen.
 
 
 
 
 
 
 
 
 
 

1.9. Trigger


Über Trigger lassen sich SQL Anweisungen festlegen, die automatisch gestartet werden, wenn ein bestimmtes Ereignis für eine Tabelle eintritt. Der Einsatz von Triggern ist z.B. für die folgenden Aufgaben sinnvoll:
 


Die Definition eines Triggers erfolgt über die

CREATE TRIGGER

Anweisung. Nur der Eigentümer einer Tabelle,  sowie alle Benutzer, die über eine GRANT Anweisung die Rechte dazu erhalten haben, dürfen einen Trigger für eine Tabelle anlegen. Bei der Definition des Triggers wird festgelegt, welches Ereignis einen Trigger auslöst. Mögliche Ereignisse sind:
 


Ebenso wie das Ereignis festgelegt wird, das einen Trigger auslöst, muß auch der Zeitpunkt festgelegt werden, zu dem die Aktionen gestartet werden sollen. Dabei gibt es sechs verschiedene Möglichkeiten:
 


Beispiele für die Definition eines Triggers:
 

CREATE TRIGGER log_stadt_delete AFTER DELETE ON stadt  FOR EACH STATEMENT INSERT INTO log_file VALUES(TODAY, USER, 'AD', 'stadt');
 

Durch diesen Trigger wird jedesmal, wenn eine DELETE Anweisung für die Tabelle stadt
ausgeführt wird, ein INSERT in die Tabelle log_file ausgeführt.
 

CREATE TRIGGER tr1 AFTER UPDATE OF gehalt ON angestellter REFERENCING NEW AS neu OLD AS alt FOR EACH ROW UPDATE abteilung SET gehaltssumme = gehaltssumme + neu.gehalt WHERE abt_nr = alt.abt_zugeh;
 
 

Dieser Trigger erhöht automatisch die Gesamtsumme der Gehälter einer Abteilung, wenn das Gehalt eines Mitarbeiters der Abteilung verändert wird. Um in den ausgelösten Aktionen auf die Daten zugreifen zu können, die in der auslösenden Anweisung aktuell sind, wird die REFERENCING Klausel verwendet. Über diese Klausel werden symbolische Namen für die Werte vor (OLD) bzw. nach (NEW) der auslösenden Anweisung vergeben. Bei einem UPDATE stehen somit die alten Werte des Datensatzes wie auch die neuen zur Verfügung. Diese Werte sind unabhängig davon, ob der Trigger vor, statt oder nach einer Operation ausgelöst wird. Bei einer INSERT Anweisung kann nicht auf die alten Werte zugegriffen werden, ebenso wie bei einem DELETE nicht auf die neuen Werte zugegriffen werden kann. Generell kann die REFERENCING Klausel nur in FOR EACH ROW Triggern verwendet werden. In FOR EACH STATEMENT Triggern ist dies nicht möglich.
 

-

-
-
-
-
-
-
-
-
-
-
-
-
-
 
 
 
 
 

1.10 geschachtelte SELECT Anweisungen

 

1.10.1 einfache Schachtelungen (IN, ALL, ANY)


Geschachtelte SELECT Anweisungen sind Anweisungen die  eine weitere  SELECT Anweisungen als Unterabfrage (subquery) enthalten. Unterabfrage bedeutet die SELECT Anweisung ist Bestandteil des Suchteils, der mit WHERE eingeleitet wird. Die Unterabfrage wird in Klammern eingeschlossen.  D.h allgemein sieht es so aus
 

SELECT ... FROM ... WHERE ..... (SELECT ... FROM .... WHERE)
 

Natürlich kann das noch weiter geschachtelt werden. Nach der subquery kann es auch noch mit GROUP BY/HAVING/ORDER BY weitergehen. Häufig gibt es die Möglichkeit Anfragen, die sonst mittels Join gelöst werden auch mittels Subquerys zu lössen. Um z.B. Bücher aus England zu finden
 

SELECT TITLE  FROM BOOKS,PUBLISHER WHERE BOOKS.PUBLISHER_ID= PUBLISHER.PUBLISHER_ID and PUBLISHER.LAND='England'
 

mittels Join. Die Idee bei Subqueries geht so, bilde eine Menge der Publisher_ID aus der Publisher Tabelle, und dann nehme nur die Bücher mit einer derartigen Publisher_ID. Dies geht folgendermassen:
 

SELECT TITLE FROM BOOKS WHERE PUBLISHER_ID = ANY (
        SELECT PUBLSIHER_ID FROM PUBLISHER WHERE LAND='England');
 

die Formulierung ANY bedeutet, dass die Operation (=) wahr wird wenn mindestens ein Wert aus der Tabelle der subquery passt. Äquivalent ginge dies auch mit der schon bekannten IN verknüpfung:
 

SELECT TITLE FROM BOOKS WHERE PUBLISHER_ID IN (
        SELECT PUBLSIHER_ID FROM PUBLISHER WHERE LAND='England');
 

Das Gegenteil ist die Abfrage nach den Büchern die nicht aus England kommen:
 

SELECT TITLE FROM BOOKS WHERE PUBLISHER_ID <> ALL (
        SELECT PUBLSIHER_ID FROM PUBLISHER WHERE LAND='England');
 

oder aber auch NOT IN. Auch das Beispiel für den Auto Join lässt sich umformulieren, es ging um Bücher die billiger als das Buch Programming in SQL war. Die Anfrage mit Join war:
 
 

SELECT x.TITLE FROM BOOKS x, BOOKS y WHERE y.TITLE='Programming in SQL' AND x.TITLE LIKE '%SQL%' AND x.PRICE < y.PRICE
 

Ohne Join geht es so:
 

SELECT TITLE FROM BOOKS WHERE TITLE LIKE '%SQL%'  AND PRICE <= ALL (
        SELECT PRICE FROM BOOKS WHERE TITLE='Programming in SQL');
 

Erlaubt ist ferner folgende Syntay wenn die subquery genau eine Zeile liefert, was in diesem Beispiel der Fall sein sollte (es existiert ein sog. Titelschutz, d.h. erigentlich ist auch ein Buchtitel ein Schlüssel)
 

SELECT TITLE FROM BOOKS WHERE TITLE LIKE '%SQL%'  AND PRICE <=  (
        SELECT PRICE FROM BOOKS WHERE TITLE='Programming in SQL');
 

d.h. in fehlt das ALL/IN/ANY/NOT IN muss die subquery genau eine Zeile liefern. All diese Anfragen kann man als eine zweistufige Abfrage auffassen, zuerst wird die subquery ausgewertet, danach die äussere Anfrage. Im folgenden Beispielen ist das anders:
 
 
 

1.10.2 verknüpfte Schachtelungen (IN)


Wenn nun Spalten aus der äusseren Abfrage in der subquery auftauchen, dann ist der Aufwand ähnlich wie beim Join, d.h. für jedes äussere Tupel wird eine subquery fällig, z.B. um die teuersten Bücher von jedem Verleger zu finden
 

SELECT * FROM BOOKS A WHERE PRICE > ALL (
        SELECT BOOKS B WHERE B.PUBLISHGER_ID = A.PUBLISHER_ID AND A.ISBN != B.ISBN);
 

aus Perfomrancegründen gibt es häufig Einschränkungen in der Anzahl der Parameter die in die subquery übergeben werden (im Beispiel 2). Daher ist diese subquery auf manchen System wo nur eine erlaubt ist, nicht lauffähig. Ferner sind derartige verknüpfte Schachtelungen meist nicht iterierbar. Diese Einschränkungen gelten bei der folgenden Methode nicht.
 
 

1.10.3 verknüpfte Schachtelungen (EXISTS)


Hier sind auch innere und äussere Anfrage verknüpft. Man interesiert sich aber nun in der subquery nicht für eine spezielle Spalte, sonder nur ob sie ein Ergebnis geliefert hat.  Als einfaches Beispiel kann man so auch wieder die Abfrage nach Büchern aus England lösen
 

SELECT TITLE FROM BOOKS WHERE EXISTS (
        SELECT * FROM PUBLISHER WHERE LAND='England' AND
        PUBLISHER.PUBLISHER_ID = BOOKS.PUBLISHER_ID );
 

Der Unterschied zu obiger Variante sind die unterschiedlichen Auswertungsreihenfolgen. Diese Variante entspricht dem Join und ist langsamer als vorhergehende zweistufige. Um ein Beispiel zu bekommen die mittels EXISTS über die Möglichkeiten des Joins hinausgehen erweitern wir die Buchhandlungsdatenbank um eine Tabelle LIEFERANT, die aus zwei Spalten besteht, ISBN und GROSSIST_ID und eine Zeile bedeutet, dass das entsprechende Buch von dem jeweiligen Grossisten geliefert wird. Eine zweite Tabelle GROSSIST soll zur GROSSIST_ID die notwendigen Daten enthalten. Das erste Beispiel, das noch per JOIN geht ist
 

SELECT ISBN FROM BOOKS WHERE EXISTS (
        SELECT * FROM LIEFERANT WHERE BOOKS.ISBN = LIEFERANT.ISBN);
 

auch die Verneinung geht, dann wären es Bücher die von keinem Grossisten geliefert werden und daher direkt vom Verlag zu bestellen sind:
 

SELECT ISBN FROM BOOKS WHERE NOT EXISTS (
        SELECT * FROM LIEFERANT WHERE BOOKS.ISBN = LIEFERANT.ISBN);
 

das ginge schon nicht mehr mit einem Join aber noch durch eine NOT IN Abfrage, die folgende kann aber nur mit EXISTS formuliert werden. Gesucht sind die Bücher die von allen Grossisten geliefert werden:
 
 

SELECT ISBN FROM BOOKS WHERE NOT EXISTS (
        SELECT * FROM GROSSIST  WHERE NOT EXISTS(
              SELECT * FROM LIEFERANT WHERE LIEFERANT.GROSSIST_ID = LIEFERANT.GROSSIST_ID
                            AND                                            LIEFERANT.ISBN = BOOKS.ISBN));
 

auch hier kann die innere Anfrage (dies sind Grossisten die ein Buch nicht liefern) als NOT IN formuliert werden. Insgesamt geht das nicht denn die innerste Suchbedingung greift auf alle drei SELECT Anweisungen zu, und das ist nur bei EXISTS erlaubt.
 
 

1.10.4 Mengentheoretische Verknüpfungen (UNION,INTERSECT,MINUS)

 

 
 
 
 
 

Bei den vorgestellten Methoden mittels subqueries Abfragen zu formulieren ging es oft um mengentheoretische Operation auf den Ergebnismengen. Es gibt auch die Möglichkeit diese Operationen direkt zu formulieren. Die entsprechendne Namen sind
 
 

UNION                                         Vereinigung
INTERSECT                                  Schnitt
MINUS oder EXCEPT                   Komplement


Der prinzpielle Aufbau ist:
 

SELECT Anweisung   Verknüpfung     SELECT Anweisung
 

dabei muss darauf geachtet werden, dass die Formate (Spalten Anzahl, Typ) kompatibel sind. Ein einfaches Beispiel wäre eine Liste der Geschäftspartner, dies sind entweder Verlage oder Grossisten

SELECT PUBLISHER_ID,NAME, ADRESS FROM PUBLISHER
UNION
SELECT GROSSIST_ID,NAME,ADRESS FROM GROSSIST;

das Ergebnis ist eine Tabelle mit dem gleichen Spaltennamen wie die erste Menge und den Einträgen aus beiden Tabellen. Voraussetzung ist die Kompatibilität, d.h. CHAR unterschiedlicher Länge ist kein Problem, aber dürfen z.B. nicht INTEGER und CHAR aufeinander treffen. Dies ist eine mengentheoretische Operation, d.h. im Ergebnis kommen keine doppelten Zeilen vor. Will man andere Spaltennnamen so muss man in der ersten SELECT Anweisung aliases definieren. Es ist klar, dass sämtliche mengentheoretische Verknüpfungen auch anders (Join, geschachtelt) umformuliert werden können.
 
 
 
 

1.11 Transaktionen

Transaktionen bieten die Möglichkeit, mehrere SQL-Anweisungen logisch zusammenzufassen, so daß entweder alle Anweisungen ausgeführt werden oder keine. Tritt bei einer Anweisung ein Fehler auf, oder wurden z.B. versehentlich falsche Werte gespeichert, können alle Anweisungen einer Transaktion rückgängig gemacht werden, solange diese Transaktion noch nicht bestätigt wurde. Dafür stehen die beiden speziellen Anweisungen

COMMIT WORK;

und

ROLLBACK WORK;

zur Verfügung, mit denen eine Transaktion entweder bestätigt oder rückgängig gemacht wird. Standardmäßig werden bei allen Datenbanken (erzeugt mit CREATE DATABASE) die SQL-Anweisungen innerhalb einer Transaktion ausgeführt.  D.h bei  keinen vorgenommenen COMMITS kann alles seit dem öffnen der Datenbank rückgängig gemacht werden. Lediglich bei Datenbanken, die explizit mit dem Zusatz NO LOG angelegt wurden, steht keine Transaktionsverwaltung zur Verfügung.  Bei diesen Datenbanken können die Anweisungen COMMIT WORK und ROLLBACK WORK nicht verwendet werden. Wird eine Transaktion über ROLLBACK WORK zurückgesetzt, gilt die Einschränkung, daß nur DML Anweisungen (INSERT,UPDATE, DELETE) auf reguläre Tabellen (keine Systemtabellen) rückgängig gemacht werden können. In einer Transaktion können Savepoints definiert werden, die es ermöglichen, daß durch eine ROLLBACK Anweisung nicht die gesamte Transaktion, sondern nur der Teil seit dem letzten Savepoint zurückgesetzt wird. Ein Savepoint wird durch einen eindeutigen Namen identifiziert und kann jederzeit über die Anweisung
 

SAVEPOINT savepoint-name;
 

definiert werden. Mit der Anweisung
 

ROLLBACK WORK TO SAVEPOINT savepoint-name;
 

wird die Transaktion bis zum Status bei der Definition des Savepoints zurückgesetzt. Es können beliebig viele Savepoints definiert werden. Beim Ende einer Transaktion (COMMIT/ROLLBACK) werden alle bestehenden Savepoints gelöscht. Ein einzelner Savepoint kann jederzeit über die Anweisung

RELEASE SAVEPOINT;

freigegeben werden.
 
 
 
 
 

-
-
-
 

1.12 Prozeduren


Damit handelt es sich um eine Erweiterung von SQL (query language) zu einer Programmiersprache. Man spricht von der procedure language (PL). Während die klassische Datenbankprogrammierung eine klare Trennung zwischen der Datenbank und der Anwendung vorsieht, ist es mit Hilfe der PL möglich, ganze Anwendungsteile in Form einer Stored Procedure in der Datenbank zu hinterlegen. Damit sind eine Reihe von Vorteilen verbunden, wie z.B.
 
 

      geringere Netzwerkbelastung im Client-Server Betrieb
      verbesserte Performance
      anwendungsweit einheitliche Implementierung von Algorithmen
      höhere Portabilität
 
 

Eine Stored Procedure ist eine benutzerdefinierte Routine  die von jedem Datenbankbenutzer über die SQL-Anweisungen CREATE
FUNCTION oder CREATE PROCEDURE angelegt werden kann. Der Benutzer muss hierzu Eigentümer der Datenbank sein oder das RESOURCE Recht in der aktuellen Datenbank haben. Routinen können aus SQL, PL oder einer Kombination aus SQL und PL-Anweisungen bestehen. Beim Anlegen der stored procedure wird diese kompiliert in der Datenbank abgelewgt. Der Unterschied zwischen PROCEDURE unD FUNCTION ist wie z.B. in PASCAL, dass eine PROCEDURE keinen Rückgabewert hat.
 
 

1.12.1 Aufbau einer Routine


Eine Routine wird durch einen Header eingeleitet. Der Header besteht aus dem Namen der Routine, einer optionalen Parameterliste und der RETURNS Klausel, wenn es sich um eine Funktion handelt.  Die Routine selbst besteht aus beliebig vielen Blöcken von PL- und/oder SQL-Anweisungen. Ein Block wird eingeleitet durch das Schlüsselwort BEGIN und endet mit dem Schlüsselwort END. Es kann nur einen äußeren BEGIN/END Block in einer Routine geben. Innerhalb dieses Blocks können jedoch weitere BEGIN/END
(geschachtelt) integriert werden. Besteht eine Routine aus genau einer (ausführbaren) PL- oder SQL-Anweisung, muß kein BEGIN/END Block definiert werden. Innerhalb eines BEGIN/END Blocks können deklarative und ausführbare Anweisungen vorkommen. Alle Deklarationen müssen am Anfang eines Blocks direkt hinter dem Schlüsselwort BEGIN stehen. Es stehen alle wesentlichen Programmiersprachenkonstrukte zur Verfügung: FOR/WHILE/CASE/Variablen/BREAK-CONTINUE/Funktionsaufrufe/..
 

Beispiel:

Das folgende Beispiel zeigt die CREATE FUNCTION Anweisung und den PL-Code zur Generierung der Funktion wochentag, wie sie in SQL ausgeführt werden kann:
 

CREATE FUNCTION wochentag(tagnr INTEGER)
        RETURNS CHAR(20);
BEGIN
        CASE tagnr
                WHEN 1 THEN RETURN ('Sonntag');
                WHEN 2 THEN RETURN ('Montag');
                WHEN 3 THEN RETURN ('Dientag');
                WHEN 4 THEN RETURN ('Mittwoch');
                WHEN 5 THEN RETURN ('Donnerstag');
                WHEN 6 THEN RETURN ('Freitag');
                WHEN 7 THEN RETURN ('Samstag');
                ELSE RETURN ('Unbekannt');
        END CASE
END
END FUNCTION;
 

In diesem Beispiel ist die Funktion wochentag eine ausschließlich mit PL-Anweisungen erstellte Funktion, die zu einer Nummer von 1-7 den entsprechenden Tag liefert.  Über die RETURNS Klausel wird der Datentyp und die Anzahl der Rückgabewerte festgelegt. In diesem Fall wird genau ein Wert vom Typ CHAR mit der maximalen Länge 20 zurückgegeben.  Innerhalb der CASE Anweisung sind die RETURN Anweisungen zur Rückgabe des Wertes angegeben.  In einer Funktion sind die RETURNS Klausel und mindestens eine RETURN Anweisung zwingender Bestandteil.

Erstellen einer Routine in Embedded SQL

Soll eine Routine über ein ESQLC-Programm erstellt werden, muß sich der PL-Code der Routine in einer Stringkonstanten, einer Hostvariablen oder einer Datei befinden. Die Routine kann dann über die CREATE PROCEDURE FROM Anweisung angelegt
werden.  Das folgende Beispiel zeigt das Anlegen der Funktion wochentag über eine Stringkonstante in einem ESQLC-Programm:
 

EXEC SQL CREATE FUNCTION FROM
'CREATE FUNCTION wochentag(tagnr INTEGER)
        RETURNS CHAR(20);
BEGIN
        CASE tagnr
                WHEN 1 THEN RETURN ('Sonntag');
                WHEN 2 THEN RETURN ('Montag');
                WHEN 3 THEN RETURN ('Dientag');
                WHEN 4 THEN RETURN ('Mittwoch');
                WHEN 5 THEN RETURN ('Donnerstag');
                WHEN 6 THEN RETURN ('Freitag');
                WHEN 7 THEN RETURN ('Samstag');
                ELSE RETURN ('Unbekannt');
        END CASE
END
END FUNCTION';
 

Wichtig ist hierbei, daß der Text der Routine eine vollständige CREATE FUNCTION oder CREATE PROCEDURE Anweisung enthalten muß.