Programmeren in COBOL/Embedded SQL
In COBOL kan SQL
-code opgenomen worden om gegevens uit databank
en 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