Most z/OS business programs do not type SQL into SPUFI. They embed SQL inside COBOL (or PL/I, C, assembler) so a payroll run, a CICS inquiry, or an IMS transaction can read and update DB2 tables with host variables, cursors, and proper units of work. This page introduces embedded SQL, the EXEC SQL / END-EXEC delimiters, static versus dynamic SQL, how SQL PL differs from COBOL SQL, and the three environments you will actually ship: batch, CICS, and IMS.
Embedded SQL means SQL statements are part of the application source. Db2 does not compile COBOL, and the COBOL compiler does not understand SELECT. A precompiler (DSNHPC) or the Db2 coprocessor inside the COBOL compiler finds the SQL, checks it enough to generate a DBRM, and replaces it with a call to the language interface. You then compile, link-edit, and BIND as described on the compile-and-bind JCL page.
Statements fall into two practical groups:
After every executable statement you inspect SQLCODE (and preferably SQLSTATE). Zero means success, +100 is not found, +nnn other positives are warnings, and negative codes are errors. Never assume a FETCH worked because the program did not abend.
In COBOL, every SQL statement sits between EXEC SQL and END-EXEC. Put EXEC in Area A (column 8) or in Area B according to shop standard; both are common. End the COBOL statement with a period after END-EXEC when the SQL is a complete imperative statement.
12345678910111213141516EXEC SQL INCLUDE SQLCA END-EXEC. EXEC SQL SELECT LASTNAME INTO :HV-LASTNAME FROM HR.EMPLOYEE WHERE EMPNO = :HV-EMPNO END-EXEC. IF SQLCODE = 0 DISPLAY 'OK ' HV-LASTNAME ELSE DISPLAY 'SQLCODE ' SQLCODE END-IF.
Rules that bite beginners:
Other languages use the same EXEC SQL idea with different terminators. C uses a semicolon. That is why HOST(IBMCOB) versus HOST(C) matters on DSNHPC.
Static SQL is statement text the precompiler can see in full. You may still use host variables for values, but you cannot change the table name or the shape of the WHERE clause at run time without going dynamic. Db2 binds an access path into the package. That is the workhorse of COBOL on z/OS: predictable, cache-friendly, and easy to EXPLAIN.
123456MOVE '000010' TO HV-EMPNO. EXEC SQL UPDATE HR.EMPLOYEE SET SALARY = SALARY * 1.03 WHERE EMPNO = :HV-EMPNO END-EXEC.
Static SQL is still “dynamic” in the everyday sense that host variable values change. What is static is the statement string and therefore the bound plan shape (table, columns, join pattern, predicate structure). If you need a different table per run, that is not static SQL.
Dynamic SQL builds or supplies statement text at run time. The simple form is EXECUTE IMMEDIATE for a string with no parameter markers. The general form is PREPARE into a statement name, then EXECUTE (optionally USING host variables or an SQLDA) or DECLARE CURSOR FOR that statement name and FETCH. Parameter markers are question marks, not host variables, in the prepared string.
123456789STRING 'UPDATE HR.EMPLOYEE SET SALARY = SALARY * 1.03' ' WHERE WORKDEPT = ?' DELIMITED BY SIZE INTO HV-SQL-TEXT. EXEC SQL PREPARE S1 FROM :HV-SQL-TEXT END-EXEC. EXEC SQL EXECUTE S1 USING :HV-DEPT END-EXEC.
Dynamic SQL is required for ad-hoc tools (DSNTEP2 is itself a dynamic SQL program) and for user-driven query builders. Costs include PREPARE CPU, more injection risk if you concatenate unchecked text, and access paths that may differ from last week. Prefer parameter markers over gluing literals into the string. Static SQL remains the default for fixed business transactions.
SQL PL (SQL procedural language) is the language of native SQL procedures and SQL functions: BEGIN...END, IF, WHILE, DECLARE handlers, SIGNAL. It runs inside Db2. You create it with CREATE PROCEDURE and you do not precompile COBOL to ship the procedure body.
COBOL embedded SQL is the opposite direction: application code in the allied address space calls Db2. COBOL has no native WHILE that is an SQL statement; you WRITE COBOL PERFORM loops and FETCH. You can mix the two: COBOL CALL a native procedure that contains SQL PL. Do not confuse SQL PL condition handlers with COBOL IF SQLCODE logic—they live in different programs.
External stored procedures can be COBOL programs with embedded SQL, prepared like any batch/CICS program and then defined with CREATE PROCEDURE ... LANGUAGE COBOL. That is still COBOL SQL, not SQL PL, even though Db2 invokes it like a routine.
A typical batch job is IKJEFT01, DSN SYSTEM(ssid), RUN PROGRAM(name) PLAN(plan). The load module is linked with DSNELI. The program issues EXEC SQL, checks SQLCODE, and issues COMMIT at designed points (end of a unit of work, every n rows for long updates). If the job steps abend, uncommitted work rolls back.
Alternatives: CAF (DSNALI) if the program must CONNECT and DISCONNECT itself, and RRSAF (DSNRLI) when the unit of recovery is RRS-coordinated with other resource managers. Stored procedures and many modern batch frameworks use RRSAF. The SQL you write does not change much; the JCL and link-edit stub do.
Batch programs often DECLARE a cursor, OPEN, FETCH in a PERFORM UNTIL SQLCODE = +100, then CLOSE. Single-row SELECT INTO is fine when you know there is at most one row (primary key lookup). Multiple rows without a cursor yield SQLCODE -811.
CICS programs are translated for CICS commands and prepared for Db2. Link-edit includes DSNCLI. Thread reuse, protected threads, and DB2CONN/DB2ENTRY resources are CICS systems-programmer topics, but application programmers must know:
Abend handling should roll back. A CICS ASRA that skips syncpoint rollback can leave surprises; use HANDLE ABEND or your framework's equivalent and be explicit.
IMS MPP, BMP, and IFP programs use the IMS attachment (DFSLI000). Db2 and IMS share the IMS syncpoint. A successful IMS checkpoint or application GU that triggers commit commits Db2 as well. SQL COMMIT in an IMS environment is not the way you coordinate DL/I and Db2 together.
Design the same way you design IMS+Db2 locking: keep the unit of work short, do not FETCH a huge cursor across a checkpoint without understanding cursor with hold and IMS rules, and bind with isolation that matches IMS throughput needs. BMP jobs look “batch-like” but they are still IMS syncpoint, not TSO DSN COMMIT, unless you are in a TSO BMP exception your site documented.
| Environment | Language interface | Typical commit |
|---|---|---|
| Batch / TSO | DSNELI (TSO) or DSNALI / DSNRLI | SQL COMMIT / ROLLBACK (or RRSAF/RRS) |
| CICS | DSNCLI | CICS SYNCPOINT / ROLLBACK |
| IMS TM / BMP / MPP | DFSLI000 | IMS syncpoint (GU/CHKP, etc.) |
Almost every COBOL Db2 program starts with EXEC SQL INCLUDE SQLCA. The SQLCA is the communications area: SQLCODE, SQLSTATE, SQLERRMC, SQLERRD, and SQLWARN flags. Under STDSQL(YES) you may declare SQLCODE and SQLSTATE as standalone hosts instead; most z/OS COBOL shops still INCLUDE SQLCA.
WHENEVER SQLERROR GOTO and WHENEVER NOT FOUND GOTO are precompiler-generated IF SQLCODE tests after each executable statement. They are easy to overuse: a GOTO out of a cursor loop on +100 is fine, but a global SQLERROR GOTO that skips CLOSE and ROLLBACK logic becomes a leak. Many modern shops prefer explicit IF SQLCODE after each statement for readability. Either style is embedded SQL; pick one per program and stay consistent.
INCLUDE of a DCLGEN member brings DECLARE TABLE plus host structures. The precompiler uses DECLARE TABLE to type-check your static SQL against the table as it was when you generated the DCLGEN—not necessarily against today's catalog until you bind. VALIDATE(BIND) then checks the real catalog. Keep DCLGEN regeneration on the same change ticket as ALTER TABLE.
12345678910111213141516171819202122IDENTIFICATION DIVISION. PROGRAM-ID. EMPINQ. DATA DIVISION. WORKING-STORAGE SECTION. EXEC SQL INCLUDE SQLCA END-EXEC. EXEC SQL INCLUDE EMPLOYEE END-EXEC. 01 HV-EMPNO PIC X(6). PROCEDURE DIVISION. MOVE '000010' TO HV-EMPNO. EXEC SQL SELECT LASTNAME, SALARY INTO :DCL-EMPLOYEE.LASTNAME, :DCL-EMPLOYEE.SALARY FROM HR.EMPLOYEE WHERE EMPNO = :HV-EMPNO END-EXEC. IF SQLCODE = 0 DISPLAY LASTNAME OF DCL-EMPLOYEE ELSE DISPLAY 'NOT FOUND OR ERROR ' SQLCODE END-IF. GOBACK.
Replace the DISPLAY path with your shop's SQL error paragraph (often DSNTIAR to format SQLCA). Add indicator variables for nullable columns. The next pages cover host variables and indicators in depth.
Embedded SQL is writing a library request on a sticky note that lives inside your COBOL story. EXEC SQL and END-EXEC are the sticky-note borders so the special Db2 helper can find them. Static SQL means the sticky note's wording is printed in the book; only the fill-in-the-blank numbers change. Dynamic SQL means you write a new sticky note while the program is already running. Batch, CICS, and IMS are three different school buildings: the sticky notes look similar, but you hand them to a different office (attachment) and you wait for the bell (commit) in different ways. SQL PL is a recipe that stays inside the library kitchen; COBOL SQL is you standing at the counter placing orders.
1. What do EXEC SQL and END-EXEC do in COBOL?
2. What is static SQL?
3. Which attachment is typical for CICS COBOL + Db2?
4. How does SQL PL differ from COBOL embedded SQL?
5. A batch COBOL program usually connects to Db2 how?