対象: COBOL から SQL を使ってデータベースにアクセスする方法 DB2(AS/400 / z/OS)と GnuCOBOL + PostgreSQL の両方をカバー
目次
- COBOLとSQLの関係
- 埋め込みSQLの基本
- CRUD操作
- カーソル処理(複数行の取得)
- エラー処理(SQLCA)
- トランザクション制御
- GnuCOBOL + PostgreSQL(OCESQL)
- DB2/400(AS/400)での実装
- DB2 z/OS(メインフレーム)での実装
- 設計パターン
1. COBOLとSQLの関係
ファイル操作 SQL操作
────────────── ──────────────
READ SELECT
WRITE INSERT
REWRITE UPDATE
DELETE DELETE
OPEN CONNECT / OPEN CURSOR
CLOSE DISCONNECT / CLOSE CURSOR
START (位置付け) WHERE 条件
READ NEXT FETCH NEXT
COBOLでSQLを使う方法は 埋め込みSQL(Embedded SQL) と呼ばれる。 COBOL のソースコード内に SQL 文を直接書く。
*> COBOLのコード内にSQLを埋め込む
EXEC SQL
SELECT EMP_NAME, SALARY
INTO :WS-EMP-NAME, :WS-SALARY
FROM EMPLOYEE
WHERE EMP_ID = :WS-EMP-ID
END-EXEC
コンパイルの流れ
2. 埋め込みSQLの基本
2.1 ホスト変数(COBOL変数とSQLの橋渡し)
WORKING-STORAGE SECTION.
*> --- ホスト変数の宣言 ---
*> EXEC SQL ... END-EXEC の間で宣言すると
*> SQL文の中で :変数名 として使える
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 WS-EMP-ID PIC X(5).
01 WS-EMP-NAME PIC X(30).
01 WS-SALARY PIC S9(9)V99 COMP-3.
01 WS-DEPT-CODE PIC X(4).
EXEC SQL END DECLARE SECTION END-EXEC.
*> --- SQL通信領域(エラー情報) ---
EXEC SQL INCLUDE SQLCA END-EXEC.
ホスト変数の命名規則:
- SQL文中では
:WS-EMP-IDのようにコロン(:)を付ける - COBOL文中ではコロンなしで普通に使う
PICとSQLの型対応:
| COBOL PIC | SQL型 | 用途 |
|---|---|---|
PIC X(n) | CHAR(n) / VARCHAR(n) | 文字列 |
PIC S9(n)V9(m) COMP-3 | DECIMAL(n+m, m) | 金額・数値 |
PIC S9(9) COMP | INTEGER | 整数 |
PIC S9(18) COMP | BIGINT | 大きな整数 |
PIC X(10) | DATE | 日付(YYYY-MM-DD形式) |
PIC X(8) | DATE | 日付(YYYYMMDD形式) |
2.2 NULL 値の扱い(インジケータ変数)
*> NULLかもしれないカラムにはインジケータ変数を使う
01 WS-PHONE PIC X(15).
01 WS-PHONE-IND PIC S9(4) COMP.
*> 0 = 値あり
*> -1 = NULL
*> > 0 = 値が切り捨てられた(元の長さ)
EXEC SQL
SELECT PHONE
INTO :WS-PHONE :WS-PHONE-IND
FROM CUSTOMER
WHERE CUSTOMER_ID = :WS-CUST-ID
END-EXEC
IF WS-PHONE-IND < 0
DISPLAY "Phone is NULL"
ELSE
DISPLAY "Phone: " WS-PHONE
END-IF
3. CRUD操作
3.1 SELECT(1行取得)
*> --- 1行だけ取得(INTO で直接受け取る) ---
MOVE "E001" TO WS-EMP-ID
EXEC SQL
SELECT EMP_NAME, SALARY, DEPT_CODE
INTO :WS-EMP-NAME, :WS-SALARY, :WS-DEPT-CODE
FROM EMPLOYEE
WHERE EMP_ID = :WS-EMP-ID
END-EXEC
EVALUATE SQLCODE
WHEN 0
DISPLAY "Found: " WS-EMP-NAME
WHEN 100
DISPLAY "Not found"
WHEN OTHER
DISPLAY "SQL error: " SQLCODE
END-EVALUATE
3.2 INSERT(追加)
EXEC SQL
INSERT INTO EMPLOYEE
(EMP_ID, EMP_NAME, SALARY, DEPT_CODE)
VALUES
(:WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY, :WS-DEPT-CODE)
END-EXEC
IF SQLCODE NOT = 0
DISPLAY "Insert error: " SQLCODE
END-IF
3.3 UPDATE(更新)
EXEC SQL
UPDATE EMPLOYEE
SET SALARY = :WS-NEW-SALARY
WHERE EMP_ID = :WS-EMP-ID
END-EXEC
*> SQLERRD(3) = 更新された行数
DISPLAY "Updated " SQLERRD(3) " rows"
3.4 DELETE(削除)
EXEC SQL
DELETE FROM EMPLOYEE
WHERE EMP_ID = :WS-EMP-ID
END-EXEC
IF SQLERRD(3) = 0
DISPLAY "No rows deleted"
END-IF
4. カーソル処理(複数行の取得)
SELECTの結果が複数行になる場合、カーソルを使って1行ずつ取得する。 COBOL のファイル操作(OPEN → READ UNTIL EOF → CLOSE)と同じ発想。
4.1 基本的なカーソル処理
*> --- 1. カーソルを宣言(DECLARE) ---
EXEC SQL
DECLARE EMP-CURSOR CURSOR FOR
SELECT EMP_ID, EMP_NAME, SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = :WS-DEPT-CODE
ORDER BY EMP_ID
END-EXEC
PROCEDURE DIVISION.
MAIN-PARA.
MOVE "DEV" TO WS-DEPT-CODE
*> --- 2. カーソルを開く(OPEN) ---
EXEC SQL OPEN EMP-CURSOR END-EXEC
*> --- 3. 1行ずつ取得(FETCH) ---
PERFORM UNTIL SQLCODE NOT = 0
EXEC SQL
FETCH EMP-CURSOR
INTO :WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY
END-EXEC
IF SQLCODE = 0
DISPLAY WS-EMP-ID " " WS-EMP-NAME " " WS-SALARY
END-IF
END-PERFORM
*> --- 4. カーソルを閉じる(CLOSE) ---
EXEC SQL CLOSE EMP-CURSOR END-EXEC.
ファイル操作との対比:
ファイル カーソル
───────── ─────────
OPEN INPUT ファイル OPEN カーソル
READ ファイル FETCH カーソル INTO ...
AT END SQLCODE = 100
CLOSE ファイル CLOSE カーソル
4.2 更新用カーソル(FOR UPDATE)
*> 取得したレコードをそのまま更新する場合
EXEC SQL
DECLARE UPD-CURSOR CURSOR FOR
SELECT EMP_ID, SALARY
FROM EMPLOYEE
WHERE DEPT_CODE = :WS-DEPT-CODE
FOR UPDATE OF SALARY *> SALARY を更新する宣言
END-EXEC
EXEC SQL OPEN UPD-CURSOR END-EXEC
PERFORM UNTIL SQLCODE NOT = 0
EXEC SQL
FETCH UPD-CURSOR
INTO :WS-EMP-ID, :WS-SALARY
END-EXEC
IF SQLCODE = 0
*> 10% 昇給
COMPUTE WS-NEW-SALARY = WS-SALARY * 1.10
EXEC SQL
UPDATE EMPLOYEE
SET SALARY = :WS-NEW-SALARY
WHERE CURRENT OF UPD-CURSOR *> 現在のカーソル位置
END-EXEC
END-IF
END-PERFORM
EXEC SQL CLOSE UPD-CURSOR END-EXEC
EXEC SQL COMMIT END-EXEC
5. エラー処理(SQLCA)
5.1 SQLCA の構造
EXEC SQL INCLUDE SQLCA END-EXEC
*> これで以下の変数が自動的に定義される
*> SQLCODE: 最も重要なエラーコード
*> 0 = 正常
*> 100 = データなし(NOT FOUND)
*> < 0 = エラー
*> > 0 = 警告
*> SQLERRD(3): 処理された行数
*> SQLSTATE: 5桁の標準エラーコード('00000' = 正常)
5.2 SQLCODE の主要な値
| SQLCODE | SQLSTATE | 意味 |
|---|---|---|
| 0 | 00000 | 正常終了 |
| 100 | 02000 | データなし(NOT FOUND) |
| -803 | 23505 | 一意制約違反(重複キー) |
| -811 | 21000 | SELECT結果が複数行(INTO使用時) |
| -204 | 42704 | テーブルが存在しない |
| -206 | 42703 | カラムが存在しない |
| -530 | 23503 | 外部キー制約違反 |
| -911 | 40001 | デッドロック(ロック競合) |
| -904 | 57011 | リソース不足 |
5.3 標準的なエラーチェックパターン
*> --- 全てのSQL文の後にチェックする ---
PERFORM CHECK-SQL-PARA
*> --- エラーチェック段落 ---
CHECK-SQL-PARA.
EVALUATE TRUE
WHEN SQLCODE = 0
CONTINUE *> 正常
WHEN SQLCODE = 100
SET WS-NOT-FOUND TO TRUE *> データなし
WHEN SQLCODE = -803
SET WS-DUPLICATE TO TRUE *> 重複キー
WHEN SQLCODE = -911
*> デッドロック → リトライ
DISPLAY "Deadlock detected, retrying..."
EXEC SQL ROLLBACK END-EXEC
SET WS-RETRY TO TRUE
WHEN SQLCODE < 0
*> その他のエラー
DISPLAY "SQL error: " SQLCODE
DISPLAY "SQLSTATE : " SQLSTATE
SET WS-SQL-ERROR TO TRUE
END-EVALUATE.
5.4 WHENEVER(自動エラーハンドリング)
*> SQL文でエラーが発生したら自動的に指定の処理を行う
EXEC SQL
WHENEVER SQLERROR GO TO SQL-ERROR-PARA
END-EXEC
EXEC SQL
WHENEVER NOT FOUND CONTINUE
END-EXEC
*> 以降の全SQL文でSQLERROR時にSQL-ERROR-PARAへジャンプ
| 条件 | 動作オプション | 意味 |
|---|---|---|
SQLERROR | SQLCODE < 0 | エラー発生時 |
SQLWARNING | SQLCODE > 0 and != 100 | 警告発生時 |
NOT FOUND | SQLCODE = 100 | データなし |
| 動作 | 意味 |
|---|---|
GO TO 段落名 | 指定段落へジャンプ |
CONTINUE | 何もしない(次の文を実行) |
STOP | プログラムを停止 |
6. トランザクション制御
*> --- COMMIT: 変更を確定 ---
EXEC SQL COMMIT END-EXEC
*> --- ROLLBACK: 変更を取消 ---
EXEC SQL ROLLBACK END-EXEC
*> --- SAVEPOINT: 中間セーブポイント ---
EXEC SQL SAVEPOINT SP1 END-EXEC
*> ... 処理 ...
*> セーブポイントまで戻す(全部は戻さない)
EXEC SQL ROLLBACK TO SAVEPOINT SP1 END-EXEC
バッチ処理でのCOMMIT間隔
*> 大量データ処理では定期的にCOMMITする
77 WS-COMMIT-INTERVAL PIC 9(5) VALUE 1000.
77 WS-COMMIT-COUNT PIC 9(5) VALUE 0.
PROCESS-LOOP.
EXEC SQL
FETCH CURSOR1 INTO :WS-DATA
END-EXEC
IF SQLCODE = 0
PERFORM BUSINESS-LOGIC-PARA
EXEC SQL
INSERT INTO OUTPUT_TABLE VALUES (:WS-RESULT)
END-EXEC
ADD 1 TO WS-COMMIT-COUNT
IF WS-COMMIT-COUNT >= WS-COMMIT-INTERVAL
EXEC SQL COMMIT END-EXEC
MOVE 0 TO WS-COMMIT-COUNT
DISPLAY "Committed at " WS-TOTAL-COUNT " records"
END-IF
END-IF.
7. GnuCOBOL + PostgreSQL(OCESQL)
7.1 環境構築
GnuCOBOL で PostgreSQL に接続するには OCESQL(Open COBOL ESQL)プリコンパイラを使う。
# OCESQL のインストール(Ubuntu/WSL)
sudo apt install libpq-dev
# OCESQL は別途ソースからビルドが必要
# https://github.com/opensourcecobol/Open-COBOL-ESQL
# または、GnuCOBOL 用の ODBC 連携も可能
sudo apt install unixodbc-dev odbc-postgresql
7.2 接続と切断
*> ソースファイルの拡張子は .sqb(SQL COBOLの慣例)
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 WS-DBNAME PIC X(30) VALUE "payroll_db".
01 WS-USER PIC X(30) VALUE "dbuser".
01 WS-PASSWORD PIC X(30) VALUE "dbpass".
EXEC SQL END DECLARE SECTION END-EXEC.
EXEC SQL INCLUDE SQLCA END-EXEC.
PROCEDURE DIVISION.
MAIN-PARA.
*> データベースに接続
EXEC SQL
CONNECT :WS-USER
IDENTIFIED BY :WS-PASSWORD
USING :WS-DBNAME
END-EXEC
IF SQLCODE NOT = 0
DISPLAY "Connect error: " SQLCODE
STOP RUN
END-IF
DISPLAY "Connected to " WS-DBNAME
*> ... 処理 ...
*> 切断
EXEC SQL DISCONNECT ALL END-EXEC
STOP RUN.
7.3 コンパイル手順
# 1. プリコンパイル(.sqb → .cob に変換)
ocesql salary-db.sqb salary-db.cob
# 2. COBOLコンパイル(PostgreSQLライブラリをリンク)
cobc -x -free -o salary-db salary-db.cob \
-L/usr/lib -lpq -locesql
# 3. 実行
./salary-db
7.4 完全なサンプル
*> ファイル: salary-db.sqb
*> GnuCOBOL + PostgreSQL での給与データ操作
IDENTIFICATION DIVISION.
PROGRAM-ID. SALARY-DB.
DATA DIVISION.
WORKING-STORAGE SECTION.
EXEC SQL BEGIN DECLARE SECTION END-EXEC.
01 WS-DBNAME PIC X(30) VALUE "payroll_db".
01 WS-USER PIC X(30) VALUE "dbuser".
01 WS-PASSWORD PIC X(30) VALUE "dbpass".
01 WS-EMP-ID PIC X(5).
01 WS-EMP-NAME PIC X(30).
01 WS-SALARY PIC S9(9)V99.
01 WS-DEPT PIC X(8).
EXEC SQL END DECLARE SECTION END-EXEC.
EXEC SQL INCLUDE SQLCA END-EXEC.
77 WS-DISP-SALARY PIC ZZZ,ZZZ,ZZ9.99.
PROCEDURE DIVISION.
MAIN-PARA.
*> 接続
EXEC SQL
CONNECT :WS-USER
IDENTIFIED BY :WS-PASSWORD
USING :WS-DBNAME
END-EXEC
*> テーブル作成(初回のみ)
EXEC SQL
CREATE TABLE IF NOT EXISTS EMPLOYEE (
EMP_ID CHAR(5) PRIMARY KEY,
EMP_NAME CHAR(30),
SALARY DECIMAL(11,2),
DEPT CHAR(8)
)
END-EXEC
*> データ挿入
MOVE "E001" TO WS-EMP-ID
MOVE "TANAKA TARO" TO WS-EMP-NAME
MOVE 250000.00 TO WS-SALARY
MOVE "DEVELOP" TO WS-DEPT
EXEC SQL
INSERT INTO EMPLOYEE VALUES
(:WS-EMP-ID, :WS-EMP-NAME, :WS-SALARY, :WS-DEPT)
END-EXEC
*> 検索
MOVE "E001" TO WS-EMP-ID
EXEC SQL
SELECT EMP_NAME, SALARY
INTO :WS-EMP-NAME, :WS-SALARY
FROM EMPLOYEE
WHERE EMP_ID = :WS-EMP-ID
END-EXEC
IF SQLCODE = 0
MOVE WS-SALARY TO WS-DISP-SALARY
DISPLAY WS-EMP-ID ": " WS-EMP-NAME
" Salary=" WS-DISP-SALARY
END-IF
EXEC SQL COMMIT END-EXEC
EXEC SQL DISCONNECT ALL END-EXEC
STOP RUN.
8. DB2/400(AS/400)での実装
8.1 AS/400 の特徴
AS/400 では物理ファイル(PF)がそのまま DB2 のテーブルとして使える。COBOLからはファイル操作でもSQL操作でもアクセスできる。
AS/400 のファイル = DB2 テーブル
COBOL
├── ファイル操作(READ/WRITE) → 物理ファイル(PF)
└── 埋め込みSQL(SELECT等) → 同じ物理ファイル
8.2 AS/400 固有のSQL文
*> AS/400 ではライブラリ.ファイル でテーブルを指定
EXEC SQL
SELECT *
FROM PAYROLL/EMPLOYEE
WHERE DEPT = :WS-DEPT
END-EXEC
*> コミットメントコントロール
*> CL コマンドで事前に設定が必要:
*> STRCMTCTL LCKLVL(*CHG)
EXEC SQL COMMIT END-EXEC
EXEC SQL ROLLBACK END-EXEC
8.3 コンパイル
/* AS/400 でのコンパイル (CL コマンド) */
CRTSQLCBL OBJ(MYLIB/MYPGM)
SRCFILE(MYLIB/QCBLLESRC)
SRCMBR(MYPGM)
COMMIT(*CHG)
OPTION(*EVENTF)
/* 手順:
1. CRTSQLCBL でプリコンパイル + コンパイルを一括実行
2. SQL文がDB2 APIコールに変換される
3. COBOLプログラムオブジェクトが生成される */
9. DB2 z/OS(メインフレーム)での実装
9.1 プリコンパイルとバインド
9.2 JCL でのコンパイル
//SQLCOBOL EXEC DSNHCOB2,MEM=MYPGM,
// PARM.PC='HOST(IBMCOB),APOST'
//PC.DBRMLIB DD DSN=MY.DBRM.LIB(MYPGM),DISP=SHR
//PC.SYSLIB DD DSN=MY.COPY.LIB,DISP=SHR
//PC.SYSIN DD DSN=MY.SOURCE.LIB(MYPGM),DISP=SHR
//*
//BIND EXEC PGM=IKJEFT01
//SYSTSIN DD *
DSN SYSTEM(DB2P)
BIND PACKAGE(MYPKG) MEMBER(MYPGM) -
ISOLATION(CS) VALIDATE(BIND)
END
/*
9.3 CICS でのオンラインSQL
*> CICS環境ではEXEC CICSとEXEC SQLが共存する
EXEC SQL
SELECT BALANCE
INTO :WS-BALANCE
FROM ACCOUNT
WHERE ACCOUNT_NO = :WS-ACCOUNT-NO
END-EXEC
IF SQLCODE NOT = 0
EXEC CICS ABEND ABCODE('SQLE') END-EXEC
END-IF
*> CICS のトランザクション制御
EXEC CICS SYNCPOINT END-EXEC *> COMMIT
EXEC CICS SYNCPOINT ROLLBACK END-EXEC *> ROLLBACK
10. 設計パターン
10.1 DAO パターン(データアクセスの分離)
業務ロジックとSQLを分離する。
*> employee-dao.cob(データアクセス層)
*> SQL文はここに集約する
PROCEDURE DIVISION
USING LK-ACTION LK-EMP-RECORD LK-RESULT.
DAO-MAIN.
EVALUATE LK-ACTION
WHEN "READ"
PERFORM DAO-READ-PARA
WHEN "INSERT"
PERFORM DAO-INSERT-PARA
WHEN "UPDATE"
PERFORM DAO-UPDATE-PARA
WHEN "DELETE"
PERFORM DAO-DELETE-PARA
END-EVALUATE
GOBACK.
DAO-READ-PARA.
EXEC SQL
SELECT EMP_NAME, SALARY, DEPT
INTO :LK-EMP-NAME, :LK-SALARY, :LK-DEPT
FROM EMPLOYEE
WHERE EMP_ID = :LK-EMP-ID
END-EXEC
MOVE SQLCODE TO LK-RESULT-CODE.
10.2 SQL vs ファイル操作の使い分け
| 基準 | ファイル操作 | SQL |
|---|---|---|
| データ量 | 全件順次処理 | 条件付き抽出 |
| アクセスパターン | 先頭から順番に処理 | ランダムアクセス |
| 結合 | マッチング処理を自分で書く | JOIN で結合 |
| 集計 | PERFORM でループして集計 | GROUP BY, SUM |
| 移植性 | COBOL標準(どこでも動く) | DB依存(DB2, PostgreSQL…) |
| 性能 | 順次処理は高速 | インデックスがあれば高速 |
| 適するもの | 大量バッチ、帳票 | マスタ検索、条件付き処理 |
10.3 環境ごとのSQL対応
| 項目 | GnuCOBOL + PostgreSQL | DB2/400 | DB2 z/OS |
|---|---|---|---|
| プリコンパイラ | OCESQL | CRTSQLCBL内蔵 | DSNHPC |
| 接続方法 | CONNECT文 | 暗黙的(同一システム) | CONNECT or PLAN |
| コミット | EXEC SQL COMMIT | EXEC SQL COMMIT | EXEC SQL COMMIT or CICS SYNCPOINT |
| ソース拡張子 | .sqb | ソースメンバ | ソースメンバ |
| 型 | PostgreSQL型 | DB2/400型 | DB2型 |
関連記事
- RPG × SQL 連携 — RPG IV の対応する SQL 連携ガイド
- COBOL 文法基礎 — COBOL の基本文法リファレンス
- COBOL 業務ロジック実装 — 業務システムの設計パターン
- COBOL 本番運用 — 本番環境での運用ガイド