1.3. Eingebettetes SQL

Werbung
1.3. Eingebettetes SQL
1.3.1. Eingebettetes SQL (1.Stufe)
Eingebettetes SQL (=embedded SQL) ist eine Erweiterung einer Gastsprache (z.B. C) um eingebettete
SQL-Befehle. Die Sprache SQL läßt sich in zahlreiche gängige Programmiersprachen einbetten, unter
anderem C/C++, Pascal, Fortran, Cobol, Ada. Die Einbettung des jeweiligen SQL-Dialektes ist nicht
vollständig standardisiert, ist aber doch von DBMS zu DBMS sehr ähnlich. Bei eingebettetem SQL
werden zwei Übersetzungsschritte durchgeführt, wovon der erste (=der Precompiler) die
eingebetteten SQL-Befehle übersetzt in Funktionsaufrufe des DBMS-Kerns und in Funktiosaufrufe
und Datenstrukturen zur Kommunikation des DBMS mit der jeweiligen Gastsprache. Eingebettete
Befehle sind für den Precompiler leicht zu erkennen, da sie mit exec sql beginnen.
Das folgende Beispiel orientiert sich an ESQL/C, der Einbettung des Sybase SQL Servers in die
Programmiersprache C. Die ESQL/C-Dateien haben die Endung .cp und werden vom Sybase Precompiler in eine Datei mit Endung .c übersetzt, die dann von einem normalen C-Compiler weiter
übersetzt werden kann.
Die sogenannte SQL Communication Area (sqlca) ist eine Datenstruktur, die Informationen zum
Betriebszustand des DBMS an die Gastsprache (z.B. C) weitergeben kann. Sie wird durch die
Schreibweise
exec sql include sqlca; /* SQL Communication Area einrichten - immer noetig */
bereits vom Precompiler textuell ersetzt (include auf Precompilerebene). So kann die folgende C Funktion fehler( ) z.B. nachsehen, welchen Fehlercode ein DBMS -Befehl auslöste.
void fehler( ) /* eine C-Funktion ! */ /* Zeige Fehler-Code und Fehlermeldung */
{ printf("\nFehler %d aufgetreten:\n%s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); }
Von der SQL Communication Area zu unterscheiden sind sogenannte Kommunikationsvariablen, in
denen Daten von der Gastsprache (C) zum DBMS (Sybase) oder vom DBMS zur Gastsprache
trasferiert werden. Diese werden innerhalb einer sogenannten declare section deklariert.
exec sql begin declare section; /* Kommunikationsvariablen deklarieren */
CS_CHAR user[31], passwd[31];
exec sql end declare section;
Die Datentypen CS_CHAR, CS_INT usw. sind nicht die Standard-C-Datentypen char oder int,
sondern werden vom DBMS speziell zum Datenaustausch angeboten. Jedoch kann eine Variable vom
Typ CS_CHAR[31] im C-Programm wie eine Variable vom Typ char[31] benutzt werden, und eine
Variable vom Typ CS_INT wie eine Variable vom Typ int.
Die Verbindung zum DBMS wird aufgebaut mit dem connect-Befehl:
exec sql connect :user identified by :passwd; /* DBMS -Verbindung aufbauen */
Dabei wird der Username aus der C-Variablen user gelesen und an den connect-Befehl uebergeben
und das Passwort wird aus der C-Variablen passwd gelesen und an den connect-Befehl uebergeben.
Eine konkrete Datenbank wird geöffnet mit dem use-Befehl:
exec sql use db_ss98; /* DB oeffnen */
Die Befehle zum Anlegen einer Tabelle und zum Einf ügen von Tupeln stellen nur ein exec sql vor die
entsprechenden interaktiven SQL-Befehle, z.B.:
exec sql create table student (Mnr int, Vorname char(20), Name cahr(20), Semester char(3) );
exec sql insert into student values (1001, 'Anna', 'Arm', 'ti2');
exec sql insert into student values (1002, 'Rita', 'Reich', 'ti2');
exec sql insert into student values (1003, 'Peter', 'Reich', 'ti2') ;
exec sql insert into student values (1004, 'Peter', 'Petersen', 'ie7') ;
Transaktionen werden beendet mit
exec sql commit work; /* Transaktion abschliessen */
und die Verbindung zum DBMS wird wieder abgebaut mit dem disconnect-Befehl:
exec sql disconnect all; /* DBMS-Verbindung abbauen */
Ein Ausnahmebehandlung für SQL-Fehler (z.B. anzulegende Tabelle existiert bereits) wird in der
Regel in einer Funktion der Gastsprache durchgeführt, hier in der C-Funktion fehler(). Der
automatische Aufruf dieser Funktion im Fehlerfall wird erreicht durch den whenever-Befehl:
exec sql whenever sqlerror call fehler(); /* Fehlerbehandlung initialisieren */
Insgesamt ergibt sich folgendes ESQL/C-Programm (example1.cp):
/* ESQL/C - Beispiel 1: Anlegen einer Tabelle und einfuegen von Tupeln */
exec sql include sqlca; /* SQL Communication Area einrichten - immer noetig */
void fehler( ) /* Zeige Fehler-Code und Fehlermeldung */
{ printf("\nFehler %d aufgetreten:\n%s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); }
main( )
{
exec sql begin declare section; /* Kommunikationsvariablen deklarieren */
CS_CHAR user[31], passwd[31];
exec sql end declare section;
exec sql whenever sqlerror call fehler(); /* Fehlerbehandlung initialisieren */
strcpy(user,"stb") ;
printf("\nPassword ? ") ;
gets(passwd) ;
exec sql connect :user identified by :passwd; /* DBMS -Verbindung aufbauen */
exec sql use db_ss98; /* DB oeffnen */
exec sql create table student (Mnr int, Vorname char(20), Name cahr(20), Semester char(3) );
exec sql insert into student values (1001, 'Anna', 'Arm', 'ti2');
exec sql insert into student values (1002, 'Rita', 'Reich', 'ti2');
exec sql insert into student values (1003, 'Peter', 'Reich', 'ti2') ;
exec sql insert into student values (1004, 'Peter', 'Petersen', 'ie7') ;
exec sql commit work; /* Transaktion abschliessen */
exec sql disconnect all; /* DBMS-Verbindung abbauen */
}
Ein weiteres Beispiel zu DB-Änderungen in ESQL/C
1.3.2 Anfragen mit Einzelergebnissen in eingebettetem SQL
Bei der Einbettung von Anfragen muß man unterscheiden, ob die Anfrage ein Ergebnis oder mehrere
Ergebnisse liefert. Liefert sie nur ein Ergebnis, so kann sie änhlich einfach eingebetten werden wie
Insert-Befehle. Beispiel:
exec sql begin declare section ; /* Kommunikationsvariablen deklarieren */
CS_INT anzahl ;
exec sql end declare section ;
...
exec sql select count ( * ) into :anzahl from student ; /* liest ein Ergebnis in die Variable anzahl des
Umgebungsprogramms */
1.3.3 Anfragen mit mehreren Ergebnissen in eingebettetem SQL
Liefert eine Anfrage mehrere Ergebnistupel, so kann man in embedded SQL einen Cursor (=Iterator
über das Anfrageergebnis) anlegen mit:
exec sql declare cursor studenten_aus_ti2 for /* generiert Code f ür folgende Anfrage */
select * from student s where s.Semester = 'ti2' ; /* suche Studenten aus Semester ti2 */
Man nennt studenten_aus_ti2 Cursor für die nachfolgende Anfrage. Der Precompiler speichert den
SQL-Befehl zusammen mit seinem Cursornamen, generiert aber keinen Code.
Wenn der Cursor zur Laufzeit geöffnet wird, wird die im declare-Befehl abgelegte Anfrage an den
DBMS-Server stellt. Statt der Konstanten 'ti2' könnte auch eine Variable vom Typ CS_CHAR
verwendet werden. Der Wert dieser Variablen würde beim open-Befehl für die Variable eingesetzt,
bevor die Anfrage an das DBMS geschickt wird. .
Die Ergebnisse der Anfrage kann man sich der Reihe nach ansehen mit fetch, bis kein weiteres
Ergebnis mehr da ist (sqlca.sqlcode==100) , z.B.:
for ( ; ; )
{
exec sql fetch for studenten_aus_ti2 into :Mnr , :Vor , :Name , :Sem
if ( sqlca.sqlcode==100 ) break ; /* Verlasse Schleife, wenn alles gelesen */
verarbeite ( Mnr , Vor , Name , Sem ) ; /* Aufruf einer beliebigen C -Funktion */
}
Voraussetzung für den fetch-Befehl ist wiederum, daß Mnr, Vor, Name und Sem in einer exec sql
declare section (irgendwo) vor dem fetch-Befehl deklariert wurden.
Schließlich sollte der Cursor nach Gebrauch wieder geschlossen werden mit
exec sql close studenten_aus_ti2 ;
Man kann einen Cursor (abwechselnd) mehrfach öffnen und schließen.
Man kann mehrere verschiedene Cursor gleichzeitig ge öffnet halten, z.B. um in zwei geschachtelten
Schleifen einen Join nachzuimplementieren.
Es ist möglich, daß zwei Cursor denselben SQL-Befehl als Anfrage enthalten.
Ein weiteres Beispiel zu Cursorn in ESQL/C
1.3.4 Parametrisierbare Anfragen in eingebettetem SQL
Was im vorigen Beispiel nicht möglich war, ist mit Methode 3 des dynamischen SQL möglich:
Anfragen können zur Laufzeit parametrisiert werden, wobei Anzahl und Typen der Parameter bei
Methode 3 bereits zur Compilezeit festgelegt werden. Ein Beispiel zu ESQL/C mit parametrisierbaren
Anfragen
1.3.5 Volles dynamisches SQL / Methode 4
Was im vorigen Beispiel nicht möglich war, ist mit Methode 4 des dynamischen SQL möglich: Anzahl
und Typ der Eingabe- und Ausgabeparameter werden erst zur Laufzeit bestimmt. Dazu werden
sogenannte dynamische Descriptoren benutzt.
Weitere Informationen gibt es im Kapitel 7 / Method 4 des [Handbuch zu ESQL/C].
1.3.6 Stored Procedures
Weitere Informationen gibt es im [Handbuch zu ESQL/C].
Herunterladen