Die eingebettete SQL-Sprache

Inhaltsübersicht

 

Die eingebettete SQL-Sprache. 1

16      Die eingebettete SQL-Sprache. 1

16.1   Einleitung. 2

16.1.1   Kennzeichnen der eingebetteten SQL-Anweisungen. 4

16.1.2   Variablen. 5

16.1.3   Fehlerbehandlung. 9

16.2   Eingebettete SQL-Anweisungen ohne Cursor 14

16.3   Verwendung des Cursors. 28

16.4   Übersetzung eines ESQL/C- bzw. ESQL/COBOL-Programms. 40

16.5   Eingebettete Anweisungen und Programmierstil 41

16.5.1   Optimierung der Datenbankzugriffe. 41

16.5.2   Explizite Abfrage bei der Änderung von Reihen. 47

16.5.3   Explizite Angabe aller Reihen in einer SELECT- bzw. 47

INSERT-Anweisung. 47

Aufgaben. 48

      

Zurück zum Inhaltsverzeichnis

 

 

 

16 Die eingebettete SQL-Sprache

 

 

         In diesem  Kapitel werden  die eingebetteten  SQL-Anweisungen

         bei  INGRES beschrieben,  wie sie  in den Programmiersprachen

         C   und  COBOL   benutzt   werden   können.   Zuerst   werden

         die   eingebetteten  SQL-Anweisungen   ohne  Cursor-Benutzung

         dargestellt.  Im   zweiten  Teil   des  Kapitels  werden  die

         Anwendung des  Cursors erklärt  und alle SQL-Anweisungen, die

         im Zusammenhang  mit dem  Cursor benutzt  werden,  erläutert.

         Der  letzte   Teil  des   Kapitels  ist  dem  Programmierstil

         gewidmet; anhand  von Beispielen  wird erklärt,  wie man  ein

         Anwendungsprogramm  mit  Hilfe  einfacher  Regeln  optimieren

         kann.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.1     Einleitung

 

 

         In  den bisherigen Kapiteln, die sich mit den SQL-Anweisungen

         von  INGRES  befaßt  haben,  haben  wir  erklärt,  wie  diese

         Anweisungen  interaktiv, d.h.  direkt am  Bildschirm  benutzt

         werden können.  Die andere  Möglichkeit stellt  die Benutzung

         der   Anweisungen    innerhalb   eines    Anwendungsprogramms

         dar.    Ob   SQL-Anweisungen   interaktiv   oder   in   einem

         Anwendungsprogramm benutzt  werden, hängt  von der  Art ihrer

         Verwendung  ab.   Interaktiv  werden  die  Anweisungen  meist

         benutzt,   wenn  Datendefinitionsanweisungen   bzw.  einfache

         Datenmanipulationsanweisungen angegeben  werden  sollen.  Die

         eingebetteten   SQL-Anweisungen   werden   vor   allem   dann

         verwendet,   wenn  komplexe  Abfragen  und  Änderungen  einer

         Datenbank durchgeführt  werden sollen.  Dabei ist  die Linie,

         die diese zwei Verwendungsarten trennt, nicht genau. Es kommt

         sowohl  vor, daß  eine Datendefinitionsanweisung  eingebettet

         benutzt   wird,  als   auch,  daß   komplexere  Abfragen  und

         Änderungen der Datenbank interaktiv durchgeführt werden.

 

         Die interaktiven  und eingebetteten  SQL-Anweisungen sind  im

         Prinzip  sehr ähnlich;  der größte  Unterschied ist,  daß die

         eingebettete SQL-Sprache  zusätzliche Anweisungen beinhaltet,

         die  bei der  interaktiven Anwendung  nicht  vorkommen.  Jede

         SQL-Anweisung,  die  interaktiv  verwendet  wird,  kann  also

         auch  eingebettet   in  einem  Anwendungsprogramm,  eventuell

         mit kleinen  Änderungen, benutzt  werden.  (Diese  Änderungen

         betreffen   nur  die  Datenmanipulationsanweisungen  und  von

         diesen besonders die SELECT-Anweisung.)

 

         INGRES unterstützt  z.Zt. in  der UNIX-  und VMS-Umgebung die

         Schnittstellen  zu folgenden  Programmiersprachen der dritten

         Generation:

 

                                - Ada ,  

                                - C,

                                - COBOL,      

                                - FORTRAN,               

                                - Basic,

                                - Pascal   und

                                - PL/I.

 

         Alle  Beispiele in  diesem und  im nächsten  Kapitel sind  in

         zwei  Programmiersprachen -  C und  COBOL -  geschrieben. Wir

         haben uns  für diese  beiden  Sprachen  aus  folgendem  Grund

         entschieden:   COBOL  ist   noch  immer  die  meistverwendete

         Programmiersprache für  die kommerziellen  Anwendungen und  C

         die  wichtigste Sprache  für  die  Betriebssysteme  UNIX  und

         DOS.  Trotz dieser  Einschränkung auf  COBOL und C gelten die

         Beschreibungen  in diesem  und im  nächsten Kapitel  generell

         für alle INGRES-Schnittstellen zu den Programmiersprachen der

         dritten Generation.

 

         Wir  gehen davon  aus, daß jeder Leser entweder die eine oder

         die andere  Programmiersprache kennt. Es ist nicht notwendig,

         die  Beispiele   in  beiden  Sprachen  nachzuvollziehen;  sie

         sind  so gestaltet,  daß jedem  COBOL-Beispiel ein  ähnliches

         C-Beispiel unmittelbar folgt.

 

         Die    eingebetteten   SQL-Anweisungen    können   auf   zwei

         verschiedene  Weisen  abgearbeitet  werden.  Meist  wird  ein

         Vorübersetzer zur  Verfügung gestellt, der jede SQL-Anweisung

         in   die  Host-Sprache   übersetzt.  Damit   wird,  nach  der

         Vorübersetzer-Phase,  ein  Programm  mit  einheitlichem  Code

         erzeugt, das anschließend übersetzt werden kann.

 

         Die  andere   Möglichkeit,  die   eingebetteten   Anweisungen

         abzuarbeiten, ist, eine Modulbibliothek zu verwenden und jede

         SQL-Anweisung an  den entsprechenden  Modul zu übergeben. Die

         Verwendung der  Modulbibliothek kommt  in der Praxis nicht so

         oft vor wie die Verwendung des Vorübersetzers.

 

         Alle  INGRES-Schnittstellen   zu  den  Sprachen  der  dritten

         Generation    enthalten   einen    Vorübersetzer,   der   die

         eingebetteten SQL-Anweisungen in die Host-Sprache übersetzt.

 

         Die INGRES-Komponente  SQL/FORMS, die  eine  Erweiterung  der

         eingebetteten  SQL ist und die Erstellung der maskenbasierten

         Anwendungen   ermöglicht,   wird   in   diesem   Buch   nicht

         beschrieben. Die Beschreibung dieser Komponente finden Sie in

         den entsprechenden INGRES-Manualen.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.1.1  Kennzeichnen der eingebetteten SQL-Anweisungen

 

 

         Damit ein Vorübersetzer die eingebetteten SQL-Anweisungen von

         den  Anweisungen der  Host-Sprache unterscheiden kann, ist es

         erforderlich, alle SQL-Anweisungen besonders zu kennzeichnen.

         Dabei  muß  sowohl  der  Anfang,  als  auch  das  Ende  einer

         SQL-Anweisung gekennzeichnet werden.

 

         Die  eingebetteten SQL-Anweisungen haben am Anfang als Präfix

         die Schlüsselwörter

 

                        EXEC SQL                 .

 

         Dies   gilt  für   alle   INGRES-Schnittstellen.   Das   Ende

         einer  eingebetteten   SQL-Anweisung  wird  für  verschiedene

         Host-Sprachen unterschiedlich  gekennzeichnet. Bei COBOL wird

         das Schlüsselwort  "END-EXEC." und  bei C das Zeichen ";" als

         Endekennzeichen benutzt.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.1.2  Variablen

 

 

         In    einem   Anwendungsprogramm    werden   am    häufigsten

         Datenmanipulationsanweisungen  (SELECT,  INSERT,  UPDATE  und

         DELETE)  verwendet.   Jede  dieser   Anweisungen   ermöglicht

         den Datenaustausch  mit  Hilfe  von  Variablen  zwischen  dem

         Anwendungsprogramm einerseits und der Datenbank andererseits.

         Die  Variablen  befinden  sich  innerhalb  der  eingebetteten

         SQL-Anweisung   und  sind   nichts  anderes   als   Variablen

         der  Host-Sprache,  die  auch  als  solche  definiert  werden

         müssen.  Damit   sie  innerhalb  der  SQL-Anweisung  von  den

         Datenbankobjekten unterschieden werden können, werden sie mit

         dem Präfix ":" gekennzeichnet.

 

         In   der    eingebetteten   SQL-Anweisung   existieren   zwei

         unterschiedliche Arten von Variablen

 

                  - Host-Variablen  und 

                  - Indikator-Variablen.

 

         Host-Variablen     dienen    als     Ziel-Variablen     einer

         Abfrage  bzw.   können  anstelle   einer  Konstante   in  den

         Datenmanipulationsanweisungen verwendet  werden.  Sie  können

         auch  als Argumente  in verschiedenen anderen SQL-Anweisungen

         verwendet werden.

 

         Host-Variablen  können  nur  die  Datentypen  haben,  die  in

         den  Host-Sprachen   erlaubt  sind.  Auf  der  anderen  Seite

         werden ihnen  die Datenwerte  zugewiesen bzw.  werden sie mit

         Datenwerten  verglichen, die  SQL-Datentypen haben. Damit die

         Zuweisung bzw.  ein Vergleich  durchgeführt werden  kann, muß

         jede Variable  zu ihrem  entsprechenden Datenwert  kompatibel

         sein.

 

         Indikator-Variablen sind  spezielle  Variablen,  die  nur  im

         Zusammenhang  mit Host-Variablen  benutzt werden können. Jede

         Host-Variable kann  also eine  zugehörige  Indikator-Variable

         haben, die  zusätzliche Information  über  den  gespeicherten

         Wert in der Host-Variablen liefert. Die Host-Variable und die

         dazu gehörende  Indikator-Variable werden  immer nacheinander

         in der folgenden Form geschrieben:

 

                        :hostvar:indvar             .

 

         Indikator-Variablen ermöglichen:

 

              - die  Prüfung, ob  einer Host-Variablen  ein  NULL-Wert

                zugewiesen wurde;

              - die Prüfung, ob bei der Zuweisung eines Datenwertes an

                eine Host-Variable der zugewiesene Wert  gekürzt  wird

                und damit Information verloren geht;

              - das Einfügen von NULL-Werten.

 

         Wenn  in einer  SELECT-Anweisung  ein  Datenwert,  der  einen

         NULL-Wert  enthält,  einer  Host-Variablen  zugewiesen  wird,

         beinhaltet   die  Host-Variable   nach  der  Zuweisung  einen

         undefinierten Wert.  Die Prüfung einer solchen Zuweisung kann

         mit  Hilfe  einer  Indikator-Variablen  durchgeführt  werden,

         die dann  den Wert  -1  enthält,  während  die  entsprechende

         Host-Variable unverändert bleibt.

 

         Wenn  in   einer  SELECT-Anweisung   ein  Datenwert   gekürzt

         einer  Host-Variablen zugewiesen  wird (z.B.  wenn die  Länge

         der  Host-Variablen kleiner  als  die  definierte  Länge  des

         Datenwertes ist), kann dies mit Hilfe der Indikator-Variablen

         festgestellt    werden.   In    diesem   Fall   enthält   die

         Indikator-Variable die  tatsächliche Länge  des  zugewiesenen

         Datenwertes.

 

         Anstatt das Schlüsselwort NULL in einer INSERT-  bzw.

         UPDATEAnweisung

                   zu  verwenden, kann  das Einfügen eines NULL-Wertes

         mit  Hilfe   der  Indikator-Variablen   durchgeführt  werden.

         In diesem  Fall  muß  der  Indikator-Variablen  der  Wert  -1

         zugewiesen werden (siehe Beispiel 16.7).

 

         Alle  Indikator-Variablen werden in einer Host-Sprache intern

         als 2  Byte lange Ganzzahlen gespeichert. Bei COBOL kann jede

         Indikator-Variable mit

 

                             S9(5) COMP

 

         und bei C mit

 

                             short     (bzw. short int)

 

         definiert werden.

 

         Jede Host-  bzw. Indikator-Variable  muß  explizit  definiert

         werden, wobei die Variablen mit Datenbankobjekten gleichnamig

         sein können. In jedem Programm wird ein spezieller Abschnitt,

         der  sogenannte DECLARE-Abschnitt aufgebaut, in dem nur Host-

         und  Indikator-Variablen definiert  werden dürfen.  In  jedem

         Programm darf nur ein DECLARE-Abschnitt existieren.

 

         Der DECLARE-Abschnitt beginnt mit der SQL-Anweisung

 

                         BEGIN DECLARE SECTION

 

         und endet mit einer weiteren SQL-Anweisung

 

                         END DECLARE SECTION             .

 

         Diese  beiden eingebetteten SQL-Anweisungen müssen als solche

         innerhalb des Programms gekennzeichnet sein.  In  COBOL  wird

         also jede von ihnen zwischen den Schlüsselwörtern

 

                         EXEC SQL               und

                         END-EXEC.

 

         geschrieben.

 

         Die   SQL-Anweisungen   BEGIN   DECLARE   SECTION   und   END

         DECLARE  SECTION  sind  keine  ausführbaren  SQL-Anweisungen,

         im  Gegensatz   zu  allen   bis  jetzt   beschriebenen.   Sie

         gehören zu  Vereinbarungs-Anweisungen,  von  welchen  weitere

         im   Verlauf   dieses   Kapitels   vorgestellt   werden.   Im

         Unterschied  zu ausführbaren  SQL-Anweisungen generieren  die

         Vereinbarungs-Anweisungen keinen Code.

 

         Die    folgenden    Beispiele    zeigen    die    Form    des

         DECLARE-Abschnitts in COBOL bzw. in C.

 

         Beispiel 16.1

         (COBOL)

 

                 EXEC SQL   BEGIN DECLARE SECTION   END-EXEC.

             77  VORNAME PIC  X(15).

             77  NACHNAME PIC X(15).

             77  IND-VORNAME   PIC S9(5) COMP.

             77  IND-NACHNAME  PIC S9(5) COMP.

                 EXEC SQL   END DECLARE SECTION     END-EXEC.

 

         Beispiel 16.2

         (C)

 

         EXEC SQL  BEGIN DECLARE SECTION;

         char  vorname[16];

         char nachname[16];

         short ind_vorname;

         short ind_nachname;

         EXEC SQL  END DECLARE SECTION;

 

         Eine  weitere, in  jedem  Programm  befindliche  eingebettete

         SQL-Anweisung ist die CONNECT-Anweisung. Mit dieser Anweisung

         wird  das Programm  mit einer Datenbank verbunden. Die Syntax

         dieser Anweisung ist

 

            CONNECT db_name [IDENTIFIED BY ben] [OPTIONS=sch_1,...]

 

         Die Angaben  der CONNECT-Anweisung  sind den  Angaben des  in

         Kapitel  15 beschriebenen  sql-Kommandos ähnlich.  Die Angabe

         IDENTIFIED BY  entspricht dem Schalter "-u" und sch_1,... den

         anderen vom sql-Kommando unterstützten Schaltern. db_name ist

         der Name  der Datenbank,  mit der das Programm verbunden ist.

         db_name kann  auch mit  Hilfe einer  Host-Variablen definiert

         werden.

 

         Die CONNECT-Anweisung muß jeder anderen SQL-Anweisung, die in

         Bezug zur  Datenbank db_name  steht, vorangehen. Ein Programm

         kann zu  einem Zeitpunkt  nur mit  einer  einzigen  Datenbank

         verbunden werden.

 

         Die zweite Anweisung

 

                        DISCONNECT

 

         beendet   die    Verbindung   zu   einer   Datenbank.   Diese

         SQL-Anweisung   ist  unbedingt   notwendig,  falls  in  einem

         Programm  die   Verbindung   zu   einer   zweiten   Datenbank

         hergestellt   werden  soll.   In  diesem   Fall  muß   zuerst

         die   Verbindung  zur   ersten  Datenbank   mit   Hilfe   der

         DISCONNECT-Anweisung    beendet   werden,   bevor   mit   der

         CONNECT-Anweisung  die  Verbindung  zu  der  neuen  Datenbank

         aufgebaut wird.

 

         Mit der  Anweisung CALL  ermöglicht INGRES  den Aufruf  eines

         Betriebssystemkommandos   bzw.  eines  Subsystems  (wie  QBF,

         Report-Writer   usw.)  aus   einem  Programm   heraus.  Diese

         Anweisung hat zwei verschiedene Formen

 

              CALL SYSTEM (COMMAND = kommando)       bzw.

 

              CALL subsystem (DATABASE=db_name [,param_1=wert_1,...])

 

         Mit   der   ersten   Form   der   CALL-Anweisung   kann   ein

         Betriebssystemkommando    und    mit    der    zweiten    ein

         INGRES-Subsystem aufgerufen werden.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.1.3  Fehlerbehandlung

 

 

         INGRES unterstützt drei verschiedene Möglichkeiten, bei einer

         eingebetteten  SQL-Anweisung zu  prüfen, ob  sie  erfolgreich

         ausgeführt wurde. Diese Möglichkeiten sind:

 

            - die Prüfung der SQLCA-Struktur,         

            - die Verwendung der WHENEVER-Anweisung   oder

            - die Verwendung der INQUIRE_INGRES-Anweisung.

 

         Nach  der Abarbeitung  jeder ausführbaren  SQL-Anweisung wird

         die Information,  ob diese  Anweisung erfolgreich  ausgeführt

         wurde,  an eine  spezielle Struktur geliefert. Diese Struktur

         heißt  SQLCA  (SQL  Communication  Area)  und  muß  in  jedem

         Host-Programm explizit  eingefügt werden,  damit die  in  ihr

         gespeicherte Information  benutzt werden  kann. Das  Einfügen

         der SQLCA-Struktur erfolgt mit folgender SQL-Anweisung:

 

                      INCLUDE SQLCA

 

         Die  INCLUDE-Anweisung   ist  eine   Vereinbarungs-Anweisung.

         Während   der  Vorübersetzungs-Phase   wird  diese  Anweisung

         durch  die Definition  mehrerer Host-Variablen ersetzt. Diese

         Host-Variablen  enthalten   Informationen  über   die  gerade

         ausgeführte SQL-Anweisung.

 

         Jedes   Programm   führt   standardmäßig   die   eingebettete

         SQL-Anweisung aus, ohne sich um eventuell aufgetretene Fehler

         zu kümmern.  Mit Hilfe  der Host-Variablen, die in der

         SQLCAStruktur

                  definiert  sind, ist  es möglich,  den Zustand  des

         Programms nach jeder ausführbaren SQL-Anweisung zu überprüfen

         und eventuell notwendige Maßnahmen zu treffen.

 

         Bei allen Programmiersprachen enthält die SQLCA-Struktur u.a.

         folgende Variablen:

 

                     - SQLCODE,

                     - SQLERRM,

                     - SQLERRD    und

                     - SQLWARN.

 

         SQLCODE  ist ein  4 Byte langes, binäres  Datenfeld, das  den

         Rückgabewert  der zuletzt ausgeführten SQL-Anweisung enthält.

         Ist  der Rückgabewert  0, wurde die SQL-Anweisung erfolgreich

         ausgeführt.

 

         Bei  einem Fehler  im Programm,  der in  einer  SQL-Anweisung

         aufgetreten  ist,   wird  der   Rückgabewert  negativ.  Jedem

         existierenden    negativen   Rückgabewert   entspricht   eine

         Fehlermeldung, deren  Text in den entsprechenden Manualen des

         Systems nachgelesen werden kann.

 

         Der  positive Rückgabewert  zeigt eine  Warnung an,  wie z.B.

         im Falle  einer SELECT-Anweisung, die keine einzige Reihe als

         Ergebnis liefert.

 

         SQLERRM  ist eine  variabel lange  Zeichenkette, die den Text

         der Fehlermeldung enthält, der dem negativen Rückgabewert der

         Variablen SQLCODE  entspricht.

 

         SQLERRD ist  ein Vektor mit 6 Elementen, die als 4 Byte lange

         binäre Datenfelder  definiert sind.  Diese Datenfelder sollen

         den  internen Zustand  des Systems  beschreiben.  Z.Zt.  wird

         nur  SQLERRD(3) unterstützt, das die Anzahl der verarbeiteten

         Reihen bei einer Datenmanipulationsanweisung anzeigt.

 

         SQLWARN  ist  eine  Struktur  mit  8  Elementen,  die  als  1

         Byte  lange   alphanumerische  Datenfelder   definiert  sind.

         Jedes dieser  Datenfelder zeigt  Warnungen  für  verschiedene

         Bedingungen  an, die  nach der Ausführung einer SQL-Anweisung

         auftreten  können. Der  Wert jedes  Datenfeldes kann entweder

         das Zeichen "W" oder das Leerzeichen sein, abhängig davon, ob

         eine entsprechende Bedingung aufgetreten ist oder nicht.

 

         In  der Praxis  sollte nach  jeder ausführbaren SQL-Anweisung

         der  Wert der  SQLCODE-Variablen überprüft  werden. Falls der

         Rückgabewert   ungleich  0  ist,  sollten  weitere  Maßnahmen

         ergriffen werden.  Beispiel 16.5  zeigt, wie  die Prüfung der

         SQLCODE-Variablen aussehen könnte.

 

         Als  Alternative  zur  expliziten Prüfung der SQL-Anweisungen

         mit  Hilfe  der  SQLCA-Struktur bietet sich die Benutzung der

         WHENEVER-Anweisung  an. Sie ist eine Vereinbarungs-Anweisung,

         die  jede  eingebettete SQL-Anweisung nach  ihrer  Ausführung

         überprüft. Die allgemeine Form der WHENEVER-Anweisung ist

 

                   WHENEVER bedingung  massnahme

 

         wobei bedingung die Werte

 

                  - SQLERROR,

                  - SQLWARNING,       

                  - NOT FOUND   sowie

                  - SQLMESSAGE

 

         und massnahme die Werte

 

                  - GO TO marke,

                  - STOP,

                  - CONTINUE  und

                  - CALL prozedur

 

         haben kann.

 

         SQLERROR   kennzeichnet  die   Bedingung,  die  erfüllt  ist,

         wenn ein  Fehler  nach  der  Ausführung  einer  SQL-Anweisung

         aufgetreten   ist,    wenn   also    der   Rückgabewert   der

         SQLCODE-Variablen  negativ ist.

 

         SQLWARNING kennzeichnet  die Bedingung, die erfüllt ist, wenn

         mindestens eine  Warnung vom  System gemeldet  wird.  In  dem

         Fall  wird das erste Datenfeld des Vektors SQLWARN überprüft;

         falls der Wert gleich "W" ist, wird die Bedingung als erfüllt

         betrachtet.

 

         NOT  FOUND kennzeichnet  die Bedingung, die erfüllt ist, wenn

         keine oder  keine weitere  Reihe nach  der  Ausführung  einer

         Datenmanipulationsanweisung  gefunden wird. Dieser Fall tritt

         auf, wenn der Rückgabewert der SQLCODE-Variablen 100 ist.

 

         Die  Bedingung SQLMESSAGE  tritt  auf,  falls  der  Wert  der

         SQLCODE-Variablen 700 beträgt. Diese Warnung kennzeichnet die

         Ausführung der MESSAGE-Anweisung in einer Datenbankprozedur.

 

         GO TO  marke setzt  den Ablauf  des Programms  ab der  Stelle

         fort, die mit "marke" gekennzeichnet ist, falls die Bedingung

         in  der   WHENEVER-Bedingung  erfüllt   ist.  Diese  Maßnahme

         entspricht dem Einfügen der Anweisung

 

                   IF bedingung THEN GO TO marke

 

         nach jeder ausführbaren SQL-Anweisung in einem Programm.

 

         Mit   STOP  wird   ein   fehlerhaftes   Programm   nach   der

         Ausgabe   einer  Fehlermeldung   beendet.  Eine   zu   diesem

         Zeitpunkt eröffnete  Datenbank  wird  geschlossen  (d.h.  die

         DISCONNECT-Anweisung wird vom System implizit durchgeführt.)

 

         CONTINUE  kennzeichnet die  Fortsetzung  des  Programms, ohne

         Maßnahmen zu  ergreifen. Damit  entspricht diese  Klausel dem

         Fall,  in  dem  keine  zusätzliche  IF-Anweisung  nach  jeder

         ausführbaren SQL-Anweisung eingeführt wird.

 

         CALL prozedur  verursacht den Aufruf einer Prozedur, die Teil

         der  Hostsprache ist  (z.B. die  Ausführung eines  Abschnitts

         entsprechend der PERFORM-Anweisung in COBOL).

 

         Die Wirkung einer WHENEVER SQLERROR-Anweisung beschränkt sich

         von ihrer  Position im  Modul, wo  sie angegeben ist, bis zur

         nächsten  WHENEVER SQLERROR-Anweisung bzw. bis zum Modulende,

         falls keine  weitere WHENEVER  SQLERROR-Anweisung  existiert.

         Die Voreinstellung  für WHENEVER  SQLERROR-Anweisung ist  die

         CONTINUE-Angabe.  Die WHENEVER  SQLERROR-Anweisung  muß  erst

         nach  der Anweisung  INCLUDE SQLCA im Programm erscheinen, um

         überhaupt benutzt werden zu können.

 

         Für  WHENEVER SQLWARNING  bzw. WHENEVER NOT FOUND gilt alles,

         was bereits über WHENEVER SQLERROR gesagt wurde.

 

         Die    INQUIRE_INGRES-Anweisung    ist    eine    spezifische

         INGRES-Anweisung,   die    dem   Benutzer   die   Möglichkeit

         bietet,   nach    jeder   ausgeführten    SQL-Anweisung   die

         Diagnoseinformationen  den  unterschiedlichen  Host-Variablen

         eines Programms zuzuweisen. Die Syntax dieser Anweisung ist

 

          INQUIRE_INGRES (host_var1=angabe_1 [,host_var2=angabe_2,..])

 

         angabe_1, angabe_2,... sind die unterschiedlichen Angaben der

         INQUIRE_INGRES-Anweisung. Die  wichtigsten von  ihnen liefern

         dieselbe  Information  wie  die  unterschiedlichen  Variablen

         der  SQLCA-Struktur  (z.B.  entspricht  die  Angabe  ROWCOUNT

         SQLERRD(3), die Angabe ERRORTEXT SQLERRM usw.)

 

         Die   Entscheidung,  ob  die  Fehlerbehandlung  explizit  mit

         der  Prüfung  der  SQLCA-Struktur  oder  implizit  mit  Hilfe

         der WHENEVER-  bzw. der INQUIRE_INGRES-Anweisung durchgeführt

         werden  soll,   ist  dem   Programmierer   überlassen.   Jede

         dieser  Methoden  hat  Vor-  und  Nachteile:  Die  Logik  der

         WHENEVER-Anweisung  ist   komplizierter  als   die  explizite

         Prüfung  der   SQLCA-Struktur  und   die  Verwendung  der  GO

         TO-Anweisung kann  zu  unübersichtlichen  Programmen  führen.

         Auf der  anderen Seite  erfordert die  explizite Prüfung  der

         SQLCA-Struktur einen erhöhten Programmieraufwand. Wichtig ist

         nicht,  welche Methode  gewählt wird,  sondern daß  auf jeden

         Fall eine Fehlerbehandlung in den Programmen stattfindet.

 

         In  den   meisten  Beispielen   dieses   und   des   nächsten

         Kapitels, die  nur Teilprogramme  oder einzelne  eingebettete

         SQL-Anweisungen  zeigen, werden  wir auf die Fehlerbehandlung

         verzichten, damit  sie so  klein wie  möglich gehalten werden

         können.  In den Beispielen, die ganze Programme umfassen wird

         die explizite  Prüfung des  Wertes der  SQLCODE-Variablen  in

         den COBOL-Programmen durchgeführt während die C-Programme die

         WHENEVER-Anweisung beinhalten.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.2      Eingebettete SQL-Anweisungen ohne Cursor

 

 

         Die eingebetteten  SQL-Anweisungen, die ohne Cursor verwendet

         werden können, sind:

 

          - alle Datendefinitionsanweisungen;

          - alle Datensteuerungsanweisungen;

          - die SELECT-Anweisung,  die nur  eine  Reihe  als  Ergebnis

            liefert;

          - die  UPDATE-Anweisung  ohne  CURRENT-Klausel

          - die  DELETE-Anweisung  ohne  CURRENT-Klausel;           

          - die INSERT-Anweisung.

 

         Anhand  von  Beispielen  werden  die  obigen  SQL-Anweisungen

         dargestellt.  Wie bereits  in den  vorhergehenden, wird  auch

         in  diesen Beispielen  die Beispieldatenbank verwendet.

 

         Beispiel 16.3

 

         Versorgen Sie  die Host-Variablen  MNAME und MVORNAME mit den

         Werten  des Namens  und Vornamens  jedes Mitarbeiters, dessen

         Personalnummer durch die Host-Variable PERSNUM gegeben ist.

 

         (COBOL) 

   EXEC SQL

                    SELECT m_name, m_vorname

                    INTO :MNAME, :MVORNAME

                        FROM mitarbeiter

                        WHERE m_nr = :PERSNUM

                END-EXEC.

 

         (C)

     EXEC SQL

                    SELECT m_name, m_vorname

                    INTO :MNAME, :MVORNAME

                        FROM mitarbeiter

                        WHERE m_nr = :PERSNUM;

 

         Im  Unterschied  zur interaktiven SELECT-Anweisung beinhaltet

         die  eingebettete SELECT-Anweisung die INTO-Klausel mit einer

         Reihe von Variablen. Die INTO-Klausel hat folgende Form

 

                INTO :hostvar1[:indvar1] [, :hostvar2[:indvar2]] ...

 

         Liefert  die  Bedingung  in  der  WHERE-Klausel  in  Beispiel

         16.3  nur eine  Reihe als Ergebnis, werden die entsprechenden

         Datenwerte   der    Spalten   m_name    und   m_vorname   der

         Host-Variablen   MNAME  und  MVORNAME  zugewiesen.  Wenn  die

         Bedingung  für  keine  der  Reihen  der  Tabelle  mitarbeiter

         erfüllt ist,  wird der  Wert  der  SQLCODE-Variable  auf  100

         gesetzt. Falls die Bedingung mehr als eine Reihe als Ergebnis

         liefert,  wird SQLCODE  auf einen  negativen Wert gesetzt.

 

         Jede  der   Host-Variablen  MNAME,   MVORNAME   und   PERSNUM

         muß   einen  kompatiblen  Datentyp  zu  ihrem  entsprechenden

         Datenwert m_name,  m_vorname bzw. m_nr haben. Die notwendigen

         Datenkonvertierungen führt  das  System  durch.  Die  Spalten

         m_name  und m_vorname  sind als  CHAR(20) definiert,  während

         m_nr  als INTEGER  definiert ist.  Dementsprechend könnte der

         DECLARE-Abschnitt für die Host-Variablen wie folgt aussehen:

 

         (COBOL)

                  EXEC SQL   BEGIN DECLARE SECTION   END-EXEC.

             77    MNAME    PIC X(20).

             77    MVORNAME PIC X(20).

             77    PERSNUM  PIC S9(9) COMP.

                  EXEC SQL   END DECLARE SECTION     END-EXEC.

 

         (C)

      EXEC SQL BEGIN DECLARE SECTION;

                  char MNAME[21];

                  char MVORNAME[21];

                  int  PERSNUM;

                  EXEC SQL END DECLARE SECTION;

 

         Beispiel 16.4

 

         Versorgen  Sie die  Host-Variablen ABTNR  und STADT  mit  der

         Abteilungsnummer und  dem Standort  für jene Abteilung, deren

         Name  durch   die  Host-Variable  ABTNAME  gegeben  ist.  Den

         Host-Variablen ABTNR und STADT sollen die Indikator-Variablen

         INDABTNR und INDSTADT zugewiesen werden.

 

         (COBOL)

     EXEC SQL

                      SELECT abt_nr, stadt

                      INTO :ABTNR:INDABTNR, :STADT:INDSTADT

                      FROM abteilung

                      WHERE abt_name = :ABTNAME

                 END-EXEC.

 

         (C)   

      EXEC SQL

                      SELECT abt_nr, stadt

                      INTO :ABTNR:INDABTNR, :STADT:INDSTADT

                      FROM abteilung

                      WHERE abt_name = :ABTNAME;

 

         Die  Indikator-Variablen sind  schon im  vorherigen Abschnitt

         beschrieben  worden.   Mit  Hilfe   der   Indikator-Variablen

         INDSTADT kann  z.B. überprüft  werden, ob  der Host-Variablen

         STADT ein  NULL-Wert zugewiesen wurde bzw. ob der zugewiesene

         Wert  gekürzt wurde.  (Mit der  Indikator-Variablen  INDABTNR

         kann  nur   geprüft  werden,  ob  der  zugewiesene  Wert  der

         Host-Variablen  ABTNR gekürzt  wurde, weil  die Spalte abt_nr

         keine NULL-Werte zuläßt.)

 

         Wie  aus Beispiel  16.4 ersichtlich,  kann eine Host-Variable

         denselben  Namen wie  ihre entsprechende  Spalte  haben.  Der

         Vorübersetzer erkennt grundsätzlich die Host- bzw.

         IndikatorVariablen

                   an dem Präfix ":".

 

         Die  Indikator-Variablen dürfen  nicht  in  der  WHERE-  bzw.

         HAVING-Klausel verwendet werden.

 

         Der DECLARE-Abschnitt für Beispiel 16.4 könnte so aussehen:

 

         (COBOL)

      EXEC SQL    BEGIN DECLARE SECTION    END-EXEC.

               77  ABTNR   PIC X(4).

               77  STADT   PIC X(15).

               77  ABTNAM  PIC X(20).

               77  INDABTNR PIC S9(5) COMP.

               77  INDSTADT PIC S9(5) COMP.

                   EXEC SQL    END DECLARE SECTION      END-EXEC.

 

         (C)

   EXEC SQL BEGIN DECLARE SECTION;

                        char ABTNR[5];

                        char STADT[16];

                        char ABTNAM[21];

                        short INDABTNR, INDSTADT;

                        EXEC SQL END DECLARE SECTION;

 

         Im nächsten Beispiel wird die Benutzung der Host-Variablen in

         der HAVING-Klausel gezeigt.

 

         Beispiel 16.5

 

         Die  Host-Variable PRNR  soll mit  der Nummer  des  Projektes

         versorgt werden, an dem genau die dem Wert der Host-Variablen

         ANZAHL entsprechende  Anzahl von  Mitarbeitern beteiligt ist.

         Nach der Ausführung der SELECT-Anweisung soll die Prüfung der

         SQLCODE-Variablen durchgeführt werden.

 

         (COBOL)

    EXEC SQL

                    SELECT pr_nr

                    INTO :PRNR

                    FROM arbeiten

                    GROUP BY pr_nr

                    HAVING COUNT(*) = :ANZAHL

                 END-EXEC.

                 IF SQLCODE OF SQLCA < 0  THEN

                    DISPLAY "Fehler in SELECT"

                    DISPLAY SQLCODE OF SQLCA

                    STOP RUN

                 ELSE DISPLAY "Select-Anweisung ausgefuehrt".

 

         (C)

     EXEC SQL

                        SELECT pr_nr

                        INTO :PRNR

                        FROM arbeiten

                        GROUP BY pr_nr

                        HAVING COUNT(*) = :ANZAHL

                        if (SQLCA.SQLCODE < 0)

                            { printf ("Fehler in SELECT\n");

                            printf ("%d\n", SQLCA.SQLCODE);

                            exit (SQLCA.SQLCODE);

                             }

 

         Die  Fehlerbehandlung   in  Beispiel  16.5  überprüft  zuerst

         den  Wert  der  SQLCODE-Variablen  nach  der  Ausführung  der

         SELECT-Anweisung.  Wenn  dieser  Wert  kleiner  0  ist,  wird

         zuerst   der  Rückgabewert   und  danach   der  entsprechende

         Fehlermeldungstext ausgegeben. Anschließend wird das Programm

         beendet. Wenn  der Wert  der SQLCODE-Variablen  größer gleich

         0 ist,  wird lediglich  die Bestätigung  für die  ausgeführte

         SELECT-Anweisung ausgegeben.

 

         Das  nächste Beispiel zeigt die Verwendung der Host-Variablen

         in der VALUES-Klausel der INSERT-Anweisung.

 

         Beispiel 16.6

 

         Fügen   Sie  eine  Reihe  mit  den  Datenwerten  einer  neuen

         Abteilung ein,  deren Nummer, Name und Standort jeweils durch

         die Host-Variablen ABTNR, ABTNAME und STADT gegeben sind.

 

         (COBOL)

         MOVE "a4" TO ABTNR.

                     MOVE "Test" TO ABTNAME.

                     MOVE "Freiburg" TO STADT.

                     EXEC SQL

                     INSERT INTO abteilung(abt_nr, abt_name, stadt)

                            VALUES(:ABTNR, :ABTNAME, :STADT)

                     END-EXEC.

 

         (C)     

         strcpy(ABTNR, "a4");

                     strcpy(ABTNAME, "Test");

                     strcpy(STADT, "Freiburg");

                     EXEC SQL

                     INSERT INTO abteilung(abt_nr,abt_name,stadt)

                        VALUES (:ABTNR, :ABTNAME, :STADT);

 

         Wie    im     folgenden    Beispiel    ersichtlich,    können

         Indikator-Variablen   verwendet    werden,   um    NULL-Werte

         einzufügen.

 

         Beispiel 16.7

 

         Fügen Sie eine Reihe mit den Datenwerten eines neuen Projekts

         ein, dessen  Nummer, Name und Mittel durch die Host-Variablen

         PRNR,   PRNAME  und   MITTEL  gegeben   sind.  Der  Wert  der

         zugewiesenen Geldmittel ist noch nicht bekannt.

 

         (COBOL)

         MOVE "p1" TO PRNR.

                     MOVE "Mars" TO PRNAME.

                     MOVE -1 TO INDMITTEL.

                     EXEC SQL

                     INSERT INTO PROJEKT(pr_nr, pr_name, mittel)

                         VALUES(:PRNR, :PRNAME, :MITTEL:INDMITTEL)

                     END-EXEC.

 

         (C)       

          strcpy(PRNR,"p1");

                     strcpy(PRNAME, "Mars");

                     INDMITTEL = -1;

                     EXEC SQL

                     INSERT INTO PROJEKT(pr_nr, pr_name, mittel)

                         VALUES(:PRNR, :PRNAME, :MITTEL:INDMITTEL);

 

         Durch  die Zuweisung  des Wertes -1 an die Indikator-Variable

         INDMITTEL vor  der INSERT-Anweisung,  wird der  Datenwert der

         Spalte mittel auf NULL gesetzt.

 

         Das  nächste  Beispiel zeigt die Benutzung der Host-Variablen

         in der SET-Klausel der UPDATE-Anweisung.

 

         Beispiel 16.8

 

         Ändern  Sie die  Aufgabe des  Mitarbeiters in  Projekt p2 mit

         Hilfe  der Host-Variablen  AUFGABE.  Die  Personalnummer  des

         Mitarbeiters ist durch die Host-Variable MNR gegeben.

 

         (COBOL)  

        MOVE "Gruppenleiter" TO AUFGABE.

                    MOVE 18316 TO MNR.

                    EXEC SQL

                    UPDATE arbeiten

                    SET aufgabe = :AUFGABE:INDAUFGABE

                    WHERE m_nr = :MNR

                    AND pr_nr = 'p2'

                    END-EXEC.

 

         (C)  

        strcpy (AUFGABE, "Gruppenleiter");

                    MNR = 18316;

                    EXEC SQL

                    UPDATE arbeiten

                    SET aufgabe = :AUFGABE:INDAUFGABE

                    WHERE m_nr = :MNR

                    AND pr_nr = 'p2';

 

         In  einer  DELETE-Anweisung  können die Host-Variablen in der

         WHERE-Klausel verwendet werden.

 

         Beispiel 16.9

 

         Löschen Sie die Datenwerte des Projektleiters in dem Projekt,

         dessen Nummer durch die Variablen PRNR gegeben ist.

 

         (COBOL)

        MOVE "p1" TO PRNR.

                    EXEC SQL

                    DELETE FROM arbeiten

                    WHERE AUFGABE = 'Projektleiter'

                    AND pr_nr = :PRNR

                    END-EXEC.

 

         (C)

       strcpy (PRNR, "p1");

                   EXEC SQL

                   DELETE FROM arbeiten

                    WHERE AUFGABE = 'Projektleiter'

                    AND pr_nr = :PRNR;

 

         Obwohl   die    Datenmanipulationsanweisungen    von    allen

         SQL-Anweisungen    am   häufigsten   in   eine   Host-Sprache

         eingebettet    werden,   kann    dies    ebenso    mit    den

         Datendefinitions- wie auch mit den Datensteuerungsanweisungen

         durchgeführt  werden.   Das  folgende   Beispiel  zeigt   die

         Einbettung einer CREATE INDEX-Anweisung.

 

 

         Beispiel 16.10

 

         Erstellen  Sie  einen zusammengesetzten Index für die Spalten

         m_nr und einst_dat der Tabelle arbeiten.

 

         (COBOL) 

  EXEC SQL

                    CREATE INDEX i_mnr_dat ON arbeiten(m_nr,einst_dat)

                    END-EXEC.

 

         (C)

     EXEC SQL

                  CREATE INDEX i_mnr_dat ON arbeiten(m_nr,einst_dat);

 

         Das   folgende    Programm   zeigt    die   Verwendung    der

         SELECT-Anweisung in einem COBOL-Programm.

 

         Beispiel 16.11

 

         Finden  Sie  Namen  und  Vornamen  des  Mitarbeiters,  dessen

         Personalnummer am Bildschirm eingegeben wird.

 

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1611.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  MNAME  PIC X(20) VALUE SPACES.

       77  MVORNAME PIC X(20) VALUE SPACES.

       77  MNR    PIC S9(9) COMP.

           EXEC SQL END DECLARE SECTION END-EXEC.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.

       A1.

           PERFORM DBOPEN.

           PERFORM A100-SELECT.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

              CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

           DISPLAY "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       A100-SELECT.

           DISPLAY  "Geben Sie die Mitarbeiternummer an".

           ACCEPT MNR.

           EXEC SQL

              SELECT m_name, m_vorname

                INTO :MNAME, :MVORNAME

                FROM mitarbeiter

                WHERE m_nr = :MNR

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in SELECT"

           ELSE

              DISPLAY "Select ausgefuehrt".

           DISPLAY "Der Mitarbeiter mit der Personalnummer: ", MNR.

           DISPLAY "heisst: ", MNAME, MVORNAME.

       DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         Im  A100-SELECT-Abschnitt   wird   der   Vergleich   in   der

         WHERE-Klausel   der SELECT-Anweisung   durchgeführt  und  die

         ausgewählten  Datenwerte der  Spalten  m_name  und  m_vorname

         werden den  Host-Variablen MNAME und MVORNAME zugewiesen. Das

         Ergebnis der  SELECT-Anweisung ist  immer eine einzige Reihe,

         weil jeder Mitarbeiter eine eindeutige Personalnummer hat.

 

         Der   Abschnitt  DBOPEN   erstellt  die   Verbindung  zu  der

         entsprechenden  Datenbank. Das  wird  mit  der  SQL-Anweisung

         CONNECT durchgeführt. Der letzte Abschnit DBCLOSE beendet die

         Verbindung zwischen der Datenbank und dem Programm.

 

         Die  Fehlerbehandlung   in  Beispiel   16.11,  wie  in  allen

         nachfolgenden Beispielen,  ist mit der expliziten Prüfung der

         SQLCODE-Variablen durchgeführt worden.

 

         Das folgende C-Programm ist mit dem vorherigen COBOL-Beispiel

         identisch.

 

         Beispiel 16.12

 

          /* Beispiel 16.12 */

          EXEC SQL

          INCLUDE sqlca;

          #include <stdio.h>

          EXEC SQL BEGIN DECLARE SECTION;

          char m_name[21 ];

          char m_vorname[21];

          int  m_nr;

          EXEC SQL END DECLARE SECTION;

 

          main()

          {

            EXEC SQL

            WHENEVER SQLERROR

              GO TO :fehler;

            EXEC SQL

            CONNECT beispiel;

            printf("Geben Sie die Nummer des Mitarbeiters an:\n");

            scanf ("%d", &m_nr);

            EXEC SQL

            SELECT  m_name, m_vorname

             INTO  :m_name, :m_vorname

             FROM  mitarbeiter

             WHERE m_nr = :m_nr;

              printf("Mitarbeiter mit der Personalnummer %d\n",m_nr);

              printf("heisst: %s %s\n", m_name ,m_vorname);

              exit(0);

          fehler: printf ("Fehler: %d\n",sqlca.sqlcode);

           EXEC SQL

           DISCONNECT ;

          }

 

         Im  folgenden  Beispiel  wird  die  Änderung  der  Datenwerte

         mittels einer UPDATE-Anweisung gezeigt.

 

         Beispiel 16.13

 

         Ändern  Sie  die Aufgabe jenes Mitarbeiters, dessen Personal-

         und Projektnummer am Bildschirm eingegeben werden.

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1613.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  PRNR     PIC X(4)  VALUE SPACES.

       77  AUFGABE  PIC X(15) VALUE SPACES.

       77  MNR    PIC S9(9) COMP.

       77  IND-AUFGABE PIC S9(5) COMP.

           EXEC SQL END DECLARE SECTION END-EXEC.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.

       A1.

           PERFORM DBOPEN.

           PERFORM A100-SELECT.

           PERFORM A200-UPDATE.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

              CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

           DISPLAY "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       A100-SELECT.

           DISPLAY  "Geben Sie die Mitarbeiternummer an".

           ACCEPT MNR.

           DISPLAY  "Geben Sie die Projektnummer an".

           ACCEPT PRNR.

           EXEC SQL

              SELECT aufgabe

                INTO :AUFGABE

                FROM arbeiten

                WHERE m_nr = :MNR

                AND  pr_nr = :PRNR

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in SELECT"

           ELSE

              DISPLAY "Select ausgefuehrt".

           DISPLAY "Der Mitarbeiter mit der Personalnummer: ", MNR.

           DISPLAY "arbeitet im Projekt ", PRNR, "als ", AUFGABE.

           DISPLAY "Geben Sie die neue Aufgabe des Mitarbeiters an".

           ACCEPT AUFGABE.

       A200-UPDATE.

           EXEC SQL

             UPDATE arbeiten

             SET aufgabe = :AUFGABE:IND-AUFGABE

                WHERE m_nr = :MNR

                AND  pr_nr = :PRNR

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in UPDATE"

           ELSE

              DISPLAY "UPDATE ausgefuehrt".

        DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         Das  Programm   beinhaltet  unter  anderem  zwei  Abschnitte:

         A100-SELECT   und  A200-UPDATE.   Mit  dem  ersten  Abschnitt

         wird die  Aufgabe  des  Mitarbeiters,  dessen  Personal-  und

         Projektnummer eingegeben  sind, ausgewählt  und am Bildschirm

         angezeigt.   Damit  wird   im   Programm   eine   zusätzliche

         Überprüfung   der    Datenwerte   eingebaut.   Im   Abschnitt

         A200-UPDATE wird  schließlich die  Änderung der  Aufgabe  des

         Mitarbeiters durchgeführt.

 

         Das   folgende    C-Programm   entspricht    dem   vorherigen

         COBOL-Beispiel.

 

         Beispiel 16.14

 

          /* Beispiel 16.14 */

          #include <stdio.h>

          EXEC SQL

          INCLUDE sqlca;

          EXEC SQL BEGIN DECLARE SECTION;

          char pr_nr[5];

          char aufgabe[16];

          int  m_nr;

          short ind_aufgabe;

          EXEC SQL END DECLARE SECTION;

 

          main()

          {

           EXEC SQL

           WHENEVER SQLERROR

              GO TO :fehler;

           EXEC SQL

           CONNECT  beispiel;

            printf("Geben Sie die Nummer des Mitarbeiters an:\n");

            scanf ("%d", &m_nr);

            printf("Geben Sie die Projektnummer an\n");

            scanf("%s", pr_nr);

            EXEC SQL

            SELECT  aufgabe

             INTO  :aufgabe

             FROM  arbeiten

             WHERE m_nr = :m_nr

             AND pr_nr = :pr_nr;

           printf("Der Mitarbeiter mit der Personalnummer: %d\n",m_nr);

           printf("ist %s im Projekt %s\n\n",aufgabe ,pr_nr);

           printf("Geben Sie die neue Aufgabe des Mitarbeiters an:\n");

           scanf("%s", aufgabe);

            EXEC SQL

            UPDATE arbeiten

            SET aufgabe = :aufgabe:ind_aufgabe

            WHERE m_nr = :m_nr

            AND  pr_nr = :pr_nr;

           exit(0);

         fehler: printf ("Fehler: %d\n", sqlca.sqlcode);

         EXEC SQL

         DISCONNECT;

         }

 

         INGRES unterstützt die Anweisung

 

             DECLARE tab_name TABLE          ,

 

         die Spalten  einer Tabelle,  gemeinsam mit  ihren  Datentypen

         auflistet.   Diese    Anweisung   ist    keine    ausführbare

         SQL-Anweisung, sondern  dient nur als Kommentar innerhalb des

         Variablendeklarationsteils. Mit  der DECLARE  TABLE-Anweisung

         ist  es lediglich  möglich, die Dokumentation eines Programms

         zu  verbessern.  Sie  wird  auch  vom  Dienstprogramm  DCLGEN

         verwendet.

 

         Das Dienstprogramm DCLGEN ("Declaration Generator") generiert

         automatisch  eine  Struktur  in  der  eingebetteten  Sprache,

         die  analog dem  Schema einer  Tabelle ist.  D.h. jede Spalte

         einer   Tabelle  wird   in  der  generierten  Struktur  durch

         eine  entsprechende Variable  dargestellt. Die  so generierte

         Struktur  erleichtert   das  Einlesen  der  Datenwerte  einer

         Tabellenreihe   in  das  Programm,  bzw.  das  Schreiben  der

         Datenwerte aus dem Programm in eine Tabellenreihe.

 

         Mit dem Betriebssystemkommando

 

             dclgen sprache db_name tab_name dat_name struk_name

 

         kann  dieses   Dienstprogramm   aufgerufen   werden.  sprache

         definiert  die   eingebettete   Programmiersprache,   während

         db_name,   tab_name  und   struk_nmame  jeweils   Datenbank-,

         Tabellen- und Strukturnamen darstellen.

 

         dat_name   ist  der   Name  der  Ausgabedatei,  die  die  von

         DCLGEN   generierte    Strukturdeklaration   enthält.   Diese

         Datei  enthält auch  die  DECLARE  TABLE-Anweisung,  die  als

         Gedächtnisstütze  für das  Tabellenschema  dient.  Mit  Hilfe

         der Vereinbarungsanweisung  INCLUDE ist es möglich, die Datei

         dat_name dem Deklarationsteil eines Programms hinzufügen.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.3     Verwendung des Cursors

 

 

         Im vorherigen  Abschnitt haben  wir gezeigt, wie man Abfragen

         durchführen  kann, wenn die eingebettete SELECT-Anweisung nur

         eine Reihe  als Ergebnis liefert. Wenn die Anzahl der Reihen,

         die eine  SELECT-Anweisung liefert,  im voraus nicht bekannt

         bzw. größer  als eins ist, muß zusätzlicher Aufwand betrieben

         werden,  damit alle  Reihen  der  Abfrage  ausgewählt  werden

         können.  Der Grund  dafür liegt in den Unterschieden zwischen

         SQL einerseits und den Host-Sprachen andererseits.

 

         SQL   ist    eine   mengenorientierte    Sprache.   Mit   den

         Datenmanipulationsanweisungen  wie SELECT,  DELETE usw.  wird

         also   vom  Benutzer   nicht  festgelegt,  wie  der  Weg  zum

         gewünschten Ergebnis  erreicht werden soll, sondern lediglich

         das,  was gemacht  werden soll.  Dadurch kann der Benutzer in

         der  Regel nicht wissen, wieviele Reihen eine Abfrage liefern

         wird.

 

         Die   Host-Sprachen   können   nicht   gleichzeitig   mehrere

         Datensätze  bearbeiten, sondern  holen sich  einen  Datensatz

         nach  dem   anderen.  Damit   dieser   Unterschied   zwischen

         Host-Sprachen  und  SQL  überbrückt  werden  kann,  wird  ein

         Datenpuffer  benutzt, in  welchem dann  alle Reihen,  die als

         Ergebnis einer Abfrage ausgewählt wurden, gespeichert werden.

         Dieser  Datenpuffer wird mit einer Art Zeiger bearbeitet, der

         bei SQL Cursor heißt.

 

         Der  Cursor hat  gewisse Ähnlichkeiten  mit einer Datei. Jede

         Datei muß  zuerst definiert  werden. Danach wird sie eröffnet

         und  anschließend wird  sequentiell ein  Datensatz  nach  dem

         anderen bearbeitet. Am Ende wird die Datei geschlossen.

 

         Für einen  Cursor gilt  dasselbe.  Für  jeden  der  genannten

         Schritte  existiert in  der eingebetteten SQL-Sprache je eine

         zusätzliche   Anweisung,  die  im  interaktiven  Modus  nicht

         vorhanden ist. Diese Anweisungen sind:

 

                     - DECLARE CURSOR,

                     - OPEN,

                     - FETCH    und

                     - CLOSE.

 

         Mit  der  DECLARE CURSOR-Anweisung wird ein Cursor definiert.

         Diese  Anweisung gehört zu den  SQL-Vereinbarungs-Anweisungen

         und sie hat folgende allgemeine Form:

 

          DECLARE cursor_name CURSOR FOR

           select_anw [FOR UPDATE [DEFERRED|DIRECT] OF spalte_1,...]

 

         cursor_name kennzeichnet  den Namen  des definierten Cursors,

         der   eindeutig  innerhalb  eines  Host-Programms  sein  muß.

         Der  Name  kann  eine  Konstante  oder  eine  alphanumerische

         Host-Variable sein  und ist  nur innerhalb des Host-Programms

         bekannt, in dem er definiert wurde.

 

         select_anw  bezeichnet   eine  SELECT-Anweisung   (oder  eine

         Vereinigung  mehrerer   SELECT-Anweisungen  mit   Hilfe   des

         Operators  UNION),   die  fest  mit  dem  Cursor  cursor_name

         verbunden ist.

 

         Jeder Cursor,  der mit  der  "FOR  UPDATE"-Klausel  definiert

         ist, kann  später  in  einer  UPDATE-  bzw.  DELETE-Anweisung

         mit  der   CURRENT  OF-Klausel   benutzt  werden  (siehe  die

         Erklärung für  diese  beiden  Anweisungen  später  in  diesem

         Kapitel). spalte_1,...  kennzeichnet alle  Spalten,  die  mit

         der UPDATE-Anweisung modifiziert werden können. Der DEFERRED-

         bzw. DIRECT-Modus  bei der  FOR UPDATE-Klausel  definiert den

         Zeitpunkt der  Änderungen der  Reihen,  die  mit  dem  Cursor

         definiert sind. Beim DEFERRED-Modus werden alle Änderungen an

         den  Reihen erst beim Schließen des Cursors durchgeführt. Der

         DIRECT-Modus führt die Änderungen der Reihen zu dem Zeitpunkt

         der Ausführung einer UPDATE- bzw. einer DELETE-Anweisung aus.

         Damit werden im DIRECT-Modus die Änderungen an den Reihen für

         das  Programm, das  der Cursor eröffnet hat, gleich sichtbar.

         Falls keiner der beiden  Modi explizit  angegeben  ist,  wird

         DEFERRED standardmäßig angenommen.

 

         Die  DECLARE   CURSOR-Anweisung   muß   vor   jeder   anderen

         SQL-Anweisung   im  Programm   erscheinen,  die  den  in  ihr

         definierten   Cursor  verwenden   will.  Die  Gültigkeit  der

         Cursor-Definition erstreckt sich auf das ganze Host-Programm,

         in dem sie definiert wurde.

 

         Im  Unterschied zu der DECLARE CURSOR-Anweisung sind die drei

         anderen SQL-Anweisungen, die  Cursor  betreffen,  ausführbare

         SQL-Anweisungen. Mit der Anweisung

 

                        OPEN cursor_name  [FOR READONLY]

 

         wird  die SELECT-Anweisung,  die sich  innerhalb der  DECLARE

         CURSOR-Anweisung  befindet, ausgeführt.  Das  Ergebnis  einer

         solchen  SELECT-Anweisung wird  oft die Treffermenge ("active

         set") genannt.  Die optionale  Klausel FOR READONLY teilt dem

         System  mit, daß  die Treffermenge  nur  gelesen,  und  nicht

         geändert wird.

 

         Nach  der Ausführung  der  OPEN-Anweisung  steht  der  Cursor

         vor  der ersten  Reihe der  Treffermenge.  Die  Änderung  der

         Host-Variablen in der WHERE-Klausel der SELECT-Anweisung nach

         der  Ausführung der  OPEN-Anweisung ändert  nicht die  einmal

         festgelegte  Treffermenge. Diese  Änderung hat erst dann eine

         Wirkung, wenn die OPEN-Anweisung noch einmal ausgeführt wird.

         Ein  erneutes Öffnen des Cursors mit Hilfe der OPEN-Anweisung

         bedeutet also  das implizite  Schließen des  Cursors und  das

         Erstellen  eines neuen  Kriteriums für  die  in  der  SELECT-

         Anweisung verwendeten Host-Variablen.

 

         Mit der  FETCH-Anweisung wird  der Cursor  auf  der  nächsten

         Reihe  der Treffermenge  positioniert.  Diese  Anweisung  hat

         folgende Form

 

                    FETCH cursor_name INTO :host_var1[,:host_var2] ...

 

         Der Cursor  muß zuvor  definiert und  eröffnet  werden.  Nach

         der  ersten FETCH-Anweisung  wird die Treffermenge festgelegt

         und der  Cursor auf  die erste Reihe positioniert. Die Reihen

         der Treffermenge  können nur sequentiell abgearbeitet werden.

         Der  Cursor kann  also in  diesem Fall nur vorwärts von einer

         Reihe  zur   nächsten  bewegt  werden.  Die  FETCH-Anweisung,

         die  ausgeführt  wird,  nachdem  der  Cursor  schon  auf  die

         letzte Reihe  der Treffermenge  positioniert war,  weist  der

         SQLCODE-Variablen den Wert 100 zu.

 

         Mit der Anweisung

 

                          CLOSE cursor_name

 

         wird   ein  eröffneter   Cursor   geschlossen.   Mit   dieser

         Anweisung wird  der Cursor  nur inaktiv;  eine  anschließende

         OPEN-Anweisung  eröffnet  ihn  wieder.  Durch  eine  Änderung

         der   Host-Variablen,  die  sich  in  der  WHERE-Klausel  der

         SELECT-Anweisung   befinden,  kann   das  Kriterium  für  die

         Treffermenge  neu   definiert  werden.  Eine  FETCH-Anweisung

         mit einem  geschlossenen Cursor  wird vom  System  mit  einer

         Fehlermeldung   abgewiesen.  Die   Anweisungen   COMMIT   und

         ROLLBACK schließen alle eröffneten Cursor.

 

         Das folgende  Beispiel zeigt  die Verwendung  des Cursors  in

         einem COBOL-Programm.

 

         Beispiel 16.15

 

         Finden    Sie    Personalnummer,    Namen,    Vornamen    und

         Abteilungsnummer  der   Mitarbeiter,   deren   Personalnummer

         kleiner  als der  am Bildschirm  eingegebene  Wert  ist.  Die

         ausgewählten  Reihen  sollen  nach  Personalnummern  sortiert

         werden.

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1615.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  MNAME  PIC X(20) VALUE SPACES.

       77  MVORNAME PIC X(20) VALUE SPACES.

       77  ABTNR  PIC X(4) VALUE SPACES.

       77  MNR    PIC S9(9) COMP.

       77  MINZAHL PIC S9(9) COMP.

           EXEC SQL END DECLARE SECTION END-EXEC.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.

       A1.

           PERFORM DBOPEN.

           PERFORM A100-DECLARE.

           PERFORM A200-OPEN.

           PERFORM A300-FETCH  UNTIL SQLCODE OF SQLCA = 100.

           PERFORM A400-CLOSE.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

              CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

           DISPLAY "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       A100-DECLARE.

           DISPLAY "Geben Sie die obere Grenze an".

           ACCEPT MINZAHL.

           EXEC SQL

             DECLARE crs_mit CURSOR FOR

              SELECT m_nr, m_name, m_vorname, abt_nr

                FROM mitarbeiter

                WHERE m_nr < :MINZAHL

                ORDER BY m_nr

           END-EXEC.

       A200-OPEN.

           EXEC SQL

             OPEN crs_mit

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in OPEN".

       A300-FETCH.

           EXEC SQL

              FETCH crs_mit

               INTO :MNR, :MNAME, :MVORNAME, :ABTNR

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in FETCH".

           IF SQLCODE OF SQLCA NOT = 100

           DISPLAY MNR," ",MNAME," ",MVORNAME," ",ABTNR.

       A400-CLOSE.

           EXEC SQL

               CLOSE crs_mit

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in CLOSE".

       DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         In Beispiel  16.15 existieren  vier  Programmabschnitte,  die

         genau  den vier beschriebenen SQL-Anweisungen DECLARE CURSOR,

         OPEN, FETCH  und CLOSE entsprechen. Im Abschnitt A100-DECLARE

         wird der  Cursor crs_mit  definiert. Der  A200-OPEN-Abschnitt

         führt die OPEN-Anweisung aus. Die Festlegung der Treffermenge

         und   die  Ausführung  der  FETCH-Anweisung  für  jede  Reihe

         wird  im Programmabschnitt  A300-FETCH durchgeführt.  Mit dem

         A400-CLOSE-Abschnitt wird der Cursor crs_mit geschlossen.

 

         Das folgende C-Programm ist mit dem vorherigen COBOL-Programm

         identisch.

 

         Beispiel 16.16

 

          /* Beispiel 16.16 */

          #include <stdio.h>

          EXEC SQL

          INCLUDE sqlca;

          EXEC SQL BEGIN DECLARE SECTION;

          char m_name[21];

          char m_vorname[21];

          int m_nr, min_zahl;

          EXEC SQL END DECLARE SECTION;

 

          main()

          {

            EXEC SQL

            WHENEVER SQLERROR

               GO TO :fehler;

            EXEC SQL

            WHENEVER NOT FOUND

               GOTO :schlies;

            EXEC SQL

            CONNECT  beispiel;

             printf("Geben Sie die obere Grenze an\n");

             scanf ("%d", &min_zahl);

             EXEC SQL

             DECLARE crs_mit CURSOR FOR

              SELECT m_nr, m_name, m_vorname

               FROM  mitarbeiter

               WHERE m_nr < :min_zahl

               ORDER by m_nr;

             EXEC SQL

               OPEN crs_mit;

            for (;;)

             {

              EXEC SQL

              FETCH crs_mit

                INTO :m_nr, :m_name, :m_vorname;

                printf("%d %s%s\n",m_nr,m_name,m_vorname);

             }

            schlies:

             EXEC SQL

             CLOSE crs_mit;

             EXEC SQL

             DISCONNECT;

            exit(0);

         fehler: printf ("Fehler: %d\n", sqlca.sqlcode);

         }

 

         In  der  DECLARE  CURSOR-Anweisung - wie aus ihrer Definition

         ersichtlich - existiert eine zusätzliche Klausel

 

                   FOR UPDATE OF spalte_1 [,spalte_2,...]

 

         Mit  dieser   Klausel  werden  die  ausgewählten  Reihen  mit

         Hilfe eines  Cursors geändert   bzw. gelöscht.

 

         Die  Voraussetzung   für  das   Ändern  der  Datenwerte  bzw.

         das Löschen  der Reihen  ist das  Vorhandensein  der  CURRENT

         OF-Klausel  innerhalb der  UPDATE- bzw. DELETE-Anweisung. Die

         UPDATE-Anweisung hat dann folgende Form

 

                     UPDATE tab_name

                       SET (sp_1=ausdr_1,...)

                       WHERE CURRENT OF cursor_name

 

         cursor_name ist  der Name  des Cursors,  der in  der  DECLARE

         CURSOR-Anweisung mit  der FOR  UPDATE-Klausel definiert  ist.

         (Die DELETE-Anweisung hat eine analoge Form.)

 

         Der   Cursor  cursor_name   muß  eröffnet  werden,  bevor  er

         innerhalb  der UPDATE-  bzw. DELETE-Anweisung verwendet wird.

         Die UPDATE-Anweisung  ändert und  die DELETE-Anweisung löscht

         die  Reihe, auf  welcher der  Cursor positioniert  ist.  Nach

         der Ausführung  der UPDATE-  bzw. DELETE-Anweisung  wird  die

         Position  des   Cursors  nicht  geändert.  Erst  die  nächste

         FETCH-Anweisung positioniert den Cursor auf die nächste Reihe

         der Treffermenge.

 

         Das  folgende  Beispiel zeigt die Verwendung des Cursors beim

         Löschen der Reihen.

 

         Beispiel 16.17

 

         Finden  Sie  alle  Reihen  der Tabelle arbeiten, deren Spalte

         m_nr  dem  eingegebenen  Wert entspricht, und löschen Sie sie

         anschließend.

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1617.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  PRNR  PIC X(4) VALUE SPACES.

       77  MNR    PIC S9(9) COMP.

       77  AUFGABE  PIC X(15) VALUE SPACES.

           EXEC SQL END DECLARE SECTION END-EXEC.

       77  JA-NEIN PIC X.

       PROCEDURE DIVISION.

           PERFORM DBOPEN.

           PERFORM A100-DECLARE.

           PERFORM A200-OPEN.

           PERFORM A300-FETCH  UNTIL SQLCODE OF SQLCA = 100.

           PERFORM A400-CLOSE.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

              CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

           DISPLAY "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       INF99. EXIT.

       A100-DECLARE.

           DISPLAY "Geben Sie die Personalnummer an".

           ACCEPT MNR.

           EXEC SQL

           DECLARE crs_arb CURSOR FOR

             SELECT m_nr, pr_nr, aufgabe

               FROM arbeiten

               WHERE m_nr = :MNR

               FOR UPDATE OF pr_nr

           END-EXEC.

       A200-OPEN.

           EXEC SQL

             OPEN crs_arb

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in OPEN".

       A300-FETCH.

           EXEC SQL

              FETCH crs_arb

                INTO :MNR, :PRNR, :AUFGABE

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in FETCH".

           IF SQLCODE OF SQLCA NOT  = 100

              THEN PERFORM A310-ABFRAGE.

       A400-CLOSE.

           EXEC SQL

             CLOSE crs_arb

           END-EXEC.

           IF SQLCODE OF SQLCA < 0   THEN

              DISPLAY "Fehler in CLOSE".

       A310-ABFRAGE.

           DISPLAY MNR, " ", PRNR, " ", AUFGABE.

           DISPLAY "Moechten Sie diese Reihe loeschen j/n?".

           ACCEPT JA-NEIN.

           IF JA-NEIN = "j"

           THEN PERFORM A311-DELETE.

       A311-DELETE.

           EXEC SQL

              DELETE FROM arbeiten

                WHERE CURRENT OF crs_arb

           END-EXEC.

       DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         In  diesem Beispiel  - ähnlich  wie in  Beispiel 16.13 - wird

         jede Reihe  vor dem  Löschen auf dem Bildschirm angezeigt und

         erst nach  der expliziten  Bestätigung gelöscht.  Mit  dieser

         Maßnahme wird  eine zusätzliche Kontrolle vor dem Löschen der

         Reihen eingeführt.

 

         Das C-Programm  in Beispiel  16.18 verhält  sich  analog  zum

         vorherigen Beispiel.

 

         Beispiel 16.18

 

          /* Beispiel 16.18 */

          #include <stdio.h>

          EXEC SQL

           INCLUDE sqlca;

          EXEC SQL BEGIN DECLARE SECTION;

          int  m_nr;

          char pr_nr[5];

          char aufgabe[16];

          EXEC SQL END DECLARE SECTION;

 

          main()

          { char ja_n[1];

            EXEC SQL

            WHENEVER SQLERROR

               GO TO :fehler;

            EXEC SQL

            WHENEVER NOT FOUND

               GO TO schlies;

            EXEC SQL

            CONNECT  beispiel;

            printf("Geben Sie die Nummer des Mitarbeiters an:\n");

            scanf ("%d", &m_nr);

            EXEC SQL

            DECLARE crs_arb CURSOR FOR

             SELECT m_nr , pr_nr, aufgabe

             FROM arbeiten

             WHERE m_nr = :m_nr

             FOR UPDATE OF m_nr;

            EXEC SQL

            OPEN crs_arb;

            for (;;)

             {

               EXEC SQL

               FETCH crs_arb

                 INTO :m_nr, :pr_nr, :aufgabe;

                 printf("%d %s%s\n",m_nr, pr_nr, aufgabe);

                 printf("Moechten Sie diese Reihe loeschen: j/n?\n");

                 scanf ("%s",&ja_n);

                 if (strcmp(ja_n,"n"))

                 {

                   EXEC SQL

                   DELETE FROM arbeiten

                      WHERE CURRENT OF crs_arb;

                 }

             }

            schlies:

            EXEC SQL

            CLOSE crs_arb;

            EXEC SQL

            DISCONNECT;

            exit(0);

           fehler: printf ("Fehler: %d\n", sqlca.sqlcode);

          }

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.4     Übersetzung eines ESQL/C- bzw. ESQL/COBOL-Programms

 

 

         Sowohl  die INGRES-Schnittstelle  zu C  als auch die zu COBOL

         verwendet einen Vorübersetzer, mit dem dann die eingebetteten

         SQL-Programme   in  C   bzw.  COBOL   übersetzt  werden.  Der

         Aufruf  des jeweiligen  Vorübersetzers geschieht  mittels des

         Betriebssystemkommandos esqlc  für C  bzw. esqlcbl für COBOL.

         Weil diese  beiden Kommandos  die gleiche  Syntax haben, wird

         nur das Kommando esqlc beschrieben.

 

         Das Kommando

 

                     esqlc sch_liste datei_name

 

         ermöglicht die  Übersetzung eines eingebetteten SQL-Programms

         in  C.   datei_name  ist   der  Name   der  Datei,   die  das

         eingebettete SQL-Programm  enthält. Zu  der sch_liste  können

         ein   oder  mehrere  Schalter  gehören,  von  denen  wir  die

         wichtigsten erläutern  werden.  Der  Schalter  "-l"  schreibt

         alle  Fehlermeldungen der  Vorübersetzerphase sowohl  in eine

         Ausgabeliste  als  auch  auf  den  Bildschirm.  Der  Schalter

         "-lo" entspricht  dem  Schalter  "-l",  nur  wird  zusätzlich

         der generierte  C-Code auch  in die Ausgabeliste geschrieben.

         Mit  dem   Schalter  "-f   dat_name"  wird  die  Ausgabe  der

         Vorübersetzerphase  in die  Datei dat_name geschrieben. Falls

         dat_name nicht  angegeben wird,  erfolgt die  Ausgabe auf die

         Standard-Ausgabe. Der Schalter "-s" veranlaßt die Eingabe des

         eingebetteten  SQL-Programms von der Standard-Eingabe und die

         Ausgabe des erstellten Codes auf die Standard-Ausgabe.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.5     Eingebettete Anweisungen und Programmierstil

 

 

         In  diesem Abschnitt  werden  wir  uns  einem  Thema  widmen,

         das  die  Programmierung  der  Datenbankanwendung  wesentlich

         beeinflussen kann.  Es handelt  sich um  allgemeine Hinweise,

         die sowohl den Ablauf der Anwendungen beschleunigen, als auch

         helfen können, mögliche Fehlerquellen zu umgehen.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.5.1  Optimierung der Datenbankzugriffe

 

 

         Den   ersten  und   gleichzeitig  wichtigsten   Punkt  stellt

         die   optimale    Programmierung    von    Datenbankzugriffen

         dar.    In    den    Datenbankanwendungen    werden    häufig

         Datenmanipulationsanweisungen   verwendet,  die  gleichzeitig

         viele  Reihen einer Tabelle auswählen bzw. ändern. In solchen

         Fällen ist es immer besser, die Treffermenge aller Reihen mit

         einer SQL-Anweisung (SELECT, UPDATE oder DELETE) festzulegen,

         als  für jede  Reihe einmal  die entsprechende  SQL-Anweisung

         durchzuführen.  Der  Vorteil  der  ersten  Programmierungsart

         ist,  daß die  entsprechende  SQL-Anweisung  eine  wesentlich

         geringere Anzahl von E/A-Operationen erfordert und nur einmal

         vorübersetzt werden muß.

 

         Das   folgende    Beispiel   zeigt,   wie   man   mit   einer

         SELECT-Anweisung  eine   größere  Anzahl   von  Reihen  einer

         Tabelle  effizient auswählen  sollte.  Für  dieses  und  drei

         folgende  Beispiele wird die Beispieldatenbank verwendet, mit

         einer zusätzlichen  Annahme: Wir  gehen davon  aus,  daß  die

         Tabelle  mitarbeiter, die  in allen  Beispielen benutzt wird,

         insgesamt 100000 Reihen hat, wobei die Spalte m_nr eindeutige

         Datenwerte zwischen 1 und 100000 aufweist.

 

         Beispiel 16.19

 

         Finden  Sie  Namen  und  Vornamen  aller  Mitarbeiter,  deren

         Personalnummer vierstellig ist.

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1619.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  MNAME  PIC X(20) VALUE SPACES.

       77  MVORNAME PIC X(20) VALUE SPACES.

       77  VAR1   PIC S9(9) COMP.

           EXEC SQL END DECLARE SECTION END-EXEC.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.

       A1.

           PERFORM DBOPEN.

           PERFORM A100-DECLARE.

           PERFORM A200-OPEN.

           PERFORM A300-FETCH  VARYING VAR1 FROM 1000 BY 1

                               UNTIL VAR1 > 9999.

           PERFORM A400-CLOSE.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

             CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

           DISPLAY  "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       A100-DECLARE.

           EXEC SQL

             DECLARE crs_mit CURSOR FOR

              SELECT  m_name, m_vorname

                FROM mitarbeiter

                WHERE m_nr BETWEEN 1000 AND 9999

           END-EXEC.

       A200-OPEN.

           EXEC SQL

             OPEN crs_mit

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

           DISPLAY  "FEHLER IN OPEN".

       A300-FETCH.

           EXEC SQL

              FETCH crs_mit

               INTO  :MNAME, :MVORNAME

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

           DISPLAY  "FEHLER IN FETCH".

           IF SQLCODE OF SQLCA NOT = 100  THEN

           DISPLAY MNAME," ",MVORNAME.

       A400-CLOSE.

           EXEC SQL

             CLOSE crs_mit

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

           DISPLAY  "FEHLER IN CLOSE".

       DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         In  Abschnitt A100-DECLARE wird der Cursor crs_mit definiert,

         mit dem dann alle Reihen, die die  Treffermenge  bilden,  mit

         einer SELECT-Anweisung ausgewählt werden.

 

         Das  folgende  Beispiel zeigt eine ineffiziente Art, dieselbe

         Aufgabe wie in Beispiel 16.19 zu lösen.

 

         Beispiel 16.20

 

       IDENTIFICATION DIVISION.

       PROGRAM-ID. BSP1620.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

           EXEC SQL BEGIN DECLARE SECTION END-EXEC.

       77  MNAME  PIC X(20) VALUE SPACES.

       77  MVORNAME PIC X(20) VALUE SPACES.

       77  VAR1   PIC S9(9) COMP.

           EXEC SQL END DECLARE SECTION END-EXEC.

           EXEC SQL INCLUDE SQLCA END-EXEC.

       PROCEDURE DIVISION.

       A1.

           PERFORM DBOPEN.

           PERFORM A100-SELECT VARYING VAR1 FROM 1000 BY 1

                                UNTIL VAR1 > 9999.

           PERFORM DBCLOSE.

           STOP RUN.

       DBOPEN.

           EXEC SQL

             CONNECT  BEISPIEL

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

           DISPLAY  "FEHLER BEIM DB-OEFFNEN"

           STOP RUN.

       A100-SELECT.

           EXEC SQL

             SELECT m_name, m_vorname

                INTO :MNAME :MVORNAME

                FROM mitarbeiter

                WHERE m_nr = :VAR1

           END-EXEC.

           IF SQLCODE OF SQLCA < 0 THEN

              DISPLAY "FEHLER IN SELECT".

           DISPLAY MNAME, MVORNAME.

        DBCLOSE.

           EXEC SQL

           DISCONNECT

           END-EXEC.

 

         Die   Anzahl  der   E/A-Operationen  in  Beispiel  16.20  ist

         wesentlich  höher als  in  Beispiel  16.19.  Zusätzlich  dazu

         wird  der Abschnitt  A100-SELECT und  damit auch  die  SELECT

         Anweisung 9000  vorübersetzt. Dadurch  wird ein Programm, das

         einen solchen  Teil beinhaltet,  wesentlich langsamer als ein

         Programm, das,  wie in  Beispiel 16.19  gezeigt  programmiert

         wurde.

 

         Das folgende Beispiel zeigt ein C-Programm, das identisch mit

         Beispiel  16.19 ist  und die effizientere Programmierart in C

         zeigt.

 

         Beispiel 16.21

 

          /* Beispel 16.21 */

          #include <stdio.h>

          EXEC SQL

          INCLUDE sqlca;

          EXEC SQL BEGIN DECLARE SECTION;

          char m_name[21];

          char m_vorname[21];

          int i;

          EXEC SQL END DECLARE SECTION;

          main()

          {

             EXEC SQL

             WHENEVER SQLERROR

               GO TO :fehler;

             EXEC SQL

             WHENEVER NOT FOUND

               GO TO :schlies;

             EXEC SQL

             CONNECT beispiel;

             EXEC SQL

             DECLARE crs_mit CURSOR FOR

                SELECT m_name, m_vorname

                  FROM mitarbeiter

                  WHERE m_nr BETWEEN 1000 AND 9999;

             EXEC SQL

             OPEN crs_mit;

             for (i=1000; i<=9999; i++)

               {

                EXEC SQL

                FETCH crs_mit INTO :m_name, :m_vorname;

                printf("%s%s\n", m_name, m_vorname);

               }

            schlies:

            EXEC SQL

            CLOSE crs_mit;

            EXEC SQL

            DISCONNECT;

            exit(0);

            fehler: printf ("Fehler: %d\n", sqlca.sqlcode);

           }

 

         Die ineffiziente Form dieses Programms zeigt Beispiel 16.22.

 

         Beispiel 16.22

 

          /* Beispel 16.22 */

          #include <stdio.h>

          EXEC SQL

          INCLUDE sqlca;

          EXEC SQL BEGIN DECLARE SECTION;

          char c_name[21];

          char c_vorname[21];

          int i;

          EXEC SQL END DECLARE SECTION;

          main()

          {

             EXEC SQL

             WHENEVER SQLERROR

                GO TO :fehler;

             EXEC SQL

             CONNECT  beispiel;

             for (i=1000; i<=9999; i++)

               {

                EXEC SQL

                SELECT m_name, m_vorname

                   INTO :c_name, :c_vorname

                   FROM mitarbeiter

                   WHERE m_nr = :i;

                printf("%s%s\n", c_name , c_vorname);

               }

            EXEC SQL

            DISCONNECT;

            exit(0);

            fehler: printf ("Fehler: %d\n", sqlca.sqlcode);

           }

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.5.2  Explizite Abfrage bei der Änderung von Reihen

 

 

         Zu  gutem  Programmierstil gehört auch die explizite Abfrage,

         ob eine Reihe geändert bzw. gelöscht werden soll.

 

         Wenn  eine   Reihe   mittels   einer   UPDATE-   bzw.   einer

         DELETE-Anweisung   bearbeitet    werden    soll,    ist    es

         empfehlenswert, die  Datenwerte dieser  Reihe  am  Bildschirm

         zuerst  anzuzeigen, bevor  sie geändert  bzw. gelöscht werden

         sollten.  Damit gibt  man dem  Anwender die Möglichkeit, sich

         zu vergewissern, ob es sich tatsächlich um die Reihe handelt,

         die er modifizieren will.

 

         Die  Beispiele 16.17 und 16.18 zeigen diesen Programmierstil.

         Bevor  eine Reihe  der Tabelle arbeiten gelöscht wird, werden

         die Datenwerte dieser Reihe am Bildschirm angezeigt, und erst

         gelöscht, wenn der Anwender dies explizit bestätigt.

 

Zurück zum Inhaltsverzeichnis

 

 

 

16.5.3  Explizite Angabe aller Reihen in einer SELECT- bzw.

                               INSERT-Anweisung

 

 

         In der  Projektion einer SELECT-Anweisung ist es möglich, die

         Angabe  aller Spaltennamen durch das Zeichen "*" zu ersetzen.

         Bei der  interaktiven Anwendung  einer SELECT-Anweisung  sind

         beide  Schreibweisen gleich  gut. Dies  ist  aber  nicht  bei

         den  eingebetteten  Anweisungen  der  Fall,  bei  denen  alle

         Spaltennamen  in   einer  SELECT-Anweisung   angegeben   sein

         sollten.

 

         Die explizite  Angabe aller  Spaltennamen ist  wichtig,  weil

         jedes Ändern  des Tabellenschemas  einen Fehler  im  Programm

         verursachen kann.  In  diesem  Fall  stimmt  die  Anzahl  der

         Spalten  mit der Anzahl der ihnen zugewiesenen Host-Variablen

         nicht mehr überein.

 

         Beispiel  16.23  zeigt,  wie  die  SELECT-Anweisung  für  die

         Tabelle  mitarbeiter in einem COBOL-Programm geschrieben sein

         sollte.

 

         Beispiel 16.23

 

         EXEC SQL

             SELECT m_nr, m_name, m_vorname, abt_nr

             INTO :MNR, :MNAME, :MVORNAME, :ABTNR

             FROM mitarbeiter

             WHERE m_nr < MINZAHL

         END-EXEC.

 

Zurück zum Inhaltsverzeichnis

 

 

 

Aufgaben

 

             Alle    Aufgaben   dieses   Kapitels   sollen   mit   der

             Programmiersprache Ihrer Wahl programmiert werden.

     

A.16.1 Finden Sie die Aufgabe des Mitarbeiters, dessen Personal-

             und Projektnummer am Bildschirm eingegeben werden.

     

A.16.2 Fügen  Sie   die  Reihe   mit  Namen,  Vornamen  und  der

             Personalnummer   eines  neuen   Mitarbeiters   ein.   Die

             Mitarbeiterdaten sollen am Bildschirm eingegeben werden.

     

A.16.3 Finden Sie die Aufgabe und Projektnummer des Mitarbeiters

             mit der Personalnummer 28559.

    

A.16.4 Vergleichen Sie  die  Laufzeiten  der  Programme  in  den

             Beispielen 16.19 und 16.20 bzw. 16.21 und 16.22.

 

Zurück zum Inhaltsverzeichnis