SQL Basics on DB2
Practice SELECT, INSERT, and UPDATE on DB2, and learn how embedded SQL blocks let COBOL programs query and update DB2 tables directly.
Introduction
The previous lesson introduced DB2 conceptually. This lesson gets hands-on with the SQL you would actually write against it. The reassuring news, worth repeating, is that the SQL itself — SELECT, INSERT, UPDATE — is standard SQL, the same language used across the relational database world. What is specific to the mainframe is the second half of this lesson: how that SQL gets embedded directly inside a COBOL program so batch jobs and CICS transactions can read and write DB2 data as part of their normal processing.
- How to write SELECT queries against DB2 tables
- How INSERT and UPDATE work on DB2
- Why DELETE deserves extra caution in a production mainframe shop
- What embedded SQL looks like inside a COBOL program
- What host variables are and why COBOL programs need them
SELECT on DB2
A SELECT statement on DB2 looks exactly like a SELECT statement anywhere else. You name the columns you want, the table to pull them from, and — usually — a WHERE clause to narrow down which rows you actually care about.
SELECT ACCT_NUM, CUSTOMER_NAME, BALANCEFROM ACCOUNTSWHERE ACCT_TYPE = 'SAVINGS' AND BALANCE > 1000ORDER BY BALANCE DESC;Click Run to see what this code prints.
Everything about that query — filtering with WHERE, sorting with ORDER BY, joining multiple tables together — behaves exactly the way it would in any relational database. There is no mainframe-specific SELECT syntax to learn separately.
INSERT and UPDATE
INSERT adds new rows, and UPDATE modifies existing ones, again with entirely standard syntax.
-- Add a brand-new accountINSERT INTO ACCOUNTS (ACCT_NUM, CUSTOMER_NAME, ACCT_TYPE, BALANCE)VALUES ('100994512', 'J. NAKAMURA', 'CHECKING', 500.00);
-- Apply a deposit to an existing accountUPDATE ACCOUNTSSET BALANCE = BALANCE + 250.00WHERE ACCT_NUM = '100994512';If SELECT, INSERT, and UPDATE already feel familiar from another database, that familiarity is not misleading you — this genuinely is the same SQL. The mainframe-specific learning curve is in how this SQL gets called from a program, not in the SQL itself.
DELETE and Data Safety
DELETE removes rows, and it deserves particular respect in a mainframe shop, for the same reason it deserves respect anywhere: a DELETE without a precise WHERE clause can remove far more data than intended, in a system that may hold decades of financial or regulatory records. Production mainframe shops typically wrap DELETE (and UPDATE) statements in strict change-control processes, exactly the kind of discipline covered earlier in this course around JCL and job scheduling.
-- Dangerous: removes EVERY row in the tableDELETE FROM ACCOUNTS;
-- Correct: removes exactly one closed, zero-balance accountDELETE FROM ACCOUNTSWHERE ACCT_NUM = '100004411' AND BALANCE = 0 AND STATUS = 'CLOSED';Before running any UPDATE or DELETE against real data, first run the equivalent SELECT with the same WHERE clause and confirm it returns exactly the rows you expect. This single habit prevents the majority of accidental data-loss incidents.
Embedded SQL in COBOL
A COBOL program does not call out to DB2 the way a web application might call a database over a network connection. Instead, SQL statements are written directly inside the COBOL source code, wrapped in an EXEC SQL ... END-EXEC block — the same pattern you saw with EXEC CICS blocks in the previous two lessons. A special preprocessor scans the COBOL source before compilation, finds these blocks, and translates them into calls the compiled program can actually use to talk to DB2 at runtime.
WORKING-STORAGE SECTION. 01 WS-ACCT-NUM PIC X(9). 01 WS-BALANCE PIC S9(9)V99 COMP-3.
PROCEDURE DIVISION. MOVE '100482913' TO WS-ACCT-NUM
EXEC SQL SELECT BALANCE INTO :WS-BALANCE FROM ACCOUNTS WHERE ACCT_NUM = :WS-ACCT-NUM END-EXEC
IF SQLCODE = 0 DISPLAY 'BALANCE: ' WS-BALANCE ELSE DISPLAY 'ACCOUNT NOT FOUND, SQLCODE: ' SQLCODE END-IF.Click Run to see what this code prints.
Host Variables
Notice the colon in front of :WS-BALANCE and :WS-ACCT-NUM in that example. That colon marks a host variable — an ordinary COBOL data field being used to pass a value into SQL, or to receive a value back out of it. Host variables are the bridge between COBOL's data world and DB2's data world: SQL cannot reach directly into arbitrary COBOL fields, so the colon syntax makes the connection explicit and lets the SQL preprocessor generate correct code. After nearly every embedded SQL statement, a program also checks a special field called SQLCODE, which DB2 sets to indicate whether the statement succeeded, found no matching rows, or failed — the pattern shown in the IF check above.
Common Mistakes
- Running UPDATE or DELETE without first confirming the WHERE clause against a SELECT — this is the single most common cause of accidental production data loss.
- Forgetting the colon before a host variable inside embedded SQL — without it, the SQL preprocessor cannot tell a COBOL field apart from a literal or column name.
- Not checking SQLCODE after an embedded SQL statement — silently assuming success can let a program continue on bad or missing data.
- Assuming embedded SQL is a different language from standard SQL — the SQL itself is standard; only the surrounding EXEC SQL / host variable mechanics are mainframe-specific.
Best Practices
- Always test a WHERE clause with SELECT before pairing it with UPDATE or DELETE, especially against production data.
- Check SQLCODE after every embedded SQL statement in a COBOL program — treat it the same way you would treat a return code from any critical operation.
- Keep host variable names distinct and clearly related to their purpose (WS-ACCT-NUM, WS-BALANCE) so embedded SQL blocks stay readable.
- Write SQL that names its columns explicitly (avoid SELECT *) so a later change to the table structure cannot silently break a program expecting specific columns in a specific order.
Frequently Asked Questions
The core language — SELECT, INSERT, UPDATE, DELETE, joins, WHERE clauses — is standard SQL and behaves the same way. DB2 does have some of its own extensions and specific functions, but the fundamentals transfer directly.
The colon marks a host variable — an ordinary COBOL field being used to pass data into or out of SQL. It lets the SQL preprocessor distinguish a program variable from an SQL literal or column name.
SQLCODE is a special field DB2 sets after every embedded SQL statement to report what happened: 0 typically means success, a positive value like +100 usually means no matching rows were found, and negative values indicate an error.
Yes. DB2 manages concurrent access from many programs at once, whether they are CICS transactions or batch jobs, and coordinates locking so updates do not corrupt each other.
Key Takeaways
- SELECT, INSERT, UPDATE, and DELETE on DB2 use standard SQL syntax, the same as most relational databases.
- DELETE and UPDATE deserve special caution in production — always confirm the WHERE clause with a SELECT first.
- Embedded SQL wraps standard SQL inside EXEC SQL ... END-EXEC blocks directly in COBOL source code.
- Host variables, marked with a colon, are how COBOL fields pass data into and out of embedded SQL statements.
- SQLCODE reports the outcome of an embedded SQL statement and should always be checked before trusting the result.
Summary
The SQL you would write against DB2 is genuinely the same SQL used across the relational database world — the real mainframe-specific skill is embedding that SQL inside COBOL using EXEC SQL blocks, host variables, and SQLCODE checks. With CICS and DB2 both covered now, the next lesson turns to a different but equally essential topic: RACF, the security system that decides who is allowed to do any of this in the first place.