Programmeren in COBOL/Embedded SQL
In COBOL kan SQL-code opgenomen worden om gegevens uit databanken te gebruiken en te bewerken. Een voorbeeld is DB2 op mainframe (z/OS). De precompiler haalt alle blokken die beginnen met EXEC SQL en eindigen met END-EXEC eruit, en laat ze door DB2 binden.
Singleton SELECT
[bewerken]Indien we slechts één rij zullen lezen:
COBOL + embedded SQL-code: Singleton SELECT
IDENTIFICATION DIVISION. PROGRAM-ID. Student. DATA DIVISION. WORKING-Storage section. 01 studid PIC X(7). 01 achternaam PIC X(10). PROCEDURE DIVISION. HOOFD. DISPLAY "Geef het studentennummer" ACCEPT studid EXEC SQL SELECT achtern INTO :achternaam FROM leerlingen WHERE sid = :studid END-EXEC DISPLAY "De gezochte persoon heeft als achternaam " achternaam STOP RUN.
De COBOL-variabelen worden in de SQL-statements voorafgegaan door een dubbelpunt. Op dezelfde manier kan een UPDATE of INSERT uitgevoerd worden:
COBOL + embedded SQL-code: Singleton UPDATE
EXEC SQL UPDATE leerlingen SET achtern = :achternaam WHERE sid = :studid END-EXEC
COBOL + embedded SQL-code: Singleton INSERT
EXEC SQL INSERT INTO leerlingen (sid, achtern) VALUES (:studid, :achternaam) END-EXEC
Indien je er niet zeker van bent of je slechts één resultaat zal hebben (omdat je niet selecteert op basis van een unieke index (zoals een primaire sleutel), dan kan je gebruik maken van FETCH FIRST ROW ONLY.
COBOL + embedded SQL-code: FETCH FIRST ROW ONLY
EXEC SQL SELECT achtern INTO :achternaam FROM leerlingen WHERE sid = :studid FETCH FIRST ROW ONLY END-EXEC
SELECT met CURSOR
[bewerken]Bij meerdere rijen, laat een CURSOR ons toe om de opgevraagde tabel die door het DBMS bijgehouden wordt, rij per rij op te vragen. Zoals bij het werken met bestanden, moet de query geopend (OPEN), gelezen (FETCH) en weer gesloten (CLOSE) worden. Bij OPEN wordt hij in het geheugen geladen en beschikbaar gehouden tot het CLOSE-commando dit geheugen weer vrijmaakt.
COBOL + embedded SQL-code: Meervoudige SELECT
IDENTIFICATION DIVISION. PROGRAM-ID. Student. DATA DIVISION. WORKING-Storage section. 01 voornaam PIC X(10). EXEC SQL INCLUDE SQLCA END-EXEC. EXEC SQL INCLUDE leraar-tabel-declaratie END-EXEC. EXEC SQL DECLARE crs CURSOR FOR SELECT voorn FROM leraren WHERE vak = 'WISKUNDE' END-EXEC. PROCEDURE DIVISION. HOOFD. DISPLAY "Hierna volgen alle voornamen van leraren die wiskunde geven: EXEC SQL OPEN crs END-EXEC PERFORM UNTIL SQLCODE NOT = 0. EXEC SQL FETCH crs INTO :voornaam END-EXEC DISPLAY voornaam END-PERFORM EXEC SQL CLOSE crs END-EXEC STOP RUN.
In bovenstaand programma wordt met INCLUDE extra COBOL-code van elders opgehaald door de precompiler. SQLCA (SQL communication area) wordt automatisch door het DBMS gegenereerd en bevat de foutcode SQLCODE en . Een tabeldefinitie kan met een commando (DCLGEN) aangemaakt worden, samen met de declaratie van de gelijknamige overeenkomstige COBOL-variabelen. Dit gegenereerde bestand kan ook met INCLUDE opgehaald worden. Op die manier hoeft de programmeur niet uit te vissen van welk overeenkomstig COBOL-datatype de DB2-variabelen zijn.
COBOL + embedded SQL + embedded SQL-code: leraar-tabel-declaratie (voor INCLUDE)
01 leraar. 02 lid PIC 9(3). 02 voorn PIC X(10). 02 vak PIC X(10). EXEC SQL DECLARE leraren ( lid SMALLINT NOT NULL, voorn CHAR(10), vak CHAR(10) ) END-EXEC.
Hieronder een overzicht van hoe DB2-variabelen kunnen omgezet worden naar COBOL-variabelen:
SQL-DB2 | COBOL |
---|---|
var CHAR(4), |
var PIC X(4). |
SMALLINT, |
PIC S9(4) COMP. |
DECIMAL (9,2), |
PIC S9(7)V9(2) COMP-3. |
var VARCHAR (60), |
01 var. 49 lengte PIC S9(4) COMP. 49 tekst PIC X(60). |
Merk op dat bij een VARCHAR, de overeenkomstige COBOL-variabele opgesplitst wordt in twee level-49 variabelen waarvan de eerst de 2 extra bytes voorstelt die de eigenlijke lengte van het veld bijhouden.
UPDATE met CURSOR
[bewerken]Het volstaat om FOR UPDATE OF variabele toe te voegen, zodat er een update-lock (U; zie locking) op de tabel geplaatst wordt door DB2. Hierdoor kunnen andere gebruikers niet tegelijkertijd deze rij wijzigen, waardoor inconsistente data zouden ontstaan. Met WHERE CURRENT OF crs kan je dan aanduiden waar er iets moet veranderen.
COBOL + embedded SQL-code: Meervoudige UPDATE
EXEC SQL DECLARE crs CURSOR FOR SELECT voorn FROM leraren WHERE vak = 'WISKUNDE' FOR UPDATE OF vak END-EXEC....
EXEC SQL OPEN crs END-EXEC EXEC SQL FETCH crs INTO :voornaam END-EXEC PERFORM update UNTIL SQLCODE NOT = 0 EXEC SQL CLOSE crs END-EXEC update. EXEC SQL UPDATE leraren SET vak = 'MEETKUNDE' WHERE CURRENT OF crs END-EXEC EXEC SQL FETCH crs INTO :voornaam END-EXEC .
NULLs
[bewerken]Databanksystemen kennen naast (alfa)numerieke waarden, ook het concept "NULL", met alle problemen van dien (zie driewaardige logica). Bij embedded SQL kan een extra NULL-indicator gedefinieerd worden, die -1 wordt indien er een NULL overgedragen wordt tussen het databanksysteem en COBOL: "01 indicator PIC S9(4) COMP." In een SQL-statement wordt dan de indicator onmiddellijk na de variabele geplaatst (met of zonder spatie ertussen): "... INTO :voornaam:indicator ... "
COBOL + embedded SQL-code: NULL-indicator
01 naam PIC X(10). 01 naam-i PIC S9(4) COMP. 01 postnr PIC 9(4). 01 post-i PIC S9(4) COMP....
EXEC SQL SELECT dorpnaam, postnummer INTO :naam:naam-i, :postnr:post-i FROM dorpen WHERE postnummer = :postnr END-EXEC
COBOL + embedded SQL-code: NULL-indicator-rij
01 dorp. 02 naam PIC X(10). 02 postnr PIC 9(4). 01 indicator PIC S9(4) COMP OCCURS 2....
EXEC SQL SELECT dorpnaam, postnummer INTO :naam:indicator(1), :postnr:indicator(2) FROM dorpen WHERE postnummer = :postnr END-EXEC
Omdat SQLCODE uit SQLCA van het datatype COMP is, en dus niet toonbaar is, moet je ze eerst overbrengen naar een zelf te definiëren variabele van het type PIC S9(3):
COBOL + embedded SQL-code: NULL-indicator-test
01 leesbare-code PIC S9(3)....
MOVE SQLCODE TO leesbare-code IF SQLCODE < 0 DISPLAY 'Even Apeldoorn bellen: error ' leesbare-code ELSE DISPLAY 'Alles OK' END-IF
Dynamische SQL
[bewerken]Sommige programma's moeten telkens een andere SQL-code uitvoeren. Denk maar aan het programma waarmee je SQL leert en verschillende SQL-statements uittest. Met PREPARE kan je een SQL-statement stap voor stap opbouwen; Met IMMEDIATE kan je een SQL-statement onmiddellijk uitvoeren:
COBOL + embedded SQL-code: PREPARE
01 sqltekst. 02 lengte PIC S9(4) COMP VALUE 250. 02 inhoud PIC X(250). 01 pid PIC X(4) EXEC SQL DECLARE crs CURSOR FOR STMT END-EXEC....
MOVE 'SELECT pid FROM personen' TO inhoud MOVE 'WHERE pid > ?' TO inhoud(25:) EXEC SQL PREPARE STMT FROM :sqltekst END-EXEC ACCEPT pid EXEC SQL OPEN crs USING :pid END-EXECEXEC SQL FETCH crs INTO :pid END-EXEC PERFORM UNTIL SQLCODE NOT = 0 DISPLAY 'pid = ' pid EXEC SQL FETCH crs INTO :pid END-EXEC END-PERFORMMOVE 'CLOSE crs' TO inhoud EXEC SQL EXECUTE IMMEDIATE :sqltekst END-EXEC