13. Weiterführende Themen zu SQL
Gespeicherte Prozeduren erzeugen und verwenden
Trigger entwerfen und verwenden
SELECT-Befehle bei UPDATE und DELETE
Ziele
Im Verlauf der letzten zwölf Tage haben Sie alles Wesentliche kennengelernt, um leistungsfähige Abfragen zum Abrufen von Daten aus einer Datenbank zu schreiben. Außerdem wurden kurz die Aspekte des Datenbankentwurfs und der Datenbanksicherheit gestreift. Zu den weiterführenden Themen im Mittelpunkt des heutigen Tages gehören unter anderem ...
Die heutigen Beispiele arbeiten mit den Implementierungen PL/SQL von Oracle8 und Transact-SQL von Microsoft/Sybase SQL Server. Wir haben uns bemüht, nach Möglichkeit zu beiden SQL-Varianten Beispiele anzugeben. Allerdings müssen Sie weder auf Oracle8 noch auf SQL Server zurückgreifen. Wählen Sie das Datenbankprodukt, das am besten Ihren Anforderungen entspricht. (Wenn Sie dieses Buch lesen, um sich für ein Projekt in Ihrer Firma zu rüsten, haben Sie wahrscheinlich schon gar keine Wahl mehr.) Die meisten Beispiele dieses Buches lassen sich auf die bekannten Datenbank-Managementsysteme übertragen. Für das heute präsentierte Material gilt diese Feststellung nicht in vollem Umfang. Viele Anbieter unterstützen immer noch keine temporären Tabellen, gespeicherten Prozeduren und Trigger. Anhand der Dokumentation Ihres bevorzugten Datenbanksystems sollten Sie sich über die jeweiligen Möglichkeiten informieren. |
Temporäre Tabellen
Bei temporären Tabellen handelt es sich einfach um Tabellen, die in einer Datenbank nur vorübergehend existieren und automatisch gelöscht werden, wenn sich der Benutzer abmeldet oder die Verbindung zur Datenbank trennt. Transact-SQL erzeugt diese temporären Tabellen in der Datenbank tempdb. Diese Datenbank wird angelegt, wenn Sie SQL Server installieren. Zum Erstellen einer temporären Tabelle gibt es zwei Syntaxformen.
SYNTAX 1:
create table #Tabellenname (
Feld1 Datentyp,
...
...
...
Feldn Datentyp)
Syntax 1 erzeugt eine Tabelle in der Datenbank tempdb. Diese Tabelle wird mit einem eindeutigen Namen angelegt, der eine Kombination aus dem im Befehl CREATE TABLE angegebenen Tabellennamen und einer Datums-/Zeitangabe - einem sogenannten Zeitstempel - besteht. Eine temporäre Tabelle ist nur dem Erzeuger zugänglich. Beispielsweise können fünfzig Benutzer gleichzeitig die folgenden Befehle auslösen:
1> create table #ALBEN (
2> KUENSTLER char(30),
3> ALBUM_NAME char(50),
4> MEDIENTYP int)
5> go
Mit dem Nummernzeichen (#) vor dem Namen der Tabelle bezeichnet SQL Server eine temporäre Tabelle. Jeder der fünfzig Benutzer erhält praktisch eine private Tabelle zur eigenen Verwendung und kann die Datensätze dieser Tabelle aktualisieren, einfügen und löschen, ohne dabei befürchten zu müssen, daß andere Benutzer die Daten dieser Tabelle ungültig machen. Die Tabelle läßt sich wie gehabt mit dem folgenden Befehl löschen:
1> drop table #ALBEN
2> go
Die Tabelle wird automatisch gelöscht, wenn sich der Benutzer, der die Tabelle erzeugt hat, vom SQL Server abmeldet. Falls Sie diese Anweisung mit einer dynamischen SQL-Verbindung (beispielsweise mit der DB-Bibliothek von SQL Server) erzeugt haben, wird die Tabelle beim Schließen der dynamischen SQL-Verbindung gelöscht.
Syntax 2 zeigt eine andere Möglichkeit, eine temporäre Tabelle auf einem SQL Server zu erzeugen. Achten Sie genau auf die syntaktischen Unterschiede, da ein anderes Ergebnis als bei Syntax 1 entsteht.
SYNTAX 2:
create table tempdb..Tabellenname (
Feld1 Datentyp,
...
...
...
Feldn Datentyp)
Erzeugt man eine Tabelle nach dem Format von Syntax 2, wird die Tabelle immer in der Datenbank tempdb angelegt. Der Name dieser Tabelle hat das gleiche Format wie der Name der nach Syntax 1 erzeugten Tabelle. Der Unterschied besteht darin, daß die Tabelle nicht gelöscht wird, wenn die Verbindung des Benutzers zur Datenbank endet. Statt dessen muß der Benutzer explizit einen DROP-TABLE-Befehl ausführen, um diese Tabelle aus der Datenbank tempdb zu entfernen.
Eine andere Möglichkeit, die mit der Syntax create table tempdb..Tabellenname erzeugte Tabelle loszuwerden, ist das Herunterfahren und Neustarten des SQL Servers. Diese Methode entfernt alle temporären Tabellen aus der Datenbank tempdb. |
Die Beispiele 13.1 und 13.2 verdeutlichen für beide Syntaxformen die Tatsache, daß temporäre Tabellen tatsächlich nur vorübergehend existieren. Das sich anschließende Beispiel 13.3 zeigt einen gebräuchlichen Einsatzfall für temporäre Tabellen: das Zwischenspeichern von Daten, die eine Abfrage zurückgibt. Diese Daten lassen sich dann in anderen Abfragen benutzen.
Damit Sie diese Beispiele nachvollziehen können, müssen Sie zunächst eine Datenbank anlegen. Die Datenbank MUSIK wurde mit den folgenden Tabellen erstellt:
Diese Tabellen erzeugen Sie mit den folgenden SQL-Anweisungen:
1> create table KUENSTLER (
2> NAME char(30),
3> HEIMATORT char(40),
4> STIL char(20),
5> KUENSTLER_ID int)
6> go
1> create table MEDIUM (
2> MEDIENTYP int,
3> BESCHREIBUNG char(30),
4> PREIS float)
5> go
1> create table AUFNAHMEN (
2> KUENSTLER_ID int,
3> MEDIENTYP int,
4> TITEL char(50),
5> JAHR int)
6> go
Die Tabellen 13.1, 13.2 und 13.3 zeigen einige Beispieldaten für diese Tabellen. |
Tabelle 13.1: Die Tabelle KUENSTLER
NAME |
HEIMATORT |
STIL |
KUENSTLER_ID |
Soul Asylum |
Minneapolis |
Rock |
1 |
Maurice Ravel |
France |
Classical |
2 |
Dave Matthews Band |
Charlottesville |
Rock |
3 |
Vince Gill |
Nashville |
Country |
4 |
Oingo Boingo |
Los Angeles |
Pop |
5 |
Crowded House |
New Zealand |
Pop |
6 |
Mary Chapin-Carpenter |
Nashville |
Country |
7 |
Edward MacDowell |
U.S.A. |
Classical |
8 |
Tabelle 13.2: Die Tabelle MEDIUM
MEDIENTYP |
BESCHREIBUNG |
PREIS |
1 |
Record |
4.99 |
2 |
Tape |
9.99 |
3 |
CD |
13.99 |
4 |
CD-ROM |
29.99 |
5 |
DAT |
19.99 |
Tabelle 13.3: Die Tabelle AUFNAHMEN
KUENSTLER_ID |
MEDIENTYP |
TITEL |
JAHR |
1 |
2 |
Hang Time |
1988 |
1 |
3 |
Made to Be Broken |
1986 |
2 |
3 |
Bolero |
1990 |
3 |
5 |
Under the Table and Dreaming |
1994 |
4 |
3 |
When Love Finds You |
1994 |
5 |
2 |
Boingo |
1987 |
5 |
1 |
Dead Man's Party |
1984 |
6 |
2 |
Woodface |
1990 |
6 |
3 |
Together Alone |
1993 |
7 |
5 |
Come On, Come On |
1992 |
7 |
3 |
Stones in the Road |
1994 |
8 |
5 |
Second Piano Concerto |
1985 |
Beispiel 13.1
In der Datenbank tempdb kann man eine temporäre Tabelle erzeugen. Nach dem Einfügen eines Dummy-Datensatzes in diese Tabelle melden Sie sich ab. Nachdem Sie sich erneut in SQL Server angemeldet haben, versuchen Sie, den Dummy-Datensatz aus der temporären Tabelle zu löschen. Beachten Sie die Ergebnisse:
1> create table #ALBEN (
2> KUENSTLER char(30),
3> ALBUM_NAME char(50),
4> MEDIENTYP int)
5> go
1> insert #ALBEN values ('The Replacements', 'Pleased To Meet Me', 1)
2> go
Melden Sie sich nun wieder vom SQL Server ab, und trennen Sie die Verbindung mit dem Befehl EXIT (oder QUIT). Nachdem Sie sich wieder in der zuletzt verwendeten Datenbank angemeldet haben, probieren Sie den folgenden Befehl aus:
1> select * from #ALBEN
2> go
Diese Tabelle existiert nicht in der aktuellen Datenbank. |
Beispiel 13.2
Erstellen Sie nun die Tabelle gemäß Syntax 2:
1> create table tempdb..ALBEN (
2> KUENSTLER char(30),
3> ALBUM_NAME char(50),
4> MEDIENTYP int)
5> go
1> insert #ALBEN values ('The Replacements', 'Pleased To Meet Me', 1)
2> go
Nach Ab- und erneutem Anmelden wechseln Sie zur Datenbank, mit der Sie bei Ausführung des Befehls create table tempdb..ALBEN() gearbeitet haben. Führen Sie dann den folgenden Befehl aus:
1> select * from #ALBEN
2> go
Dieses Mal erhalten Sie die folgenden Ergebnisse:
KUENSTLER ALBUM_NAME MEDIENTYP
__________________________________________________________________________________
The Replacements Pleased To Meet Me 1
Beispiel 13.3
Dieses Beispiel zeigt eine gebräuchliche Verwendung von temporären Tabellen: Speichern der Ergebnisse von komplexen Abfragen für den Einsatz in späteren Abfragen.
1> create table #TEMP_INFO (
2> NAME char(30),
3> HEIMATORT char(40),
4> STIL char(20),
5> KUENSTLER_ID int)
6> insert #TEMP_INFO
7> select * from KUENSTLER where HEIMATORT = 'Nashville'
8> select AUFNAHMEN.* from AUFNAHMEN, KUENSTLER
9> where AUFNAHMEN.KUENSTLER_ID = #TEMP_INFO.KUENSTLER_ID
10> go
Der obige Befehlsstapel wählt die Aufnahmen für alle Künstler mit dem Heimatort Nashville aus.
Der nächste Befehl zeigt eine weitere Möglichkeit, den in Beispiel 13.3 verwendeten Satz von SQL-Anweisungen zu formulieren:
1> select KUENSTLER.* from KUENSTLER, AUFNAHMEN where KUENSTLER.HEIMATORT = 'Nashville'
2> go
Cursor
Ein Datenbankcursor läßt sich mit dem Cursor einer Textverarbeitung vergleichen, der die aktuelle Position auf dem Bildschirm anzeigt. Wenn man die Pfeiltasten nach oben oder unten betätigt, bewegt sich der Cursor um eine Zeile nach oben bzw. unten. Andere Tasten wie (Bild½) oder (Bild¼) bewirken einen Sprung von mehreren Zeilen in die entsprechende Richtung. In der gleichen Weise funktionieren Datenbankcursor.
Mit einem Datenbankcursor wählt man eine Gruppe von Daten aus, scrollt durch die Gruppe der Datensätze (auch Recordset genannt) und untersucht die einzelne Datenzeile, auf die der Cursor zeigt. Man kann eine Kombination von lokalen Variablen und einen Cursor verwenden, um jeden Datensatz einzeln zu untersuchen und alle erforderlichen externen Operationen auszuführen, bevor man zum nächsten Datensatz weitergeht.
Ein anderer häufiger Einsatzfall der Cursor ist die Speicherung von Abfrageergebnissen für spätere Verwendung. Die Ergebnismenge eines Cursors wird aus der Ergebnismenge einer SELECT-Abfrage gebildet. Wenn Ihre Anwendung oder Prozedur wiederholt auf eine Gruppe von Datensätzen zugreifen muß, geht es schneller, einen Cursor einmalig zu erzeugen und ihn mehrmals wiederzuverwenden als wiederholt die Abfrage der Datenbank auszuführen. (Darüber hinaus hat man den Vorteil, daß man mit einem Cursor durch die Ergebnismenge der Abfrage schneller scrollen kann.)
Um einen Datenbankcursor zu erzeugen, zu verwenden und zu schließen, führen Sie die folgenden Schritte aus:
1. Erzeugen Sie den Cursor.
2. Öffnen Sie den Cursor für die Verwendung innerhalb der Prozedur oder Anwendung.
3. Holen Sie die Daten eines Datensatzes zeilenweise, bis das Ende der Datensätze im Cursor erreicht ist.
4. Schließen Sie den Cursor, wenn Sie die Arbeit damit beendet haben.
5. Geben Sie den Cursor frei, um ihn vollständig zu verwerfen.
Einen Cursor erzeugen
In Transact-SQL erzeugen Sie einen Cursor mit der folgenden Syntax:
declare Cursorname cursor
for Selectanweisung
[for {read only | update [of Spaltenliste]}]
In Oracle8 sieht die SQL-Syntax zum Erzeugen eines Cursors folgendermaßen aus:
DECLARE Cursorname CURSOR
FOR {SELECT Befehl | Anweisungsname | Blockname}
Durch Ausführung der Anweisung DECLARE Cursorname CURSOR haben Sie die Ergebnismenge des Cursors definiert, die für alle Ihre Cursoroperationen verwendet wird. Ein Cursor besteht aus zwei wichtigen Teilen: der Ergebnismenge des Cursors und der Cursorposition.
Die folgende Anweisung erzeugt einen Cursor auf der Basis der Tabelle KUENSTLER:
1> create KUENSTLER_CURSOR cursor
2> for select * from KUENSTLER
3> go
Nun verfügen Sie über ein einfaches Cursorobjekt namens KUENSTLER_CURSOR, das alle Datensätze in der Tabelle KUENSTLER enthält. Zunächst müssen Sie aber den Cursor öffnen. |
Einen Cursor öffnen
Einen Cursor öffnet man mit dem folgenden Befehl:
open Cursorname
Die Ausführung der folgenden Anweisung öffnet KUENSTLER_CURSOR:
1> open KUENSTLER_CURSOR
2> go
Jetzt können Sie den Cursor verwenden, um durch die Ergebnismenge zu scrollen.
Einen Cursor scrollen
Um durch die Ergebnismenge eines Cursors zu scrollen, bietet Transact-SQL den folgenden FETCH-Befehl.
fetch Cursorname [into Fetch_Zielliste]
Oracle-SQL stellt die folgende Syntax bereit:
FETCH Cursorname {INTO : Hostvariable
[[INDICATOR] : Indikatorvariable]
[, : Hostvariable
[[INDICATOR] : Indikatorvariable] ]...
| USING DESCRIPTOR Deskriptor
Bei jeder Ausführung des FETCH-Befehls wandert der Cursorzeiger um eine Zeile durch die Ergebnismenge weiter. Falls gewünscht, kann man die Daten aus jeder Zeile in die Variablen der Fetch_Zielliste holen.
Die folgenden Anweisungen holen die Daten aus der Ergebnismenge KUENSTLER_CURSOR und geben die Daten an die Programmvariablen zurück:
1> declare @NAME char(30)
2> declare @HEIMATORT char(40)
3> declare @STIL char(20)
4> declare @KUENSTLER_ID int
5> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
6> print @NAME
7> print @HEIMATORT
8> print @STIL
9> print char(@KUENSTLER_ID)
10> go
Mit Hilfe der WHILE-Schleife (siehe Tag 19) können Sie nacheinander die gesamte Ergebnismenge durchlaufen. Woher weiß man aber, wann das Ende der Datensätze erreicht ist?
Den Status eines Cursors testen
In Transact-SQL läßt sich zu jedem Zeitpunkt der Status des Cursors testen. Für diesen Zweck werden zwei globale Variablen verwaltet: @@fetch_status und @@rowcount.
Die Variable @@fetch_status liefert Statusinformationen, die sich auf die zuletzt ausgeführte FETCH-Anweisung beziehen. Diese Variable enthält einen von drei Werten. Die folgende Tabelle entstammt der Dokumentation zu Transact-SQL:
Status |
Bedeutung |
0 |
FETCH-Anweisung erfolgreich abgeschlossen. |
-1 |
FETCH-Anweisung fehlerhaft oder Zeile außerhalb der Ergebnismenge. |
-2 |
Keine Daten mehr in der Ergebnismenge. |
Die Variable @@rowcount enthält die Anzahl der Zeilen, die aus der Ergebnismenge des Cursors bis zur vorherigen FETCH-Anweisung zurückgegeben wurden. Anhand dieser Zahl kann man die Anzahl der Datensätze in einer Ergebnismenge des Cursors bestimmen.
Der folgende Code ergänzt das bei der Behandlung der FETCH-Anweisung gezeigte Beispiel. Jetzt kommt die WHILE-Schleife in Verbindung mit der Variablen @@fetch_status zum Einsatz, um durch den Cursor zu scrollen:
1> declare @NAME char(30)
2> declare @HEIMATORT char(40)
3> declare @STIL char(20)
4> declare @KUENSTLER_ID int
5> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
6> while (@@fetch_status = 0)
7> begin
8> print @NAME
9> print @HEIMATORT
10> print @STIL
11> print char(@KUENSTLER_ID)
12> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
13> end
14> go
Nun verfügen Sie über einen voll funktionsfähigen Cursor! Der einzige noch verbleibende Schritt ist das Schließen des Cursors. |
Einen Cursor schließen
Das Schließen eines Cursors ist eine sehr einfache Angelegenheit. Die entsprechende Anweisung lautet:
close Cursorname
Der Cursor existiert trotzdem weiter. Man muß ihn allerdings erneut öffnen. Beim Schließen eines Cursors wird praktisch nur die Ergebnismenge geschlossen, nicht aber die Existenz des Cursors aufgehoben. Wenn man die Arbeit mit einem Cursor im Ganzen abgeschlossen hat, gibt der Befehl DEALLOCATE den für einen Cursor zugeordneten Speicher frei und erlaubt damit die erneute Verwendung des Cursornamens. Die Syntax der DEALLOCATE-Anweisung lautet:
deallocate cursor Cursorname
Beispiel 13.4 verdeutlicht den vollständigen Ablauf mit Erstellen, Verwenden und Schließen eines Cursors in der Sprache Transact-SQL.
Beispiel 13.4
1> declare @NAME char(30)
2> declare @HEIMATORT char(40)
3> declare @STIL char(20)
4> declare @KUENSTLER_ID int
5> create KUENSTLER_CURSOR cursor
6> for select * from KUENSTLER
7> open KUENSTLER_CURSOR
8> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
9> while (@@fetch_status = 0)
10> begin
11> print @NAME
12> print @HEIMATORT
13> print @STIL
14> print char(@KUENSTLER_ID)
15> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
16> end
17> close KUENSTLER_CURSOR
18> deallocate cursor KUENSTLER_CURSOR
19> go
Die folgende Ausgabe zeigt lediglich Beispieldaten. |
Soul Asylum Minneapolis Rock 1
Maurice Ravel France Classical 2
Dave Matthews Band Charlottesville Rock 3
Vince Gill Nashville Country 4
Oingo Boingo Los Angeles Pop 5
Crowded House New Zealand Pop 6
Mary Chapin-Carpenter Nashville Country 7
Edward MacDowell U.S.A. Classical 8
Gültigkeitsbereich von Cursor
Im Gegensatz zu Tabellen, Indizes und anderen Objekten wie Trigger und gespeicherten Prozeduren existieren Cursor nach deren Erzeugung nicht als Datenbankobjekte. Statt dessen haben Cursor einen eingeschränkten Gültigkeitsbereich.
Ein Cursor läßt sich in drei Bereichen erzeugen:
Gespeicherte Prozeduren erzeugen und verwenden
Das Konzept der gespeicherten Prozeduren (oder Stored Procedures) muß der professionelle Datenbankprogrammierer auf jeden Fall beherrschen. Gespeicherte Prozeduren sind Funktionen, die - möglicherweise große - Gruppierungen von SQL-Anweisungen enthalten und wie Funktionen in C, Fortran oder Visual Basic aufgerufen und ausgeführt werden. Eine gespeicherte Prozedur sollte einen logischen Satz von Befehlen verkapseln, die man häufig ausführt (beispielsweise komplexe Sätze von Abfragen, Aktualisierungen oder Einfügungen). Mit gespeicherten Prozeduren ruft der Programmierer einfach die gespeicherte Prozedur als Funktion auf, statt wiederholt die Anweisungen innerhalb der gespeicherten Prozedur auszuführen. Daneben weisen gespeicherte Prozeduren zusätzliche Vorteile auf.
In den späten 80er Jahren war Sybase mit seinem Produkt SQL Server Vorreiter bei gespeicherten Prozeduren. Diese Prozeduren wurden erstellt und dann als Teil einer Datenbank gespeichert - genau wie man Tabellen und Indizes innerhalb einer Datenbank speichert. In Transact-SQL kann man sowohl Eingabe- als auch Ausgabeparameter in den Aufrufen von gespeicherten Prozeduren übergeben. Damit lassen sich allgemeingültige Prozeduren erstellen, die man mit den übergebenen Variablen an den jeweiligen Einsatzfall anpaßt.
Einen der größten Vorteile der gespeicherten Prozeduren bietet die Art ihrer Ausführung. Wenn man einen großen Stapel von SQL-Anweisungen auf einem Datenbank-Server über ein Netzwerk ausführt, steht Ihre Anwendung in einem ständigen Datenaustausch mit dem Server, wodurch sich schnell eine erhebliche Netzbelastung ergeben kann. Sind mehrere Benutzer an diesem Datenaustausch beteiligt, geht die Leistung des Netzwerks und des Datenbank-Servers zunehmend zurück.
Nach Ausführung der gespeicherten Prozedur laufen die SQL-Anweisungen nacheinander auf dem Datenbank-Server ab. Meldungen oder Daten werden an den Computer des Benutzers nur zurückgegeben, wenn die Prozedur abgeschlossen ist. Dieses Verfahren verbessert die Leistung und bietet daneben weitere Vorteile. Die Datenbank-Engines kompilieren die gespeicherten Prozeduren bei ihrer erstmaligen Verwendung. Das kompilierte Abbild wird auf dem Server mit der Prozedur gespeichert. Demzufolge müssen Sie nicht jedesmal die SQL-Anweisungen bei der Ausführung optimieren, was ebenso die Leistungsbilanz verbessert.
Mit der folgenden Syntax in der Sprache Transact-SQL erzeugen Sie eine gespeicherte Prozedur:
create procedure Prozedurname
[[(]@Parametername
Datentyp [(Laenge) | (Genauigkeit [, Skala])
[= default][output]
[, @Parametername
Datentyp [(Laenge) | (Genauigkeit [, Skala])
[= default][output]]...[)]]
[with recompile]
as SQL_Anweisungen
Der folgende EXECUTE-Befehl führt die Prozedur aus:
execute [@Rueckgabestatus = ]
Prozedurname
[[@Parametername =] Wert |
[@Parametername =] @Variable [output]...]]
[with recompile]
Beispiel 13.5
Dieses Beispiel erzeugt eine einfache Prozedur und verwendet dabei den Inhalt von Beispiel 13.4.
1> create procedure PRINT_KUENSTLER_NAME
2> as
3> declare @NAME char(30)
4> declare @HEIMATORT char(40)
5> declare @STIL char(20)
6> declare @KUENSTLER_ID int
7> create KUENSTLER_CURSOR cursor
8> for select * from KUENSTLER
9> open KUENSTLER_CURSOR
10> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
11> while (@@fetch_status = 0)
12> begin
13> print @NAME
14> fetch KUENSTLER_CURSOR into @NAME, @HEIMATORT, @STIL, @KUENSTLER_ID
15> end
16> close KUENSTLER_CURSOR
17> deallocate cursor KUENSTLER_CURSOR
18> go
Nun können Sie die Prozedur PRINT_KUENSTLER_NAME mit der Anweisung EXECUTE ausführen:
1> execute PRINT_KUENSTLER_NAME
2> go
Soul Asylum
Maurice Ravel
Dave Matthews Band
Vince Gill
Oingo Boingo
Crowded House
Mary Chapin-Carpenter
Edward MacDowell
Beispiel 13.5 zeigt nur eine kleine gespeicherte Prozedur. In der Praxis kann eine gespeicherte Prozedur viele Anweisungen enthalten. Damit muß man nicht jede Anweisung einzeln ausführen. |
Parameter gespeicherter Prozeduren
Beispiel 13.5 hat als wichtigen ersten Schritt die Verwendung der einfachsten CREATE PROCEDURE-Anweisung gezeigt. Aus der hier angegebenen Syntax geht aber hervor, daß zur Anweisung CREATE PROCEDURE mehr als das in Beispiel 13.5 Gezeigte gehört. Gespeicherte Prozeduren übernehmen auch Eingabeparameter an die enthaltenen SQL-Anweisungen. Außerdem lassen sich Daten aus einer gespeicherten Prozedur per Ausgabeparameter zurückgeben.
Die Namen der Eingabeparameter müssen mit dem Symbol @ beginnen und einen gültigen Transact-SQL-Datentyp aufweisen. Bei Ausgabeparametern ist ebenfalls das Symbol @ als erstes Zeichen erforderlich. Zusätzlich muß man hinter den Namen der Ausgabeparameter das Schlüsselwort OUTPUT angeben. (Dieses Schlüsselwort ist auch zu schreiben, wenn man die gespeicherte Prozedur ausführt.)
Beispiel 13.6 zeigt die Verwendung von Eingabeparametern an eine gespeicherte Prozedur.
Beispiel 13.6
Die folgende gespeicherte Prozedur wählt die Namen aller Künstler aus, die ihre Aufnahmen auf CD veröffentlicht haben:
1> create procedure NAMEN_UND_MEDIEN @BESCHREIBUNG char(30)
2> as
3> select KUENSTLER.NAME from KUENSTLER, MEDIUM, AUFNAHMEN
4> where MEDIUM.BESCHREIBUNG = @BESCHREIBUNG and
5> MEDIUM.MEDIENTYP = AUFNAHMEN.MEDIENTYP and
6> AUFNAHMEN.KUENSTLER_ID = KUENSTLER.KUENSTLER_ID
7> go
1> execute NAMEN_UND_MEDIEN 'CD'
2> go
Die Ausführung dieser Anweisung liefert die folgenden Datensätze zurück:
NAME
Soul Asylum
Maurice Ravel
Vince Gill
Crowded House
Mary Chapin-Carpenter
Beispiel 13.7
Dieses Beispiel zeigt die Verwendung von Ausgabeparametern. Diese Funktion übernimmt den Heimatort des Künstlers als Eingabe und gibt den Namen des Künstlers als Ausgabe zurück:
1> create procedure HEIMATORT_UND_NAME @HEIMATORT char(40), @NAME char(30) output
2> as
3> select @NAME = NAME from KUENSTLER where HEIMATORT = @HEIMATORT
4> go
1> declare @RUECKGABE_NAME char(30)
2> execute HEIMATORT_UND_NAME 'Los Angeles', @RUECKGABE_NAME = @NAME output
3> print @NAME
4> go
Oingo Boingo
Eine gespeicherte Prozedur entfernen
Bisher haben Sie wahrscheinlich nur eine begründete Vermutung, wie man eine gespeicherte Prozedur wieder los wird. Wenn Sie auf den Befehl DROP getippt haben, liegen Sie genau richtig. Die folgende Anweisung entfernt eine gespeicherte Prozedur aus der Datenbank:
drop procedure Prozedurname
Der Befehl DROP kommt häufig zum Einsatz: Bevor sich eine gespeicherte Prozedur erneut erstellen läßt, muß man nämlich die alte Prozedur mit ihrem Namen löschen. Die Erfahrung zeigt, daß eine erstellte Prozedur selten ohne Modifikationen bleibt. In der Tat treten oftmals Fehler in den Anweisungen auf, aus denen sich die Prozedur zusammensetzt. Es empfiehlt sich, gespeicherte Prozeduren mit Hilfe einer SQL-Skript-Datei, die alle Anweisungen enthält, zu erstellen. Diese Skript-Datei können Sie über Ihren Datenbank-Server starten, um die gewünschten Anweisungen auszuführen und Ihre Prozeduren neu aufzubauen. Die SQL-Skripts lassen sich dabei mit gebräuchlichen Texteditoren wie vi oder dem Windows-Editor erstellen und speichern. Beim Starten dieser Skripts müssen Sie aber daran denken, immer die Prozedur, Tabelle usw. aus der Datenbank zu löschen, bevor Sie eine neue erstellen. Wenn Sie den Befehl DROP vergessen, erhalten Sie Fehlermeldungen.
Die folgende Syntax wird oft in SQL-Server-Skripts verwendet, bevor man ein Datenbankobjekt erzeugt:
if exists (select * from sysobjects where NAME = 'Prozedurname')
begin
drop procedure Prozedurname
end
go
create procedure Prozedurname
as
...
...
...
Diese Befehle prüfen anhand der Tabelle SYSOBJECTS (wo die Datenbankinformationen in SQL Server abgelegt sind), ob das Objekt existiert. Wenn das der Fall ist, wird es gelöscht, bevor das neue erstellt wird. Man erspart sich auf lange Sicht viel Zeit (und viele mögliche Fehler), wenn man Skript-Dateien schreibt und die obigen Schritte befolgt.
Gespeicherte Prozeduren verschachteln
Gespeicherte Prozeduren lassen sich im Sinne modularer Programme auch verschachteln. Eine gespeicherte Prozedur kann eine andere gespeicherte Prozedur aufrufen, die wiederum eine andere gespeicherte Prozedur aufrufen kann usw. Das Verschachteln gespeicherter Prozeduren bietet sich aus mehreren Gründen an:
In verschachtelten gespeicherten Prozeduren sind alle Variablen oder Datenbankobjekte, die man in einer Prozedur erzeugt, allen von dieser Prozedur aufgerufenen Prozeduren zugänglich. Alle lokalen Variablen oder temporären Objekte (wie etwa temporäre Tabellen) werden am Ende der gespeicherten Prozedur, die diese Elemente erzeugt hat, gelöscht.
Bei der Vorbereitung großer SQL-Skripts kann es zu Problemen bei der Referenzierung von Tabellen oder Datenbankobjekten kommen. Bevor man gespeicherte Prozeduren aufrufen kann, muß man sie zunächst erzeugen. Allerdings kann die aufrufende Prozedur temporäre Tabellen oder Cursor erzeugen, auf die die aufgerufenen gespeicherten Prozeduren zurückgreifen. Die später in der Skript-Datei erzeugten temporären Tabellen oder Cursor sind aber den aufgerufenen Prozeduren noch nicht bekannt. Dieses Problem läßt sich am einfachsten umgehen, indem man die temporären Objekte erzeugt, bevor man die gespeicherten Prozeduren erstellt. Dann löscht man die temporären Elemente (in der Skript-Datei), bevor sie erneut in der gespeicherten Prozedur erzeugt werden. Bringt Sie das etwas durcheinander? Beispiel 13.8 sollte Ihnen diesen Prozeß verdeutlichen.
1> create procedure Beispiel13_8b
2> as
3> select * from #Temp_Tabelle
4> go
1> create procedure Beispiel13_8a
2> as
3> create #Temp_Tabelle (
4> DATEN char(20),
5> ZAHLEN int)
6> execute Beispiel13_8b
7> drop table #Temp_Tabelle
8> go
Prozedur Beispiel13_8b verwendet die temporäre Tabelle #Temp_Tabelle. Allerdings wird #Temp_Tabelle erst später erzeugt (in der Prozedur Beispiel13_8a). Das führt zu einem Fehler beim Erstellen der Prozedur. Da aber nun Prozedur Beispiel13_8b nicht erzeugt wird (infolge der fehlenden Tabelle #Temp_Tabelle), wird die Prozedur Beispiel13_8a ebenfalls nicht erstellt (weil das Erstellen der Prozedur Beispiel13_8b gescheitert ist). |
Der folgende Code beseitigt dieses Problem. Zunächst wird die Tabelle #Temp_Tabelle erzeugt. Daran schließt sich das Erstellen der ersten Prozedur an. Dann löscht man #Temp_Tabelle und erstellt erst danach die zweite Prozedur:
1> create #Temp_Tabelle (
2> DATEN char(20),
3> ZAHLEN int)
4> go
1> create procedure Beispiel13_8b
2> as
3> select * from #Temp_Tabelle
4> go
1> drop table #Temp_Tabelle
2> go
1> create procedure Beispiel13_8a
2> as
3> create #Temp_Tabelle (
4> DATEN char(20),
5> ZAHLEN int)
6> execute Beispiel13_8b
7> drop table #Temp_Tabelle
8> go
Trigger entwerfen und verwenden
Ein Trigger ist ein spezieller Typ einer gespeicherten Prozedur und wird als Reaktion auf eine der folgenden drei Operationen ausgelöst bzw. ausgeführt:
Die Syntax zum Erzeugen eines Triggers sieht in Transact-SQL wie folgt aus:
create trigger Triggername
on Tabellenname
for {insert, update, delete}
as SQL_Anweisungen
CREATE [OR REPLACE] TRIGGER [Schema.]Triggername
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF Spalte[, Spalte]...]}
[OR {DELETE | INSERT | UPDATE [OF Spalte [, Spalte] ...]}]...
ON [Schema.]Tabelle
[[REFERENCING { OLD [AS] alt [NEW [AS] neu]
| NEW [AS] neu [OLD [AS] alt]}]
FOR EACH ROW
[WHEN (Bedingung)] ]
PL/SQL-Block...
Mit Triggern läßt sich insbesondere die referentielle Integrität durchsetzen, wie es bereits Lektion 9 zum Thema Erstellen von Tabellen erwähnt hat. Referentielle Integrität ist ein Regelsystem, das die Gültigkeit der Beziehungen zwischen Datensätzen verknüpfter Tabellen sicherstellt. Nehmen wir an, ein Benutzer gibt den folgenden Befehl ein:
1> insert AUFNAHMEN values (12, 'The Cross of Changes', 3, 1994)
2> go
Diese absolut gültige SQL-Anweisung fügt einen neuen Datensatz in die Tabelle AUFNAHMEN ein. Ein kurzer Blick auf die Tabelle KUENSTLER zeigt aber, daß es keine KUENSTLER_ID = 12 gibt. Ein Benutzer mit INSERT-Privilegien für die Tabelle AUFNAHMEN kann damit die referentielle Integrität zerstören. |
Trigger und Transaktionen
Die innerhalb eines Triggers ausgeführten Aktionen werden implizit als Teil einer Transaktion ausgeführt. Die gesamte Folge der Ereignisse sieht folgendermaßen aus:
1. Eine BEGIN TRANSACTION-Anweisung wird implizit ausgelöst (bei Tabellen mit Triggern).
2. Es treten die Operationen zum Einfügen, Aktualisieren oder Löschen auf.
3. Der Trigger wird aufgerufen, seine Anweisungen werden ausgeführt.
4. Der Trigger macht die Transaktion entweder rückgängig, oder die Transaktion wird explizit bestätigt.
Beispiel 13.9
Dieses Beispiel zeigt die Lösung für das weiter vorn erwähnte Problem bei der Aktualisierung der Tabelle AUFNAHMEN.
1> create trigger KUENSTLER_PRUEFEN
2> on AUFNAHMEN
3> for insert, update as
4> if not exists (select * from KUENSTLER, AUFNAHMEN
5> where KUENSTLER.KUENSTLER_ID = AUFNAHMEN.KUENSTLER_ID)
6> begin
7> print 'Illegale KUENSTLER_ID!'
8> rollback transaction
9> end
10> go
Beispiel 13.10a
1> create trigger KUENSTLER_loeschen
2> on AUFNAHMEN
3> for delete as
4> begin
5> delete from KUENSTLER where KUENSTLER_ID not in
6> (select KUENSTLER_ID from AUFNAHMEN)
7> end
8> go
Beispiel 13.10b
1> create trigger KUENSTLER_loeschen
2> on AUFNAHMEN
3> for delete as
4> begin
5> delete KUENSTLER from KUENSTLER, deleted
6> where KUENSTLER.KUENSTLER_ID = deleted.KUENSTLER_ID
7> end
8> go
Einschränkungen bei Triggern
Beim Einsatz von Triggern sind die folgenden Einschränkungen zu beachten:
Verschachtelte Trigger
Trigger lassen sich auch verschachteln. Nehmen wir an, daß Sie einen Trigger erzeugt haben, der zum Beispiel bei einem Löschvorgang ausgelöst wird. Wenn dieser Trigger dann selbst einen Datensatz löscht, kann man den Datenbank-Server so einrichten, daß er einen anderen Trigger auslöst. Diese Methode führt natürlich zu einer Schleife, die erst endet, wenn alle Datensätze in der Tabelle gelöscht sind (oder irgendwelche anderen internen Trigger-Bedingungen erfüllt sind). Das Verschachteln ist allerdings nicht von vornherein möglich. Man muß die Umgebung dafür einrichten. Sehen Sie bitte in der Dokumentation zu Ihrem Datenbank-Server nach, um nähere Informationen zu diesem Thema zu erhalten.
SELECT-Befehle bei UPDATE und DELETE
Die folgenden Beispiele zeigen komplexe SQL-Anweisungen, die mit UPDATE und DELETE arbeiten:
SQL> UPDATE MITARBEITER_TBL
SET NACHNAME = 'SMITH'
WHERE EXISTS (SELECT MITARBEITER_ID
FROM LOHN_TBL
WHERE MITARBEITER_ID = 2);
1 Zeile wurde aktualisiert.
Die Tabelle MITARBEITER_TBL enthielt einen falschen Mitarbeiternamen. Wir haben die Tabelle MITARBEITER_TBL nur aktualisiert, wenn die Tabelle mit der Gehaltsliste (LOHN_TBL) die korrekte ID aufweist. |
SQL> UPDATE MITARBEITER_TBL
SET STUNDENLOHN = STUNDENLOHN * 1.1
WHERE MITARBEITER_ID = (SELECT MITARBEITER_ID
FROM LOHN_TBL
WHERE MITARBEITER_ID = '222222222');
1 Zeile wurde aktualisiert.
Wir haben den Stundenlohn um 10 Prozent erhöht. |
SQL> DELETE FROM MITARBEITER_TBL
WHERE MITARBEITER_ID = (SELECT MITARBEITER_ID
FROM LOHN_TBL
WHERE MITARBEITER_ID = '222222222');
1 Zeile wurde gelöscht.
Hier haben wir den Mitarbeiter mit der ID 222222222 gelöscht. |
SELECT-Anweisungen vor der Implementierung testen
Wenn man einen Bericht erzeugt (zum Beispiel mit SQL*Plus) und der Bericht ziemlich umfangreich ist, möchte man die Abstände, Spalten und Titel prüfen, bevor man das Programm ausführt und eine Menge Zeit verschwendet. Die Testläufe kann man in einfacher Weise abkürzen, wenn man Anweisungen wie where rownum < 3 in die SQL-Anweisung einbaut:
SQL> select *
from MITARBEITER_TBL
where rownum < 5;
Die Ausgabe liefert nur die ersten vier Zeilen der Tabelle. Hieraus kann man sicherlich schon ersehen, ob die Rechtschreibung korrekt ist und die Abstände den eigenen Vorstellungen entsprechen. Ohne die Einschränkung auf die ersten Zeilen könnte der Bericht Hunderte oder Tausende von Zeilen enthalten, bevor man eine falsche Schreibweise oder unpassende Abstände entdeckt. |
Einen wesentlichen Teil - schätzungsweise 50 Prozent - Ihrer Arbeitszeit sind Sie damit beschäftigt, die Wünsche und den wirklichen Bedarf der Kunden herauszufinden. Gute Kommunikationsfertigkeiten und eine Kenntnis der jeweiligen Firma, für die Sie arbeiten, ergänzen sich mit Ihren Programmierfähigkeiten. Nehmen wir zum Beispiel an, daß Sie als Programmierer für einen Autohändler arbeiten. Der Gebrauchtwagenhändler möchte wissen, wie viele Autos er bei einer anstehenden Inventur zur Verfügung hat. (Zu sich selbst) denken Sie: Zähl' sie doch einfach. Gut, er hat Sie gefragt, wie viele Autos er hat. Aber Sie wissen, daß der Manager für eine Inventur eigentlich die Anzahl der Typen (Personenwagen, Lastwagen), Modelle, Baujahre usw. braucht. Sollten Sie ihm geben, wonach er Sie gebeten hat, und Ihre Zeit damit verschwenden, oder sollten Sie ihm geben, was er wirklich braucht? |
Eingebettetes SQL
Dieses Buch bezieht sich mit dem Begriff eingebettetes SQL
In einem anderen Sinne bezieht sich eingebettetes SQL häufig auf das, was man technisch als statisches SQL bezeichnet.
Statisches und dynamisches SQL
Statisches SQL bedeutet, daß SQL-Anweisungen direkt in den Programmcode eingebettet sind. Dieser Code läßt sich nicht zur Laufzeit modifizieren. In der Tat erfordern die meisten Implementierungen von statischem SQL die Verwendung eines Präcompilers, der die SQL-Anweisungen zur Laufzeit festlegt. Sowohl Oracle als auch Informix haben zu ihren Datenbanksystemen Pakete für statisches SQL entwickelt. Diese Produkte enthalten Präcompiler für mehrere Sprachen, einschließlich der folgenden:
Zu den Vorteilen von statischem SQL gehören:
Nachteile von statischem SQL sind:
Der erstellte Code läßt eine gewisse Ähnlichkeit der SQL-Anweisungen mit der Programmiersprache C (oder der jeweils verwendeten Sprache) erkennen. Programmvariablen werden mit einem Präcompiler-Befehl an Datenbankfelder gebunden. Beispiel 13.11 zeigt ein einfaches Beispiel für statischen SQL-Code.
Mit dynamischem SQL kann der Programmierer dagegen eine SQL-Anweisung zur Laufzeit aufbauen und diese Anweisung an die Datenbank-Engine übergeben. Die Engine gibt dann die Daten an Programmvariablen zurück, die ebenfalls zur Laufzeit gebunden werden. Diesem Thema widmet sich Tag 14 im Detail.
Beispiel 13.11
Dieses Beispiel zeigt statisches SQL in einer C-Funktion. Beachten Sie bitte, daß die hier verwendete Syntax nicht mit dem ANSI-Standard konform geht. Die Syntax für dieses statische SQL entspricht eigentlich überhaupt keinem kommerziellen Produkt, auch wenn sie eine gewisse Ähnlichkeit mit den meisten dieser Produkte aufweist.
BOOL PRINT_MITARBEITER_INFO (void)
{
int Alt = 0;
char Name[41] = '\0';
char Adresse[81] = '\0';
/* Alle Felder binden, die in eine Programmvariable selektiert werden. */
#SQL BIND(ALT, Alt)
#SQL BIND(NAME, Name);
#SQL BIND(ADRESSE, Adresse);
/* Die obigen Anweisungen "binden" Felder aus der Datenbank an Variablen im Programm.
Nach Abfrage der Datenbank werden die Datensätze durchlaufen und
auf dem Bildschirm ausgegeben. */
#SQL SELECT ALT, NAME, ADRESSE FROM MITARBEITER;
#SQL FIRST_RECORD
if (Alt == NULL)
{
return FALSE;
}
while (Alt != NULL)
{
printf("ALT = %d\n", Alt);
printf("NAME = %s\n", Name);
printf("ADRESSE = %s\n", Adresse);
#SQL NEXT_RECORD
}
return TRUE;
}
Falls Sie noch nie ein C-Programm gesehen haben, brauchen Sie sich um die in Beispiel 13.11 verwendete Syntax keine Sorgen zu machen. (Wie bereits erwähnt, ist die Syntax für statisches SQL nur ein Pseudocode. Die tatsächliche Syntax entnehmen Sie bitte der jeweiligen SQL-Dokumentation für statisches SQL.)
Programmieren mit SQL
Bisher haben wir zwei Einsatzfälle für die Programmierung mit SQL kennengelernt. Die ersten zwölf Lektionen dieses Buchs sind darauf eingegangen, wie man mit SQL Abfragen schreibt und Daten modifiziert. Weiterhin lassen sich SQL-Anweisungen in den Code von Sprachen der dritten oder vierten Generation einbetten. Offensichtlich ist der erste Einsatzfall von SQL für Sie wesentlich, wenn Sie die Sprache und die Datenbankprogrammierung im allgemeinen kennenlernen möchten. Wir haben bereits auf die Nachteile bei eingebettetem oder statischem SQL im Vergleich zu dynamischem SQL hingewiesen. Die Tage 18 und 19 bringen zwei Erweiterungen zu SQL, die Sie anstelle von eingebettetem SQL für die Ausführung der gleichen - in diesem Abschnitt besprochenen - Funktionstypen verwenden können.
Zusammenfassung
Die populären Programmierumgebungen wie Visual Basic, Delphi und PowerBuilder bieten dem Datenbankprogrammierer viele Werkzeuge, die sich hervorragend für die Ausführung von Abfragen und Aktualisierung von Daten in einer Datenbank eignen. Wenn Sie sich allerdings näher mit Datenbanken beschäftigen, werden Sie die Vorteile der heute behandelten Werkzeuge und Verfahren entdekken. Leider gehören Konzepte wie Cursor, Trigger und gespeicherte Prozeduren zu den neueren Datenbankkonzepten und sind erst zu einem geringen Maß über die Produkte hinweg standardisiert. Allerdings liegt allen Datenbank-Managementsystemen die gleiche Theorie zum Einsatz dieser Elemente zugrunde.
Temporäre Tabellen sind Tabellen, die während einer Sitzung des Benutzers existieren. Diese Tabellen sind in der Regel in einer speziellen Datenbank abgelegt (unter SQL Server mit tempdb bezeichnet) und werden oftmals mit einem eindeutigen Datums-/Zeitwert sowie einem Namen gekennzeichnet. Temporäre Tabellen können die Ergebnismenge einer Abfrage für spätere Verwendung durch andere Abfragen speichern. Allerdings kann sich die Leistungsbilanz der Datenbank verschlechtern, wenn viele Benutzer gleichzeitig temporäre Tabellen erzeugen und verwenden. Das hängt mit den zahlreichen Aktivitäten zusammen, die in der Datenbank tempdb ablaufen.
Cursor können eine Ergebnismenge speichern, um durch diese Ergebnismenge datensatzweise (oder um mehrere Datensätze auf einmal) zu scrollen. Mit dem Befehl FETCH lassen sich die Daten eines individuellen Datensatzes im Cursor abrufen und der Cursor auf den nächsten Datensatz setzen. Anhand verschiedener Systemvariablen kann man unter anderem ermitteln, ob das Ende der Datensätze erreicht ist.
Gespeicherte Prozeduren sind Datenbankobjekte, die mehrere SQL-Anweisungen in einer Funktion zusammenfassen. Gespeicherte Prozeduren können Parameter übernehmen und zurückgeben sowie andere gespeicherte Prozeduren aufrufen. Diese Prozeduren werden auf dem Datenbank-Server ausgeführt und in kompilierter Form in der Datenbank gespeichert. Der Einsatz gespeicherter Prozeduren statt der Ausführung eigenständiger Abfragen verbessert die Leistungsbilanz.
Trigger sind spezielle gespeicherte Prozeduren. Sie werden ausgeführt, wenn eine Tabelle einer INSERT-, DELETE- oder UPDATE-Operation unterliegt. Trigger setzen oft die referentielle Integrität durch und können auch andere gespeicherte Prozeduren aufrufen.
Eingebettetes SQL bezeichnet die Verwendung von SQL im Code eines Programms. Eingebettetes SQL besteht sowohl aus statischen als auch dynamischen SQL-Anweisungen. Statische SQL-Anweisungen lassen sich zur Laufzeit nicht modifizieren. Dynamische SQL-Anweisungen sind veränderbar.
Fragen & Antworten
Frage:
Wenn ich eine temporäre Tabelle erzeuge, können dann andere Benutzer auf meine Tabelle zugreifen?
Antwort:
Nein. Die temporäre Tabelle ist nur ihrem Erzeuger zugänglich.
Frage:
Warum muß ich einen Cursor schließen und den Speicher freigeben?
Antwort:
Der Speicher bleibt sonst weiterhin für den Cursor reserviert, auch wenn sein Name nicht mehr existiert.
Workshop
Kontrollfragen
1. Richtig oder falsch: In Microsoft Visual C++ kann der Programmierer das ODBC-API direkt aufrufen.
2. Richtig oder falsch: Das ODBC-API läßt sich nur aus einem C-Programm direkt aufrufen.
3. Richtig oder falsch: Dynamisches SQL erfordert den Einsatz eines Präcompilers.
4. Wofür steht das Nummernzeichen (#) vor dem Namen einer temporären Tabelle?
5. Wie gibt man den Speicher frei, nachdem man einen Cursor geschlossen hat?
6. Setzt man Trigger in Verbindung mit der SELECT-Anweisung ein?
7. Existiert ein Trigger weiterhin, wenn man die Tabelle löscht, auf der dieser Trigger eingerichtet ist?
Übungen
1. Erzeugen Sie eine Beispieldatenbankanwendung. (Die heute behandelten Themen haben wir an einer Musiksammlung dargestellt.) Teilen Sie diese Anwendung in logische Datengruppen.
2. Listen Sie die Abfragen auf, von denen Sie annehmen, daß sie für die Komplettierung dieser Anwendung erforderlich sind.
3. Listen Sie die verschiedenen Regeln auf, die Sie in der Datenbank verwalten möchten.
4. Erzeugen Sie ein Datenbankschema für die verschiedenen Datengruppen, die Sie in Schritt 1 beschrieben haben.
5. Konvertieren Sie die Abfragen aus Schritt 2 in gespeicherte Prozeduren.
6. Konvertieren Sie die Regeln aus Schritt 3 in Trigger.
7. Kombinieren Sie die Schritte 4, 5 und 6 zu einer großen Skript-Datei, die sich für den Aufbau der Datenbank und aller damit im Zusammenhang stehenden Prozeduren eignet.
8. Fügen Sie einige Beispieldaten ein. (Dieser Schritt kann ebenfalls Teil der Skript-Datei von Schritt 7 sein.)
9. Führen Sie die erzeugten Prozeduren aus, um deren Funktionalität zu testen.